diff --git a/.clang-tidy b/.clang-tidy index 9f3240b87..66e6dd078 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,14 +1,31 @@ --- -Checks: '*,-android-cloexec-*,-cert-dcl21-cpp,-cert-err58-cpp,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-vararg,-fuchsia-*,-google-runtime-references,-hicpp-invalid-access-moved,-hicpp-no-array-decay,-hicpp-no-assembler,-hicpp-vararg,-misc-macro-parentheses,-misc-unused-parameters,-modernize-make-unique,-modernize-raw-string-literal,-readability-avoid-const-params-in-decls,-readability-implicit-bool-cast,-readability-implicit-bool-conversion' +Checks: '*,-abseil-string-find-str-contains,-altera-*,-android-cloexec-*,-bugprone-branch-clone,-bugprone-easily-swappable-parameters,-bugprone-macro-parentheses,-cert-dcl21-cpp,-cert-err58-cpp,-clang-analyzer-optin.cplusplus.VirtualCall,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-macro-usage,-cppcoreguidelines-non-private-member-variables-in-classes,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-const-cast,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-type-static-cast-downcast,-cppcoreguidelines-pro-type-vararg,-fuchsia-*,-google-runtime-references,-hicpp-avoid-c-arrays,-hicpp-invalid-access-moved,-hicpp-no-array-decay,-hicpp-no-assembler,-hicpp-vararg,-llvmlibc-*,-llvm-qualified-auto,-misc-macro-parentheses,-misc-non-private-member-variables-in-classes,-misc-no-recursion,-misc-unused-parameters,-modernize-avoid-c-arrays,-modernize-make-unique,-modernize-raw-string-literal,-modernize-use-trailing-return-type,-readability-avoid-const-params-in-decls,-readability-convert-member-functions-to-static,-readability-function-cognitive-complexity,-readability-identifier-length,-readability-implicit-bool-cast,-readability-implicit-bool-conversion,-readability-magic-numbers,-readability-qualified-auto' # # For a list of check options, see: -# http://clang.llvm.org/extra/clang-tidy/checks/list.html +# https://clang.llvm.org/extra/clang-tidy/checks/list.html # # Disabled checks: # +# abseil-string-find-str-contains +# We don't want the dependency. +# +# altera-* +# Doesn't apply. +# # android-cloexec-* # O_CLOEXEC isn't available on Windows making this non-portable. # +# bugprone-branch-clone +# Nice idea but collides but with switch statements we'll need to use +# fall-throughs to fix this, which is also bad. +# +# bugprone-easily-swappable-parameters +# Interesting test, but not something we can do much about in many places. +# +# bugprone-macro-parentheses +# False positive in the only place where it reports something and +# disabling locally doesn't work. +# # cert-dcl21-cpp # It is unclear whether this is still a good recommendation in modern C++. # @@ -16,6 +33,22 @@ Checks: '*,-android-cloexec-*,-cert-dcl21-cpp,-cert-err58-cpp,-cppcoreguidelines # Used in several singelton factory functions. I don't know of a better # way to do this and it is rather unlikely that this will be a problem. # +# clang-analyzer-optin.cplusplus.VirtualCall +# Disable a warning we get from the Catch test framework. +# +# cppcoreguidelines-avoid-c-arrays +# Alias for modernize-avoid-c-arrays. +# +# cppcoreguidelines-avoid-magic-numbers +# Generally good advice, but there are too many places where this is +# useful, for instance in tests. +# +# cppcoreguidelines-macro-usage +# There are cases where we actually need macros. +# +# cppcoreguidelines-non-private-member-variables-in-classes +# Alias for misc-non-private-member-variables-in-classes +# # cppcoreguidelines-owning-memory # Don't want to add dependency on gsl library. # @@ -48,6 +81,9 @@ Checks: '*,-android-cloexec-*,-cert-dcl21-cpp,-cert-err58-cpp,-cppcoreguidelines # This is just a matter of preference, and we can't change the interfaces # now anyways. # +# hicpp-avoid-c-arrays +# Alias for modernize-avoid-c-arrays. +# # hicpp-invalid-access-moved # Creates false positives. # @@ -60,14 +96,33 @@ Checks: '*,-android-cloexec-*,-cert-dcl21-cpp,-cert-err58-cpp,-cppcoreguidelines # hicpp-vararg # Too strict, sometimes calling vararg functions is necessary. # +# llvm-qualified-auto +# readability-qualified-auto +# This reports too many cases. Typical case is an iterator that might be +# a pointer on one system but some special type on another. +# +# llvmlibc-* +# Doesn't apply to us. +# # misc-macro-parentheses -# False positive in the only place where it reports something and -# disabling locally doesn't work. +# Old name for bugprone-macro-parentheses. +# +# misc-non-private-member-variables-in-classes +# Reports this also for structs, which doesn't make any sense. There is +# an option "IgnoreClassesWithAllMemberVariablesBeingPublic" which should +# disable this, but it didn't work for me. +# +# misc-no-recursion +# There is nothing wrong with recursion. # # misc-unused-parameters # Can't be fixed, because then Doxygen will complain. (In file # include/osmium/area/problem_reporter.hpp). # +# modernize-avoid-c-arrays +# Makes sense for some array, but especially for char arrays using +# std::array isn't a good solution. +# # modernize-make-unique # This is a C++11 library and C++ doesn't have std::make_unique. # @@ -75,16 +130,31 @@ Checks: '*,-android-cloexec-*,-cert-dcl21-cpp,-cert-err58-cpp,-cppcoreguidelines # Readability isn't that much better, arguably worse because of the new # syntax. # +# modernize-use-trailing-return-type +# I am not quite that modern. +# # readability-avoid-const-params-in-decls # This is header only library, so the declaration and implementation are # often the same and we want to have the const in implementations. # +# readability-convert-member-functions-to-static +# Reports too many false positives +# +# readability-function-cognitive-complexity +# Sometimes the large functions are needed. +# +# readability-identifier-length +# Too strict. +# # readability-implicit-bool-cast # Old name for readability-implicit-bool-conversion. # # readability-implicit-bool-conversion # I don't think this makes the code more readable. # +# readability-magic-numbers +# Alias for cppcoreguidelines-avoid-magic-numbers. +# #WarningsAsErrors: '*' HeaderFilterRegex: '\/include\/osmium\/.*' ... diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..74b20fde6 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: "https://osmcode.org/sponsors.html" diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 000000000..553d2f1d7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,48 @@ +--- +name: Report problems with the software +about: You found a (possible) bug in libosmium +title: '' +labels: '' +assignees: '' + +--- + +## What version of libosmium are you using? + + + + +## What operating system and compiler are you using? + + + + +## Tell us something about your system + + + + +## What did you do exactly? + + + + +## What did you expect to happen? + + + + +## What did happen instead? + + + + +## What did you do to try analyzing the problem? + + + diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..af95e9080 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,4 @@ +contact_links: + - name: help.osm.org + url: https://help.openstreetmap.org/ + about: Ask questions and get support from the community. diff --git a/.github/actions/build-windows/action.yml b/.github/actions/build-windows/action.yml new file mode 100644 index 000000000..1514577ad --- /dev/null +++ b/.github/actions/build-windows/action.yml @@ -0,0 +1,10 @@ +name: Windows Build + +runs: + using: composite + steps: + - name: Build + run: cmake --build . --config Release --verbose + shell: bash + working-directory: build + diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml new file mode 100644 index 000000000..81161d5ee --- /dev/null +++ b/.github/actions/build/action.yml @@ -0,0 +1,10 @@ +name: Build + +runs: + using: composite + steps: + - name: Build + run: make VERBOSE=1 + shell: bash + working-directory: build + diff --git a/.github/actions/cmake-windows/action.yml b/.github/actions/cmake-windows/action.yml new file mode 100644 index 000000000..2eafcb796 --- /dev/null +++ b/.github/actions/cmake-windows/action.yml @@ -0,0 +1,19 @@ +name: Windows CMake + +runs: + using: composite + steps: + - name: Create build directory + run: mkdir build + shell: bash + - name: Configure + run: | + cmake -LA .. \ + -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake \ + -DBUILD_HEADERS=OFF \ + -DBUILD_BENCHMARKS=ON \ + -DOsmium_DEBUG=TRUE \ + -DPROTOZERO_INCLUDE_DIR=${GITHUB_WORKSPACE}/../protozero/include + shell: bash + working-directory: build + diff --git a/.github/actions/cmake/action.yml b/.github/actions/cmake/action.yml new file mode 100644 index 000000000..b0f00ba78 --- /dev/null +++ b/.github/actions/cmake/action.yml @@ -0,0 +1,19 @@ +name: CMake + +runs: + using: composite + steps: + - name: Create build directory + run: mkdir build + shell: bash + - name: Configure + run: | + cmake -LA .. \ + -DBUILD_DATA_TESTS=ON \ + -DUSE_CPP_VERSION=${CPP_VERSION} \ + -DWITH_PROJ=${WITH_PROJ} \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DPROTOZERO_INCLUDE_DIR=${GITHUB_WORKSPACE}/../protozero/include + shell: bash + working-directory: build + diff --git a/.github/actions/ctest-windows/action.yml b/.github/actions/ctest-windows/action.yml new file mode 100644 index 000000000..5c56da4da --- /dev/null +++ b/.github/actions/ctest-windows/action.yml @@ -0,0 +1,10 @@ +name: Windows Test + +runs: + using: composite + steps: + - name: Test + run: ctest --output-on-failure -C Release + shell: bash + working-directory: build + diff --git a/.github/actions/ctest/action.yml b/.github/actions/ctest/action.yml new file mode 100644 index 000000000..ae0d9bb37 --- /dev/null +++ b/.github/actions/ctest/action.yml @@ -0,0 +1,10 @@ +name: ctest + +runs: + using: composite + steps: + - name: Test + run: ctest --output-on-failure + shell: bash + working-directory: build + diff --git a/.github/actions/install-macos/action.yml b/.github/actions/install-macos/action.yml new file mode 100644 index 000000000..896eb723b --- /dev/null +++ b/.github/actions/install-macos/action.yml @@ -0,0 +1,13 @@ +name: Install homebrew packages on macOS + +runs: + using: composite + steps: + - name: Install packages + run: | + brew install \ + boost \ + gdal \ + google-sparsehash + shell: bash + diff --git a/.github/actions/install-protozero/action.yml b/.github/actions/install-protozero/action.yml new file mode 100644 index 000000000..7526c0e62 --- /dev/null +++ b/.github/actions/install-protozero/action.yml @@ -0,0 +1,9 @@ +name: Install Protozero from git + +runs: + using: composite + steps: + - name: Install from git + run: git clone --quiet --depth 1 https://github.com/mapbox/protozero.git ../protozero + shell: bash + diff --git a/.github/actions/install-ubuntu/action.yml b/.github/actions/install-ubuntu/action.yml new file mode 100644 index 000000000..1711847c0 --- /dev/null +++ b/.github/actions/install-ubuntu/action.yml @@ -0,0 +1,20 @@ +name: Install apt packages on Ubuntu/Debian + +runs: + using: composite + steps: + - name: Install packages + run: | + sudo apt-get update -qq + sudo apt-get install -yq \ + doxygen \ + libboost-dev \ + libgdal-dev \ + libgeos++-dev \ + liblz4-dev \ + libproj-dev \ + libsparsehash-dev \ + ruby-json \ + spatialite-bin + shell: bash + diff --git a/.github/actions/install-windows/action.yml b/.github/actions/install-windows/action.yml new file mode 100644 index 000000000..ffe3a3c68 --- /dev/null +++ b/.github/actions/install-windows/action.yml @@ -0,0 +1,17 @@ +name: Install vcpkg packages on Windows + +runs: + using: composite + steps: + - name: Install packages + run: | + vcpkg install \ + boost-crc:x64-windows \ + boost-variant:x64-windows \ + bzip2:x64-windows \ + expat:x64-windows \ + lz4:x64-windows \ + sparsehash:x64-windows \ + zlib:x64-windows + shell: bash + diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..95469979f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,294 @@ +name: CI + +on: [ push, pull_request ] + +jobs: + linux: + runs-on: ubuntu-latest + timeout-minutes: 40 + strategy: + fail-fast: false + matrix: + name: [Ubuntu-18, Ubuntu-20, Ubuntu-22, Debian-10, Debian-11, Debian-12, Debian-Testing, Debian-Experimental, Fedora-35, Fedora-36, Fedora-37, Fedora-38] + build_type: [Dev] + cpp_compiler: [g++] + cpp_version: [c++11] + include: + - name: Ubuntu-18 + # Uses gcc 7.5.0, clang 6.0.0, cmake 3.10.2 + image: "ubuntu:18.04" + ubuntu: 18 + - name: Ubuntu-20 + # Uses gcc 9.3.0, clang 10.0.0, cmake 3.16.3 + image: "ubuntu:20.04" + ubuntu: 20 + - name: Ubuntu-22 + # Uses gcc 12.2.0, clang 15.0.7, cmake 3.24.2 + image: "ubuntu:22.04" + ubuntu: 22 + CXXFLAGS: -Wno-stringop-overread + - name: Debian-10 + # Uses gcc 8.3.0, clang 7.0.1, cmake 3.13.4 + image: "debian:buster" + - name: Debian-11 + # Uses gcc 10.2.1, clang 11.0.1, cmake 3.18.4 + image: "debian:bullseye" + - name: Debian-11 + image: "debian:bullseye" + cpp_version: c++17 + - name: Debian-11 + image: "debian:bullseye" + cpp_version: c++20 + - name: Debian-11 + image: "debian:bullseye" + c_compiler: clang + cpp_compiler: clang++ + - name: Debian-11 + image: "debian:bullseye" + c_compiler: clang + cpp_compiler: clang++ + cpp_version: c++17 + - name: Debian-11 + image: "debian:bullseye" + c_compiler: clang + cpp_compiler: clang++ + cpp_version: c++20 + - name: Debian-11 + image: "debian:bullseye" + build_type: RelWithDebInfo + - name: Debian-11 + image: "debian:bullseye" + c_compiler: clang + cpp_compiler: clang++ + CXXFLAGS: -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer + LDFLAGS: -fsanitize=address,undefined + - name: Debian-12 + # Uses gcc 12.2.0, clang 15.0.6, cmake 3.25.1 + image: "debian:bookworm" + CXXFLAGS: -Wno-stringop-overread + - name: Debian-12 + image: "debian:bookworm" + cpp_version: c++17 + CXXFLAGS: -Wno-stringop-overread + - name: Debian-12 + image: "debian:bookworm" + cpp_version: c++20 + CXXFLAGS: -Wno-stringop-overread + - name: Debian-12 + image: "debian:bookworm" + c_compiler: clang + cpp_compiler: clang++ + - name: Debian-12 + image: "debian:bookworm" + c_compiler: clang + cpp_compiler: clang++ + cpp_version: c++17 + - name: Debian-12 + image: "debian:bookworm" + c_compiler: clang + cpp_compiler: clang++ + cpp_version: c++20 + - name: Debian-12 + image: "debian:bookworm" + build_type: RelWithDebInfo + CXXFLAGS: -Wno-stringop-overread + - name: Debian-12 + image: "debian:bookworm" + c_compiler: clang + cpp_compiler: clang++ + CXXFLAGS: -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer + LDFLAGS: -fsanitize=address,undefined + - name: Debian-Testing + image: "debian:testing" + CXXFLAGS: -Wno-stringop-overread -Wno-dangling-reference + - name: Debian-Testing + image: "debian:testing" + c_compiler: clang + cpp_compiler: clang++ + - name: Debian-Experimental + image: "debian:experimental" + CXXFLAGS: -Wno-stringop-overread -Wno-dangling-reference + - name: Debian-Experimental + image: "debian:experimental" + c_compiler: clang + cpp_compiler: clang++ + - name: Fedora-35 + # Uses gcc 11.2.1, clang 12.0.1, cmake 3.20.5 + image: "fedora:35" + CXXFLAGS: -Wno-stringop-overread + - name: Fedora-36 + # Uses gcc 12.2.0, clang 14.0.5, cmake 3.24.2 + image: "fedora:36" + CXXFLAGS: -Wno-stringop-overread + - name: Fedora-37 + # Uses gcc 12.3.1, clang 15.0.7, cmake 3.26.4 + image: "fedora:37" + CXXFLAGS: -Wno-stringop-overread + - name: Fedora-38 + # Uses gcc 13.0.1, clang 16.0.5, cmake 3.26.4 + image: "fedora:38" + CXXFLAGS: -Wno-stringop-overread -Wno-dangling-reference + container: + image: ${{ matrix.image }} + env: + LANG: en_US.UTF-8 + BUILD_TYPE: ${{ matrix.build_type }} + CC: ${{ matrix.c_compiler }} + CXX: ${{ matrix.cpp_compiler }} + CXXFLAGS: ${{ matrix.CXXFLAGS }} + LDFLAGS: ${{ matrix.LDFLAGS }} + CPP_VERSION: ${{ matrix.cpp_version }} + WITH_PROJ: ON + APT_LISTCHANGES_FRONTEND: none + DEBIAN_FRONTEND: noninteractive + steps: + - name: Prepare container (apt) + shell: bash + if: startsWith(matrix.image, 'debian:') || startsWith(matrix.image, 'ubuntu:') + run: | + apt-get update -qq + apt-get install -yq \ + clang \ + cmake \ + doxygen \ + g++ \ + git \ + graphviz \ + libboost-dev \ + libbz2-dev \ + libexpat1-dev \ + libgdal-dev \ + libgeos++-dev \ + liblz4-dev \ + libproj-dev \ + libsparsehash-dev \ + make \ + ruby \ + ruby-json \ + spatialite-bin \ + zlib1g-dev + - name: Install compiler + shell: bash + if: matrix.cpp_compiler == 'clang++-14' + run: apt-get install -yq clang-14 + - name: Prepare container (dnf) + shell: bash + if: startsWith(matrix.image, 'fedora:') + run: | + dnf install --quiet --assumeyes \ + boost-devel \ + bzip2-devel \ + cmake \ + doxygen \ + expat-devel \ + gcc-c++ \ + gdal-devel \ + gdalcpp-static \ + geos-devel \ + git \ + graphviz \ + lz4-devel \ + make \ + proj-devel \ + ruby \ + rubygem-json \ + sparsehash-devel \ + spatialite-tools \ + zlib-devel + # Use v1 of checkout because v3 doesn't work with submodules + - uses: actions/checkout@v1 + with: + submodules: true + - uses: ./.github/actions/install-protozero + - uses: ./.github/actions/cmake + - uses: ./.github/actions/build + - uses: ./.github/actions/ctest + + ubuntu-latest: + runs-on: ubuntu-22.04 + timeout-minutes: 40 + env: + CC: clang-15 + CXX: clang++-15 + BUILD_TYPE: Dev + WITH_PROJ: ON + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - name: Install new clang + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/llvm-snapshot.asc + sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main' + sudo apt-get update -qq + sudo apt-get install -yq clang-15 + shell: bash + - uses: ./.github/actions/install-ubuntu + - uses: ./.github/actions/install-protozero + - uses: ./.github/actions/cmake + - uses: ./.github/actions/build + - uses: ./.github/actions/ctest + + macos: + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + os: + - "macos-11" + - "macos-12" + - "macos-13" + build_type: [Dev] + include: + - os: "macos-12" + build_type: Release + runs-on: ${{ matrix.os }} + env: + CC: clang + CXX: clang++ + BUILD_TYPE: ${{ matrix.build_type }} + WITH_PROJ: OFF + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - uses: ./.github/actions/install-macos + - uses: ./.github/actions/install-protozero + - uses: ./.github/actions/cmake + - uses: ./.github/actions/build + - uses: ./.github/actions/ctest + + windows-minimal: + timeout-minutes: 40 + strategy: + fail-fast: false + matrix: + os: + - windows-2019 + - windows-2022 + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - uses: ./.github/actions/install-windows + - uses: ./.github/actions/install-protozero + - uses: ./.github/actions/cmake-windows + - uses: ./.github/actions/build-windows + - uses: ./.github/actions/ctest-windows + + windows-2019-full: + runs-on: windows-2019 + steps: + - uses: actions/checkout@v3 + with: + submodules: true + - uses: ./.github/actions/install-windows + - name: Install extra packages + run: vcpkg install geos:x64-windows gdal:x64-windows proj4:x64-windows + shell: bash + - uses: ./.github/actions/install-protozero + - uses: ./.github/actions/cmake-windows + - uses: ./.github/actions/build-windows + - uses: ./.github/actions/ctest-windows + diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml new file mode 100644 index 000000000..a6c0b3359 --- /dev/null +++ b/.github/workflows/clang-tidy.yml @@ -0,0 +1,65 @@ +name: clang-tidy + +on: workflow_dispatch + +jobs: + clang-tidy: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + image: ["debian:bullseye", "debian:testing", "debian:experimental"] + include: + - image: "debian:bullseye" + clang: 11 + - image: "debian:testing" + clang: 15 + - image: "debian:experimental" + clang: 15 + container: + image: ${{ matrix.image }} + env: + BUILD_TYPE: Dev + CC: clang-${{ matrix.clang }} + CXX: clang++-${{ matrix.clang }} + CPP_VERSION: c++11 + WITH_PROJ: ON + APT_LISTCHANGES_FRONTEND: none + DEBIAN_FRONTEND: noninteractive + steps: + - name: Prepare container (apt) + run: | + apt-get update -qq + apt-get install -yq \ + clang-${{ matrix.clang }} \ + clang-tidy-${{ matrix.clang }} \ + cmake \ + git \ + libboost-dev \ + libbz2-dev \ + libexpat1-dev \ + libgdal-dev \ + libgeos++-dev \ + liblz4-dev \ + libproj-dev \ + libsparsehash-dev \ + make \ + zlib1g-dev + shell: bash + # Use v1 of checkout because v3 doesn't work with submodules + - uses: actions/checkout@v1 + with: + submodules: true + - uses: ./.github/actions/install-protozero + - uses: ./.github/actions/cmake + - name: clang-tidy + run: make clang-tidy | tee clang-tidy-${{ matrix.clang }}.log + shell: bash + working-directory: build + - name: Upload Log + uses: actions/upload-artifact@v3 + if: always() + with: + name: clang-tidy-${{ matrix.clang }}-log + path: build/clang-tidy-${{ matrix.clang }}.log + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bc5615d9b..000000000 --- a/.travis.yml +++ /dev/null @@ -1,243 +0,0 @@ -#----------------------------------------------------------------------------- -# -# Configuration for continuous integration service at travis-ci.org -# -#----------------------------------------------------------------------------- - -language: generic - -sudo: false - -dist: trusty - -#----------------------------------------------------------------------------- - -cache: - directories: - - $HOME/.ccache - -env: - global: - - CCACHE_TEMPDIR=/tmp/.ccache-temp - - CCACHE_COMPRESS=1 - - CASHER_TIME_OUT=1000 - -#----------------------------------------------------------------------------- - -# Save common build configurations as shortcuts, so we can reference them later. -addons_shortcuts: - addons_clang35: &clang35 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] - packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'clang-3.5'] - addons_clang38: &clang38 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] - packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'clang-3.8'] - addons_clang39: &clang39 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] - packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'clang-3.9'] - addons_clang40: &clang40 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'boost-latest', 'llvm-toolchain-trusty-4.0' ] - packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'clang-4.0'] - addons_clang50: &clang50 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'boost-latest', 'llvm-toolchain-trusty-5.0' ] - packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'clang-5.0', 'g++-6', 'gcc-6'] - addons_gcc48: &gcc48 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] - packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'g++-4.8', 'gcc-4.8' ] - addons_gcc49: &gcc49 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] - packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'g++-4.9', 'gcc-4.9' ] - addons_gcc5: &gcc5 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] - packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'g++-5', 'gcc-5' ] - addons_gcc6: &gcc6 - apt: - sources: [ 'ubuntu-toolchain-r-test', 'boost-latest' ] - packages: [ 'libboost1.55-all-dev', 'libgdal-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin', 'g++-6', 'gcc-6' ] - -#----------------------------------------------------------------------------- - -matrix: - include: - - # 1/ Linux Clang Builds - - os: linux - compiler: linux-clang35-release - env: CC='clang-3.5' CXX='clang++-3.5' BUILD_TYPE='Release' - addons: *clang35 - - - os: linux - compiler: linux-clang35-dev - env: CC='clang-3.5' CXX='clang++-3.5' BUILD_TYPE='Dev' - addons: *clang35 - - - os: linux - compiler: linux-clang38-release - env: CC='clang-3.8' CXX='clang++-3.8' BUILD_TYPE='Release' - addons: *clang38 - - - os: linux - compiler: linux-clang38-dev - env: CC='clang-3.8' CXX='clang++-3.8' BUILD_TYPE='Dev' - addons: *clang38 - - - os: linux - compiler: linux-clang39-release - env: CC='clang-3.9' CXX='clang++-3.9' BUILD_TYPE='Release' - addons: *clang39 - - - os: linux - compiler: linux-clang39-dev - env: CC='clang-3.9' CXX='clang++-3.9' BUILD_TYPE='Dev' - addons: *clang39 - - - os: linux - compiler: linux-clang40-release - env: CC='clang-4.0' CXX='clang++-4.0' BUILD_TYPE='Release' - addons: *clang40 - - - os: linux - compiler: linux-clang40-dev - env: CC='clang-4.0' CXX='clang++-4.0' BUILD_TYPE='Dev' - addons: *clang40 - - - os: linux - compiler: linux-clang50-release - env: CC='clang-5.0' CXX='clang++-5.0' BUILD_TYPE='Release' - addons: *clang50 - - - os: linux - compiler: linux-clang50-dev - env: CC='clang-5.0' CXX='clang++-5.0' BUILD_TYPE='Dev' - addons: *clang50 - -# Disabled because it creates false-positives on the old travis systems -# - os: linux -# compiler: linux-clang50-debug -# env: CC='clang-5.0' CXX='clang++-5.0' BUILD_TYPE='Debug' -# CXXFLAGS="-fsanitize=address,undefined,integer -fno-sanitize-recover=all -fno-omit-frame-pointer" -# LDFLAGS="-fsanitize=address,undefined,integer" -# # LSAN doesn't work on container-based system -# sudo: required -# addons: *clang50 - - # 2/ Linux GCC Builds - - os: linux - compiler: linux-gcc48-release - env: CC='gcc-4.8' CXX='g++-4.8' BUILD_TYPE='Release' - CXXFLAGS='-Wno-return-type' - addons: *gcc48 - - - os: linux - compiler: linux-gcc48-dev - env: CC='gcc-4.8' CXX='g++-4.8' BUILD_TYPE='Dev' - CXXFLAGS='-Wno-return-type' - addons: *gcc48 - - - os: linux - compiler: linux-gcc49-release - env: CC='gcc-4.9' CXX='g++-4.9' BUILD_TYPE='Release' - addons: *gcc49 - - - os: linux - compiler: linux-gcc49-dev - env: CC='gcc-4.9' CXX='g++-4.9' BUILD_TYPE='Dev' - addons: *gcc49 - - - os: linux - compiler: linux-gcc5-release - env: CC='gcc-5' CXX='g++-5' BUILD_TYPE='Release' - addons: *gcc5 - - - os: linux - compiler: linux-gcc5-dev - env: CC='gcc-5' CXX='g++-5' BUILD_TYPE='Dev' - addons: *gcc5 - - - os: linux - compiler: linux-gcc6-release - env: CC='gcc-6' CXX='g++-6' BUILD_TYPE='Release' - addons: *gcc6 - - - os: linux - compiler: linux-gcc6-dev - env: CC='gcc-6' CXX='g++-6' BUILD_TYPE='Dev' - addons: *gcc6 - - - os: linux - compiler: linux-gcc6-coverage - env: CC='gcc-6' CXX='g++-6' BUILD_TYPE='Coverage' - addons: *gcc6 - - # 3/ OSX Clang Builds - - os: osx - osx_image: xcode6.4 - compiler: xcode64-clang-release - env: CC='clang' CXX='clang++' BUILD_TYPE='Release' - - - os: osx - osx_image: xcode6.4 - compiler: xcode64-clang-dev - env: CC='clang' CXX='clang++' BUILD_TYPE='Dev' - - - os: osx - osx_image: xcode7 - compiler: xcode7-clang-release - env: CC='clang' CXX='clang++' BUILD_TYPE='Release' - - - os: osx - osx_image: xcode7 - compiler: xcode7-clang-dev - env: CC='clang' CXX='clang++' BUILD_TYPE='Dev' - - - os: osx - osx_image: xcode8.3 - compiler: xcode8-clang-release - env: CC='clang' CXX='clang++' BUILD_TYPE='Release' - - - os: osx - osx_image: xcode8.3 - compiler: xcode8-clang-dev - env: CC='clang' CXX='clang++' BUILD_TYPE='Dev' - - -install: - - git clone --quiet --depth 1 https://github.com/mapbox/protozero.git ../protozero - - | - if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then - brew remove gdal - brew install cmake boost google-sparsehash gdal || true - fi - - cmake --version - -before_script: - - cd ${TRAVIS_BUILD_DIR} - - git submodule update --init - - mkdir build && cd build - - cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_DATA_TESTS=ON -DBUILD_WITH_CCACHE=1 - -script: - - make VERBOSE=1 && ctest --output-on-failure - -after_success: - - | - if [ "${BUILD_TYPE}" = "Coverage" ]; then - curl -S -f https://codecov.io/bash -o codecov - chmod +x codecov - gcov-${CXX#g++-} -p $(find test/CMakeFiles -name '*.o') - ./codecov -Z -c -X gcov -F unit_tests - gcov-${CXX#g++-} -p $(find test/data-tests -name '*.o') - ./codecov -Z -c -X gcov -F data_tests - gcov-${CXX#g++-} -p $(find examples -name '*.o') - ./codecov -Z -c -X gcov -F examples - fi - -#----------------------------------------------------------------------------- diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py deleted file mode 100644 index 6fc19376d..000000000 --- a/.ycm_extra_conf.py +++ /dev/null @@ -1,53 +0,0 @@ -#----------------------------------------------------------------------------- -# -# Configuration for YouCompleteMe Vim plugin -# -# http://valloric.github.io/YouCompleteMe/ -# -#----------------------------------------------------------------------------- - -from os.path import realpath, dirname - -basedir = dirname(realpath(__file__)) - -# some default flags -# for more information install clang-3.2-doc package and -# check UsersManual.html -flags = [ -'-Werror', -'-Wall', -'-Wextra', -'-pedantic', -'-Wno-return-type', -'-Wno-unused-parameter', -'-Wno-unused-variable', - -'-std=c++11', - -# '-x' and 'c++' also required -# use 'c' for C projects -'-x', -'c++', - -# workaround for https://github.com/Valloric/YouCompleteMe/issues/303 -# also see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=800618 -'-isystem', -'/usr/lib/ycmd/clang_includes/', - -# libosmium include dirs -'-I%s/include' % basedir, -'-I%s/test/include' % basedir, -'-I%s/test/data-test/include' % basedir, - -# include third party libraries -'-I/usr/include/gdal', -] - -# youcompleteme is calling this function to get flags -# You can also set database for flags. Check: JSONCompilationDatabase.html in -# clang-3.2-doc package -def FlagsForFile( filename ): - return { - 'flags': flags, - 'do_cache': True - } diff --git a/CHANGELOG.md b/CHANGELOG.md index fa838f50b..231f3a25d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,468 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### Fixed +## [2.20.0] - 2023-09-20 + +### Changed + +* Optionally allow fractional seconds in timestamps in OSM files. +* Enable `posix_fadvise` usage on FreeBSD. +* Make parsing PBFs a bit less picky. +* Various small code cleanups. + +### Fixed + +* Don't use class template arguments on `GeometryFactory` constructor + definition. + +## [2.19.0] - 2023-01-19 + +### Changed + +* Mark RapidJSON support as deprecated. +* Update included Catch to v2.13.10. +* Remove deprecated BoolVector class. +* Remove deprecated NWRIdSet class. +* Remove deprecated AssemblerConfig constructor. +* Print start of offending string in overlong string exception. +* Implement `set_thread_name()` on FreeBSD. +* Some small code cleanups. + +### Fixed + +* Fix return type in `MembersDatabaseCommon::count_not_removed()`. +* Make bzip2 unit tests pass on musl-based systems. +* Fix bug in members database test case. + +## [2.18.0] - 2022-02-07 + +### Changed + +* Use `system_error` instead of `runtime_error` where it fits better. +* Remove `OSMIUM_NORETURN` macro. This hasn't been used in a while. + +### Removed deprecated parts of the code + +Several parts of libosmium have been marked deprecated, many of them for a very +long time. These are now removed: + +* Sparsehash index class `osmium::index::map::SparseMemTable` as well as the + complete file `osmium/index/map/sparse_mem_table.hpp`. +* Callback functionality of the `osmium::memory::Buffer` class. The + `set_full_callback()` will not be available any more. See the source + for replacement options. +* Various `osmium::builder::build_*` functions in + `osmium/builder/builder_helper.hpp`. Use `osmium::builder::add_*` + functions instead. Removes `builder_helper.hpp`. +* `osmium::builder::Builder::add_item(const osmium::memory::Item* item)`. + Use the function of the same name taking a reference instead. +* `osmium::builder::OSMObject/ChangesetBuilder::add_user()`. Use + `set_user()` instead. +* `osmium::builder::ChangesetBuilder::bounds()` returning a modifiable + reference. Use `set_bounds()` instead. +* Several functions around `osmium::io::OutputIterator`. +* `osmium::Area::inner_ring_cbegin/cend()`, use `inner_rings()` instead. +* `osmium::RelationMember::ref()`, use `set_ref()` instead. +* Implicit conversion from `osmium::Timestamp` to `std::time_t`. Use + `seconds_since_epoch()` instead. +* `osmium::string_to_user_id()`, use `string_to_uid` instead. +* `osmium::static_cast_with_assert()` helper functions as well as the + complete include file `osmium/util/cast.hpp`. +* Some constructors of `osmium::util::MemoryMapping` and + `osmium::util::TypedMemoryMapping`. Use other constructor instead. + + +## [2.17.3] - 2022-01-19 + +### Fixed + +* Removed possible deadlock when shutting down active Reader. + + +## [2.17.2] - 2021-12-16 + +### Changed + +* Libosmium now supports being compiled in C++17 and C++20 mode. The minimum + version required is still C++11, but if you use libosmium in an C++17 or + C++20 application this should work properly. +* Switch from catch version 1 to catch2 as test framework. +* When `std::variant` is available (C++17 and above), libosmium will use that + instead of `boost::variant` reducing the dependencies a little bit. +* Removed various workaround that were needed for older MSVC compilers. +* Remove use of `boost::filter_iterator` and `boost::indirect_iterator`. The + removes the dependency on Boost Iterator. +* Examples now mostly use the somewhat cleaner `return` instead of + `std::exit()` to return an exit code from `main`. +* As always: Various small code cleanups. + +### Fixed + +* When ordering OSM objects (mostly use in the `CheckOrder` handler), the + smallest id possible (`INTMIN`) wasn't sorted correctly. +* Threading problem when reading files. +* Possible dereference of invalid iterator in legacy area assembler. This + only affects the legacy area assembler that takes old-style multipolygons + into account, so modern code that is not working with history data is not + affected. +* Fixed read from an empty queue when reading a file which could block + libosmium forever when an error was encountered while reading a file. + +### Deprecated + +Several parts of libosmium have been marked deprecated, many of them for a very +long time. These will not be part of the next version of libosmium: + +* Sparsehash index class `osmium::index::map::SparseMemTable` as well as the + complete file `osmium/index/map/sparse_mem_table.hpp`. +* Callback functionality of the `osmium::memory::Buffer` class. The + `set_full_callback()` will not be available any more. See the source + for replacement options. +* Various `osmium::builder::build_*` functions in + `osmium/builder/builder_helper.hpp`. Use `osmium::builder::add_*` + functions instead. Removes `builder_helper.hpp`. +* `osmium::builder::Builder::add_item(const osmium::memory::Item* item)`. + Use the function of the same name taking a reference instead. +* `osmium::builder::OSMObject/ChangesetBuilder::add_user()`. Use + `set_user()` instead. +* `osmium::builder::ChangesetBuilder::bounds()` returning a modifiable + reference. Use `set_bounds()` instead. +* Several functions around `osmium::io::OutputIterator`. +* `osmium::Area::inner_ring_cbegin/cend()`, use `inner_rings()` instead. +* `osmium::RelationMember::ref()`, use `set_ref()` instead. +* Implicit conversion from `osmium::Timestamp` to `std::time_t`. Use + `seconds_since_epoch()` instead. +* `osmium::string_to_user_id()`, use `string_to_uid` instead. +* `osmium::static_cast_with_assert()` helper functions as well as the + complete include file `osmium/util/cast.hpp`. +* Some constructors of `osmium::util::MemoryMapping` and + `osmium::util::TypedMemoryMapping`. Use other constructor instead. + + +## [2.17.1] - 2021-10-05 + +### Added + +* Add `osmium_tags_filter` example showing use of tags filter. +* Add `Writer::set_header()` function to set header after constructing. + +### Changed + +* Various improvements in PBF file reading make it slightly faster and less + CPU intensive. +* Since 2.17.0 Osmium will, when reading files, tell the kernel using + `fadvise` that it can remove pages from the buffer cache that are not + needed any more. This is usually beneficial, because the memory can be used + for something else. But if you are reading the same OSM file multiple times + at the same time or in short succession, it might be better to keep those + buffer pages. In that case you can set the environment variable + `OSMIUM_CLEAN_PAGE_CACHE_AFTER_READ` to `no` and Osmium will not call + `fadvise`. Set it to `yes` or anything else (or not set it at all) to get + the default behaviour. +* If the macro `OSMIUM_DEFINE_EXPORT` is defined, all exception classes used + by Osmium will get "tagged as exported" using `__declspec(dllexport)` when + using MSVC or `__attribute__ ((visibility("default")))` on other compilers. + This is needed in PyOsmium. + +### Fixed + +* Fix integer parser. IDs in OPL files can now be anything between -2^63 and + 2^63-1. + + +## [2.17.0] - 2021-04-26 + +### Added + +* Add "ids" output format. New IDS output format that is similar to + the OPL format, but only the entity type and id is written out. +* Add convenience functions `left()`, `right()`, `top()`, `bottom()` to + access `osmium::Box` boundaries. +* Add polygon output to WKB factory. +* Add functions to access storage from `node_locations_for_ways` + handler. +* Add flag `osmium::io::buffers_type` for telling the `Reader` class whether + you want buffers read to only contain a single type of OSM entity. +* Add convenient named `nodes()`, `ways()`, and `relations()` accessor + functions to `nwr_array` class. +* Add `DeltaDecode::value()` accessor function. +* Add variant of the `Buffer::purge_removed()` function which doesn't take + a callback parameter. + +### Changed + +* Different varint decoding for faster PBF decoding. This makes PBF + decoding about 15% faster. +* Several code optimmizations in (PBF) writer code that speed up + writing of OSM files considerably while using less CPU and spreading + the load on multiple CPUs. +* Use memset/memcpy instead of `std::fill_n` and `std::copy` in object + builder for some slight speedups. +* Ignore metadata setting on reader for history/change files. History + and change files must be read with metadata, because otherwise the + information is lost whether an object is visible or deleted. So + ignore this setting in that case. +* On Linux: Use fadvise() to tell kernel about our reading patterns: + 1. Tell kernel that we are reading OSM files sequentially. This + should improve pre-fetching of data blocks. + 2. Tell kernel that we are done with block so they can be released. + This means we don't hog the buffer cache for something that + will, in all likelyhood, not be needed any more. +* Use assert() instead of exception in "can not happen" situation in + the relations manager code. +* Various code cleanups. + +### Fixed + +* Test failure with `add_tag_list` on some systems. +* Test framework fix for aarch64 architecture. +* Remove undefined behaviour in bzip2 compression code. +* Rename some local variables to not shadow member functions. +* Wrap `osmium::util::MemoryMapping::unmap()` in try/catch on Windows + also because we call this from a noexcept function. +* Removed superfluous `std::forward`s and fixed code that called + `std::forward` multiple times on the same object. +* Fix in OPL parser which could lead to invalid data being generated. +* Fixed three bugs in O5M parser which could lead to an infinit loop + or segmentation faults. + +## [2.16.0] - 2021-01-08 + +### Added + +* The PBF reader and writer now understand PBF blobs compressed with the LZ4 + compression algorithm in addition to the usual ZLIB compression (or no + compression at all). LZ4 is much faster to compress and uncompress. Use + by setting the `pbf_compression` output file format option to `lz4`. You + have to define `OSMIUM_WITH_LZ4` to enable this before including any + libosmium includes. +* The function `osmium::io::supported_pbf_compression_types` can now be used + to get a list of all PBF compression types supported. +* The output file option `pbf_compression_level` can now be set to an integer. + The range depends on the compression type used, 0-9 for zlib compression + and 1-65537 for lz4 compression. +* Adds `ptr_begin()`/`ptr_end()` functions to `ObjectPointerCollection` for + accessing the pointers instead of the underlying objects. + +### Changed + +* The `osmium::io::Writer::close()` function now returns the number of bytes + written to an OSM file if it is available (and 0 otherwise). +* Use stable sort when sorting `ObjectPointerCollection`. + +### Fixed + +* Various small fixes and cleanups. + + +## [2.15.6] - 2020-06-27 + +### Added + +* Add `IdSetSmall::merge_sorted` function. + +### Changed + +* Little optimization for IdSetSmall: Don't add the same id twice in a row. + +### Fixed + +* Do not build areas with "recursion depth > 20". This happens when there + are complex multipolygon with many rings touching in single points. This + is a quick fix that hopefully keeps us going until we find a better + solution. + +## [2.15.5] - 2020-04-21 + +### Added + +* Additional constructor for `builder::attr::member_type(_string)` taking + char type making it even easier to generate test data. +* Allow single C string or `std::string` as argument for `builder::attr::_tag`. + Must contain key and value separated by the equal sign. +* New `builder::attr::_t()` function to set tags from comma-separated string. +* New `nwr_array` iterator. +* Support for the PROJ library has now been declared deprecated. The old + PROJ API (up to version PROJ 6) is currently still available, but will + be removed in a future version. Support for the new PROJ API will not be + in libosmium. See https://github.com/osmcode/osmium-proj for some code + that might help you if you need this. + +### Changed + +* Check how much space is available in file system before resizing memory + mapped file (not on Windows). This means we can, at least in some cases, + show an error message instead of crashing the program. + +### Fixed + +* Parsing coordinates in PBF files did not work correctly if an lat/lon + offset was specified (which almost never happens). +* Make OPL parser more strict: Attributes can only be specified once. +* Do not close stdout after writing OSM file to it. + +## [2.15.4] - 2019-11-28 + +### Added + +* Add osmium::Options::empty() for consistency with STL containers. + +### Fixed + +* Massive reduction of memory consumption in area assembly code. For some + very complex polygons memory usage can drop from multiple gigabytes to just + megabytes. + +## [2.15.3] - 2019-09-16 + +### Added + +* New header option "sorting" when reading and writing PBFs. If the header + option "sorting" is set to `Type_then_ID`, the optional header property + `Sort.Type_then_ID` is set on writing to PBF files. When reading PBF files + with this header property, the "sorting" header option is set accordingly. + +### Fixed + +* Do not propagate C++ exception through C code. We are using the Expat + XML parser, a C library. It calls callbacks in our code. When those + callbacks throw, the exception was propagated through the C code. This + did work in the tests, but that behaviour isn't guaranteed (C++ + standard says it is implementation defined). This fixes it by catching + the exception and rethrowing it later. + +## [2.15.2] - 2019-08-16 + +### Added + +* Instead of handler classes, the `apply` function can now also take + lambdas (or objects from classes implementing `operator()`). +* Add swap, copy constructor and assignment operator to IdSetDense. + +### Changed + +* Enable use of the old proj API in proj version 6. This is a stopgap + solution until we find a better one. +* Better error messages when there is an error parsing a timestamp. +* Cleaned up a lot of code based on clang-tidy warnings. +* Ignore or subelement of or . + elements are created by Overpass API as subelements of ways or relations + when the "out bb" format is used. subelements turn up in files + downloaded from http://download.openstreetmap.fr/replication . Libosmium + used to throw an error like "Unknown element in : bbox". With this + commit, these subelements are ignored, ie. there is no error any more, + but the data is not read. +* Add swap, copy constructor and assignment operator to IdSetDense. +* Update included catch.hpp to 1.12.2. +* Retire use of `OSMIUM_NORETURN` macro. Use `[[noreturn]]` instead. + +### Fixed + +* Do not build areas with more than 100 locations where rings touch. + Places where rings touch are unusual for normal multipolygons and the + algorithm in libosmium that assembles multipolygons does not handle + them well. If there are too many touching points it becomes very slow. + This is not a problem for almost all multipolygons. As I am writing + this there are only three relations in the OSM database with more than + 100 touching points, all of them rather weird boundaries in the US. + With this commit libosmium will simply ignore those areas to keep the + processing speed within reasonable bounds. + + +## [2.15.1] - 2019-02-26 + +### Added + +* More tests. +* CMake config: also find clang-tidy-7. + +### Changed + +* Example and benchmark programs now don't crash with exceptions any more + but report them properly. + +### Fixed + +* Compile with NDEBUG in RelWithDebInfo mode. +* Correctly throw exception in `multimap::dump_as_list()`. +* Integer truncation on 32 bit systems in `MemoryUsage`. +* Exception specification on some functions. +* Forwarding references that might have hidden copy/move constructors. + + +## [2.15.0] - 2018-12-07 + +### Added + +* Function `dump_as_array()` to dump sparse array indexes. +* Set the `xml_josm_upload` header option when reading XML files. +* New function `OSMObject::remove_tags()` marks tags on OSM objects as + removed. +* More tests. + +### Changed + +* When reading OSM files Libosmium now has less memory overhead, especially + when reading PBF files. This works by using more, but smaller buffers. +* The `TagsFilter` class is now based on the `TagsFilterBase` template + class which allows setting the result type. This allows the filter to + return more data depending on the rule that matched. +* Use enums for many constants instead of (static) const(expr) variables. +* Make `chunk_bits` in `IdSetDense` configurable. +* Hardcode `%lld` format instead of using `` PRI macro. +* Update included gdalcpp to version 1.2.0. + +### Fixed + +* The gzip/bzip2 compression code was overhauled and is better tested now. + This fixes some bugs on Windows. + + +## [2.14.2] - 2018-07-23 + +### Fixed + +* PBF reader and writer depended on byte order of system architecture. +* Removed an unreliable test that didn't work on some architectures. + + +## [2.14.1] - 2018-07-23 + +### Changed + +* Libosmium now needs the newest Protozero version 1.6.3. +* Removes dependency on the utfcpp library for conversions between Unicode + code points and UTF-8. We have our own functions for this now. This also + gives us more control on where errors are thrown in this code. +* Add support for using the CRC32 implementation from the zlib library in + addition to the one from Boost. It is significantly faster and means we + have one less dependency, because zlib is needed anyway in almost all + programs using Osmium due to its use in the PBF format. Set macro + `OSMIUM_TEST_CRC_USE_BOOST` before compiling the tests, if you want to + run the tests with the boost library code, otherwise it will use the + zlib code. Note that to use this you have to change your software slightly, + see the documentation of the `CRC_zlib` class for details. +* Add a `clear_user()` function to OSMObject and Changeset which allows + removing the user name of an entity without re-creating it in a new buffer. +* In Osmium the 0 value of the Timestamp is used to denote the "invalid" + Timestamp, and its output using the `to_iso()` function is the empty + string. But this is the wrong output for OSM XML files, where a + timestamp that's not set should still be output as + 1970-01-01T00:00:00Z. This version introduces a new `to_is_all()` + function which will do this and uses that function in the XML writer. +* Use `protozero::byteswap_inplace` instead of `htonl`/`ntohl`. Makes the + code simpler and also works on Windows. +* Marked `MultipolygonCollector` class as deprecated. Use the + `MultipolygonManager` class introduced in 2.13.0 instead. +* Lots of code cleanups especially around `assert`s. Libosmium checks out + clean with `clang-tidy` now. Some documentation updates. + +### Fixed + +* Fix compilation error when `fileno()` is a macro (as in OpenBSD 6.3). +* Make `Box` output consistent with the output of a single `Location` + and avoids problems with some locales. + ## [2.14.0] - 2018-03-31 @@ -823,7 +1285,24 @@ This project adheres to [Semantic Versioning](https://semver.org/). Doxygen (up to version 1.8.8). This version contains a workaround to fix this. -[unreleased]: https://github.com/osmcode/libosmium/compare/v2.14.0...HEAD +[unreleased]: https://github.com/osmcode/libosmium/compare/v2.20.0...HEAD +[2.20.0]: https://github.com/osmcode/libosmium/compare/v2.19.0...v2.20.0 +[2.19.0]: https://github.com/osmcode/libosmium/compare/v2.18.9...v2.19.0 +[2.18.0]: https://github.com/osmcode/libosmium/compare/v2.17.3...v2.18.0 +[2.17.3]: https://github.com/osmcode/libosmium/compare/v2.17.2...v2.17.3 +[2.17.2]: https://github.com/osmcode/libosmium/compare/v2.17.1...v2.17.2 +[2.17.1]: https://github.com/osmcode/libosmium/compare/v2.17.0...v2.17.1 +[2.17.0]: https://github.com/osmcode/libosmium/compare/v2.16.0...v2.17.0 +[2.16.0]: https://github.com/osmcode/libosmium/compare/v2.15.6...v2.16.0 +[2.15.6]: https://github.com/osmcode/libosmium/compare/v2.15.5...v2.15.6 +[2.15.5]: https://github.com/osmcode/libosmium/compare/v2.15.4...v2.15.5 +[2.15.4]: https://github.com/osmcode/libosmium/compare/v2.15.3...v2.15.4 +[2.15.3]: https://github.com/osmcode/libosmium/compare/v2.15.2...v2.15.3 +[2.15.2]: https://github.com/osmcode/libosmium/compare/v2.15.1...v2.15.2 +[2.15.1]: https://github.com/osmcode/libosmium/compare/v2.15.0...v2.15.1 +[2.15.0]: https://github.com/osmcode/libosmium/compare/v2.14.2...v2.15.0 +[2.14.2]: https://github.com/osmcode/libosmium/compare/v2.14.1...v2.14.2 +[2.14.1]: https://github.com/osmcode/libosmium/compare/v2.14.0...v2.14.1 [2.14.0]: https://github.com/osmcode/libosmium/compare/v2.13.1...v2.14.0 [2.13.1]: https://github.com/osmcode/libosmium/compare/v2.13.0...v2.13.1 [2.13.0]: https://github.com/osmcode/libosmium/compare/v2.12.2...v2.13.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index a8409133f..c9c15bafa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,25 +6,40 @@ # #----------------------------------------------------------------------------- -cmake_minimum_required(VERSION 2.8 FATAL_ERROR) +cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +#----------------------------------------------------------------------------- +# +# Configurations +# +#----------------------------------------------------------------------------- + +set(CMAKE_CXX_FLAGS_COVERAGE + "-g -O0 -fno-inline-functions -fno-inline --coverage ${extra_coverage_flags_}" + CACHE STRING "Flags used by the compiler during coverage builds.") + +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "--coverage" + CACHE STRING "Flags used by the linker during coverage builds.") + +set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Coverage" + CACHE STRING + "List of available configuration types" + FORCE) + + #----------------------------------------------------------------------------- # # Project version # #----------------------------------------------------------------------------- -set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Coverage" - CACHE STRING - "List of available configuration types" - FORCE) - project(libosmium) set(LIBOSMIUM_VERSION_MAJOR 2) -set(LIBOSMIUM_VERSION_MINOR 14) +set(LIBOSMIUM_VERSION_MINOR 20) set(LIBOSMIUM_VERSION_PATCH 0) set(LIBOSMIUM_VERSION @@ -62,10 +77,11 @@ option(BUILD_BENCHMARKS "compile benchmark programs" ${dev_build}) option(BUILD_DATA_TESTS "compile data tests, please run them with ctest" ${data_test_build}) option(INSTALL_GDALCPP "also install gdalcpp headers" OFF) -option(INSTALL_UTFCPP "also install utfcpp headers" OFF) option(WITH_PROFILING "add flags needed for profiling" OFF) +option(WITH_PROJ "build/test with proj" ON) + #----------------------------------------------------------------------------- # @@ -101,14 +117,6 @@ endif() # set(extra_coverage_flags_ "-fkeep-inline-functions") #endif() -set(CMAKE_CXX_FLAGS_COVERAGE - "-g -O0 -fno-inline-functions -fno-inline --coverage ${extra_coverage_flags_}" - CACHE STRING "Flags used by the compiler during coverage builds.") - -set(CMAKE_EXE_LINKER_FLAGS_COVERAGE - "--coverage" - CACHE STRING "Flags used by the linker during coverage builds.") - if(CMAKE_BUILD_TYPE STREQUAL "Coverage") if(BUILD_EXAMPLES OR BUILD_HEADERS OR BUILD_BENCHMARKS) message(WARNING "Coverage builds don't work for anything but the tests") @@ -161,7 +169,11 @@ set(OSMIUM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") include_directories(${OSMIUM_INCLUDE_DIR}) -find_package(Osmium COMPONENTS io gdal geos proj sparsehash) +if(WITH_PROJ) + find_package(Osmium COMPONENTS lz4 io gdal geos proj) +else() + find_package(Osmium COMPONENTS lz4 io gdal geos) +endif() # The find_package put the directory where it found the libosmium includes # into OSMIUM_INCLUDE_DIRS. We remove it again, because we want to make @@ -200,11 +212,13 @@ endif() # #----------------------------------------------------------------------------- if(MSVC) - set(USUAL_COMPILE_OPTIONS "/Ox") + set(DEV_COMPILE_OPTIONS "/Ox") + set(RWD_COMPILE_OPTIONS "/Ox /DNDEBUG") # do not show warnings caused by missing .pdb files for libraries set(USUAL_LINK_OPTIONS "/debug /ignore:4099") else() - set(USUAL_COMPILE_OPTIONS "-O3 -g") + set(DEV_COMPILE_OPTIONS "-O3 -g") + set(RWD_COMPILE_OPTIONS "-O3 -g -DNDEBUG") set(USUAL_LINK_OPTIONS "") endif() @@ -212,7 +226,7 @@ if(WIN32) add_definitions(-DWIN32 -D_WIN32 -DMSWIN32 -DBGDWIN32) endif() -set(CMAKE_CXX_FLAGS_DEV "${USUAL_COMPILE_OPTIONS}" +set(CMAKE_CXX_FLAGS_DEV "${DEV_COMPILE_OPTIONS}" CACHE STRING "Flags used by the compiler during developer builds." FORCE) @@ -224,7 +238,7 @@ mark_as_advanced( CMAKE_EXE_LINKER_FLAGS_DEV ) -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${USUAL_COMPILE_OPTIONS}" +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${RWD_COMPILE_OPTIONS}" CACHE STRING "Flags used by the compiler during RELWITHDEBINFO builds." FORCE) @@ -318,9 +332,6 @@ if(CPPCHECK) --force -Uassert -DPROTOZERO_STRICT_API -DPROTOZERO_USE_BUILTIN_BSWAP -UPROTOZERO_USE_VIEW) - # cpp doesn't find system includes for some reason, suppress that report - set(CPPCHECK_OPTIONS ${CPPCHECK_OPTIONS} --suppress=missingIncludeSystem) - file(GLOB_RECURSE ALL_INCLUDES include/osmium/*.hpp) file(GLOB ALL_EXAMPLES examples/*.cpp) file(GLOB ALL_BENCHMARKS benchmarks/*.cpp) @@ -390,14 +401,14 @@ if(BUILD_HEADERS) file(MAKE_DIRECTORY header_check) foreach(hpp ${ALL_HPPS}) - if(GDAL_FOUND OR NOT ((hpp STREQUAL "osmium/area/problem_reporter_ogr.hpp") OR (hpp STREQUAL "osmium/geom/ogr.hpp"))) + if((GDAL_FOUND AND PROJ_FOUND) OR NOT ((hpp STREQUAL "osmium/area/problem_reporter_ogr.hpp") OR (hpp STREQUAL "osmium/geom/ogr.hpp") OR (hpp STREQUAL "osmium/geom/projection.hpp"))) string(REPLACE ".hpp" "" tmp ${hpp}) string(REPLACE "/" "__" libname ${tmp}) # Create a dummy .cpp file that includes the header file we want to # check. set(DUMMYCPP ${CMAKE_BINARY_DIR}/header_check/${libname}.cpp) - file(WRITE ${DUMMYCPP} "#include <${hpp}> // IWYU pragma: keep\n") + file(WRITE ${DUMMYCPP} "#define OSMIUM_UTIL_COMPATIBILITY_HPP\n#define OSMIUM_DEPRECATED\n#define OSMIUM_EXPORT\n#include <${hpp}> // IWYU pragma: keep\n") # There is no way in CMake to just compile but not link a C++ file, # so we pretend to build a library here. @@ -416,7 +427,7 @@ endif() # #----------------------------------------------------------------------------- message(STATUS "Looking for clang-tidy") -find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-6.0 clang-tidy-5.0) +find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-16 clang-tidy-15 clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11) if(CLANG_TIDY) message(STATUS "Looking for clang-tidy - found ${CLANG_TIDY}") @@ -459,6 +470,7 @@ if(CLANG_TIDY) add_custom_target(clang-tidy ${CLANG_TIDY} -p ${CMAKE_BINARY_DIR} + "-extra-arg=-Wno-#pragma-messages" ${CT_CHECK_FILES} ) else() @@ -480,11 +492,6 @@ if(INSTALL_GDALCPP) install(FILES include/gdalcpp.hpp DESTINATION include) endif() -if(INSTALL_UTFCPP) - install(FILES include/utf8.h DESTINATION include) - install(DIRECTORY include/utf8 DESTINATION include) -endif() - #----------------------------------------------------------------------------- # diff --git a/EXTERNAL_LICENSES.txt b/EXTERNAL_LICENSES.txt deleted file mode 100644 index 9484c0891..000000000 --- a/EXTERNAL_LICENSES.txt +++ /dev/null @@ -1,27 +0,0 @@ - -==== For utf8.h - -Copyright 2006 Nemanja Trifunovic - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - diff --git a/NOTES_FOR_DEVELOPERS.md b/NOTES_FOR_DEVELOPERS.md index 7ffca5ec5..f00f61b40 100644 --- a/NOTES_FOR_DEVELOPERS.md +++ b/NOTES_FOR_DEVELOPERS.md @@ -89,11 +89,11 @@ between compilers due to different C++11 support. ## Operating systems -Usually all code must work on Linux, OSX, and Windows. Execptions are allowed +Usually all code must work on Linux, macOS, and Windows. Execptions are allowed for some minor functionality, but please discuss this first. When writing code and tests, care must be taken that everything works with the -CR line ending convention used on Linux and OSX and the CRLF line ending used +CR line ending convention used on Linux and macOS and the CRLF line ending used on Windows. Note that `git` can be run with different settings regarding line ending rewritings on different machines making things more difficult. Some files have been "forced" to LF line endings using `.gitattributes` files. @@ -136,15 +136,7 @@ directory, some data tests in `test/osm-testdata` and tests of the examples in `test/examples`. They are built by the default cmake config. Run `ctest` to run them. We can always use more tests. -Tests are run automatically using the Travis (Linux/Mac) and Appveyor (Windows) -services. We automatically create coverage reports on Codevoc.io. Note that -the coverage percentages reported are not always accurate, because code that -is not used in tests at all will not necessarily end up in the binary and -the code coverage tool will not know it is there. - -[![Travis Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium) -[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium) -[![Coverage Status](https://codecov.io/gh/osmcode/libosmium/branch/master/graph/badge.svg)](https://codecov.io/gh/osmcode/libosmium) +Tests are run automatically using Github Actions. ## Documenting the code diff --git a/README.md b/README.md index 91d8f507c..4d73ed7a8 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,34 @@ # Libosmium -http://osmcode.org/libosmium +https://osmcode.org/libosmium A fast and flexible C++ library for working with OpenStreetMap data. -Libosmium works on Linux, Mac OSX and Windows. +Libosmium works on Linux, macOS and Windows. -[![Travis Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium) -[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium) -[![Coverage Status](https://codecov.io/gh/osmcode/libosmium/branch/master/graph/badge.svg)](https://codecov.io/gh/osmcode/libosmium) +[![Github Build Status](https://github.com/osmcode/libosmium/workflows/CI/badge.svg?branch=master)](https://github.com/osmcode/libosmium/actions) [![Packaging status](https://repology.org/badge/tiny-repos/libosmium.svg)](https://repology.org/metapackage/libosmium) -Please see the [Libosmium manual](http://osmcode.org/libosmium/manual.html) +Please see the [Libosmium manual](https://osmcode.org/libosmium/manual.html) for more details than this README can provide. ## Prerequisites -Because Libosmium uses many C++11 features you need a modern compiler and -standard C++ library. Osmium needs at least GCC 4.8 or clang (LLVM) 3.4. -(Some parts may work with older versions.) +You need a C++11 compiler and standard C++ library. Osmium needs at least GCC +4.8 or clang (LLVM) 3.4. (Some parts may work with older versions.) Different parts of Libosmium (and the applications built on top of it) need different libraries. You DO NOT NEED to install all of them, just install those you need for your programs. For details see the [list of -dependencies](http://osmcode.org/libosmium/manual.html#dependencies) in the +dependencies](https://osmcode.org/libosmium/manual.html#dependencies) in the manual. The following external (header-only) libraries are included in the libosmium repository: * [gdalcpp](https://github.com/joto/gdalcpp) -* [utfcpp](http://utfcpp.sourceforge.net/) Note that [protozero](https://github.com/mapbox/protozero) was included in earlier versions of libosmium, but isn't any more. @@ -69,8 +65,8 @@ cmake: This will build the examples and tests. Call `ctest` to run the tests. -For more detals see the -[Building Libosmium](http://osmcode.org/libosmium/manual.html#building-libosmium) +For more details see the +[Building Libosmium](https://osmcode.org/libosmium/manual.html#building-libosmium) chapter in the manual. @@ -85,20 +81,13 @@ git submodule update --init This will enable additional tests. See the -[Libosmium Manual](http://osmcode.org/libosmium/manual.html#running-tests) +[Libosmium Manual](https://osmcode.org/libosmium/manual.html#running-tests) for instructions. -## Switching from the old Osmium - -If you have been using the old version of Osmium at -https://github.com/joto/osmium you might want to read about the [changes -needed](http://osmcode.org/libosmium/manual.html#changes-from-old-versions-of-osmium). - - ## License -Libosmium is available under the Boost Software License. See LICENSE.txt. +Libosmium is available under the Boost Software License. See LICENSE. ## Authors diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 2ac3e2546..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,59 +0,0 @@ -#----------------------------------------------------------------------------- -# -# Configuration for continuous integration service at appveyor.com -# -#----------------------------------------------------------------------------- - -environment: - matrix: - - config: MSYS2 - autocrlf: true - - config: Debug - autocrlf: true - - config: Release - autocrlf: true - - config: Debug - autocrlf: false - - config: Release - autocrlf: false - -clone_depth: 1 - -# Operating system (build VM template) -os: Visual Studio 2015 - -platform: x64 - -# scripts that are called at very beginning, before repo cloning -init: - - git config --global core.autocrlf %autocrlf% - - git config --get core.autocrlf - -clone_folder: c:\projects\libosmium - -# The option --ask=20 is a workaround for a problem with the MSYS2 update -# process. Without it the following error is printed and the appveyor script -# halts: "msys2-runtime and catgets are in conflict. Remove catgets?" -# See also: https://github.com/Alexpux/MSYS2-packages/issues/1141 -install: - - git submodule update --init - - cd c:\projects - - git clone --depth 1 https://github.com/mapbox/protozero - - if [%config%]==[MSYS2] ( - C:\msys64\usr\bin\pacman --noconfirm --sync --refresh --refresh --sysupgrade --sysupgrade --ask=20 - && C:\msys64\usr\bin\pacman -Rc --noconfirm mingw-w64-x86_64-gcc-libs - ) - -build_script: - - cd c:\projects\libosmium - - if [%config%]==[MSYS2] ( - build-msys2.bat - ) else ( - build-appveyor.bat - ) - -# remove garbage VS messages -# https://help.appveyor.com/discussions/problems/4569-the-target-_convertpdbfiles-listed-in-a-beforetargets-attribute-at-c-does-not-exist-in-the-project-and-will-be-ignored -before_build: - - del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets" - diff --git a/benchmarks/osmium_benchmark_count.cpp b/benchmarks/osmium_benchmark_count.cpp index 65051b0f9..14aab4342 100644 --- a/benchmarks/osmium_benchmark_count.cpp +++ b/benchmarks/osmium_benchmark_count.cpp @@ -36,19 +36,26 @@ struct CountHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - std::exit(1); + return 1; } - const std::string input_filename{argv[1]}; + try { + const std::string input_filename{argv[1]}; - osmium::io::Reader reader{input_filename}; + osmium::io::Reader reader{input_filename}; - CountHandler handler; - osmium::apply(reader, handler); - reader.close(); + CountHandler handler; + osmium::apply(reader, handler); + reader.close(); - std::cout << "Nodes: " << handler.nodes << '\n'; - std::cout << "Ways: " << handler.ways << '\n'; - std::cout << "Relations: " << handler.relations << '\n'; + std::cout << "Nodes: " << handler.nodes << '\n'; + std::cout << "Ways: " << handler.ways << '\n'; + std::cout << "Relations: " << handler.relations << '\n'; + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + return 1; + } + + return 0; } diff --git a/benchmarks/osmium_benchmark_count_tag.cpp b/benchmarks/osmium_benchmark_count_tag.cpp index 12d5c8201..e3d303ce9 100644 --- a/benchmarks/osmium_benchmark_count_tag.cpp +++ b/benchmarks/osmium_benchmark_count_tag.cpp @@ -39,17 +39,24 @@ struct CountHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - std::exit(1); + return 1; } - const std::string input_filename{argv[1]}; + try { + const std::string input_filename{argv[1]}; - osmium::io::Reader reader{input_filename}; + osmium::io::Reader reader{input_filename}; - CountHandler handler; - osmium::apply(reader, handler); - reader.close(); + CountHandler handler; + osmium::apply(reader, handler); + reader.close(); - std::cout << "r_all=" << handler.all << " r_counter=" << handler.counter << '\n'; + std::cout << "r_all=" << handler.all << " r_counter=" << handler.counter << '\n'; + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + return 1; + } + + return 0; } diff --git a/benchmarks/osmium_benchmark_index_map.cpp b/benchmarks/osmium_benchmark_index_map.cpp index e89898246..70fe210da 100644 --- a/benchmarks/osmium_benchmark_index_map.cpp +++ b/benchmarks/osmium_benchmark_index_map.cpp @@ -21,20 +21,27 @@ using location_handler_type = osmium::handler::NodeLocationsForWays; int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " OSMFILE FORMAT\n"; - std::exit(1); + return 1; } - const std::string input_filename{argv[1]}; - const std::string location_store{argv[2]}; + try { + const std::string input_filename{argv[1]}; + const std::string location_store{argv[2]}; - osmium::io::Reader reader{input_filename}; + osmium::io::Reader reader{input_filename}; - const auto& map_factory = osmium::index::MapFactory::instance(); - std::unique_ptr index = map_factory.create_map(location_store); - location_handler_type location_handler{*index}; - location_handler.ignore_errors(); + const auto& map_factory = osmium::index::MapFactory::instance(); + std::unique_ptr index = map_factory.create_map(location_store); + location_handler_type location_handler{*index}; + location_handler.ignore_errors(); - osmium::apply(reader, location_handler); - reader.close(); + osmium::apply(reader, location_handler); + reader.close(); + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + return 1; + } + + return 0; } diff --git a/benchmarks/osmium_benchmark_mercator.cpp b/benchmarks/osmium_benchmark_mercator.cpp index f9672c305..153b047e0 100644 --- a/benchmarks/osmium_benchmark_mercator.cpp +++ b/benchmarks/osmium_benchmark_mercator.cpp @@ -28,15 +28,22 @@ struct GeomHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - std::exit(1); + return 1; } - const std::string input_filename{argv[1]}; + try { + const std::string input_filename{argv[1]}; - osmium::io::Reader reader{input_filename}; + osmium::io::Reader reader{input_filename}; - GeomHandler handler; - osmium::apply(reader, handler); - reader.close(); + GeomHandler handler; + osmium::apply(reader, handler); + reader.close(); + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + return 1; + } + + return 0; } diff --git a/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp b/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp index 1a689b187..e8d383867 100644 --- a/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp +++ b/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp @@ -41,104 +41,111 @@ using dynamic_location_handler_type = osmium::handler::NodeLocationsForWays::instance(); + const auto& map_factory = osmium::index::MapFactory::instance(); - const auto buffer_size = buffer.committed() / (1024 * 1024); // buffer size in MBytes - const int runs = std::max(10, static_cast(5000ull / buffer_size)); + const auto buffer_size = buffer.committed() / (1024UL * 1024UL); // buffer size in MBytes + const int runs = std::max(10, static_cast(5000ULL / buffer_size)); - std::cout << "input: filename=" << input_filename << " buffer_size=" << buffer_size << "MBytes\n"; - std::cout << "runs: " << runs << "\n"; + std::cout << "input: filename=" << input_filename << " buffer_size=" << buffer_size << "MBytes\n"; + std::cout << "runs: " << runs << "\n"; - double static_min = std::numeric_limits::max(); - double static_sum = 0; - double static_max = 0; + double static_min = std::numeric_limits::max(); + double static_sum = 0; + double static_max = 0; - double dynamic_min = std::numeric_limits::max(); - double dynamic_sum = 0; - double dynamic_max = 0; + double dynamic_min = std::numeric_limits::max(); + double dynamic_sum = 0; + double dynamic_max = 0; - for (int i = 0; i < runs; ++i) { + for (int i = 0; i < runs; ++i) { - { - // static index - osmium::memory::Buffer tmp_buffer{buffer.committed()}; - for (const auto& item : buffer) { - tmp_buffer.add_item(item); - tmp_buffer.commit(); + { + // static index + osmium::memory::Buffer tmp_buffer{buffer.committed()}; + for (const auto& item : buffer) { + tmp_buffer.add_item(item); + tmp_buffer.commit(); + } + + static_index_type static_index; + static_location_handler_type static_location_handler{static_index}; + + const auto start = std::chrono::steady_clock::now(); + osmium::apply(tmp_buffer, static_location_handler); + const auto end = std::chrono::steady_clock::now(); + + const double duration = std::chrono::duration(end - start).count(); + + if (duration < static_min) { + static_min = duration; + } + if (duration > static_max) { + static_max = duration; + } + static_sum += duration; } - static_index_type static_index; - static_location_handler_type static_location_handler{static_index}; + { + // dynamic index + osmium::memory::Buffer tmp_buffer{buffer.committed()}; + for (const auto& item : buffer) { + tmp_buffer.add_item(item); + tmp_buffer.commit(); + } - const auto start = std::chrono::steady_clock::now(); - osmium::apply(tmp_buffer, static_location_handler); - const auto end = std::chrono::steady_clock::now(); + std::unique_ptr index = map_factory.create_map(location_store); + dynamic_location_handler_type dynamic_location_handler{*index}; + dynamic_location_handler.ignore_errors(); - const double duration = std::chrono::duration(end - start).count(); + const auto start = std::chrono::steady_clock::now(); + osmium::apply(tmp_buffer, dynamic_location_handler); + const auto end = std::chrono::steady_clock::now(); - if (duration < static_min) { - static_min = duration; + const double duration = std::chrono::duration(end - start).count(); + + if (duration < dynamic_min) { + dynamic_min = duration; + } + if (duration > dynamic_max) { + dynamic_max = duration; + } + dynamic_sum += duration; } - if (duration > static_max) { - static_max = duration; - } - static_sum += duration; } - { - // dynamic index - osmium::memory::Buffer tmp_buffer{buffer.committed()}; - for (const auto& item : buffer) { - tmp_buffer.add_item(item); - tmp_buffer.commit(); - } + const double static_avg = static_sum / runs; + const double dynamic_avg = dynamic_sum / runs; - std::unique_ptr index = map_factory.create_map(location_store); - dynamic_location_handler_type dynamic_location_handler{*index}; - dynamic_location_handler.ignore_errors(); + std::cout << "static min=" << static_min << "ms avg=" << static_avg << "ms max=" << static_max << "ms\n"; + std::cout << "dynamic min=" << dynamic_min << "ms avg=" << dynamic_avg << "ms max=" << dynamic_max << "ms\n"; - const auto start = std::chrono::steady_clock::now(); - osmium::apply(tmp_buffer, dynamic_location_handler); - const auto end = std::chrono::steady_clock::now(); + const double rfactor = 100.0; + const double diff_min = std::round((dynamic_min - static_min) * rfactor) / rfactor; + const double diff_avg = std::round((dynamic_avg - static_avg) * rfactor) / rfactor; + const double diff_max = std::round((dynamic_max - static_max) * rfactor) / rfactor; - const double duration = std::chrono::duration(end - start).count(); + const double prfactor = 10.0; + const double percent_min = std::round((100.0 * diff_min / static_min) * prfactor) / prfactor; + const double percent_avg = std::round((100.0 * diff_avg / static_avg) * prfactor) / prfactor; + const double percent_max = std::round((100.0 * diff_max / static_max) * prfactor) / prfactor; - if (duration < dynamic_min) { - dynamic_min = duration; - } - if (duration > dynamic_max) { - dynamic_max = duration; - } - dynamic_sum += duration; - } + std::cout << "difference:"; + std::cout << " min=" << diff_min << "ms (" << percent_min << "%)"; + std::cout << " avg=" << diff_avg << "ms (" << percent_avg << "%)"; + std::cout << " max=" << diff_max << "ms (" << percent_max << "%)\n"; + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + return 1; } - const double static_avg = static_sum / runs; - const double dynamic_avg = dynamic_sum / runs; - - std::cout << "static min=" << static_min << "ms avg=" << static_avg << "ms max=" << static_max << "ms\n"; - std::cout << "dynamic min=" << dynamic_min << "ms avg=" << dynamic_avg << "ms max=" << dynamic_max << "ms\n"; - - const double rfactor = 100.0; - const double diff_min = std::round((dynamic_min - static_min) * rfactor) / rfactor; - const double diff_avg = std::round((dynamic_avg - static_avg) * rfactor) / rfactor; - const double diff_max = std::round((dynamic_max - static_max) * rfactor) / rfactor; - - const double prfactor = 10.0; - const double percent_min = std::round((100.0 * diff_min / static_min) * prfactor) / prfactor; - const double percent_avg = std::round((100.0 * diff_avg / static_avg) * prfactor) / prfactor; - const double percent_max = std::round((100.0 * diff_max / static_max) * prfactor) / prfactor; - - std::cout << "difference:"; - std::cout << " min=" << diff_min << "ms (" << percent_min << "%)"; - std::cout << " avg=" << diff_avg << "ms (" << percent_avg << "%)"; - std::cout << " max=" << diff_max << "ms (" << percent_max << "%)\n"; + return 0; } diff --git a/benchmarks/osmium_benchmark_write_pbf.cpp b/benchmarks/osmium_benchmark_write_pbf.cpp index 632dd26eb..e4542140c 100644 --- a/benchmarks/osmium_benchmark_write_pbf.cpp +++ b/benchmarks/osmium_benchmark_write_pbf.cpp @@ -14,22 +14,29 @@ int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " INPUT-FILE OUTPUT-FILE\n"; - std::exit(1); + return 1; } - std::string input_filename{argv[1]}; - std::string output_filename{argv[2]}; + try { + const std::string input_filename{argv[1]}; + const std::string output_filename{argv[2]}; - osmium::io::Reader reader{input_filename}; - osmium::io::File output_file{output_filename, "pbf"}; - osmium::io::Header header; - osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow}; + osmium::io::Reader reader{input_filename}; + const osmium::io::File output_file{output_filename, "pbf"}; + const osmium::io::Header header; + osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow}; - while (osmium::memory::Buffer buffer = reader.read()) { - writer(std::move(buffer)); + while (osmium::memory::Buffer buffer = reader.read()) { // NOLINT(bugprone-use-after-move) Bug in clang-tidy https://bugs.llvm.org/show_bug.cgi?id=36516 + writer(std::move(buffer)); + } + + writer.close(); + reader.close(); + } catch (const std::exception& e) { + std::cerr << e.what() << '\n'; + return 1; } - writer.close(); - reader.close(); + return 0; } diff --git a/build-appveyor.bat b/build-appveyor.bat index 74ea0c91a..5d0064b40 100644 --- a/build-appveyor.bat +++ b/build-appveyor.bat @@ -24,6 +24,11 @@ IF %ERRORLEVEL% NEQ 0 GOTO ERROR SET PATH=C:/projects/bzip2.v140.1.0.6.9/build/native/bin/x64/%config%;%PATH% +nuget install lz4 -Version 1.3.1.2 +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +SET PATH=C:/projects/lz4.1.3.1.2/build/native/bin/x64/%config%;%PATH% + CD libosmium ECHO config^: %config% @@ -46,13 +51,15 @@ SET CMAKE_CMD=cmake .. -LA -G "Visual Studio 14 Win64" ^ -DOsmium_DEBUG=TRUE ^ -DCMAKE_BUILD_TYPE=%config% ^ -DBUILD_HEADERS=OFF ^ --DBOOST_ROOT=C:/Libraries/boost_1_63_0 ^ +-DBOOST_ROOT=C:/Libraries/boost_1_67_0 ^ -DZLIB_INCLUDE_DIR=C:/projects/zlib-vc140-static-64.1.2.11/lib/native/include ^ -DZLIB_LIBRARY=C:/projects/zlib-vc140-static-64.1.2.11/lib/native/libs/x64/static/%config%/zlibstatic.lib ^ -DEXPAT_INCLUDE_DIR=C:/projects/expat.v140.2.2.5/build/native/include ^ -DEXPAT_LIBRARY=C:/projects/expat.v140.2.2.5/build/native/lib/x64/%config%/libexpat%libpostfix%.lib ^ -DBZIP2_INCLUDE_DIR=C:/projects/bzip2.v140.1.0.6.9/build/native/include ^ --DBZIP2_LIBRARIES=C:/projects/bzip2.v140.1.0.6.9/build/native/lib/x64/%config%/libbz2%libpostfix%.lib +-DBZIP2_LIBRARIES=C:/projects/bzip2.v140.1.0.6.9/build/native/lib/x64/%config%/libbz2%libpostfix%.lib ^ +-DLZ4_INCLUDE_DIR=C:/projects/lz4.1.3.1.2/build/native/include ^ +-DLZ4_LIBRARY=C:/projects/lz4.1.3.1.2/build/native/lib/x64/%config%/liblz4%libpostfix%.lib ECHO calling^: %CMAKE_CMD% %CMAKE_CMD% @@ -68,7 +75,7 @@ msbuild libosmium.sln ^ /p:PlatformToolset=v140 %avlogger% IF %ERRORLEVEL% NEQ 0 GOTO ERROR -ctest --output-on-failure -C %config% -E testdata-overview +ctest --output-on-failure -C %config% IF %ERRORLEVEL% NEQ 0 GOTO ERROR GOTO DONE diff --git a/build-msys2.bat b/build-msys2.bat index a4f0f7a1c..9c00a3573 100644 --- a/build-msys2.bat +++ b/build-msys2.bat @@ -9,12 +9,18 @@ SET "PATH=C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%" echo %PATH% echo "Installing MSYS2 packages..." -bash -lc "pacman -S --needed --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-geos mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-cppcheck mingw-w64-x86_64-doxygen mingw-w64-x86_64-gdb mingw-w64-x86_64-sparsehash mingw-w64-x86_64-gdal mingw-w64-x86_64-ruby mingw-w64-x86_64-libspatialite mingw-w64-x86_64-spatialite-tools mingw-w64-x86_64-clang-tools-extra" -bash -lc "pacman -S --needed --noconfirm mingw-w64-x86_64-postgresql mingw-w64-x86_64-netcdf mingw-w64-x86_64-crypto++" +bash -lc "pacman -Sy --needed --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-geos mingw-w64-x86_64-cmake mingw-w64-x86_64-boost mingw-w64-x86_64-cppcheck mingw-w64-x86_64-doxygen mingw-w64-x86_64-gdb mingw-w64-x86_64-gdal mingw-w64-x86_64-ruby mingw-w64-x86_64-libspatialite mingw-w64-x86_64-spatialite-tools mingw-w64-x86_64-clang-tools-extra" +bash -lc "pacman -Sy --needed --noconfirm mingw-w64-x86_64-postgresql mingw-w64-x86_64-netcdf mingw-w64-x86_64-crypto++" call C:\msys64\mingw64\bin\gem.cmd install json -echo "Setting PROJ_LIB variable for correct PROJ.4 working" +REM Workaround for problem with spatialite (see https://github.com/osmcode/libosmium/issues/262) +copy /y C:\msys64\mingw64\bin\libreadline8.dll C:\msys64\mingw64\bin\libreadline7.dll + +echo "Setting PROJ_LIB/DATA variable for correct PROJ.4 working" set PROJ_LIB=c:\msys64\mingw64\share\proj +set PROJ_DATA=c:\msys64\mingw64\share\proj + +set CXXFLAGS=-Wno-stringop-overflow echo "Generating makefiles" mkdir build diff --git a/cmake/FindGem.cmake b/cmake/FindGem.cmake index 5d78a9026..9927ea814 100644 --- a/cmake/FindGem.cmake +++ b/cmake/FindGem.cmake @@ -149,7 +149,7 @@ if(DEFINED GEM_INCLUDE_DIRS) LIST(REMOVE_DUPLICATES GEM_INCLUDE_DIRS) endif() -find_package_handle_standard_args(GEM +find_package_handle_standard_args(Gem REQUIRED_VARS ${components_found_vars} FAIL_MESSAGE "Could not find all required gems") diff --git a/cmake/FindLZ4.cmake b/cmake/FindLZ4.cmake new file mode 100644 index 000000000..8c94e3bcd --- /dev/null +++ b/cmake/FindLZ4.cmake @@ -0,0 +1,38 @@ +find_path(LZ4_INCLUDE_DIR + NAMES lz4.h + DOC "lz4 include directory") +mark_as_advanced(LZ4_INCLUDE_DIR) +find_library(LZ4_LIBRARY + NAMES lz4 liblz4 + DOC "lz4 library") +mark_as_advanced(LZ4_LIBRARY) + +if (LZ4_INCLUDE_DIR) + file(STRINGS "${LZ4_INCLUDE_DIR}/lz4.h" _lz4_version_lines + REGEX "#define[ \t]+LZ4_VERSION_(MAJOR|MINOR|RELEASE)") + string(REGEX REPLACE ".*LZ4_VERSION_MAJOR *\([0-9]*\).*" "\\1" _lz4_version_major "${_lz4_version_lines}") + string(REGEX REPLACE ".*LZ4_VERSION_MINOR *\([0-9]*\).*" "\\1" _lz4_version_minor "${_lz4_version_lines}") + string(REGEX REPLACE ".*LZ4_VERSION_RELEASE *\([0-9]*\).*" "\\1" _lz4_version_release "${_lz4_version_lines}") + set(LZ4_VERSION "${_lz4_version_major}.${_lz4_version_minor}.${_lz4_version_release}") + unset(_lz4_version_major) + unset(_lz4_version_minor) + unset(_lz4_version_release) + unset(_lz4_version_lines) +endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LZ4 + REQUIRED_VARS LZ4_LIBRARY LZ4_INCLUDE_DIR + VERSION_VAR LZ4_VERSION) + +if (LZ4_FOUND) + set(LZ4_INCLUDE_DIRS "${LZ4_INCLUDE_DIR}") + set(LZ4_LIBRARIES "${LZ4_LIBRARY}") + + if (NOT TARGET LZ4::LZ4) + add_library(LZ4::LZ4 UNKNOWN IMPORTED) + set_target_properties(LZ4::LZ4 PROPERTIES + IMPORTED_LOCATION "${LZ4_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LZ4_INCLUDE_DIR}") + endif () +endif () diff --git a/cmake/FindOsmium.cmake b/cmake/FindOsmium.cmake index 9eaf7349e..651c09a72 100644 --- a/cmake/FindOsmium.cmake +++ b/cmake/FindOsmium.cmake @@ -33,7 +33,8 @@ # geos - include if you want to use any of the GEOS functions # gdal - include if you want to use any of the OGR functions # proj - include if you want to use any of the Proj.4 functions -# sparsehash - include if you use the sparsehash index +# sparsehash - include if you use the sparsehash index (deprecated!) +# lz4 - include support for LZ4 compression of PBF files # # You can check for success with something like this: # @@ -71,6 +72,9 @@ find_path(OSMIUM_INCLUDE_DIR osmium/version.hpp # Check libosmium version number if(Osmium_FIND_VERSION) + if(NOT EXISTS "${OSMIUM_INCLUDE_DIR}/osmium/version.hpp") + message(FATAL_ERROR "Missing ${OSMIUM_INCLUDE_DIR}/osmium/version.hpp. Either your libosmium version is too old, or libosmium wasn't found in the place you said.") + endif() file(STRINGS "${OSMIUM_INCLUDE_DIR}/osmium/version.hpp" _libosmium_version_define REGEX "#define LIBOSMIUM_VERSION_STRING") if("${_libosmium_version_define}" MATCHES "#define LIBOSMIUM_VERSION_STRING \"([0-9.]+)\"") set(_libosmium_version "${CMAKE_MATCH_1}") @@ -111,16 +115,23 @@ endif() if(Osmium_USE_PBF) find_package(ZLIB) find_package(Threads) - find_package(Protozero 1.5.1) + find_package(Protozero 1.6.3) + + if(Osmium_USE_LZ4) + find_package(LZ4 REQUIRED) + add_definitions(-DOSMIUM_WITH_LZ4) + endif() list(APPEND OSMIUM_EXTRA_FIND_VARS ZLIB_FOUND Threads_FOUND PROTOZERO_INCLUDE_DIR) if(ZLIB_FOUND AND Threads_FOUND AND PROTOZERO_FOUND) list(APPEND OSMIUM_PBF_LIBRARIES ${ZLIB_LIBRARIES} + ${LZ4_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) list(APPEND OSMIUM_INCLUDE_DIRS ${ZLIB_INCLUDE_DIR} + ${LZ4_INCLUDE_DIRS} ${PROTOZERO_INCLUDE_DIR} ) else() @@ -213,6 +224,7 @@ endif() #---------------------------------------------------------------------- # Component 'sparsehash' if(Osmium_USE_SPARSEHASH) + message(WARNING "Osmium: Use of Google SparseHash is deprecated. Please switch to a different index type.") find_path(SPARSEHASH_INCLUDE_DIR google/sparsetable) list(APPEND OSMIUM_EXTRA_FIND_VARS SPARSEHASH_INCLUDE_DIR) @@ -324,7 +336,7 @@ if(MSVC) add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_WARNINGS) endif() -if(APPLE) +if(APPLE AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # following only available from cmake 2.8.12: # add_compile_options(-stdlib=libc++) # so using this instead: diff --git a/cmake/build.bat b/cmake/build.bat deleted file mode 100644 index 5ffab124e..000000000 --- a/cmake/build.bat +++ /dev/null @@ -1,15 +0,0 @@ -call "%VS120COMNTOOLS%\..\..\VC\vcvarsall.bat" x86_amd64 -set CMAKE_PREFIX_PATH=C:\PROJ -set VERSION=Debug -set TESTS=ON -set ALLHPPS=ON -set PREFIX=d:\libs18d -set BOOST_ROOT=d:\boost - -cmake .. -G "Visual Studio 12 Win64" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=%PREFIX% -DBOOST_ROOT=%BOOST_ROOT% -DBoost_USE_STATIC_LIBS=ON -DBUILD_TESTING=%TESTS% -DBUILD_TRY_HPPS=%ALLHPPS$ -T CTP_Nov2013 -msbuild /clp:Verbosity=minimal /nologo libosmium.sln /flp1:logfile=build_errors.txt;errorsonly /flp2:logfile=build_warnings.txt;warningsonly -set PATH=%PATH%;%PREFIX%/bin - -del test\osm-testdata\*.db -del test\osm-testdata\*.json -if "%TESTS%"=="ON" ctest -VV >build_tests.log diff --git a/doc/doc.md b/doc/doc.md index 309f23f9c..db9b43cc9 100644 --- a/doc/doc.md +++ b/doc/doc.md @@ -4,7 +4,7 @@ data. This is the API documentation that was automatically created from the source code. For more information about the Osmium Library see -http://osmcode.org/libosmium . +https://osmcode.org/libosmium . Osmium is free software and available under the Boost Software License. The source code is available at https://github.com/osmcode/libosmium . diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c1498cc35..38a00d61c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -25,6 +25,7 @@ set(EXAMPLES read read_with_progress road_length + tags_filter tiles CACHE STRING "Example programs" ) diff --git a/examples/README.md b/examples/README.md index 282d03d44..16b78b8a1 100644 --- a/examples/README.md +++ b/examples/README.md @@ -29,6 +29,7 @@ them. * `osmium_area_test` * `osmium_create_pois` +* `osmium_tags_filter` ## Even more advanced examples diff --git a/examples/osmium_amenity_list.cpp b/examples/osmium_amenity_list.cpp index 7e8d79a9e..3103d0679 100644 --- a/examples/osmium_amenity_list.cpp +++ b/examples/osmium_amenity_list.cpp @@ -25,7 +25,6 @@ */ #include // for std::printf -#include // for std::exit #include // for std::cerr #include // for std::string @@ -58,12 +57,12 @@ using location_handler_type = osmium::handler::NodeLocationsForWays; class AmenityHandler : public osmium::handler::Handler { // Print info about one amenity to stdout. - void print_amenity(const char* type, const char* name, const osmium::geom::Coordinates& c) { + static void print_amenity(const char* type, const char* name, const osmium::geom::Coordinates& c) { std::printf("%8.4f,%8.4f %-15s %s\n", c.x, c.y, type, name ? name : ""); } // Calculate the center point of a NodeRefList. - osmium::geom::Coordinates calc_center(const osmium::NodeRefList& nr_list) { + static osmium::geom::Coordinates calc_center(const osmium::NodeRefList& nr_list) { // Coordinates simply store an X and Y coordinate pair as doubles. // (Unlike osmium::Location which stores them more efficiently as // 32 bit integers.) Use Coordinates when you want to do calculations @@ -75,15 +74,17 @@ class AmenityHandler : public osmium::handler::Handler { c.y += nr.lat(); } - c.x /= nr_list.size(); - c.y /= nr_list.size(); + c.x /= static_cast(nr_list.size()); + c.y /= static_cast(nr_list.size()); return c; } public: - void node(const osmium::Node& node) { + // The callback functions can be either static or not depending on whether + // you need to access any member variables of the handler. + static void node(const osmium::Node& node) { // Getting a tag value can be expensive, because a list of tags has // to be gone through and each tag has to be checked. So we store the // result and reuse it. @@ -93,7 +94,9 @@ public: } } - void area(const osmium::Area& area) { + // The callback functions can be either static or not depending on whether + // you need to access any member variables of the handler. + static void area(const osmium::Area& area) { const char* amenity = area.tags()["amenity"]; if (amenity) { // Use the center of the first outer ring. Because we set @@ -110,62 +113,68 @@ public: int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - std::exit(1); + return 1; } - // The input file - const osmium::io::File input_file{argv[1]}; + try { + // The input file + const osmium::io::File input_file{argv[1]}; - // Configuration for the multipolygon assembler. We disable the option to - // create empty areas when invalid multipolygons are encountered. This - // means areas created have a valid geometry and invalid multipolygons - // are simply ignored. - osmium::area::Assembler::config_type assembler_config; - assembler_config.create_empty_areas = false; + // Configuration for the multipolygon assembler. We disable the option to + // create empty areas when invalid multipolygons are encountered. This + // means areas created have a valid geometry and invalid multipolygons + // are simply ignored. + osmium::area::Assembler::config_type assembler_config; + assembler_config.create_empty_areas = false; - // Initialize the MultipolygonManager. Its job is to collect all - // relations and member ways needed for each area. It then calls an - // instance of the osmium::area::Assembler class (with the given config) - // to actually assemble one area. - osmium::area::MultipolygonManager mp_manager{assembler_config}; + // Initialize the MultipolygonManager. Its job is to collect all + // relations and member ways needed for each area. It then calls an + // instance of the osmium::area::Assembler class (with the given config) + // to actually assemble one area. + osmium::area::MultipolygonManager mp_manager{assembler_config}; - // We read the input file twice. In the first pass, only relations are - // read and fed into the multipolygon manager. - std::cerr << "Pass 1...\n"; - osmium::relations::read_relations(input_file, mp_manager); - std::cerr << "Pass 1 done\n"; + // We read the input file twice. In the first pass, only relations are + // read and fed into the multipolygon manager. + std::cerr << "Pass 1...\n"; + osmium::relations::read_relations(input_file, mp_manager); + std::cerr << "Pass 1 done\n"; - // The index storing all node locations. - index_type index; + // The index storing all node locations. + index_type index; - // The handler that stores all node locations in the index and adds them - // to the ways. - location_handler_type location_handler{index}; + // The handler that stores all node locations in the index and adds them + // to the ways. + location_handler_type location_handler{index}; - // If a location is not available in the index, we ignore it. It might - // not be needed (if it is not part of a multipolygon relation), so why - // create an error? - location_handler.ignore_errors(); + // If a location is not available in the index, we ignore it. It might + // not be needed (if it is not part of a multipolygon relation), so why + // create an error? + location_handler.ignore_errors(); - // Create our handler. - AmenityHandler data_handler; + // Create our handler. + AmenityHandler data_handler; - // On the second pass we read all objects and run them first through the - // node location handler and then the multipolygon manager. The manager - // will put the areas it has created into the "buffer" which are then - // fed through our handler. - // - // The read_meta::no option disables reading of meta data (such as version - // numbers, timestamps, etc.) which are not needed in this case. Disabling - // this can speed up your program. - std::cerr << "Pass 2...\n"; - osmium::io::Reader reader{input_file, osmium::io::read_meta::no}; + // On the second pass we read all objects and run them first through the + // node location handler and then the multipolygon manager. The manager + // will put the areas it has created into the "buffer" which are then + // fed through our handler. + // + // The read_meta::no option disables reading of meta data (such as version + // numbers, timestamps, etc.) which are not needed in this case. Disabling + // this can speed up your program. + std::cerr << "Pass 2...\n"; + osmium::io::Reader reader{input_file, osmium::io::read_meta::no}; - osmium::apply(reader, location_handler, data_handler, mp_manager.handler([&data_handler](const osmium::memory::Buffer& area_buffer) { - osmium::apply(area_buffer, data_handler); - })); + osmium::apply(reader, location_handler, data_handler, mp_manager.handler([&data_handler](const osmium::memory::Buffer& area_buffer) { + osmium::apply(area_buffer, data_handler); + })); - reader.close(); - std::cerr << "Pass 2 done\n"; + reader.close(); + std::cerr << "Pass 2 done\n"; + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; + } } diff --git a/examples/osmium_area_test.cpp b/examples/osmium_area_test.cpp index 5a29472e5..ac24ac810 100644 --- a/examples/osmium_area_test.cpp +++ b/examples/osmium_area_test.cpp @@ -25,7 +25,6 @@ */ -#include // for std::exit #include // for std::strcmp #include // for std::cout, std::cerr @@ -93,106 +92,113 @@ void print_help() { void print_usage(const char* prgname) { std::cerr << "Usage: " << prgname << " [OPTIONS] OSMFILE\n"; - std::exit(1); } int main(int argc, char* argv[]) { if (argc > 1 && (!std::strcmp(argv[1], "-h") || !std::strcmp(argv[1], "--help"))) { print_help(); - std::exit(0); + return 0; } if (argc != 3) { print_usage(argv[0]); + return 1; } - // Initialize an empty DynamicHandler. Later it will be associated - // with one of the handlers. You can think of the DynamicHandler as - // a kind of "variant handler" or a "pointer handler" pointing to the - // real handler. - osmium::handler::DynamicHandler handler; + try { + // Initialize an empty DynamicHandler. Later it will be associated + // with one of the handlers. You can think of the DynamicHandler as + // a kind of "variant handler" or a "pointer handler" pointing to the + // real handler. + osmium::handler::DynamicHandler handler; - if (!std::strcmp(argv[1], "-w") || !std::strcmp(argv[1], "--dump-wkt")) { - handler.set(); - } else if (!std::strcmp(argv[1], "-o") || !std::strcmp(argv[1], "--dump-objects")) { - handler.set(std::cout); - } else { - print_usage(argv[0]); - } - - osmium::io::File input_file{argv[2]}; - - // Configuration for the multipolygon assembler. Here the default settings - // are used, but you could change multiple settings. - osmium::area::Assembler::config_type assembler_config; - - // Set up a filter matching only forests. This will be used to only build - // areas with matching tags. - osmium::TagsFilter filter{false}; - filter.add_rule(true, "landuse", "forest"); - filter.add_rule(true, "natural", "wood"); - - // Initialize the MultipolygonManager. Its job is to collect all - // relations and member ways needed for each area. It then calls an - // instance of the osmium::area::Assembler class (with the given config) - // to actually assemble one area. The filter parameter is optional, if - // it is not set, all areas will be built. - osmium::area::MultipolygonManager mp_manager{assembler_config, filter}; - - // We read the input file twice. In the first pass, only relations are - // read and fed into the multipolygon manager. - std::cerr << "Pass 1...\n"; - osmium::relations::read_relations(input_file, mp_manager); - std::cerr << "Pass 1 done\n"; - - // Output the amount of main memory used so far. All multipolygon relations - // are in memory now. - std::cerr << "Memory:\n"; - osmium::relations::print_used_memory(std::cerr, mp_manager.used_memory()); - - // The index storing all node locations. - index_type index; - - // The handler that stores all node locations in the index and adds them - // to the ways. - location_handler_type location_handler{index}; - - // If a location is not available in the index, we ignore it. It might - // not be needed (if it is not part of a multipolygon relation), so why - // create an error? - location_handler.ignore_errors(); - - // On the second pass we read all objects and run them first through the - // node location handler and then the multipolygon collector. The collector - // will put the areas it has created into the "buffer" which are then - // fed through our "handler". - std::cerr << "Pass 2...\n"; - osmium::io::Reader reader{input_file}; - osmium::apply(reader, location_handler, mp_manager.handler([&handler](osmium::memory::Buffer&& buffer) { - osmium::apply(buffer, handler); - })); - reader.close(); - std::cerr << "Pass 2 done\n"; - - // Output the amount of main memory used so far. All complete multipolygon - // relations have been cleaned up. - std::cerr << "Memory:\n"; - osmium::relations::print_used_memory(std::cerr, mp_manager.used_memory()); - - // If there were multipolgyon relations in the input, but some of their - // members are not in the input file (which often happens for extracts) - // this will write the IDs of the incomplete relations to stderr. - std::vector incomplete_relations_ids; - mp_manager.for_each_incomplete_relation([&](const osmium::relations::RelationHandle& handle){ - incomplete_relations_ids.push_back(handle->id()); - }); - if (!incomplete_relations_ids.empty()) { - std::cerr << "Warning! Some member ways missing for these multipolygon relations:"; - for (const auto id : incomplete_relations_ids) { - std::cerr << " " << id; + if (!std::strcmp(argv[1], "-w") || !std::strcmp(argv[1], "--dump-wkt")) { + handler.set(); + } else if (!std::strcmp(argv[1], "-o") || !std::strcmp(argv[1], "--dump-objects")) { + handler.set(std::cout); + } else { + print_usage(argv[0]); + return 1; } - std::cerr << "\n"; + + const osmium::io::File input_file{argv[2]}; + + // Configuration for the multipolygon assembler. Here the default settings + // are used, but you could change multiple settings. + const osmium::area::Assembler::config_type assembler_config; + + // Set up a filter matching only forests. This will be used to only build + // areas with matching tags. + osmium::TagsFilter filter{false}; + filter.add_rule(true, "landuse", "forest"); + filter.add_rule(true, "natural", "wood"); + + // Initialize the MultipolygonManager. Its job is to collect all + // relations and member ways needed for each area. It then calls an + // instance of the osmium::area::Assembler class (with the given config) + // to actually assemble one area. The filter parameter is optional, if + // it is not set, all areas will be built. + osmium::area::MultipolygonManager mp_manager{assembler_config, filter}; + + // We read the input file twice. In the first pass, only relations are + // read and fed into the multipolygon manager. + std::cerr << "Pass 1...\n"; + osmium::relations::read_relations(input_file, mp_manager); + std::cerr << "Pass 1 done\n"; + + // Output the amount of main memory used so far. All multipolygon relations + // are in memory now. + std::cerr << "Memory:\n"; + osmium::relations::print_used_memory(std::cerr, mp_manager.used_memory()); + + // The index storing all node locations. + index_type index; + + // The handler that stores all node locations in the index and adds them + // to the ways. + location_handler_type location_handler{index}; + + // If a location is not available in the index, we ignore it. It might + // not be needed (if it is not part of a multipolygon relation), so why + // create an error? + location_handler.ignore_errors(); + + // On the second pass we read all objects and run them first through the + // node location handler and then the multipolygon collector. The collector + // will put the areas it has created into the "buffer" which are then + // fed through our "handler". + std::cerr << "Pass 2...\n"; + osmium::io::Reader reader{input_file}; + osmium::apply(reader, location_handler, mp_manager.handler([&handler](osmium::memory::Buffer&& buffer) { + osmium::apply(buffer, handler); + })); + reader.close(); + std::cerr << "Pass 2 done\n"; + + // Output the amount of main memory used so far. All complete multipolygon + // relations have been cleaned up. + std::cerr << "Memory:\n"; + osmium::relations::print_used_memory(std::cerr, mp_manager.used_memory()); + + // If there were multipolgyon relations in the input, but some of their + // members are not in the input file (which often happens for extracts) + // this will write the IDs of the incomplete relations to stderr. + std::vector incomplete_relations_ids; + mp_manager.for_each_incomplete_relation([&](const osmium::relations::RelationHandle& handle){ + incomplete_relations_ids.push_back(handle->id()); + }); + if (!incomplete_relations_ids.empty()) { + std::cerr << "Warning! Some member ways missing for these multipolygon relations:"; + for (const auto id : incomplete_relations_ids) { + std::cerr << " " << id; + } + std::cerr << "\n"; + } + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; } } diff --git a/examples/osmium_change_tags.cpp b/examples/osmium_change_tags.cpp index e00ee0339..e85e5aaee 100644 --- a/examples/osmium_change_tags.cpp +++ b/examples/osmium_change_tags.cpp @@ -22,7 +22,6 @@ */ -#include // for std::exit #include // for std::strcmp #include // for std::exception #include // for std::cout, std::cerr @@ -66,7 +65,7 @@ class RewriteHandler : public osmium::handler::Handler { // Copy all tags with two changes: // * Do not copy "created_by" tags // * Change "landuse=forest" into "natural=wood" - void copy_tags(osmium::builder::Builder& parent, const osmium::TagList& tags) { + static void copy_tags(osmium::builder::Builder& parent, const osmium::TagList& tags) { // The TagListBuilder is used to create a list of tags. The parameter // to create it is a reference to the builder of the object that @@ -150,12 +149,12 @@ public: int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " INFILE OUTFILE\n"; - std::exit(1); + return 1; } // Get input and output file names from command line. - std::string input_file_name{argv[1]}; - std::string output_file_name{argv[2]}; + const std::string input_file_name{argv[1]}; + const std::string output_file_name{argv[2]}; try { // Initialize Reader @@ -196,8 +195,8 @@ int main(int argc, char* argv[]) { reader.close(); } catch (const std::exception& e) { // All exceptions used by the Osmium library derive from std::exception. - std::cerr << e.what() << "\n"; - std::exit(1); + std::cerr << e.what() << '\n'; + return 1; } } diff --git a/examples/osmium_convert.cpp b/examples/osmium_convert.cpp index d28663b89..63c0440ce 100644 --- a/examples/osmium_convert.cpp +++ b/examples/osmium_convert.cpp @@ -17,7 +17,6 @@ */ -#include // for std::exit #include // for std::strcmp #include // for std::exception #include // for std::cout, std::cerr @@ -53,18 +52,18 @@ void print_help() { void print_usage(const char* prgname) { std::cerr << "Usage: " << prgname << " [OPTIONS] [INFILE [OUTFILE]]\n"; - std::exit(0); } int main(int argc, char* argv[]) { if (argc == 1) { print_usage(argv[0]); + return 0; } if (argc > 1 && (!std::strcmp(argv[1], "-h") || !std::strcmp(argv[1], "--help"))) { print_help(); - std::exit(0); + return 0; } // Input and output format are empty by default. Later this will mean that @@ -83,6 +82,7 @@ int main(int argc, char* argv[]) { input_format = argv[i]; } else { print_usage(argv[0]); + return 1; } } else if (!std::strncmp(argv[i], "--from-format=", 14)) { input_format = argv[i] + 14; @@ -92,6 +92,7 @@ int main(int argc, char* argv[]) { output_format = argv[i]; } else { print_usage(argv[0]); + return 1; } } else if (!std::strncmp(argv[i], "--to-format=", 12)) { output_format = argv[i] + 12; @@ -101,6 +102,7 @@ int main(int argc, char* argv[]) { output_file_name = argv[i]; } else { print_usage(argv[0]); + return 1; } } @@ -135,7 +137,7 @@ int main(int argc, char* argv[]) { // a time. This is much easier and faster than copying each object // in the file. Buffers are moved around, so there is no cost for // copying in memory. - while (osmium::memory::Buffer buffer = reader.read()) { + while (osmium::memory::Buffer buffer = reader.read()) { // NOLINT(bugprone-use-after-move) Bug in clang-tidy https://bugs.llvm.org/show_bug.cgi?id=36516 writer(std::move(buffer)); } @@ -147,8 +149,8 @@ int main(int argc, char* argv[]) { reader.close(); } catch (const std::exception& e) { // All exceptions used by the Osmium library derive from std::exception. - std::cerr << e.what() << "\n"; - std::exit(1); + std::cerr << e.what() << '\n'; + return 1; } } diff --git a/examples/osmium_count.cpp b/examples/osmium_count.cpp index 130b98a0c..34bc68748 100644 --- a/examples/osmium_count.cpp +++ b/examples/osmium_count.cpp @@ -18,7 +18,6 @@ */ #include // for std::uint64_t -#include // for std::exit #include // for std::cout, std::cerr // Allow any format of input files (XML, PBF, ...) @@ -63,33 +62,39 @@ struct CountHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - std::exit(1); + return 1; } - // The Reader is initialized here with an osmium::io::File, but could - // also be directly initialized with a file name. - osmium::io::File input_file{argv[1]}; - osmium::io::Reader reader{input_file}; + try { + // The Reader is initialized here with an osmium::io::File, but could + // also be directly initialized with a file name. + const osmium::io::File input_file{argv[1]}; + osmium::io::Reader reader{input_file}; - // Create an instance of our own CountHandler and push the data from the - // input file through it. - CountHandler handler; - osmium::apply(reader, handler); + // Create an instance of our own CountHandler and push the data from the + // input file through it. + CountHandler handler; + osmium::apply(reader, handler); - // You do not have to close the Reader explicitly, but because the - // destructor can't throw, you will not see any errors otherwise. - reader.close(); + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. + reader.close(); - std::cout << "Nodes: " << handler.nodes << "\n"; - std::cout << "Ways: " << handler.ways << "\n"; - std::cout << "Relations: " << handler.relations << "\n"; + std::cout << "Nodes: " << handler.nodes << "\n"; + std::cout << "Ways: " << handler.ways << "\n"; + std::cout << "Relations: " << handler.relations << "\n"; - // Because of the huge amount of OSM data, some Osmium-based programs - // (though not this one) can use huge amounts of data. So checking actual - // memore usage is often useful and can be done easily with this class. - // (Currently only works on Linux, not OSX and Windows.) - osmium::MemoryUsage memory; + // Because of the huge amount of OSM data, some Osmium-based programs + // (though not this one) can use huge amounts of data. So checking actual + // memore usage is often useful and can be done easily with this class. + // (Currently only works on Linux, not macOS and Windows.) + const osmium::MemoryUsage memory; - std::cout << "\nMemory used: " << memory.peak() << " MBytes\n"; + std::cout << "\nMemory used: " << memory.peak() << " MBytes\n"; + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; + } } diff --git a/examples/osmium_create_pois.cpp b/examples/osmium_create_pois.cpp index dedbdb88a..8a30ff22e 100644 --- a/examples/osmium_create_pois.cpp +++ b/examples/osmium_create_pois.cpp @@ -19,7 +19,6 @@ */ -#include // for std::exit #include // for std::strcmp #include // for std::time #include // for std::exception @@ -37,15 +36,15 @@ int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OUTFILE\n"; - std::exit(1); + return 1; } // Get output file name from command line. - std::string output_file_name{argv[1]}; + const std::string output_file_name{argv[1]}; // If output file name is "-", this means STDOUT. Set the OPL file type // in this case. Otherwise take the file type from the file name suffix. - osmium::io::File output_file{output_file_name, output_file_name == "-" ? ".opl" : ""}; + const osmium::io::File output_file{output_file_name, output_file_name == "-" ? ".opl" : ""}; try { // Create a buffer where all objects will live. Use a sensible initial @@ -93,8 +92,8 @@ int main(int argc, char* argv[]) { writer.close(); } catch (const std::exception& e) { // All exceptions used by the Osmium library derive from std::exception. - std::cerr << e.what() << "\n"; - std::exit(1); + std::cerr << e.what() << '\n'; + return 1; } } diff --git a/examples/osmium_debug.cpp b/examples/osmium_debug.cpp index ed6859612..8c9844c5c 100644 --- a/examples/osmium_debug.cpp +++ b/examples/osmium_debug.cpp @@ -17,7 +17,6 @@ */ -#include // for std::exit #include // for std::cout, std::cerr #include // for std::string @@ -34,51 +33,57 @@ int main(int argc, char* argv[]) { if (argc < 2 || argc > 3) { std::cerr << "Usage: " << argv[0] << " OSMFILE [TYPES]\n"; std::cerr << "TYPES can be any combination of 'n', 'w', 'r', and 'c' to indicate what types of OSM entities you want (default: all).\n"; - std::exit(1); + return 1; } - // Default is all entity types: nodes, ways, relations, and changesets - osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all; + try { + // Default is all entity types: nodes, ways, relations, and changesets + osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all; - // Get entity types from command line if there is a 2nd argument. - if (argc == 3) { - read_types = osmium::osm_entity_bits::nothing; - std::string types = argv[2]; - if (types.find('n') != std::string::npos) { - read_types |= osmium::osm_entity_bits::node; + // Get entity types from command line if there is a 2nd argument. + if (argc == 3) { + read_types = osmium::osm_entity_bits::nothing; + const std::string types = argv[2]; + if (types.find('n') != std::string::npos) { + read_types |= osmium::osm_entity_bits::node; + } + if (types.find('w') != std::string::npos) { + read_types |= osmium::osm_entity_bits::way; + } + if (types.find('r') != std::string::npos) { + read_types |= osmium::osm_entity_bits::relation; + } + if (types.find('c') != std::string::npos) { + read_types |= osmium::osm_entity_bits::changeset; + } } - if (types.find('w') != std::string::npos) { - read_types |= osmium::osm_entity_bits::way; - } - if (types.find('r') != std::string::npos) { - read_types |= osmium::osm_entity_bits::relation; - } - if (types.find('c') != std::string::npos) { - read_types |= osmium::osm_entity_bits::changeset; + + // Initialize Reader with file name and the types of entities we want to + // read. + osmium::io::Reader reader{argv[1], read_types}; + + // The file header can contain metadata such as the program that generated + // the file and the bounding box of the data. + osmium::io::Header header = reader.header(); + std::cout << "HEADER:\n generator=" << header.get("generator") << "\n"; + + for (const auto& bbox : header.boxes()) { + std::cout << " bbox=" << bbox << "\n"; } + + // Initialize Dump handler. + osmium::handler::Dump dump{std::cout}; + + // Read from input and send everything to Dump handler. + osmium::apply(reader, dump); + + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. + reader.close(); + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; } - - // Initialize Reader with file name and the types of entities we want to - // read. - osmium::io::Reader reader{argv[1], read_types}; - - // The file header can contain metadata such as the program that generated - // the file and the bounding box of the data. - osmium::io::Header header = reader.header(); - std::cout << "HEADER:\n generator=" << header.get("generator") << "\n"; - - for (const auto& bbox : header.boxes()) { - std::cout << " bbox=" << bbox << "\n"; - } - - // Initialize Dump handler. - osmium::handler::Dump dump{std::cout}; - - // Read from input and send everything to Dump handler. - osmium::apply(reader, dump); - - // You do not have to close the Reader explicitly, but because the - // destructor can't throw, you will not see any errors otherwise. - reader.close(); } diff --git a/examples/osmium_dump_internal.cpp b/examples/osmium_dump_internal.cpp index 39523fa54..16f11d831 100644 --- a/examples/osmium_dump_internal.cpp +++ b/examples/osmium_dump_internal.cpp @@ -29,7 +29,6 @@ */ #include // for errno -#include // for std::exit #include // for std::strerror #include // for std::cout, std::cerr #include // for std::string @@ -73,8 +72,7 @@ public: explicit IndexFile(const std::string& filename) : m_fd(::open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666)) { // NOLINT(hicpp-signed-bitwise) if (m_fd < 0) { - std::cerr << "Can't open index file '" << filename << "': " << std::strerror(errno) << "\n"; - std::exit(2); + throw std::system_error{errno, std::system_category(), "Can't open index file '" + filename}; } #ifdef _WIN32 _setmode(m_fd, _O_BINARY); @@ -102,95 +100,101 @@ public: int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " OSMFILE DIR\n"; - std::exit(2); + return 2; } - const std::string input_file_name{argv[1]}; - const std::string output_dir{argv[2]}; + try { + const std::string input_file_name{argv[1]}; + const std::string output_dir{argv[2]}; - // Create output directory. Ignore the error if it already exists. + // Create output directory. Ignore the error if it already exists. #ifndef _WIN32 - const int result = ::mkdir(output_dir.c_str(), 0777); + const int result = ::mkdir(output_dir.c_str(), 0777); #else - const int result = mkdir(output_dir.c_str()); + const int result = mkdir(output_dir.c_str()); #endif - if (result == -1 && errno != EEXIST) { - std::cerr << "Problem creating directory '" << output_dir << "': " << std::strerror(errno) << "\n"; - std::exit(2); - } + if (result == -1 && errno != EEXIST) { + std::cerr << "Problem creating directory '" << output_dir << "': " << std::strerror(errno) << "\n"; + return 2; + } - // Create the output file which will contain our serialized OSM data - const std::string data_file{output_dir + "/data.osm.ser"}; - const int data_fd = ::open(data_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); // NOLINT(hicpp-signed-bitwise) - if (data_fd < 0) { - std::cerr << "Can't open data file '" << data_file << "': " << std::strerror(errno) << "\n"; - std::exit(2); - } + // Create the output file which will contain our serialized OSM data + const std::string data_file{output_dir + "/data.osm.ser"}; + const int data_fd = ::open(data_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); // NOLINT(hicpp-signed-bitwise) + if (data_fd < 0) { + std::cerr << "Can't open data file '" << data_file << "': " << std::strerror(errno) << "\n"; + return 2; + } #ifdef _WIN32 - _setmode(data_fd, _O_BINARY); + _setmode(data_fd, _O_BINARY); #endif - // These indexes store the offset in the data file where each node, way, - // or relation is stored. - offset_index_type node_index; - offset_index_type way_index; - offset_index_type relation_index; + // These indexes store the offset in the data file where each node, way, + // or relation is stored. + offset_index_type node_index; + offset_index_type way_index; + offset_index_type relation_index; - // This handler will dump the internal data to disk using the given file - // descriptor while updating the indexes. - osmium::handler::DiskStore disk_store_handler{data_fd, node_index, way_index, relation_index}; + // This handler will dump the internal data to disk using the given file + // descriptor while updating the indexes. + osmium::handler::DiskStore disk_store_handler{data_fd, node_index, way_index, relation_index}; - // These indexes store the mapping from node id to the ids of the ways - // containing this node, and from node/way/relation ids to the ids of the - // relations containing those objects. - map_type map_node2way; - map_type map_node2relation; - map_type map_way2relation; - map_type map_relation2relation; + // These indexes store the mapping from node id to the ids of the ways + // containing this node, and from node/way/relation ids to the ids of the + // relations containing those objects. + map_type map_node2way; + map_type map_node2relation; + map_type map_way2relation; + map_type map_relation2relation; - // This handler will update the map indexes. - osmium::handler::ObjectRelations object_relations_handler{map_node2way, map_node2relation, map_way2relation, map_relation2relation}; + // This handler will update the map indexes. + osmium::handler::ObjectRelations object_relations_handler{map_node2way, map_node2relation, map_way2relation, map_relation2relation}; - // Read OSM data buffer by buffer. - osmium::io::Reader reader{input_file_name}; + // Read OSM data buffer by buffer. + osmium::io::Reader reader{input_file_name}; - while (osmium::memory::Buffer buffer = reader.read()) { - // Write buffer to disk and update indexes. - disk_store_handler(buffer); + while (osmium::memory::Buffer buffer = reader.read()) { + // Write buffer to disk and update indexes. + disk_store_handler(buffer); - // Update object relation index maps. - osmium::apply(buffer, object_relations_handler); + // Update object relation index maps. + osmium::apply(buffer, object_relations_handler); + } + + reader.close(); + + // Write out node, way, and relation offset indexes to disk. + const IndexFile nodes_idx{output_dir + "/nodes.idx"}; + node_index.dump_as_list(nodes_idx.fd()); + + const IndexFile ways_idx{output_dir + "/ways.idx"}; + way_index.dump_as_list(ways_idx.fd()); + + const IndexFile relations_idx{output_dir + "/relations.idx"}; + relation_index.dump_as_list(relations_idx.fd()); + + // Sort the maps (so later binary search will work on them) and write + // them to disk. + map_node2way.sort(); + const IndexFile node2way_idx{output_dir + "/node2way.map"}; + map_node2way.dump_as_list(node2way_idx.fd()); + + map_node2relation.sort(); + const IndexFile node2relation_idx{output_dir + "/node2rel.map"}; + map_node2relation.dump_as_list(node2relation_idx.fd()); + + map_way2relation.sort(); + const IndexFile way2relation_idx{output_dir + "/way2rel.map"}; + map_way2relation.dump_as_list(way2relation_idx.fd()); + + map_relation2relation.sort(); + const IndexFile relation2relation_idx{output_dir + "/rel2rel.map"}; + map_relation2relation.dump_as_list(relation2relation_idx.fd()); + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; } - - reader.close(); - - // Write out node, way, and relation offset indexes to disk. - IndexFile nodes_idx{output_dir + "/nodes.idx"}; - node_index.dump_as_list(nodes_idx.fd()); - - IndexFile ways_idx{output_dir + "/ways.idx"}; - way_index.dump_as_list(ways_idx.fd()); - - IndexFile relations_idx{output_dir + "/relations.idx"}; - relation_index.dump_as_list(relations_idx.fd()); - - // Sort the maps (so later binary search will work on them) and write - // them to disk. - map_node2way.sort(); - IndexFile node2way_idx{output_dir + "/node2way.map"}; - map_node2way.dump_as_list(node2way_idx.fd()); - - map_node2relation.sort(); - IndexFile node2relation_idx{output_dir + "/node2rel.map"}; - map_node2relation.dump_as_list(node2relation_idx.fd()); - - map_way2relation.sort(); - IndexFile way2relation_idx{output_dir + "/way2rel.map"}; - map_way2relation.dump_as_list(way2relation_idx.fd()); - - map_relation2relation.sort(); - IndexFile relation2relation_idx{output_dir + "/rel2rel.map"}; - map_relation2relation.dump_as_list(relation2relation_idx.fd()); } diff --git a/examples/osmium_filter_discussions.cpp b/examples/osmium_filter_discussions.cpp index c4d092a0a..2de151724 100644 --- a/examples/osmium_filter_discussions.cpp +++ b/examples/osmium_filter_discussions.cpp @@ -22,7 +22,6 @@ */ #include // for std::copy_if -#include // for std::exit #include // for std::cout, std::cerr // We want to read OSM files in XML format @@ -43,50 +42,56 @@ int main(int argc, char* argv[]) { if (argc != 3) { std::cout << "Usage: " << argv[0] << " INFILE OUTFILE\n"; - std::exit(1); + return 1; } - // The input file, deduce file format from file suffix. - osmium::io::File input_file{argv[1]}; + try { + // The input file, deduce file format from file suffix. + const osmium::io::File input_file{argv[1]}; - // The output file, force XML OSM file format. - osmium::io::File output_file{argv[2], "osm"}; + // The output file, force XML OSM file format. + const osmium::io::File output_file{argv[2], "osm"}; - // Initialize Reader for the input file. - // Read only changesets (will ignore nodes, ways, and - // relations if there are any). - osmium::io::Reader reader{input_file, osmium::osm_entity_bits::changeset}; + // Initialize Reader for the input file. + // Read only changesets (will ignore nodes, ways, and + // relations if there are any). + osmium::io::Reader reader{input_file, osmium::osm_entity_bits::changeset}; - // Get the header from the input file. - osmium::io::Header header = reader.header(); + // Get the header from the input file. + osmium::io::Header header = reader.header(); - // Set the "generator" on the header to ourselves. - header.set("generator", "osmium_filter_discussions"); + // Set the "generator" on the header to ourselves. + header.set("generator", "osmium_filter_discussions"); - // Initialize writer for the output file. Use the header from the input - // file for the output file. This will copy over some header information. - // The last parameter will tell the writer that it is allowed to overwrite - // an existing file. Without it, it will refuse to do so. - osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow}; + // Initialize writer for the output file. Use the header from the input + // file for the output file. This will copy over some header information. + // The last parameter will tell the writer that it is allowed to overwrite + // an existing file. Without it, it will refuse to do so. + osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow}; - // Create range of input iterators that will iterator over all changesets - // delivered from input file through the "reader". - auto input_range = osmium::io::make_input_iterator_range(reader); + // Create range of input iterators that will iterator over all changesets + // delivered from input file through the "reader". + auto input_range = osmium::io::make_input_iterator_range(reader); - // Create an output iterator writing through the "writer" object to the - // output file. - auto output_iterator = osmium::io::make_output_iterator(writer); + // Create an output iterator writing through the "writer" object to the + // output file. + auto output_iterator = osmium::io::make_output_iterator(writer); - // Copy all changesets from input to output that have at least one comment. - std::copy_if(input_range.begin(), input_range.end(), output_iterator, [](const osmium::Changeset& changeset) { - return changeset.num_comments() > 0; - }); + // Copy all changesets from input to output that have at least one comment. + std::copy_if(input_range.begin(), input_range.end(), output_iterator, [](const osmium::Changeset& changeset) { + return changeset.num_comments() > 0; + }); - // Explicitly close the writer and reader. Will throw an exception if - // there is a problem. If you wait for the destructor to close the writer - // and reader, you will not notice the problem, because destructors must - // not throw. - writer.close(); - reader.close(); + // Explicitly close the writer and reader. Will throw an exception if + // there is a problem. If you wait for the destructor to close the writer + // and reader, you will not notice the problem, because destructors must + // not throw. + writer.close(); + reader.close(); + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; + } } diff --git a/examples/osmium_index_lookup.cpp b/examples/osmium_index_lookup.cpp index 2f428bf7c..fa60d0433 100644 --- a/examples/osmium_index_lookup.cpp +++ b/examples/osmium_index_lookup.cpp @@ -67,10 +67,10 @@ public: IndexAccess(const IndexAccess&) = delete; IndexAccess& operator=(const IndexAccess&) = delete; - IndexAccess(IndexAccess&&) = delete; - IndexAccess& operator=(IndexAccess&&) = delete; + IndexAccess(IndexAccess&&) noexcept = delete; + IndexAccess& operator=(IndexAccess&&) noexcept = delete; - virtual ~IndexAccess() = default; + virtual ~IndexAccess() noexcept = default; virtual void dump() const = 0; @@ -97,8 +97,16 @@ public: IndexAccess(fd) { } + IndexAccessDense(const IndexAccessDense&) = default; + IndexAccessDense& operator=(const IndexAccessDense&) = default; + + IndexAccessDense(IndexAccessDense&&) noexcept = default; + IndexAccessDense& operator=(IndexAccessDense&&) noexcept = default; + + ~IndexAccessDense() noexcept override = default; + void dump() const override { - index_type index{this->fd()}; + const index_type index{this->fd()}; for (std::size_t i = 0; i < index.size(); ++i) { if (index.get(i) != TValue{}) { @@ -108,7 +116,7 @@ public: } bool search(const osmium::unsigned_object_id_type& key) const override { - index_type index{this->fd()}; + const index_type index{this->fd()}; try { TValue value = index.get(key); @@ -136,8 +144,16 @@ public: IndexAccess(fd) { } + IndexAccessSparse(const IndexAccessSparse&) = default; + IndexAccessSparse& operator=(const IndexAccessSparse&) = default; + + IndexAccessSparse(IndexAccessSparse&&) noexcept = default; + IndexAccessSparse& operator=(IndexAccessSparse&&) noexcept = default; + + ~IndexAccessSparse() noexcept override = default; + void dump() const override { - index_type index{this->fd()}; + const index_type index{this->fd()}; for (const auto& element : index) { std::cout << element.first << " " << element.second << "\n"; @@ -148,7 +164,7 @@ public: using element_type = typename index_type::element_type; index_type index{this->fd()}; - element_type elem{key, TValue{}}; + const element_type elem{key, TValue{}}; const auto positions = std::equal_range(index.begin(), index.end(), elem, @@ -182,7 +198,7 @@ class Options { bool m_array_format = false; bool m_list_format = false; - void print_help() { + static void print_help() { std::cout << "Usage: osmium_index_lookup [OPTIONS]\n\n" << "-h, --help Print this help message\n" << "-a, --array=FILE Read given index file in array format\n" @@ -193,7 +209,7 @@ class Options { ; } - void print_usage(const char* prgname) { + static void print_usage(const char* prgname) { std::cout << "Usage: " << prgname << " [OPTIONS]\n\n"; std::exit(0); } @@ -331,14 +347,14 @@ int run(const IndexAccess& index, const Options& options) { int main(int argc, char* argv[]) { // Parse command line options. - Options options{argc, argv}; + const Options options{argc, argv}; // Open the index file. const int fd = ::open(options.filename(), O_RDWR); if (fd < 0) { std::cerr << "Can not open file '" << options.filename() << "': " << std::strerror(errno) << '\n'; - std::exit(2); + return 2; } #ifdef _WIN32 @@ -363,8 +379,9 @@ int main(int argc, char* argv[]) { const auto index = create(options.dense_format(), fd); return run(*index, options); } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << '\n'; - std::exit(1); + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; } } diff --git a/examples/osmium_location_cache_create.cpp b/examples/osmium_location_cache_create.cpp index d1b381082..7afa8b8a3 100644 --- a/examples/osmium_location_cache_create.cpp +++ b/examples/osmium_location_cache_create.cpp @@ -25,7 +25,6 @@ */ #include // for errno -#include // for std::exit #include // for strerror #include // for open #include // for std::cout, std::cerr @@ -62,33 +61,39 @@ using location_handler_type = osmium::handler::NodeLocationsForWays; int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n"; - std::exit(1); + return 1; } - const std::string input_filename{argv[1]}; - const std::string cache_filename{argv[2]}; + try { + const std::string input_filename{argv[1]}; + const std::string cache_filename{argv[2]}; - // Construct Reader reading only nodes - osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::node}; + // Construct Reader reading only nodes + osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::node}; - // Initialize location index on disk creating a new file. - const int fd = ::open(cache_filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666); // NOLINT(hicpp-signed-bitwise) - if (fd == -1) { - std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; - std::exit(1); - } + // Initialize location index on disk creating a new file. + const int fd = ::open(cache_filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666); // NOLINT(hicpp-signed-bitwise) + if (fd == -1) { + std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; + return 1; + } #ifdef _WIN32 - _setmode(fd, _O_BINARY); + _setmode(fd, _O_BINARY); #endif - index_type index{fd}; + index_type index{fd}; - // The handler that stores all node locations in the index. - location_handler_type location_handler{index}; + // The handler that stores all node locations in the index. + location_handler_type location_handler{index}; - // Feed all nodes through the location handler. - osmium::apply(reader, location_handler); + // Feed all nodes through the location handler. + osmium::apply(reader, location_handler); - // Explicitly close input so we get notified of any errors. - reader.close(); + // Explicitly close input so we get notified of any errors. + reader.close(); + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; + } } diff --git a/examples/osmium_location_cache_use.cpp b/examples/osmium_location_cache_use.cpp index 6af0fe67a..533499670 100644 --- a/examples/osmium_location_cache_use.cpp +++ b/examples/osmium_location_cache_use.cpp @@ -25,7 +25,6 @@ */ #include // for errno -#include // for std::exit #include // for strerror #include // for open #include // for std::cout, std::cerr @@ -63,7 +62,9 @@ using location_handler_type = osmium::handler::NodeLocationsForWays; // ID and all nodes IDs and locations in those ways. struct MyHandler : public osmium::handler::Handler { - void way(const osmium::Way& way) { + // The callback functions can be either static or not depending on whether + // you need to access any member variables of the handler. + static void way(const osmium::Way& way) { std::cout << "way " << way.id() << "\n"; for (const auto& nr : way.nodes()) { std::cout << " node " << nr.ref() << " " << nr.location() << "\n"; @@ -75,34 +76,40 @@ struct MyHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n"; - std::exit(1); - } - - const std::string input_filename{argv[1]}; - const std::string cache_filename{argv[2]}; - - // Construct Reader reading only ways - osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::way}; - - // Initialize location index on disk using an existing file - const int fd = ::open(cache_filename.c_str(), O_RDWR); - if (fd == -1) { - std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; return 1; } + + try { + const std::string input_filename{argv[1]}; + const std::string cache_filename{argv[2]}; + + // Construct Reader reading only ways + osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::way}; + + // Initialize location index on disk using an existing file + const int fd = ::open(cache_filename.c_str(), O_RDWR); + if (fd == -1) { + std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; + return 1; + } #ifdef _WIN32 - _setmode(fd, _O_BINARY); + _setmode(fd, _O_BINARY); #endif - index_type index{fd}; + index_type index{fd}; - // The handler that adds node locations from the index to the ways. - location_handler_type location_handler{index}; + // The handler that adds node locations from the index to the ways. + location_handler_type location_handler{index}; - // Feed all ways through the location handler and then our own handler. - MyHandler handler; - osmium::apply(reader, location_handler, handler); + // Feed all ways through the location handler and then our own handler. + MyHandler handler; + osmium::apply(reader, location_handler, handler); - // Explicitly close input so we get notified of any errors. - reader.close(); + // Explicitly close input so we get notified of any errors. + reader.close(); + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; + } } diff --git a/examples/osmium_pub_names.cpp b/examples/osmium_pub_names.cpp index dbc37c332..087eec715 100644 --- a/examples/osmium_pub_names.cpp +++ b/examples/osmium_pub_names.cpp @@ -18,7 +18,6 @@ */ -#include // for std::exit #include // for std::strncmp #include // for std::cout, std::cerr @@ -33,7 +32,7 @@ class NamesHandler : public osmium::handler::Handler { - void output_pubs(const osmium::OSMObject& object) { + static void output_pubs(const osmium::OSMObject& object) { const osmium::TagList& tags = object.tags(); if (tags.has_tag("amenity", "pub")) { @@ -57,13 +56,17 @@ class NamesHandler : public osmium::handler::Handler { public: + // The callback functions can be either static or not depending on whether + // you need to access any member variables of the handler. // Nodes can be tagged amenity=pub. - void node(const osmium::Node& node) { + static void node(const osmium::Node& node) { output_pubs(node); } + // The callback functions can be either static or not depending on whether + // you need to access any member variables of the handler. // Ways can be tagged amenity=pub, too (typically buildings). - void way(const osmium::Way& way) { + static void way(const osmium::Way& way) { output_pubs(way); } @@ -72,18 +75,24 @@ public: int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - std::exit(1); + return 1; } - // Construct the handler defined above - NamesHandler names_handler; + try { + // Construct the handler defined above + NamesHandler names_handler; - // Initialize the reader with the filename from the command line and - // tell it to only read nodes and ways. We are ignoring multipolygon - // relations in this simple example. - osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::node | osmium::osm_entity_bits::way}; + // Initialize the reader with the filename from the command line and + // tell it to only read nodes and ways. We are ignoring multipolygon + // relations in this simple example. + osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::node | osmium::osm_entity_bits::way}; - // Apply input data to our own handler - osmium::apply(reader, names_handler); + // Apply input data to our own handler + osmium::apply(reader, names_handler); + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; + } } diff --git a/examples/osmium_read.cpp b/examples/osmium_read.cpp index 9f391c874..ac8531044 100644 --- a/examples/osmium_read.cpp +++ b/examples/osmium_read.cpp @@ -13,7 +13,6 @@ */ -#include // for std::exit #include // for std::cerr // Allow any format of input files (XML, PBF, ...) @@ -22,21 +21,27 @@ int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - std::exit(1); + return 1; } - // The Reader is initialized here with an osmium::io::File, but could - // also be directly initialized with a file name. - osmium::io::File input_file{argv[1]}; - osmium::io::Reader reader{input_file}; + try { + // The Reader is initialized here with an osmium::io::File, but could + // also be directly initialized with a file name. + const osmium::io::File input_file{argv[1]}; + osmium::io::Reader reader{input_file}; - // OSM data comes in buffers, read until there are no more. - while (osmium::memory::Buffer buffer = reader.read()) { - // do nothing + // OSM data comes in buffers, read until there are no more. + while (const osmium::memory::Buffer buffer = reader.read()) { + // do nothing + } + + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. + reader.close(); + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; } - - // You do not have to close the Reader explicitly, but because the - // destructor can't throw, you will not see any errors otherwise. - reader.close(); } diff --git a/examples/osmium_read_with_progress.cpp b/examples/osmium_read_with_progress.cpp index 415e78de8..345348f7a 100644 --- a/examples/osmium_read_with_progress.cpp +++ b/examples/osmium_read_with_progress.cpp @@ -16,7 +16,6 @@ */ -#include // for std::exit #include // for std::cerr // Allow any format of input files (XML, PBF, ...) @@ -29,28 +28,34 @@ int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - std::exit(1); + return 1; } - // The Reader is initialized here with an osmium::io::File, but could - // also be directly initialized with a file name. - osmium::io::File input_file{argv[1]}; - osmium::io::Reader reader{input_file}; + try { + // The Reader is initialized here with an osmium::io::File, but could + // also be directly initialized with a file name. + const osmium::io::File input_file{argv[1]}; + osmium::io::Reader reader{input_file}; - // Initialize progress bar, enable it only if STDERR is a TTY. - osmium::ProgressBar progress{reader.file_size(), osmium::isatty(2)}; + // Initialize progress bar, enable it only if STDERR is a TTY. + osmium::ProgressBar progress{reader.file_size(), osmium::isatty(2)}; - // OSM data comes in buffers, read until there are no more. - while (osmium::memory::Buffer buffer = reader.read()) { - // Update progress bar for each buffer. - progress.update(reader.offset()); + // OSM data comes in buffers, read until there are no more. + while (const osmium::memory::Buffer buffer = reader.read()) { + // Update progress bar for each buffer. + progress.update(reader.offset()); + } + + // Progress bar is done. + progress.done(); + + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. + reader.close(); + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; } - - // Progress bar is done. - progress.done(); - - // You do not have to close the Reader explicitly, but because the - // destructor can't throw, you will not see any errors otherwise. - reader.close(); } diff --git a/examples/osmium_road_length.cpp b/examples/osmium_road_length.cpp index 4aced95f8..2b2a44a99 100644 --- a/examples/osmium_road_length.cpp +++ b/examples/osmium_road_length.cpp @@ -20,7 +20,6 @@ */ -#include // for std::exit #include // for std::cout, std::cerr // Allow any format of input files (XML, PBF, ...) @@ -65,28 +64,34 @@ struct RoadLengthHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - std::exit(1); + return 1; } - // Initialize the reader with the filename from the command line and - // tell it to only read nodes and ways. - osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::node | osmium::osm_entity_bits::way}; + try { + // Initialize the reader with the filename from the command line and + // tell it to only read nodes and ways. + osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::node | osmium::osm_entity_bits::way}; - // The index to hold node locations. - index_type index; + // The index to hold node locations. + index_type index; - // The location handler will add the node locations to the index and then - // to the ways - location_handler_type location_handler{index}; + // The location handler will add the node locations to the index and then + // to the ways + location_handler_type location_handler{index}; - // Our handler defined above - RoadLengthHandler road_length_handler; + // Our handler defined above + RoadLengthHandler road_length_handler; - // Apply input data to first the location handler and then our own handler - osmium::apply(reader, location_handler, road_length_handler); + // Apply input data to first the location handler and then our own handler + osmium::apply(reader, location_handler, road_length_handler); - // Output the length. The haversine function calculates it in meters, - // so we first devide by 1000 to get kilometers. - std::cout << "Length: " << road_length_handler.length / 1000 << " km\n"; + // Output the length. The haversine function calculates it in meters, + // so we first devide by 1000 to get kilometers. + std::cout << "Length: " << road_length_handler.length / 1000 << " km\n"; + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; + } } diff --git a/examples/osmium_tags_filter.cpp b/examples/osmium_tags_filter.cpp new file mode 100644 index 000000000..ea62ba73c --- /dev/null +++ b/examples/osmium_tags_filter.cpp @@ -0,0 +1,171 @@ +/* + + EXAMPLE osmium_filter + + Filter OSM files + + DEMONSTRATES USE OF: + * file input and output + * file types + * Osmium buffers + * Tags filter + + SIMPLER EXAMPLES you might want to understand first: + * osmium_convert + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for std::strcmp +#include // for std::exception +#include // for std::cout, std::cerr +#include // for std::string + +// Allow any format of input files (XML, PBF, ...) +#include + +// Allow any format of output files (XML, PBF, ...) +#include + +#include +#include + +void print_help() { + std::cout << "osmium_filter [OPTIONS] [INFILE [OUTFILE]]\n\n" \ + << "If INFILE or OUTFILE is not given stdin/stdout is assumed.\n" \ + << "File format is autodetected from file name suffix.\n" \ + << "Use -f and -t options to force file format.\n" \ + << "\nFile types:\n" \ + << " osm normal OSM file\n" \ + << " osc OSM change file\n" \ + << " osh OSM file with history information\n" \ + << "\nFile format:\n" \ + << " (default) XML encoding\n" \ + << " pbf binary PBF encoding\n" \ + << " opl OPL encoding\n" \ + << "\nFile compression\n" \ + << " gz compressed with gzip\n" \ + << " bz2 compressed with bzip2\n" \ + << "\nOptions:\n" \ + << " -h, --help This help message\n" \ + << " -f, --from-format=FORMAT Input format\n" \ + << " -t, --to-format=FORMAT Output format\n"; +} + +void print_usage(const char* prgname) { + std::cerr << "Usage: " << prgname << " [OPTIONS] [INFILE [OUTFILE]]\n"; +} + +int main(int argc, char* argv[]) { + if (argc == 1) { + print_usage(argv[0]); + return 0; + } + + if (argc > 1 && (!std::strcmp(argv[1], "-h") || + !std::strcmp(argv[1], "--help"))) { + print_help(); + return 0; + } + + // Input and output format are empty by default. Later this will mean that + // the format should be taken from the input and output file suffix, + // respectively. + std::string input_format; + std::string output_format; + + std::string input_file_name; + std::string output_file_name; + + for (int i = 1; i < argc; ++i) { + if (!std::strcmp(argv[i], "-f") || !std::strcmp(argv[i], "--from-format")) { + ++i; + if (i < argc) { + input_format = argv[i]; + } else { + print_usage(argv[0]); + return 1; + } + } else if (!std::strncmp(argv[i], "--from-format=", 14)) { + input_format = argv[i] + 14; + } else if (!std::strcmp(argv[i], "-t") || !std::strcmp(argv[i], "--to-format")) { + ++i; + if (i < argc) { + output_format = argv[i]; + } else { + print_usage(argv[0]); + return 1; + } + } else if (!std::strncmp(argv[i], "--to-format=", 12)) { + output_format = argv[i] + 12; + } else if (input_file_name.empty()) { + input_file_name = argv[i]; + } else if (output_file_name.empty()) { + output_file_name = argv[i]; + } else { + print_usage(argv[0]); + return 1; + } + } + + // This declares the input and output files using either the suffix of + // the file names or the format in the 2nd argument. It does not yet open + // the files. + const osmium::io::File input_file{input_file_name, input_format}; + const osmium::io::File output_file{output_file_name, output_format}; + + // Input and output files can be OSM data files (without history) or + // OSM history files. History files are detected if they use the '.osh' + // file suffix. + if ( input_file.has_multiple_object_versions() && + !output_file.has_multiple_object_versions()) { + std::cerr << "Warning! You are converting from an OSM file with (potentially) several versions of the same object to one that is not marked as such.\n"; + } + + try { + // Initialize Reader + osmium::io::Reader reader{input_file}; + + // Get header from input file and change the "generator" setting to + // ourselves. + osmium::io::Header header = reader.header(); + header.set("generator", "osmium_convert"); + + // Initialize Writer using the header from above and tell it that it + // is allowed to overwrite a possibly existing file. + osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow}; + + // Match highway=primary or highway=secondary + osmium::TagsFilter filter1{false}; + filter1.add_rule(true, "highway", "primary"); + filter1.add_rule(true, "highway", "secondary"); + + // Match oneway=yes + osmium::TagsFilter filter2{false}; + filter2.add_rule(true, "oneway", "yes"); + + // Get all object matching both filters + while (osmium::memory::Buffer buffer = reader.read()) { // NOLINT(bugprone-use-after-move) Bug in clang-tidy https://bugs.llvm.org/show_bug.cgi?id=36516 + for (const auto& object : buffer.select()) { + if (osmium::tags::match_any_of(object.tags(), filter1) && + osmium::tags::match_any_of(object.tags(), filter2)) { + writer(object); + } + } + } + + // Explicitly close the writer and reader. Will throw an exception if + // there is a problem. If you wait for the destructor to close the writer + // and reader, you will not notice the problem, because destructors must + // not throw. + writer.close(); + reader.close(); + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. + std::cerr << e.what() << '\n'; + return 1; + } +} + diff --git a/examples/osmium_tiles.cpp b/examples/osmium_tiles.cpp index 52a1a1f5d..4a6a1f7b9 100644 --- a/examples/osmium_tiles.cpp +++ b/examples/osmium_tiles.cpp @@ -15,7 +15,7 @@ */ -#include // for std::exit, std::atoi, std::atof +#include // for std::atoi, std::atof #include // for std::cout, std::cerr // The Location contains a longitude and latitude and is usually used inside @@ -33,14 +33,14 @@ int main(int argc, char* argv[]) { if (argc != 4) { std::cerr << "Usage: " << argv[0] << " ZOOM LON LAT\n"; - std::exit(1); + return 1; } const int zoom = std::atoi(argv[1]); // NOLINT(cert-err34-c) if (zoom < 0 || zoom > 30) { std::cerr << "ERROR: Zoom must be between 0 and 30\n"; - std::exit(1); + return 1; } osmium::Location location{}; @@ -49,14 +49,14 @@ int main(int argc, char* argv[]) { location.set_lat(argv[3]); } catch (const osmium::invalid_location&) { std::cerr << "ERROR: Location is invalid\n"; - std::exit(1); + return 1; } // A location can store some invalid locations, ie locations outside the // -180 to 180 and -90 to 90 degree range. This function checks for that. if (!location.valid()) { std::cerr << "ERROR: Location is invalid\n"; - std::exit(1); + return 1; } std::cout << "WGS84: lon=" << location.lon() << " lat=" << location.lat() << "\n"; @@ -68,7 +68,7 @@ int main(int argc, char* argv[]) { // Create a tile at this location. This will also internally use the // Mercator projection and then calculate the tile coordinates. - const osmium::geom::Tile tile{uint32_t(zoom), location}; + const osmium::geom::Tile tile{static_cast(zoom), location}; std::cout << "Tile: zoom=" << tile.z << " x=" << tile.x << " y=" << tile.y << "\n"; } diff --git a/include/gdalcpp.hpp b/include/gdalcpp.hpp index 4f3d48096..45b5cea2a 100644 --- a/include/gdalcpp.hpp +++ b/include/gdalcpp.hpp @@ -5,11 +5,11 @@ C++11 wrapper classes for GDAL/OGR. -Version 1.1.1 +Version 1.3.0 https://github.com/joto/gdalcpp -Copyright 2015 Jochen Topf +Copyright 2015-2021 Jochen Topf Boost Software License - Version 1.0 - August 17th, 2003 @@ -37,17 +37,25 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include -#include -#include -#include - #include #include #include #include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +# define GDALCPP_EXPORT __declspec(dllexport) +#else +# define GDALCPP_EXPORT __attribute__ ((visibility("default"))) +#endif + namespace gdalcpp { #if GDAL_VERSION_MAJOR >= 2 @@ -61,7 +69,7 @@ namespace gdalcpp { /** * Exception thrown for all errors in this class. */ - class gdal_error : public std::runtime_error { + class GDALCPP_EXPORT gdal_error : public std::runtime_error { std::string m_driver; std::string m_dataset; @@ -85,23 +93,23 @@ namespace gdalcpp { m_error(error) { } - const std::string& driver() const { + const std::string& driver() const noexcept { return m_driver; } - const std::string& dataset() const { + const std::string& dataset() const noexcept { return m_dataset; } - const std::string& layer() const { + const std::string& layer() const noexcept { return m_layer; } - const std::string& field() const { + const std::string& field() const noexcept { return m_field; } - OGRErr error() const { + OGRErr error() const noexcept { return m_error; } @@ -111,18 +119,26 @@ namespace gdalcpp { struct init_wrapper { #if GDAL_VERSION_MAJOR >= 2 - init_wrapper() { GDALAllRegister(); } + init_wrapper() noexcept { + GDALAllRegister(); + } #else - init_wrapper() { OGRRegisterAll(); } - ~init_wrapper() { OGRCleanupAll(); } + init_wrapper() noexcept { + OGRRegisterAll(); + } + ~init_wrapper() noexcept { + OGRCleanupAll(); + } #endif - }; + }; // struct init_wrapper struct init_library { + init_library() { static init_wrapper iw; } - }; + + }; // struct init_library class Driver : private init_library { @@ -138,11 +154,13 @@ namespace gdalcpp { m_driver(OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str())) { #endif if (!m_driver) { - throw gdal_error(std::string("unknown driver: '") + driver_name + "'", OGRERR_NONE, driver_name); + throw gdal_error{std::string{"unknown driver: '"} + driver_name + "'", + OGRERR_NONE, + driver_name}; } } - gdal_driver_type& get() const { + gdal_driver_type& get() const noexcept { return *m_driver; } @@ -155,14 +173,14 @@ namespace gdalcpp { Options(const std::vector& options) : m_options(options), - m_ptrs(new const char*[options.size()+1]) { + m_ptrs(new const char*[options.size() + 1]) { std::transform(m_options.begin(), m_options.end(), m_ptrs.get(), [&](const std::string& s) { return s.data(); }); m_ptrs[options.size()] = nullptr; } - char** get() const { + char** get() const noexcept { return const_cast(m_ptrs.get()); } @@ -178,33 +196,37 @@ namespace gdalcpp { SRS() : m_spatial_reference() { - auto result = m_spatial_reference.SetWellKnownGeogCS("WGS84"); + const auto result = m_spatial_reference.SetWellKnownGeogCS("CRS84"); if (result != OGRERR_NONE) { - throw gdal_error(std::string("can not initialize spatial reference system WGS84"), result); + throw gdal_error{std::string{"can not initialize spatial reference system WGS84"}, + result}; } } explicit SRS(int epsg) : m_spatial_reference() { - auto result = m_spatial_reference.importFromEPSG(epsg); + const auto result = m_spatial_reference.importFromEPSG(epsg); if (result != OGRERR_NONE) { - throw gdal_error(std::string("can not initialize spatial reference system for EPSG:") + std::to_string(epsg), result); + throw gdal_error{std::string{"can not initialize spatial reference system for EPSG:"} + std::to_string(epsg), + result}; } } explicit SRS(const char* name) : m_spatial_reference() { - auto result = m_spatial_reference.importFromProj4(name); + const auto result = m_spatial_reference.importFromProj4(name); if (result != OGRERR_NONE) { - throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result); + throw gdal_error{std::string{"can not initialize spatial reference system '"} + name + "'", + result}; } } explicit SRS(const std::string& name) : m_spatial_reference() { - auto result = m_spatial_reference.importFromProj4(name.c_str()); + const auto result = m_spatial_reference.importFromProj4(name.c_str()); if (result != OGRERR_NONE) { - throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result); + throw gdal_error{std::string{"can not initialize spatial reference system '"} + name + "'", + result}; } } @@ -212,11 +234,11 @@ namespace gdalcpp { m_spatial_reference(spatial_reference) { } - OGRSpatialReference& get() { + OGRSpatialReference& get() noexcept { return m_spatial_reference; } - const OGRSpatialReference& get() const { + const OGRSpatialReference& get() const noexcept { return m_spatial_reference; } @@ -257,11 +279,14 @@ namespace gdalcpp { m_dataset(detail::Driver(driver_name).get().CreateDataSource(dataset_name.c_str(), m_options.get())) { #endif if (!m_dataset) { - throw gdal_error(std::string("failed to create dataset '") + dataset_name + "'", OGRERR_NONE, driver_name, dataset_name); + throw gdal_error{std::string{"failed to create dataset '"} + dataset_name + "'", + OGRERR_NONE, + driver_name, + dataset_name}; } } - ~Dataset() { + ~Dataset() noexcept { try { if (m_edit_count > 0) { commit_transaction(); @@ -270,24 +295,24 @@ namespace gdalcpp { } } - const std::string& driver_name() const { + const std::string& driver_name() const noexcept { return m_driver_name; } - const std::string& dataset_name() const { + const std::string& dataset_name() const noexcept { return m_dataset_name; } - gdal_dataset_type& get() const { + gdal_dataset_type& get() const noexcept { return *m_dataset; } - SRS& srs() { + SRS& srs() noexcept { return m_srs; } void exec(const char* sql) { - auto result = m_dataset->ExecuteSQL(sql, nullptr, nullptr); + const auto result = m_dataset->ExecuteSQL(sql, nullptr, nullptr); if (result) { m_dataset->ReleaseResultSet(result); } @@ -334,7 +359,7 @@ namespace gdalcpp { } } - Dataset& enable_auto_transactions(uint64_t edits = 100000) { + Dataset& enable_auto_transactions(uint64_t edits = 100000) noexcept { m_max_edit_count = edits; return *this; } @@ -362,20 +387,23 @@ namespace gdalcpp { m_dataset(dataset), m_layer(dataset.get().CreateLayer(layer_name.c_str(), &dataset.srs().get(), type, m_options.get())) { if (!m_layer) { - throw gdal_error(std::string("failed to create layer '") + layer_name + "'", OGRERR_NONE, - dataset.driver_name(), dataset.dataset_name(), layer_name); + throw gdal_error{std::string{"failed to create layer '"} + layer_name + "'", + OGRERR_NONE, + dataset.driver_name(), + dataset.dataset_name(), + layer_name}; } } - OGRLayer& get() { + OGRLayer& get() noexcept { return *m_layer; } - const OGRLayer& get() const { + const OGRLayer& get() const noexcept { return *m_layer; } - Dataset& dataset() const { + Dataset& dataset() const noexcept { return m_dataset; } @@ -389,8 +417,12 @@ namespace gdalcpp { field.SetPrecision(precision); if (m_layer->CreateField(&field) != OGRERR_NONE) { - throw gdal_error(std::string("failed to create field '") + field_name + "' in layer '" + name() + "'", OGRERR_NONE, - m_dataset.driver_name(), m_dataset.dataset_name(), name(), field_name); + throw gdal_error{std::string{"failed to create field '"} + field_name + "' in layer '" + name() + "'", + OGRERR_NONE, + m_dataset.driver_name(), + m_dataset.dataset_name(), + name(), + field_name}; } return *this; @@ -398,18 +430,25 @@ namespace gdalcpp { void create_feature(OGRFeature* feature) { dataset().prepare_edit(); - OGRErr result = m_layer->CreateFeature(feature); + const auto result = m_layer->CreateFeature(feature); if (result != OGRERR_NONE) { - throw gdal_error(std::string("creating feature in layer '") + name() + "' failed", result, dataset().driver_name(), dataset().dataset_name()); + throw gdal_error{std::string{"creating feature in layer '"} + name() + "' failed", + result, + dataset().driver_name(), + dataset().dataset_name()}; } dataset().finalize_edit(); } Layer& start_transaction() { #if GDAL_VERSION_MAJOR < 2 - OGRErr result = m_layer->StartTransaction(); + const auto result = m_layer->StartTransaction(); if (result != OGRERR_NONE) { - throw gdal_error(std::string("starting transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name()); + throw gdal_error{std::string{"starting transaction on layer '"} + name() + "' failed", + result, + m_dataset.driver_name(), + m_dataset.dataset_name(), + name()}; } #endif return *this; @@ -417,9 +456,13 @@ namespace gdalcpp { Layer& commit_transaction() { #if GDAL_VERSION_MAJOR < 2 - OGRErr result = m_layer->CommitTransaction(); + const auto result = m_layer->CommitTransaction(); if (result != OGRERR_NONE) { - throw gdal_error(std::string("committing transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name()); + throw gdal_error{std::string{"committing transaction on layer '"} + name() + "' failed", + result, + m_dataset.driver_name(), + m_dataset.dataset_name(), + name()}; } #endif return *this; @@ -446,11 +489,14 @@ namespace gdalcpp { m_layer(layer), m_feature(OGRFeature::CreateFeature(m_layer.get().GetLayerDefn())) { if (!m_feature) { - throw std::bad_alloc(); + throw std::bad_alloc{}; } - OGRErr result = m_feature->SetGeometryDirectly(geometry.release()); + const auto result = m_feature->SetGeometryDirectly(geometry.release()); if (result != OGRERR_NONE) { - throw gdal_error(std::string("setting feature geometry in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name()); + throw gdal_error{std::string{"setting feature geometry in layer '"} + m_layer.name() + "' failed", + result, + m_layer.dataset().driver_name(), + m_layer.dataset().dataset_name()}; } } @@ -458,13 +504,13 @@ namespace gdalcpp { m_layer.create_feature(m_feature.get()); } - template + template Feature& set_field(int n, T&& arg) { m_feature->SetField(n, std::forward(arg)); return *this; } - template + template Feature& set_field(const char* name, T&& arg) { m_feature->SetField(name, std::forward(arg)); return *this; @@ -474,4 +520,6 @@ namespace gdalcpp { } // namespace gdalcpp +#undef GDALCPP_EXPORT + #endif // GDALCPP_HPP diff --git a/include/osmium/area/assembler.hpp b/include/osmium/area/assembler.hpp index 29fd6a596..9ed2d5ff1 100644 --- a/include/osmium/area/assembler.hpp +++ b/include/osmium/area/assembler.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -213,7 +213,7 @@ namespace osmium { // Now create the Area object and add the attributes and tags // from the relation. - bool okay = create_area(out_buffer, relation, members); + const bool okay = create_area(out_buffer, relation, members); if (okay) { out_buffer.commit(); } else { diff --git a/include/osmium/area/assembler_config.hpp b/include/osmium/area/assembler_config.hpp index fce53badb..5fa11d2ca 100644 --- a/include/osmium/area/assembler_config.hpp +++ b/include/osmium/area/assembler_config.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,8 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include - namespace osmium { namespace area { @@ -126,25 +124,6 @@ namespace osmium { AssemblerConfig() noexcept = default; - /** - * Constructor - * @deprecated Use default constructor and set values afterwards. - */ - explicit AssemblerConfig(ProblemReporter* pr, bool d = false) : - problem_reporter(pr), - debug_level(d) { - } - - /** - * Enable or disable debug output to stderr. This is for Osmium - * developers only. - * - * @deprecated Set debug_level directly. - */ - OSMIUM_DEPRECATED void enable_debug_output(bool d = true) { - debug_level = d; - } - }; // struct AssemblerConfig } // namespace area diff --git a/include/osmium/area/assembler_legacy.hpp b/include/osmium/area/assembler_legacy.hpp index 2531ad863..23bfa2bca 100644 --- a/include/osmium/area/assembler_legacy.hpp +++ b/include/osmium/area/assembler_legacy.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -72,10 +72,6 @@ namespace osmium { */ class AssemblerLegacy : public detail::BasicAssemblerWithTags { - void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Way& way) const { - builder.add_item(way.tags()); - } - void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set& ways) const { std::map counter; for (const osmium::Way* way : ways) { @@ -166,7 +162,7 @@ namespace osmium { const bool area_okay = create_rings(); if (area_okay || config().create_empty_areas) { - add_tags_to_area(builder, way); + builder.add_item(way.tags()); } if (area_okay) { add_rings_to_area(builder); @@ -336,12 +332,15 @@ namespace osmium { if (!way.nodes().empty() && way.is_closed() && !way.tags().empty()) { const auto d = std::count_if(way.tags().cbegin(), way.tags().cend(), std::cref(filter())); if (d > 0) { - osmium::tags::KeyFilter::iterator way_fi_begin(std::cref(filter()), way.tags().cbegin(), way.tags().cend()); - osmium::tags::KeyFilter::iterator way_fi_end(std::cref(filter()), way.tags().cend(), way.tags().cend()); - osmium::tags::KeyFilter::iterator area_fi_begin(std::cref(filter()), area_tags.cbegin(), area_tags.cend()); - osmium::tags::KeyFilter::iterator area_fi_end(std::cref(filter()), area_tags.cend(), area_tags.cend()); - - if (!std::equal(way_fi_begin, way_fi_end, area_fi_begin) || d != std::distance(area_fi_begin, area_fi_end)) { + const osmium::tags::KeyFilter::iterator way_fi_begin(std::cref(filter()), way.tags().cbegin(), way.tags().cend()); + const osmium::tags::KeyFilter::iterator way_fi_end(std::cref(filter()), way.tags().cend(), way.tags().cend()); + const osmium::tags::KeyFilter::iterator area_fi_begin(std::cref(filter()), area_tags.cbegin(), area_tags.cend()); + const osmium::tags::KeyFilter::iterator area_fi_end(std::cref(filter()), area_tags.cend(), area_tags.cend()); +#ifdef __cpp_lib_robust_nonmodifying_seq_ops + if (!std::equal(way_fi_begin, way_fi_end, area_fi_begin, area_fi_end)) { +#else + if (d != std::distance(area_fi_begin, area_fi_end) || !std::equal(way_fi_begin, way_fi_end, area_fi_begin)) { +#endif ways_that_should_be_areas.push_back(&way); } else { ++stats().inner_with_same_tags; diff --git a/include/osmium/area/detail/basic_assembler.hpp b/include/osmium/area/detail/basic_assembler.hpp index 485d22239..842136249 100644 --- a/include/osmium/area/detail/basic_assembler.hpp +++ b/include/osmium/area/detail/basic_assembler.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -51,6 +51,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include #include @@ -103,9 +104,19 @@ namespace osmium { */ class BasicAssembler { + static constexpr const std::size_t max_split_locations = 100ULL; + + // Maximum recursion depth, stops complex multipolygons from + // breaking everything. + enum : unsigned { + max_depth = 20U + }; + struct slocation { - static constexpr const uint32_t invalid_item = 1u << 30u; + enum { + invalid_item = 1U << 30U + }; uint32_t item : 31; uint32_t reverse : 1; @@ -185,7 +196,7 @@ namespace osmium { ++m_stats.wrong_role; if (debug()) { std::cerr << " Segment " << *segment << " from way " << segment->way()->id() << " has role '" << segment->role_name() - << "', but should have role '" << (ring.is_outer() ? "outer" : "inner") << "'\n"; + << "', but should have role '" << (ring.is_outer() ? "outer" : "inner") << "'\n"; } if (m_config.problem_reporter) { if (ring.is_outer()) { @@ -269,7 +280,7 @@ namespace osmium { using rings_stack = std::vector; - void remove_duplicates(rings_stack& outer_rings) { + static void remove_duplicates(rings_stack& outer_rings) { while (true) { const auto it = std::adjacent_find(outer_rings.begin(), outer_rings.end()); if (it == outer_rings.end()) { @@ -315,7 +326,7 @@ namespace osmium { const int64_t ay = a.y(); const int64_t by = b.y(); const int64_t ly = end_location.y(); - const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax); + const auto z = (bx - ax) * (ly - ay) - (by - ay) * (lx - ax); if (debug()) { std::cerr << " Segment z=" << z << '\n'; } @@ -342,7 +353,7 @@ namespace osmium { const int64_t ay = a.y(); const int64_t by = b.y(); const int64_t ly = location.y(); - const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax); + const auto z = (bx - ax) * (ly - ay) - (by - ay) * (lx - ax); if (z >= 0) { nesting += segment->is_reverse() ? -1 : 1; @@ -350,7 +361,8 @@ namespace osmium { std::cerr << " Segment is below (nesting=" << nesting << ")\n"; } if (segment->ring()->is_outer()) { - const double y = ay + (by - ay) * (lx - ax) / double(bx - ax); + const double y = static_cast(ay) + + static_cast((by - ay) * (lx - ax)) / static_cast(bx - ax); if (debug()) { std::cerr << " Segment belongs to outer ring (y=" << y << " ring=" << *segment->ring() << ")\n"; } @@ -373,7 +385,7 @@ namespace osmium { } assert(!outer_rings.empty()); - std::sort(outer_rings.rbegin(), outer_rings.rend()); + std::stable_sort(outer_rings.rbegin(), outer_rings.rend()); if (debug()) { for (const auto& o : outer_rings) { std::cerr << " y=" << o.y() << " " << o.ring() << "\n"; @@ -396,7 +408,7 @@ namespace osmium { return std::find(m_split_locations.cbegin(), m_split_locations.cend(), location) != m_split_locations.cend(); } - uint32_t add_new_ring(slocation& node) { + uint32_t add_new_ring(const slocation& node) { NodeRefSegment* segment = &m_segment_list[node.item]; assert(!segment->is_done()); @@ -454,7 +466,7 @@ namespace osmium { return nodes; } - uint32_t add_new_ring_complex(slocation& node) { + uint32_t add_new_ring_complex(const slocation& node) { NodeRefSegment* segment = &m_segment_list[node.item]; assert(!segment->is_done()); @@ -500,7 +512,12 @@ namespace osmium { void create_locations_list() { m_locations.reserve(m_segment_list.size() * 2); - for (uint32_t n = 0; n < m_segment_list.size(); ++n) { + // static_cast is okay here: The 32bit limit is way past + // anything that makes sense here and even if there are + // 2^32 segments here, it would simply not go through + // all of them not building the multipolygon correctly. + assert(m_segment_list.size() < std::numeric_limits::max()); + for (uint32_t n = 0; n < static_cast(m_segment_list.size()); ++n) { m_locations.emplace_back(n, false); m_locations.emplace_back(n, true); } @@ -536,7 +553,7 @@ namespace osmium { return; } - std::sort(rings.begin(), rings.end(), [](ProtoRing* a, ProtoRing* b) { + std::stable_sort(rings.begin(), rings.end(), [](ProtoRing* a, ProtoRing* b) { return a->min_segment() < b->min_segment(); }); @@ -576,7 +593,7 @@ namespace osmium { } ++m_stats.open_rings; } else { - if (loc == previous_location && (m_split_locations.empty() || m_split_locations.back() != previous_location )) { + if (loc == previous_location && (m_split_locations.empty() || m_split_locations.back() != previous_location)) { m_split_locations.push_back(previous_location); } ++it; @@ -591,7 +608,7 @@ namespace osmium { void create_rings_simple_case() { auto count_remaining = m_segment_list.size(); - for (slocation& sl : m_locations) { + for (const slocation& sl : m_locations) { const NodeRefSegment& segment = m_segment_list[sl.item]; if (!segment.is_done()) { count_remaining -= add_new_ring(sl); @@ -602,7 +619,7 @@ namespace osmium { } } - std::vector create_location_to_ring_map(open_ring_its_type& open_ring_its) { + std::vector create_location_to_ring_map(open_ring_its_type& open_ring_its) const { std::vector xrings; xrings.reserve(open_ring_its.size() * 2); @@ -614,7 +631,7 @@ namespace osmium { xrings.emplace_back((*it)->get_node_ref_stop().location(), it, false); } - std::sort(xrings.begin(), xrings.end()); + std::stable_sort(xrings.begin(), xrings.end()); return xrings; } @@ -654,7 +671,7 @@ namespace osmium { std::cerr << " Trying to merge " << open_ring_its.size() << " open rings (try_to_merge)\n"; } - std::vector xrings = create_location_to_ring_map(open_ring_its); + const auto xrings = create_location_to_ring_map(open_ring_its); auto it = xrings.cbegin(); while (it != xrings.cend()) { @@ -679,7 +696,7 @@ namespace osmium { } bool there_are_open_rings() const noexcept { - return std::any_of(m_rings.cbegin(), m_rings.cend(), [](const ProtoRing& ring){ + return std::any_of(m_rings.cbegin(), m_rings.cend(), [](const ProtoRing& ring) { return !ring.closed(); }); } @@ -703,7 +720,15 @@ namespace osmium { }; - void find_candidates(std::vector& candidates, std::unordered_set& loc_done, const std::vector& xrings, candidate& cand) { + struct exceeded_max_depth : public std::exception {}; + + using location_set = std::vector; + + void find_candidates(std::vector& candidates, location_set& loc_done, const std::vector& xrings, const candidate& cand, unsigned depth = 0) { + if (depth > max_depth) { + throw exceeded_max_depth{}; + } + if (debug()) { std::cerr << " find_candidates sum=" << cand.sum << " start=" << cand.start_location << " stop=" << cand.stop_location << "\n"; for (const auto& ring : cand.rings) { @@ -741,14 +766,32 @@ namespace osmium { if (debug()) { std::cerr << " found candidate\n"; } - candidates.push_back(c); - } else if (loc_done.count(c.stop_location) == 0) { - if (debug()) { - std::cerr << " recurse...\n"; + + if (candidates.empty()) { + candidates.push_back(c); + } else if (candidates.size() == 1) { + // add new candidate to vector, keep sorted + if (std::abs(c.sum) < std::abs(candidates.front().sum)) { + candidates.insert(candidates.begin(), c); + } else { + candidates.push_back(c); + } + } else { + // add new candidate if it has either smallest or largest area + if (std::abs(c.sum) < std::abs(candidates.front().sum)) { + candidates.front() = c; + } else if (std::abs(c.sum) > std::abs(candidates.back().sum)) { + candidates.back() = c; + } } - loc_done.insert(c.stop_location); - find_candidates(candidates, loc_done, xrings, c); - loc_done.erase(c.stop_location); + } else if (std::find(loc_done.cbegin(), loc_done.cend(), c.stop_location) == loc_done.cend()) { + if (debug()) { + std::cerr << " recurse... (depth=" << depth << " candidates.size=" << candidates.size() << " loc_done.size=" << loc_done.size() << ")\n"; + } + loc_done.push_back(c.stop_location); + find_candidates(candidates, loc_done, xrings, c, depth + 1); + assert(!loc_done.empty() && loc_done.back() == c.stop_location); + loc_done.pop_back(); if (debug()) { std::cerr << " ...back\n"; } @@ -782,7 +825,7 @@ namespace osmium { find_inner_outer_complex(); ProtoRing* outer_ring = find_enclosing_ring(ring_min->ring().min_segment()); - bool ring_min_is_outer = !outer_ring; + const bool ring_min_is_outer = !outer_ring; if (debug()) { std::cerr << " Open ring is " << (ring_min_is_outer ? "outer" : "inner") << " ring\n"; } @@ -790,16 +833,23 @@ namespace osmium { ring.reset(); } - candidate cand{*ring_min, false}; + const candidate cand{*ring_min, false}; // Locations we have visited while finding candidates, used // to detect loops. - std::unordered_set loc_done; + location_set loc_done; - loc_done.insert(cand.stop_location); + loc_done.push_back(cand.stop_location); std::vector candidates; - find_candidates(candidates, loc_done, xrings, cand); + try { + find_candidates(candidates, loc_done, xrings, cand); + } catch (const exceeded_max_depth&) { + if (m_config.debug_level > 0) { + std::cerr << " Exceeded max depth (" << static_cast(max_depth) << ")\n"; + } + return false; + } if (candidates.empty()) { if (debug()) { @@ -819,35 +869,29 @@ namespace osmium { if (debug()) { std::cerr << " Found candidates:\n"; - for (const auto& cand : candidates) { - std::cerr << " sum=" << cand.sum << "\n"; - for (const auto& ring : cand.rings) { + for (const auto& c : candidates) { + std::cerr << " sum=" << c.sum << "\n"; + for (const auto& ring : c.rings) { std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n"; } } } // Find the candidate with the smallest/largest area - const auto chosen_cand = ring_min_is_outer ? - std::min_element(candidates.cbegin(), candidates.cend(), [](const candidate& lhs, const candidate& rhs) { - return std::abs(lhs.sum) < std::abs(rhs.sum); - }) : - std::max_element(candidates.cbegin(), candidates.cend(), [](const candidate& lhs, const candidate& rhs) { - return std::abs(lhs.sum) < std::abs(rhs.sum); - }); + const auto chosen_cand = ring_min_is_outer ? candidates.front() : candidates.back(); if (debug()) { - std::cerr << " Decided on: sum=" << chosen_cand->sum << "\n"; - for (const auto& ring : chosen_cand->rings) { + std::cerr << " Decided on: sum=" << chosen_cand.sum << "\n"; + for (const auto& ring : chosen_cand.rings) { std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n"; } } // Join all (open) rings in the candidate to get one closed ring. - assert(chosen_cand->rings.size() > 1); - const auto& first_ring = chosen_cand->rings.front().first; + assert(chosen_cand.rings.size() > 1); + const auto& first_ring = chosen_cand.rings.front().first; const ProtoRing& remaining_ring = first_ring.ring(); - for (auto it = std::next(chosen_cand->rings.begin()); it != chosen_cand->rings.end(); ++it) { + for (auto it = std::next(chosen_cand.rings.begin()); it != chosen_cand.rings.end(); ++it) { merge_two_rings(open_ring_its, first_ring, it->first); } @@ -863,11 +907,11 @@ namespace osmium { auto count_remaining = m_segment_list.size(); for (const osmium::Location& location : m_split_locations) { const auto locs = make_range(std::equal_range(m_locations.begin(), - m_locations.end(), - slocation{}, - [this, &location](const slocation& lhs, const slocation& rhs) { - return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); - })); + m_locations.end(), + slocation{}, + [this, &location](const slocation& lhs, const slocation& rhs) { + return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); + })); for (auto& loc : locs) { if (!m_segment_list[loc.item].is_done()) { count_remaining -= add_new_ring_complex(loc); @@ -880,7 +924,7 @@ namespace osmium { // Now find all the rest of the rings (ie not starting at split locations) if (count_remaining > 0) { - for (slocation& sl : m_locations) { + for (const slocation& sl : m_locations) { const NodeRefSegment& segment = m_segment_list[sl.item]; if (!segment.is_done()) { count_remaining -= add_new_ring_complex(sl); @@ -1066,28 +1110,38 @@ namespace osmium { // whether there were any split locations or not. If there // are no splits, we use the faster "simple algorithm", if // there are, we use the slower "complex algorithm". - osmium::Timer timer_simple_case; - osmium::Timer timer_complex_case; + osmium::Timer timer; if (m_split_locations.empty()) { if (debug()) { std::cerr << " No split locations -> using simple algorithm\n"; } ++m_stats.area_simple_case; - timer_simple_case.start(); + timer.start(); create_rings_simple_case(); - timer_simple_case.stop(); + timer.stop(); + } else if (m_split_locations.size() > max_split_locations) { + if (m_config.debug_level > 0) { + std::cerr << " Ignoring polygon with " + << m_split_locations.size() + << " split locations (>" + << max_split_locations + << ")\n"; + } + return false; } else { - if (debug()) { - std::cerr << " Found split locations -> using complex algorithm\n"; + if (m_config.debug_level > 0) { + std::cerr << " Found " + << m_split_locations.size() + << " split locations -> using complex algorithm\n"; } ++m_stats.area_touching_rings_case; - timer_complex_case.start(); + timer.start(); if (!create_rings_complex_case()) { return false; } - timer_complex_case.stop(); + timer.stop(); } // If the assembler was so configured, now check whether the @@ -1098,7 +1152,7 @@ namespace osmium { timer_roles.stop(); } - m_stats.outer_rings = std::count_if(m_rings.cbegin(), m_rings.cend(), [](const ProtoRing& ring){ + m_stats.outer_rings = std::count_if(m_rings.cbegin(), m_rings.cend(), [](const ProtoRing& ring) { return ring.is_outer(); }); m_stats.inner_rings = m_rings.size() - m_stats.outer_rings; @@ -1112,11 +1166,9 @@ namespace osmium { ' ' << timer_split.elapsed_microseconds(); if (m_split_locations.empty()) { - std::cout << ' ' << timer_simple_case.elapsed_microseconds() << - " 0"; + std::cout << ' ' << timer.elapsed_microseconds() << " 0"; } else { - std::cout << " 0" << - ' ' << timer_complex_case.elapsed_microseconds(); + std::cout << " 0" << ' ' << timer.elapsed_microseconds(); } std::cout << diff --git a/include/osmium/area/detail/basic_assembler_with_tags.hpp b/include/osmium/area/detail/basic_assembler_with_tags.hpp index 821e914de..b5a173b44 100644 --- a/include/osmium/area/detail/basic_assembler_with_tags.hpp +++ b/include/osmium/area/detail/basic_assembler_with_tags.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -56,13 +56,13 @@ namespace osmium { return false; } return stats().duplicate_nodes || - stats().duplicate_segments || - stats().intersections || - stats().open_rings || - stats().short_ways || - stats().touching_rings || - stats().ways_in_multiple_rings || - stats().wrong_role; + stats().duplicate_segments || + stats().intersections || + stats().open_rings || + stats().short_ways || + stats().touching_rings || + stats().ways_in_multiple_rings || + stats().wrong_role; } static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) { diff --git a/include/osmium/area/detail/node_ref_segment.hpp b/include/osmium/area/detail/node_ref_segment.hpp index dc3782dbe..8da4f8ccf 100644 --- a/include/osmium/area/detail/node_ref_segment.hpp +++ b/include/osmium/area/detail/node_ref_segment.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -38,6 +38,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include #include @@ -101,14 +102,10 @@ namespace osmium { NodeRefSegment() noexcept = default; NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, role_type role, const osmium::Way* way) noexcept : - m_first(nr1), - m_second(nr2), + m_first(nr1.location() < nr2.location() ? nr1 : nr2), + m_second(nr1.location() < nr2.location() ? nr2 : nr1), m_way(way), m_role(role) { - if (nr2.location() < nr1.location()) { - using std::swap; - swap(m_first, m_second); - } } /** @@ -195,8 +192,8 @@ namespace osmium { } const char* role_name() const noexcept { - static const char* names[] = { "unknown", "outer", "inner", "empty" }; - return names[int(m_role)]; + static const std::array names = {{"unknown", "outer", "inner", "empty"}}; + return names[static_cast(m_role)]; } const osmium::Way* way() const noexcept { @@ -205,7 +202,7 @@ namespace osmium { /** * The "determinant" of this segment. Used for calculating - * the winding order or a ring. + * the winding order of a ring. */ int64_t det() const noexcept { const vec a{start()}; @@ -222,7 +219,7 @@ namespace osmium { } inline bool operator!=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { - return ! (lhs == rhs); + return !(lhs == rhs); } /** @@ -330,9 +327,9 @@ namespace osmium { if ((d > 0 && na >= 0 && na <= d && nb >= 0 && nb <= d) || (d < 0 && na <= 0 && na >= d && nb <= 0 && nb >= d)) { - const double ua = double(na) / d; + const double ua = static_cast(na) / static_cast(d); const vec i = p0 + ua * (p1 - p0); - return osmium::Location{int32_t(i.x), int32_t(i.y)}; + return osmium::Location{static_cast(i.x), static_cast(i.y)}; } return osmium::Location{}; @@ -348,13 +345,14 @@ namespace osmium { osmium::Location location; }; - seg_loc sl[4]; - sl[0] = {0, s1.first().location() }; - sl[1] = {0, s1.second().location()}; - sl[2] = {1, s2.first().location() }; - sl[3] = {1, s2.second().location()}; + std::array sl = {{ + {0, s1.first().location()}, + {0, s1.second().location()}, + {1, s2.first().location()}, + {1, s2.second().location()}, + }}; - std::sort(sl, sl+4, [](const seg_loc& lhs, const seg_loc& rhs) { + std::sort(sl.begin(), sl.end(), [](const seg_loc& lhs, const seg_loc& rhs) { return lhs.location < rhs.location; }); diff --git a/include/osmium/area/detail/proto_ring.hpp b/include/osmium/area/detail/proto_ring.hpp index 6be29f94d..9b773a0a9 100644 --- a/include/osmium/area/detail/proto_ring.hpp +++ b/include/osmium/area/detail/proto_ring.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -86,16 +86,16 @@ namespace osmium { int64_t m_num; #endif - int64_t m_sum; + int64_t m_sum = 0; public: explicit ProtoRing(NodeRefSegment* segment) noexcept : - m_min_segment(segment), + m_min_segment(segment) #ifdef OSMIUM_DEBUG_RING_NO - m_num(next_num()), + , m_num(next_num()) #endif - m_sum(0) { + { add_segment_back(segment); } @@ -196,12 +196,14 @@ namespace osmium { } void join_forward(ProtoRing& other) { + m_segments.reserve(m_segments.size() + other.m_segments.size()); for (NodeRefSegment* segment : other.m_segments) { add_segment_back(segment); } } void join_backward(ProtoRing& other) { + m_segments.reserve(m_segments.size() + other.m_segments.size()); for (auto it = other.m_segments.rbegin(); it != other.m_segments.rend(); ++it) { (*it)->reverse(); add_segment_back(*it); diff --git a/include/osmium/area/detail/segment_list.hpp b/include/osmium/area/detail/segment_list.hpp index 776507b46..53f203215 100644 --- a/include/osmium/area/detail/segment_list.hpp +++ b/include/osmium/area/detail/segment_list.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -147,14 +147,14 @@ namespace osmium { m_debug(debug) { } - ~SegmentList() noexcept = default; - SegmentList(const SegmentList&) = delete; SegmentList(SegmentList&&) = delete; SegmentList& operator=(const SegmentList&) = delete; SegmentList& operator=(SegmentList&&) = delete; + ~SegmentList() noexcept = default; + /// The number of segments in the list. std::size_t size() const noexcept { return m_segments.size(); @@ -181,7 +181,7 @@ namespace osmium { return m_segments[n]; } - NodeRefSegment& operator[](std::size_t n) noexcept { + NodeRefSegment& operator[](const std::size_t n) noexcept { assert(n < m_segments.size()); return m_segments[n]; } @@ -206,7 +206,7 @@ namespace osmium { * Enable or disable debug output to stderr. This is used * for debugging libosmium itself. */ - void enable_debug_output(bool debug = true) noexcept { + void enable_debug_output(const bool debug = true) noexcept { m_debug = debug; } @@ -296,14 +296,14 @@ namespace osmium { } } - if (it+2 != m_segments.end() && *it == *(it+2)) { + if (it + 2 != m_segments.end() && *it == *(it + 2)) { ++overlapping_segments; if (problem_reporter) { problem_reporter->report_overlapping_segment(it->first(), it->second()); } } - m_segments.erase(it, it+2); + m_segments.erase(it, it + 2); } } @@ -323,7 +323,7 @@ namespace osmium { for (auto it1 = m_segments.cbegin(); it1 != m_segments.cend() - 1; ++it1) { const NodeRefSegment& s1 = *it1; - for (auto it2 = it1+1; it2 != m_segments.end(); ++it2) { + for (auto it2 = it1 + 1; it2 != m_segments.end(); ++it2) { const NodeRefSegment& s2 = *it2; assert(s1 != s2); // erase_duplicate_segments() should have made sure of that @@ -333,7 +333,7 @@ namespace osmium { } if (y_range_overlap(s1, s2)) { - osmium::Location intersection{calculate_intersection(s1, s2)}; + const osmium::Location intersection{calculate_intersection(s1, s2)}; if (intersection) { ++found_intersections; if (m_debug) { diff --git a/include/osmium/area/detail/vector.hpp b/include/osmium/area/detail/vector.hpp index dae6c6347..e52a65cc5 100644 --- a/include/osmium/area/detail/vector.hpp +++ b/include/osmium/area/detail/vector.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -89,12 +89,12 @@ namespace osmium { // scale vector constexpr inline vec operator*(double s, const vec& v) noexcept { - return vec{int64_t(s * v.x), int64_t(s * v.y)}; + return vec{static_cast(s * static_cast(v.x)), static_cast(s * static_cast(v.y))}; } // scale vector constexpr inline vec operator*(const vec& v, double s) noexcept { - return vec{int64_t(s * v.x), int64_t(s * v.y)}; + return vec{static_cast(s * static_cast(v.x)), static_cast(s * static_cast(v.y))}; } // equality diff --git a/include/osmium/area/geom_assembler.hpp b/include/osmium/area/geom_assembler.hpp index 4c9fc276e..6a665b4a9 100644 --- a/include/osmium/area/geom_assembler.hpp +++ b/include/osmium/area/geom_assembler.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -68,8 +68,6 @@ namespace osmium { detail::BasicAssembler(config) { } - ~GeomAssembler() noexcept = default; - /** * Assemble an area from the given way. * diff --git a/include/osmium/area/multipolygon_collector.hpp b/include/osmium/area/multipolygon_collector.hpp index d57294120..a2a70b69f 100644 --- a/include/osmium/area/multipolygon_collector.hpp +++ b/include/osmium/area/multipolygon_collector.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -70,6 +70,8 @@ namespace osmium { * * @tparam TAssembler Multipolygon Assembler class. * @pre The Ids of all objects must be unique in the input data. + * + * @deprecated Use MultipolygonManager instead. */ template class MultipolygonCollector : public osmium::relations::Collector, false, true, false> { @@ -83,8 +85,13 @@ namespace osmium { area_stats m_stats; - static constexpr size_t initial_output_buffer_size = 1024 * 1024; - static constexpr size_t max_buffer_size_for_flush = 100 * 1024; + enum { + initial_output_buffer_size = 1024UL * 1024UL + }; + + enum { + max_buffer_size_for_flush = 100UL * 1024UL + }; void flush_output_buffer() { if (this->callback()) { @@ -165,7 +172,7 @@ namespace osmium { } } - void complete_relation(osmium::relations::RelationMeta& relation_meta) { + void complete_relation(const osmium::relations::RelationMeta& relation_meta) { const osmium::Relation& relation = this->get_relation(relation_meta); const osmium::memory::Buffer& buffer = this->members_buffer(); diff --git a/include/osmium/area/multipolygon_manager.hpp b/include/osmium/area/multipolygon_manager.hpp index dd3c4d875..c478aa158 100644 --- a/include/osmium/area/multipolygon_manager.hpp +++ b/include/osmium/area/multipolygon_manager.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -63,8 +63,8 @@ namespace osmium { /** * This class collects all data needed for creating areas from * relations tagged with type=multipolygon or type=boundary. - * Most of its functionality is derived from the parent class - * osmium::relations::Collector. + * Most of its functionality is derived from the parent template class + * osmium::relations::RelationsManager. * * The actual assembling of the areas is done by the assembler * class given as template argument. diff --git a/include/osmium/area/multipolygon_manager_legacy.hpp b/include/osmium/area/multipolygon_manager_legacy.hpp index c611d3447..feea9a04b 100644 --- a/include/osmium/area/multipolygon_manager_legacy.hpp +++ b/include/osmium/area/multipolygon_manager_legacy.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/area/problem_reporter.hpp b/include/osmium/area/problem_reporter.hpp index 36f0833fa..4c32ed970 100644 --- a/include/osmium/area/problem_reporter.hpp +++ b/include/osmium/area/problem_reporter.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -80,7 +80,7 @@ namespace osmium { ProblemReporter(ProblemReporter&&) noexcept = default; ProblemReporter& operator=(ProblemReporter&&) noexcept = default; - virtual ~ProblemReporter() = default; + virtual ~ProblemReporter() noexcept = default; /** * Set the object the next problem reports will be on. @@ -93,6 +93,10 @@ namespace osmium { m_object_id = object_id; } + osmium::object_id_type object_id() const noexcept { + return m_object_id; + } + void set_nodes(size_t nodes) noexcept { m_nodes = nodes; } diff --git a/include/osmium/area/problem_reporter_exception.hpp b/include/osmium/area/problem_reporter_exception.hpp index 2224f66ea..4527a9984 100644 --- a/include/osmium/area/problem_reporter_exception.hpp +++ b/include/osmium/area/problem_reporter_exception.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/area/problem_reporter_ogr.hpp b/include/osmium/area/problem_reporter_ogr.hpp index 748cf059c..bad49f89c 100644 --- a/include/osmium/area/problem_reporter_ogr.hpp +++ b/include/osmium/area/problem_reporter_ogr.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -75,15 +75,15 @@ namespace osmium { void set_object(gdalcpp::Feature& feature) { const char t[2] = {osmium::item_type_to_char(m_object_type), '\0'}; feature.set_field("obj_type", t); - feature.set_field("obj_id", int32_t(m_object_id)); - feature.set_field("nodes", int32_t(m_nodes)); + feature.set_field("obj_id", static_cast(m_object_id)); + feature.set_field("nodes", static_cast(m_nodes)); } void write_point(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location location) { gdalcpp::Feature feature{m_layer_perror, m_ogr_factory.create_point(location)}; set_object(feature); - feature.set_field("id1", double(id1)); - feature.set_field("id2", double(id2)); + feature.set_field("id1", static_cast(id1)); + feature.set_field("id2", static_cast(id2)); feature.set_field("problem", problem_type); feature.add_to_layer(); } @@ -117,8 +117,7 @@ namespace osmium { .add_field("nodes", OFTInteger, 8) .add_field("id1", OFTReal, 12, 1) .add_field("id2", OFTReal, 12, 1) - .add_field("problem", OFTString, 30) - ; + .add_field("problem", OFTString, 30); m_layer_lerror .add_field("obj_type", OFTString, 1) @@ -126,15 +125,13 @@ namespace osmium { .add_field("nodes", OFTInteger, 8) .add_field("id1", OFTReal, 12, 1) .add_field("id2", OFTReal, 12, 1) - .add_field("problem", OFTString, 30) - ; + .add_field("problem", OFTString, 30); m_layer_ways .add_field("obj_type", OFTString, 1) .add_field("obj_id", OFTInteger, 10) .add_field("way_id", OFTInteger, 10) - .add_field("nodes", OFTInteger, 8) - ; + .add_field("nodes", OFTInteger, 8); } void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override { @@ -179,7 +176,7 @@ namespace osmium { try { gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)}; set_object(feature); - feature.set_field("id1", int32_t(way.id())); + feature.set_field("id1", static_cast(way.id())); feature.set_field("id2", 0); feature.set_field("problem", "way_in_multiple_rings"); feature.add_to_layer(); @@ -195,7 +192,7 @@ namespace osmium { try { gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)}; set_object(feature); - feature.set_field("id1", int32_t(way.id())); + feature.set_field("id1", static_cast(way.id())); feature.set_field("id2", 0); feature.set_field("problem", "inner_with_same_tags"); feature.add_to_layer(); @@ -211,7 +208,7 @@ namespace osmium { try { gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)}; set_object(feature); - feature.set_field("id1", int32_t(way.id())); + feature.set_field("id1", static_cast(way.id())); feature.set_field("id2", 0); feature.set_field("problem", "duplicate_way"); feature.add_to_layer(); @@ -232,7 +229,7 @@ namespace osmium { try { gdalcpp::Feature feature{m_layer_ways, m_ogr_factory.create_linestring(way)}; set_object(feature); - feature.set_field("way_id", int32_t(way.id())); + feature.set_field("way_id", static_cast(way.id())); feature.add_to_layer(); } catch (const osmium::geometry_error&) { // XXX diff --git a/include/osmium/area/problem_reporter_stream.hpp b/include/osmium/area/problem_reporter_stream.hpp index 09c48d5dc..9fa2ccdb7 100644 --- a/include/osmium/area/problem_reporter_stream.hpp +++ b/include/osmium/area/problem_reporter_stream.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/area/stats.hpp b/include/osmium/area/stats.hpp index 780411181..d081e6110 100644 --- a/include/osmium/area/stats.hpp +++ b/include/osmium/area/stats.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/builder/attr.hpp b/include/osmium/builder/attr.hpp index 2dd6239ea..b11272659 100644 --- a/include/osmium/builder/attr.hpp +++ b/include/osmium/builder/attr.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -45,6 +45,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include @@ -226,8 +227,8 @@ namespace osmium { OSMIUM_ATTRIBUTE(node_handler, _location, osmium::Location) constexpr explicit _location(const osmium::Location& value) noexcept : type_wrapper(value) {} - explicit _location(double lat, double lon) : - type_wrapper(osmium::Location{lat, lon}) {} + explicit _location(double lon, double lat) : + type_wrapper(osmium::Location{lon, lat}) {} }; OSMIUM_ATTRIBUTE(entity_handler, _user, const char*) @@ -254,6 +255,10 @@ namespace osmium { m_role(role) { } + member_type(char type, osmium::object_id_type ref, const char* role = "") noexcept : + member_type(osmium::char_to_item_type(type), ref, role) { + } + constexpr osmium::item_type type() const noexcept { return m_type; } @@ -282,6 +287,10 @@ namespace osmium { m_role(std::move(role)) { } + member_type_string(char type, osmium::object_id_type ref, std::string&& role) noexcept : + member_type_string(osmium::char_to_item_type(type), ref, std::move(role)) { + } + osmium::item_type type() const noexcept { return m_type; } @@ -360,6 +369,15 @@ namespace osmium { type_wrapper(std::make_pair(key, val)) {} explicit _tag(const std::string& key, const std::string& val) : type_wrapper(std::make_pair(key.c_str(), val.c_str())) {} + explicit _tag(const char* const key_value) : + type_wrapper(pair_of_cstrings{key_value, nullptr}) {} + explicit _tag(const std::string& key_value) : + type_wrapper(pair_of_cstrings{key_value.c_str(), nullptr}) {} + }; + + OSMIUM_ATTRIBUTE(tags_handler, _t, const char*) + explicit _t(const char *tags) : + type_wrapper(tags) {} }; template @@ -659,7 +677,19 @@ namespace osmium { } static void set_value(TagListBuilder& builder, const attr::_tag& tag) { - builder.add_tag(tag.value); + if (tag.value.second != nullptr) { + builder.add_tag(tag.value); + return; + } + const char* key = tag.value.first; + const char* const equal_sign = std::strchr(key, '='); + if (!equal_sign) { + builder.add_tag(key, ""); + return; + } + const char* value = equal_sign + 1; + builder.add_tag(key, equal_sign - key, + value, std::strlen(value)); } template @@ -669,6 +699,21 @@ namespace osmium { } } + static void set_value(TagListBuilder& builder, const attr::_t& tags) { + const auto taglist = osmium::split_string(tags.value, ',', true); + for (const auto& tag : taglist) { + const std::size_t pos = tag.find_first_of('='); + if (pos == std::string::npos) { + builder.add_tag(tag, ""); + } else { + const char* value = tag.c_str() + pos + 1; + builder.add_tag(tag.c_str(), pos, + value, tag.size() - pos - 1); + } + + } + } + }; // struct tags_handler struct nodes_handler { @@ -764,7 +809,7 @@ namespace osmium { template inline typename std::enable_if::value>::type add_list(osmium::builder::Builder& parent, const TArgs&... args) { - TBuilder builder(parent.buffer(), &parent); + TBuilder builder{parent.buffer(), &parent}; (void)std::initializer_list{ (THandler::set_value(builder, args), 0)... }; @@ -792,7 +837,7 @@ namespace osmium { static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_node()"); { - NodeBuilder builder(buffer); + NodeBuilder builder{buffer}; detail::add_basic(builder, args...); detail::add_user(builder, args...); @@ -815,7 +860,7 @@ namespace osmium { static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_way()"); { - WayBuilder builder(buffer); + WayBuilder builder{buffer}; detail::add_basic(builder, args...); detail::add_user(builder, args...); @@ -839,7 +884,7 @@ namespace osmium { static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_relation()"); { - RelationBuilder builder(buffer); + RelationBuilder builder{buffer}; detail::add_basic(builder, args...); detail::add_user(builder, args...); @@ -863,7 +908,7 @@ namespace osmium { static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_changeset()"); { - ChangesetBuilder builder(buffer); + ChangesetBuilder builder{buffer}; detail::add_basic(builder, args...); detail::add_user(builder, args...); @@ -887,7 +932,7 @@ namespace osmium { static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_area()"); { - AreaBuilder builder(buffer); + AreaBuilder builder{buffer}; detail::add_basic(builder, args...); detail::add_user(builder, args...); @@ -914,7 +959,7 @@ namespace osmium { static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_way_node_list()"); { - WayNodeListBuilder builder(buffer); + WayNodeListBuilder builder{buffer}; (void)std::initializer_list{ (detail::nodes_handler::set_value(builder, args), 0)... }; @@ -936,7 +981,7 @@ namespace osmium { static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_tag_list()"); { - TagListBuilder builder(buffer); + TagListBuilder builder{buffer}; (void)std::initializer_list{ (detail::tags_handler::set_value(builder, args), 0)... }; diff --git a/include/osmium/builder/builder.hpp b/include/osmium/builder/builder.hpp index ba0443700..feda7c36e 100644 --- a/include/osmium/builder/builder.hpp +++ b/include/osmium/builder/builder.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include @@ -65,7 +64,7 @@ namespace osmium { explicit Builder(osmium::memory::Buffer& buffer, Builder* parent, osmium::memory::item_size_type size) : m_buffer(buffer), m_parent(parent), - m_item_offset(buffer.written()) { + m_item_offset(buffer.written() - buffer.committed()) { reserve_space(size); assert(buffer.is_aligned()); if (m_parent) { @@ -80,15 +79,19 @@ namespace osmium { } #ifdef NDEBUG - ~Builder() = default; + ~Builder() noexcept = default; #else ~Builder() noexcept { m_buffer.decrement_builder_count(); } #endif - osmium::memory::Item& item() const { - return *reinterpret_cast(m_buffer.data() + m_item_offset); + unsigned char* item_pos() const noexcept { + return m_buffer.data() + m_buffer.committed() + m_item_offset; + } + + osmium::memory::Item& item() const noexcept { + return *reinterpret_cast(item_pos()); } unsigned char* reserve_space(std::size_t size) { @@ -183,18 +186,6 @@ namespace osmium { return append(str, static_cast(std::strlen(str) + 1)); } - /** - * Append '\0' to the buffer. - * - * @deprecated Use append_with_zero() instead. - * - * @returns The number of bytes appended (always 1). - */ - OSMIUM_DEPRECATED osmium::memory::item_size_type append_zero() { - *reserve_space(1) = '\0'; - return 1; - } - public: Builder(const Builder&) = delete; @@ -217,15 +208,6 @@ namespace osmium { add_size(item.padded_size()); } - /** - * @deprecated Use the version of add_item() taking a - * reference instead. - */ - OSMIUM_DEPRECATED void add_item(const osmium::memory::Item* item) { - assert(item); - add_item(*item); - } - }; // class Builder } // namespace builder diff --git a/include/osmium/builder/builder_helper.hpp b/include/osmium/builder/builder_helper.hpp deleted file mode 100644 index b6c929680..000000000 --- a/include/osmium/builder/builder_helper.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#ifndef OSMIUM_BUILDER_BUILDER_HELPER_HPP -#define OSMIUM_BUILDER_BUILDER_HELPER_HPP - -/* - -This file is part of Osmium (http://osmcode.org/libosmium). - -Copyright 2013-2018 Jochen Topf and others (see README). - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - -*/ - -#include -#include -#include - -#include -#include -#include -#include -#include - -namespace osmium { - - class NodeRef; - class TagList; - class WayNodeList; - - namespace builder { - - /** - * @deprecated - * Use osmium::builder::add_way_node_list() instead. - */ - OSMIUM_DEPRECATED inline const osmium::WayNodeList& build_way_node_list(osmium::memory::Buffer& buffer, const std::initializer_list& nodes) { - const size_t pos = buffer.committed(); - { - osmium::builder::WayNodeListBuilder wnl_builder(buffer); - for (const auto& node_ref : nodes) { - wnl_builder.add_node_ref(node_ref); - } - } - buffer.commit(); - return buffer.get(pos); - } - - /** - * @deprecated - * Use osmium::builder::add_tag_list() instead. - */ - OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list(osmium::memory::Buffer& buffer, const std::initializer_list>& tags) { - const size_t pos = buffer.committed(); - { - osmium::builder::TagListBuilder tl_builder(buffer); - for (const auto& p : tags) { - tl_builder.add_tag(p.first, p.second); - } - } - buffer.commit(); - return buffer.get(pos); - } - - /** - * @deprecated - * Use osmium::builder::add_tag_list() instead. - */ - OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list_from_map(osmium::memory::Buffer& buffer, const std::map& tags) { - const size_t pos = buffer.committed(); - { - osmium::builder::TagListBuilder tl_builder(buffer); - for (const auto& p : tags) { - tl_builder.add_tag(p.first, p.second); - } - } - buffer.commit(); - return buffer.get(pos); - } - - /** - * @deprecated - * Use osmium::builder::add_tag_list() instead. - */ - OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list_from_func(osmium::memory::Buffer& buffer, const std::function& func) { - const size_t pos = buffer.committed(); - { - osmium::builder::TagListBuilder tl_builder(buffer); - func(tl_builder); - } - buffer.commit(); - return buffer.get(pos); - } - - } // namespace builder - -} // namespace osmium - -#endif // OSMIUM_BUILDER_BUILDER_HELPER_HPP diff --git a/include/osmium/builder/osm_object_builder.hpp b/include/osmium/builder/osm_object_builder.hpp index dce4d1b01..c3228ca7d 100644 --- a/include/osmium/builder/osm_object_builder.hpp +++ b/include/osmium/builder/osm_object_builder.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -48,7 +48,6 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include @@ -126,8 +125,8 @@ namespace osmium { if (value_length > osmium::max_osm_string_length) { throw std::length_error{"OSM tag value is too long"}; } - add_size(append_with_zero(key, osmium::memory::item_size_type(key_length))); - add_size(append_with_zero(value, osmium::memory::item_size_type(value_length))); + add_size(append_with_zero(key, static_cast(key_length))); + add_size(append_with_zero(value, static_cast(value_length))); } /** @@ -143,8 +142,8 @@ namespace osmium { if (value.size() > osmium::max_osm_string_length) { throw std::length_error{"OSM tag value is too long"}; } - add_size(append(key.data(), osmium::memory::item_size_type(key.size()) + 1)); - add_size(append(value.data(), osmium::memory::item_size_type(value.size()) + 1)); + add_size(append(key.data(), static_cast(key.size()) + 1)); + add_size(append(value.data(), static_cast(value.size()) + 1)); } /** @@ -241,8 +240,8 @@ namespace osmium { if (length > osmium::max_osm_string_length) { throw std::length_error{"OSM relation member role is too long"}; } - member.set_role_size(osmium::string_size_type(length) + 1); - add_size(append_with_zero(role, osmium::memory::item_size_type(length))); + member.set_role_size(static_cast(length) + 1); + add_size(append_with_zero(role, static_cast(length))); add_padding(true); } @@ -331,16 +330,16 @@ namespace osmium { if (length > osmium::max_osm_string_length) { throw std::length_error{"OSM user name is too long"}; } - comment.set_user_size(osmium::string_size_type(length) + 1); - add_size(append_with_zero(user, osmium::memory::item_size_type(length))); + comment.set_user_size(static_cast(length) + 1); + add_size(append_with_zero(user, static_cast(length))); } void add_text(osmium::ChangesetComment& comment, const char* text, const std::size_t length) { if (length > std::numeric_limits::max() - 1) { throw std::length_error{"OSM changeset comment is too long"}; } - comment.set_text_size(osmium::changeset_comment_size_type(length) + 1); - add_size(append_with_zero(text, osmium::memory::item_size_type(length))); + comment.set_text_size(static_cast(length) + 1); + add_size(append_with_zero(text, static_cast(length))); add_padding(true); } @@ -405,18 +404,22 @@ namespace osmium { constexpr static const std::size_t min_size_for_user = osmium::memory::padded_length(sizeof(string_size_type) + 1); + void set_user_size(string_size_type size) noexcept { + std::memcpy(item_pos() + sizeof(T), &size, sizeof(string_size_type)); + } + public: explicit OSMObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : Builder(buffer, parent, sizeof(T) + min_size_for_user) { new (&item()) T{}; add_size(min_size_for_user); - std::fill_n(object().data() + sizeof(T), min_size_for_user, 0); - object().set_user_size(1); + std::memset(object().data() + sizeof(T), 0, min_size_for_user); + set_user_size(1); } /** - * Get a reference to the object buing built. + * Get a reference to the object being built. * * Note that this reference will be invalidated by every action * on the builder that might make the buffer grow. This includes @@ -427,7 +430,7 @@ namespace osmium { } /** - * Get a const reference to the object buing built. + * Get a const reference to the object being built. * * Note that this reference will be invalidated by every action * on the builder that might make the buffer grow. This includes @@ -447,14 +450,14 @@ namespace osmium { const auto size_of_object = sizeof(T) + sizeof(string_size_type); assert(cobject().user_size() == 1 && (size() <= size_of_object + osmium::memory::padded_length(1)) && "set_user() must be called at most once and before any sub-builders"); - const auto available_space = min_size_for_user - sizeof(string_size_type) - 1; + constexpr const auto available_space = min_size_for_user - sizeof(string_size_type) - 1; if (length > available_space) { const auto space_needed = osmium::memory::padded_length(length - available_space); - std::fill_n(reserve_space(space_needed), space_needed, 0); + std::memset(reserve_space(space_needed), 0, space_needed); add_size(static_cast(space_needed)); } - std::copy_n(user, length, object().data() + size_of_object); - object().set_user_size(length + 1); + std::memcpy(object().data() + size_of_object, user, length); + set_user_size(length + 1); return static_cast(*this); } @@ -484,12 +487,6 @@ namespace osmium { return set_user(user.data(), static_cast(user.size())); } - /// @deprecated Use set_user(...) instead. - template - OSMIUM_DEPRECATED void add_user(TArgs&&... args) { - set_user(std::forward(args)...); - } - OSMIUM_FORWARD(set_id) OSMIUM_FORWARD(set_visible) OSMIUM_FORWARD(set_deleted) @@ -608,7 +605,7 @@ namespace osmium { Builder(buffer, parent, sizeof(Changeset) + min_size_for_user) { new (&item()) Changeset{}; add_size(min_size_for_user); - std::fill_n(object().data() + sizeof(Changeset), min_size_for_user, 0); + std::memset(object().data() + sizeof(Changeset), 0, min_size_for_user); object().set_user_size(1); } @@ -644,11 +641,6 @@ namespace osmium { OSMIUM_FORWARD(set_attribute) OSMIUM_FORWARD(set_removed) - // @deprecated Use set_bounds() instead. - OSMIUM_DEPRECATED osmium::Box& bounds() noexcept { - return object().bounds(); - } - ChangesetBuilder& set_bounds(const osmium::Box& box) noexcept { object().bounds() = box; return *this; @@ -663,13 +655,13 @@ namespace osmium { ChangesetBuilder& set_user(const char* user, const string_size_type length) { assert(cobject().user_size() == 1 && (size() <= sizeof(Changeset) + osmium::memory::padded_length(1)) && "set_user() must be called at most once and before any sub-builders"); - const auto available_space = min_size_for_user - 1; + constexpr const auto available_space = min_size_for_user - 1; if (length > available_space) { const auto space_needed = osmium::memory::padded_length(length - available_space); - std::fill_n(reserve_space(space_needed), space_needed, 0); + std::memset(reserve_space(space_needed), 0, space_needed); add_size(static_cast(space_needed)); } - std::copy_n(user, length, object().data() + sizeof(Changeset)); + std::memcpy(object().data() + sizeof(Changeset), user, length); object().set_user_size(length + 1); return *this; @@ -700,12 +692,6 @@ namespace osmium { return set_user(user.data(), static_cast(user.size())); } - /// @deprecated Use set_user(...) instead. - template - OSMIUM_DEPRECATED void add_user(TArgs&&... args) { - set_user(std::forward(args)...); - } - }; // class ChangesetBuilder #undef OSMIUM_FORWARD diff --git a/include/osmium/diff_handler.hpp b/include/osmium/diff_handler.hpp index 639a5f45f..1f88f768d 100644 --- a/include/osmium/diff_handler.hpp +++ b/include/osmium/diff_handler.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -48,13 +48,13 @@ namespace osmium { DiffHandler() = default; - void node(const osmium::DiffNode&) const noexcept { + void node(const osmium::DiffNode& /*node*/) const noexcept { } - void way(const osmium::DiffWay&) const noexcept { + void way(const osmium::DiffWay& /*way*/) const noexcept { } - void relation(const osmium::DiffRelation&) const noexcept { + void relation(const osmium::DiffRelation& /*relation*/) const noexcept { } }; // class DiffHandler diff --git a/include/osmium/diff_iterator.hpp b/include/osmium/diff_iterator.hpp index 0ea5f8445..08b8ea09b 100644 --- a/include/osmium/diff_iterator.hpp +++ b/include/osmium/diff_iterator.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -49,6 +49,9 @@ namespace osmium { * An input iterator wrapping any iterator over OSMObjects. When * dereferenced it will yield DiffObject objects pointing to the * underlying OSMObjects. + * + * Note that this class uses a mutable member variable internally. + * It can not be used safely in multiple threads! */ template class DiffIterator { @@ -88,8 +91,7 @@ namespace osmium { m_prev(begin), m_curr(begin), m_next(begin == end ? begin : ++begin), - m_end(std::move(end)), - m_diff() { + m_end(std::move(end)) { } DiffIterator& operator++() { diff --git a/include/osmium/diff_visitor.hpp b/include/osmium/diff_visitor.hpp index 1db5ce295..976c63313 100644 --- a/include/osmium/diff_visitor.hpp +++ b/include/osmium/diff_visitor.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/dynamic_handler.hpp b/include/osmium/dynamic_handler.hpp index 96906760b..73f2ac54a 100644 --- a/include/osmium/dynamic_handler.hpp +++ b/include/osmium/dynamic_handler.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -62,7 +62,7 @@ namespace osmium { HandlerWrapperBase(HandlerWrapperBase&&) noexcept = default; HandlerWrapperBase& operator=(HandlerWrapperBase&&) noexcept = default; - virtual ~HandlerWrapperBase() = default; + virtual ~HandlerWrapperBase() noexcept = default; virtual void node(const osmium::Node& /*node*/) { } @@ -126,6 +126,14 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> m_handler(std::forward(args)...) { } + HandlerWrapper(const HandlerWrapper&) = default; + HandlerWrapper& operator=(const HandlerWrapper&) = default; + + HandlerWrapper(HandlerWrapper&&) noexcept = default; + HandlerWrapper& operator=(HandlerWrapper&&) noexcept = default; + + ~HandlerWrapper() noexcept override = default; + void node(const osmium::Node& node) final { node_dispatch(m_handler, node, 0); } diff --git a/include/osmium/experimental/flex_reader.hpp b/include/osmium/experimental/flex_reader.hpp index ead9e36b3..dc8289624 100644 --- a/include/osmium/experimental/flex_reader.hpp +++ b/include/osmium/experimental/flex_reader.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -73,7 +73,6 @@ namespace osmium { m_entities((entities & ~osmium::osm_entity_bits::area) | (m_with_areas ? osmium::osm_entity_bits::node | osmium::osm_entity_bits::way : osmium::osm_entity_bits::nothing)), m_location_handler(location_handler), m_reader(file, m_entities), - m_assembler_config(), m_collector(m_assembler_config) { m_location_handler.ignore_errors(); diff --git a/include/osmium/fwd.hpp b/include/osmium/fwd.hpp index eede6fbfd..f556d3fc3 100644 --- a/include/osmium/fwd.hpp +++ b/include/osmium/fwd.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/geom/coordinates.hpp b/include/osmium/geom/coordinates.hpp index f2f926d71..161c0021e 100644 --- a/include/osmium/geom/coordinates.hpp +++ b/include/osmium/geom/coordinates.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -74,6 +74,7 @@ namespace osmium { * This constructor is not explicit on purpose allowing use of * a Location everywhere a Coordinates object is needed. */ + // cppcheck-suppress noExplicitConstructor Coordinates(const osmium::Location& location) : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) x(location.lon()), y(location.lat()) { diff --git a/include/osmium/geom/factory.hpp b/include/osmium/geom/factory.hpp index f878931be..07c7f2e67 100644 --- a/include/osmium/geom/factory.hpp +++ b/include/osmium/geom/factory.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -44,6 +44,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include @@ -56,7 +57,7 @@ namespace osmium { * Exception thrown when an invalid geometry is encountered. An example * would be a linestring with less than two points. */ - class geometry_error : public std::runtime_error { + class OSMIUM_EXPORT geometry_error : public std::runtime_error { std::string m_message; osmium::object_id_type m_id; @@ -131,11 +132,11 @@ namespace osmium { return Coordinates{location.lon(), location.lat()}; } - int epsg() const noexcept { + static int epsg() noexcept { return 4326; } - std::string proj_string() const { + static std::string proj_string() noexcept { return "+proj=longlat +datum=WGS84 +no_defs"; } @@ -165,7 +166,7 @@ namespace osmium { public: - GeometryFactory() : + GeometryFactory() : m_projection(), m_impl(m_projection.epsg()) { } @@ -174,7 +175,7 @@ namespace osmium { * Constructor for default initialized projection. */ template - explicit GeometryFactory(TArgs&&... args) : + explicit GeometryFactory(TArgs&&... args) : m_projection(), m_impl(m_projection.epsg(), std::forward(args)...) { } @@ -184,7 +185,7 @@ namespace osmium { * projection is moved into the GeometryFactory. */ template - explicit GeometryFactory(TProjection&& projection, TArgs&&... args) : + explicit GeometryFactory(TProjection&& projection, TArgs&&... args) : m_projection(std::move(projection)), m_impl(m_projection.epsg(), std::forward(args)...) { } @@ -385,7 +386,7 @@ namespace osmium { for (const auto& item : area) { if (item.type() == osmium::item_type::outer_ring) { - auto& ring = static_cast(item); + const auto& ring = static_cast(item); if (num_polygons > 0) { m_impl.multipolygon_polygon_finish(); } @@ -396,7 +397,7 @@ namespace osmium { ++num_rings; ++num_polygons; } else if (item.type() == osmium::item_type::inner_ring) { - auto& ring = static_cast(item); + const auto& ring = static_cast(item); m_impl.multipolygon_inner_ring_start(); add_points(ring); m_impl.multipolygon_inner_ring_finish(); diff --git a/include/osmium/geom/geojson.hpp b/include/osmium/geom/geojson.hpp index 2b5218b1b..193c8cd22 100644 --- a/include/osmium/geom/geojson.hpp +++ b/include/osmium/geom/geojson.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/geom/geos.hpp b/include/osmium/geom/geos.hpp index c1daa8618..73538f9b5 100644 --- a/include/osmium/geom/geos.hpp +++ b/include/osmium/geom/geos.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -54,7 +54,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include @@ -70,20 +69,13 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include #include #include #include -// MSVC doesn't support throw_with_nested yet -#ifdef _MSC_VER -# define THROW throw -#else -# include -# define THROW std::throw_with_nested -#endif - namespace osmium { struct geos_geometry_error : public geometry_error { @@ -123,16 +115,6 @@ namespace osmium { m_geos_factory(&geos_factory) { } - /** - * @deprecated Do not set SRID explicitly. It will be set to the - * correct value automatically. - */ - OSMIUM_DEPRECATED explicit GEOSFactoryImpl(int /* srid */, int srid) : - m_precision_model(new geos::geom::PrecisionModel), - m_our_geos_factory(new geos::geom::GeometryFactory{m_precision_model.get(), srid}), - m_geos_factory(m_our_geos_factory.get()) { - } - explicit GEOSFactoryImpl(int srid) : m_precision_model(new geos::geom::PrecisionModel), m_our_geos_factory(new geos::geom::GeometryFactory{m_precision_model.get(), srid}), @@ -145,7 +127,7 @@ namespace osmium { try { return point_type{m_geos_factory->createPoint(geos::geom::Coordinate{xy.x, xy.y})}; } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -155,7 +137,7 @@ namespace osmium { try { m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -163,7 +145,7 @@ namespace osmium { try { m_coordinate_sequence->add(geos::geom::Coordinate{xy.x, xy.y}); } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -171,7 +153,7 @@ namespace osmium { try { return linestring_type{m_geos_factory->createLineString(m_coordinate_sequence.release())}; } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -195,7 +177,7 @@ namespace osmium { m_polygons.emplace_back(m_geos_factory->createPolygon(m_rings[0].release(), inner_rings)); m_rings.clear(); } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -203,7 +185,7 @@ namespace osmium { try { m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -211,7 +193,7 @@ namespace osmium { try { m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release())); } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -219,7 +201,7 @@ namespace osmium { try { m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -227,7 +209,7 @@ namespace osmium { try { m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release())); } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -235,7 +217,7 @@ namespace osmium { try { m_coordinate_sequence->add(geos::geom::Coordinate{xy.x, xy.y}); } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -248,7 +230,7 @@ namespace osmium { m_polygons.clear(); return multipolygon_type{m_geos_factory->createMultiPolygon(polygons)}; } catch (const geos::util::GEOSException& e) { - THROW(osmium::geos_geometry_error(e.what())); + std::throw_with_nested(osmium::geos_geometry_error(e.what())); } } @@ -264,8 +246,6 @@ namespace osmium { } // namespace osmium -#undef THROW - #endif #endif // OSMIUM_GEOM_GEOS_HPP diff --git a/include/osmium/geom/haversine.hpp b/include/osmium/geom/haversine.hpp index 46cc30850..3da00d588 100644 --- a/include/osmium/geom/haversine.hpp +++ b/include/osmium/geom/haversine.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -35,7 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include -#include +#include #include #include @@ -63,13 +63,13 @@ namespace osmium { * * @pre @code c1.valid() && c2.valid() @endcode */ - inline double distance(const osmium::geom::Coordinates& c1, const osmium::geom::Coordinates& c2) { - double lonh = sin(deg_to_rad(c1.x - c2.x) * 0.5); + inline double distance(const osmium::geom::Coordinates& c1, const osmium::geom::Coordinates& c2) noexcept { + double lonh = std::sin(deg_to_rad(c1.x - c2.x) * 0.5); lonh *= lonh; - double lath = sin(deg_to_rad(c1.y - c2.y) * 0.5); + double lath = std::sin(deg_to_rad(c1.y - c2.y) * 0.5); lath *= lath; - const double tmp = cos(deg_to_rad(c1.y)) * cos(deg_to_rad(c2.y)); - return 2.0 * EARTH_RADIUS_IN_METERS * asin(sqrt(lath + tmp * lonh)); + const double tmp = std::cos(deg_to_rad(c1.y)) * std::cos(deg_to_rad(c2.y)); + return 2.0 * EARTH_RADIUS_IN_METERS * std::asin(std::sqrt(lath + tmp * lonh)); } /** @@ -78,7 +78,7 @@ namespace osmium { inline double distance(const osmium::WayNodeList& wnl) { double sum_length = 0; - for (auto it = wnl.begin(); it != wnl.end(); ++it) { + for (const auto* it = wnl.begin(); it != wnl.end(); ++it) { if (std::next(it) != wnl.end()) { sum_length += distance(it->location(), std::next(it)->location()); } @@ -87,6 +87,21 @@ namespace osmium { return sum_length; } + /** + * Calculate length of node list. + */ + inline double distance(const osmium::NodeRefList& nrl) { + double sum_length = 0; + + for (const auto* it = nrl.begin(); it != nrl.end(); ++it) { + if (std::next(it) != nrl.end()) { + sum_length += distance(it->location(), std::next(it)->location()); + } + } + + return sum_length; + } + } // namespace haversine } // namespace geom diff --git a/include/osmium/geom/mercator_projection.hpp b/include/osmium/geom/mercator_projection.hpp index febb372ac..acb4d27f1 100644 --- a/include/osmium/geom/mercator_projection.hpp +++ b/include/osmium/geom/mercator_projection.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -49,7 +49,7 @@ namespace osmium { constexpr double earth_radius_for_epsg3857 = 6378137.0; constexpr double max_coordinate_epsg3857 = 20037508.34; - constexpr inline double lon_to_x(double lon) { + constexpr inline double lon_to_x(double lon) noexcept { return earth_radius_for_epsg3857 * deg_to_rad(lon); } @@ -116,6 +116,9 @@ namespace osmium { * Convert the coordinates from WGS84 lon/lat to web mercator. * * @pre @code c.valid() @endcode + * @pre Coordinates must be in valid range, longitude between + * -180 and +180 degree, latitude between -MERCATOR_MAX_LAT + * and MERCATOR_MAX_LAT. */ inline Coordinates lonlat_to_mercator(const Coordinates& c) { return Coordinates{detail::lon_to_x(c.x), detail::lat_to_y(c.y)}; @@ -125,6 +128,8 @@ namespace osmium { * Convert the coordinates from web mercator to WGS84 lon/lat. * * @pre @code c.valid() @endcode + * @pre Coordinates must be in valid range (longitude and + * latidude between -/+20037508.34). */ inline Coordinates mercator_to_lonlat(const Coordinates& c) { return Coordinates{detail::x_to_lon(c.x), detail::y_to_lat(c.y)}; @@ -145,15 +150,22 @@ namespace osmium { MercatorProjection() { // NOLINT(hicpp-use-equals-default, modernize-use-equals-default) } + /** + * Do coordinate transformation. + * + * @pre Coordinates must be in valid range, longitude between + * -180 and +180 degree, latitude between -MERCATOR_MAX_LAT + * and MERCATOR_MAX_LAT. + */ Coordinates operator()(osmium::Location location) const { return Coordinates{detail::lon_to_x(location.lon()), detail::lat_to_y(location.lat())}; } - int epsg() const noexcept { + static int epsg() noexcept { return 3857; } - std::string proj_string() const { + static std::string proj_string() noexcept { return "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"; } diff --git a/include/osmium/geom/ogr.hpp b/include/osmium/geom/ogr.hpp index 73276c1f0..226684b28 100644 --- a/include/osmium/geom/ogr.hpp +++ b/include/osmium/geom/ogr.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -82,7 +82,7 @@ namespace osmium { /* Point */ - point_type make_point(const osmium::geom::Coordinates& xy) const { + static point_type make_point(const osmium::geom::Coordinates& xy) { return point_type{new OGRPoint{xy.x, xy.y}}; } diff --git a/include/osmium/geom/projection.hpp b/include/osmium/geom/projection.hpp index 4b6629709..b96980f86 100644 --- a/include/osmium/geom/projection.hpp +++ b/include/osmium/geom/projection.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -46,8 +46,15 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include -#include +#ifdef ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +# include +#else +# define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +# include +# undef ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +#endif #include #include @@ -58,8 +65,10 @@ namespace osmium { /** * C++ wrapper for a Coordinate Reference System of the proj library. + * + * @deprecated Only supports the old PROJ API. */ - class CRS { + class OSMIUM_DEPRECATED CRS { struct ProjCRSDeleter { void operator()(void* crs) { @@ -110,9 +119,10 @@ namespace osmium { * Coordinates have to be in radians and are produced in radians. * * @throws osmium::projection_error if the projection fails + * + * @deprecated Only supports the old PROJ API. */ - // cppcheck-suppress passedByValue (because c is small and we want to change it) - inline Coordinates transform(const CRS& src, const CRS& dest, Coordinates c) { + inline OSMIUM_DEPRECATED Coordinates transform(const CRS& src, const CRS& dest, Coordinates c) { const int result = pj_transform(src.get(), dest.get(), 1, 1, &c.x, &c.y, nullptr); if (result != 0) { throw osmium::projection_error{std::string{"projection failed: "} + pj_strerrno(result)}; @@ -130,8 +140,10 @@ namespace osmium { * implementation of the Mercator projection is used, otherwise this * falls back to using the proj.4 library. Note that this "magic" does * not work if you use any of the constructors taking a string. + * + * @deprecated Only supports the old PROJ API. */ - class Projection { + class OSMIUM_DEPRECATED Projection { int m_epsg; std::string m_proj_string; @@ -140,9 +152,9 @@ namespace osmium { public: - explicit Projection(const std::string& proj_string) : + explicit Projection(std::string proj_string) : m_epsg(-1), - m_proj_string(proj_string), + m_proj_string(std::move(proj_string)), m_crs_user(proj_string) { } @@ -158,6 +170,12 @@ namespace osmium { m_crs_user(epsg) { } + /** + * Do coordinate transformation. + * + * @pre Location must be in valid range (depends on projection + * used). + */ Coordinates operator()(osmium::Location location) const { if (m_epsg == 4326) { return Coordinates{location.lon(), location.lat()}; diff --git a/include/osmium/geom/rapid_geojson.hpp b/include/osmium/geom/rapid_geojson.hpp index 53fc79847..94f172609 100644 --- a/include/osmium/geom/rapid_geojson.hpp +++ b/include/osmium/geom/rapid_geojson.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -47,6 +47,9 @@ namespace osmium { /** * A geometry factory implementation that can be used with the * RapidJSON (https://github.com/miloyip/rapidjson) JSON writer. + * + * @deprecated The RapidJSON support will be removed in a future + * version of libosmium. */ template class RapidGeoJSONFactoryImpl { diff --git a/include/osmium/geom/relations.hpp b/include/osmium/geom/relations.hpp index c3b28c4dd..6c4352d3e 100644 --- a/include/osmium/geom/relations.hpp +++ b/include/osmium/geom/relations.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/geom/tile.hpp b/include/osmium/geom/tile.hpp index 133916e64..d03504bc9 100644 --- a/include/osmium/geom/tile.hpp +++ b/include/osmium/geom/tile.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -58,7 +58,7 @@ namespace osmium { * level. */ inline constexpr uint32_t num_tiles_in_zoom(uint32_t zoom) noexcept { - return 1u << zoom; + return 1U << zoom; } /** @@ -77,8 +77,7 @@ namespace osmium { inline constexpr uint32_t mercx_to_tilex(uint32_t zoom, double x) noexcept { return static_cast(detail::clamp( static_cast((x + detail::max_coordinate_epsg3857) / tile_extent_in_zoom(zoom)), - 0, num_tiles_in_zoom(zoom) -1 - )); + 0, num_tiles_in_zoom(zoom) - 1)); } /** @@ -89,8 +88,7 @@ namespace osmium { inline constexpr uint32_t mercy_to_tiley(uint32_t zoom, double y) noexcept { return static_cast(detail::clamp( static_cast((detail::max_coordinate_epsg3857 - y) / tile_extent_in_zoom(zoom)), - 0, num_tiles_in_zoom(zoom) -1 - )); + 0, num_tiles_in_zoom(zoom) - 1)); } /** @@ -98,6 +96,10 @@ namespace osmium { */ struct Tile { + enum { + max_zoom = 30U + }; + /// x coordinate uint32_t x; @@ -119,7 +121,7 @@ namespace osmium { x(tx), y(ty), z(zoom) { - assert(zoom <= 30u); + assert(zoom <= max_zoom); assert(x < num_tiles_in_zoom(zoom)); assert(y < num_tiles_in_zoom(zoom)); } @@ -134,7 +136,7 @@ namespace osmium { */ explicit Tile(uint32_t zoom, const osmium::Location& location) : z(zoom) { - assert(zoom <= 30u); + assert(zoom <= max_zoom); assert(location.valid()); const auto coordinates = lonlat_to_mercator(location); x = mercx_to_tilex(zoom, coordinates.x); @@ -151,7 +153,7 @@ namespace osmium { */ explicit Tile(uint32_t zoom, const osmium::geom::Coordinates& coordinates) : z(zoom) { - assert(zoom <= 30u); + assert(zoom <= max_zoom); x = mercx_to_tilex(zoom, coordinates.x); y = mercy_to_tiley(zoom, coordinates.y); } @@ -162,7 +164,7 @@ namespace osmium { * each be between 0 and 2^zoom-1. */ bool valid() const noexcept { - if (z > 30) { + if (z > max_zoom) { return false; } const auto max = num_tiles_in_zoom(z); diff --git a/include/osmium/geom/util.hpp b/include/osmium/geom/util.hpp index 6984edf49..2e8ee858c 100644 --- a/include/osmium/geom/util.hpp +++ b/include/osmium/geom/util.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -33,6 +33,8 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include #include @@ -42,7 +44,7 @@ namespace osmium { * Exception thrown when a projection object can not be initialized or the * projection of some coordinates can not be calculated. */ - struct projection_error : public std::runtime_error { + struct OSMIUM_EXPORT projection_error : public std::runtime_error { explicit projection_error(const std::string& what) : std::runtime_error(what) { diff --git a/include/osmium/geom/wkb.hpp b/include/osmium/geom/wkb.hpp index 405d92def..15a7d44ec 100644 --- a/include/osmium/geom/wkb.hpp +++ b/include/osmium/geom/wkb.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include @@ -69,9 +68,9 @@ namespace osmium { std::string out; out.reserve(str.size() * 2); - for (char c : str) { - out += lookup_hex[(static_cast(c) >> 4u) & 0xfu]; - out += lookup_hex[ static_cast(c) & 0xfu]; + for (const char c : str) { + out += lookup_hex[(static_cast(c) >> 4U) & 0xfU]; + out += lookup_hex[ static_cast(c) & 0xfU]; } return out; @@ -202,6 +201,34 @@ namespace osmium { return data; } + /* Polygon */ + + void polygon_start() { + m_data.clear(); + set_size(header(m_data, wkbPolygon, true), 1); + m_ring_size_offset = m_data.size(); + str_push(m_data, static_cast(0)); + } + + void polygon_add_location(const osmium::geom::Coordinates& xy) { + str_push(m_data, xy.x); + str_push(m_data, xy.y); + } + + polygon_type polygon_finish(std::size_t num_points) { + set_size(m_ring_size_offset, num_points); + std::string data; + + using std::swap; + swap(data, m_data); + + if (m_out_type == out_type::hex) { + return convert_to_hex(data); + } + + return data; + } + /* MultiPolygon */ void multipolygon_start() { diff --git a/include/osmium/geom/wkt.hpp b/include/osmium/geom/wkt.hpp index 5801c652b..216873dfe 100644 --- a/include/osmium/geom/wkt.hpp +++ b/include/osmium/geom/wkt.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/handler.hpp b/include/osmium/handler.hpp index fe9dca3f4..58bc427cd 100644 --- a/include/osmium/handler.hpp +++ b/include/osmium/handler.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/handler/chain.hpp b/include/osmium/handler/chain.hpp index 5f4bb78e7..70ad76b3c 100644 --- a/include/osmium/handler/chain.hpp +++ b/include/osmium/handler/chain.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -80,7 +80,7 @@ namespace osmium { template struct call_flush { - void operator()(THandlers&) {} + void operator()(THandlers& /*handlers*/) {} }; // struct call_flush OSMIUM_CHAIN_HANDLER_CALL(node, Node) diff --git a/include/osmium/handler/check_order.hpp b/include/osmium/handler/check_order.hpp index c6d77bbaf..1daedda31 100644 --- a/include/osmium/handler/check_order.hpp +++ b/include/osmium/handler/check_order.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -39,8 +39,8 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include -#include #include #include @@ -50,7 +50,7 @@ namespace osmium { * Exception thrown when a method in the CheckOrder class detects * that the input is out of order. */ - struct out_of_order_error : public std::runtime_error { + struct OSMIUM_EXPORT out_of_order_error : public std::runtime_error { osmium::object_id_type object_id; @@ -86,51 +86,69 @@ namespace osmium { */ class CheckOrder : public osmium::handler::Handler { - osmium::object_id_type m_max_node_id = std::numeric_limits::min(); - osmium::object_id_type m_max_way_id = std::numeric_limits::min(); - osmium::object_id_type m_max_relation_id = std::numeric_limits::min(); + osmium::object_id_type m_max_node_id = 0; + osmium::object_id_type m_max_way_id = 0; + osmium::object_id_type m_max_relation_id = 0; + bool m_has_node = false; + bool m_has_way = false; + bool m_has_relation = false; public: void node(const osmium::Node& node) { - if (m_max_way_id > std::numeric_limits::min()) { + if (m_has_way) { throw out_of_order_error{"Found a node after a way.", node.id()}; } - if (m_max_relation_id > std::numeric_limits::min()) { + if (m_has_relation) { throw out_of_order_error{"Found a node after a relation.", node.id()}; } - if (m_max_node_id == node.id()) { - throw out_of_order_error{"Node ID twice in input. Maybe you are using a history or change file?", node.id()}; + if (m_has_node) { + if (m_max_node_id == node.id()) { + throw out_of_order_error{"Node ID twice in input. Maybe you are using a history or change file?", node.id()}; + } + if (id_order{}(node.id(), m_max_node_id)) { + throw out_of_order_error{"Node IDs out of order: " + std::to_string(node.id()), node.id()}; + } + m_max_node_id = node.id(); + } else { + m_max_node_id = node.id(); + m_has_node = true; } - if (id_order{}(node.id(), m_max_node_id)) { - throw out_of_order_error{"Node IDs out of order.", node.id()}; - } - m_max_node_id = node.id(); } void way(const osmium::Way& way) { - if (m_max_relation_id > std::numeric_limits::min()) { + if (m_has_relation) { throw out_of_order_error{"Found a way after a relation.", way.id()}; } - if (m_max_way_id == way.id()) { - throw out_of_order_error{"Way ID twice in input. Maybe you are using a history or change file?", way.id()}; + if (m_has_way) { + if (m_max_way_id == way.id()) { + throw out_of_order_error{"Way ID twice in input. Maybe you are using a history or change file?", way.id()}; + } + if (id_order{}(way.id(), m_max_way_id)) { + throw out_of_order_error{"Way IDs out of order: " + std::to_string(way.id()), way.id()}; + } + m_max_way_id = way.id(); + } else { + m_max_way_id = way.id(); + m_has_way = true; } - if (id_order{}(way.id(), m_max_way_id)) { - throw out_of_order_error{"Way IDs out of order.", way.id()}; - } - m_max_way_id = way.id(); } void relation(const osmium::Relation& relation) { - if (m_max_relation_id == relation.id()) { - throw out_of_order_error{"Relation ID twice in input. Maybe you are using a history or change file?", relation.id()}; + if (m_has_relation) { + if (m_max_relation_id == relation.id()) { + throw out_of_order_error{"Relation ID twice in input. Maybe you are using a history or change file?", relation.id()}; + } + if (id_order{}(relation.id(), m_max_relation_id)) { + throw out_of_order_error{"Relation IDs out of order: " + std::to_string(relation.id()), relation.id()}; + } + m_max_relation_id = relation.id(); + } else { + m_max_relation_id = relation.id(); + m_has_relation = true; } - if (id_order{}(relation.id(), m_max_relation_id)) { - throw out_of_order_error{"Relation IDs out of order.", relation.id()}; - } - m_max_relation_id = relation.id(); } osmium::object_id_type max_node_id() const noexcept { diff --git a/include/osmium/handler/disk_store.hpp b/include/osmium/handler/disk_store.hpp index 8ec4c3ecc..e5bd36c51 100644 --- a/include/osmium/handler/disk_store.hpp +++ b/include/osmium/handler/disk_store.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/handler/dump.hpp b/include/osmium/handler/dump.hpp index 8827387b6..86398c9cd 100644 --- a/include/osmium/handler/dump.hpp +++ b/include/osmium/handler/dump.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/handler/node_locations_for_ways.hpp b/include/osmium/handler/node_locations_for_ways.hpp index 640b7016f..8e66f3837 100644 --- a/include/osmium/handler/node_locations_for_ways.hpp +++ b/include/osmium/handler/node_locations_for_ways.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -115,6 +115,14 @@ namespace osmium { m_ignore_errors = true; } + TStoragePosIDs& storage_pos() noexcept { + return m_storage_pos; + } + + TStorageNegIDs& storage_neg() noexcept { + return m_storage_neg; + } + /** * Store the location of the node in the storage. */ diff --git a/include/osmium/handler/object_relations.hpp b/include/osmium/handler/object_relations.hpp index fd903ea22..a50b61c65 100644 --- a/include/osmium/handler/object_relations.hpp +++ b/include/osmium/handler/object_relations.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/index/detail/create_map_with_fd.hpp b/include/osmium/index/detail/create_map_with_fd.hpp index 32a9b6317..c67329f9a 100644 --- a/include/osmium/index/detail/create_map_with_fd.hpp +++ b/include/osmium/index/detail/create_map_with_fd.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -35,10 +35,10 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include #include +#include #include namespace osmium { @@ -56,7 +56,7 @@ namespace osmium { const std::string& filename = config[1]; const int fd = ::open(filename.c_str(), O_CREAT | O_RDWR, 0644); // NOLINT(hicpp-signed-bitwise) if (fd == -1) { - throw std::runtime_error{std::string{"can't open file '"} + filename + "': " + std::strerror(errno)}; + throw std::system_error{errno, std::system_category(), "can't open file '" + filename + "'"}; } return new T{fd}; } diff --git a/include/osmium/index/detail/mmap_vector_anon.hpp b/include/osmium/index/detail/mmap_vector_anon.hpp index 555246dc5..075d187e8 100644 --- a/include/osmium/index/detail/mmap_vector_anon.hpp +++ b/include/osmium/index/detail/mmap_vector_anon.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/index/detail/mmap_vector_base.hpp b/include/osmium/index/detail/mmap_vector_base.hpp index 6e0bbf42d..3da81ca4b 100644 --- a/include/osmium/index/detail/mmap_vector_base.hpp +++ b/include/osmium/index/detail/mmap_vector_base.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -46,7 +46,9 @@ namespace osmium { namespace detail { - constexpr size_t mmap_vector_size_increment = 1024 * 1024; + enum { + mmap_vector_size_increment = 1024UL * 1024UL + }; /** * This is a base class for implementing classes that look like @@ -58,12 +60,12 @@ namespace osmium { protected: - size_t m_size = 0; + std::size_t m_size = 0; osmium::TypedMemoryMapping m_mapping; public: - mmap_vector_base(int fd, size_t capacity, size_t size = 0) : + mmap_vector_base(const int fd, const std::size_t capacity, const std::size_t size = 0) : m_size(size), m_mapping(capacity, osmium::MemoryMapping::mapping_mode::write_shared, fd) { assert(size <= capacity); @@ -71,7 +73,7 @@ namespace osmium { shrink_to_fit(); } - explicit mmap_vector_base(size_t capacity = mmap_vector_size_increment) : + explicit mmap_vector_base(const std::size_t capacity = mmap_vector_size_increment) : m_mapping(capacity) { std::fill_n(data(), capacity, osmium::index::empty_value()); } @@ -88,11 +90,11 @@ namespace osmium { m_mapping.unmap(); } - size_t capacity() const noexcept { + std::size_t capacity() const noexcept { return m_mapping.size(); } - size_t size() const noexcept { + std::size_t size() const noexcept { return m_size; } @@ -108,17 +110,17 @@ namespace osmium { return m_mapping.begin(); } - const_reference operator[](size_t n) const { + const_reference operator[](const std::size_t n) const { assert(n < m_size); return data()[n]; } - reference operator[](size_t n) { + reference operator[](const std::size_t n) { assert(n < m_size); return data()[n]; } - value_type at(size_t n) const { + value_type at(const std::size_t n) const { if (n >= m_size) { throw std::out_of_range{"out of range"}; } @@ -140,17 +142,17 @@ namespace osmium { data()[m_size - 1] = value; } - void reserve(size_t new_capacity) { + void reserve(const std::size_t new_capacity) { if (new_capacity > capacity()) { - const size_t old_capacity = capacity(); + const std::size_t old_capacity = capacity(); m_mapping.resize(new_capacity); std::fill(data() + old_capacity, data() + new_capacity, osmium::index::empty_value()); } } - void resize(size_t new_size) { + void resize(const std::size_t new_size) { if (new_size > capacity()) { - reserve(new_size + osmium::detail::mmap_vector_size_increment); + reserve(new_size + mmap_vector_size_increment); } m_size = new_size; } diff --git a/include/osmium/index/detail/mmap_vector_file.hpp b/include/osmium/index/detail/mmap_vector_file.hpp index 8258bb374..5f048e072 100644 --- a/include/osmium/index/detail/mmap_vector_file.hpp +++ b/include/osmium/index/detail/mmap_vector_file.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -53,7 +53,7 @@ namespace osmium { template class mmap_vector_file : public mmap_vector_base { - static std::size_t filesize(int fd) { + static std::size_t filesize(const int fd) { const auto size = osmium::file_size(fd); if (size % sizeof(T) != 0) { @@ -71,10 +71,10 @@ namespace osmium { osmium::detail::mmap_vector_size_increment) { } - explicit mmap_vector_file(int fd) : + explicit mmap_vector_file(const int fd) : mmap_vector_base( fd, - std::max(osmium::detail::mmap_vector_size_increment, filesize(fd)), + std::max(static_cast(mmap_vector_size_increment), filesize(fd)), filesize(fd)) { } diff --git a/include/osmium/index/detail/tmpfile.hpp b/include/osmium/index/detail/tmpfile.hpp index 26a39de15..38746d3f1 100644 --- a/include/osmium/index/detail/tmpfile.hpp +++ b/include/osmium/index/detail/tmpfile.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 diff --git a/include/osmium/index/detail/vector_map.hpp b/include/osmium/index/detail/vector_map.hpp index c560ed91e..d9ee56db4 100644 --- a/include/osmium/index/detail/vector_map.hpp +++ b/include/osmium/index/detail/vector_map.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -39,8 +39,10 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include + namespace osmium { namespace index { @@ -63,6 +65,14 @@ namespace osmium { m_vector() { } + VectorBasedDenseMap(const VectorBasedDenseMap&) = default; + VectorBasedDenseMap& operator=(const VectorBasedDenseMap&) = default; + + VectorBasedDenseMap(VectorBasedDenseMap&&) noexcept = default; + VectorBasedDenseMap& operator=(VectorBasedDenseMap&&) noexcept = default; + + ~VectorBasedDenseMap() noexcept override = default; + explicit VectorBasedDenseMap(int fd) : m_vector(fd) { } @@ -159,10 +169,9 @@ namespace osmium { vector_type m_vector; typename vector_type::const_iterator find_id(const TId id) const noexcept { - const element_type element { + const element_type element{ id, - osmium::index::empty_value() - }; + osmium::index::empty_value()}; return std::lower_bound(m_vector.begin(), m_vector.end(), element, [](const element_type& a, const element_type& b) { return a.first < b.first; }); @@ -178,6 +187,14 @@ namespace osmium { m_vector(fd) { } + VectorBasedSparseMap(const VectorBasedSparseMap&) = default; + VectorBasedSparseMap& operator=(const VectorBasedSparseMap&) = default; + + VectorBasedSparseMap(VectorBasedSparseMap&&) noexcept = default; + VectorBasedSparseMap& operator=(VectorBasedSparseMap&&) noexcept = default; + + ~VectorBasedSparseMap() noexcept override = default; + void set(const TId id, const TValue value) final { m_vector.push_back(element_type(id, value)); } @@ -221,6 +238,26 @@ namespace osmium { std::sort(m_vector.begin(), m_vector.end()); } + void dump_as_array(const int fd) final { + constexpr const size_t value_size = sizeof(TValue); + constexpr const size_t buffer_size = (10L * 1024L * 1024L) / value_size; + const std::unique_ptr output_buffer{new TValue[buffer_size]}; + + size_t buffer_start_id = 0; + for (auto it = cbegin(); it != cend();) { + std::fill_n(output_buffer.get(), buffer_size, osmium::index::empty_value()); + size_t offset = 0; + for (; offset < buffer_size && it != end(); ++offset) { + if (buffer_start_id + offset == it->first) { + output_buffer[offset] = it->second; + ++it; + } + } + osmium::io::detail::reliable_write(fd, reinterpret_cast(output_buffer.get()), offset * value_size); + buffer_start_id += buffer_size; + } + } + void dump_as_list(const int fd) final { osmium::io::detail::reliable_write(fd, reinterpret_cast(m_vector.data()), byte_size()); } diff --git a/include/osmium/index/detail/vector_multimap.hpp b/include/osmium/index/detail/vector_multimap.hpp index b7fdf84db..ab272618e 100644 --- a/include/osmium/index/detail/vector_multimap.hpp +++ b/include/osmium/index/detail/vector_multimap.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -61,7 +61,7 @@ namespace osmium { vector_type m_vector; - static bool is_removed(element_type& element) { + static bool is_removed(const element_type& element) { return element.second == osmium::index::empty_value(); } @@ -75,6 +75,14 @@ namespace osmium { m_vector(fd) { } + VectorBasedSparseMultimap(const VectorBasedSparseMultimap&) = default; + VectorBasedSparseMultimap& operator=(const VectorBasedSparseMultimap&) = default; + + VectorBasedSparseMultimap(VectorBasedSparseMultimap&&) noexcept = default; + VectorBasedSparseMultimap& operator=(VectorBasedSparseMultimap&&) noexcept = default; + + ~VectorBasedSparseMultimap() noexcept override = default; + void set(const TId id, const TValue value) final { m_vector.push_back(element_type(id, value)); } @@ -84,20 +92,18 @@ namespace osmium { } std::pair get_all(const TId id) { - const element_type element { + const element_type element{ id, - osmium::index::empty_value() - }; + osmium::index::empty_value()}; return std::equal_range(m_vector.begin(), m_vector.end(), element, [](const element_type& a, const element_type& b) { return a.first < b.first; }); } std::pair get_all(const TId id) const { - const element_type element { + const element_type element{ id, - osmium::index::empty_value() - }; + osmium::index::empty_value()}; return std::equal_range(m_vector.cbegin(), m_vector.cend(), element, [](const element_type& a, const element_type& b) { return a.first < b.first; }); @@ -141,8 +147,7 @@ namespace osmium { void erase_removed() { m_vector.erase( std::remove_if(m_vector.begin(), m_vector.end(), is_removed), - m_vector.end() - ); + m_vector.end()); } void dump_as_list(const int fd) final { diff --git a/include/osmium/index/id_set.hpp b/include/osmium/index/id_set.hpp index eefdf821c..c2f44c59e 100644 --- a/include/osmium/index/id_set.hpp +++ b/include/osmium/index/id_set.hpp @@ -3,9 +3,9 @@ /* -This file is part of Osmium (http://osmcode.org/libosmium). +This file is part of Osmium (https://osmcode.org/libosmium). -Copyright 2013-2018 Jochen Topf and others (see README). +Copyright 2013-2023 Jochen Topf and others (see README). Boost Software License - Version 1.0 - August 17th, 2003 @@ -37,6 +37,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include #include @@ -66,7 +67,7 @@ namespace osmium { IdSet(IdSet&&) noexcept = default; IdSet& operator=(IdSet&&) noexcept = default; - virtual ~IdSet() = default; + virtual ~IdSet() noexcept = default; /** * Add the given Id to the set. @@ -95,33 +96,47 @@ namespace osmium { }; // class IdSet - template + namespace detail { + + // This value is a compromise. For node Ids it could be bigger + // which would mean less (but larger) memory allocations. For + // relations Ids it could be smaller, because they would all fit + // into a smaller allocation. + enum : std::size_t { + default_chunk_bits = 22U + }; + + } // namespace detail + + template class IdSetDense; /** * Const_iterator for iterating over a IdSetDense. */ - template + template class IdSetDenseIterator { static_assert(std::is_unsigned::value, "Needs unsigned type"); static_assert(sizeof(T) >= 4, "Needs at least 32bit type"); - const IdSetDense* m_set; + using id_set = IdSetDense; + + const id_set* m_set; T m_value; T m_last; void next() noexcept { while (m_value != m_last && !m_set->get(m_value)) { - const T cid = IdSetDense::chunk_id(m_value); + const T cid = id_set::chunk_id(m_value); assert(cid < m_set->m_data.size()); if (!m_set->m_data[cid]) { - m_value = (cid + 1) << (IdSetDense::chunk_bits + 3); + m_value = (cid + 1) << (chunk_bits + 3); } else { - const auto slot = m_set->m_data[cid][IdSetDense::offset(m_value)]; + const auto slot = m_set->m_data[cid][id_set::offset(m_value)]; if (slot == 0) { m_value += 8; - m_value &= ~0x7ull; + m_value &= ~0x7ULL; } else { ++m_value; } @@ -136,14 +151,14 @@ namespace osmium { using pointer = value_type*; using reference = value_type&; - IdSetDenseIterator(const IdSetDense* set, T value, T last) noexcept : + IdSetDenseIterator(const id_set* set, T value, T last) noexcept : m_set(set), m_value(value), m_last(last) { next(); } - IdSetDenseIterator& operator++() noexcept { + IdSetDenseIterator& operator++() noexcept { if (m_value != m_last) { ++m_value; next(); @@ -151,17 +166,17 @@ namespace osmium { return *this; } - IdSetDenseIterator operator++(int) noexcept { - IdSetDenseIterator tmp{*this}; + IdSetDenseIterator operator++(int) noexcept { + IdSetDenseIterator tmp{*this}; operator++(); return tmp; } - bool operator==(const IdSetDenseIterator& rhs) const noexcept { + bool operator==(const IdSetDenseIterator& rhs) const noexcept { return m_set == rhs.m_set && m_value == rhs.m_value; } - bool operator!=(const IdSetDenseIterator& rhs) const noexcept { + bool operator!=(const IdSetDenseIterator& rhs) const noexcept { return !(*this == rhs); } @@ -179,34 +194,31 @@ namespace osmium { * and larger Id sets. If it is not used, no memory is allocated at * all. */ - template + template class IdSetDense : public IdSet { static_assert(std::is_unsigned::value, "Needs unsigned type"); static_assert(sizeof(T) >= 4, "Needs at least 32bit type"); - friend class IdSetDenseIterator; + friend class IdSetDenseIterator; - // This value is a compromise. For node Ids it could be bigger - // which would mean less (but larger) memory allocations. For - // relations Ids it could be smaller, because they would all fit - // into a smaller allocation. - constexpr static const std::size_t chunk_bits = 22u; - constexpr static const std::size_t chunk_size = 1u << chunk_bits; + enum : std::size_t { + chunk_size = 1U << chunk_bits + }; std::vector> m_data; T m_size = 0; static std::size_t chunk_id(T id) noexcept { - return id >> (chunk_bits + 3u); + return id >> (chunk_bits + 3U); } static std::size_t offset(T id) noexcept { - return (id >> 3u) & ((1u << chunk_bits) - 1u); + return (id >> 3U) & ((1U << chunk_bits) - 1U); } - static unsigned char bitmask(T id) noexcept { - return 1u << (id & 0x7u); + static unsigned int bitmask(T id) noexcept { + return 1U << (id & 0x7U); } T last() const noexcept { @@ -230,10 +242,43 @@ namespace osmium { public: - using const_iterator = IdSetDenseIterator; + using const_iterator = IdSetDenseIterator; + + friend void swap(IdSetDense& first, IdSetDense& second) noexcept { + using std::swap; + swap(first.m_data, second.m_data); + swap(first.m_size, second.m_size); + } IdSetDense() = default; + IdSetDense(const IdSetDense& other) : + IdSet(other), + m_size(other.m_size) { + m_data.reserve(other.m_data.size()); + for (const auto& ptr : other.m_data) { + if (ptr) { + m_data.emplace_back(new unsigned char[chunk_size]); + ::memcpy(m_data.back().get(), ptr.get(), chunk_size); + } else { + m_data.emplace_back(); + } + } + } + + IdSetDense& operator=(IdSetDense other) { + swap(*this, other); + return *this; + } + + IdSetDense(IdSetDense&&) noexcept = default; + + // This should really be noexcept, but GCC 4.8 doesn't like it. + // NOLINTNEXTLINE(hicpp-noexcept-move, performance-noexcept-move-constructor) + IdSetDense& operator=(IdSetDense&&) = default; + + ~IdSetDense() noexcept override = default; + /** * Add the Id to the set if it is not already in there. * @@ -284,7 +329,7 @@ namespace osmium { if (chunk_id(id) >= m_data.size()) { return false; } - auto* r = m_data[chunk_id(id)].get(); + const auto* r = m_data[chunk_id(id)].get(); if (!r) { return false; } @@ -317,11 +362,11 @@ namespace osmium { return m_data.size() * chunk_size; } - IdSetDenseIterator begin() const { + const_iterator begin() const { return {this, 0, last()}; } - IdSetDenseIterator end() const { + const_iterator end() const { return {this, last(), last()}; } @@ -338,11 +383,23 @@ namespace osmium { public: + IdSetSmall() = default; + + IdSetSmall(const IdSetSmall&) = default; + IdSetSmall& operator=(const IdSetSmall&) = default; + + IdSetSmall(IdSetSmall&&) noexcept = default; + IdSetSmall& operator=(IdSetSmall&&) noexcept = default; + + ~IdSetSmall() noexcept override = default; + /** * Add the given Id to the set. */ void set(T id) final { - m_data.push_back(id); + if (m_data.empty() || m_data.back() != id) { + m_data.push_back(id); + } } /** @@ -385,7 +442,8 @@ namespace osmium { /** * Sort the internal vector and remove any duplicates. Call this - * before using size(), get_binary_search() or using an iterator. + * before using size(), get_binary_search(), merge_sorted() or + * using an iterator. */ void sort_unique() { std::sort(m_data.begin(), m_data.end()); @@ -408,6 +466,22 @@ namespace osmium { return m_data.capacity() * sizeof(T); } + /** + * Merge the other set into this one. The result is sorted. + * + * @pre Both sets must be sorted and must not contain any + * duplicates. Call sort_unique() if you are not sure. + */ + void merge_sorted(const IdSetSmall& other) { + std::vector new_data; + new_data.reserve(m_data.size() + other.m_data.size()); + std::set_union(m_data.cbegin(), m_data.cend(), + other.m_data.cbegin(), other.m_data.cend(), + std::back_inserter(new_data)); + using std::swap; + swap(new_data, m_data); + } + /// Iterator type. There is no non-const iterator. using const_iterator = typename std::vector::const_iterator; @@ -429,26 +503,6 @@ namespace osmium { }; // class IdSetSmall - /// @deprecated Use nwr_array helper class instead. - template