├── .clang-tidy ├── .github ├── actions │ ├── build │ │ └── action.yml │ ├── cmake-windows │ │ └── action.yml │ ├── cmake │ │ └── action.yml │ ├── ctest │ │ └── action.yml │ ├── install-macos │ │ └── action.yml │ ├── install-protozero │ │ └── action.yml │ └── install-windows │ │ └── action.yml └── workflows │ ├── ci.yml │ └── clang-tidy.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CMakeLists.txt ├── CONTRIBUTING.md ├── EXTERNAL_LICENSES.txt ├── LICENSE ├── README.md ├── appveyor.yml ├── build-appveyor.bat ├── build-msys2.bat ├── cmake └── FindProtozero.cmake ├── codecov.yml ├── doc ├── CMakeLists.txt ├── Doxyfile.in ├── advanced.md ├── doc.md ├── reading.md ├── tutorial.md └── writing.md ├── examples ├── CMakeLists.txt ├── utils.cpp ├── utils.hpp ├── vtzero-check.cpp ├── vtzero-create.cpp ├── vtzero-encode-geom.cpp ├── vtzero-filter.cpp ├── vtzero-show.cpp ├── vtzero-stats.cpp └── vtzero-streets.cpp ├── include-external └── clara.hpp ├── include └── vtzero │ ├── builder.hpp │ ├── builder_impl.hpp │ ├── encoded_property_value.hpp │ ├── exception.hpp │ ├── feature.hpp │ ├── feature_builder_impl.hpp │ ├── geometry.hpp │ ├── index.hpp │ ├── layer.hpp │ ├── output.hpp │ ├── property.hpp │ ├── property_mapper.hpp │ ├── property_value.hpp │ ├── types.hpp │ ├── vector_tile.hpp │ └── version.hpp └── test ├── CMakeLists.txt ├── catch └── catch.hpp ├── data └── mapbox-streets-v6-14-8714-8017.mvt ├── fixture_tests.cpp ├── include └── test.hpp ├── t ├── test_builder.cpp ├── test_builder_linestring.cpp ├── test_builder_point.cpp ├── test_builder_polygon.cpp ├── test_exceptions.cpp ├── test_feature.cpp ├── test_geometry.cpp ├── test_geometry_linestring.cpp ├── test_geometry_point.cpp ├── test_geometry_polygon.cpp ├── test_index.cpp ├── test_layer.cpp ├── test_output.cpp ├── test_point.cpp ├── test_property_map.cpp ├── test_property_value.cpp ├── test_types.cpp └── test_vector_tile.cpp └── test_main.cpp /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '*,-altera-*,-boost-use-ranges,-bugprone-chained-comparison,-bugprone-easily-swappable-parameters,-cert-dcl21-cpp,-cert-err60-cpp,-clang-analyzer-core.CallAndMessage,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-non-private-member-variables-in-classes,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-fuchsia-*,-google-runtime-references,-hicpp-no-array-decay,-hicpp-vararg,-llvmlibc-*,-misc-non-private-member-variables-in-classes,-modernize-use-trailing-return-type,-readability-avoid-const-params-in-decls,-readability-function-cognitive-complexity,-readability-identifier-length,-readability-implicit-bool-cast,-readability-implicit-bool-conversion,-readability-magic-numbers' 3 | # 4 | # Disabled checks: 5 | # 6 | # altera-* 7 | # Not applicable 8 | # 9 | # boost-use-ranges 10 | # Would introduce extra dependency on boost. 11 | # 12 | # bugprone-chained-comparison 13 | # These are generated by our test framework. 14 | # 15 | # bugprone-easily-swappable-parameters 16 | # When you need them, you need them. 17 | # 18 | # cert-dcl21-cpp 19 | # It is unclear whether this is still a good recommendation in modern C++. 20 | # 21 | # cert-err60-cpp 22 | # Reports std::runtime_error as broken which we can't do anything about. 23 | # 24 | # clang-analyzer-core.CallAndMessage 25 | # Produces false positives 26 | # 27 | # cppcoreguidelines-avoid-magic-numbers 28 | # readability-magic-numbers 29 | # Not wrong, but many tests just have those 30 | # 31 | # cppcoreguidelines-non-private-member-variables-in-classes 32 | # misc-non-private-member-variables-in-classes 33 | # Can be useful, we use it for some mock classes in tests for instance 34 | # 35 | # cppcoreguidelines-owning-memory 36 | # Don't want to add dependency on gsl library. 37 | # 38 | # cppcoreguidelines-pro-bounds-array-to-pointer-decay 39 | # Limited use and many false positives including for all asserts. 40 | # 41 | # cppcoreguidelines-pro-bounds-pointer-arithmetic 42 | # This is a low-level library, it needs to do pointer arithmetic. 43 | # 44 | # fuchsia-* 45 | # Much too strict. 46 | # 47 | # google-runtime-references 48 | # This is just a matter of preference, and we can't change the interfaces 49 | # now anyways. 50 | # 51 | # hicpp-no-array-decay 52 | # Limited use and many false positives including for all asserts. 53 | # 54 | # hicpp-vararg 55 | # False positives from Catch2 56 | # 57 | # llvmlibc-* 58 | # Not applicable 59 | # 60 | # modernize-use-trailing-return-type 61 | # We are not that modern. 62 | # 63 | # readability-avoid-const-params-in-decls 64 | # Inconsistently complaines about some cases but not others. It is nice 65 | # to have const in parameters if we don't change them just like with any 66 | # other variables. 67 | # 68 | # readability-function-cognitive-complexity 69 | # Gets triggered by lots of tests. 70 | # 71 | # readability-identifier-length 72 | # Generally a good idea, but sometimes short names are okay. 73 | # 74 | # readability-implicit-bool-cast 75 | # Old name for readability-implicit-bool-conversion. 76 | # 77 | # readability-implicit-bool-conversion 78 | # I don't think this makes the code more readable. 79 | # 80 | #WarningsAsErrors: '*' 81 | HeaderFilterRegex: '\/include\/' 82 | ... 83 | -------------------------------------------------------------------------------- /.github/actions/build/action.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Build 7 | run: cmake --build . --config Release --verbose 8 | shell: bash 9 | working-directory: build 10 | -------------------------------------------------------------------------------- /.github/actions/cmake-windows/action.yml: -------------------------------------------------------------------------------- 1 | name: CMake on Windows 2 | 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Create build directory 7 | run: mkdir build 8 | shell: bash 9 | - name: Configure 10 | run: | 11 | cmake -LA .. \ 12 | -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake 13 | shell: bash 14 | working-directory: build 15 | -------------------------------------------------------------------------------- /.github/actions/cmake/action.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Create build directory 7 | run: mkdir build 8 | shell: bash 9 | - name: Configure 10 | run: | 11 | cmake -LA .. \ 12 | -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ 13 | -DPROTOZERO_DATA_VIEW=${PROTOZERO_DATA_VIEW} 14 | shell: bash 15 | working-directory: build 16 | -------------------------------------------------------------------------------- /.github/actions/ctest/action.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Test 7 | run: ctest --output-on-failure -C Release 8 | shell: bash 9 | working-directory: build 10 | -------------------------------------------------------------------------------- /.github/actions/install-macos/action.yml: -------------------------------------------------------------------------------- 1 | name: Install homebrew packages on macOS 2 | 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Install homebrew packages 7 | run: | 8 | brew install \ 9 | boost 10 | shell: bash 11 | -------------------------------------------------------------------------------- /.github/actions/install-protozero/action.yml: -------------------------------------------------------------------------------- 1 | name: Install Protozero from git 2 | 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Install from git 7 | run: | 8 | git clone --quiet --depth 1 \ 9 | https://github.com/mapbox/protozero.git ../protozero 10 | shell: bash 11 | -------------------------------------------------------------------------------- /.github/actions/install-windows/action.yml: -------------------------------------------------------------------------------- 1 | name: Install vcpkg packages on Windows 2 | 3 | runs: 4 | using: composite 5 | steps: 6 | - name: Install packages 7 | run: | 8 | vcpkg install \ 9 | boost-variant:x64-windows 10 | shell: bash 11 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | linux: 7 | runs-on: ubuntu-latest 8 | timeout-minutes: 30 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | image: 13 | - "ubuntu:20.04" # gcc 9.3.0, clang 10.0.0, cmake 3.16.3 14 | - "ubuntu:22.04" # gcc 12.2.0, clang 15.0.7, cmake 3.24.2 15 | - "ubuntu:24.04" # gcc 14.2.0, clang 18.1.3, cmake 3.28.3 16 | - "debian:bullseye" # gcc 10.2.1, clang 11.0.1, cmake 3.18.4 17 | - "debian:bookworm" # gcc 12.2.0, clang 15.0.6, cmake 3.25.1 18 | - "debian:testing" 19 | - "debian:experimental" 20 | - "fedora:39" 21 | - "fedora:40" 22 | - "fedora:41" 23 | build_type: [Debug] 24 | cpp_compiler: [g++] 25 | include: 26 | - image: "debian:bookworm" 27 | c_compiler: clang 28 | cpp_compiler: clang++ 29 | - image: "debian:bookworm" 30 | build_type: Release 31 | - image: "debian:bookworm" 32 | c_compiler: clang 33 | cpp_compiler: clang++ 34 | data_view: std::string_view 35 | - image: "debian:testing" 36 | c_compiler: clang 37 | cpp_compiler: clang++ 38 | - image: "debian:experimental" 39 | c_compiler: clang 40 | cpp_compiler: clang++ 41 | container: 42 | image: ${{ matrix.image }} 43 | env: 44 | BUILD_TYPE: ${{ matrix.build_type }} 45 | CC: ${{ matrix.c_compiler }} 46 | CXX: ${{ matrix.cpp_compiler }} 47 | PROTOZERO_DATA_VIEW: ${{ matrix.data_view }} 48 | APT_LISTCHANGES_FRONTEND: none 49 | DEBIAN_FRONTEND: noninteractive 50 | steps: 51 | - name: Prepare container (apt) 52 | shell: bash 53 | if: startsWith(matrix.image, 'debian:') || startsWith(matrix.image, 'ubuntu:') 54 | run: | 55 | apt-get update -qq 56 | apt-get install -y \ 57 | clang \ 58 | cmake \ 59 | doxygen \ 60 | g++ \ 61 | git \ 62 | graphviz \ 63 | libboost-dev \ 64 | make 65 | - name: Prepare container (dnf) 66 | shell: bash 67 | if: startsWith(matrix.image, 'fedora:') 68 | run: | 69 | dnf install --quiet --assumeyes \ 70 | boost-devel \ 71 | cmake \ 72 | doxygen \ 73 | gcc-c++ \ 74 | git \ 75 | graphviz \ 76 | make 77 | - uses: actions/checkout@v4 78 | with: 79 | submodules: true 80 | - uses: ./.github/actions/install-protozero 81 | - uses: ./.github/actions/cmake 82 | - uses: ./.github/actions/build 83 | - uses: ./.github/actions/ctest 84 | 85 | ubuntu-latest: 86 | runs-on: ubuntu-24.04 87 | timeout-minutes: 30 88 | env: 89 | CC: clang-18 90 | CXX: clang++-18 91 | BUILD_TYPE: Debug 92 | steps: 93 | - uses: actions/checkout@v4 94 | with: 95 | submodules: true 96 | - uses: ./.github/actions/install-protozero 97 | - uses: ./.github/actions/cmake 98 | - uses: ./.github/actions/build 99 | - uses: ./.github/actions/ctest 100 | 101 | macos: 102 | runs-on: ${{ matrix.os }} 103 | timeout-minutes: 30 104 | strategy: 105 | fail-fast: false 106 | matrix: 107 | os: 108 | - macos-14 109 | - macos-15 110 | build_type: [Debug] 111 | include: 112 | - os: macos-15 113 | build_type: Release 114 | env: 115 | CC: clang 116 | CXX: clang++ 117 | BUILD_TYPE: ${{ matrix.build_type }} 118 | steps: 119 | - uses: actions/checkout@v4 120 | with: 121 | submodules: true 122 | - uses: ./.github/actions/install-protozero 123 | - uses: ./.github/actions/install-macos 124 | - uses: ./.github/actions/cmake 125 | - uses: ./.github/actions/build 126 | - uses: ./.github/actions/ctest 127 | 128 | windows: 129 | runs-on: ${{ matrix.os }} 130 | timeout-minutes: 30 131 | strategy: 132 | fail-fast: false 133 | matrix: 134 | os: 135 | - windows-2019 136 | - windows-2022 137 | steps: 138 | - uses: actions/checkout@v4 139 | with: 140 | submodules: true 141 | - uses: ./.github/actions/install-protozero 142 | - uses: ./.github/actions/install-windows 143 | - uses: ./.github/actions/cmake-windows 144 | - uses: ./.github/actions/build 145 | - uses: ./.github/actions/ctest 146 | -------------------------------------------------------------------------------- /.github/workflows/clang-tidy.yml: -------------------------------------------------------------------------------- 1 | name: clang-tidy 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | clang-tidy: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | image: ["debian:bookworm", "debian:testing", "debian:experimental"] 12 | include: 13 | - image: "debian:bookworm" 14 | clang: 15 15 | - image: "debian:testing" 16 | clang: 19 17 | - image: "debian:experimental" 18 | clang: 20 19 | container: 20 | image: ${{ matrix.image }} 21 | env: 22 | BUILD_TYPE: Debug 23 | CC: clang-${{ matrix.clang }} 24 | CXX: clang++-${{ matrix.clang }} 25 | CXXFLAGS: -Wno-c++20-extensions 26 | CPP_VERSION: 14 27 | APT_LISTCHANGES_FRONTEND: none 28 | DEBIAN_FRONTEND: noninteractive 29 | steps: 30 | - name: Prepare container (apt) 31 | run: | 32 | apt-get update -qq 33 | apt-get install -yq \ 34 | clang-${{ matrix.clang }} \ 35 | clang-tidy-${{ matrix.clang }} \ 36 | cmake \ 37 | git \ 38 | make 39 | shell: bash 40 | - uses: actions/checkout@v4 41 | - uses: ./.github/actions/install-protozero 42 | - uses: ./.github/actions/cmake 43 | - name: Run clang-tidy 44 | run: make clang-tidy | tee vtzero-${{ github.sha }}-clang-tidy-${{ matrix.clang }}.log 45 | shell: bash 46 | working-directory: build 47 | - name: Upload clang-tidy log 48 | uses: actions/upload-artifact@v4 49 | if: always() 50 | with: 51 | name: vtzero-${{ github.sha }}-clang-tidy-${{ matrix.clang }}-log 52 | path: build/vtzero-${{ github.sha }}-clang-tidy-${{ matrix.clang }}.log 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/mvt-fixtures"] 2 | path = test/mvt-fixtures 3 | url = https://github.com/mapbox/mvt-fixtures.git 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Changelog 3 | 4 | All notable changes to this project will be documented in this file. 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/) 6 | This project adheres to [Semantic Versioning](https://semver.org/). 7 | 8 | ## [unreleased] - 9 | 10 | ### Added 11 | 12 | ### Changed 13 | 14 | ### Fixed 15 | 16 | 17 | ## [1.2.0] - 2025-01-13 18 | 19 | ### Added 20 | 21 | * Add layer raw size and geometries raw size to vtzero-stats, use CSV format. 22 | 23 | ### Changed 24 | 25 | * Switched to C++14 as minimum requirement (because the Protozero library 26 | we depend on switched). 27 | * Various updates to build and clang-tidy config 28 | * Lots of small code cleanups. 29 | 30 | ### Fixed 31 | 32 | * Remove superfluous std::forwards. 33 | 34 | 35 | ## [1.1.0] - 2020-06-11 36 | 37 | ### Changed 38 | 39 | * Now needs protozero 1.7.0 or above. 40 | * Use `protozero::basic_pbf_builder` to make buffer type configurable. This 41 | allows you to create the final vector tile in any type of buffer, not just 42 | `std::string`. See documentation for details. 43 | * Switch to catch2 for testing. 44 | 45 | ### Fixed 46 | 47 | * Examples `vtzero-create` and `vtzero-streets` now commit features written. 48 | * Various fixes and small cleanups, mostly based on clang-tidy reports. 49 | 50 | 51 | ## [1.0.3] - 2018-07-17 52 | 53 | ### Added 54 | 55 | * New `copy_id()` helper function on feature builder copies ID (if it exists) 56 | from an existing feature. 57 | * New `copy_properties()` helper funtion on feature builder copies all 58 | properties from an existing feature, optionally using a `property_mapper`. 59 | * New `feature::for_each_property_indexes()` member function. 60 | 61 | ### Fixed 62 | 63 | * The example program `vtzero-stats` now catches exceptions and exists with 64 | an error message. 65 | * Fix an assert where a wrong iterator was checked. 66 | 67 | 68 | ## [1.0.2] - 2018-06-26 69 | 70 | ### Fixed 71 | 72 | * `layer_builder::add_feature()` did not work, because it didn't commit 73 | the features it added. 74 | 75 | 76 | ## [1.0.1] - 2018-04-12 77 | 78 | ### Added 79 | 80 | * Some documentation and tests. 81 | 82 | ### Changed 83 | 84 | * Catch exceptions in vtzero-streets example and output error message. 85 | * Adds a template parameter to the `create_property_map` function allowing 86 | mapping between value types. 87 | 88 | ### Fixed 89 | 90 | * The indexes returned by `feature::next_property_indexes()` are now 91 | checked against the size of the key/value tables in the layer. If 92 | an index is too large a `vtzero::out_of_range_exception` is returned. 93 | This way the user code doesn't have to check this. The function 94 | `feature::for_each_property()` now also uses these checks. 95 | 96 | 97 | ## [1.0.0] - 2018-03-09 98 | 99 | First release 100 | 101 | 102 | [unreleased]: https://github.com/osmcode/libosmium/compare/v1.2.0...HEAD 103 | [1.2.0]: https://github.com/osmcode/libosmium/compare/v1.1.0...v1.2.0 104 | [1.1.0]: https://github.com/osmcode/libosmium/compare/v1.0.3...v1.1.0 105 | [1.0.3]: https://github.com/osmcode/libosmium/compare/v1.0.2...v1.0.3 106 | [1.0.2]: https://github.com/osmcode/libosmium/compare/v1.0.1...v1.0.2 107 | [1.0.1]: https://github.com/osmcode/libosmium/compare/v1.0.0...v1.0.1 108 | 109 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------- 2 | # 3 | # CMake config 4 | # 5 | # vtzero 6 | # 7 | #----------------------------------------------------------------------------- 8 | 9 | cmake_minimum_required(VERSION 3.10 FATAL_ERROR) 10 | 11 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") 12 | 13 | #----------------------------------------------------------------------------- 14 | 15 | project(vtzero) 16 | 17 | set(VTZERO_VERSION_MAJOR 1) 18 | set(VTZERO_VERSION_MINOR 2) 19 | set(VTZERO_VERSION_PATCH 0) 20 | 21 | set(VTZERO_VERSION 22 | "${VTZERO_VERSION_MAJOR}.${VTZERO_VERSION_MINOR}.${VTZERO_VERSION_PATCH}") 23 | 24 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 25 | 26 | #----------------------------------------------------------------------------- 27 | 28 | # This variable must be set to the directory where the mvt-fixtures from the 29 | # https://github.com/mapbox/mvt-fixtures repository are to be found. Usually 30 | # this is the directory where the submodule is checked out as described in 31 | # the README, but you can also set this to a different path, for instance 32 | # to change the setting while doing development. 33 | 34 | set(MVT_FIXTURES "${CMAKE_SOURCE_DIR}/test/mvt-fixtures" CACHE PATH "mvt-fixtures directory for tests") 35 | 36 | #----------------------------------------------------------------------------- 37 | 38 | option(WERROR "Add -Werror flag to build (turns warnings into errors)" ON) 39 | 40 | if(MSVC) 41 | add_definitions(-std=c++14 /W3) 42 | add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS) 43 | else() 44 | add_definitions(-std=c++14 -Wall -Wextra -pedantic -Wsign-compare -Wconversion) 45 | # add_definitions(-Weverything -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-padded -Wno-documentation-unknown-command -Wno-exit-time-destructors) 46 | if(WERROR) 47 | add_definitions(-Werror) 48 | endif() 49 | endif() 50 | 51 | include_directories("${CMAKE_SOURCE_DIR}/include") 52 | 53 | set(PROTOZERO_DATA_VIEW "" CACHE STRING "Type used for vtzero::data_view") 54 | if(NOT PROTOZERO_DATA_VIEW STREQUAL "") 55 | add_definitions(-DPROTOZERO_DATA_VIEW=${PROTOZERO_DATA_VIEW}) 56 | endif() 57 | 58 | 59 | #----------------------------------------------------------------------------- 60 | # 61 | # Find dependencies 62 | # 63 | #----------------------------------------------------------------------------- 64 | 65 | find_package(Protozero 1.7.0 REQUIRED) 66 | 67 | include_directories(SYSTEM ${PROTOZERO_INCLUDE_DIR}) 68 | 69 | find_package(Boost) 70 | 71 | 72 | #----------------------------------------------------------------------------- 73 | # 74 | # Optional "clang-tidy" target 75 | # 76 | #----------------------------------------------------------------------------- 77 | message(STATUS "Looking for clang-tidy") 78 | find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-20 clang-tidy-19 clang-tidy-18 clang-tidy-17 clang-tidy-16 clang-tidy-15) 79 | 80 | if(CLANG_TIDY) 81 | message(STATUS "Looking for clang-tidy - found ${CLANG_TIDY}") 82 | add_custom_target(clang-tidy 83 | ${CLANG_TIDY} 84 | -p ${CMAKE_BINARY_DIR} 85 | ${CMAKE_SOURCE_DIR}/examples/*.cpp 86 | ${CMAKE_SOURCE_DIR}/test/*.cpp 87 | ${CMAKE_SOURCE_DIR}/test/t/*.cpp 88 | ) 89 | else() 90 | message(STATUS "Looking for clang-tidy - not found") 91 | message(STATUS " Build target 'clang-tidy' will not be available.") 92 | endif() 93 | 94 | 95 | #----------------------------------------------------------------------------- 96 | # 97 | # Optional "cppcheck" target 98 | # 99 | #----------------------------------------------------------------------------- 100 | message(STATUS "Looking for cppcheck") 101 | find_program(CPPCHECK NAMES cppcheck) 102 | 103 | if(CPPCHECK) 104 | message(STATUS "Looking for cppcheck - found") 105 | add_custom_target(cppcheck 106 | ${CPPCHECK} 107 | -Uassert --std=c++14 --enable=all 108 | ${CMAKE_SOURCE_DIR}/examples/*.cpp 109 | ${CMAKE_SOURCE_DIR}/test/*.cpp 110 | ${CMAKE_SOURCE_DIR}/test/t/*.cpp 111 | ) 112 | else() 113 | message(STATUS "Looking for cppcheck - not found") 114 | message(STATUS " Build target 'cppcheck' will not be available.") 115 | endif() 116 | 117 | 118 | #----------------------------------------------------------------------------- 119 | # 120 | # Include what you use 121 | # 122 | #----------------------------------------------------------------------------- 123 | message(STATUS "Looking for iwyu") 124 | find_program(IWYU_TOOL NAMES iwyu_tool) 125 | 126 | if(IWYU_TOOL) 127 | message(STATUS "Looking for iwyu - found") 128 | add_custom_target(iwyu 129 | ${IWYU_TOOL} -p ${CMAKE_BINARY_DIR} 130 | ) 131 | else() 132 | message(STATUS "Looking for iwyu - not found") 133 | message(STATUS " Build target 'iwyu' will not be available.") 134 | endif() 135 | 136 | 137 | #----------------------------------------------------------------------------- 138 | # 139 | # Installation 140 | # 141 | #----------------------------------------------------------------------------- 142 | 143 | install(DIRECTORY include/vtzero DESTINATION include) 144 | 145 | 146 | #----------------------------------------------------------------------------- 147 | 148 | enable_testing() 149 | 150 | add_subdirectory(doc) 151 | 152 | add_subdirectory(examples) 153 | 154 | add_subdirectory(test) 155 | 156 | 157 | #----------------------------------------------------------------------------- 158 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to vtzero 2 | 3 | ## Releasing 4 | 5 | To release a new vtzero version: 6 | 7 | - Make sure all tests are passing locally and on Github and on Appveyor 8 | - Update version number in 9 | - `CMakeLists.txt` (one place) 10 | - `include/vtzero/version.hpp` (two places) 11 | - Update CHANGELOG.md 12 | - Update UPGRADING.md if necessary 13 | - `git commit -m "Release X.Y.Z" include/vtzero/version.hpp CMakeLists.txt CHANGELOG.md UPGRADING.md` 14 | - `git tag vX.Y.Z` 15 | - `git push` 16 | - `git push --tags` 17 | - Go to https://github.com/mapbox/vtzero/releases 18 | and edit the new release. Put "Version x.y.z" in title and 19 | cut-and-paste entry from CHANGELOG.md. 20 | 21 | ## Updating submodules 22 | 23 | Call `git submodule update --recursive --remote` to update to the newest 24 | version of the mvt fixtures used for testing. 25 | 26 | -------------------------------------------------------------------------------- /EXTERNAL_LICENSES.txt: -------------------------------------------------------------------------------- 1 | 2 | ==== clara.hpp 3 | 4 | Copyright 2017 Two Blue Cubes Ltd. All rights reserved. 5 | 6 | Boost Software License - Version 1.0 - August 17th, 2003 7 | 8 | Permission is hereby granted, free of charge, to any person or organization 9 | obtaining a copy of the software and accompanying documentation covered by 10 | this license (the "Software") to use, reproduce, display, distribute, 11 | execute, and transmit the Software, and to prepare derivative works of the 12 | Software, and to permit third-parties to whom the Software is furnished to 13 | do so, all subject to the following: 14 | 15 | The copyright notices in the Software and this entire statement, including 16 | the above license grant, this restriction and the following disclaimer, 17 | must be included in all copies of the Software, in whole or in part, and 18 | all derivative works of the Software, unless such copies or derivative 19 | works are solely in the form of machine-executable object code generated by 20 | a source language processor. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 25 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 26 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | DEALINGS IN THE SOFTWARE. 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017, Mapbox 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vtzero 2 | 3 | Tiny and fast vector tile decoder and encoder in C++. 4 | 5 | Implements the [Mapbox Vector Tile Specification 2.x](https://www.mapbox.com/vector-tiles/specification). 6 | 7 | [![Github Build Status](https://github.com/mapbox/vtzero/actions/workflows/ci.yml/badge.svg)](https://github.com/mapbox/vtzero/actions/workflows/ci.yml) 8 | [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/mapbox/vtzero?svg=true)](https://ci.appveyor.com/project/Mapbox/vtzero) 9 | 10 | 11 | ## Depends 12 | 13 | * C++14 compiler 14 | * CMake 15 | * [Protozero](https://github.com/mapbox/protozero) version >= 1.7.0 16 | 17 | 18 | ## Build 19 | 20 | First clone `protozero`: 21 | 22 | ``` 23 | git clone git@github.com:mapbox/protozero.git 24 | ``` 25 | 26 | Then clone `vtzero` beside `protozero`. The `vtzero` build system will, among 27 | several places, look for `protozero` at `../protozero`. (If you would like to 28 | use `protozero` from a different path you can set `PROTOZERO_INCLUDE_DIR` in 29 | the CMake configuration step.) 30 | 31 | Then, inside the `vtzero` directory do: 32 | 33 | ``` 34 | git submodule update --init 35 | ``` 36 | 37 | Finally, to build the examples and tests do: 38 | 39 | ``` 40 | mkdir build 41 | cd build 42 | cmake .. 43 | make 44 | ``` 45 | 46 | Call `ctest` to run the tests. 47 | 48 | 49 | ## Examples 50 | 51 | Several examples are provided to show usage of the library. 52 | 53 | Call 54 | 55 | examples/vtzero-create 56 | 57 | to create test tile named `test.mvt`. 58 | 59 | Call 60 | 61 | examples/vtzero-show TILE-FILE 62 | 63 | to show the contents of `TILE-FILE`. 64 | 65 | You can use 66 | 67 | examples/vtzero-check TILE-FILE 68 | 69 | to check vector tile for validity. 70 | 71 | 72 | ## Docs 73 | 74 | Extensive documentation is available: 75 | * [Tutorial](doc/tutorial.md) (start here) 76 | * [Reading vector tiles](doc/reading.md) 77 | * [Writing vector tiles](doc/writing.md) 78 | * [Advanced vtzero topics](doc/advanced.md) 79 | 80 | Make sure to read all of it before using vtzero. Vtzero isn't the simplest 81 | library to use, because it always chooses performance over ease of use. 82 | 83 | If [Doxygen](https://www.doxygen.nl/) is installed on your 84 | system, the build process will automatically create the API docs for you. 85 | The results will be in your build directory under `doc/html`. 86 | 87 | 88 | ## Authors 89 | 90 | Jochen Topf (jochen@topf.org), 91 | Dane Springmeyer (dane@mapbox.com) 92 | 93 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------- 2 | # 3 | # Configuration for continuous integration service at appveyor.com 4 | # 5 | #----------------------------------------------------------------------------- 6 | 7 | platform: x64 8 | 9 | image: Visual Studio 2017 10 | 11 | clone_depth: 1 12 | 13 | #----------------------------------------------------------------------------- 14 | 15 | environment: 16 | matrix: 17 | # commented since this job is currently broken 18 | # - config: MSYS2 19 | # autocrlf: true 20 | - config: Debug 21 | autocrlf: true 22 | - config: RelWithDebInfo 23 | autocrlf: true 24 | - config: Debug 25 | autocrlf: false 26 | - config: RelWithDebInfo 27 | autocrlf: false 28 | 29 | #----------------------------------------------------------------------------- 30 | 31 | init: 32 | - git config --global core.autocrlf %autocrlf% 33 | - git config --get core.autocrlf 34 | 35 | # The option --ask=20 is a workaround for a problem with the MSYS2 update 36 | # process. Without it the following error is printed and the appveyor script 37 | # halts: "msys2-runtime and catgets are in conflict. Remove catgets?" 38 | # See also: https://github.com/Alexpux/MSYS2-packages/issues/1141 39 | install: 40 | - cd c:\projects 41 | - git clone --depth=1 https://github.com/mapbox/protozero 42 | - if [%config%]==[MSYS2] ( 43 | C:\msys64\usr\bin\pacman --noconfirm --sync --refresh --refresh --sysupgrade --sysupgrade --ask=20 44 | && C:\msys64\usr\bin\pacman -Rc --noconfirm mingw-w64-x86_64-gcc-libs 45 | ) 46 | 47 | build_script: 48 | - cd c:\projects\vtzero 49 | - git submodule update --init 50 | - if [%config%]==[MSYS2] ( 51 | build-msys2.bat 52 | ) else ( 53 | build-appveyor.bat 54 | ) 55 | 56 | 57 | #----------------------------------------------------------------------------- 58 | -------------------------------------------------------------------------------- /build-appveyor.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | SETLOCAL 3 | SET EL=0 4 | 5 | ECHO ~~~~~~ %~f0 ~~~~~~ 6 | 7 | ::show all available env vars 8 | SET 9 | ECHO cmake on AppVeyor 10 | cmake -version 11 | 12 | ECHO activating VS cmd prompt && CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" 13 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 14 | 15 | IF EXIST build ECHO deleting build dir... && RD /Q /S build 16 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 17 | 18 | MKDIR build 19 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 20 | 21 | CD build 22 | ECHO config^: %config% 23 | 24 | SET CMAKE_CMD=cmake .. ^ 25 | -LA -G "Visual Studio 15 2017 Win64" 26 | 27 | ECHO calling^: %CMAKE_CMD% 28 | %CMAKE_CMD% 29 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 30 | 31 | SET avlogger= 32 | IF /I "%APPVEYOR%"=="True" SET avlogger=/logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" 33 | 34 | msbuild vtzero.sln ^ 35 | /p:Configuration=%config% ^ 36 | %avlogger% 37 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 38 | 39 | ctest --output-on-failure ^ 40 | -C %config% ^ 41 | IF %ERRORLEVEL% NEQ 0 GOTO ERROR 42 | 43 | GOTO DONE 44 | 45 | :ERROR 46 | ECHO ~~~~~~ ERROR %~f0 ~~~~~~ 47 | SET EL=%ERRORLEVEL% 48 | 49 | :DONE 50 | IF %EL% NEQ 0 ECHO. && ECHO !!! ERRORLEVEL^: %EL% !!! && ECHO. 51 | ECHO ~~~~~~ DONE %~f0 ~~~~~~ 52 | 53 | EXIT /b %EL% 54 | -------------------------------------------------------------------------------- /build-msys2.bat: -------------------------------------------------------------------------------- 1 | echo "Adding MSYS2 to path..." 2 | SET "PATH=C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%" 3 | echo %PATH% 4 | 5 | echo "Installing MSYS2 packages..." 6 | bash -lc "pacman -S --needed --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-boost" 7 | 8 | echo "Generating makefiles" 9 | mkdir build 10 | cd build 11 | cmake .. -LA -G "MSYS Makefiles" 12 | 13 | echo "Building" 14 | make VERBOSE=1 15 | 16 | echo "Testing" 17 | ctest --output-on-failure 18 | 19 | -------------------------------------------------------------------------------- /cmake/FindProtozero.cmake: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------- 2 | # 3 | # FindProtozero.cmake 4 | # 5 | # Find the protozero headers. 6 | # 7 | #---------------------------------------------------------------------- 8 | # 9 | # Usage: 10 | # 11 | # Copy this file somewhere into your project directory, where cmake can 12 | # find it. Usually this will be a directory called "cmake" which you can 13 | # add to the CMake module search path with the following line in your 14 | # CMakeLists.txt: 15 | # 16 | # list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") 17 | # 18 | # Then add the following in your CMakeLists.txt: 19 | # 20 | # find_package(Protozero [version] [REQUIRED]) 21 | # include_directories(SYSTEM ${PROTOZERO_INCLUDE_DIR}) 22 | # 23 | # The version number is optional. If it is not set, any version of 24 | # protozero will do. 25 | # 26 | # if(NOT PROTOZERO_FOUND) 27 | # message(WARNING "Protozero not found!\n") 28 | # endif() 29 | # 30 | #---------------------------------------------------------------------- 31 | # 32 | # Variables: 33 | # 34 | # PROTOZERO_FOUND - True if Protozero was found. 35 | # PROTOZERO_INCLUDE_DIR - Where to find include files. 36 | # 37 | #---------------------------------------------------------------------- 38 | 39 | # find include path 40 | find_path(PROTOZERO_INCLUDE_DIR protozero/version.hpp 41 | PATH_SUFFIXES include 42 | PATHS ../protozero 43 | ) 44 | 45 | # Check version number 46 | if(Protozero_FIND_VERSION) 47 | file(STRINGS "${PROTOZERO_INCLUDE_DIR}/protozero/version.hpp" _version_define REGEX "#define PROTOZERO_VERSION_STRING") 48 | if("${_version_define}" MATCHES "#define PROTOZERO_VERSION_STRING \"([0-9.]+)\"") 49 | set(_version "${CMAKE_MATCH_1}") 50 | else() 51 | set(_version "unknown") 52 | endif() 53 | endif() 54 | 55 | #set(PROTOZERO_INCLUDE_DIRS "${PROTOZERO_INCLUDE_DIR}") 56 | 57 | include(FindPackageHandleStandardArgs) 58 | find_package_handle_standard_args(Protozero 59 | REQUIRED_VARS PROTOZERO_INCLUDE_DIR 60 | VERSION_VAR _version) 61 | 62 | 63 | #---------------------------------------------------------------------- 64 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "bench" 3 | - "test" -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------- 2 | # 3 | # CMake Config 4 | # 5 | # vtzero documentation 6 | # 7 | #----------------------------------------------------------------------------- 8 | 9 | message(STATUS "Configuring documentation") 10 | 11 | message(STATUS "Looking for doxygen") 12 | find_package(Doxygen) 13 | 14 | if(DOXYGEN_FOUND) 15 | message(STATUS "Looking for doxygen - found") 16 | configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) 17 | file(GLOB HEADER_FILES "${CMAKE_SOURCE_DIR}/include/vtzero/*.hpp") 18 | add_custom_command(OUTPUT html/index.html 19 | COMMAND ${DOXYGEN_EXECUTABLE} 20 | ARGS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 21 | DEPENDS Doxyfile.in advanced.md doc.md 22 | ${HEADER_FILES} 23 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 24 | COMMENT "Generating API documentation with Doxygen" VERBATIM) 25 | add_custom_target(doc ALL 26 | DEPENDS html/index.html) 27 | else() 28 | message(STATUS "Looking for doxygen - not found") 29 | message(STATUS " Disabled making of documentation.") 30 | endif() 31 | 32 | #----------------------------------------------------------------------------- 33 | message(STATUS "Configuring documentation - done") 34 | 35 | 36 | #----------------------------------------------------------------------------- 37 | -------------------------------------------------------------------------------- /doc/advanced.md: -------------------------------------------------------------------------------- 1 | 2 | # Advanced vtzero topics 3 | 4 | ## Differences between the protocol buffer specification and the vtzero implementation 5 | 6 | The [protobuf specification 7 | says](https://developers.google.com/protocol-buffers/docs/encoding#optional) 8 | that a decoder library must handle repeated *non-packed* fields if repeated 9 | *packed* fields are expected and it must handle multiple repeated packed fields 10 | as if the items are concatenated. Encoders should never encode fields in this 11 | way, though, so it is very unlikely that this would ever happen. For 12 | performance reasons vtzero doesn't handle this case. 13 | 14 | ## Differences between the vector tile specification and the vtzero implementation 15 | 16 | The [vector tile specification](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/README.md#41-layers) 17 | clearly says that you can not have two layers with the same 18 | name in a vector tile. For performance reasons this is neither checked on 19 | reading nor on writing. 20 | 21 | ## The `create_vtzero_point` customization point 22 | 23 | The vtzero builder classes have several functions which take a `vtzero::point` 24 | as argument. But chances are that you are using a different point type in your 25 | code. That's why these functions have overloads taking any type `TPoint` that 26 | can be converted to a `vtzero::point`. This conversion is done by calling the 27 | function `create_vtzero_point()`. Vtzero supplies a version of this function 28 | which will work with any type with members `x` and `y`: 29 | 30 | ```cpp 31 | template 32 | vtzero::point create_vtzero_point(TPoint p) noexcept { 33 | return {p.x, p.y}; 34 | } 35 | ``` 36 | 37 | You can define your own overload of that function taking your own point type 38 | as parameter and returning a `vtzero::point`. Vtzero will find your function 39 | using [ADL](http://en.cppreference.com/w/cpp/language/adl) which magically 40 | makes the vtzero builders work with your point type. 41 | 42 | ## Using the `property_mapper` class when copying layers 43 | 44 | Sometimes you want to copy some features of a layer into a new layer. Because 45 | you only copy some features (and/or only some properties of the features), the 46 | key and value tables in the layer have to be rebuilt. This is where the 47 | `property_mapper` class helps you. It keeps the mapping between the index 48 | values of the old and the new table adding property keys and values as needed 49 | to the new table. 50 | 51 | Here is some code that shows you how to use it: 52 | 53 | ```cpp 54 | #include // you have to include this 55 | 56 | vtzero::layer layer = ...; // layer you got from an existing tile 57 | vtzero::layer_builder layer_builder{...}; // create new layer 58 | 59 | // instantiate the property mapper with the old and new layers 60 | vtzero::property_mapper mapper{layer, layer_builder}; 61 | 62 | // you'll probably want to iterate over all features in the old layer... 63 | while (auto feature = layer.next_feature()) { 64 | // ... and decide somehow which ones you want to keep 65 | if (keep_feature(feature)) { 66 | // instantiate a feature builder as usual and copy id and geometry 67 | vtzero::geometry_feature_builder feature_builder{layer_builder}; 68 | feature_builder.copy_id(feature); 69 | feature_builder.set_geometry(feature.geometry()); 70 | 71 | // now iterate over all properties... 72 | while (auto idxs = feature.next_property_indexes()) { 73 | // ... decide which ones to keep, 74 | if (keep_property(idxs)) { 75 | // ... and add them to the new layer using the mapper 76 | feature_builder.add_property(mapper(idxs)); 77 | } 78 | } 79 | } 80 | } 81 | ``` 82 | 83 | ## Protection against huge memory use 84 | 85 | When decoding a vector tile we got from an unknown source, we don't know what 86 | surprises it might contain. Building data structures based on the vector tile 87 | sometimes means we have to allocate memory and in the worst case this might be 88 | quite a lot of memory. Vtzero usually doesn't allocate any memory when decoding 89 | a tile, except when reading properties, when there is space for lookup tables 90 | allocated. The memory use for these lookup tables is `sizeof(data_view)` times 91 | the number of entries in the key/value table. In the worst case, when a vector 92 | tile basically only contains such a table, memory use is proportional to the 93 | size of the vector tile. But memory use can be an order of magnitude larger 94 | than the tile size! If you are concerned about memory use, limit the size 95 | of the vector tiles you give to vtzero. 96 | 97 | When reading geometries from vector tiles, vtzero doesn't need much memory 98 | itself, but the users of vtzero might. In a typical case you might reserve 99 | enough memory to store, say, a linestring, and then fill that memory. To allow 100 | you to do this, vtzero tells you about the number of points in the linestring. 101 | This number comes from the tile and it might be rather large. Vtzero does a 102 | consistency check comparing the number of points the geometry says it has with 103 | the number of bytes used for the geometry and it will throw an exception if the 104 | numbers can't fit. So you are protected against tiny tiles pretending to 105 | contain a huge geometry. But there still could be a medium-sized tile which 106 | gets "blown up" into a huge memory hog. Your representation of a linestring 107 | can be an order of magnitude larger than the minimum 2 bytes per point 108 | needed in the encoded tile. 109 | 110 | So again: If you are concerned about memory use, limit the size of the vector 111 | tiles you give to vtzero. 112 | 113 | -------------------------------------------------------------------------------- /doc/doc.md: -------------------------------------------------------------------------------- 1 | 2 | Tiny and fast vector tile decoder and encoder in C++. 3 | 4 | This is the API documentation that was automatically created from the 5 | source code. For more information about vtzero go to 6 | https://github.com/mapbox/vtzero . 7 | 8 | Vtzero is a header-only library. You do not need to compile and link it, 9 | just include the headers you need. 10 | 11 | Everything in namespaces called "detail" is for internal vtzero use only, 12 | do not depend on it in your code. 13 | 14 | -------------------------------------------------------------------------------- /doc/tutorial.md: -------------------------------------------------------------------------------- 1 | 2 | # Vtzero Tutorial 3 | 4 | The vtzero header-only library is used to read and write vector tile data 5 | as specified in the [Mapbox Vector Tile 6 | Specification](https://github.com/mapbox/vector-tile-spec). This document 7 | assumes that you are familiar with that specification. 8 | 9 | ## Overview 10 | 11 | The library has basically two parts: The part concerned with decoding existing 12 | vector tiles and the part concerned with creating new vector tiles. You can 13 | use either one without knowing much about the other side, but it is, of course 14 | also possible to read parts of a vector tile and stick it into a new one. 15 | 16 | Vtzero is trying to do as little work as possible while still giving you a 17 | reasonably easy to use interface. This means that it will, as much as feasible, 18 | decode the different parts of a vector tile only when you ask for them. Most 19 | importantly it will try to avoid memory allocation and it will not copy data 20 | around unnecessarily but work with references instead. 21 | 22 | On the writing side it means that you have to call the API in a specific order 23 | when adding more data to a vector tile. This allows vtzero to avoid multiple 24 | copies of the data. 25 | 26 | ## Basic types 27 | 28 | Vtzero contains several basic small (value) types such as `GeomType`, 29 | `property_value_type`, `index_value`, `index_value_pair`, and `point` which 30 | hold basic values in a type-safe way. Most of them are defined in `types.hpp` 31 | (`point` is in `geometry.hpp`). 32 | 33 | Sometimes it is useful to be able to print the values of those types, for 34 | instance when debugging. For this overloads of `operator<<` on `basic_ostream` 35 | are available in `vtzero/output.hpp`. Include this file and you can use the 36 | usual `std::cout << some_value;` to print those values. 37 | 38 | ## Use of asserts and exceptions 39 | 40 | The vtzero library uses a lot of asserts to help you use it correctly. It is 41 | recommended you use debug builds while developing your code, because you'll 42 | get asserts from vtzero telling you when you use it in a wrong way. This is 43 | especially important when writing tiles using the builder classes, because 44 | their methods have to be called in a certain order that might not always be 45 | obvious but is checked by the asserts. 46 | 47 | Exceptions, on the other hand, are used when errors occur during the normal run 48 | of vtzero. This is especially important on the reading side when vtzero makes 49 | every effort to handle any kind of input, even if the input data is corrupt 50 | in some way. (Vtzero can't detect all problems though, your code still has to 51 | do its own checking, see the [advanced topics](advanced.md) for some more 52 | information.) 53 | 54 | Many vtzero functions can throw exceptions. Most of them fall into these 55 | categories: 56 | 57 | * If the underlying protocol buffers data has some kind of problem, you'll 58 | get an exception from the [protozero 59 | library](https://github.com/mapbox/protozero/blob/master/doc/tutorial.md#asserts-and-exceptions-in-the-protozero-library). 60 | They are all derived from `protozero::exception`. 61 | * If the protocol buffers data is okay, but the vector tile data is invalid 62 | in some way, you'll get an exception from the vtzero library. 63 | * If any memory allocation failed, you'll get a `std::bad_alloc` exception. 64 | 65 | All the exceptions thrown directly by the vtzero library are derived from 66 | `vtzero::exception`. These exceptions are: 67 | 68 | * A `format_exception` is thrown when vector tile encoding isn't valid 69 | according to the vector tile specification. 70 | * A `geometry_exception` is thrown when a geometry encoding isn't valid 71 | according to the vector tile specification. 72 | * A `type_exception` is thrown when a property value is accessed using the 73 | wrong type. 74 | * A `version_exception` is thrown when an unknown version number is found in 75 | the layer. Currently vtzero only supports version 1 and 2. 76 | * An `out_of_range_exception` is thrown when an index into the key or value 77 | table in a layer is out of range. This can only happen if the tile data is 78 | invalid. 79 | 80 | ## Include files 81 | 82 | Usually you only directly include the following files: 83 | 84 | * When reading: `` 85 | * When writing: `` 86 | * If you need any of the special indexes: `` 87 | * If you want overloads of `operator<<` for basic types: `` 88 | * If you need the version: `` 89 | 90 | ## Reading and writing vector tiles 91 | 92 | * [Reading vector tiles](reading.md) 93 | * [Writing vector tiles](writing.md) 94 | 95 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------------- 2 | # 3 | # CMake config 4 | # 5 | # vtzero examples 6 | # 7 | #----------------------------------------------------------------------------- 8 | 9 | include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/include-external") 10 | 11 | set(TEST_FILE "${CMAKE_SOURCE_DIR}/test/data/mapbox-streets-v6-14-8714-8017.mvt") 12 | 13 | add_executable(vtzero-check vtzero-check.cpp utils.cpp) 14 | 15 | add_executable(vtzero-create vtzero-create.cpp utils.cpp) 16 | 17 | add_executable(vtzero-encode-geom vtzero-encode-geom.cpp utils.cpp) 18 | 19 | add_executable(vtzero-stats vtzero-stats.cpp utils.cpp) 20 | 21 | add_executable(vtzero-streets vtzero-streets.cpp utils.cpp) 22 | 23 | #------------------------------------------------------------- 24 | 25 | add_executable(vtzero-filter vtzero-filter.cpp utils.cpp) 26 | 27 | add_test(NAME vtzero-filter-empty 28 | COMMAND vtzero-filter) 29 | set_tests_properties(vtzero-filter-empty PROPERTIES 30 | PASS_REGULAR_EXPRESSION "^Error in command line: Missing file name of vector tile to read") 31 | 32 | add_test(NAME vtzero-filter-help 33 | COMMAND vtzero-filter -h) 34 | set_tests_properties(vtzero-filter-help PROPERTIES 35 | PASS_REGULAR_EXPRESSION "^usage:\n vtzero-filter") 36 | 37 | add_test(NAME vtzero-filter-layer 38 | COMMAND vtzero-filter -o ${CMAKE_CURRENT_BINARY_DIR}/bridges.mvt ${TEST_FILE} bridge) 39 | 40 | add_test(NAME vtzero-filter-feature 41 | COMMAND vtzero-filter -o ${CMAKE_CURRENT_BINARY_DIR}/bridges.mvt ${TEST_FILE} waterway_label 221925711) 42 | 43 | add_test(NAME vtzero-filter-invalid-id 44 | COMMAND vtzero-filter -o ${CMAKE_CURRENT_BINARY_DIR}/bridges.mvt ${TEST_FILE} waterway_label abc) 45 | set_tests_properties(vtzero-filter-invalid-id PROPERTIES 46 | WILL_FAIL true) 47 | 48 | #------------------------------------------------------------- 49 | 50 | add_executable(vtzero-show vtzero-show.cpp utils.cpp) 51 | 52 | add_test(NAME vtzero-show-empty 53 | COMMAND vtzero-show) 54 | set_tests_properties(vtzero-show-empty PROPERTIES 55 | PASS_REGULAR_EXPRESSION "^Error in command line: Missing file name of vector tile to read") 56 | 57 | add_test(NAME vtzero-show-help 58 | COMMAND vtzero-show -h) 59 | set_tests_properties(vtzero-show-help PROPERTIES 60 | PASS_REGULAR_EXPRESSION "^usage:\n vtzero-show") 61 | 62 | add_test(NAME vtzero-show-layers 63 | COMMAND vtzero-show -l ${TEST_FILE}) 64 | set_tests_properties(vtzero-show-layers PROPERTIES 65 | PASS_REGULAR_EXPRESSION "^landuse 78\nwaterway 327\n.*\nwaterway_label 4\n$") 66 | 67 | add_test(NAME vtzero-show-layer-num 68 | COMMAND vtzero-show ${TEST_FILE} 2) 69 | set_tests_properties(vtzero-show-layer-num PROPERTIES 70 | PASS_REGULAR_EXPRESSION "layer: [0-9]+\n name: water\n") 71 | 72 | add_test(NAME vtzero-show-layer-name 73 | COMMAND vtzero-show ${CMAKE_SOURCE_DIR}/test/data/mapbox-streets-v6-14-8714-8017.mvt water) 74 | set_tests_properties(vtzero-show-layer-name PROPERTIES 75 | PASS_REGULAR_EXPRESSION "layer: [0-9]+\n name: water\n") 76 | 77 | #------------------------------------------------------------- 78 | 79 | file(GLOB ext_tests RELATIVE ${CMAKE_SOURCE_DIR}/test/data/ ${CMAKE_SOURCE_DIR}/test/data/*.mvt) 80 | 81 | foreach(_test IN LISTS ext_tests) 82 | message(STATUS "Adding ext test: ${_test}") 83 | add_test(NAME ext-tests-${_test} 84 | COMMAND vtzero-show ${CMAKE_SOURCE_DIR}/test/data/${_test}) 85 | endforeach() 86 | 87 | 88 | #----------------------------------------------------------------------------- 89 | -------------------------------------------------------------------------------- /examples/utils.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | Utility functions for vtzero example programs. 4 | 5 | *****************************************************************************/ 6 | 7 | #include "utils.hpp" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | /** 18 | * Read complete contents of a file into a string. 19 | * 20 | * The file is read in binary mode. 21 | * 22 | * @param filename The file name. Can be empty or "-" to read from STDIN. 23 | * @returns a string with the contents of the file. 24 | * @throws various exceptions if there is an error 25 | */ 26 | std::string read_file(const std::string& filename) { 27 | if (filename.empty() || (filename.size() == 1 && filename[0] == '-')) { 28 | return std::string{std::istreambuf_iterator(std::cin.rdbuf()), 29 | std::istreambuf_iterator()}; 30 | } 31 | 32 | std::ifstream stream{filename, std::ios_base::in | std::ios_base::binary}; 33 | if (!stream) { 34 | throw std::runtime_error{std::string{"Can not open file '"} + filename + "'"}; 35 | } 36 | 37 | stream.exceptions(std::ifstream::failbit); 38 | 39 | std::string buffer{std::istreambuf_iterator(stream.rdbuf()), 40 | std::istreambuf_iterator()}; 41 | stream.close(); 42 | 43 | return buffer; 44 | } 45 | 46 | /** 47 | * Write contents of a buffer into a file. 48 | * 49 | * The file is written in binary mode. 50 | * 51 | * @param buffer The data to be written. 52 | * @param filename The file name. 53 | * @throws various exceptions if there is an error 54 | */ 55 | void write_data_to_file(const std::string& buffer, const std::string& filename) { 56 | std::ofstream stream{filename, std::ios_base::out | std::ios_base::binary}; 57 | if (!stream) { 58 | throw std::runtime_error{std::string{"Can not open file '"} + filename + "'"}; 59 | } 60 | 61 | stream.exceptions(std::ifstream::failbit); 62 | 63 | stream.write(buffer.data(), static_cast(buffer.size())); 64 | 65 | stream.close(); 66 | } 67 | 68 | /** 69 | * Get a specific layer from a vector tile. The layer can be specified as a 70 | * number n in which case the nth layer in this tile is returned. Or it can 71 | * be specified as text, in which case the layer with that name is returned. 72 | * 73 | * Calls exit(1) if there is an error. 74 | * 75 | * @param tile The vector tile. 76 | * @param layer_name_or_num specifies the layer. 77 | */ 78 | vtzero::layer get_layer(const vtzero::vector_tile& tile, const std::string& layer_name_or_num) { 79 | vtzero::layer layer; 80 | char* str_end = nullptr; 81 | const long num = std::strtol(layer_name_or_num.c_str(), &str_end, 10); // NOLINT(google-runtime-int) 82 | 83 | if (str_end == layer_name_or_num.data() + layer_name_or_num.size()) { 84 | if (num >= 0 && num < std::numeric_limits::max()) { // NOLINT(google-runtime-int) 85 | layer = tile.get_layer(static_cast(num)); 86 | if (!layer) { 87 | std::cerr << "No such layer: " << num << '\n'; 88 | std::exit(1); 89 | } 90 | return layer; 91 | } 92 | } 93 | 94 | layer = tile.get_layer_by_name(layer_name_or_num); 95 | if (!layer) { 96 | std::cerr << "No layer named '" << layer_name_or_num << "'.\n"; 97 | std::exit(1); 98 | } 99 | return layer; 100 | } 101 | 102 | -------------------------------------------------------------------------------- /examples/utils.hpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | 6 | std::string read_file(const std::string& filename); 7 | 8 | void write_data_to_file(const std::string& buffer, const std::string& filename); 9 | 10 | vtzero::layer get_layer(const vtzero::vector_tile& tile, const std::string& layer_name_or_num); 11 | 12 | -------------------------------------------------------------------------------- /examples/vtzero-check.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | Example program for vtzero library. 4 | 5 | vtzero-check - Check vector tiles for validity 6 | 7 | *****************************************************************************/ 8 | 9 | #include "utils.hpp" 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace { 20 | 21 | class result { 22 | 23 | int m_return_code = 0; 24 | 25 | public: 26 | 27 | void has_warning() noexcept { 28 | if (m_return_code < 1) { 29 | m_return_code = 1; 30 | } 31 | } 32 | 33 | void has_error() noexcept { 34 | if (m_return_code < 2) { 35 | m_return_code = 2; 36 | } 37 | } 38 | 39 | void has_fatal_error() noexcept { 40 | if (m_return_code < 3) { 41 | m_return_code = 3; 42 | } 43 | } 44 | 45 | int return_code() const noexcept { 46 | return m_return_code; 47 | } 48 | 49 | } result; 50 | 51 | class CheckGeomHandler { 52 | 53 | vtzero::point m_prev_point; 54 | int m_layer_num; 55 | int m_feature_num; 56 | int64_t m_extent; 57 | bool m_is_first_point = false; 58 | int m_count = 0; 59 | 60 | void print_context() const { 61 | std::cerr << " in layer " << m_layer_num 62 | << " in feature " << m_feature_num 63 | << " in geometry " << m_count 64 | << ": "; 65 | } 66 | 67 | void print_error(const char* message) const { 68 | result.has_error(); 69 | std::cerr << "Error"; 70 | print_context(); 71 | std::cerr << message << '\n'; 72 | } 73 | 74 | void print_warning(const char* message) const { 75 | result.has_warning(); 76 | std::cerr << "Warning"; 77 | print_context(); 78 | std::cerr << message << '\n'; 79 | } 80 | 81 | void check_point_location(const vtzero::point point) const { 82 | if (point.x < -m_extent || 83 | point.y < -m_extent || 84 | point.x > 2 * m_extent || 85 | point.y > 2 * m_extent) { 86 | print_warning("point waaaay beyond the extent"); 87 | } 88 | } 89 | 90 | public: 91 | 92 | CheckGeomHandler(uint32_t extent, int layer_num, int feature_num) : 93 | m_layer_num(layer_num), 94 | m_feature_num(feature_num), 95 | m_extent(static_cast(extent)) { 96 | } 97 | 98 | // ---------------------------------------------------------------------- 99 | 100 | void points_begin(const uint32_t /*count*/) const { 101 | } 102 | 103 | void points_point(const vtzero::point point) const { 104 | check_point_location(point); 105 | } 106 | 107 | void points_end() const { 108 | } 109 | 110 | // ---------------------------------------------------------------------- 111 | 112 | void linestring_begin(const uint32_t count) { 113 | if (count < 2) { 114 | print_error("Not enough points in linestring"); 115 | } 116 | m_is_first_point = true; 117 | } 118 | 119 | void linestring_point(const vtzero::point point) { 120 | if (m_is_first_point) { 121 | m_is_first_point = false; 122 | } else { 123 | if (point == m_prev_point) { 124 | print_error("Duplicate point in linestring"); 125 | } 126 | } 127 | m_prev_point = point; 128 | 129 | check_point_location(point); 130 | } 131 | 132 | void linestring_end() { 133 | ++m_count; 134 | } 135 | 136 | // ---------------------------------------------------------------------- 137 | 138 | void ring_begin(const uint32_t count) { 139 | if (count < 4) { 140 | print_error("Not enough points in ring"); 141 | } 142 | m_is_first_point = true; 143 | } 144 | 145 | void ring_point(const vtzero::point point) { 146 | if (m_is_first_point) { 147 | m_is_first_point = false; 148 | } else { 149 | if (point == m_prev_point) { 150 | print_error("Duplicate point in ring"); 151 | } 152 | } 153 | m_prev_point = point; 154 | 155 | check_point_location(point); 156 | } 157 | 158 | void ring_end(const vtzero::ring_type rt) { 159 | if (rt == vtzero::ring_type::invalid) { 160 | print_error("Invalid ring with area 0"); 161 | } 162 | if (m_count == 0 && rt != vtzero::ring_type::outer) { 163 | print_error("First ring isn't an outer ring"); 164 | } 165 | ++m_count; 166 | } 167 | 168 | }; // class CheckGeomHandler 169 | 170 | } // anonymous namespace 171 | 172 | int main(int argc, char* argv[]) { 173 | if (argc != 2) { 174 | std::cerr << "Usage: " << argv[0] << " TILE\n"; 175 | return 1; 176 | } 177 | 178 | const std::string input_file{argv[1]}; 179 | const auto data = read_file(input_file); 180 | 181 | std::set layer_names; 182 | 183 | vtzero::vector_tile tile{data}; 184 | 185 | int layer_num = 0; 186 | int feature_num = -1; 187 | try { 188 | while (auto layer = tile.next_layer()) { 189 | if (layer.name().empty()) { 190 | std::cerr << "Error in layer " << layer_num << ": name is empty (spec 4.1)\n"; 191 | result.has_error(); 192 | } 193 | 194 | const std::string name{layer.name()}; 195 | if (layer_names.count(name) > 0) { 196 | std::cerr << "Error in layer " << layer_num << ": name is duplicate of previous layer ('" << name << "') (spec 4.1)\n"; 197 | result.has_error(); 198 | } 199 | 200 | layer_names.insert(name); 201 | 202 | feature_num = 0; 203 | while (auto feature = layer.next_feature()) { 204 | CheckGeomHandler handler{layer.extent(), layer_num, feature_num}; 205 | vtzero::decode_geometry(feature.geometry(), handler); 206 | ++feature_num; 207 | } 208 | if (feature_num == 0) { 209 | std::cerr << "Warning: No features in layer " << layer_num << " (spec 4.1)\n"; 210 | result.has_warning(); 211 | } 212 | feature_num = -1; 213 | ++layer_num; 214 | } 215 | if (layer_num == 0) { 216 | std::cerr << "Warning: No layers in vector tile (spec 4.1)\n"; 217 | result.has_warning(); 218 | } 219 | } catch (const std::exception& e) { 220 | std::cerr << "Fatal error in layer " << layer_num; 221 | if (feature_num >= 0) { 222 | std::cerr << " in feature " << feature_num; 223 | } 224 | std::cerr << ": " << e.what() << '\n'; 225 | result.has_fatal_error(); 226 | } 227 | 228 | return result.return_code(); 229 | } 230 | 231 | -------------------------------------------------------------------------------- /examples/vtzero-create.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | Example program for vtzero library. 4 | 5 | vtzero-create - Create a vector tile 6 | 7 | *****************************************************************************/ 8 | 9 | #include "utils.hpp" 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | int main() { 22 | try { 23 | vtzero::tile_builder tile; 24 | vtzero::layer_builder layer_points{tile, "points"}; 25 | vtzero::layer_builder layer_lines{tile, "lines"}; 26 | const vtzero::layer_builder layer_polygons{tile, "polygons"}; 27 | 28 | vtzero::key_index idx{layer_points}; 29 | 30 | { 31 | vtzero::point_feature_builder feature{layer_points}; 32 | feature.set_id(1); 33 | feature.add_points(1); 34 | feature.set_point(10, 10); 35 | feature.add_property("foo", "bar"); 36 | feature.add_property("x", "y"); 37 | feature.rollback(); 38 | } 39 | 40 | const auto some = idx("some"); 41 | 42 | { 43 | vtzero::point_feature_builder feature{layer_points}; 44 | feature.set_id(2); 45 | feature.add_point(20, 20); 46 | feature.add_property(some, "attr"); 47 | feature.commit(); 48 | } 49 | { 50 | vtzero::point_feature_builder feature{layer_points}; 51 | feature.set_id(3); 52 | feature.add_point(20, 20); 53 | feature.add_property(idx("some"), "attr"); 54 | feature.commit(); 55 | } 56 | 57 | { 58 | vtzero::point_feature_builder feature{layer_points}; 59 | feature.set_id(4); 60 | feature.add_point(20, 20); 61 | feature.add_property(idx("some"), "otherattr"); 62 | feature.commit(); 63 | } 64 | 65 | 66 | vtzero::point_feature_builder feature1{layer_points}; 67 | feature1.set_id(5); 68 | feature1.add_point(vtzero::point{20, 20}); 69 | feature1.add_property("otherkey", "attr"); 70 | feature1.commit(); 71 | 72 | vtzero::value_index maxspeed_index{layer_lines}; 73 | { 74 | vtzero::linestring_feature_builder feature{layer_lines}; 75 | feature.set_id(6); 76 | feature.add_linestring(3); 77 | feature.set_point(10, 10); 78 | feature.set_point(10, 20); 79 | feature.set_point(vtzero::point{20, 20}); 80 | const std::vector points = {{11, 11}, {12, 13}}; 81 | feature.add_linestring_from_container(points); 82 | feature.add_property("highway", "primary"); 83 | feature.add_property(std::string{"maxspeed"}, maxspeed_index(50)); 84 | feature.commit(); 85 | } 86 | 87 | { 88 | vtzero::polygon_feature_builder feature{layer_polygons}; 89 | feature.set_id(7); 90 | feature.add_ring(5); 91 | feature.set_point(0, 0); 92 | feature.set_point(10, 0); 93 | feature.set_point(10, 10); 94 | feature.set_point(0, 10); 95 | feature.set_point(0, 0); 96 | feature.add_ring(4); 97 | feature.set_point(3, 3); 98 | feature.set_point(3, 5); 99 | feature.set_point(5, 5); 100 | feature.close_ring(); 101 | feature.add_property("natural", "wood"); 102 | feature.add_property("number_of_trees", vtzero::sint_value_type{23402752}); 103 | feature.commit(); 104 | } 105 | 106 | const auto data = tile.serialize(); 107 | write_data_to_file(data, "test.mvt"); 108 | } catch (const std::exception& e) { 109 | std::cerr << "Fatal error: " << e.what() << '\n'; 110 | return 1; 111 | } 112 | 113 | return 0; 114 | } 115 | 116 | -------------------------------------------------------------------------------- /examples/vtzero-encode-geom.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | Example program for vtzero library. 4 | 5 | vtzero-encode-geom - Encode geometry on command line 6 | 7 | This can be used for debugging. Uses internals of vtzero! 8 | 9 | *****************************************************************************/ 10 | 11 | #include "utils.hpp" 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace { 24 | 25 | int64_t get_int(const char* arg) { 26 | char* endptr = nullptr; 27 | const int64_t value = std::strtoll(arg, &endptr, 10); 28 | 29 | if (*endptr == '\0') { 30 | return value; 31 | } 32 | 33 | throw std::runtime_error{"not a valid number"}; 34 | } 35 | 36 | uint32_t move_to(const char* arg) { 37 | if (!std::isdigit(arg[0])) { 38 | throw std::runtime_error{"need count after M command"}; 39 | } 40 | 41 | const auto scount = get_int(arg); 42 | if (scount <= 0) { 43 | throw std::runtime_error{"count after M command must be 1 or larger"}; 44 | } 45 | const auto count = static_cast(scount); 46 | std::cout << "MOVE_TO(" << count << ")\t" << vtzero::detail::command_move_to(count) << '\n'; 47 | 48 | return vtzero::detail::command_move_to(count); 49 | } 50 | 51 | uint32_t line_to(const char* arg) { 52 | if (!std::isdigit(arg[0])) { 53 | throw std::runtime_error{"need count after L command"}; 54 | } 55 | 56 | const auto scount = get_int(arg); 57 | if (scount <= 0) { 58 | throw std::runtime_error{"count after L command must be 1 or larger"}; 59 | } 60 | const auto count = static_cast(scount); 61 | std::cout << "LINE_TO(" << count << ")\t" << vtzero::detail::command_line_to(count) << '\n'; 62 | 63 | return vtzero::detail::command_line_to(count); 64 | } 65 | 66 | uint32_t close_path(const char* arg) { 67 | if (arg[0] != '\0') { 68 | throw std::runtime_error{"extra data after C command"}; 69 | } 70 | std::cout << "CLOSE_PATH\t" << vtzero::detail::command_close_path() << '\n'; 71 | 72 | return vtzero::detail::command_close_path(); 73 | } 74 | 75 | uint32_t number(const char* arg) { 76 | const auto num = static_cast(get_int(arg)); 77 | std::cout << "number(" << num << ")\t" << protozero::encode_zigzag32(num) << '\n'; 78 | 79 | return protozero::encode_zigzag32(num); 80 | } 81 | 82 | } // anonymous namespace 83 | 84 | int main(int argc, char* argv[]) { 85 | if (argc < 2) { 86 | std::cerr << "Usage: " << argv[0] << " GEOMETRY ELEMENTS...\n" 87 | << "GEOMETRY ELEMENTS are:\n" 88 | << " M[count] -- MOVE_TO count\n" 89 | << " L[count] -- LINE_TO count\n" 90 | << " C -- CLOSE_PATH\n" 91 | << " [number] -- number that will be zigzag encoded\n"; 92 | return 1; 93 | } 94 | 95 | std::vector values; 96 | 97 | std::cout << "raw data\tencoded\n-----------------------------------\n"; 98 | for (int i = 1; i < argc; ++i) { 99 | try { 100 | switch (argv[i][0]) { 101 | case '\0': 102 | break; 103 | case 'M': 104 | values.push_back(move_to(argv[i] + 1)); 105 | break; 106 | case 'L': 107 | values.push_back(line_to(argv[i] + 1)); 108 | break; 109 | case 'C': 110 | values.push_back(close_path(argv[i] + 1)); 111 | break; 112 | case '-': 113 | case '0': 114 | case '1': 115 | case '2': 116 | case '3': 117 | case '4': 118 | case '5': 119 | case '6': 120 | case '7': 121 | case '8': 122 | case '9': 123 | values.push_back(number(argv[i])); 124 | break; 125 | default: 126 | throw std::runtime_error{std::string{"unknown data: "} + argv[i]}; 127 | return 1; 128 | } 129 | } catch (const std::runtime_error& e) { 130 | std::cerr << "error(" << i << "): " << e.what() << '\n'; 131 | return 1; 132 | } 133 | } 134 | 135 | std::string out{"["}; 136 | 137 | for (auto value : values) { 138 | out += ' '; 139 | out += std::to_string(value); 140 | out += ','; 141 | } 142 | 143 | out.back() = ' '; 144 | 145 | std::cout << '\n' << out << "]\n"; 146 | 147 | return 0; 148 | } 149 | 150 | -------------------------------------------------------------------------------- /examples/vtzero-filter.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | Example program for vtzero library. 4 | 5 | vtzero-filter - Copy parts of a vector tile into a new tile. 6 | 7 | *****************************************************************************/ 8 | 9 | #include "utils.hpp" 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | int main(int argc, char* argv[]) { 24 | try { 25 | std::string filename; 26 | std::string layer_num_or_name; 27 | std::string idstr; 28 | std::string output_file{"filtered.mvt"}; 29 | 30 | bool help = false; 31 | 32 | const auto cli 33 | = clara::Opt(output_file, "FILE") 34 | ["-o"]["--output"] 35 | ("write output to FILE") 36 | | clara::Help(help) 37 | | clara::Arg(filename, "FILENAME").required() 38 | ("vector tile") 39 | | clara::Arg(layer_num_or_name, "LAYER-NUM|LAYER-NAME").required() 40 | ("layer") 41 | | clara::Arg(idstr, "ID") 42 | ("feature_id"); 43 | 44 | const auto result = cli.parse(clara::Args(argc, argv)); 45 | if (!result) { 46 | std::cerr << "Error in command line: " << result.errorMessage() << '\n'; 47 | return 1; 48 | } 49 | 50 | if (help) { 51 | std::cout << cli 52 | << "\nFilter contents of vector tile.\n"; 53 | return 0; 54 | } 55 | 56 | if (filename.empty()) { 57 | std::cerr << "Error in command line: Missing file name of vector tile to read\n"; 58 | return 1; 59 | } 60 | 61 | if (layer_num_or_name.empty()) { 62 | std::cerr << "Error in command line: Missing layer number or name\n"; 63 | return 1; 64 | } 65 | 66 | const auto data = read_file(filename); 67 | const vtzero::vector_tile tile{data}; 68 | 69 | auto layer = get_layer(tile, layer_num_or_name); 70 | std::cerr << "Found layer: " << std::string(layer.name()) << "\n"; 71 | 72 | vtzero::tile_builder tb; 73 | 74 | if (idstr.empty()) { 75 | tb.add_existing_layer(layer); 76 | } else { 77 | char* str_end = nullptr; 78 | const int64_t id = std::strtoll(idstr.c_str(), &str_end, 10); 79 | if (str_end != idstr.c_str() + idstr.size()) { 80 | std::cerr << "Feature ID must be numeric.\n"; 81 | return 1; 82 | } 83 | if (id < 0) { 84 | std::cerr << "Feature ID must be >= 0.\n"; 85 | return 1; 86 | } 87 | 88 | const auto feature = layer.get_feature_by_id(static_cast(id)); 89 | if (!feature.valid()) { 90 | std::cerr << "No feature with that id: " << id << '\n'; 91 | return 1; 92 | } 93 | 94 | vtzero::layer_builder layer_builder{tb, layer}; 95 | layer_builder.add_feature(feature); 96 | } 97 | 98 | const std::string output = tb.serialize(); 99 | 100 | write_data_to_file(output, output_file); 101 | } catch (const std::exception& e) { 102 | std::cerr << "Fatal error: " << e.what() << '\n'; 103 | return 1; 104 | } 105 | 106 | return 0; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /examples/vtzero-show.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | Example program for vtzero library. 4 | 5 | vtzero-show - Show content of vector tile 6 | 7 | *****************************************************************************/ 8 | 9 | #include "utils.hpp" 10 | 11 | #include 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | class geom_handler { 21 | 22 | std::string output; 23 | uint64_t num = 0; 24 | 25 | public: 26 | 27 | void points_begin(const uint32_t /*count*/) const noexcept { 28 | } 29 | 30 | void points_point(const vtzero::point point) { 31 | std::cout << " [" << num << "] POINT(" << point.x << ',' << point.y << ")\n"; 32 | ++num; 33 | } 34 | 35 | void points_end() const noexcept { 36 | } 37 | 38 | void linestring_begin(const uint32_t count) { 39 | output = " ["; 40 | output += std::to_string(num); 41 | output += "] LINESTRING[count="; 42 | output += std::to_string(count); 43 | output += "]("; 44 | ++num; 45 | } 46 | 47 | void linestring_point(const vtzero::point point) { 48 | output += std::to_string(point.x); 49 | output += ' '; 50 | output += std::to_string(point.y); 51 | output += ','; 52 | } 53 | 54 | void linestring_end() { 55 | if (output.empty()) { 56 | return; 57 | } 58 | if (output.back() == ',') { 59 | output.resize(output.size() - 1); 60 | } 61 | output += ")\n"; 62 | std::cout << output; 63 | } 64 | 65 | void ring_begin(const uint32_t count) { 66 | output = " ["; 67 | output += std::to_string(num); 68 | output += "] RING[count="; 69 | output += std::to_string(count); 70 | output += "]("; 71 | ++num; 72 | } 73 | 74 | void ring_point(const vtzero::point point) { 75 | output += std::to_string(point.x); 76 | output += ' '; 77 | output += std::to_string(point.y); 78 | output += ','; 79 | } 80 | 81 | void ring_end(const vtzero::ring_type rt) { 82 | if (output.empty()) { 83 | return; 84 | } 85 | if (output.back() == ',') { 86 | output.back() = ')'; 87 | } 88 | switch (rt) { 89 | case vtzero::ring_type::outer: 90 | output += "[OUTER]\n"; 91 | break; 92 | case vtzero::ring_type::inner: 93 | output += "[INNER]\n"; 94 | break; 95 | default: 96 | output += "[INVALID]\n"; 97 | } 98 | std::cout << output; 99 | } 100 | 101 | }; // class geom_handler 102 | 103 | template 104 | std::basic_ostream& operator<<(std::basic_ostream& out, vtzero::data_view value) { 105 | out.write(value.data(), static_cast(value.size())); 106 | return out; 107 | } 108 | 109 | struct print_value { 110 | 111 | template 112 | void operator()(T value) const { 113 | std::cout << value; 114 | } 115 | 116 | void operator()(const vtzero::data_view value) const { 117 | std::cout << '"' << value << '"'; 118 | } 119 | 120 | }; // struct print_value 121 | 122 | namespace { 123 | 124 | void print_layer(vtzero::layer& layer, bool print_tables, bool print_value_types, int layer_num, int& feature_num) { 125 | std::cout << "=============================================================\n" 126 | << "layer: " << layer_num << '\n' 127 | << " name: " << std::string(layer.name()) << '\n' 128 | << " version: " << layer.version() << '\n' 129 | << " extent: " << layer.extent() << '\n'; 130 | 131 | if (print_tables) { 132 | std::cout << " keys:\n"; 133 | int n = 0; 134 | for (const auto& key : layer.key_table()) { 135 | std::cout << " " << n++ << ": " << key << '\n'; 136 | } 137 | std::cout << " values:\n"; 138 | n = 0; 139 | for (const vtzero::property_value& value : layer.value_table()) { 140 | std::cout << " " << n++ << ": "; 141 | vtzero::apply_visitor(print_value{}, value); 142 | if (print_value_types) { 143 | std::cout << " [" << vtzero::property_value_type_name(value.type()) << "]\n"; 144 | } else { 145 | std::cout << '\n'; 146 | } 147 | } 148 | } 149 | 150 | feature_num = 0; 151 | while (auto feature = layer.next_feature()) { 152 | std::cout << " feature: " << feature_num << '\n' 153 | << " id: "; 154 | if (feature.has_id()) { 155 | std::cout << feature.id() << '\n'; 156 | } else { 157 | std::cout << "(none)\n"; 158 | } 159 | std::cout << " geomtype: " << vtzero::geom_type_name(feature.geometry_type()) << '\n' 160 | << " geometry:\n"; 161 | vtzero::decode_geometry(feature.geometry(), geom_handler{}); 162 | std::cout << " properties:\n"; 163 | while (auto property = feature.next_property()) { 164 | std::cout << " " << property.key() << '='; 165 | vtzero::apply_visitor(print_value{}, property.value()); 166 | if (print_value_types) { 167 | std::cout << " [" << vtzero::property_value_type_name(property.value().type()) << "]\n"; 168 | } else { 169 | std::cout << '\n'; 170 | } 171 | } 172 | ++feature_num; 173 | } 174 | } 175 | 176 | void print_layer_overview(const vtzero::layer& layer) { 177 | std::cout << layer.name() << ' ' << layer.num_features() << '\n'; 178 | } 179 | 180 | } // anonymous namespace 181 | 182 | int main(int argc, char* argv[]) { 183 | std::string filename; 184 | std::string layer_num_or_name; 185 | bool layer_overview = false; 186 | bool print_tables = false; 187 | bool print_value_types = false; 188 | bool help = false; 189 | 190 | const auto cli 191 | = clara::Opt(layer_overview) 192 | ["-l"]["--layers"] 193 | ("show layer overview with feature count") 194 | | clara::Opt(print_tables) 195 | ["-t"]["--tables"] 196 | ("also print key/value tables") 197 | | clara::Opt(print_value_types) 198 | ["-T"]["--value-types"] 199 | ("also show value types") 200 | | clara::Help(help) 201 | | clara::Arg(filename, "FILENAME").required() 202 | ("vector tile") 203 | | clara::Arg(layer_num_or_name, "LAYER-NUM|LAYER-NAME") 204 | ("layer"); 205 | 206 | const auto result = cli.parse(clara::Args(argc, argv)); 207 | if (!result) { 208 | std::cerr << "Error in command line: " << result.errorMessage() << '\n'; 209 | return 1; 210 | } 211 | 212 | if (help) { 213 | std::cout << cli 214 | << "\nShow contents of vector tile FILENAME.\n"; 215 | return 0; 216 | } 217 | 218 | if (filename.empty()) { 219 | std::cerr << "Error in command line: Missing file name of vector tile to read\n"; 220 | return 1; 221 | } 222 | 223 | int layer_num = 0; 224 | int feature_num = 0; 225 | try { 226 | const auto data = read_file(filename); 227 | 228 | vtzero::vector_tile tile{data}; 229 | 230 | if (layer_num_or_name.empty()) { 231 | while (auto layer = tile.next_layer()) { 232 | if (layer_overview) { 233 | print_layer_overview(layer); 234 | } else { 235 | print_layer(layer, print_tables, print_value_types, layer_num, feature_num); 236 | } 237 | ++layer_num; 238 | } 239 | } else { 240 | auto layer = get_layer(tile, layer_num_or_name); 241 | if (layer_overview) { 242 | print_layer_overview(layer); 243 | } else { 244 | print_layer(layer, print_tables, print_value_types, layer_num, feature_num); 245 | } 246 | } 247 | } catch (const std::exception& e) { 248 | std::cerr << "Error in layer " << layer_num << " (feature " << feature_num << "): " << e.what() << '\n'; 249 | return 1; 250 | } 251 | 252 | return 0; 253 | } 254 | 255 | -------------------------------------------------------------------------------- /examples/vtzero-stats.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | Example program for vtzero library. 4 | 5 | vtzero-stats - Output some stats on layers 6 | 7 | *****************************************************************************/ 8 | 9 | #include "utils.hpp" 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace { 19 | 20 | std::size_t geometries_size(const vtzero::layer& layer) { 21 | std::size_t sum = 0; 22 | 23 | layer.for_each_feature([&sum](vtzero::feature&& feature) { 24 | sum += feature.geometry().data().size(); 25 | return true; 26 | }); 27 | 28 | return sum; 29 | } 30 | 31 | } // anonymous namespace 32 | 33 | int main(int argc, char* argv[]) { 34 | if (argc != 2) { 35 | std::cerr << "Usage: " << argv[0] << " TILE\n"; 36 | return 1; 37 | } 38 | 39 | std::cout << "layer,num_features,raw_size,raw_geometries_size,key_table_size,value_table_size\n"; 40 | try { 41 | const std::string input_file{argv[1]}; 42 | const auto data = read_file(input_file); 43 | 44 | vtzero::vector_tile tile{data}; 45 | 46 | while (const auto layer = tile.next_layer()) { 47 | std::cout.write(layer.name().data(), static_cast(layer.name().size())); 48 | std::cout << ',' 49 | << layer.num_features() << ',' 50 | << layer.data().size() << ',' 51 | << geometries_size(layer) << ',' 52 | << layer.key_table().size() << ',' 53 | << layer.value_table().size() << '\n'; 54 | } 55 | } catch (const std::exception& e) { 56 | std::cerr << "Error: " << e.what() << '\n'; 57 | return 1; 58 | } 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /examples/vtzero-streets.cpp: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | 3 | Example program for vtzero library. 4 | 5 | vtzero-streets - Copy features from road_label layer if they have property 6 | class="street". Output is always in file "streets.mvt". 7 | 8 | *****************************************************************************/ 9 | 10 | #include "utils.hpp" 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | namespace { 21 | 22 | bool keep_feature(const vtzero::feature& feature) { 23 | static const std::string key{"class"}; 24 | static const std::string val{"street"}; 25 | 26 | bool found = false; 27 | 28 | feature.for_each_property([&](const vtzero::property& prop) { 29 | found = key == prop.key() && val == prop.value().string_value(); 30 | return !found; 31 | }); 32 | 33 | return found; 34 | } 35 | 36 | } // anonymous namespace 37 | 38 | int main(int argc, char* argv[]) { 39 | if (argc != 2) { 40 | std::cerr << "Usage: " << argv[0] << " TILE\n"; 41 | return 1; 42 | } 43 | 44 | const std::string input_file{argv[1]}; 45 | const std::string output_file{"streets.mvt"}; 46 | 47 | const auto data = read_file(input_file); 48 | 49 | try { 50 | const vtzero::vector_tile tile{data}; 51 | 52 | auto layer = get_layer(tile, "road_label"); 53 | if (!layer) { 54 | std::cerr << "No 'road_label' layer found\n"; 55 | return 1; 56 | } 57 | 58 | vtzero::tile_builder tb; 59 | vtzero::layer_builder layer_builder{tb, layer}; 60 | 61 | vtzero::property_mapper mapper{layer, layer_builder}; 62 | 63 | while (auto feature = layer.next_feature()) { 64 | if (keep_feature(feature)) { 65 | vtzero::geometry_feature_builder feature_builder{layer_builder}; 66 | feature_builder.copy_id(feature); 67 | feature_builder.set_geometry(feature.geometry()); 68 | 69 | while (auto idxs = feature.next_property_indexes()) { 70 | feature_builder.add_property(mapper(idxs)); 71 | } 72 | 73 | feature_builder.commit(); 74 | } 75 | } 76 | 77 | const std::string output = tb.serialize(); 78 | write_data_to_file(output, output_file); 79 | } catch (const std::exception& e) { 80 | std::cerr << "Error: " << e.what() << '\n'; 81 | return 1; 82 | } 83 | 84 | return 0; 85 | } 86 | 87 | -------------------------------------------------------------------------------- /include/vtzero/builder_impl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VTZERO_BUILDER_IMPL_HPP 2 | #define VTZERO_BUILDER_IMPL_HPP 3 | 4 | /***************************************************************************** 5 | 6 | vtzero - Tiny and fast vector tile decoder and encoder in C++. 7 | 8 | This file is from https://github.com/mapbox/vtzero where you can find more 9 | documentation. 10 | 11 | *****************************************************************************/ 12 | 13 | /** 14 | * @file builder_impl.hpp 15 | * 16 | * @brief Contains classes internal to the builder. 17 | */ 18 | 19 | #include "encoded_property_value.hpp" 20 | #include "property_value.hpp" 21 | #include "types.hpp" 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | namespace vtzero { 32 | 33 | namespace detail { 34 | 35 | class layer_builder_impl { 36 | 37 | // If this layer is copied from an existing layer, this points 38 | // to the data of the original layer. For newly built layers, 39 | // this is empty. 40 | data_view m_data_view; 41 | 42 | // Buffer containing the encoded layer metadata and features 43 | std::string m_data; 44 | 45 | // Buffer containing the encoded keys table 46 | std::string m_keys_data; 47 | 48 | // Buffer containing the encoded values table 49 | std::string m_values_data; 50 | 51 | protozero::pbf_builder m_pbf_message_layer; 52 | protozero::pbf_builder m_pbf_message_keys; 53 | protozero::pbf_builder m_pbf_message_values; 54 | 55 | // The number of features in the layer 56 | std::size_t m_num_features = 0; 57 | 58 | // Vector tile spec version 59 | uint32_t m_version = 0; 60 | 61 | // The number of keys in the keys table 62 | uint32_t m_num_keys = 0; 63 | 64 | // The number of values in the values table 65 | uint32_t m_num_values = 0; 66 | 67 | // Below this value, no index will be used to find entries in the 68 | // key/value tables. This number is based on some initial 69 | // benchmarking but probably needs some tuning. 70 | // See also https://github.com/mapbox/vtzero/issues/30 71 | static constexpr const uint32_t max_entries_flat = 20; 72 | 73 | using map_type = std::unordered_map; 74 | map_type m_keys_index; 75 | map_type m_values_index; 76 | 77 | static index_value find_in_table(const data_view text, const std::string& data) { 78 | uint32_t index = 0; 79 | protozero::pbf_message pbf_table{data}; 80 | 81 | while (pbf_table.next()) { 82 | const auto v = pbf_table.get_view(); 83 | if (v == text) { 84 | return index_value{index}; 85 | } 86 | ++index; 87 | } 88 | 89 | return index_value{}; 90 | } 91 | 92 | // Read the key or value table and populate an index from its 93 | // entries. This is done once the table becomes too large to do 94 | // linear search in it. 95 | static void populate_index(const std::string& data, map_type& map) { 96 | uint32_t index = 0; 97 | protozero::pbf_message pbf_table{data}; 98 | 99 | while (pbf_table.next()) { 100 | map[pbf_table.get_string()] = index++; 101 | } 102 | } 103 | 104 | index_value add_value_without_dup_check(const data_view text) { 105 | m_pbf_message_values.add_string(detail::pbf_layer::values, text); 106 | return m_num_values++; 107 | } 108 | 109 | index_value add_value(const data_view text) { 110 | const auto index = find_in_values_table(text); 111 | if (index.valid()) { 112 | return index; 113 | } 114 | return add_value_without_dup_check(text); 115 | } 116 | 117 | index_value find_in_keys_table(const data_view text) { 118 | if (m_num_keys < max_entries_flat) { 119 | return find_in_table(text, m_keys_data); 120 | } 121 | 122 | if (m_keys_index.empty()) { 123 | populate_index(m_keys_data, m_keys_index); 124 | } 125 | 126 | auto& v = m_keys_index[std::string(text)]; 127 | if (!v.valid()) { 128 | v = add_key_without_dup_check(text); 129 | } 130 | return v; 131 | } 132 | 133 | index_value find_in_values_table(const data_view text) { 134 | if (m_num_values < max_entries_flat) { 135 | return find_in_table(text, m_values_data); 136 | } 137 | 138 | if (m_values_index.empty()) { 139 | populate_index(m_values_data, m_values_index); 140 | } 141 | 142 | auto& v = m_values_index[std::string(text)]; 143 | if (!v.valid()) { 144 | v = add_value_without_dup_check(text); 145 | } 146 | return v; 147 | } 148 | 149 | public: 150 | 151 | // This layer should be a copy of an existing layer 152 | explicit layer_builder_impl(const data_view data) : 153 | m_data_view(data) { 154 | } 155 | 156 | // This layer is being created from scratch 157 | template 158 | layer_builder_impl(TString&& name, uint32_t version, uint32_t extent) : 159 | m_pbf_message_layer(m_data), 160 | m_pbf_message_keys(m_keys_data), 161 | m_pbf_message_values(m_values_data), 162 | m_version(version) { 163 | m_pbf_message_layer.add_uint32(detail::pbf_layer::version, version); 164 | m_pbf_message_layer.add_string(detail::pbf_layer::name, std::forward(name)); 165 | m_pbf_message_layer.add_uint32(detail::pbf_layer::extent, extent); 166 | } 167 | 168 | ~layer_builder_impl() noexcept = default; 169 | 170 | layer_builder_impl(const layer_builder_impl&) = delete; 171 | layer_builder_impl& operator=(const layer_builder_impl&) = delete; 172 | 173 | layer_builder_impl(layer_builder_impl&&) = default; 174 | layer_builder_impl& operator=(layer_builder_impl&&) = default; 175 | 176 | uint32_t version() const noexcept { 177 | return m_version; 178 | } 179 | 180 | index_value add_key_without_dup_check(const data_view text) { 181 | m_pbf_message_keys.add_string(detail::pbf_layer::keys, text); 182 | return m_num_keys++; 183 | } 184 | 185 | index_value add_key(const data_view text) { 186 | const auto index = find_in_keys_table(text); 187 | if (index.valid()) { 188 | return index; 189 | } 190 | return add_key_without_dup_check(text); 191 | } 192 | 193 | index_value add_value_without_dup_check(const property_value value) { 194 | return add_value_without_dup_check(value.data()); 195 | } 196 | 197 | index_value add_value_without_dup_check(const encoded_property_value& value) { 198 | return add_value_without_dup_check(value.data()); 199 | } 200 | 201 | index_value add_value(const property_value value) { 202 | return add_value(value.data()); 203 | } 204 | 205 | index_value add_value(const encoded_property_value& value) { 206 | return add_value(value.data()); 207 | } 208 | 209 | const std::string& data() const noexcept { 210 | return m_data; 211 | } 212 | 213 | const std::string& keys_data() const noexcept { 214 | return m_keys_data; 215 | } 216 | 217 | const std::string& values_data() const noexcept { 218 | return m_values_data; 219 | } 220 | 221 | protozero::pbf_builder& message() noexcept { 222 | return m_pbf_message_layer; 223 | } 224 | 225 | void increment_feature_count() noexcept { 226 | ++m_num_features; 227 | } 228 | 229 | std::size_t estimated_size() const { 230 | if (m_data_view.data()) { 231 | // This is a layer created as copy from an existing layer 232 | constexpr const std::size_t estimated_overhead_for_pbf_encoding = 8; 233 | return m_data_view.size() + estimated_overhead_for_pbf_encoding; 234 | } 235 | 236 | // This is a layer created from scratch 237 | constexpr const std::size_t estimated_overhead_for_pbf_encoding = 8; 238 | return data().size() + 239 | keys_data().size() + 240 | values_data().size() + 241 | estimated_overhead_for_pbf_encoding; 242 | } 243 | 244 | template 245 | void build(protozero::basic_pbf_builder& pbf_tile_builder) const { 246 | if (m_data_view.data()) { 247 | // This is a layer created as copy from an existing layer 248 | pbf_tile_builder.add_bytes(detail::pbf_tile::layers, m_data_view); 249 | return; 250 | } 251 | 252 | // This is a layer created from scratch 253 | if (m_num_features > 0) { 254 | pbf_tile_builder.add_bytes_vectored(detail::pbf_tile::layers, 255 | data(), 256 | keys_data(), 257 | values_data()); 258 | } 259 | } 260 | 261 | }; // class layer_builder_impl 262 | 263 | } // namespace detail 264 | 265 | } // namespace vtzero 266 | 267 | #endif // VTZERO_BUILDER_IMPL_HPP 268 | -------------------------------------------------------------------------------- /include/vtzero/encoded_property_value.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VTZERO_ENCODED_PROPERTY_VALUE_HPP 2 | #define VTZERO_ENCODED_PROPERTY_VALUE_HPP 3 | 4 | /***************************************************************************** 5 | 6 | vtzero - Tiny and fast vector tile decoder and encoder in C++. 7 | 8 | This file is from https://github.com/mapbox/vtzero where you can find more 9 | documentation. 10 | 11 | *****************************************************************************/ 12 | 13 | /** 14 | * @file encoded_property_value.hpp 15 | * 16 | * @brief Contains the encoded_property_value class. 17 | */ 18 | 19 | #include "types.hpp" 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | namespace vtzero { 27 | 28 | /** 29 | * A property value encoded in the vector_tile internal format. Can be 30 | * created from values of many different types and then later added to 31 | * a layer/feature. 32 | */ 33 | class encoded_property_value { 34 | 35 | std::string m_data; 36 | 37 | public: 38 | 39 | /// Construct from vtzero::string_value_type. 40 | explicit encoded_property_value(string_value_type value) { 41 | protozero::pbf_builder pbf_message_value{m_data}; 42 | pbf_message_value.add_string(detail::pbf_value::string_value, value.value); 43 | } 44 | 45 | /// Construct from const char*. 46 | explicit encoded_property_value(const char* value) { 47 | protozero::pbf_builder pbf_message_value{m_data}; 48 | pbf_message_value.add_string(detail::pbf_value::string_value, value); 49 | } 50 | 51 | /// Construct from const char* and size_t. 52 | explicit encoded_property_value(const char* value, std::size_t size) { 53 | protozero::pbf_builder pbf_message_value{m_data}; 54 | pbf_message_value.add_string(detail::pbf_value::string_value, value, size); 55 | } 56 | 57 | /// Construct from std::string. 58 | explicit encoded_property_value(const std::string& value) { 59 | protozero::pbf_builder pbf_message_value{m_data}; 60 | pbf_message_value.add_string(detail::pbf_value::string_value, value); 61 | } 62 | 63 | /// Construct from vtzero::data_view. 64 | explicit encoded_property_value(const data_view& value) { 65 | protozero::pbf_builder pbf_message_value{m_data}; 66 | pbf_message_value.add_string(detail::pbf_value::string_value, value); 67 | } 68 | 69 | // ------------------ 70 | 71 | /// Construct from vtzero::float_value_type. 72 | explicit encoded_property_value(float_value_type value) { 73 | protozero::pbf_builder pbf_message_value{m_data}; 74 | pbf_message_value.add_float(detail::pbf_value::float_value, value.value); 75 | } 76 | 77 | /// Construct from float. 78 | explicit encoded_property_value(float value) { 79 | protozero::pbf_builder pbf_message_value{m_data}; 80 | pbf_message_value.add_float(detail::pbf_value::float_value, value); 81 | } 82 | 83 | // ------------------ 84 | 85 | /// Construct from vtzero::double_value_type. 86 | explicit encoded_property_value(double_value_type value) { 87 | protozero::pbf_builder pbf_message_value{m_data}; 88 | pbf_message_value.add_double(detail::pbf_value::double_value, value.value); 89 | } 90 | 91 | /// Construct from double. 92 | explicit encoded_property_value(double value) { 93 | protozero::pbf_builder pbf_message_value{m_data}; 94 | pbf_message_value.add_double(detail::pbf_value::double_value, value); 95 | } 96 | 97 | // ------------------ 98 | 99 | /// Construct from vtzero::int_value_type. 100 | explicit encoded_property_value(int_value_type value) { 101 | protozero::pbf_builder pbf_message_value{m_data}; 102 | pbf_message_value.add_int64(detail::pbf_value::int_value, value.value); 103 | } 104 | 105 | /// Construct from int64_t. 106 | explicit encoded_property_value(int64_t value) { 107 | protozero::pbf_builder pbf_message_value{m_data}; 108 | pbf_message_value.add_int64(detail::pbf_value::int_value, value); 109 | } 110 | 111 | /// Construct from int32_t. 112 | explicit encoded_property_value(int32_t value) { 113 | protozero::pbf_builder pbf_message_value{m_data}; 114 | pbf_message_value.add_int64(detail::pbf_value::int_value, static_cast(value)); 115 | } 116 | 117 | /// Construct from int16_t. 118 | explicit encoded_property_value(int16_t value) { 119 | protozero::pbf_builder pbf_message_value{m_data}; 120 | pbf_message_value.add_int64(detail::pbf_value::int_value, static_cast(value)); 121 | } 122 | 123 | // ------------------ 124 | 125 | /// Construct from vtzero::uint_value_type. 126 | explicit encoded_property_value(uint_value_type value) { 127 | protozero::pbf_builder pbf_message_value{m_data}; 128 | pbf_message_value.add_uint64(detail::pbf_value::uint_value, value.value); 129 | } 130 | 131 | /// Construct from uint64_t. 132 | explicit encoded_property_value(uint64_t value) { 133 | protozero::pbf_builder pbf_message_value{m_data}; 134 | pbf_message_value.add_uint64(detail::pbf_value::uint_value, value); 135 | } 136 | 137 | /// Construct from uint32_t. 138 | explicit encoded_property_value(uint32_t value) { 139 | protozero::pbf_builder pbf_message_value{m_data}; 140 | pbf_message_value.add_uint64(detail::pbf_value::uint_value, static_cast(value)); 141 | } 142 | 143 | /// Construct from uint16_t. 144 | explicit encoded_property_value(uint16_t value) { 145 | protozero::pbf_builder pbf_message_value{m_data}; 146 | pbf_message_value.add_uint64(detail::pbf_value::uint_value, static_cast(value)); 147 | } 148 | 149 | // ------------------ 150 | 151 | /// Construct from vtzero::sint_value_type. 152 | explicit encoded_property_value(sint_value_type value) { 153 | protozero::pbf_builder pbf_message_value{m_data}; 154 | pbf_message_value.add_sint64(detail::pbf_value::sint_value, value.value); 155 | } 156 | 157 | // ------------------ 158 | 159 | /// Construct from vtzero::bool_value_type. 160 | explicit encoded_property_value(bool_value_type value) { 161 | protozero::pbf_builder pbf_message_value{m_data}; 162 | pbf_message_value.add_bool(detail::pbf_value::bool_value, value.value); 163 | } 164 | 165 | /// Construct from bool. 166 | explicit encoded_property_value(bool value) { 167 | protozero::pbf_builder pbf_message_value{m_data}; 168 | pbf_message_value.add_bool(detail::pbf_value::bool_value, value); 169 | } 170 | 171 | // ------------------ 172 | 173 | /** 174 | * Get view of the raw data stored inside. 175 | */ 176 | data_view data() const noexcept { 177 | return {m_data.data(), m_data.size()}; 178 | } 179 | 180 | /** 181 | * Hash function compatible with std::hash. 182 | */ 183 | std::size_t hash() const noexcept { 184 | return std::hash{}(m_data); 185 | } 186 | 187 | }; // class encoded_property_value 188 | 189 | /// Encoded property values are equal if they contain the same data. 190 | inline bool operator==(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept { 191 | return lhs.data() == rhs.data(); 192 | } 193 | 194 | /// Encoded property values are unequal if they are not equal. 195 | inline bool operator!=(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept { 196 | return !(lhs == rhs); 197 | } 198 | 199 | /// Arbitrary ordering based on internal data. 200 | inline bool operator<(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept { 201 | return lhs.data() < rhs.data(); 202 | } 203 | 204 | /// Arbitrary ordering based on internal data. 205 | inline bool operator<=(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept { 206 | return lhs.data() <= rhs.data(); 207 | } 208 | 209 | /// Arbitrary ordering based on internal data. 210 | inline bool operator>(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept { 211 | return lhs.data() > rhs.data(); 212 | } 213 | 214 | /// Arbitrary ordering based on internal data. 215 | inline bool operator>=(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept { 216 | return lhs.data() >= rhs.data(); 217 | } 218 | 219 | } // namespace vtzero 220 | 221 | namespace std { 222 | 223 | /** 224 | * Specialization of std::hash for encoded_property_value. 225 | */ 226 | template <> 227 | struct hash { 228 | 229 | /// key vtzero::encoded_property_value 230 | using argument_type = vtzero::encoded_property_value; 231 | 232 | /// hash result: size_t 233 | using result_type = std::size_t; 234 | 235 | /// calculate the hash of the argument 236 | std::size_t operator()(const vtzero::encoded_property_value& value) const noexcept { 237 | return value.hash(); 238 | } 239 | 240 | }; // struct hash 241 | 242 | } // namespace std 243 | 244 | #endif // VTZERO_ENCODED_PROPERTY_VALUE_HPP 245 | -------------------------------------------------------------------------------- /include/vtzero/exception.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VTZERO_EXCEPTION_HPP 2 | #define VTZERO_EXCEPTION_HPP 3 | 4 | /***************************************************************************** 5 | 6 | vtzero - Tiny and fast vector tile decoder and encoder in C++. 7 | 8 | This file is from https://github.com/mapbox/vtzero where you can find more 9 | documentation. 10 | 11 | *****************************************************************************/ 12 | 13 | /** 14 | * @file exception.hpp 15 | * 16 | * @brief Contains the exceptions used in the vtzero library. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | namespace vtzero { 24 | 25 | /** 26 | * Base class for all exceptions directly thrown by the vtzero library. 27 | */ 28 | class exception : public std::runtime_error { 29 | 30 | public: 31 | 32 | /// Constructor 33 | explicit exception(const char* message) : 34 | std::runtime_error(message) { 35 | } 36 | 37 | /// Constructor 38 | explicit exception(const std::string& message) : 39 | std::runtime_error(message) { 40 | } 41 | 42 | }; // class exception 43 | 44 | /** 45 | * This exception is thrown when vector tile encoding isn't valid according 46 | * to the vector tile specification. 47 | */ 48 | class format_exception : public exception { 49 | 50 | public: 51 | 52 | /// Constructor 53 | explicit format_exception(const char* message) : 54 | exception(message) { 55 | } 56 | 57 | /// Constructor 58 | explicit format_exception(const std::string& message) : 59 | exception(message) { 60 | } 61 | 62 | }; // class format_exception 63 | 64 | /** 65 | * This exception is thrown when a geometry encoding isn't valid according 66 | * to the vector tile specification. 67 | */ 68 | class geometry_exception : public format_exception { 69 | 70 | public: 71 | 72 | /// Constructor 73 | explicit geometry_exception(const char* message) : 74 | format_exception(message) { 75 | } 76 | 77 | /// Constructor 78 | explicit geometry_exception(const std::string& message) : 79 | format_exception(message) { 80 | } 81 | 82 | }; // class geometry_exception 83 | 84 | /** 85 | * This exception is thrown when a property value is accessed using the 86 | * wrong type. 87 | */ 88 | class type_exception : public exception { 89 | 90 | public: 91 | 92 | /// Constructor 93 | explicit type_exception() : 94 | exception("wrong property value type") { 95 | } 96 | 97 | }; // class type_exception 98 | 99 | /** 100 | * This exception is thrown when an unknown version number is found in the 101 | * layer. 102 | */ 103 | class version_exception : public exception { 104 | 105 | public: 106 | 107 | /// Constructor 108 | explicit version_exception(const uint32_t version) : 109 | exception(std::string{"unknown vector tile version: "} + 110 | std::to_string(version)) { 111 | } 112 | 113 | }; // version_exception 114 | 115 | /** 116 | * This exception is thrown when an index into the key or value table 117 | * in a layer is out of range. This can only happen if the tile data is 118 | * invalid. 119 | */ 120 | class out_of_range_exception : public exception { 121 | 122 | public: 123 | 124 | /// Constructor 125 | explicit out_of_range_exception(const uint32_t index) : 126 | exception(std::string{"index out of range: "} + 127 | std::to_string(index)) { 128 | } 129 | 130 | }; // out_of_range_exception 131 | 132 | } // namespace vtzero 133 | 134 | #endif // VTZERO_EXCEPTION_HPP 135 | -------------------------------------------------------------------------------- /include/vtzero/feature_builder_impl.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VTZERO_FEATURE_BUILDER_IMPL_HPP 2 | #define VTZERO_FEATURE_BUILDER_IMPL_HPP 3 | 4 | /***************************************************************************** 5 | 6 | vtzero - Tiny and fast vector tile decoder and encoder in C++. 7 | 8 | This file is from https://github.com/mapbox/vtzero where you can find more 9 | documentation. 10 | 11 | *****************************************************************************/ 12 | 13 | /** 14 | * @file feature_builder_impl.hpp 15 | * 16 | * @brief Contains classes internal to the builder. 17 | */ 18 | 19 | #include "builder_impl.hpp" 20 | #include "encoded_property_value.hpp" 21 | #include "geometry.hpp" 22 | #include "property.hpp" 23 | #include "property_value.hpp" 24 | 25 | #include 26 | 27 | namespace vtzero { 28 | 29 | namespace detail { 30 | 31 | class feature_builder_base { 32 | 33 | layer_builder_impl* m_layer; 34 | 35 | void add_key_internal(index_value idx) { 36 | vtzero_assert(idx.valid()); 37 | m_pbf_tags.add_element(idx.value()); 38 | } 39 | 40 | template 41 | void add_key_internal(T&& key) { 42 | add_key_internal(m_layer->add_key(data_view{std::forward(key)})); 43 | } 44 | 45 | void add_value_internal(index_value idx) { 46 | vtzero_assert(idx.valid()); 47 | m_pbf_tags.add_element(idx.value()); 48 | } 49 | 50 | void add_value_internal(property_value value) { 51 | add_value_internal(m_layer->add_value(value)); 52 | } 53 | 54 | template 55 | void add_value_internal(T&& value) { 56 | const encoded_property_value v{std::forward(value)}; 57 | add_value_internal(m_layer->add_value(v)); 58 | } 59 | 60 | protected: 61 | 62 | protozero::pbf_builder m_feature_writer; 63 | protozero::packed_field_uint32 m_pbf_tags; 64 | 65 | explicit feature_builder_base(layer_builder_impl* layer) : 66 | m_layer(layer), 67 | m_feature_writer(layer->message(), detail::pbf_layer::features) { 68 | } 69 | 70 | ~feature_builder_base() noexcept = default; 71 | 72 | feature_builder_base(const feature_builder_base&) = delete; // NOLINT(hicpp-use-equals-delete, modernize-use-equals-delete) 73 | 74 | feature_builder_base& operator=(const feature_builder_base&) = delete; // NOLINT(hicpp-use-equals-delete, modernize-use-equals-delete) 75 | // The check wants these functions to be public... 76 | 77 | feature_builder_base(feature_builder_base&&) noexcept = default; 78 | 79 | feature_builder_base& operator=(feature_builder_base&&) noexcept = default; 80 | 81 | uint32_t version() const noexcept { 82 | return m_layer->version(); 83 | } 84 | 85 | void set_id_impl(uint64_t id) { 86 | m_feature_writer.add_uint64(detail::pbf_feature::id, id); 87 | } 88 | 89 | void add_property_impl(const property& property) { 90 | add_key_internal(property.key()); 91 | add_value_internal(property.value()); 92 | } 93 | 94 | void add_property_impl(const index_value_pair idxs) { 95 | add_key_internal(idxs.key()); 96 | add_value_internal(idxs.value()); 97 | } 98 | 99 | template 100 | void add_property_impl(TKey&& key, TValue&& value) { 101 | add_key_internal(std::forward(key)); 102 | add_value_internal(std::forward(value)); 103 | } 104 | 105 | void do_commit() { 106 | if (m_pbf_tags.valid()) { 107 | m_pbf_tags.commit(); 108 | } 109 | m_feature_writer.commit(); 110 | m_layer->increment_feature_count(); 111 | } 112 | 113 | void do_rollback() { 114 | if (m_pbf_tags.valid()) { 115 | m_pbf_tags.rollback(); 116 | } 117 | m_feature_writer.rollback(); 118 | } 119 | 120 | }; // class feature_builder_base 121 | 122 | } // namespace detail 123 | 124 | } // namespace vtzero 125 | 126 | #endif // VTZERO_FEATURE_BUILDER_IMPL_HPP 127 | -------------------------------------------------------------------------------- /include/vtzero/index.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VTZERO_INDEX_HPP 2 | #define VTZERO_INDEX_HPP 3 | 4 | /***************************************************************************** 5 | 6 | vtzero - Tiny and fast vector tile decoder and encoder in C++. 7 | 8 | This file is from https://github.com/mapbox/vtzero where you can find more 9 | documentation. 10 | 11 | *****************************************************************************/ 12 | 13 | /** 14 | * @file index.hpp 15 | * 16 | * @brief Contains classes for indexing the key/value tables inside layers. 17 | */ 18 | 19 | #include "builder.hpp" 20 | #include "types.hpp" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace vtzero { 27 | 28 | /** 29 | * Used to store the mapping between property keys and the index value 30 | * in the table stored in a layer. 31 | * 32 | * @tparam TMap The map class to use (std::map, std::unordered_map or 33 | * something compatible). 34 | */ 35 | template