Squashed 'third_party/vtzero/' content from commit 2b43c8f5c
git-subtree-dir: third_party/vtzero git-subtree-split: 2b43c8f5c7cc461a874a657673dfc15f8fb21d1d
This commit is contained in:
commit
6bee8866de
48
.clang-tidy
Normal file
48
.clang-tidy
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
Checks: '*,-cert-dcl21-cpp,-cert-err60-cpp,-clang-analyzer-core.CallAndMessage,-cppcoreguidelines-owning-memory,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-fuchsia-*,-google-runtime-references,-hicpp-no-array-decay,-readability-avoid-const-params-in-decls,-readability-implicit-bool-cast,-readability-implicit-bool-conversion'
|
||||||
|
#
|
||||||
|
# Disabled checks:
|
||||||
|
#
|
||||||
|
# cert-dcl21-cpp
|
||||||
|
# It is unclear whether this is still a good recommendation in modern C++.
|
||||||
|
#
|
||||||
|
# cert-err60-cpp
|
||||||
|
# Reports std::runtime_error as broken which we can't do anything about.
|
||||||
|
#
|
||||||
|
# clang-analyzer-core.CallAndMessage
|
||||||
|
# Produces false positives
|
||||||
|
#
|
||||||
|
# cppcoreguidelines-owning-memory
|
||||||
|
# Don't want to add dependency on gsl library.
|
||||||
|
#
|
||||||
|
# cppcoreguidelines-pro-bounds-array-to-pointer-decay
|
||||||
|
# Limited use and many false positives including for all asserts.
|
||||||
|
#
|
||||||
|
# cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||||
|
# This is a low-level library, it needs to do pointer arithmetic.
|
||||||
|
#
|
||||||
|
# fuchsia-*
|
||||||
|
# Much too strict.
|
||||||
|
#
|
||||||
|
# google-runtime-references
|
||||||
|
# This is just a matter of preference, and we can't change the interfaces
|
||||||
|
# now anyways.
|
||||||
|
#
|
||||||
|
# hicpp-no-array-decay
|
||||||
|
# Limited use and many false positives including for all asserts.
|
||||||
|
#
|
||||||
|
# readability-avoid-const-params-in-decls
|
||||||
|
# Inconsistently complaines about some cases but not others. It is nice
|
||||||
|
# to have const in parameters if we don't change them just like with any
|
||||||
|
# other variables.
|
||||||
|
#
|
||||||
|
# readability-implicit-bool-cast
|
||||||
|
# Old name for readability-implicit-bool-conversion.
|
||||||
|
#
|
||||||
|
# readability-implicit-bool-conversion
|
||||||
|
# I don't think this makes the code more readable.
|
||||||
|
#
|
||||||
|
WarningsAsErrors: '*'
|
||||||
|
HeaderFilterRegex: '\/include\/'
|
||||||
|
AnalyzeTemporaryDtors: false
|
||||||
|
...
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "test/mvt-fixtures"]
|
||||||
|
path = test/mvt-fixtures
|
||||||
|
url = https://github.com/mapbox/mvt-fixtures.git
|
178
.travis.yml
Normal file
178
.travis.yml
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Configuration for continuous integration service at travis-ci.org
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
language: generic
|
||||||
|
|
||||||
|
sudo: false
|
||||||
|
|
||||||
|
dist: trusty
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Save common build configurations as shortcuts, so we can reference them later.
|
||||||
|
addons_shortcuts:
|
||||||
|
addons_clang35: &clang35
|
||||||
|
apt:
|
||||||
|
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-3.5' ]
|
||||||
|
packages: [ 'libboost1.55-dev', 'clang-3.5' ]
|
||||||
|
addons_clang38: &clang38
|
||||||
|
apt:
|
||||||
|
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-3.8' ]
|
||||||
|
packages: [ 'libboost1.55-dev', 'clang-3.8' ]
|
||||||
|
addons_clang39: &clang39
|
||||||
|
apt:
|
||||||
|
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-3.9' ]
|
||||||
|
packages: [ 'libboost1.55-dev', 'clang-3.9' ]
|
||||||
|
addons_clang40: &clang40
|
||||||
|
apt:
|
||||||
|
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-4.0' ]
|
||||||
|
packages: [ 'libboost1.55-dev', 'clang-4.0' ]
|
||||||
|
addons_clang50: &clang50
|
||||||
|
apt:
|
||||||
|
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-5.0' ]
|
||||||
|
packages: [ 'libboost1.55-dev', 'clang-5.0', 'clang-tidy-5.0' ]
|
||||||
|
addons_clang60: &clang60
|
||||||
|
apt:
|
||||||
|
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-6.0' ]
|
||||||
|
packages: [ 'libboost1.55-dev', 'clang-6.0', 'clang-tidy-6.0' ]
|
||||||
|
addons_gcc48: &gcc48
|
||||||
|
apt:
|
||||||
|
sources: [ 'ubuntu-toolchain-r-test' ]
|
||||||
|
packages: [ 'libboost1.55-dev', 'g++-4.8', 'gcc-4.8' ]
|
||||||
|
addons_gcc49: &gcc49
|
||||||
|
apt:
|
||||||
|
sources: [ 'ubuntu-toolchain-r-test' ]
|
||||||
|
packages: [ 'libboost1.55-dev', 'g++-4.9', 'gcc-4.9' ]
|
||||||
|
addons_gcc5: &gcc5
|
||||||
|
apt:
|
||||||
|
sources: [ 'ubuntu-toolchain-r-test' ]
|
||||||
|
packages: [ 'libboost1.55-dev', 'g++-5', 'gcc-5' ]
|
||||||
|
addons_gcc6: &gcc6
|
||||||
|
apt:
|
||||||
|
sources: [ 'ubuntu-toolchain-r-test' ]
|
||||||
|
packages: [ 'libboost1.55-dev', 'g++-6', 'gcc-6' ]
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: linux
|
||||||
|
compiler: "clang-3.5"
|
||||||
|
env: BUILD='Debug' CC=clang-3.5 CXX=clang++-3.5
|
||||||
|
addons: *clang35
|
||||||
|
- os: linux
|
||||||
|
compiler: "clang-3.8"
|
||||||
|
env: BUILD='Debug' CC=clang-3.8 CXX=clang++-3.8
|
||||||
|
addons: *clang38
|
||||||
|
- os: linux
|
||||||
|
compiler: "clang-3.9"
|
||||||
|
env: BUILD='Debug' CC=clang-3.9 CXX=clang++-3.9
|
||||||
|
addons: *clang39
|
||||||
|
- os: linux
|
||||||
|
compiler: "clang-4.0"
|
||||||
|
env: BUILD='Debug' CC=clang-4.0 CXX=clang++-4.0
|
||||||
|
addons: *clang40
|
||||||
|
- os: linux
|
||||||
|
compiler: "clang-5.0"
|
||||||
|
env: BUILD='Debug' CC=clang-5.0 CXX=clang++-5.0
|
||||||
|
CLANG_TIDY=clang-tidy-5.0
|
||||||
|
addons: *clang50
|
||||||
|
- os: linux
|
||||||
|
compiler: "clang-5.0"
|
||||||
|
env: BUILD='Release' CC=clang-5.0 CXX=clang++-5.0
|
||||||
|
addons: *clang50
|
||||||
|
- os: linux
|
||||||
|
compiler: "clang-5.0"
|
||||||
|
env: BUILD='Debug' CC=clang-5.0 CXX=clang++-5.0
|
||||||
|
CXXFLAGS="-fsanitize=address,undefined,integer -fno-sanitize-recover=all -fno-omit-frame-pointer"
|
||||||
|
LDFLAGS="-fsanitize=address,undefined,integer"
|
||||||
|
# LSAN doesn't work on container-based system
|
||||||
|
sudo: required
|
||||||
|
addons: *clang50
|
||||||
|
- os: linux
|
||||||
|
compiler: "clang-6.0"
|
||||||
|
env: BUILD='Debug' CC=clang-6.0 CXX=clang++-6.0
|
||||||
|
addons: *clang60
|
||||||
|
- os: linux
|
||||||
|
compiler: "gcc-4.8"
|
||||||
|
env: BUILD='Debug' CC=gcc-4.8 CXX=g++-4.8
|
||||||
|
addons: *gcc48
|
||||||
|
- os: linux
|
||||||
|
compiler: "gcc-4.9"
|
||||||
|
env: BUILD='Debug' CC=gcc-4.9 CXX=g++-4.9
|
||||||
|
COVERAGE=gcov-4.9
|
||||||
|
CXXFLAGS="--coverage" LDFLAGS="--coverage"
|
||||||
|
addons: *gcc49
|
||||||
|
- os: linux
|
||||||
|
compiler: "gcc-5"
|
||||||
|
env: BUILD='Debug' CC=gcc-5 CXX=g++-5
|
||||||
|
CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0"
|
||||||
|
addons: *gcc5
|
||||||
|
- os: linux
|
||||||
|
compiler: "gcc-5"
|
||||||
|
env: BUILD='Debug' CC=gcc-5 CXX=g++-5
|
||||||
|
CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=1"
|
||||||
|
addons: *gcc5
|
||||||
|
- os: linux
|
||||||
|
compiler: "gcc-6"
|
||||||
|
env: BUILD='Debug' CC=gcc-6 CXX=g++-6
|
||||||
|
addons: *gcc6
|
||||||
|
- os: linux
|
||||||
|
compiler: "gcc-6"
|
||||||
|
env: BUILD='Debug' CC=gcc-6 CXX=g++-6
|
||||||
|
PROTOZERO_DATA_VIEW=std::experimental::string_view
|
||||||
|
addons: *gcc6
|
||||||
|
- os: linux
|
||||||
|
compiler: "gcc-6"
|
||||||
|
env: BUILD='Release' CC=gcc-6 CXX=g++-6
|
||||||
|
addons: *gcc6
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode6.4
|
||||||
|
compiler: clang
|
||||||
|
env: BUILD='Debug'
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode7.3
|
||||||
|
compiler: clang
|
||||||
|
env: BUILD='Debug'
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode8.3
|
||||||
|
compiler: clang
|
||||||
|
env: BUILD='Debug'
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode9.1
|
||||||
|
compiler: clang
|
||||||
|
env: BUILD='Debug'
|
||||||
|
- os: osx
|
||||||
|
osx_image: xcode9.1
|
||||||
|
compiler: clang
|
||||||
|
env: BUILD='Release'
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install:
|
||||||
|
- git submodule update --init
|
||||||
|
- (cd ..; git clone --depth=1 https://github.com/mapbox/protozero)
|
||||||
|
|
||||||
|
script:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
- cmake .. -LA -DCMAKE_BUILD_TYPE=${BUILD} -DPROTOZERO_DATA_VIEW=$PROTOZERO_DATA_VIEW -DCLANG_TIDY=$(which ${CLANG_TIDY})
|
||||||
|
- make VERBOSE=1
|
||||||
|
- ctest --output-on-failure
|
||||||
|
- if [ -n "${CLANG_TIDY}" ]; then make clang-tidy; fi
|
||||||
|
- |
|
||||||
|
if [ -n "${COVERAGE}" ]; then
|
||||||
|
which ${COVERAGE}
|
||||||
|
curl -S -f https://codecov.io/bash -o codecov
|
||||||
|
chmod +x codecov
|
||||||
|
${COVERAGE} -p $(find test -name 'test_*.o')
|
||||||
|
./codecov -Z -c -X gcov -F unit_tests
|
||||||
|
${COVERAGE} -p $(find test -name 'fixture_tests.cpp.o')
|
||||||
|
./codecov -Z -c -X gcov -F fixture_tests
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
50
.ycm_extra_conf.py
Normal file
50
.ycm_extra_conf.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Configuration for YouCompleteMe Vim plugin
|
||||||
|
#
|
||||||
|
# http://valloric.github.io/YouCompleteMe/
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from os.path import realpath, dirname
|
||||||
|
|
||||||
|
basedir = dirname(realpath(__file__))
|
||||||
|
|
||||||
|
# some default flags
|
||||||
|
# for more information install clang-3.2-doc package and
|
||||||
|
# check UsersManual.html
|
||||||
|
flags = [
|
||||||
|
'-Werror',
|
||||||
|
'-Wall',
|
||||||
|
'-Wextra',
|
||||||
|
'-pedantic',
|
||||||
|
'-Wno-return-type',
|
||||||
|
'-Wno-unused-parameter',
|
||||||
|
'-Wno-unused-variable',
|
||||||
|
|
||||||
|
'-std=c++11',
|
||||||
|
|
||||||
|
# '-x' and 'c++' also required
|
||||||
|
# use 'c' for C projects
|
||||||
|
'-x',
|
||||||
|
'c++',
|
||||||
|
|
||||||
|
# workaround for https://github.com/Valloric/YouCompleteMe/issues/303
|
||||||
|
# also see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=800618
|
||||||
|
'-isystem',
|
||||||
|
'/usr/lib/ycmd/clang_includes/',
|
||||||
|
|
||||||
|
'-I%s/../protozero/include' % basedir,
|
||||||
|
'-I%s/include' % basedir,
|
||||||
|
'-I%s/test/include' % basedir,
|
||||||
|
'-I%s/test/catch' % basedir,
|
||||||
|
]
|
||||||
|
|
||||||
|
# youcompleteme is calling this function to get flags
|
||||||
|
# You can also set database for flags. Check: JSONCompilationDatabase.html in
|
||||||
|
# clang-3.2-doc package
|
||||||
|
def FlagsForFile( filename ):
|
||||||
|
return {
|
||||||
|
'flags': flags,
|
||||||
|
'do_cache': True
|
||||||
|
}
|
44
CHANGELOG.md
Normal file
44
CHANGELOG.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/)
|
||||||
|
This project adheres to [Semantic Versioning](https://semver.org/).
|
||||||
|
|
||||||
|
## [unreleased] -
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
|
||||||
|
## [1.0.1] - 2018-04-12
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Some documentation and tests.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Catch exceptions in vtzero-streets example and output error message.
|
||||||
|
* Adds a template parameter to the `create_property_map` function allowing
|
||||||
|
mapping between value types.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* The indexes returned by `feature::next_property_indexes()` are now
|
||||||
|
checked against the size of the key/value tables in the layer. If
|
||||||
|
an index is too large a `vtzero::out_of_range_exception` is returned.
|
||||||
|
This way the user code doesn't have to check this. The function
|
||||||
|
`feature::for_each_property()` now also uses these checks.
|
||||||
|
|
||||||
|
|
||||||
|
## [1.0.0] - 2018-03-09
|
||||||
|
|
||||||
|
First release
|
||||||
|
|
||||||
|
|
||||||
|
[unreleased]: https://github.com/osmcode/libosmium/compare/v1.1.0...HEAD
|
||||||
|
|
157
CMakeLists.txt
Normal file
157
CMakeLists.txt
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# CMake config
|
||||||
|
#
|
||||||
|
# vtzero
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
|
||||||
|
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
project(vtzero)
|
||||||
|
|
||||||
|
set(VTZERO_VERSION_MAJOR 1)
|
||||||
|
set(VTZERO_VERSION_MINOR 0)
|
||||||
|
set(VTZERO_VERSION_PATCH 1)
|
||||||
|
|
||||||
|
set(VTZERO_VERSION
|
||||||
|
"${VTZERO_VERSION_MAJOR}.${VTZERO_VERSION_MINOR}.${VTZERO_VERSION_PATCH}")
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# This variable must be set to the directory where the mvt-fixtures from the
|
||||||
|
# https://github.com/mapbox/mvt-fixtures repository are to be found. Usually
|
||||||
|
# this is the directory where the submodule is checked out as described in
|
||||||
|
# the README, but you can also set this to a different path, for instance
|
||||||
|
# to change the setting while doing development.
|
||||||
|
|
||||||
|
set(MVT_FIXTURES "${CMAKE_SOURCE_DIR}/test/mvt-fixtures" CACHE PATH "mvt-fixtures directory for tests")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
option(WERROR "Add -Werror flag to build (turns warnings into errors)" ON)
|
||||||
|
|
||||||
|
if(MSVC)
|
||||||
|
add_definitions(-std=c++11 /W3)
|
||||||
|
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
|
||||||
|
else()
|
||||||
|
add_definitions(-std=c++11 -Wall -Wextra -pedantic -Wsign-compare -Wconversion)
|
||||||
|
# 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)
|
||||||
|
if(WERROR)
|
||||||
|
add_definitions(-Werror)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include_directories("${CMAKE_SOURCE_DIR}/include")
|
||||||
|
|
||||||
|
set(PROTOZERO_DATA_VIEW "" CACHE STRING "Type used for vtzero::data_view")
|
||||||
|
if(NOT PROTOZERO_DATA_VIEW STREQUAL "")
|
||||||
|
add_definitions(-DPROTOZERO_DATA_VIEW=${PROTOZERO_DATA_VIEW})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Find dependencies
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
find_package(Protozero 1.6.0 REQUIRED)
|
||||||
|
|
||||||
|
include_directories(SYSTEM ${PROTOZERO_INCLUDE_DIR})
|
||||||
|
|
||||||
|
find_package(Boost)
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Optional "clang-tidy" target
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
message(STATUS "Looking for clang-tidy")
|
||||||
|
find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-6.0 clang-tidy-5.0)
|
||||||
|
|
||||||
|
if(CLANG_TIDY)
|
||||||
|
message(STATUS "Looking for clang-tidy - found ${CLANG_TIDY}")
|
||||||
|
add_custom_target(clang-tidy
|
||||||
|
${CLANG_TIDY}
|
||||||
|
-p ${CMAKE_BINARY_DIR}
|
||||||
|
${CMAKE_SOURCE_DIR}/examples/*.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/test/*.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/test/t/*.cpp
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "Looking for clang-tidy - not found")
|
||||||
|
message(STATUS " Build target 'clang-tidy' will not be available.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Optional "cppcheck" target
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
message(STATUS "Looking for cppcheck")
|
||||||
|
find_program(CPPCHECK NAMES cppcheck)
|
||||||
|
|
||||||
|
if(CPPCHECK)
|
||||||
|
message(STATUS "Looking for cppcheck - found")
|
||||||
|
add_custom_target(cppcheck
|
||||||
|
${CPPCHECK}
|
||||||
|
-Uassert --std=c++11 --enable=all
|
||||||
|
${CMAKE_SOURCE_DIR}/examples/*.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/test/*.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/test/t/*.cpp
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "Looking for cppcheck - not found")
|
||||||
|
message(STATUS " Build target 'cppcheck' will not be available.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Include what you use
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
message(STATUS "Looking for iwyu")
|
||||||
|
find_program(IWYU_TOOL NAMES iwyu_tool)
|
||||||
|
|
||||||
|
if(IWYU_TOOL)
|
||||||
|
message(STATUS "Looking for iwyu - found")
|
||||||
|
add_custom_target(iwyu
|
||||||
|
${IWYU_TOOL} -p ${CMAKE_BINARY_DIR}
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(STATUS "Looking for iwyu - not found")
|
||||||
|
message(STATUS " Build target 'iwyu' will not be available.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Installation
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install(DIRECTORY include/vtzero DESTINATION include)
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
add_subdirectory(doc)
|
||||||
|
|
||||||
|
add_subdirectory(examples)
|
||||||
|
|
||||||
|
add_subdirectory(test)
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
25
CONTRIBUTING.md
Normal file
25
CONTRIBUTING.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Contributing to vtzero
|
||||||
|
|
||||||
|
## Releasing
|
||||||
|
|
||||||
|
To release a new vtzero version:
|
||||||
|
|
||||||
|
- Make sure all tests are passing locally and on travis/appveyor
|
||||||
|
- Update version number in
|
||||||
|
- `CMakeLists.txt` (one place)
|
||||||
|
- `include/vtzero/version.hpp` (two places)
|
||||||
|
- Update CHANGELOG.md
|
||||||
|
- Update UPGRADING.md if necessary
|
||||||
|
- `git commit -m "Release X.Y.Z" include/vtzero/version.hpp CMakeLists.txt CHANGELOG.md UPGRADING.md`
|
||||||
|
- `git tag vX.Y.Z`
|
||||||
|
- `git push`
|
||||||
|
- `git push --tags`
|
||||||
|
- Go to https://github.com/mapbox/vtzero/releases
|
||||||
|
and edit the new release. Put "Version x.y.z" in title and
|
||||||
|
cut-and-paste entry from CHANGELOG.md.
|
||||||
|
|
||||||
|
## Updating submodules
|
||||||
|
|
||||||
|
Call `git submodule update --recursive --remote` to update to the newest
|
||||||
|
version of the mvt fixtures used for testing.
|
||||||
|
|
28
EXTERNAL_LICENSES.txt
Normal file
28
EXTERNAL_LICENSES.txt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
==== clara.hpp
|
||||||
|
|
||||||
|
Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
||||||
|
|
||||||
|
Boost Software License - Version 1.0 - August 17th, 2003
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person or organization
|
||||||
|
obtaining a copy of the software and accompanying documentation covered by
|
||||||
|
this license (the "Software") to use, reproduce, display, distribute,
|
||||||
|
execute, and transmit the Software, and to prepare derivative works of the
|
||||||
|
Software, and to permit third-parties to whom the Software is furnished to
|
||||||
|
do so, all subject to the following:
|
||||||
|
|
||||||
|
The copyright notices in the Software and this entire statement, including
|
||||||
|
the above license grant, this restriction and the following disclaimer,
|
||||||
|
must be included in all copies of the Software, in whole or in part, and
|
||||||
|
all derivative works of the Software, unless such copies or derivative
|
||||||
|
works are solely in the form of machine-executable object code generated by
|
||||||
|
a source language processor.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||||
|
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
25
LICENSE
Normal file
25
LICENSE
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2017, Mapbox
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
93
README.md
Normal file
93
README.md
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# vtzero
|
||||||
|
|
||||||
|
Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
Implements the [Mapbox Vector Tile Specification 2.x](https://www.mapbox.com/vector-tiles/specification).
|
||||||
|
|
||||||
|
[](https://travis-ci.org/mapbox/vtzero)
|
||||||
|
[](https://ci.appveyor.com/project/Mapbox/vtzero)
|
||||||
|
[](https://codecov.io/gh/mapbox/vtzero)
|
||||||
|
|
||||||
|
|
||||||
|
## Depends
|
||||||
|
|
||||||
|
* C++11 compiler (GCC 4.8 or higher, clang 3.5 or higher, ...)
|
||||||
|
* CMake
|
||||||
|
* [Protozero](https://github.com/mapbox/protozero) version >= 1.6.0
|
||||||
|
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
First clone `protozero`:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone git@github.com:mapbox/protozero.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Then clone `vtzero` beside `protozero`. The `vtzero` build system will, among
|
||||||
|
several places, look for `protozero` at `../protozero`. (If you would like to
|
||||||
|
use `protozero` from a different path you can set `PROTOZERO_INCLUDE_DIR` in
|
||||||
|
the CMake configuration step.)
|
||||||
|
|
||||||
|
Then, inside the `vtzero` directory do:
|
||||||
|
|
||||||
|
```
|
||||||
|
git submodule update --init
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, to build the examples and tests do:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
Call `ctest` to run the tests.
|
||||||
|
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Several examples are provided to show usage of the library.
|
||||||
|
|
||||||
|
Call
|
||||||
|
|
||||||
|
examples/vtzero-create
|
||||||
|
|
||||||
|
to create test tile named `test.mvt`.
|
||||||
|
|
||||||
|
Call
|
||||||
|
|
||||||
|
examples/vtzero-show TILE-FILE
|
||||||
|
|
||||||
|
to show the contents of `TILE-FILE`.
|
||||||
|
|
||||||
|
You can use
|
||||||
|
|
||||||
|
examples/vtzero-check TILE-FILE
|
||||||
|
|
||||||
|
to check vector tile for validity.
|
||||||
|
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
|
||||||
|
Extensive documentation is available:
|
||||||
|
* [Tutorial](doc/tutorial.md) (start here)
|
||||||
|
* [Reading vector tiles](doc/reading.md)
|
||||||
|
* [Writing vector tiles](doc/writing.md)
|
||||||
|
* [Advanced vtzero topics](doc/advanced.md)
|
||||||
|
|
||||||
|
Make sure to read all of it before using vtzero. Vtzero isn't the simplest
|
||||||
|
library to use, because it always chooses performance over ease of use.
|
||||||
|
|
||||||
|
If [Doxygen](http://www.stack.nl/~dimitri/doxygen/) is installed on your
|
||||||
|
system, the build process will automatically create the API docs for you.
|
||||||
|
The results will be in your build directory under `doc/html`.
|
||||||
|
|
||||||
|
|
||||||
|
## Authors
|
||||||
|
|
||||||
|
Jochen Topf (jochen@topf.org),
|
||||||
|
Dane Springmeyer (dane@mapbox.com)
|
||||||
|
|
61
appveyor.yml
Normal file
61
appveyor.yml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Configuration for continuous integration service at appveyor.com
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
platform: x64
|
||||||
|
|
||||||
|
image: Visual Studio 2017
|
||||||
|
|
||||||
|
clone_depth: 1
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
environment:
|
||||||
|
matrix:
|
||||||
|
- config: MSYS2
|
||||||
|
autocrlf: true
|
||||||
|
- config: Debug
|
||||||
|
autocrlf: true
|
||||||
|
- config: RelWithDebInfo
|
||||||
|
autocrlf: true
|
||||||
|
- config: Debug
|
||||||
|
autocrlf: false
|
||||||
|
- config: RelWithDebInfo
|
||||||
|
autocrlf: false
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
init:
|
||||||
|
- git config --global core.autocrlf %autocrlf%
|
||||||
|
- git config --get core.autocrlf
|
||||||
|
|
||||||
|
# The option --ask=20 is a workaround for a problem with the MSYS2 update
|
||||||
|
# process. Without it the following error is printed and the appveyor script
|
||||||
|
# halts: "msys2-runtime and catgets are in conflict. Remove catgets?"
|
||||||
|
# See also: https://github.com/Alexpux/MSYS2-packages/issues/1141
|
||||||
|
install:
|
||||||
|
- cd c:\projects
|
||||||
|
- git clone --depth=1 https://github.com/mapbox/protozero
|
||||||
|
- if [%config%]==[MSYS2] (
|
||||||
|
C:\msys64\usr\bin\pacman --noconfirm --sync --refresh --refresh --sysupgrade --sysupgrade --ask=20
|
||||||
|
&& C:\msys64\usr\bin\pacman -Rc --noconfirm mingw-w64-x86_64-gcc-libs
|
||||||
|
)
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- cd c:\projects\vtzero
|
||||||
|
- git submodule update --init
|
||||||
|
- if [%config%]==[MSYS2] (
|
||||||
|
build-msys2.bat
|
||||||
|
) else (
|
||||||
|
build-appveyor.bat
|
||||||
|
)
|
||||||
|
|
||||||
|
# remove garbage VS messages
|
||||||
|
# https://help.appveyor.com/discussions/problems/4569-the-target-_convertpdbfiles-listed-in-a-beforetargets-attribute-at-c-does-not-exist-in-the-project-and-will-be-ignored
|
||||||
|
before_build:
|
||||||
|
- del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets"
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
58
build-appveyor.bat
Normal file
58
build-appveyor.bat
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
SETLOCAL
|
||||||
|
SET EL=0
|
||||||
|
|
||||||
|
ECHO ~~~~~~ %~f0 ~~~~~~
|
||||||
|
|
||||||
|
::show all available env vars
|
||||||
|
SET
|
||||||
|
ECHO cmake on AppVeyor
|
||||||
|
cmake -version
|
||||||
|
|
||||||
|
ECHO activating VS cmd prompt && CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
|
||||||
|
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
|
||||||
|
|
||||||
|
IF EXIST build ECHO deleting build dir... && RD /Q /S build
|
||||||
|
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
|
||||||
|
|
||||||
|
MKDIR build
|
||||||
|
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
|
||||||
|
|
||||||
|
CD build
|
||||||
|
ECHO config^: %config%
|
||||||
|
|
||||||
|
::This will produce lots of LNK4099 warnings which can be ignored.
|
||||||
|
::Unfortunately they can't be disabled, see
|
||||||
|
::https://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings
|
||||||
|
SET CMAKE_CMD=cmake .. ^
|
||||||
|
-LA -G "Visual Studio 14 Win64"
|
||||||
|
|
||||||
|
ECHO calling^: %CMAKE_CMD%
|
||||||
|
%CMAKE_CMD%
|
||||||
|
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
|
||||||
|
|
||||||
|
SET avlogger=
|
||||||
|
IF /I "%APPVEYOR%"=="True" SET avlogger=/logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
||||||
|
|
||||||
|
msbuild vtzero.sln ^
|
||||||
|
/p:Configuration=%config% ^
|
||||||
|
/toolsversion:14.0 ^
|
||||||
|
/p:Platform=x64 ^
|
||||||
|
/p:PlatformToolset=v140 %avlogger%
|
||||||
|
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
|
||||||
|
|
||||||
|
ctest --output-on-failure ^
|
||||||
|
-C %config% ^
|
||||||
|
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
|
||||||
|
|
||||||
|
GOTO DONE
|
||||||
|
|
||||||
|
:ERROR
|
||||||
|
ECHO ~~~~~~ ERROR %~f0 ~~~~~~
|
||||||
|
SET EL=%ERRORLEVEL%
|
||||||
|
|
||||||
|
:DONE
|
||||||
|
IF %EL% NEQ 0 ECHO. && ECHO !!! ERRORLEVEL^: %EL% !!! && ECHO.
|
||||||
|
ECHO ~~~~~~ DONE %~f0 ~~~~~~
|
||||||
|
|
||||||
|
EXIT /b %EL%
|
18
build-msys2.bat
Normal file
18
build-msys2.bat
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
echo "Adding MSYS2 to path..."
|
||||||
|
SET "PATH=C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%"
|
||||||
|
echo %PATH%
|
||||||
|
|
||||||
|
echo "Installing MSYS2 packages..."
|
||||||
|
bash -lc "pacman -S --needed --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-boost"
|
||||||
|
|
||||||
|
echo "Generating makefiles"
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake .. -LA -G "MSYS Makefiles"
|
||||||
|
|
||||||
|
echo "Building"
|
||||||
|
make VERBOSE=1
|
||||||
|
|
||||||
|
echo "Testing"
|
||||||
|
ctest --output-on-failure
|
||||||
|
|
63
cmake/FindProtozero.cmake
Normal file
63
cmake/FindProtozero.cmake
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#----------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# FindProtozero.cmake
|
||||||
|
#
|
||||||
|
# Find the protozero headers.
|
||||||
|
#
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
#
|
||||||
|
# Copy this file somewhere into your project directory, where cmake can
|
||||||
|
# find it. Usually this will be a directory called "cmake" which you can
|
||||||
|
# add to the CMake module search path with the following line in your
|
||||||
|
# CMakeLists.txt:
|
||||||
|
#
|
||||||
|
# list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||||
|
#
|
||||||
|
# Then add the following in your CMakeLists.txt:
|
||||||
|
#
|
||||||
|
# find_package(Protozero [version] [REQUIRED])
|
||||||
|
# include_directories(SYSTEM ${PROTOZERO_INCLUDE_DIR})
|
||||||
|
#
|
||||||
|
# The version number is optional. If it is not set, any version of
|
||||||
|
# protozero will do.
|
||||||
|
#
|
||||||
|
# if(NOT PROTOZERO_FOUND)
|
||||||
|
# message(WARNING "Protozero not found!\n")
|
||||||
|
# endif()
|
||||||
|
#
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Variables:
|
||||||
|
#
|
||||||
|
# PROTOZERO_FOUND - True if Protozero was found.
|
||||||
|
# PROTOZERO_INCLUDE_DIR - Where to find include files.
|
||||||
|
#
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# find include path
|
||||||
|
find_path(PROTOZERO_INCLUDE_DIR protozero/version.hpp
|
||||||
|
PATH_SUFFIXES include
|
||||||
|
PATHS ../protozero
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check version number
|
||||||
|
if(Protozero_FIND_VERSION)
|
||||||
|
file(STRINGS "${PROTOZERO_INCLUDE_DIR}/protozero/version.hpp" _version_define REGEX "#define PROTOZERO_VERSION_STRING")
|
||||||
|
if("${_version_define}" MATCHES "#define PROTOZERO_VERSION_STRING \"([0-9.]+)\"")
|
||||||
|
set(_version "${CMAKE_MATCH_1}")
|
||||||
|
else()
|
||||||
|
set(_version "unknown")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#set(PROTOZERO_INCLUDE_DIRS "${PROTOZERO_INCLUDE_DIR}")
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Protozero
|
||||||
|
REQUIRED_VARS PROTOZERO_INCLUDE_DIR
|
||||||
|
VERSION_VAR _version)
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------------
|
3
codecov.yml
Normal file
3
codecov.yml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
ignore:
|
||||||
|
- "bench"
|
||||||
|
- "test"
|
36
doc/CMakeLists.txt
Normal file
36
doc/CMakeLists.txt
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# CMake Config
|
||||||
|
#
|
||||||
|
# vtzero documentation
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
message(STATUS "Configuring documentation")
|
||||||
|
|
||||||
|
message(STATUS "Looking for doxygen")
|
||||||
|
find_package(Doxygen)
|
||||||
|
|
||||||
|
if(DOXYGEN_FOUND)
|
||||||
|
message(STATUS "Looking for doxygen - found")
|
||||||
|
configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
|
||||||
|
file(GLOB HEADER_FILES "${CMAKE_SOURCE_DIR}/include/vtzero/*.hpp")
|
||||||
|
add_custom_command(OUTPUT html/index.html
|
||||||
|
COMMAND ${DOXYGEN_EXECUTABLE}
|
||||||
|
ARGS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||||
|
DEPENDS Doxyfile.in advanced.md doc.md
|
||||||
|
${HEADER_FILES}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
COMMENT "Generating API documentation with Doxygen" VERBATIM)
|
||||||
|
add_custom_target(doc ALL
|
||||||
|
DEPENDS html/index.html)
|
||||||
|
else()
|
||||||
|
message(STATUS "Looking for doxygen - not found")
|
||||||
|
message(STATUS " Disabled making of documentation.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
message(STATUS "Configuring documentation - done")
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
2353
doc/Doxyfile.in
Normal file
2353
doc/Doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
114
doc/advanced.md
Normal file
114
doc/advanced.md
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
|
||||||
|
# Advanced vtzero topics
|
||||||
|
|
||||||
|
## Differences between the protocol buffer specification and the vtzero implementation
|
||||||
|
|
||||||
|
The [protobuf specification
|
||||||
|
says](https://developers.google.com/protocol-buffers/docs/encoding#optional)
|
||||||
|
that a decoder library must handle repeated *non-packed* fields if repeated
|
||||||
|
*packed* fields are expected and it must handle multiple repeated packed fields
|
||||||
|
as if the items are concatenated. Encoders should never encode fields in this
|
||||||
|
way, though, so it is very unlikely that this would ever happen. For
|
||||||
|
performance reasons vtzero doesn't handle this case.
|
||||||
|
|
||||||
|
## Differences between the vector tile specification and the vtzero implementation
|
||||||
|
|
||||||
|
The [vector tile specification](https://github.com/mapbox/vector-tile-spec/blob/master/2.1/README.md#41-layers)
|
||||||
|
clearly says that you can not have two layers with the same
|
||||||
|
name in a vector tile. For performance reasons this is neither checked on
|
||||||
|
reading nor on writing.
|
||||||
|
|
||||||
|
## The `create_vtzero_point` customization point
|
||||||
|
|
||||||
|
The vtzero builder classes have several functions which take a `vtzero::point`
|
||||||
|
as argument. But chances are that you are using a different point type in your
|
||||||
|
code. That's why these functions have overloads taking any type `TPoint` that
|
||||||
|
can be converted to a `vtzero::point`. This conversion is done by calling the
|
||||||
|
function `create_vtzero_point()`. Vtzero supplies a version of this function
|
||||||
|
which will work with any type with members `x` and `y`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template <typename TPoint>
|
||||||
|
vtzero::point create_vtzero_point(TPoint p) noexcept {
|
||||||
|
return {p.x, p.y};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can define your own overload of that function taking your own point type
|
||||||
|
as parameter and returning a `vtzero::point`. Vtzero will find your function
|
||||||
|
using [ADL](http://en.cppreference.com/w/cpp/language/adl) which magically
|
||||||
|
makes the vtzero builders work with your point type.
|
||||||
|
|
||||||
|
## Using the `property_mapper` class when copying layers
|
||||||
|
|
||||||
|
Sometimes you want to copy some features of a layer into a new layer. Because
|
||||||
|
you only copy some features (and/or only some properties of the features), the
|
||||||
|
key and value tables in the layer have to be rebuilt. This is where the
|
||||||
|
`property_mapper` class helps you. It keeps the mapping between the index
|
||||||
|
values of the old and the new table adding property keys and values as needed
|
||||||
|
to the new table.
|
||||||
|
|
||||||
|
Here is some code that shows you how to use it:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <vtzero/property_mapper.hpp> // you have to include this
|
||||||
|
|
||||||
|
vtzero::layer layer = ...; // layer you got from an existing tile
|
||||||
|
vtzero::layer_builder layer_builder{...}; // create new layer
|
||||||
|
|
||||||
|
// instantiate the property mapper with the old and new layers
|
||||||
|
vtzero::property_mapper mapper{layer, layer_builder};
|
||||||
|
|
||||||
|
// you'll probably want to iterate over all features in the old layer...
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
// ... and decide somehow which ones you want to keep
|
||||||
|
if (keep_feature(feature)) {
|
||||||
|
// instantiate a feature builder as usual and copy id and geometry
|
||||||
|
vtzero::geometry_feature_builder feature_builder{layer_builder};
|
||||||
|
if (feature.has_id()) {
|
||||||
|
feature_builder.set_id(feature.id());
|
||||||
|
}
|
||||||
|
feature_builder.set_geometry(feature.geometry());
|
||||||
|
|
||||||
|
// now iterate over all properties...
|
||||||
|
while (auto idxs = feature.next_property_indexes()) {
|
||||||
|
// ... decide which ones to keep,
|
||||||
|
if (keep_property(idxs)) {
|
||||||
|
// ... and add them to the new layer using the mapper
|
||||||
|
feature_builder.add_property(mapper(idxs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Protection against huge memory use
|
||||||
|
|
||||||
|
When decoding a vector tile we got from an unknown source, we don't know what
|
||||||
|
surprises it might contain. Building data structures based on the vector tile
|
||||||
|
sometimes means we have to allocate memory and in the worst case this might be
|
||||||
|
quite a lot of memory. Vtzero usually doesn't allocate any memory when decoding
|
||||||
|
a tile, except when reading properties, when there is space for lookup tables
|
||||||
|
allocated. The memory use for these lookup tables is `sizeof(data_view)` times
|
||||||
|
the number of entries in the key/value table. In the worst case, when a vector
|
||||||
|
tile basically only contains such a table, memory use is proportional to the
|
||||||
|
size of the vector tile. But memory use can be an order of magnitude larger
|
||||||
|
than the tile size! If you are concerned about memory use, limit the size
|
||||||
|
of the vector tiles you give to vtzero.
|
||||||
|
|
||||||
|
When reading geometries from vector tiles, vtzero doesn't need much memory
|
||||||
|
itself, but the users of vtzero might. In a typical case you might reserve
|
||||||
|
enough memory to store, say, a linestring, and then fill that memory. To allow
|
||||||
|
you to do this, vtzero tells you about the number of points in the linestring.
|
||||||
|
This number comes from the tile and it might be rather large. Vtzero does a
|
||||||
|
consistency check comparing the number of points the geometry says it has with
|
||||||
|
the number of bytes used for the geometry and it will throw an exception if the
|
||||||
|
numbers can't fit. So you are protected against tiny tiles pretending to
|
||||||
|
contain a huge geometry. But there still could be a medium-sized tile which
|
||||||
|
gets "blown up" into a huge memory hog. Your representation of a linestring
|
||||||
|
can be an order of magnitude larger than the minimum 2 bytes per point
|
||||||
|
needed in the encoded tile.
|
||||||
|
|
||||||
|
So again: If you are concerned about memory use, limit the size of the vector
|
||||||
|
tiles you give to vtzero.
|
||||||
|
|
13
doc/doc.md
Normal file
13
doc/doc.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This is the API documentation that was automatically created from the
|
||||||
|
source code. For more information about vtzero go to
|
||||||
|
https://github.com/mapbox/vtzero .
|
||||||
|
|
||||||
|
Vtzero is a header-only library. You do not need to compile and link it,
|
||||||
|
just include the headers you need.
|
||||||
|
|
||||||
|
Everything in namespaces called "detail" is for internal vtzero use only,
|
||||||
|
do not depend on it in your code.
|
||||||
|
|
459
doc/reading.md
Normal file
459
doc/reading.md
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
|
||||||
|
# Reading vector tiles
|
||||||
|
|
||||||
|
To access the contents of vector tiles with vtzero create a `vector_tile`
|
||||||
|
object first with the data of the tile as first argument:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <vtzero/vector_tile.hpp> // always needed when reading vector tiles
|
||||||
|
|
||||||
|
std::string vt_data = ...;
|
||||||
|
vtzero::vector_tile tile{vt_data};
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead of a string, you can also initialize the `vector_tile` using a
|
||||||
|
`vtzero::data_view`. This class contains only a pointer and size referencing
|
||||||
|
some data similar to the C++17 `std::string_view` class. It is typedef'd from
|
||||||
|
the `protozero::data_view`. See [the protozero
|
||||||
|
doc](https://github.com/mapbox/protozero/blob/master/doc/advanced.md#protozero_use_view)
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
vtzero::data_view vt_data = ...;
|
||||||
|
vtzero::vector_tile tile{vt_data};
|
||||||
|
```
|
||||||
|
|
||||||
|
In both cases the `vector_tile` object contains references to the original
|
||||||
|
tile data. You have to make sure this data stays available through the whole
|
||||||
|
lifetime of the `vector_tile` object and all the other objects we'll create
|
||||||
|
in this tutorial for accessing parts of the vector tile. The data is **not**
|
||||||
|
copied by vtzero when accessing vector tiles.
|
||||||
|
|
||||||
|
You can think of the `vector_tile` class as a "proxy" class giving you access
|
||||||
|
to the decoded data, similarly the classes `layer`, `feature`, and
|
||||||
|
`property` described in the next chapters are "proxy" classes, too.
|
||||||
|
|
||||||
|
## Accessing layers
|
||||||
|
|
||||||
|
Vector tiles consist of a list of layers. The list of layers can be empty
|
||||||
|
in which case `tile.empty()` will return true.
|
||||||
|
|
||||||
|
The simplest and fasted way to access the layers is through the `next_layer()`
|
||||||
|
function:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this creates new layer objects on the fly referencing the layer you
|
||||||
|
are currently looking at. Once you have iterated over all the layers,
|
||||||
|
`next_layer()` will return the "invalid" (default constructed) layer object
|
||||||
|
which converts to false in an boolean context.
|
||||||
|
|
||||||
|
You can reset the layer iterator to the beginning again if you need to go
|
||||||
|
over the layers again:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
tile.reset_layer();
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead of using this external iterator, you can use a different function with
|
||||||
|
an internal iterator that calls a function defined by you for each layer. Your
|
||||||
|
function must take a `layer&&` as parameter and return `true` if the iteration
|
||||||
|
should continue and `false` otherwise:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
tile.for_each_layer([&](layer&& l) {
|
||||||
|
// do something with layer
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Both the external and internal iteration do basically the same and have the
|
||||||
|
same performance characteristics.
|
||||||
|
|
||||||
|
You can also access layers through their index or name:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
tile.get_layer(3);
|
||||||
|
```
|
||||||
|
|
||||||
|
will give you the 4th layer in the tile. With
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
tile.get_layer_by_name("foobar");
|
||||||
|
```
|
||||||
|
|
||||||
|
you'll get the layer with the specified name. Both will return the invalid
|
||||||
|
layer if that layer doesn't exist.
|
||||||
|
|
||||||
|
Note that accessing layers by index or name is less efficient than iterating
|
||||||
|
over them using `next_layer()` if you are accessing several layers. So usually
|
||||||
|
you should only use those function if you want to access one specific layer
|
||||||
|
only.
|
||||||
|
|
||||||
|
If you need the number of layers, you can call `tile.count_layers()`. This
|
||||||
|
function still has to iterate over the layers internally decoding some of the
|
||||||
|
data, so it is not cheap.
|
||||||
|
|
||||||
|
## The layer
|
||||||
|
|
||||||
|
Once you have a layer as described in the previous chapter you can access the
|
||||||
|
metadata of this layer easily:
|
||||||
|
|
||||||
|
* The version is available with `layer.version()`. Only version 1 and 2 are
|
||||||
|
currently supported by this library.
|
||||||
|
* The extent of the tile is available through `layer.extent()`. This is usually
|
||||||
|
4096.
|
||||||
|
* The function `layer.name()` returns the name of the layer as `data_view`.
|
||||||
|
This does **not** include a final 0-byte!
|
||||||
|
* The number of features is returned by the `layer.num_features()` function.
|
||||||
|
If it doesn't contain any features `layer.empty()` will return true.
|
||||||
|
(Different then the `vector_tile::count_layers()`, the `layer::num_features()`
|
||||||
|
function is `O(1)`).
|
||||||
|
|
||||||
|
To access the features call the `next_feature()` function until it returns
|
||||||
|
the invalid (default constructed) feature:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `reset_feature()` to restart the feature iterator from the beginning.
|
||||||
|
|
||||||
|
Instead of using this external iterator, you can use a different function with
|
||||||
|
an internal iterator that calls a function defined by you for each feature.
|
||||||
|
Your function must take a `feature&&` as parameter and return `true` if the
|
||||||
|
iteration should continue and `false` otherwise:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
layer.for_each_feature([&](feature&& f) {
|
||||||
|
// do something with the feature
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Both the external and internal iteration do basically the same and have the
|
||||||
|
same performance characteristics.
|
||||||
|
|
||||||
|
If you know the ID of a feature you can get the feature using
|
||||||
|
`get_feature_by_id()`, but note that this will do a linear search through
|
||||||
|
all the features in the layer, decoding each one until it finds the right ID.
|
||||||
|
This is almost always **not** what you want.
|
||||||
|
|
||||||
|
Note that the feature returned by `next_feature()` or `get_feature_by_id()`
|
||||||
|
will internally contain a pointer to the layer it came from. The layer has to
|
||||||
|
stay valid as long as the feature is used.
|
||||||
|
|
||||||
|
## The feature
|
||||||
|
|
||||||
|
You get features from the layer as described in the previous chapter. The
|
||||||
|
`feature` class gives you access to the ID, the geometry and the properties
|
||||||
|
of the feature. Access the ID using the `id()` method which will return 0
|
||||||
|
if no ID is set. You can ask for the existence of the ID using `has_id()`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto feature = layer...;
|
||||||
|
if (feature.has_id()) {
|
||||||
|
cout << feature.id() << '\n';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `geometry()` method returns an object of the `geometry` class. It contains
|
||||||
|
the geometry type and a reference to the (un-decoded) geometry data. See a
|
||||||
|
later chapter on the details of decoding this geometry. You can also directly
|
||||||
|
add this geometry to a new feature you are writing.
|
||||||
|
|
||||||
|
The number of properties in the feature is returned by the
|
||||||
|
`feature::num_properties()` function. If the feature doesn't contain any
|
||||||
|
properties `feature.empty()` will return true. (Different then the
|
||||||
|
`vector_tile::count_layers()`, the `feature::num_properties()` function is
|
||||||
|
`O(1)`).
|
||||||
|
|
||||||
|
To access the properties call the `next_property()` function until it returns
|
||||||
|
the invalid (default constructed) property:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
while (auto property = feature.next_property()) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `reset_property()` to restart the property iterator from the beginning.
|
||||||
|
|
||||||
|
Instead of using this external iterator, you can use a different function with
|
||||||
|
an internal iterator that calls a function defined by you for each property.
|
||||||
|
Your function must take a `property&&` as parameter and return `true` if the
|
||||||
|
iteration should continue and `false` otherwise:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
feature.for_each_property([&](property&& p) {
|
||||||
|
...
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Both the external and internal iteration do basically the same and have the
|
||||||
|
same performance characteristics.
|
||||||
|
|
||||||
|
## The property
|
||||||
|
|
||||||
|
Each property you get from the feature is an object of the `property` class. It
|
||||||
|
contains a view of the property key and value. The key is always a string
|
||||||
|
encoded in a `vtzero::data_view`, the value can be of different types but is
|
||||||
|
always encapsulated in a `property_value` type, a variant type that can be
|
||||||
|
converted into whatever type the value really has.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto property = ...;
|
||||||
|
std::string pkey = property.key(); // returns a vtzero::data_view which can
|
||||||
|
// be converted to std::string
|
||||||
|
property_value pvalue = property.value();
|
||||||
|
```
|
||||||
|
|
||||||
|
To get the type of the property value, call `type()`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const auto type = pvalue.type();
|
||||||
|
```
|
||||||
|
|
||||||
|
If the property value is an int, for instance, you can get it like this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if (pvalue.type() == property_value_type::int_value)
|
||||||
|
int64_t v = pvalue.int_value();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead of accessing the values this way, you'll often use the visitor
|
||||||
|
interface. Here is an example where the `print_visitor` struct is used to print
|
||||||
|
out the values. In this case one overload is used for all primitive types
|
||||||
|
(`double`, `float`, `int`, `uint`, `bool`), one overload is used for the `string_value`
|
||||||
|
type which is encoded in a `data_view`. You must make sure your visitor handles
|
||||||
|
all those types.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct print_visitor {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void operator()(T value) {
|
||||||
|
std::cout << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(vtzero::data_view value) {
|
||||||
|
std::cout << std::string(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
vtzero::apply_visitor(print_visitor{}, pvalue));
|
||||||
|
```
|
||||||
|
|
||||||
|
All call operators of your visitor class have to return the same type. In the
|
||||||
|
case above this was `void`, but it can be something else. That return type
|
||||||
|
will be the return type of the `apply_visitor` function. This can be used,
|
||||||
|
for instance, to convert the values into one type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct to_string_visitor {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::string operator()(T value) {
|
||||||
|
reutrn std::to_string(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(vtzero::data_view value) {
|
||||||
|
return std::string(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string v = vtzero::apply_visitor(to_string_visitor{}, pvalue);
|
||||||
|
```
|
||||||
|
|
||||||
|
Sometimes you want to convert the `property_value` type into your own variant
|
||||||
|
type. You can use the `vtzero::convert_property_value()` free function for
|
||||||
|
this.
|
||||||
|
|
||||||
|
Lets say you are using `boost` and this is your variant:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using variant_type = boost::variant<std::string, float, double, int64_t, uint64_t, bool>;
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then use the following line to convert the data:
|
||||||
|
```cpp
|
||||||
|
variant_type v = vtzero::convert_property_value<variant_type>(pvalue);
|
||||||
|
```
|
||||||
|
|
||||||
|
Your variant type must be constructible from all the types `std::string`,
|
||||||
|
`float`, `double`, `int64_t`, `uint64_t`, and `bool`. If it is not, you can
|
||||||
|
define a mapping between those types and the types you use in your variant
|
||||||
|
class.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using variant_type = boost::variant<mystring, double, int64_t, uint64_t, bool>;
|
||||||
|
|
||||||
|
struct mapping : vtzero::property_value_mapping {
|
||||||
|
using string_type = mystring; // use your own string type which must be
|
||||||
|
// convertible from data_view
|
||||||
|
using float_type = double; // no float in variant, so convert to double
|
||||||
|
};
|
||||||
|
|
||||||
|
variant_type v = vtzero::convert_property_value<variant_type, mapping>(pvalue);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Creating a properties map
|
||||||
|
|
||||||
|
This linear access to the properties with lazy decoding of each property only
|
||||||
|
when it is accessed saves memory allocations, especially if you are only
|
||||||
|
interested in very few properties. But sometimes it is easier to create a
|
||||||
|
mapping (based on `std::unordered_map` for instance) between keys and values. This is where
|
||||||
|
the `vtzero::create_properties_map()` templated free function comes in. It
|
||||||
|
needs the map type as template parameter:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using key_type = std::string; // must be something that can be converted from data_view
|
||||||
|
using value_type = boost::variant<std::string, float, double, int64_t, uint64_t, bool>;
|
||||||
|
using map_type = std::map<key_type, value_type>;
|
||||||
|
|
||||||
|
auto feature = ...;
|
||||||
|
auto map = create_properties_map<map_type>(feature);
|
||||||
|
```
|
||||||
|
|
||||||
|
Both `std::map` and `std::unordered_map` are supported as map type, but this
|
||||||
|
should also work with any other map type that has an `emplace()` method.
|
||||||
|
|
||||||
|
## Geometries
|
||||||
|
|
||||||
|
Features must contain a geometry of type UNKNOWN, POINT, LINESTRING, or
|
||||||
|
POLYGON. The UNKNOWN type is not further specified by the vector tile spec,
|
||||||
|
this library doesn't allow you to do anything with this type. Note that
|
||||||
|
multipoint, multilinestring, and multipolygon geometries are also possible,
|
||||||
|
they don't have special types.
|
||||||
|
|
||||||
|
You can get the geometry type with `feature.geometry_type()`, but usually
|
||||||
|
you'll get the geometry with `feature.geometry()`. This will return an object
|
||||||
|
of type `vtzero::geometry` which contains the geometry type and a view of
|
||||||
|
the raw geometry data. To decode the data you have to call one of the decoder
|
||||||
|
free functions `decode_geometry()`, `decode_point_geometry()`,
|
||||||
|
`decode_linestring_geometry()`, or `decode_polygon_geometry()`. The first of
|
||||||
|
these functions can decode any point, linestring, or polygon geometry. The
|
||||||
|
others must be called with a geometry of the specified type and will only
|
||||||
|
decode that type.
|
||||||
|
|
||||||
|
For all the decoder functions the first parameter is the geometry (as returned
|
||||||
|
by `feature.geometry()`), the second parameter is a *handler* object that you
|
||||||
|
must implement. The decoder function will call certain callbacks on this object
|
||||||
|
that give you part of the geometry data which allows you to use this data in
|
||||||
|
any way you like.
|
||||||
|
|
||||||
|
The handler for `decode_point_geometry()` must implement the following
|
||||||
|
functions:
|
||||||
|
|
||||||
|
* `void points_begin(uint32_t count)`: This is called once at the beginning
|
||||||
|
with the number of points. For a point geometry, this will be 1, for
|
||||||
|
multipoint geometries this will be larger.
|
||||||
|
* `void points_point(vtzero::point point)`: This is called once for each
|
||||||
|
point.
|
||||||
|
* `void points_end()`: This is called once at the end.
|
||||||
|
|
||||||
|
The handler for `decode_linestring_geometry()` must implement the following
|
||||||
|
functions:
|
||||||
|
|
||||||
|
* `void linestring_begin(uint32_t count)`: This is called at the beginning
|
||||||
|
of each linestring with the number of points in this linestring. For a simple
|
||||||
|
linestring this function will only be called once, for a multilinestring
|
||||||
|
it will be called several times.
|
||||||
|
* `void linestring_point(vtzero::point point)`: This is called once for each
|
||||||
|
point.
|
||||||
|
* `void linestring_end()`: This is called at the end of each linestring.
|
||||||
|
|
||||||
|
The handler for `decode_polygon_geometry` must implement the following
|
||||||
|
functions:
|
||||||
|
|
||||||
|
* `void ring_begin(uint32_t count)`: This is called at the beginning
|
||||||
|
of each ring with the number of points in this ring. For a simple polygon
|
||||||
|
with only one outer ring, this function will only be called once, if there
|
||||||
|
are inner rings or if this is a multipolygon, it will be called several
|
||||||
|
times.
|
||||||
|
* `void ring_point(vtzero::point point)`: This is called once for each
|
||||||
|
point.
|
||||||
|
* `void ring_end(vtzero::ring_type)`: This is called at the end of each ring.
|
||||||
|
The parameter tells you whether the ring is an outer or inner ring or whether
|
||||||
|
the ring was invalid (if the area is 0).
|
||||||
|
|
||||||
|
The handler for `decode_geometry()` must implement all of the functions
|
||||||
|
mentioned above for the different types. It is guaranteed that only one
|
||||||
|
set of functions will be called depending on the geometry type.
|
||||||
|
|
||||||
|
If your handler implements the `result()` method, the decode functions will
|
||||||
|
have the return type of the `result()` method and will return whatever
|
||||||
|
result returns. If the `result()` method is not available, the decode functions
|
||||||
|
return void.
|
||||||
|
|
||||||
|
Here is a typical implementation of a linestring handler:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct linestring_handler {
|
||||||
|
|
||||||
|
using linestring = std::vector<my_point_type>;
|
||||||
|
|
||||||
|
linestring points;
|
||||||
|
|
||||||
|
void linestring_begin(uint32_t count) {
|
||||||
|
points.reserve(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_point(vtzero::point point) noexcept {
|
||||||
|
points.push_back(convert_to_my_point(point));
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_end() const noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
linestring result() {
|
||||||
|
return std::move(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the `count` given to the `linestring_begin()` method is used here to
|
||||||
|
reserve memory. This is potentially problematic if the count is large. Please
|
||||||
|
keep this in mind.
|
||||||
|
|
||||||
|
## Accessing the key/value lookup tables in a layer
|
||||||
|
|
||||||
|
Vector tile layers contain two tables with all the property keys and all
|
||||||
|
property values used in the features in that layer. Vtzero usually handles
|
||||||
|
those table lookups internally without you noticing. But sometimes it might
|
||||||
|
be necessary to access this data directly.
|
||||||
|
|
||||||
|
From the layer object you can get references to the tables:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
vtzero::layer layer = ...;
|
||||||
|
const auto& kt = layer.key_table();
|
||||||
|
const auto& vt = layer.value_table();
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead you can also lookup keys and values using methods on the layer object:
|
||||||
|
```cpp
|
||||||
|
vtzero::layer layer = ...;
|
||||||
|
const vtzero::data_view k = layer.key(17);
|
||||||
|
const vtzero::property_value_view v = layer.value(42);
|
||||||
|
```
|
||||||
|
|
||||||
|
As usual in vtzero you only get views back, so you need to keep the layer
|
||||||
|
object around as long as you are accessing the results of those methods.
|
||||||
|
|
||||||
|
Note that the lookup tables are created on first access from the layer data. As
|
||||||
|
long as you are not accessing those tables directly or by looking up any
|
||||||
|
properties in a feature, the tables are not created and no extra memory is
|
||||||
|
used.
|
||||||
|
|
94
doc/tutorial.md
Normal file
94
doc/tutorial.md
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
|
||||||
|
# Vtzero Tutorial
|
||||||
|
|
||||||
|
The vtzero header-only library is used to read and write vector tile data
|
||||||
|
as specified in the [Mapbox Vector Tile
|
||||||
|
Specification](https://github.com/mapbox/vector-tile-spec). This document
|
||||||
|
assumes that you are familiar with that specification.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The library has basically two parts: The part concerned with decoding existing
|
||||||
|
vector tiles and the part concerned with creating new vector tiles. You can
|
||||||
|
use either one without knowing much about the other side, but it is, of course
|
||||||
|
also possible to read parts of a vector tile and stick it into a new one.
|
||||||
|
|
||||||
|
Vtzero is trying to do as little work as possible while still giving you a
|
||||||
|
reasonably easy to use interface. This means that it will, as much as feasible,
|
||||||
|
decode the different parts of a vector tile only when you ask for them. Most
|
||||||
|
importantly it will try to avoid memory allocation and it will not copy data
|
||||||
|
around unnecessarily but work with references instead.
|
||||||
|
|
||||||
|
On the writing side it means that you have to call the API in a specific order
|
||||||
|
when adding more data to a vector tile. This allows vtzero to avoid multiple
|
||||||
|
copies of the data.
|
||||||
|
|
||||||
|
## Basic types
|
||||||
|
|
||||||
|
Vtzero contains several basic small (value) types such as `GeomType`,
|
||||||
|
`property_value_type`, `index_value`, `index_value_pair`, and `point` which
|
||||||
|
hold basic values in a type-safe way. Most of them are defined in `types.hpp`
|
||||||
|
(`point` is in `geometry.hpp`).
|
||||||
|
|
||||||
|
Sometimes it is useful to be able to print the values of those types, for
|
||||||
|
instance when debugging. For this overloads of `operator<<` on `basic_ostream`
|
||||||
|
are available in `vtzero/output.hpp`. Include this file and you can use the
|
||||||
|
usual `std::cout << some_value;` to print those values.
|
||||||
|
|
||||||
|
## Use of asserts and exceptions
|
||||||
|
|
||||||
|
The vtzero library uses a lot of asserts to help you use it correctly. It is
|
||||||
|
recommended you use debug builds while developing your code, because you'll
|
||||||
|
get asserts from vtzero telling you when you use it in a wrong way. This is
|
||||||
|
especially important when writing tiles using the builder classes, because
|
||||||
|
their methods have to be called in a certain order that might not always be
|
||||||
|
obvious but is checked by the asserts.
|
||||||
|
|
||||||
|
Exceptions, on the other hand, are used when errors occur during the normal run
|
||||||
|
of vtzero. This is especially important on the reading side when vtzero makes
|
||||||
|
every effort to handle any kind of input, even if the input data is corrupt
|
||||||
|
in some way. (Vtzero can't detect all problems though, your code still has to
|
||||||
|
do its own checking, see the [advanced topics](advanced.md) for some more
|
||||||
|
information.)
|
||||||
|
|
||||||
|
Many vtzero functions can throw exceptions. Most of them fall into these
|
||||||
|
categories:
|
||||||
|
|
||||||
|
* If the underlying protocol buffers data has some kind of problem, you'll
|
||||||
|
get an exception from the [protozero
|
||||||
|
library](https://github.com/mapbox/protozero/blob/master/doc/tutorial.md#asserts-and-exceptions-in-the-protozero-library).
|
||||||
|
They are all derived from `protozero::exception`.
|
||||||
|
* If the protocol buffers data is okay, but the vector tile data is invalid
|
||||||
|
in some way, you'll get an exception from the vtzero library.
|
||||||
|
* If any memory allocation failed, you'll get a `std::bad_alloc` exception.
|
||||||
|
|
||||||
|
All the exceptions thrown directly by the vtzero library are derived from
|
||||||
|
`vtzero::exception`. These exceptions are:
|
||||||
|
|
||||||
|
* A `format_exception` is thrown when vector tile encoding isn't valid
|
||||||
|
according to the vector tile specification.
|
||||||
|
* A `geometry_exception` is thrown when a geometry encoding isn't valid
|
||||||
|
according to the vector tile specification.
|
||||||
|
* A `type_exception` is thrown when a property value is accessed using the
|
||||||
|
wrong type.
|
||||||
|
* A `version_exception` is thrown when an unknown version number is found in
|
||||||
|
the layer. Currently vtzero only supports version 1 and 2.
|
||||||
|
* An `out_of_range_exception` is thrown when an index into the key or value
|
||||||
|
table in a layer is out of range. This can only happen if the tile data is
|
||||||
|
invalid.
|
||||||
|
|
||||||
|
## Include files
|
||||||
|
|
||||||
|
Usually you only directly include the following files:
|
||||||
|
|
||||||
|
* When reading: `<vtzero/vector_tile.hpp>`
|
||||||
|
* When writing: `<vtzero/builder.hpp>`
|
||||||
|
* If you need any of the special indexes: `<vtzero/index.hpp>`
|
||||||
|
* If you want overloads of `operator<<` for basic types: `<vtzero/output.hpp>`
|
||||||
|
* If you need the version: `<vtzero/version.hpp>`
|
||||||
|
|
||||||
|
## Reading and writing vector tiles
|
||||||
|
|
||||||
|
* [Reading vector tiles](reading.md)
|
||||||
|
* [Writing vector tiles](writing.md)
|
||||||
|
|
539
doc/writing.md
Normal file
539
doc/writing.md
Normal file
@ -0,0 +1,539 @@
|
|||||||
|
|
||||||
|
# Writing vector tiles
|
||||||
|
|
||||||
|
Writing vector tiles start with creating a `tile_builder`. This builder will
|
||||||
|
then be used to add layers and features in those layers. Once all this is done,
|
||||||
|
you call `serialize()` to actually build the vector tile from the data you
|
||||||
|
provided to the builders:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <vtzero/builder.hpp> // always needed when writing vector tiles
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
// add lots of data to builder...
|
||||||
|
std::string buffer = tbuilder.serialize();
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also serialize the data into an existing buffer instead:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::string buffer; // got buffer from somewhere
|
||||||
|
tbuilder.serialize(buffer);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding layers to tiles
|
||||||
|
|
||||||
|
Once you have a tile builder, you'll first need some layers:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder layer_pois{tbuilder, "pois", 2, 4096};
|
||||||
|
vtzero::layer_builder layer_roads{tbuilder, "roads"};
|
||||||
|
vtzero::layer_builder layer_forests{tbuilder, "forests"};
|
||||||
|
```
|
||||||
|
|
||||||
|
Here three layers called "pois", "roads", and "forests" are added. The first
|
||||||
|
one explicitly specifies the vector tile version used and the extent. The
|
||||||
|
values specified here are the default, so all layers in this example will have
|
||||||
|
a version of 2 and an extent of 4096.
|
||||||
|
|
||||||
|
If you have read a layer from an existing vector tile and want to copy over
|
||||||
|
some of the data, you can use this layer to initialize the new layer in the
|
||||||
|
new vector tile with the name, version and extent from the existing layer like
|
||||||
|
this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
vtzero::layer some_layer = ...;
|
||||||
|
vtzero::layer_builder layer_pois{tbuilder, some_layer};
|
||||||
|
// same as...
|
||||||
|
vtzero::layer_builder layer_pois{tbuilder, some_layer.name(),
|
||||||
|
some_layer.version(),
|
||||||
|
some_layer.extent()};
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to copy over an existing layer completely, you can use the
|
||||||
|
`add_existing_layer()` function instead:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
vtzero::layer some_layer = ...;
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
tbuilder.add_existing_layer(some_layer);
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, if you have the encoded layer data available in a `data_view` this also
|
||||||
|
works:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
vtzero::data_view layer_data = ...;
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
tbuilder.add_existing_layer(layer_data);
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this call will only store a reference to the data to be added in the
|
||||||
|
tile builder. The data will only be copied when the final `serialize()` is
|
||||||
|
called, so the input data must still be available then!
|
||||||
|
|
||||||
|
You can mix any of the ways of adding a layer to the tile mentioned above. The
|
||||||
|
layers will be added to the tile in the order you add them to the
|
||||||
|
`tile_builder`.
|
||||||
|
|
||||||
|
The tile builder is smart enough to not add empty layers, so you can start
|
||||||
|
out with all the layers you might need and if some of them stay empty, they
|
||||||
|
will not be added to the tile when `serialize()` is called.
|
||||||
|
|
||||||
|
## Adding features to layers
|
||||||
|
|
||||||
|
Once we have one or more `layer_builder`s instantiated, we can add features
|
||||||
|
to them. This is done through the following feature builder classes:
|
||||||
|
|
||||||
|
* `point_feature_builder` to add a feature with a (multi)point geometry,
|
||||||
|
* `linestring_feature_builder` to add a feature with a (multi)linestring
|
||||||
|
geometry,
|
||||||
|
* `polygon_feature_builder` to add a feature with a (multi)polygon geometry, or
|
||||||
|
* `geometry_feature_builder` to add a feature with an existing geometry you
|
||||||
|
got from reading a vector tile.
|
||||||
|
|
||||||
|
In all cases you need to instantiate the feature builder class, optionally
|
||||||
|
add the feature ID using the `set_id()` method, add the geometry and then
|
||||||
|
add all the properties of this feature. You have to keep to this order!
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
...
|
||||||
|
vtzero::layer_builder lbuilder{...};
|
||||||
|
{
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
// optionally set the ID
|
||||||
|
fbuilder.set_id(23);
|
||||||
|
// add the geometry (exact calls are different for different feature builders)
|
||||||
|
fbuilder.add_point(99, 33);
|
||||||
|
// add the properties
|
||||||
|
fbuilder.add_property("amenity", "restaurant");
|
||||||
|
// call commit() when you are done
|
||||||
|
fbuilder.commit()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You have to call `commit()` on the feature builder object after you set all the
|
||||||
|
data to actually add it to the layer. If you don't do this, the feature will
|
||||||
|
not be added to the layer! This can be useful, for instance, if you detect that
|
||||||
|
you have an invalid geometry while you are adding the geometry to the feature
|
||||||
|
builder. In that case you can call `rollback()` explicitly or just let the
|
||||||
|
feature builder go out of scope and it will do the rollback automatically.
|
||||||
|
|
||||||
|
Only the first call to `commit()` or `rollback()` will take effect, any further
|
||||||
|
calls to these functions on the same feature builder object are ignored.
|
||||||
|
|
||||||
|
## Adding a geometry to the feature
|
||||||
|
|
||||||
|
There are different ways of adding the geometry to the feature, depending on
|
||||||
|
the geometry type.
|
||||||
|
|
||||||
|
### Adding a point geometry
|
||||||
|
|
||||||
|
Simply call `add_point()` to set the point geometry. There are three different
|
||||||
|
overloads for this function. One takes a `vtzero::point`, one takes two
|
||||||
|
`uint32_t`s with the x and y coordinates and one takes any type `T` that can
|
||||||
|
be converted to a `vtzero::point` using the `create_vtzero_point` function.
|
||||||
|
This templated function works on any type that has `x` and `y` members and
|
||||||
|
you can create your own overload of this function. See the
|
||||||
|
[advanced.md](advanced topics documentation).
|
||||||
|
|
||||||
|
### Adding a multipoint geometry
|
||||||
|
|
||||||
|
Call `add_points()` with the number of points in the geometry as only argument.
|
||||||
|
After that call `set_point()` for each of those points. `set_point()` has
|
||||||
|
multiple overloads just like the `add_point()` method described above.
|
||||||
|
|
||||||
|
There is also the `add_points_from_container()` function which copies the
|
||||||
|
point from any container type supporting the `size()` function and which
|
||||||
|
iterator yields a `vtzero::point` or something convertible to it.
|
||||||
|
|
||||||
|
### Adding a linestring geometry
|
||||||
|
|
||||||
|
Call `add_linestring()` with the number of points in the linestring as only
|
||||||
|
argument. After that call `set_point()` for each of those points. `set_point()`
|
||||||
|
has multiple overloads just like the `add_point()` method described above.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
...
|
||||||
|
vtzero::layer_builder lbuilder{...};
|
||||||
|
try {
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
// optionally set the ID
|
||||||
|
fbuilder.set_id(23);
|
||||||
|
// add the geometry
|
||||||
|
fbuilder.add_linestring(2);
|
||||||
|
fbuilder.set_point(1, 2);
|
||||||
|
fbuilder.set_point(3, 4);
|
||||||
|
// add the properties
|
||||||
|
fbuilder.add_property("highway", "primary");
|
||||||
|
fbuilder.add_property("maxspeed", 80);
|
||||||
|
// call commit() when you are done
|
||||||
|
fbuilder.commit()
|
||||||
|
} catch (const vtzero::geometry_exception& e) {
|
||||||
|
// if we are here, something was wrong with the geometry.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that we have wrapped the feature builder in a try-catch-block here. This
|
||||||
|
will ignore all geometry errors (which can happen if two consective points
|
||||||
|
are the same creating a zero-length segment).
|
||||||
|
|
||||||
|
There are two other versions of the `add_linestring()` function. They take two
|
||||||
|
iterators defining a range to get the points from. Dereferencing those
|
||||||
|
iterators must yield a `vtzero::point` or something convertible to it. One of
|
||||||
|
these functions takes a third argument, the number of points the iterator will
|
||||||
|
yield. If this is not available `std::distance(begin, end)` is called which
|
||||||
|
internally by the `add_linestring()` function which might be slow depending on
|
||||||
|
your iterator type.
|
||||||
|
|
||||||
|
### Adding a multilinestring geometry
|
||||||
|
|
||||||
|
Adding a multilinestring works just like adding a linestring, just do the
|
||||||
|
calls to `add_linestring()` etc. repeatedly for each of the linestrings.
|
||||||
|
|
||||||
|
### Adding a polygon geometry
|
||||||
|
|
||||||
|
A polygon consists of one outer ring and zero or more inner rings. You have
|
||||||
|
to first add the outer ring and then the inner rings, if any.
|
||||||
|
|
||||||
|
Call `add_ring()` with the number of points in the ring as only argument. After
|
||||||
|
that call `set_point()` for each of those points. `set_point()` has multiple
|
||||||
|
overloads just like the `add_point()` method described above. The minimum
|
||||||
|
number of points is 4 and the last point must be the same as the first point
|
||||||
|
(or call `close_ring()` instead of the last `set_point()`).
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
...
|
||||||
|
vtzero::layer_builder lbuilder{...};
|
||||||
|
try {
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
// optionally set the ID
|
||||||
|
fbuilder.set_id(23);
|
||||||
|
// add the geometry
|
||||||
|
fbuilder.add_ring(5);
|
||||||
|
fbuilder.set_point(1, 1);
|
||||||
|
fbuilder.set_point(1, 2);
|
||||||
|
fbuilder.set_point(2, 2);
|
||||||
|
fbuilder.set_point(2, 1);
|
||||||
|
fbuilder.set_point(1, 1); // or call fbuilder.close_ring() instead
|
||||||
|
// add the properties
|
||||||
|
fbuilder.add_property("landuse", "forest");
|
||||||
|
// call commit() when you are done
|
||||||
|
fbuilder.commit()
|
||||||
|
} catch (const vtzero::geometry_exception& e) {
|
||||||
|
// if we are here, something was wrong with the geometry.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that we have wrapped the feature builder in a try-catch-block here. This
|
||||||
|
will ignore all geometry errors (which can happen if two consective points
|
||||||
|
are the same creating a zero-length segment or if the last point is not the
|
||||||
|
same as the first point).
|
||||||
|
|
||||||
|
There are two other versions of the `add_ring()` function. They take two
|
||||||
|
iterators defining a range to get the points from. Dereferencing those
|
||||||
|
iterators must yield a `vtzero::point` or something convertible to it. One of
|
||||||
|
these functions takes a third argument, the number of points the iterator will
|
||||||
|
yield. If this is not available `std::distance(begin, end)` is called which
|
||||||
|
internally by the `add_ring()` function which might be slow depending on your
|
||||||
|
iterator type.
|
||||||
|
|
||||||
|
### Adding a multipolygon geometry
|
||||||
|
|
||||||
|
Adding a multipolygon works just like adding a polygon, just do the calls to
|
||||||
|
`add_ring()` etc. repeatedly for each of the rings. Make sure to always first
|
||||||
|
add an outer ring, then the inner rings in this outer ring, then the next
|
||||||
|
outer ring and so on.
|
||||||
|
|
||||||
|
### Adding an existing geometry
|
||||||
|
|
||||||
|
The `geometry_feature_builder` class is used to add geometries you got from
|
||||||
|
reading a vector tile. This is useful when you want to copy over a geometry
|
||||||
|
from a feature without decoding it.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto geom = ... // get geometry from a feature you are reading
|
||||||
|
...
|
||||||
|
vtzero::tile_builder tb;
|
||||||
|
vtzero::layer_builder lb{tb};
|
||||||
|
vtzero::geometry_feature_builder fb{lb};
|
||||||
|
fb.set_id(123); // optionally set ID
|
||||||
|
fb.add_geometry(geom) // add geometry
|
||||||
|
fb.add_property("foo", "bar"); // add properties
|
||||||
|
fb.commit();
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding properties to the feature
|
||||||
|
|
||||||
|
A feature can have any number of properties. They are added with the
|
||||||
|
`add_property()` method called on the feature builder. There are two different
|
||||||
|
ways of doing this. The *simple approach* which does all the work for you and
|
||||||
|
the *advanced approach* which can be more efficient, but you have to to some
|
||||||
|
more work. It is recommended that you start out with the simple approach and
|
||||||
|
only switch to the advanced approach once you have a working program and want
|
||||||
|
to get the last bit of performance out of it.
|
||||||
|
|
||||||
|
The difference stems from the way properties are encoded in vector tiles. While
|
||||||
|
properties "belong" to features, they are really stored in two tables (for the
|
||||||
|
keys and values) in the layer. The individual feature only references the
|
||||||
|
entries in those tables by index. This make the encoded tile smaller, but it
|
||||||
|
means somebody has to manage those tables. In the simple approach this is done
|
||||||
|
behind the scenes by the `layer_builder` object, in the advanced approach you
|
||||||
|
handle that yourself.
|
||||||
|
|
||||||
|
Do not mix the simple and the advanced approach unless you know what you are
|
||||||
|
doing.
|
||||||
|
|
||||||
|
### The simple approach to adding properties
|
||||||
|
|
||||||
|
For the simple approach call `add_property()` with two arguments. The first is
|
||||||
|
the key, it must have some kind of string type (`std::string`, `const char*`,
|
||||||
|
`vtzero::data_view`, anything really that converts to a `data_view`). The
|
||||||
|
second argument is the value, for which most basic C++ types are allowed
|
||||||
|
(string types, integer types, double, ...). See the API documentation for the
|
||||||
|
constructors of the `encoded_property_value` class for a list.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
vtzero::layer_builder lb{...};
|
||||||
|
vtzero::linestring_feature_builder fb{lb};
|
||||||
|
...
|
||||||
|
fb.add_property("waterway", "stream"); // string value
|
||||||
|
fb.add_property("name", "Little Creek");
|
||||||
|
fb.add_property("width", 1.5); // double value
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Sometimes you need to specify exactly which type should be used in the
|
||||||
|
encoding. The `encoded_property_value` constructor can take special types for
|
||||||
|
that like in the following example, where you force the `sint` encoding:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
fb.add_property("layer", vtzero::sint_value_type(2));
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also call `add_property()` with a single `vtzero::property` argument
|
||||||
|
(which is handy if you are copying this property over from a tile you are
|
||||||
|
reading):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
while (auto property = feature.next_property()) {
|
||||||
|
if (property.key() == "name") {
|
||||||
|
feature_builder.add_property(property);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### The advanced approach to adding properties
|
||||||
|
|
||||||
|
In the advanced approach you have to do the indexing yourself. Here is a very
|
||||||
|
basic example:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
const vtzero::index_value highway = lbuilder.add_key("highway");
|
||||||
|
const vtzero::index_value primary = lbuilder.add_value("primary");
|
||||||
|
...
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
...
|
||||||
|
fbuilder.add_property(highway, primary);
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
The methods `add_key()` and `add_value()` on the layer builder are used to add
|
||||||
|
keys and values to the tables in the layer. They both return the index (of type
|
||||||
|
`vtzero::index_value`) of those keys or values in the tables. You store
|
||||||
|
those index values somewhere (in this case in the `highway` and `primary`
|
||||||
|
variables) and use them when calling `add_property()` on the feature builder.
|
||||||
|
|
||||||
|
In some cases you only have a few property keys and know them beforehand,
|
||||||
|
then storing the key indexes in individual variables might work. But for
|
||||||
|
values this usually doesn't make much sense, and if all your keys and values
|
||||||
|
are only known at runtime, it doesn't work either. For this you need some kind
|
||||||
|
of index data structure mapping from keys/values to index values. You can
|
||||||
|
implement this yourself, but it is easier to use some classes provided by
|
||||||
|
vtzero. Then the code looks like this:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <vtzero/index.hpp> // use this include to get the index classes
|
||||||
|
...
|
||||||
|
vtzero::layer_builder lb{...};
|
||||||
|
vtzero::key_index<std::map> key_index{lb};
|
||||||
|
vtzero::value_index_internal<std::unordered_map> value_index{lb};
|
||||||
|
...
|
||||||
|
vtzero::point_feature_builder fb{lb};
|
||||||
|
...
|
||||||
|
fb.add_property(key_index("highway"), value_index("primary"));
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example the `key_index` template class is used for keys, it uses
|
||||||
|
`std::map` internally as can be seen by its template argument. The
|
||||||
|
`value_index_internal` template class is used for values, it uses
|
||||||
|
`std::unordered_map` internally in this example. Whether you specify `std::map`
|
||||||
|
or `std::unordered_map` or something else (that needs to be compatible to those
|
||||||
|
classes) is up to you. Benchmark your use case and decide then.
|
||||||
|
|
||||||
|
Keys are always strings, so they are easy to handle. For keys there is only the
|
||||||
|
single `key_index` in vtzero.
|
||||||
|
|
||||||
|
For values this is more difficult. Basically there are two choices:
|
||||||
|
|
||||||
|
1. Encode the value according to the vector tile encoding rules which results
|
||||||
|
in a string and store this in the index. This is what the
|
||||||
|
`value_index_internal` class does.
|
||||||
|
2. Store the un-encoded value in the index. The index lookup will be faster,
|
||||||
|
but you need a different index type for each value type. This is what the
|
||||||
|
`value_index` classes do.
|
||||||
|
|
||||||
|
The `value_index` template classes need three template arguments: The type
|
||||||
|
used internally to encode the value, the type used externally, and the map
|
||||||
|
type.
|
||||||
|
|
||||||
|
In this example the user program has the values as `int`, the index will store
|
||||||
|
them in a `std::map<int>`. The integer value is then encoded in an `sint`
|
||||||
|
int the vector tile:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
vtzero::value_index<vtzero::sint_value:type, int, std::map> index;
|
||||||
|
```
|
||||||
|
|
||||||
|
Sometimes these generic indexes based on `std::map` or `std::unordered_map`
|
||||||
|
are inefficient, that's why there are specialized indexes for special cases:
|
||||||
|
|
||||||
|
* The `value_index_bool` class can only index boolean values.
|
||||||
|
* The `value_index_small_uint` class can only index small unsigned integer
|
||||||
|
values (up to `uint16_t`). It uses a vector internally, so if all your
|
||||||
|
numbers are small and densely packed, this is very efficient. This is
|
||||||
|
especially useful for `enum` types.
|
||||||
|
|
||||||
|
## The `add_property()` function.
|
||||||
|
|
||||||
|
The last chapters already talked about the `add_property()` function of the
|
||||||
|
`feature_builder` class. But because it is a bit difficult to see all the
|
||||||
|
different ways `add_property()` can be called, here is some more information.
|
||||||
|
|
||||||
|
The `add_property()` function is called with either two parameters for the
|
||||||
|
key and value or with one parameter that combines the key and value.
|
||||||
|
|
||||||
|
If it is called with an `index_value` for the key or value, that index value is
|
||||||
|
stored directly into the feature. If it is called with an `index_value_pair`,
|
||||||
|
the index values in the `index_value_pair` are stored directly in the feature.
|
||||||
|
|
||||||
|
If it is called with something that is not an `index_value` or
|
||||||
|
`index_value_pair`, the function will interpret the data as keys or values.
|
||||||
|
It will add those keys and values to the layer (if they are not already there),
|
||||||
|
find the corresponding index values and store them in the feature.
|
||||||
|
|
||||||
|
You can mix index-use with non-index use. For instance
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
index_value key_maxspeed = lbuilder.add_key("maxspeed");
|
||||||
|
...
|
||||||
|
fbuilder.add_property(key_maxspeed, 30);
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case the key ("maxspeed") was added to the layer once and its index
|
||||||
|
value (`key_maxspeed`) can later be reused. The value (30), on the other hand,
|
||||||
|
is only added to the layer in the `add_property()` call.
|
||||||
|
|
||||||
|
So for keys, you can have as argument:
|
||||||
|
* An `index_value`.
|
||||||
|
* A `data_view` or something that converts to it like a `const char*` or `std::string`.
|
||||||
|
|
||||||
|
For values, you can have as argument:
|
||||||
|
* An `index_value`.
|
||||||
|
* A `property_value`.
|
||||||
|
* An `encoded_property_value` or anything that converts to it.
|
||||||
|
|
||||||
|
For combined keys and values, you can have as argument:
|
||||||
|
* An `index_value_pair`.
|
||||||
|
* A `property`.
|
||||||
|
|
||||||
|
|
||||||
|
## Deriving from `layer_builder` and `feature_builder`
|
||||||
|
|
||||||
|
The `vtzero::layer_builder` and `vtzero::feature_builder` classes have been
|
||||||
|
designed in a way that they can be derived from easily. This allows you to
|
||||||
|
encapsulate part of your vector tile writing code if some aspects of your
|
||||||
|
layers/features are always the same, such as the layer name and the names
|
||||||
|
and types of properties.
|
||||||
|
|
||||||
|
Say you want to write a layer named "restaurants" with point geometries.
|
||||||
|
Each feature should have a name and a 5-star-rating. First you create a
|
||||||
|
class derived from the `layer_builder` with all the indexes you want to use.
|
||||||
|
For the keys you don't need indexes in this case, because there are only
|
||||||
|
two keys for which we can easily store the index values in the layer.
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class restaurant_layer_builder : public vtzero::layer_builder {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// The index we'll use for the "name" property values
|
||||||
|
vtzero::value_index<vtzero::string_value_type, std::string, std::unordered_map> string_index;
|
||||||
|
|
||||||
|
// The index we'll use for the "stars" property values
|
||||||
|
vtzero::value_index_small_uint stars_index;
|
||||||
|
|
||||||
|
// The index value of the "name" key
|
||||||
|
vtzero::index_value key_name;
|
||||||
|
|
||||||
|
// The index value of the "stars" key
|
||||||
|
vtzero::index_value key_stars;
|
||||||
|
|
||||||
|
restaurant_layer_builder(vtzero::tile_builder& tile) :
|
||||||
|
layer_builder(tile, "restaurants"), // the name of the layer
|
||||||
|
string_index(*this),
|
||||||
|
stars_index(*this),
|
||||||
|
key_name(add_key_without_dup_check("name")),
|
||||||
|
key_stars(add_key_without_dup_check("stars")) {
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The we'll add a class derived from `feature_builder` to help with adding
|
||||||
|
features:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class restaurant_feature_builder : public vtzero::feature_builder {
|
||||||
|
|
||||||
|
restaurant_layer_builder& m_layer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
restaurant_feature_builder(restaurant_layer_builder& layer, uint64_t id) :
|
||||||
|
vtzero::point_feature_builder(layer), // always a point geometry
|
||||||
|
m_layer(layer) {
|
||||||
|
set_id(id); // we always have an ID in this case
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_location(mylocation& loc) { // restaurant location is stored in your own type
|
||||||
|
add_point(loc.lon(), loc.lat());
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_name(const std::string& name) {
|
||||||
|
add_property(m_layer.key_name,
|
||||||
|
m_layer.string_index(vtzero::encoded_property_value{name}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_stars(stars s) { // your own "stars" type
|
||||||
|
vtzero::encoded_property_value svalue{ s.num_stars() }; // convert stars type to small integer
|
||||||
|
|
||||||
|
add_property(m_layer.key_stars,
|
||||||
|
m_layer.stars_index(svalue));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
This example only shows a general pattern you can follow to derive from the
|
||||||
|
`layer_builder` and `feature_builder` classes. In some cases this makes more
|
||||||
|
sense then in others. The derived classes make it easy for you to mix your
|
||||||
|
own functions (for instance when you need to convert from your own types to
|
||||||
|
vtzero types like with the `mylocation` and `stars` types above) or just use
|
||||||
|
the functions in the base classes.
|
||||||
|
|
88
examples/CMakeLists.txt
Normal file
88
examples/CMakeLists.txt
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# CMake config
|
||||||
|
#
|
||||||
|
# vtzero examples
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
include_directories(SYSTEM "${CMAKE_SOURCE_DIR}/include-external")
|
||||||
|
|
||||||
|
set(TEST_FILE "${CMAKE_SOURCE_DIR}/test/data/mapbox-streets-v6-14-8714-8017.mvt")
|
||||||
|
|
||||||
|
add_executable(vtzero-check vtzero-check.cpp utils.cpp)
|
||||||
|
|
||||||
|
add_executable(vtzero-create vtzero-create.cpp utils.cpp)
|
||||||
|
|
||||||
|
add_executable(vtzero-encode-geom vtzero-encode-geom.cpp utils.cpp)
|
||||||
|
|
||||||
|
add_executable(vtzero-stats vtzero-stats.cpp utils.cpp)
|
||||||
|
|
||||||
|
add_executable(vtzero-streets vtzero-streets.cpp utils.cpp)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------
|
||||||
|
|
||||||
|
add_executable(vtzero-filter vtzero-filter.cpp utils.cpp)
|
||||||
|
|
||||||
|
add_test(NAME vtzero-filter-empty
|
||||||
|
COMMAND vtzero-filter)
|
||||||
|
set_tests_properties(vtzero-filter-empty PROPERTIES
|
||||||
|
PASS_REGULAR_EXPRESSION "^Error in command line: Missing file name of vector tile to read")
|
||||||
|
|
||||||
|
add_test(NAME vtzero-filter-help
|
||||||
|
COMMAND vtzero-filter -h)
|
||||||
|
set_tests_properties(vtzero-filter-help PROPERTIES
|
||||||
|
PASS_REGULAR_EXPRESSION "^usage:\n vtzero-filter")
|
||||||
|
|
||||||
|
add_test(NAME vtzero-filter-layer
|
||||||
|
COMMAND vtzero-filter -o ${CMAKE_CURRENT_BINARY_DIR}/bridges.mvt ${TEST_FILE} bridge)
|
||||||
|
|
||||||
|
add_test(NAME vtzero-filter-feature
|
||||||
|
COMMAND vtzero-filter -o ${CMAKE_CURRENT_BINARY_DIR}/bridges.mvt ${TEST_FILE} waterway_label 221925711)
|
||||||
|
|
||||||
|
add_test(NAME vtzero-filter-invalid-id
|
||||||
|
COMMAND vtzero-filter -o ${CMAKE_CURRENT_BINARY_DIR}/bridges.mvt ${TEST_FILE} waterway_label abc)
|
||||||
|
set_tests_properties(vtzero-filter-invalid-id PROPERTIES
|
||||||
|
WILL_FAIL true)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------
|
||||||
|
|
||||||
|
add_executable(vtzero-show vtzero-show.cpp utils.cpp)
|
||||||
|
|
||||||
|
add_test(NAME vtzero-show-empty
|
||||||
|
COMMAND vtzero-show)
|
||||||
|
set_tests_properties(vtzero-show-empty PROPERTIES
|
||||||
|
PASS_REGULAR_EXPRESSION "^Error in command line: Missing file name of vector tile to read")
|
||||||
|
|
||||||
|
add_test(NAME vtzero-show-help
|
||||||
|
COMMAND vtzero-show -h)
|
||||||
|
set_tests_properties(vtzero-show-help PROPERTIES
|
||||||
|
PASS_REGULAR_EXPRESSION "^usage:\n vtzero-show")
|
||||||
|
|
||||||
|
add_test(NAME vtzero-show-layers
|
||||||
|
COMMAND vtzero-show -l ${TEST_FILE})
|
||||||
|
set_tests_properties(vtzero-show-layers PROPERTIES
|
||||||
|
PASS_REGULAR_EXPRESSION "^landuse 78\nwaterway 327\n.*\nwaterway_label 4\n$")
|
||||||
|
|
||||||
|
add_test(NAME vtzero-show-layer-num
|
||||||
|
COMMAND vtzero-show ${TEST_FILE} 2)
|
||||||
|
set_tests_properties(vtzero-show-layer-num PROPERTIES
|
||||||
|
PASS_REGULAR_EXPRESSION "layer: [0-9]+\n name: water\n")
|
||||||
|
|
||||||
|
add_test(NAME vtzero-show-layer-name
|
||||||
|
COMMAND vtzero-show ${CMAKE_SOURCE_DIR}/test/data/mapbox-streets-v6-14-8714-8017.mvt water)
|
||||||
|
set_tests_properties(vtzero-show-layer-name PROPERTIES
|
||||||
|
PASS_REGULAR_EXPRESSION "layer: [0-9]+\n name: water\n")
|
||||||
|
|
||||||
|
#-------------------------------------------------------------
|
||||||
|
|
||||||
|
file(GLOB ext_tests RELATIVE ${CMAKE_SOURCE_DIR}/test/data/ ${CMAKE_SOURCE_DIR}/test/data/*.mvt)
|
||||||
|
|
||||||
|
foreach(_test IN LISTS ext_tests)
|
||||||
|
message(STATUS "Adding ext test: ${_test}")
|
||||||
|
add_test(NAME ext-tests-${_test}
|
||||||
|
COMMAND vtzero-show ${CMAKE_SOURCE_DIR}/test/data/${_test})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
100
examples/utils.cpp
Normal file
100
examples/utils.cpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
Utility functions for vtzero example programs.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read complete contents of a file into a string.
|
||||||
|
*
|
||||||
|
* The file is read in binary mode.
|
||||||
|
*
|
||||||
|
* @param filename The file name. Can be empty or "-" to read from STDIN.
|
||||||
|
* @returns a string with the contents of the file.
|
||||||
|
* @throws various exceptions if there is an error
|
||||||
|
*/
|
||||||
|
std::string read_file(const std::string& filename) {
|
||||||
|
if (filename.empty() || (filename.size() == 1 && filename[0] == '-')) {
|
||||||
|
return std::string{std::istreambuf_iterator<char>(std::cin.rdbuf()),
|
||||||
|
std::istreambuf_iterator<char>()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream stream{filename, std::ios_base::in | std::ios_base::binary};
|
||||||
|
if (!stream) {
|
||||||
|
throw std::runtime_error{std::string{"Can not open file '"} + filename + "'"};
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.exceptions(std::ifstream::failbit);
|
||||||
|
|
||||||
|
std::string buffer{std::istreambuf_iterator<char>(stream.rdbuf()),
|
||||||
|
std::istreambuf_iterator<char>()};
|
||||||
|
stream.close();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write contents of a buffer into a file.
|
||||||
|
*
|
||||||
|
* The file is written in binary mode.
|
||||||
|
*
|
||||||
|
* @param buffer The data to be written.
|
||||||
|
* @param filename The file name.
|
||||||
|
* @throws various exceptions if there is an error
|
||||||
|
*/
|
||||||
|
void write_data_to_file(const std::string& buffer, const std::string& filename) {
|
||||||
|
std::ofstream stream{filename, std::ios_base::out | std::ios_base::binary};
|
||||||
|
if (!stream) {
|
||||||
|
throw std::runtime_error{std::string{"Can not open file '"} + filename + "'"};
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.exceptions(std::ifstream::failbit);
|
||||||
|
|
||||||
|
stream.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a specific layer from a vector tile. The layer can be specified as a
|
||||||
|
* number n in which case the nth layer in this tile is returned. Or it can
|
||||||
|
* be specified as text, in which case the layer with that name is returned.
|
||||||
|
*
|
||||||
|
* Calls exit(1) if there is an error.
|
||||||
|
*
|
||||||
|
* @param tile The vector tile.
|
||||||
|
* @param layer_name_or_num specifies the layer.
|
||||||
|
*/
|
||||||
|
vtzero::layer get_layer(const vtzero::vector_tile& tile, const std::string& layer_name_or_num) {
|
||||||
|
vtzero::layer layer;
|
||||||
|
char* str_end = nullptr;
|
||||||
|
const long num = std::strtol(layer_name_or_num.c_str(), &str_end, 10); // NOLINT(google-runtime-int)
|
||||||
|
|
||||||
|
if (str_end == layer_name_or_num.data() + layer_name_or_num.size()) {
|
||||||
|
if (num >= 0 && num < std::numeric_limits<long>::max()) { // NOLINT(google-runtime-int)
|
||||||
|
layer = tile.get_layer(static_cast<std::size_t>(num));
|
||||||
|
if (!layer) {
|
||||||
|
std::cerr << "No such layer: " << num << '\n';
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
return layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layer = tile.get_layer_by_name(layer_name_or_num);
|
||||||
|
if (!layer) {
|
||||||
|
std::cerr << "No layer named '" << layer_name_or_num << "'.\n";
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
return layer;
|
||||||
|
}
|
||||||
|
|
11
examples/utils.hpp
Normal file
11
examples/utils.hpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
#include <vtzero/vector_tile.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
std::string read_file(const std::string& filename);
|
||||||
|
|
||||||
|
void write_data_to_file(const std::string& buffer, const std::string& filename);
|
||||||
|
|
||||||
|
vtzero::layer get_layer(const vtzero::vector_tile& tile, const std::string& layer_name_or_num);
|
||||||
|
|
225
examples/vtzero-check.cpp
Normal file
225
examples/vtzero-check.cpp
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
Example program for vtzero library.
|
||||||
|
|
||||||
|
vtzero-check - Check vector tiles for validity
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <vtzero/vector_tile.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <set>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class result {
|
||||||
|
|
||||||
|
int m_return_code = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void has_warning() noexcept {
|
||||||
|
if (m_return_code < 1) {
|
||||||
|
m_return_code = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void has_error() noexcept {
|
||||||
|
if (m_return_code < 2) {
|
||||||
|
m_return_code = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void has_fatal_error() noexcept {
|
||||||
|
if (m_return_code < 3) {
|
||||||
|
m_return_code = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int return_code() const noexcept {
|
||||||
|
return m_return_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
} result;
|
||||||
|
|
||||||
|
class CheckGeomHandler {
|
||||||
|
|
||||||
|
vtzero::point m_prev_point{};
|
||||||
|
int m_layer_num;
|
||||||
|
int m_feature_num;
|
||||||
|
int64_t m_extent;
|
||||||
|
bool m_is_first_point = false;
|
||||||
|
int m_count = 0;
|
||||||
|
|
||||||
|
void print_context() const {
|
||||||
|
std::cerr << " in layer " << m_layer_num
|
||||||
|
<< " in feature " << m_feature_num
|
||||||
|
<< " in geometry " << m_count
|
||||||
|
<< ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_error(const char* message) const {
|
||||||
|
result.has_error();
|
||||||
|
std::cerr << "Error";
|
||||||
|
print_context();
|
||||||
|
std::cerr << message << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_warning(const char* message) const {
|
||||||
|
result.has_warning();
|
||||||
|
std::cerr << "Warning";
|
||||||
|
print_context();
|
||||||
|
std::cerr << message << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_point_location(const vtzero::point point) const {
|
||||||
|
if (point.x < -m_extent ||
|
||||||
|
point.y < -m_extent ||
|
||||||
|
point.x > 2 * m_extent ||
|
||||||
|
point.y > 2 * m_extent) {
|
||||||
|
print_warning("point waaaay beyond the extent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
CheckGeomHandler(uint32_t extent, int layer_num, int feature_num) :
|
||||||
|
m_layer_num(layer_num),
|
||||||
|
m_feature_num(feature_num),
|
||||||
|
m_extent(static_cast<int64_t>(extent)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void points_begin(const uint32_t /*count*/) const {
|
||||||
|
}
|
||||||
|
|
||||||
|
void points_point(const vtzero::point point) const {
|
||||||
|
check_point_location(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
void points_end() const {
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void linestring_begin(const uint32_t count) {
|
||||||
|
if (count < 2) {
|
||||||
|
print_error("Not enough points in linestring");
|
||||||
|
}
|
||||||
|
m_is_first_point = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_point(const vtzero::point point) {
|
||||||
|
if (m_is_first_point) {
|
||||||
|
m_is_first_point = false;
|
||||||
|
} else {
|
||||||
|
if (point == m_prev_point) {
|
||||||
|
print_error("Duplicate point in linestring");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_prev_point = point;
|
||||||
|
|
||||||
|
check_point_location(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_end() {
|
||||||
|
++m_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void ring_begin(const uint32_t count) {
|
||||||
|
if (count < 4) {
|
||||||
|
print_error("Not enough points in ring");
|
||||||
|
}
|
||||||
|
m_is_first_point = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_point(const vtzero::point point) {
|
||||||
|
if (m_is_first_point) {
|
||||||
|
m_is_first_point = false;
|
||||||
|
} else {
|
||||||
|
if (point == m_prev_point) {
|
||||||
|
print_error("Duplicate point in ring");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_prev_point = point;
|
||||||
|
|
||||||
|
check_point_location(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_end(const vtzero::ring_type rt) {
|
||||||
|
if (rt == vtzero::ring_type::invalid) {
|
||||||
|
print_error("Invalid ring with area 0");
|
||||||
|
}
|
||||||
|
if (m_count == 0 && rt != vtzero::ring_type::outer) {
|
||||||
|
print_error("First ring isn't an outer ring");
|
||||||
|
}
|
||||||
|
++m_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class CheckGeomHandler
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cerr << "Usage: " << argv[0] << " TILE\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string input_file{argv[1]};
|
||||||
|
const auto data = read_file(input_file);
|
||||||
|
|
||||||
|
std::set<std::string> layer_names;
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
int layer_num = 0;
|
||||||
|
int feature_num = -1;
|
||||||
|
try {
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
if (layer.name().empty()) {
|
||||||
|
std::cerr << "Error in layer " << layer_num << ": name is empty (spec 4.1)\n";
|
||||||
|
result.has_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string name(layer.name());
|
||||||
|
if (layer_names.count(name) > 0) {
|
||||||
|
std::cerr << "Error in layer " << layer_num << ": name is duplicate of previous layer ('" << name << "') (spec 4.1)\n";
|
||||||
|
result.has_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
layer_names.insert(name);
|
||||||
|
|
||||||
|
feature_num = 0;
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
CheckGeomHandler handler{layer.extent(), layer_num, feature_num};
|
||||||
|
vtzero::decode_geometry(feature.geometry(), handler);
|
||||||
|
++feature_num;
|
||||||
|
}
|
||||||
|
if (feature_num == 0) {
|
||||||
|
std::cerr << "Warning: No features in layer " << layer_num << " (spec 4.1)\n";
|
||||||
|
result.has_warning();
|
||||||
|
}
|
||||||
|
feature_num = -1;
|
||||||
|
++layer_num;
|
||||||
|
}
|
||||||
|
if (layer_num == 0) {
|
||||||
|
std::cerr << "Warning: No layers in vector tile (spec 4.1)\n";
|
||||||
|
result.has_warning();
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Fatal error in layer " << layer_num;
|
||||||
|
if (feature_num >= 0) {
|
||||||
|
std::cerr << " in feature " << feature_num;
|
||||||
|
}
|
||||||
|
std::cerr << ": " << e.what() << '\n';
|
||||||
|
result.has_fatal_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.return_code();
|
||||||
|
}
|
||||||
|
|
101
examples/vtzero-create.cpp
Normal file
101
examples/vtzero-create.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
Example program for vtzero library.
|
||||||
|
|
||||||
|
vtzero-create - Create a vector tile
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <vtzero/builder.hpp>
|
||||||
|
#include <vtzero/index.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
vtzero::tile_builder tile;
|
||||||
|
vtzero::layer_builder layer_points{tile, "points"};
|
||||||
|
vtzero::layer_builder layer_lines{tile, "lines"};
|
||||||
|
vtzero::layer_builder layer_polygons{tile, "polygons"};
|
||||||
|
|
||||||
|
vtzero::key_index<std::unordered_map> idx{layer_points};
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::point_feature_builder feature{layer_points};
|
||||||
|
feature.set_id(1);
|
||||||
|
feature.add_points(1);
|
||||||
|
feature.set_point(10, 10);
|
||||||
|
feature.add_property("foo", "bar");
|
||||||
|
feature.add_property("x", "y");
|
||||||
|
feature.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto some = idx("some");
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::point_feature_builder feature{layer_points};
|
||||||
|
feature.set_id(2);
|
||||||
|
feature.add_point(20, 20);
|
||||||
|
feature.add_property(some, "attr");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
vtzero::point_feature_builder feature{layer_points};
|
||||||
|
feature.set_id(3);
|
||||||
|
feature.add_point(20, 20);
|
||||||
|
feature.add_property(idx("some"), "attr");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::point_feature_builder feature{layer_points};
|
||||||
|
feature.set_id(4);
|
||||||
|
feature.add_point(20, 20);
|
||||||
|
feature.add_property(idx("some"), "otherattr");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vtzero::point_feature_builder feature1{layer_points};
|
||||||
|
feature1.set_id(5);
|
||||||
|
feature1.add_point(vtzero::point{20, 20});
|
||||||
|
feature1.add_property("otherkey", "attr");
|
||||||
|
feature1.commit();
|
||||||
|
|
||||||
|
vtzero::value_index<vtzero::sint_value_type, int32_t, std::unordered_map> maxspeed_index{layer_lines};
|
||||||
|
{
|
||||||
|
vtzero::linestring_feature_builder feature{layer_lines};
|
||||||
|
feature.set_id(6);
|
||||||
|
feature.add_linestring(3);
|
||||||
|
feature.set_point(10, 10);
|
||||||
|
feature.set_point(10, 20);
|
||||||
|
feature.set_point(vtzero::point{20, 20});
|
||||||
|
std::vector<vtzero::point> points = {{11, 11}, {12, 13}};
|
||||||
|
feature.add_linestring_from_container(points);
|
||||||
|
feature.add_property("highway", "primary");
|
||||||
|
feature.add_property(std::string{"maxspeed"}, maxspeed_index(50));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::polygon_feature_builder feature{layer_polygons};
|
||||||
|
feature.set_id(7);
|
||||||
|
feature.add_ring(5);
|
||||||
|
feature.set_point(0, 0);
|
||||||
|
feature.set_point(10, 0);
|
||||||
|
feature.set_point(10, 10);
|
||||||
|
feature.set_point(0, 10);
|
||||||
|
feature.set_point(0, 0);
|
||||||
|
feature.add_ring(4);
|
||||||
|
feature.set_point(3, 3);
|
||||||
|
feature.set_point(3, 5);
|
||||||
|
feature.set_point(5, 5);
|
||||||
|
feature.close_ring();
|
||||||
|
feature.add_property("natural", "wood");
|
||||||
|
feature.add_property("number_of_trees", vtzero::sint_value_type{23402752});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto data = tile.serialize();
|
||||||
|
write_data_to_file(data, "test.mvt");
|
||||||
|
}
|
||||||
|
|
142
examples/vtzero-encode-geom.cpp
Normal file
142
examples/vtzero-encode-geom.cpp
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
Example program for vtzero library.
|
||||||
|
|
||||||
|
vtzero-encode-geom - Encode geometry on command line
|
||||||
|
|
||||||
|
This can be used for debugging. Uses internals of vtzero!
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <vtzero/geometry.hpp>
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
int64_t get_int(const char* arg) {
|
||||||
|
char* endptr = nullptr;
|
||||||
|
const int64_t value = std::strtoll(arg, &endptr, 10);
|
||||||
|
|
||||||
|
if (*endptr == '\0') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error{"not a valid number"};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t move_to(const char* arg) {
|
||||||
|
if (!std::isdigit(arg[0])) {
|
||||||
|
throw std::runtime_error{"need count after M command"};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto scount = get_int(arg);
|
||||||
|
if (scount <= 0) {
|
||||||
|
throw std::runtime_error{"count after M command must be 1 or larger"};
|
||||||
|
}
|
||||||
|
const auto count = static_cast<uint32_t>(scount);
|
||||||
|
std::cout << "MOVE_TO(" << count << ")\t" << vtzero::detail::command_move_to(count) << '\n';
|
||||||
|
|
||||||
|
return vtzero::detail::command_move_to(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t line_to(const char* arg) {
|
||||||
|
if (!std::isdigit(arg[0])) {
|
||||||
|
throw std::runtime_error{"need count after L command"};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto scount = get_int(arg);
|
||||||
|
if (scount <= 0) {
|
||||||
|
throw std::runtime_error{"count after L command must be 1 or larger"};
|
||||||
|
}
|
||||||
|
const auto count = static_cast<uint32_t>(scount);
|
||||||
|
std::cout << "LINE_TO(" << count << ")\t" << vtzero::detail::command_line_to(count) << '\n';
|
||||||
|
|
||||||
|
return vtzero::detail::command_line_to(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t close_path(const char* arg) {
|
||||||
|
if (arg[0] != '\0') {
|
||||||
|
throw std::runtime_error{"extra data after C command"};
|
||||||
|
}
|
||||||
|
std::cout << "CLOSE_PATH\t" << vtzero::detail::command_close_path() << '\n';
|
||||||
|
|
||||||
|
return vtzero::detail::command_close_path();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t number(const char* arg) {
|
||||||
|
const auto num = static_cast<int32_t>(get_int(arg));
|
||||||
|
std::cout << "number(" << num << ")\t" << protozero::encode_zigzag32(num) << '\n';
|
||||||
|
|
||||||
|
return protozero::encode_zigzag32(num);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc < 2) {
|
||||||
|
std::cerr << "Usage: " << argv[0] << " GEOMETRY ELEMENTS...\n"
|
||||||
|
<< "GEOMETRY ELEMENTS are:\n"
|
||||||
|
<< " M[count] -- MOVE_TO count\n"
|
||||||
|
<< " L[count] -- LINE_TO count\n"
|
||||||
|
<< " C -- CLOSE_PATH\n"
|
||||||
|
<< " [number] -- number that will be zigzag encoded\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> values;
|
||||||
|
|
||||||
|
std::cout << "raw data\tencoded\n-----------------------------------\n";
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
try {
|
||||||
|
switch (argv[i][0]) {
|
||||||
|
case '\0':
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
values.push_back(move_to(argv[i] + 1));
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
values.push_back(line_to(argv[i] + 1));
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
values.push_back(close_path(argv[i] + 1));
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
case '0':
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
values.push_back(number(argv[i]));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error{std::string{"unknown data: "} + argv[i]};
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
} catch (const std::runtime_error& e) {
|
||||||
|
std::cerr << "error(" << i << "): " << e.what() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string out{"["};
|
||||||
|
|
||||||
|
for (auto value : values) {
|
||||||
|
out += ' ';
|
||||||
|
out += std::to_string(value);
|
||||||
|
out += ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
out.back() = ' ';
|
||||||
|
|
||||||
|
std::cout << '\n' << out << "]\n";
|
||||||
|
}
|
||||||
|
|
101
examples/vtzero-filter.cpp
Normal file
101
examples/vtzero-filter.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
Example program for vtzero library.
|
||||||
|
|
||||||
|
vtzero-filter - Copy parts of a vector tile into a new tile.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <vtzero/builder.hpp>
|
||||||
|
#include <vtzero/vector_tile.hpp>
|
||||||
|
|
||||||
|
#include <clara.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
std::string filename;
|
||||||
|
std::string layer_num_or_name;
|
||||||
|
std::string idstr;
|
||||||
|
std::string output_file{"filtered.mvt"};
|
||||||
|
|
||||||
|
bool help = false;
|
||||||
|
|
||||||
|
const auto cli
|
||||||
|
= clara::Opt(output_file, "FILE")
|
||||||
|
["-o"]["--output"]
|
||||||
|
("write output to FILE")
|
||||||
|
| clara::Help(help)
|
||||||
|
| clara::Arg(filename, "FILENAME").required()
|
||||||
|
("vector tile")
|
||||||
|
| clara::Arg(layer_num_or_name, "LAYER-NUM|LAYER-NAME").required()
|
||||||
|
("layer")
|
||||||
|
| clara::Arg(idstr, "ID")
|
||||||
|
("feature_id");
|
||||||
|
|
||||||
|
const auto result = cli.parse(clara::Args(argc, argv));
|
||||||
|
if (!result) {
|
||||||
|
std::cerr << "Error in command line: " << result.errorMessage() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (help) {
|
||||||
|
std::cout << cli
|
||||||
|
<< "\nFilter contents of vector tile.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename.empty()) {
|
||||||
|
std::cerr << "Error in command line: Missing file name of vector tile to read\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layer_num_or_name.empty()) {
|
||||||
|
std::cerr << "Error in command line: Missing layer number or name\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto data = read_file(filename);
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = get_layer(tile, layer_num_or_name);
|
||||||
|
std::cerr << "Found layer: " << std::string(layer.name()) << "\n";
|
||||||
|
|
||||||
|
vtzero::tile_builder tb;
|
||||||
|
|
||||||
|
if (idstr.empty()) {
|
||||||
|
tb.add_existing_layer(layer);
|
||||||
|
} else {
|
||||||
|
char* str_end = nullptr;
|
||||||
|
const int64_t id = std::strtoll(idstr.c_str(), &str_end, 10);
|
||||||
|
if (str_end != idstr.c_str() + idstr.size()) {
|
||||||
|
std::cerr << "Feature ID must be numeric.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (id < 0) {
|
||||||
|
std::cerr << "Feature ID must be >= 0.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto feature = layer.get_feature_by_id(static_cast<uint64_t>(id));
|
||||||
|
if (!feature.valid()) {
|
||||||
|
std::cerr << "No feature with that id: " << id << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vtzero::layer_builder layer_builder{tb, layer};
|
||||||
|
layer_builder.add_feature(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string output = tb.serialize();
|
||||||
|
|
||||||
|
write_data_to_file(output, output_file);
|
||||||
|
}
|
||||||
|
|
242
examples/vtzero-show.cpp
Normal file
242
examples/vtzero-show.cpp
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
Example program for vtzero library.
|
||||||
|
|
||||||
|
vtzero-show - Show content of vector tile
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <vtzero/vector_tile.hpp>
|
||||||
|
|
||||||
|
#include <clara.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <exception>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class geom_handler {
|
||||||
|
|
||||||
|
std::string output{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void points_begin(const uint32_t /*count*/) const noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
void points_point(const vtzero::point point) const {
|
||||||
|
std::cout << " POINT(" << point.x << ',' << point.y << ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void points_end() const noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_begin(const uint32_t count) {
|
||||||
|
output = " LINESTRING[count=";
|
||||||
|
output += std::to_string(count);
|
||||||
|
output += "](";
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_point(const vtzero::point point) {
|
||||||
|
output += std::to_string(point.x);
|
||||||
|
output += ' ';
|
||||||
|
output += std::to_string(point.y);
|
||||||
|
output += ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_end() {
|
||||||
|
if (output.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (output.back() == ',') {
|
||||||
|
output.resize(output.size() - 1);
|
||||||
|
}
|
||||||
|
output += ")\n";
|
||||||
|
std::cout << output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_begin(const uint32_t count) {
|
||||||
|
output = " RING[count=";
|
||||||
|
output += std::to_string(count);
|
||||||
|
output += "](";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_point(const vtzero::point point) {
|
||||||
|
output += std::to_string(point.x);
|
||||||
|
output += ' ';
|
||||||
|
output += std::to_string(point.y);
|
||||||
|
output += ',';
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_end(const vtzero::ring_type rt) {
|
||||||
|
if (output.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (output.back() == ',') {
|
||||||
|
output.back() = ')';
|
||||||
|
}
|
||||||
|
switch (rt) {
|
||||||
|
case vtzero::ring_type::outer:
|
||||||
|
output += "[OUTER]\n";
|
||||||
|
break;
|
||||||
|
case vtzero::ring_type::inner:
|
||||||
|
output += "[INNER]\n";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
output += "[INVALID]\n";
|
||||||
|
}
|
||||||
|
std::cout << output;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class geom_handler
|
||||||
|
|
||||||
|
template <typename TChar, typename TTraits>
|
||||||
|
std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, vtzero::data_view value) {
|
||||||
|
out.write(value.data(), static_cast<std::streamsize>(value.size()));
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct print_value {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void operator()(T value) const {
|
||||||
|
std::cout << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(const vtzero::data_view value) const {
|
||||||
|
std::cout << '"' << value << '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct print_value
|
||||||
|
|
||||||
|
static void print_layer(vtzero::layer& layer, bool print_tables, bool print_value_types, int& layer_num, int& feature_num) {
|
||||||
|
std::cout << "=============================================================\n"
|
||||||
|
<< "layer: " << layer_num << '\n'
|
||||||
|
<< " name: " << std::string(layer.name()) << '\n'
|
||||||
|
<< " version: " << layer.version() << '\n'
|
||||||
|
<< " extent: " << layer.extent() << '\n';
|
||||||
|
|
||||||
|
if (print_tables) {
|
||||||
|
std::cout << " keys:\n";
|
||||||
|
int n = 0;
|
||||||
|
for (const auto& key : layer.key_table()) {
|
||||||
|
std::cout << " " << n++ << ": " << key << '\n';
|
||||||
|
}
|
||||||
|
std::cout << " values:\n";
|
||||||
|
n = 0;
|
||||||
|
for (const vtzero::property_value& value : layer.value_table()) {
|
||||||
|
std::cout << " " << n++ << ": ";
|
||||||
|
vtzero::apply_visitor(print_value{}, value);
|
||||||
|
if (print_value_types) {
|
||||||
|
std::cout << " [" << vtzero::property_value_type_name(value.type()) << "]\n";
|
||||||
|
} else {
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
feature_num = 0;
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
std::cout << " feature: " << feature_num << '\n'
|
||||||
|
<< " id: ";
|
||||||
|
if (feature.has_id()) {
|
||||||
|
std::cout << feature.id() << '\n';
|
||||||
|
} else {
|
||||||
|
std::cout << "(none)\n";
|
||||||
|
}
|
||||||
|
std::cout << " geomtype: " << vtzero::geom_type_name(feature.geometry_type()) << '\n'
|
||||||
|
<< " geometry:\n";
|
||||||
|
vtzero::decode_geometry(feature.geometry(), geom_handler{});
|
||||||
|
std::cout << " properties:\n";
|
||||||
|
while (auto property = feature.next_property()) {
|
||||||
|
std::cout << " " << property.key() << '=';
|
||||||
|
vtzero::apply_visitor(print_value{}, property.value());
|
||||||
|
if (print_value_types) {
|
||||||
|
std::cout << " [" << vtzero::property_value_type_name(property.value().type()) << "]\n";
|
||||||
|
} else {
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++feature_num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_layer_overview(const vtzero::layer& layer) {
|
||||||
|
std::cout << layer.name() << ' ' << layer.num_features() << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
std::string filename;
|
||||||
|
std::string layer_num_or_name;
|
||||||
|
bool layer_overview = false;
|
||||||
|
bool print_tables = false;
|
||||||
|
bool print_value_types = false;
|
||||||
|
bool help = false;
|
||||||
|
|
||||||
|
const auto cli
|
||||||
|
= clara::Opt(layer_overview)
|
||||||
|
["-l"]["--layers"]
|
||||||
|
("show layer overview with feature count")
|
||||||
|
| clara::Opt(print_tables)
|
||||||
|
["-t"]["--tables"]
|
||||||
|
("also print key/value tables")
|
||||||
|
| clara::Opt(print_value_types)
|
||||||
|
["-T"]["--value-types"]
|
||||||
|
("also show value types")
|
||||||
|
| clara::Help(help)
|
||||||
|
| clara::Arg(filename, "FILENAME").required()
|
||||||
|
("vector tile")
|
||||||
|
| clara::Arg(layer_num_or_name, "LAYER-NUM|LAYER-NAME")
|
||||||
|
("layer");
|
||||||
|
|
||||||
|
const auto result = cli.parse(clara::Args(argc, argv));
|
||||||
|
if (!result) {
|
||||||
|
std::cerr << "Error in command line: " << result.errorMessage() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (help) {
|
||||||
|
std::cout << cli
|
||||||
|
<< "\nShow contents of vector tile FILENAME.\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename.empty()) {
|
||||||
|
std::cerr << "Error in command line: Missing file name of vector tile to read\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int layer_num = 0;
|
||||||
|
int feature_num = 0;
|
||||||
|
try {
|
||||||
|
const auto data = read_file(filename);
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
if (layer_num_or_name.empty()) {
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
if (layer_overview) {
|
||||||
|
print_layer_overview(layer);
|
||||||
|
} else {
|
||||||
|
print_layer(layer, print_tables, print_value_types, layer_num, feature_num);
|
||||||
|
}
|
||||||
|
++layer_num;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto layer = get_layer(tile, layer_num_or_name);
|
||||||
|
if (layer_overview) {
|
||||||
|
print_layer_overview(layer);
|
||||||
|
} else {
|
||||||
|
print_layer(layer, print_tables, print_value_types, layer_num, feature_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Error in layer " << layer_num << " (feature " << feature_num << "): " << e.what() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
36
examples/vtzero-stats.cpp
Normal file
36
examples/vtzero-stats.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
Example program for vtzero library.
|
||||||
|
|
||||||
|
vtzero-stats - Output some stats on layers
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <vtzero/vector_tile.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cerr << "Usage: " << argv[0] << " TILE\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string input_file{argv[1]};
|
||||||
|
const auto data = read_file(input_file);
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
while (const auto layer = tile.next_layer()) {
|
||||||
|
std::cout.write(layer.name().data(), static_cast<std::streamsize>(layer.name().size()));
|
||||||
|
std::cout << ' '
|
||||||
|
<< layer.num_features() << ' '
|
||||||
|
<< layer.key_table().size() << ' '
|
||||||
|
<< layer.value_table().size() << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
79
examples/vtzero-streets.cpp
Normal file
79
examples/vtzero-streets.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
Example program for vtzero library.
|
||||||
|
|
||||||
|
vtzero-streets - Copy features from road_label layer if they have property
|
||||||
|
class="street". Output is always in file "streets.mvt".
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
#include <vtzero/builder.hpp>
|
||||||
|
#include <vtzero/property_mapper.hpp>
|
||||||
|
#include <vtzero/vector_tile.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
static bool keep_feature(const vtzero::feature& feature) {
|
||||||
|
static const std::string key{"class"};
|
||||||
|
static const std::string val{"street"};
|
||||||
|
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
feature.for_each_property([&](const vtzero::property& prop) {
|
||||||
|
found = key == prop.key() && val == prop.value().string_value();
|
||||||
|
return !found;
|
||||||
|
});
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cerr << "Usage: " << argv[0] << " TILE\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string input_file{argv[1]};
|
||||||
|
std::string output_file{"streets.mvt"};
|
||||||
|
|
||||||
|
const auto data = read_file(input_file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = get_layer(tile, "road_label");
|
||||||
|
if (!layer) {
|
||||||
|
std::cerr << "No 'road_label' layer found\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
vtzero::tile_builder tb;
|
||||||
|
vtzero::layer_builder layer_builder{tb, layer};
|
||||||
|
|
||||||
|
vtzero::property_mapper mapper{layer, layer_builder};
|
||||||
|
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
if (keep_feature(feature)) {
|
||||||
|
vtzero::geometry_feature_builder feature_builder{layer_builder};
|
||||||
|
if (feature.has_id()) {
|
||||||
|
feature_builder.set_id(feature.id());
|
||||||
|
}
|
||||||
|
feature_builder.set_geometry(feature.geometry());
|
||||||
|
|
||||||
|
while (auto idxs = feature.next_property_indexes()) {
|
||||||
|
feature_builder.add_property(mapper(idxs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string output = tb.serialize();
|
||||||
|
write_data_to_file(output, output_file);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Error: " << e.what() << '\n';
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1239
include-external/clara.hpp
Normal file
1239
include-external/clara.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1256
include/vtzero/builder.hpp
Normal file
1256
include/vtzero/builder.hpp
Normal file
File diff suppressed because it is too large
Load Diff
274
include/vtzero/builder_impl.hpp
Normal file
274
include/vtzero/builder_impl.hpp
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
#ifndef VTZERO_BUILDER_IMPL_HPP
|
||||||
|
#define VTZERO_BUILDER_IMPL_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file builder_impl.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains classes internal to the builder.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "encoded_property_value.hpp"
|
||||||
|
#include "property_value.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
#include <protozero/pbf_builder.hpp>
|
||||||
|
#include <protozero/pbf_message.hpp>
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class layer_builder_base {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
layer_builder_base() noexcept = default;
|
||||||
|
|
||||||
|
virtual ~layer_builder_base() noexcept = default;
|
||||||
|
|
||||||
|
layer_builder_base(const layer_builder_base&) noexcept = default;
|
||||||
|
layer_builder_base& operator=(const layer_builder_base&) noexcept = default;
|
||||||
|
|
||||||
|
layer_builder_base(layer_builder_base&&) noexcept = default;
|
||||||
|
layer_builder_base& operator=(layer_builder_base&&) noexcept = default;
|
||||||
|
|
||||||
|
virtual std::size_t estimated_size() const = 0;
|
||||||
|
|
||||||
|
virtual void build(protozero::pbf_builder<detail::pbf_tile>& pbf_tile_builder) const = 0;
|
||||||
|
|
||||||
|
}; // class layer_builder_base
|
||||||
|
|
||||||
|
class layer_builder_impl : public layer_builder_base {
|
||||||
|
|
||||||
|
// Buffer containing the encoded layer metadata and features
|
||||||
|
std::string m_data;
|
||||||
|
|
||||||
|
// Buffer containing the encoded keys table
|
||||||
|
std::string m_keys_data;
|
||||||
|
|
||||||
|
// Buffer containing the encoded values table
|
||||||
|
std::string m_values_data;
|
||||||
|
|
||||||
|
protozero::pbf_builder<detail::pbf_layer> m_pbf_message_layer;
|
||||||
|
protozero::pbf_builder<detail::pbf_layer> m_pbf_message_keys;
|
||||||
|
protozero::pbf_builder<detail::pbf_layer> m_pbf_message_values;
|
||||||
|
|
||||||
|
// The number of features in the layer
|
||||||
|
std::size_t m_num_features = 0;
|
||||||
|
|
||||||
|
// The number of keys in the keys table
|
||||||
|
uint32_t m_num_keys = 0;
|
||||||
|
|
||||||
|
// The number of values in the values table
|
||||||
|
uint32_t m_num_values = 0;
|
||||||
|
|
||||||
|
// Below this value, no index will be used to find entries in the
|
||||||
|
// key/value tables. This number is based on some initial
|
||||||
|
// benchmarking but probably needs some tuning.
|
||||||
|
// See also https://github.com/mapbox/vtzero/issues/30
|
||||||
|
static constexpr const uint32_t max_entries_flat = 20;
|
||||||
|
|
||||||
|
using map_type = std::unordered_map<std::string, index_value>;
|
||||||
|
map_type m_keys_index;
|
||||||
|
map_type m_values_index;
|
||||||
|
|
||||||
|
static index_value find_in_table(const data_view text, const std::string& data) {
|
||||||
|
uint32_t index = 0;
|
||||||
|
protozero::pbf_message<detail::pbf_layer> pbf_table{data};
|
||||||
|
|
||||||
|
while (pbf_table.next()) {
|
||||||
|
const auto v = pbf_table.get_view();
|
||||||
|
if (v == text) {
|
||||||
|
return index_value{index};
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return index_value{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the key or value table and populate an index from its
|
||||||
|
// entries. This is done once the table becomes too large to do
|
||||||
|
// linear search in it.
|
||||||
|
static void populate_index(const std::string& data, map_type& map) {
|
||||||
|
uint32_t index = 0;
|
||||||
|
protozero::pbf_message<detail::pbf_layer> pbf_table{data};
|
||||||
|
|
||||||
|
while (pbf_table.next()) {
|
||||||
|
map[pbf_table.get_string()] = index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index_value add_value_without_dup_check(const data_view text) {
|
||||||
|
m_pbf_message_values.add_string(detail::pbf_layer::values, text);
|
||||||
|
return m_num_values++;
|
||||||
|
}
|
||||||
|
|
||||||
|
index_value add_value(const data_view text) {
|
||||||
|
const auto index = find_in_values_table(text);
|
||||||
|
if (index.valid()) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
return add_value_without_dup_check(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
index_value find_in_keys_table(const data_view text) {
|
||||||
|
if (m_num_keys < max_entries_flat) {
|
||||||
|
return find_in_table(text, m_keys_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_keys_index.empty()) {
|
||||||
|
populate_index(m_keys_data, m_keys_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& v = m_keys_index[std::string(text)];
|
||||||
|
if (!v.valid()) {
|
||||||
|
v = add_key_without_dup_check(text);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
index_value find_in_values_table(const data_view text) {
|
||||||
|
if (m_num_values < max_entries_flat) {
|
||||||
|
return find_in_table(text, m_values_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_values_index.empty()) {
|
||||||
|
populate_index(m_values_data, m_values_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& v = m_values_index[std::string(text)];
|
||||||
|
if (!v.valid()) {
|
||||||
|
v = add_value_without_dup_check(text);
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
template <typename TString>
|
||||||
|
layer_builder_impl(TString&& name, uint32_t version, uint32_t extent) :
|
||||||
|
m_pbf_message_layer(m_data),
|
||||||
|
m_pbf_message_keys(m_keys_data),
|
||||||
|
m_pbf_message_values(m_values_data) {
|
||||||
|
m_pbf_message_layer.add_uint32(detail::pbf_layer::version, version);
|
||||||
|
m_pbf_message_layer.add_string(detail::pbf_layer::name, std::forward<TString>(name));
|
||||||
|
m_pbf_message_layer.add_uint32(detail::pbf_layer::extent, extent);
|
||||||
|
}
|
||||||
|
|
||||||
|
~layer_builder_impl() noexcept override = default;
|
||||||
|
|
||||||
|
layer_builder_impl(const layer_builder_impl&) = delete;
|
||||||
|
layer_builder_impl& operator=(const layer_builder_impl&) = delete;
|
||||||
|
|
||||||
|
layer_builder_impl(layer_builder_impl&&) = default;
|
||||||
|
layer_builder_impl& operator=(layer_builder_impl&&) = default;
|
||||||
|
|
||||||
|
index_value add_key_without_dup_check(const data_view text) {
|
||||||
|
m_pbf_message_keys.add_string(detail::pbf_layer::keys, text);
|
||||||
|
return m_num_keys++;
|
||||||
|
}
|
||||||
|
|
||||||
|
index_value add_key(const data_view text) {
|
||||||
|
const auto index = find_in_keys_table(text);
|
||||||
|
if (index.valid()) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
return add_key_without_dup_check(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
index_value add_value_without_dup_check(const property_value value) {
|
||||||
|
return add_value_without_dup_check(value.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
index_value add_value_without_dup_check(const encoded_property_value& value) {
|
||||||
|
return add_value_without_dup_check(value.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
index_value add_value(const property_value value) {
|
||||||
|
return add_value(value.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
index_value add_value(const encoded_property_value& value) {
|
||||||
|
return add_value(value.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& data() const noexcept {
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& keys_data() const noexcept {
|
||||||
|
return m_keys_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& values_data() const noexcept {
|
||||||
|
return m_values_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
protozero::pbf_builder<detail::pbf_layer>& message() noexcept {
|
||||||
|
return m_pbf_message_layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void increment_feature_count() noexcept {
|
||||||
|
++m_num_features;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t estimated_size() const override {
|
||||||
|
constexpr const std::size_t estimated_overhead_for_pbf_encoding = 8;
|
||||||
|
return data().size() +
|
||||||
|
keys_data().size() +
|
||||||
|
values_data().size() +
|
||||||
|
estimated_overhead_for_pbf_encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void build(protozero::pbf_builder<detail::pbf_tile>& pbf_tile_builder) const override {
|
||||||
|
if (m_num_features > 0) {
|
||||||
|
pbf_tile_builder.add_bytes_vectored(detail::pbf_tile::layers,
|
||||||
|
data(),
|
||||||
|
keys_data(),
|
||||||
|
values_data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class layer_builder_impl
|
||||||
|
|
||||||
|
class layer_builder_existing : public layer_builder_base {
|
||||||
|
|
||||||
|
data_view m_data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit layer_builder_existing(const data_view data) :
|
||||||
|
m_data(data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t estimated_size() const override {
|
||||||
|
constexpr const std::size_t estimated_overhead_for_pbf_encoding = 8;
|
||||||
|
return m_data.size() + estimated_overhead_for_pbf_encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
void build(protozero::pbf_builder<detail::pbf_tile>& pbf_tile_builder) const override {
|
||||||
|
pbf_tile_builder.add_bytes(detail::pbf_tile::layers, m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class layer_builder_existing
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_BUILDER_IMPL_HPP
|
244
include/vtzero/encoded_property_value.hpp
Normal file
244
include/vtzero/encoded_property_value.hpp
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
#ifndef VTZERO_ENCODED_PROPERTY_VALUE_HPP
|
||||||
|
#define VTZERO_ENCODED_PROPERTY_VALUE_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file encoded_property_value.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains the encoded_property_value class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
#include <protozero/pbf_builder.hpp>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A property value encoded in the vector_tile internal format. Can be
|
||||||
|
* created from values of many different types and then later added to
|
||||||
|
* a layer/feature.
|
||||||
|
*/
|
||||||
|
class encoded_property_value {
|
||||||
|
|
||||||
|
std::string m_data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Construct from vtzero::string_value_type.
|
||||||
|
explicit encoded_property_value(string_value_type value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_string(detail::pbf_value::string_value, value.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from const char*.
|
||||||
|
explicit encoded_property_value(const char* value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_string(detail::pbf_value::string_value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from const char* and size_t.
|
||||||
|
explicit encoded_property_value(const char* value, std::size_t size) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_string(detail::pbf_value::string_value, value, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from std::string.
|
||||||
|
explicit encoded_property_value(const std::string& value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_string(detail::pbf_value::string_value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from vtzero::data_view.
|
||||||
|
explicit encoded_property_value(const data_view& value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_string(detail::pbf_value::string_value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
/// Construct from vtzero::float_value_type.
|
||||||
|
explicit encoded_property_value(float_value_type value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_float(detail::pbf_value::float_value, value.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from float.
|
||||||
|
explicit encoded_property_value(float value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_float(detail::pbf_value::float_value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
/// Construct from vtzero::double_value_type.
|
||||||
|
explicit encoded_property_value(double_value_type value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_double(detail::pbf_value::double_value, value.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from double.
|
||||||
|
explicit encoded_property_value(double value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_double(detail::pbf_value::double_value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
/// Construct from vtzero::int_value_type.
|
||||||
|
explicit encoded_property_value(int_value_type value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_int64(detail::pbf_value::int_value, value.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from int64_t.
|
||||||
|
explicit encoded_property_value(int64_t value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_int64(detail::pbf_value::int_value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from int32_t.
|
||||||
|
explicit encoded_property_value(int32_t value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_int64(detail::pbf_value::int_value, static_cast<int64_t>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from int16_t.
|
||||||
|
explicit encoded_property_value(int16_t value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_int64(detail::pbf_value::int_value, static_cast<int64_t>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
/// Construct from vtzero::uint_value_type.
|
||||||
|
explicit encoded_property_value(uint_value_type value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_uint64(detail::pbf_value::uint_value, value.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from uint64_t.
|
||||||
|
explicit encoded_property_value(uint64_t value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_uint64(detail::pbf_value::uint_value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from uint32_t.
|
||||||
|
explicit encoded_property_value(uint32_t value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_uint64(detail::pbf_value::uint_value, static_cast<uint64_t>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from uint16_t.
|
||||||
|
explicit encoded_property_value(uint16_t value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_uint64(detail::pbf_value::uint_value, static_cast<uint64_t>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
/// Construct from vtzero::sint_value_type.
|
||||||
|
explicit encoded_property_value(sint_value_type value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_sint64(detail::pbf_value::sint_value, value.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
/// Construct from vtzero::bool_value_type.
|
||||||
|
explicit encoded_property_value(bool_value_type value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_bool(detail::pbf_value::bool_value, value.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct from bool.
|
||||||
|
explicit encoded_property_value(bool value) {
|
||||||
|
protozero::pbf_builder<detail::pbf_value> pbf_message_value{m_data};
|
||||||
|
pbf_message_value.add_bool(detail::pbf_value::bool_value, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get view of the raw data stored inside.
|
||||||
|
*/
|
||||||
|
data_view data() const noexcept {
|
||||||
|
return {m_data.data(), m_data.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash function compatible with std::hash.
|
||||||
|
*/
|
||||||
|
std::size_t hash() const noexcept {
|
||||||
|
return std::hash<std::string>{}(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class encoded_property_value
|
||||||
|
|
||||||
|
/// Encoded property values are equal if they contain the same data.
|
||||||
|
inline bool operator==(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept {
|
||||||
|
return lhs.data() == rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encoded property values are unequal if they are not equal.
|
||||||
|
inline bool operator!=(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arbitrary ordering based on internal data.
|
||||||
|
inline bool operator<(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept {
|
||||||
|
return lhs.data() < rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arbitrary ordering based on internal data.
|
||||||
|
inline bool operator<=(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept {
|
||||||
|
return lhs.data() <= rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arbitrary ordering based on internal data.
|
||||||
|
inline bool operator>(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept {
|
||||||
|
return lhs.data() > rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Arbitrary ordering based on internal data.
|
||||||
|
inline bool operator>=(const encoded_property_value& lhs, const encoded_property_value& rhs) noexcept {
|
||||||
|
return lhs.data() >= rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specialization of std::hash for encoded_property_value.
|
||||||
|
*/
|
||||||
|
template <>
|
||||||
|
struct hash<vtzero::encoded_property_value> {
|
||||||
|
|
||||||
|
/// key vtzero::encoded_property_value
|
||||||
|
using argument_type = vtzero::encoded_property_value;
|
||||||
|
|
||||||
|
/// hash result: size_t
|
||||||
|
using result_type = std::size_t;
|
||||||
|
|
||||||
|
/// calculate the hash of the argument
|
||||||
|
std::size_t operator()(const vtzero::encoded_property_value& value) const noexcept {
|
||||||
|
return value.hash();
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct hash
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
#endif // VTZERO_ENCODED_PROPERTY_VALUE_HPP
|
134
include/vtzero/exception.hpp
Normal file
134
include/vtzero/exception.hpp
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
#ifndef VTZERO_EXCEPTION_HPP
|
||||||
|
#define VTZERO_EXCEPTION_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file exception.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains the exceptions used in the vtzero library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for all exceptions directly thrown by the vtzero library.
|
||||||
|
*/
|
||||||
|
class exception : public std::runtime_error {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
explicit exception(const char* message) :
|
||||||
|
std::runtime_error(message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
explicit exception(const std::string& message) :
|
||||||
|
std::runtime_error(message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class exception
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when vector tile encoding isn't valid according
|
||||||
|
* to the vector tile specification.
|
||||||
|
*/
|
||||||
|
class format_exception : public exception {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
explicit format_exception(const char* message) :
|
||||||
|
exception(message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
explicit format_exception(const std::string& message) :
|
||||||
|
exception(message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class format_exception
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when a geometry encoding isn't valid according
|
||||||
|
* to the vector tile specification.
|
||||||
|
*/
|
||||||
|
class geometry_exception : public format_exception {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
explicit geometry_exception(const char* message) :
|
||||||
|
format_exception(message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
explicit geometry_exception(const std::string& message) :
|
||||||
|
format_exception(message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class geometry_exception
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when a property value is accessed using the
|
||||||
|
* wrong type.
|
||||||
|
*/
|
||||||
|
class type_exception : public exception {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
explicit type_exception() :
|
||||||
|
exception("wrong property value type") {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class type_exception
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when an unknown version number is found in the
|
||||||
|
* layer.
|
||||||
|
*/
|
||||||
|
class version_exception : public exception {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
explicit version_exception(const uint32_t version) :
|
||||||
|
exception(std::string{"unknown vector tile version: "} +
|
||||||
|
std::to_string(version)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // version_exception
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown when an index into the key or value table
|
||||||
|
* in a layer is out of range. This can only happen if the tile data is
|
||||||
|
* invalid.
|
||||||
|
*/
|
||||||
|
class out_of_range_exception : public exception {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
explicit out_of_range_exception(const uint32_t index) :
|
||||||
|
exception(std::string{"index out of range: "} +
|
||||||
|
std::to_string(index)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // out_of_range_exception
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_EXCEPTION_HPP
|
302
include/vtzero/feature.hpp
Normal file
302
include/vtzero/feature.hpp
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
#ifndef VTZERO_FEATURE_HPP
|
||||||
|
#define VTZERO_FEATURE_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file feature.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains the feature class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "exception.hpp"
|
||||||
|
#include "property.hpp"
|
||||||
|
#include "property_value.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
#include <protozero/pbf_message.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
class layer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A feature according to spec 4.2.
|
||||||
|
*
|
||||||
|
* Note that a feature will internally contain a pointer to the layer it
|
||||||
|
* came from. The layer has to stay valid as long as the feature is used.
|
||||||
|
*/
|
||||||
|
class feature {
|
||||||
|
|
||||||
|
using uint32_it_range = protozero::iterator_range<protozero::pbf_reader::const_uint32_iterator>;
|
||||||
|
|
||||||
|
const layer* m_layer = nullptr;
|
||||||
|
uint64_t m_id = 0; // defaults to 0, see https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L32
|
||||||
|
uint32_it_range m_properties{};
|
||||||
|
protozero::pbf_reader::const_uint32_iterator m_property_iterator{};
|
||||||
|
std::size_t m_num_properties = 0;
|
||||||
|
data_view m_geometry{};
|
||||||
|
GeomType m_geometry_type = GeomType::UNKNOWN; // defaults to UNKNOWN, see https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L41
|
||||||
|
bool m_has_id = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an invalid feature object.
|
||||||
|
*/
|
||||||
|
feature() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a feature object.
|
||||||
|
*
|
||||||
|
* @throws format_exception if the layer data is ill-formed.
|
||||||
|
*/
|
||||||
|
feature(const layer* layer, const data_view data) :
|
||||||
|
m_layer(layer) {
|
||||||
|
vtzero_assert(layer);
|
||||||
|
vtzero_assert(data.data());
|
||||||
|
|
||||||
|
protozero::pbf_message<detail::pbf_feature> reader{data};
|
||||||
|
|
||||||
|
while (reader.next()) {
|
||||||
|
switch (reader.tag_and_type()) {
|
||||||
|
case protozero::tag_and_type(detail::pbf_feature::id, protozero::pbf_wire_type::varint):
|
||||||
|
m_id = reader.get_uint64();
|
||||||
|
m_has_id = true;
|
||||||
|
break;
|
||||||
|
case protozero::tag_and_type(detail::pbf_feature::tags, protozero::pbf_wire_type::length_delimited):
|
||||||
|
if (m_properties.begin() != protozero::pbf_reader::const_uint32_iterator{}) {
|
||||||
|
throw format_exception{"Feature has more than one tags field"};
|
||||||
|
}
|
||||||
|
m_properties = reader.get_packed_uint32();
|
||||||
|
m_property_iterator = m_properties.begin();
|
||||||
|
break;
|
||||||
|
case protozero::tag_and_type(detail::pbf_feature::type, protozero::pbf_wire_type::varint): {
|
||||||
|
const auto type = reader.get_enum();
|
||||||
|
// spec 4.3.4 "Geometry Types"
|
||||||
|
if (type < 0 || type > 3) {
|
||||||
|
throw format_exception{"Unknown geometry type (spec 4.3.4)"};
|
||||||
|
}
|
||||||
|
m_geometry_type = static_cast<GeomType>(type);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case protozero::tag_and_type(detail::pbf_feature::geometry, protozero::pbf_wire_type::length_delimited):
|
||||||
|
if (!m_geometry.empty()) {
|
||||||
|
throw format_exception{"Feature has more than one geometry field"};
|
||||||
|
}
|
||||||
|
m_geometry = reader.get_view();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reader.skip(); // ignore unknown fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec 4.2 "A feature MUST contain a geometry field."
|
||||||
|
if (m_geometry.empty()) {
|
||||||
|
throw format_exception{"Missing geometry field in feature (spec 4.2)"};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto size = m_properties.size();
|
||||||
|
if (size % 2 != 0) {
|
||||||
|
throw format_exception{"unpaired property key/value indexes (spec 4.4)"};
|
||||||
|
}
|
||||||
|
m_num_properties = size / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a valid feature? Valid features are those not created from
|
||||||
|
* the default constructor.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*/
|
||||||
|
bool valid() const noexcept {
|
||||||
|
return m_geometry.data() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a valid feature? Valid features are those not created from
|
||||||
|
* the default constructor.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*/
|
||||||
|
explicit operator bool() const noexcept {
|
||||||
|
return valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of this feature. According to the spec IDs should be unique
|
||||||
|
* in a layer if they are set (spec 4.2).
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* Always returns 0 for invalid features.
|
||||||
|
*/
|
||||||
|
uint64_t id() const noexcept {
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this feature have an ID?
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* Always returns false for invalid features.
|
||||||
|
*/
|
||||||
|
bool has_id() const noexcept {
|
||||||
|
return m_has_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The geometry type of this feature.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* Always returns GeomType::UNKNOWN for invalid features.
|
||||||
|
*/
|
||||||
|
GeomType geometry_type() const noexcept {
|
||||||
|
return m_geometry_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the geometry of this feature.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
vtzero::geometry geometry() const noexcept {
|
||||||
|
vtzero_assert_in_noexcept_function(valid());
|
||||||
|
return {m_geometry, m_geometry_type};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this feature doesn't have any properties.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* Always returns true for invalid features.
|
||||||
|
*/
|
||||||
|
bool empty() const noexcept {
|
||||||
|
return m_num_properties == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of properties in this feature.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* Always returns 0 for invalid features.
|
||||||
|
*/
|
||||||
|
std::size_t num_properties() const noexcept {
|
||||||
|
return m_num_properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next property in this feature.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* @returns The next property or the invalid property if there are no
|
||||||
|
* more properties.
|
||||||
|
* @throws format_exception if the feature data is ill-formed.
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
property next_property();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the indexes into the key/value table for the next property in
|
||||||
|
* this feature.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* @returns The next index_value_pair or an invalid index_value_pair
|
||||||
|
* if there are no more properties.
|
||||||
|
* @throws format_exception if the feature data is ill-formed.
|
||||||
|
* @throws out_of_range_exception if the key or value index is not
|
||||||
|
* within the range of indexes in the layer key/value table.
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
index_value_pair next_property_indexes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the property iterator. The next time next_property() or
|
||||||
|
* next_property_indexes() is called, it will begin from the first
|
||||||
|
* property again.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
void reset_property() noexcept {
|
||||||
|
vtzero_assert_in_noexcept_function(valid());
|
||||||
|
m_property_iterator = m_properties.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call a function for each property of this feature.
|
||||||
|
*
|
||||||
|
* @tparam TFunc The type of the function. It must take a single
|
||||||
|
* argument of type property&& and return a bool. If the
|
||||||
|
* function returns false, the iteration will be stopped.
|
||||||
|
* @param func The function to call.
|
||||||
|
* @returns true if the iteration was completed and false otherwise.
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
template <typename TFunc>
|
||||||
|
bool for_each_property(TFunc&& func) const;
|
||||||
|
|
||||||
|
}; // class feature
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create some kind of mapping from property keys to property values.
|
||||||
|
*
|
||||||
|
* This can be used to read all properties into a std::map or similar
|
||||||
|
* object.
|
||||||
|
*
|
||||||
|
* @tparam TMap Map type (std::map, std::unordered_map, ...) Must support
|
||||||
|
* the emplace() method.
|
||||||
|
* @tparam TKey Key type, usually the key of the map type. The data_view
|
||||||
|
* of the property key is converted to this type before
|
||||||
|
* adding it to the map.
|
||||||
|
* @tparam TValue Value type, usally the value of the map type. The
|
||||||
|
* property_value is converted to this type before
|
||||||
|
* adding it to the map.
|
||||||
|
* @tparam TMapping A struct derived from property_value_mapping with the
|
||||||
|
* mapping for vtzero property value types to TValue-constructing
|
||||||
|
* types. (See convert_property_value() for details.)
|
||||||
|
* @param feature The feature to get the properties from.
|
||||||
|
* @returns An object of type TMap with all the properties.
|
||||||
|
* @pre @code feature.valid() @endcode
|
||||||
|
*/
|
||||||
|
template <typename TMap,
|
||||||
|
typename TKey = typename TMap::key_type,
|
||||||
|
typename TValue = typename TMap::mapped_type,
|
||||||
|
typename TMapping = property_value_mapping>
|
||||||
|
TMap create_properties_map(const vtzero::feature& feature) {
|
||||||
|
TMap map;
|
||||||
|
|
||||||
|
feature.for_each_property([&map](const property& p) {
|
||||||
|
map.emplace(TKey(p.key()), convert_property_value<TValue, TMapping>(p.value()));
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_FEATURE_HPP
|
118
include/vtzero/feature_builder_impl.hpp
Normal file
118
include/vtzero/feature_builder_impl.hpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
#ifndef VTZERO_FEATURE_BUILDER_IMPL_HPP
|
||||||
|
#define VTZERO_FEATURE_BUILDER_IMPL_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file feature_builder_impl.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains classes internal to the builder.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "builder_impl.hpp"
|
||||||
|
#include "encoded_property_value.hpp"
|
||||||
|
#include "geometry.hpp"
|
||||||
|
#include "property.hpp"
|
||||||
|
#include "property_value.hpp"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class feature_builder_base {
|
||||||
|
|
||||||
|
layer_builder_impl* m_layer;
|
||||||
|
|
||||||
|
void add_key_internal(index_value idx) {
|
||||||
|
vtzero_assert(idx.valid());
|
||||||
|
m_pbf_tags.add_element(idx.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void add_key_internal(T&& key) {
|
||||||
|
add_key_internal(m_layer->add_key(data_view{std::forward<T>(key)}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_value_internal(index_value idx) {
|
||||||
|
vtzero_assert(idx.valid());
|
||||||
|
m_pbf_tags.add_element(idx.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_value_internal(property_value value) {
|
||||||
|
add_value_internal(m_layer->add_value(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void add_value_internal(T&& value) {
|
||||||
|
encoded_property_value v{std::forward<T>(value)};
|
||||||
|
add_value_internal(m_layer->add_value(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
protozero::pbf_builder<detail::pbf_feature> m_feature_writer;
|
||||||
|
protozero::packed_field_uint32 m_pbf_tags;
|
||||||
|
|
||||||
|
explicit feature_builder_base(layer_builder_impl* layer) :
|
||||||
|
m_layer(layer),
|
||||||
|
m_feature_writer(layer->message(), detail::pbf_layer::features) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~feature_builder_base() noexcept = default;
|
||||||
|
|
||||||
|
feature_builder_base(const feature_builder_base&) = delete; // NOLINT(hicpp-use-equals-delete, modernize-use-equals-delete)
|
||||||
|
|
||||||
|
feature_builder_base& operator=(const feature_builder_base&) = delete; // NOLINT(hicpp-use-equals-delete, modernize-use-equals-delete)
|
||||||
|
// The check wants these functions to be public...
|
||||||
|
|
||||||
|
feature_builder_base(feature_builder_base&&) noexcept = default;
|
||||||
|
|
||||||
|
feature_builder_base& operator=(feature_builder_base&&) noexcept = default;
|
||||||
|
|
||||||
|
void add_property_impl(const property& property) {
|
||||||
|
add_key_internal(property.key());
|
||||||
|
add_value_internal(property.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_property_impl(const index_value_pair idxs) {
|
||||||
|
add_key_internal(idxs.key());
|
||||||
|
add_value_internal(idxs.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TKey, typename TValue>
|
||||||
|
void add_property_impl(TKey&& key, TValue&& value) {
|
||||||
|
add_key_internal(std::forward<TKey>(key));
|
||||||
|
add_value_internal(std::forward<TValue>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_commit() {
|
||||||
|
if (m_pbf_tags.valid()) {
|
||||||
|
m_pbf_tags.commit();
|
||||||
|
}
|
||||||
|
m_feature_writer.commit();
|
||||||
|
m_layer->increment_feature_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_rollback() {
|
||||||
|
if (m_pbf_tags.valid()) {
|
||||||
|
m_pbf_tags.rollback();
|
||||||
|
}
|
||||||
|
m_feature_writer.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class feature_builder_base
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_FEATURE_BUILDER_IMPL_HPP
|
444
include/vtzero/geometry.hpp
Normal file
444
include/vtzero/geometry.hpp
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
#ifndef VTZERO_GEOMETRY_HPP
|
||||||
|
#define VTZERO_GEOMETRY_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file geometry.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains classes and functions related to geometry handling.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "exception.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
#include <protozero/pbf_reader.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/// A simple point class
|
||||||
|
struct point {
|
||||||
|
|
||||||
|
/// X coordinate
|
||||||
|
int32_t x = 0;
|
||||||
|
|
||||||
|
/// Y coordinate
|
||||||
|
int32_t y = 0;
|
||||||
|
|
||||||
|
/// Default construct to 0 coordinates
|
||||||
|
constexpr point() noexcept = default;
|
||||||
|
|
||||||
|
/// Constructor
|
||||||
|
constexpr point(int32_t x_, int32_t y_) noexcept :
|
||||||
|
x(x_),
|
||||||
|
y(y_) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct point
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type of a polygon ring. This can either be "outer", "inner", or
|
||||||
|
* "invalid". Invalid is used when the area of the ring is 0.
|
||||||
|
*/
|
||||||
|
enum class ring_type {
|
||||||
|
outer = 0,
|
||||||
|
inner = 1,
|
||||||
|
invalid = 2
|
||||||
|
}; // enum class ring_type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to create a point from any type that has members x
|
||||||
|
* and y.
|
||||||
|
*
|
||||||
|
* If your point type doesn't have members x any y, you can overload this
|
||||||
|
* function for your type and it will be used by vtzero.
|
||||||
|
*/
|
||||||
|
template <typename TPoint>
|
||||||
|
point create_vtzero_point(const TPoint& p) noexcept {
|
||||||
|
return {p.x, p.y};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Points are equal if their coordinates are
|
||||||
|
inline constexpr bool operator==(const point a, const point b) noexcept {
|
||||||
|
return a.x == b.x && a.y == b.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Points are not equal if their coordinates aren't
|
||||||
|
inline constexpr bool operator!=(const point a, const point b) noexcept {
|
||||||
|
return !(a == b);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
/// The command id type as specified in the vector tile spec
|
||||||
|
enum class CommandId : uint32_t {
|
||||||
|
MOVE_TO = 1,
|
||||||
|
LINE_TO = 2,
|
||||||
|
CLOSE_PATH = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr uint32_t command_integer(CommandId id, const uint32_t count) noexcept {
|
||||||
|
return (static_cast<uint32_t>(id) & 0x7u) | (count << 3u);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr uint32_t command_move_to(const uint32_t count) noexcept {
|
||||||
|
return command_integer(CommandId::MOVE_TO, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr uint32_t command_line_to(const uint32_t count) noexcept {
|
||||||
|
return command_integer(CommandId::LINE_TO, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr uint32_t command_close_path() noexcept {
|
||||||
|
return command_integer(CommandId::CLOSE_PATH, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr uint32_t get_command_id(const uint32_t command_integer) noexcept {
|
||||||
|
return command_integer & 0x7u;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr uint32_t get_command_count(const uint32_t command_integer) noexcept {
|
||||||
|
return command_integer >> 3u;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The maximum value for the command count according to the spec.
|
||||||
|
inline constexpr uint32_t max_command_count() noexcept {
|
||||||
|
return get_command_count(std::numeric_limits<uint32_t>::max());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr int64_t det(const point a, const point b) noexcept {
|
||||||
|
return static_cast<int64_t>(a.x) * static_cast<int64_t>(b.y) -
|
||||||
|
static_cast<int64_t>(b.x) * static_cast<int64_t>(a.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct get_result {
|
||||||
|
|
||||||
|
using type = void;
|
||||||
|
|
||||||
|
template <typename TGeomHandler>
|
||||||
|
void operator()(TGeomHandler&& /*geom_handler*/) const noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct get_result<T, typename std::enable_if<!std::is_same<decltype(std::declval<T>().result()), void>::value>::type> {
|
||||||
|
|
||||||
|
using type = decltype(std::declval<T>().result());
|
||||||
|
|
||||||
|
template <typename TGeomHandler>
|
||||||
|
type operator()(TGeomHandler&& geom_handler) {
|
||||||
|
return std::forward<TGeomHandler>(geom_handler).result();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a geometry as specified in spec 4.3 from a sequence of 32 bit
|
||||||
|
* unsigned integers. This templated base class can be instantiated
|
||||||
|
* with a different iterator type for testing than for normal use.
|
||||||
|
*/
|
||||||
|
template <typename TIterator>
|
||||||
|
class geometry_decoder {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
using iterator_type = TIterator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
iterator_type m_it;
|
||||||
|
iterator_type m_end;
|
||||||
|
|
||||||
|
point m_cursor{0, 0};
|
||||||
|
|
||||||
|
// maximum value for m_count before we throw an exception
|
||||||
|
uint32_t m_max_count;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current count value is set from the CommandInteger and
|
||||||
|
* then counted down with each next_point() call. So it must be
|
||||||
|
* greater than 0 when next_point() is called and 0 when
|
||||||
|
* next_command() is called.
|
||||||
|
*/
|
||||||
|
uint32_t m_count = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
geometry_decoder(iterator_type begin, iterator_type end, std::size_t max) :
|
||||||
|
m_it(begin),
|
||||||
|
m_end(end),
|
||||||
|
m_max_count(static_cast<uint32_t>(max)) {
|
||||||
|
vtzero_assert(max <= detail::max_command_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t count() const noexcept {
|
||||||
|
return m_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool done() const noexcept {
|
||||||
|
return m_it == m_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool next_command(const CommandId expected_command_id) {
|
||||||
|
vtzero_assert(m_count == 0);
|
||||||
|
|
||||||
|
if (m_it == m_end) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto command_id = get_command_id(*m_it);
|
||||||
|
if (command_id != static_cast<uint32_t>(expected_command_id)) {
|
||||||
|
throw geometry_exception{std::string{"expected command "} +
|
||||||
|
std::to_string(static_cast<uint32_t>(expected_command_id)) +
|
||||||
|
" but got " +
|
||||||
|
std::to_string(command_id)};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected_command_id == CommandId::CLOSE_PATH) {
|
||||||
|
// spec 4.3.3.3 "A ClosePath command MUST have a command count of 1"
|
||||||
|
if (get_command_count(*m_it) != 1) {
|
||||||
|
throw geometry_exception{"ClosePath command count is not 1"};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_count = get_command_count(*m_it);
|
||||||
|
if (m_count > m_max_count) {
|
||||||
|
throw geometry_exception{"count too large"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++m_it;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
point next_point() {
|
||||||
|
vtzero_assert(m_count > 0);
|
||||||
|
|
||||||
|
if (m_it == m_end || std::next(m_it) == m_end) {
|
||||||
|
throw geometry_exception{"too few points in geometry"};
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec 4.3.2 "A ParameterInteger is zigzag encoded"
|
||||||
|
int64_t x = protozero::decode_zigzag32(*m_it++);
|
||||||
|
int64_t y = protozero::decode_zigzag32(*m_it++);
|
||||||
|
|
||||||
|
// x and y are int64_t so this addition can never overflow
|
||||||
|
x += m_cursor.x;
|
||||||
|
y += m_cursor.y;
|
||||||
|
|
||||||
|
// The cast is okay, because a valid vector tile can never
|
||||||
|
// contain values that would overflow here and we don't care
|
||||||
|
// what happens to invalid tiles here.
|
||||||
|
m_cursor.x = static_cast<int32_t>(x);
|
||||||
|
m_cursor.y = static_cast<int32_t>(y);
|
||||||
|
|
||||||
|
--m_count;
|
||||||
|
|
||||||
|
return m_cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TGeomHandler>
|
||||||
|
typename detail::get_result<TGeomHandler>::type decode_point(TGeomHandler&& geom_handler) {
|
||||||
|
// spec 4.3.4.2 "MUST consist of a single MoveTo command"
|
||||||
|
if (!next_command(CommandId::MOVE_TO)) {
|
||||||
|
throw geometry_exception{"expected MoveTo command (spec 4.3.4.2)"};
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec 4.3.4.2 "command count greater than 0"
|
||||||
|
if (count() == 0) {
|
||||||
|
throw geometry_exception{"MoveTo command count is zero (spec 4.3.4.2)"};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::forward<TGeomHandler>(geom_handler).points_begin(count());
|
||||||
|
while (count() > 0) {
|
||||||
|
std::forward<TGeomHandler>(geom_handler).points_point(next_point());
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec 4.3.4.2 "MUST consist of of a single ... command"
|
||||||
|
if (!done()) {
|
||||||
|
throw geometry_exception{"additional data after end of geometry (spec 4.3.4.2)"};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::forward<TGeomHandler>(geom_handler).points_end();
|
||||||
|
|
||||||
|
return detail::get_result<TGeomHandler>{}(std::forward<TGeomHandler>(geom_handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TGeomHandler>
|
||||||
|
typename detail::get_result<TGeomHandler>::type decode_linestring(TGeomHandler&& geom_handler) {
|
||||||
|
// spec 4.3.4.3 "1. A MoveTo command"
|
||||||
|
while (next_command(CommandId::MOVE_TO)) {
|
||||||
|
// spec 4.3.4.3 "with a command count of 1"
|
||||||
|
if (count() != 1) {
|
||||||
|
throw geometry_exception{"MoveTo command count is not 1 (spec 4.3.4.3)"};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto first_point = next_point();
|
||||||
|
|
||||||
|
// spec 4.3.4.3 "2. A LineTo command"
|
||||||
|
if (!next_command(CommandId::LINE_TO)) {
|
||||||
|
throw geometry_exception{"expected LineTo command (spec 4.3.4.3)"};
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec 4.3.4.3 "with a command count greater than 0"
|
||||||
|
if (count() == 0) {
|
||||||
|
throw geometry_exception{"LineTo command count is zero (spec 4.3.4.3)"};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::forward<TGeomHandler>(geom_handler).linestring_begin(count() + 1);
|
||||||
|
|
||||||
|
std::forward<TGeomHandler>(geom_handler).linestring_point(first_point);
|
||||||
|
while (count() > 0) {
|
||||||
|
std::forward<TGeomHandler>(geom_handler).linestring_point(next_point());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::forward<TGeomHandler>(geom_handler).linestring_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
return detail::get_result<TGeomHandler>{}(std::forward<TGeomHandler>(geom_handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TGeomHandler>
|
||||||
|
typename detail::get_result<TGeomHandler>::type decode_polygon(TGeomHandler&& geom_handler) {
|
||||||
|
// spec 4.3.4.4 "1. A MoveTo command"
|
||||||
|
while (next_command(CommandId::MOVE_TO)) {
|
||||||
|
// spec 4.3.4.4 "with a command count of 1"
|
||||||
|
if (count() != 1) {
|
||||||
|
throw geometry_exception{"MoveTo command count is not 1 (spec 4.3.4.4)"};
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t sum = 0;
|
||||||
|
const point start_point = next_point();
|
||||||
|
point last_point = start_point;
|
||||||
|
|
||||||
|
// spec 4.3.4.4 "2. A LineTo command"
|
||||||
|
if (!next_command(CommandId::LINE_TO)) {
|
||||||
|
throw geometry_exception{"expected LineTo command (spec 4.3.4.4)"};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::forward<TGeomHandler>(geom_handler).ring_begin(count() + 2);
|
||||||
|
|
||||||
|
std::forward<TGeomHandler>(geom_handler).ring_point(start_point);
|
||||||
|
|
||||||
|
while (count() > 0) {
|
||||||
|
const point p = next_point();
|
||||||
|
sum += detail::det(last_point, p);
|
||||||
|
last_point = p;
|
||||||
|
std::forward<TGeomHandler>(geom_handler).ring_point(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
// spec 4.3.4.4 "3. A ClosePath command"
|
||||||
|
if (!next_command(CommandId::CLOSE_PATH)) {
|
||||||
|
throw geometry_exception{"expected ClosePath command (4.3.4.4)"};
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += detail::det(last_point, start_point);
|
||||||
|
|
||||||
|
std::forward<TGeomHandler>(geom_handler).ring_point(start_point);
|
||||||
|
|
||||||
|
std::forward<TGeomHandler>(geom_handler).ring_end(sum > 0 ? ring_type::outer :
|
||||||
|
sum < 0 ? ring_type::inner : ring_type::invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return detail::get_result<TGeomHandler>{}(std::forward<TGeomHandler>(geom_handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class geometry_decoder
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a point geometry.
|
||||||
|
*
|
||||||
|
* @tparam TGeomHandler Handler class. See tutorial for details.
|
||||||
|
* @param geometry The geometry as returned by feature.geometry().
|
||||||
|
* @param geom_handler An object of TGeomHandler.
|
||||||
|
* @throws geometry_error If there is a problem with the geometry.
|
||||||
|
* @pre Geometry must be a point geometry.
|
||||||
|
*/
|
||||||
|
template <typename TGeomHandler>
|
||||||
|
typename detail::get_result<TGeomHandler>::type decode_point_geometry(const geometry& geometry, TGeomHandler&& geom_handler) {
|
||||||
|
vtzero_assert(geometry.type() == GeomType::POINT);
|
||||||
|
detail::geometry_decoder<decltype(geometry.begin())> decoder{geometry.begin(), geometry.end(), geometry.data().size() / 2};
|
||||||
|
return decoder.decode_point(std::forward<TGeomHandler>(geom_handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a linestring geometry.
|
||||||
|
*
|
||||||
|
* @tparam TGeomHandler Handler class. See tutorial for details.
|
||||||
|
* @param geometry The geometry as returned by feature.geometry().
|
||||||
|
* @param geom_handler An object of TGeomHandler.
|
||||||
|
* @returns whatever geom_handler.result() returns if that function exists,
|
||||||
|
* void otherwise
|
||||||
|
* @throws geometry_error If there is a problem with the geometry.
|
||||||
|
* @pre Geometry must be a linestring geometry.
|
||||||
|
*/
|
||||||
|
template <typename TGeomHandler>
|
||||||
|
typename detail::get_result<TGeomHandler>::type decode_linestring_geometry(const geometry& geometry, TGeomHandler&& geom_handler) {
|
||||||
|
vtzero_assert(geometry.type() == GeomType::LINESTRING);
|
||||||
|
detail::geometry_decoder<decltype(geometry.begin())> decoder{geometry.begin(), geometry.end(), geometry.data().size() / 2};
|
||||||
|
return decoder.decode_linestring(std::forward<TGeomHandler>(geom_handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a polygon geometry.
|
||||||
|
*
|
||||||
|
* @tparam TGeomHandler Handler class. See tutorial for details.
|
||||||
|
* @param geometry The geometry as returned by feature.geometry().
|
||||||
|
* @param geom_handler An object of TGeomHandler.
|
||||||
|
* @returns whatever geom_handler.result() returns if that function exists,
|
||||||
|
* void otherwise
|
||||||
|
* @throws geometry_error If there is a problem with the geometry.
|
||||||
|
* @pre Geometry must be a polygon geometry.
|
||||||
|
*/
|
||||||
|
template <typename TGeomHandler>
|
||||||
|
typename detail::get_result<TGeomHandler>::type decode_polygon_geometry(const geometry& geometry, TGeomHandler&& geom_handler) {
|
||||||
|
vtzero_assert(geometry.type() == GeomType::POLYGON);
|
||||||
|
detail::geometry_decoder<decltype(geometry.begin())> decoder{geometry.begin(), geometry.end(), geometry.data().size() / 2};
|
||||||
|
return decoder.decode_polygon(std::forward<TGeomHandler>(geom_handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a geometry.
|
||||||
|
*
|
||||||
|
* @tparam TGeomHandler Handler class. See tutorial for details.
|
||||||
|
* @param geometry The geometry as returned by feature.geometry().
|
||||||
|
* @param geom_handler An object of TGeomHandler.
|
||||||
|
* @returns whatever geom_handler.result() returns if that function exists,
|
||||||
|
* void otherwise
|
||||||
|
* @throws geometry_error If the geometry has type UNKNOWN of if there is
|
||||||
|
* a problem with the geometry.
|
||||||
|
*/
|
||||||
|
template <typename TGeomHandler>
|
||||||
|
typename detail::get_result<TGeomHandler>::type decode_geometry(const geometry& geometry, TGeomHandler&& geom_handler) {
|
||||||
|
detail::geometry_decoder<decltype(geometry.begin())> decoder{geometry.begin(), geometry.end(), geometry.data().size() / 2};
|
||||||
|
switch (geometry.type()) {
|
||||||
|
case GeomType::POINT:
|
||||||
|
return decoder.decode_point(std::forward<TGeomHandler>(geom_handler));
|
||||||
|
case GeomType::LINESTRING:
|
||||||
|
return decoder.decode_linestring(std::forward<TGeomHandler>(geom_handler));
|
||||||
|
case GeomType::POLYGON:
|
||||||
|
return decoder.decode_polygon(std::forward<TGeomHandler>(geom_handler));
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw geometry_exception{"unknown geometry type"};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_GEOMETRY_HPP
|
264
include/vtzero/index.hpp
Normal file
264
include/vtzero/index.hpp
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
#ifndef VTZERO_INDEX_HPP
|
||||||
|
#define VTZERO_INDEX_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file index.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains classes for indexing the key/value tables inside layers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "builder.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to store the mapping between property keys and the index value
|
||||||
|
* in the table stored in a layer.
|
||||||
|
*
|
||||||
|
* @tparam TMap The map class to use (std::map, std::unordered_map or
|
||||||
|
* something compatible).
|
||||||
|
*/
|
||||||
|
template <template <typename...> class TMap>
|
||||||
|
class key_index {
|
||||||
|
|
||||||
|
layer_builder& m_builder;
|
||||||
|
|
||||||
|
TMap<std::string, index_value> m_index;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct index.
|
||||||
|
*
|
||||||
|
* @param builder The layer we are building containing the key table
|
||||||
|
* we are creating the index for.
|
||||||
|
*/
|
||||||
|
explicit key_index(layer_builder& builder) :
|
||||||
|
m_builder(builder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index value for the specified key. If the key was not in
|
||||||
|
* the table, it will be added.
|
||||||
|
*
|
||||||
|
* @param key The key to store.
|
||||||
|
* @returns The index value of they key.
|
||||||
|
*/
|
||||||
|
index_value operator()(const data_view key) {
|
||||||
|
std::string text(key);
|
||||||
|
const auto it = m_index.find(text);
|
||||||
|
if (it == m_index.end()) {
|
||||||
|
const auto idx = m_builder.add_key_without_dup_check(key);
|
||||||
|
m_index.emplace(std::move(text), idx);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class key_index
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to store the mapping between property values and the index value
|
||||||
|
* in the table stored in a layer. Stores the values in their original
|
||||||
|
* form (as type TExternal) which is more efficient than the way
|
||||||
|
* value_index_internal does it, but you need an instance of this class
|
||||||
|
* for each value type you use.
|
||||||
|
*
|
||||||
|
* @tparam TInternal The type used in the vector tile to encode the value.
|
||||||
|
* Must be one of string/float/double/int/uint/sint/bool_value_type.
|
||||||
|
* @tparam TExternal The type for the value used by the user of this class.
|
||||||
|
* @tparam TMap The map class to use (std::map, std::unordered_map or
|
||||||
|
* something compatible).
|
||||||
|
*/
|
||||||
|
template <typename TInternal, typename TExternal, template <typename...> class TMap>
|
||||||
|
class value_index {
|
||||||
|
|
||||||
|
layer_builder& m_builder;
|
||||||
|
|
||||||
|
TMap<TExternal, index_value> m_index;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct index.
|
||||||
|
*
|
||||||
|
* @param builder The layer we are building containing the value
|
||||||
|
* table we are creating the index for.
|
||||||
|
*/
|
||||||
|
explicit value_index(layer_builder& builder) :
|
||||||
|
m_builder(builder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index value for the specified value. If the value was not in
|
||||||
|
* the table, it will be added.
|
||||||
|
*
|
||||||
|
* @param value The value to store.
|
||||||
|
* @returns The index value of they value.
|
||||||
|
*/
|
||||||
|
index_value operator()(const TExternal& value) {
|
||||||
|
const auto it = m_index.find(value);
|
||||||
|
if (it == m_index.end()) {
|
||||||
|
const auto idx = m_builder.add_value_without_dup_check(encoded_property_value{TInternal{value}});
|
||||||
|
m_index.emplace(value, idx);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class value_index
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to store the mapping between bool property values and the index
|
||||||
|
* value in the table stored in a layer.
|
||||||
|
*
|
||||||
|
* This is the most efficient index if you know that your property values
|
||||||
|
* are bools.
|
||||||
|
*/
|
||||||
|
class value_index_bool {
|
||||||
|
|
||||||
|
layer_builder& m_builder;
|
||||||
|
|
||||||
|
std::array<index_value, 2> m_index;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct index.
|
||||||
|
*
|
||||||
|
* @param builder The layer we are building containing the value
|
||||||
|
* table we are creating the index for.
|
||||||
|
*/
|
||||||
|
explicit value_index_bool(layer_builder& builder) :
|
||||||
|
m_builder(builder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index value for the specified value. If the value was not in
|
||||||
|
* the table, it will be added.
|
||||||
|
*
|
||||||
|
* @param value The value to store.
|
||||||
|
* @returns The index value of they value.
|
||||||
|
*/
|
||||||
|
index_value operator()(const bool value) {
|
||||||
|
auto& idx = m_index[static_cast<std::size_t>(value)]; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
|
||||||
|
if (!idx.valid()) {
|
||||||
|
idx = m_builder.add_value_without_dup_check(encoded_property_value{value});
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class value_index_bool
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to store the mapping between small unsigned int property values and
|
||||||
|
* the index value in the table stored in a layer.
|
||||||
|
*
|
||||||
|
* This is the most efficient index if you know that your property values
|
||||||
|
* are densly used small unsigned integers. This is usually the case for
|
||||||
|
* enums.
|
||||||
|
*
|
||||||
|
* Interally a simple vector<index_value> is used and the value is used
|
||||||
|
* as is to look up the index value in the vector.
|
||||||
|
*/
|
||||||
|
class value_index_small_uint {
|
||||||
|
|
||||||
|
layer_builder& m_builder;
|
||||||
|
|
||||||
|
std::vector<index_value> m_index;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct index.
|
||||||
|
*
|
||||||
|
* @param builder The layer we are building containing the value
|
||||||
|
* table we are creating the index for.
|
||||||
|
*/
|
||||||
|
explicit value_index_small_uint(layer_builder& builder) :
|
||||||
|
m_builder(builder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index value for the specified value. If the value was not in
|
||||||
|
* the table, it will be added.
|
||||||
|
*
|
||||||
|
* @param value The value to store.
|
||||||
|
* @returns The index value of they value.
|
||||||
|
*/
|
||||||
|
index_value operator()(const uint16_t value) {
|
||||||
|
if (value >= m_index.size()) {
|
||||||
|
m_index.resize(value + 1);
|
||||||
|
}
|
||||||
|
if (!m_index[value].valid()) {
|
||||||
|
m_index[value] = m_builder.add_value_without_dup_check(encoded_property_value{value});
|
||||||
|
}
|
||||||
|
return m_index[value];
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class value_index_small_uint
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to store the mapping between property values and the index value
|
||||||
|
* in the table stored in a layer. Stores the values in the already
|
||||||
|
* encoded form. This is simpler to use than the value_index class, but
|
||||||
|
* has a higher overhead.
|
||||||
|
*
|
||||||
|
* @tparam TMap The map class to use (std::map, std::unordered_map or
|
||||||
|
* something compatible).
|
||||||
|
*/
|
||||||
|
template <template <typename...> class TMap>
|
||||||
|
class value_index_internal {
|
||||||
|
|
||||||
|
layer_builder& m_builder;
|
||||||
|
|
||||||
|
TMap<encoded_property_value, index_value> m_index;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct index.
|
||||||
|
*
|
||||||
|
* @param builder The layer we are building containing the value
|
||||||
|
* table we are creating the index for.
|
||||||
|
*/
|
||||||
|
explicit value_index_internal(layer_builder& builder) :
|
||||||
|
m_builder(builder) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index value for the specified value. If the value was not in
|
||||||
|
* the table, it will be added.
|
||||||
|
*
|
||||||
|
* @param value The value to store.
|
||||||
|
* @returns The index value of they value.
|
||||||
|
*/
|
||||||
|
index_value operator()(const encoded_property_value& value) {
|
||||||
|
const auto it = m_index.find(value);
|
||||||
|
if (it == m_index.end()) {
|
||||||
|
const auto idx = m_builder.add_value_without_dup_check(value);
|
||||||
|
m_index.emplace(value, idx);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class value_index_internal
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_INDEX_HPP
|
484
include/vtzero/layer.hpp
Normal file
484
include/vtzero/layer.hpp
Normal file
@ -0,0 +1,484 @@
|
|||||||
|
#ifndef VTZERO_LAYER_HPP
|
||||||
|
#define VTZERO_LAYER_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file layer.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains the layer class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "exception.hpp"
|
||||||
|
#include "feature.hpp"
|
||||||
|
#include "geometry.hpp"
|
||||||
|
#include "property_value.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
#include <protozero/pbf_message.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iterator>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A layer according to spec 4.1. It contains a version, the extent,
|
||||||
|
* and a name. For the most efficient way to access the features in this
|
||||||
|
* layer call next_feature() until it returns an invalid feature:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* std::string data = ...;
|
||||||
|
* vector_tile tile{data};
|
||||||
|
* layer = tile.next_layer();
|
||||||
|
* while (auto feature = layer.next_feature()) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* If you know the ID of a feature, you can get it directly with
|
||||||
|
* @code
|
||||||
|
* layer.get_feature_by_id(7);
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
class layer {
|
||||||
|
|
||||||
|
data_view m_data{};
|
||||||
|
uint32_t m_version = 1; // defaults to 1, see https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L55
|
||||||
|
uint32_t m_extent = 4096; // defaults to 4096, see https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto#L70
|
||||||
|
std::size_t m_num_features = 0;
|
||||||
|
data_view m_name{};
|
||||||
|
protozero::pbf_message<detail::pbf_layer> m_layer_reader{m_data};
|
||||||
|
mutable std::vector<data_view> m_key_table;
|
||||||
|
mutable std::vector<property_value> m_value_table;
|
||||||
|
mutable std::size_t m_key_table_size = 0;
|
||||||
|
mutable std::size_t m_value_table_size = 0;
|
||||||
|
|
||||||
|
void initialize_tables() const {
|
||||||
|
m_key_table.reserve(m_key_table_size);
|
||||||
|
m_key_table_size = 0;
|
||||||
|
|
||||||
|
m_value_table.reserve(m_value_table_size);
|
||||||
|
m_value_table_size = 0;
|
||||||
|
|
||||||
|
protozero::pbf_message<detail::pbf_layer> reader{m_data};
|
||||||
|
while (reader.next()) {
|
||||||
|
switch (reader.tag_and_type()) {
|
||||||
|
case protozero::tag_and_type(detail::pbf_layer::keys, protozero::pbf_wire_type::length_delimited):
|
||||||
|
m_key_table.push_back(reader.get_view());
|
||||||
|
break;
|
||||||
|
case protozero::tag_and_type(detail::pbf_layer::values, protozero::pbf_wire_type::length_delimited):
|
||||||
|
m_value_table.emplace_back(reader.get_view());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reader.skip(); // ignore unknown fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct an invalid layer object.
|
||||||
|
*/
|
||||||
|
layer() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a layer object. This is usually not something done in
|
||||||
|
* user code, because layers are created by the tile_iterator.
|
||||||
|
*
|
||||||
|
* @throws format_exception if the layer data is ill-formed.
|
||||||
|
* @throws version_exception if the layer contains an unsupported version
|
||||||
|
* number (only version 1 and 2 are supported)
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
*/
|
||||||
|
explicit layer(const data_view data) :
|
||||||
|
m_data(data) {
|
||||||
|
protozero::pbf_message<detail::pbf_layer> reader{data};
|
||||||
|
while (reader.next()) {
|
||||||
|
switch (reader.tag_and_type()) {
|
||||||
|
case protozero::tag_and_type(detail::pbf_layer::version, protozero::pbf_wire_type::varint):
|
||||||
|
m_version = reader.get_uint32();
|
||||||
|
break;
|
||||||
|
case protozero::tag_and_type(detail::pbf_layer::name, protozero::pbf_wire_type::length_delimited):
|
||||||
|
m_name = reader.get_view();
|
||||||
|
break;
|
||||||
|
case protozero::tag_and_type(detail::pbf_layer::features, protozero::pbf_wire_type::length_delimited):
|
||||||
|
reader.skip(); // ignore features for now
|
||||||
|
++m_num_features;
|
||||||
|
break;
|
||||||
|
case protozero::tag_and_type(detail::pbf_layer::keys, protozero::pbf_wire_type::length_delimited):
|
||||||
|
reader.skip();
|
||||||
|
++m_key_table_size;
|
||||||
|
break;
|
||||||
|
case protozero::tag_and_type(detail::pbf_layer::values, protozero::pbf_wire_type::length_delimited):
|
||||||
|
reader.skip();
|
||||||
|
++m_value_table_size;
|
||||||
|
break;
|
||||||
|
case protozero::tag_and_type(detail::pbf_layer::extent, protozero::pbf_wire_type::varint):
|
||||||
|
m_extent = reader.get_uint32();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_exception{"unknown field in layer (tag=" +
|
||||||
|
std::to_string(static_cast<uint32_t>(reader.tag())) +
|
||||||
|
", type=" +
|
||||||
|
std::to_string(static_cast<uint32_t>(reader.wire_type())) +
|
||||||
|
")"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This library can only handle version 1 and 2.
|
||||||
|
if (m_version < 1 || m_version > 2) {
|
||||||
|
throw version_exception{m_version};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4.1 "A layer MUST contain a name field."
|
||||||
|
if (m_name.data() == nullptr) {
|
||||||
|
throw format_exception{"missing name field in layer (spec 4.1)"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a valid layer? Valid layers are those not created from the
|
||||||
|
* default constructor.
|
||||||
|
*/
|
||||||
|
bool valid() const noexcept {
|
||||||
|
return m_data.data() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a valid layer? Valid layers are those not created from the
|
||||||
|
* default constructor.
|
||||||
|
*/
|
||||||
|
explicit operator bool() const noexcept {
|
||||||
|
return valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a reference to the raw data this layer is created from.
|
||||||
|
*/
|
||||||
|
data_view data() const noexcept {
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the layer.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
data_view name() const noexcept {
|
||||||
|
vtzero_assert_in_noexcept_function(valid());
|
||||||
|
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the version of this layer.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
std::uint32_t version() const noexcept {
|
||||||
|
vtzero_assert_in_noexcept_function(valid());
|
||||||
|
|
||||||
|
return m_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the extent of this layer.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
std::uint32_t extent() const noexcept {
|
||||||
|
vtzero_assert_in_noexcept_function(valid());
|
||||||
|
|
||||||
|
return m_extent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this layer contain any features?
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*/
|
||||||
|
bool empty() const noexcept {
|
||||||
|
return m_num_features == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of features in this layer.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*/
|
||||||
|
std::size_t num_features() const noexcept {
|
||||||
|
return m_num_features;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a reference to the key table.
|
||||||
|
*
|
||||||
|
* Complexity: Amortized constant. First time the table is needed
|
||||||
|
* it needs to be created.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
const std::vector<data_view>& key_table() const {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
|
||||||
|
if (m_key_table_size > 0) {
|
||||||
|
initialize_tables();
|
||||||
|
}
|
||||||
|
return m_key_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a reference to the value table.
|
||||||
|
*
|
||||||
|
* Complexity: Amortized constant. First time the table is needed
|
||||||
|
* it needs to be created.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
const std::vector<property_value>& value_table() const {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
|
||||||
|
if (m_value_table_size > 0) {
|
||||||
|
initialize_tables();
|
||||||
|
}
|
||||||
|
return m_value_table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the size of the key table. This returns the correct value
|
||||||
|
* whether the key table was already built or not.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* @returns Size of the key table.
|
||||||
|
*/
|
||||||
|
std::size_t key_table_size() const noexcept {
|
||||||
|
return m_key_table_size > 0 ? m_key_table_size : m_key_table.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the size of the value table. This returns the correct value
|
||||||
|
* whether the value table was already built or not.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* @returns Size of the value table.
|
||||||
|
*/
|
||||||
|
std::size_t value_table_size() const noexcept {
|
||||||
|
return m_value_table_size > 0 ? m_value_table_size : m_value_table.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the property key with the given index.
|
||||||
|
*
|
||||||
|
* Complexity: Amortized constant. First time the table is needed
|
||||||
|
* it needs to be created.
|
||||||
|
*
|
||||||
|
* @throws out_of_range_exception if the index is out of range.
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
data_view key(index_value index) const {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
|
||||||
|
const auto& table = key_table();
|
||||||
|
if (index.value() >= table.size()) {
|
||||||
|
throw out_of_range_exception{index.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
return table[index.value()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the property value with the given index.
|
||||||
|
*
|
||||||
|
* Complexity: Amortized constant. First time the table is needed
|
||||||
|
* it needs to be created.
|
||||||
|
*
|
||||||
|
* @throws out_of_range_exception if the index is out of range.
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
property_value value(index_value index) const {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
|
||||||
|
const auto& table = value_table();
|
||||||
|
if (index.value() >= table.size()) {
|
||||||
|
throw out_of_range_exception{index.value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
return table[index.value()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next feature in this layer.
|
||||||
|
*
|
||||||
|
* Note that the feature returned will internally contain a pointer to
|
||||||
|
* the layer it came from. The layer has to stay valid as long as the
|
||||||
|
* feature is used.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* @returns The next feature or the invalid feature if there are no
|
||||||
|
* more features.
|
||||||
|
* @throws format_exception if the layer data is ill-formed.
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
feature next_feature() {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
|
||||||
|
const bool has_next = m_layer_reader.next(detail::pbf_layer::features,
|
||||||
|
protozero::pbf_wire_type::length_delimited);
|
||||||
|
|
||||||
|
return has_next ? feature{this, m_layer_reader.get_view()} : feature{};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the feature iterator. The next time next_feature() is called,
|
||||||
|
* it will begin from the first feature again.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
void reset_feature() noexcept {
|
||||||
|
vtzero_assert_in_noexcept_function(valid());
|
||||||
|
|
||||||
|
m_layer_reader = protozero::pbf_message<detail::pbf_layer>{m_data};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call a function for each feature in this layer.
|
||||||
|
*
|
||||||
|
* @tparam The type of the function. It must take a single argument
|
||||||
|
* of type feature&& and return a bool. If the function returns
|
||||||
|
* false, the iteration will be stopped.
|
||||||
|
* @param func The function to call.
|
||||||
|
* @returns true if the iteration was completed and false otherwise.
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
template <typename TFunc>
|
||||||
|
bool for_each_feature(TFunc&& func) const {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
|
||||||
|
protozero::pbf_message<detail::pbf_layer> layer_reader{m_data};
|
||||||
|
while (layer_reader.next(detail::pbf_layer::features,
|
||||||
|
protozero::pbf_wire_type::length_delimited)) {
|
||||||
|
if (!std::forward<TFunc>(func)(feature{this, layer_reader.get_view()})) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the feature with the specified ID. If there are several features
|
||||||
|
* with the same ID, it is undefined which one you'll get.
|
||||||
|
*
|
||||||
|
* Note that the feature returned will internally contain a pointer to
|
||||||
|
* the layer it came from. The layer has to stay valid as long as the
|
||||||
|
* feature is used.
|
||||||
|
*
|
||||||
|
* Complexity: Linear in the number of features.
|
||||||
|
*
|
||||||
|
* @param id The ID to look for.
|
||||||
|
* @returns Feature with the specified ID or the invalid feature if
|
||||||
|
* there is no feature with this ID.
|
||||||
|
* @throws format_exception if the layer data is ill-formed.
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
*/
|
||||||
|
feature get_feature_by_id(uint64_t id) const {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
|
||||||
|
protozero::pbf_message<detail::pbf_layer> layer_reader{m_data};
|
||||||
|
while (layer_reader.next(detail::pbf_layer::features, protozero::pbf_wire_type::length_delimited)) {
|
||||||
|
const auto feature_data = layer_reader.get_view();
|
||||||
|
protozero::pbf_message<detail::pbf_feature> feature_reader{feature_data};
|
||||||
|
if (feature_reader.next(detail::pbf_feature::id, protozero::pbf_wire_type::varint)) {
|
||||||
|
if (feature_reader.get_uint64() == id) {
|
||||||
|
return feature{this, feature_data};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return feature{};
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class layer
|
||||||
|
|
||||||
|
inline property feature::next_property() {
|
||||||
|
const auto idxs = next_property_indexes();
|
||||||
|
property p{};
|
||||||
|
if (idxs.valid()) {
|
||||||
|
p = {m_layer->key(idxs.key()),
|
||||||
|
m_layer->value(idxs.value())};
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline index_value_pair feature::next_property_indexes() {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
if (m_property_iterator == m_properties.end()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ki = *m_property_iterator++;
|
||||||
|
if (!index_value{ki}.valid()) {
|
||||||
|
throw out_of_range_exception{ki};
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(m_property_iterator != m_properties.end());
|
||||||
|
const auto vi = *m_property_iterator++;
|
||||||
|
if (!index_value{vi}.valid()) {
|
||||||
|
throw out_of_range_exception{vi};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ki >= m_layer->key_table_size()) {
|
||||||
|
throw out_of_range_exception{ki};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vi >= m_layer->value_table_size()) {
|
||||||
|
throw out_of_range_exception{vi};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {ki, vi};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TFunc>
|
||||||
|
bool feature::for_each_property(TFunc&& func) const {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
|
||||||
|
for (auto it = m_properties.begin(); it != m_properties.end();) {
|
||||||
|
const uint32_t ki = *it++;
|
||||||
|
if (!index_value{ki}.valid()) {
|
||||||
|
throw out_of_range_exception{ki};
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(m_property_iterator != m_properties.end());
|
||||||
|
const uint32_t vi = *it++;
|
||||||
|
if (!index_value{vi}.valid()) {
|
||||||
|
throw out_of_range_exception{vi};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::forward<TFunc>(func)(property{m_layer->key(ki), m_layer->value(vi)})) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_LAYER_HPP
|
64
include/vtzero/output.hpp
Normal file
64
include/vtzero/output.hpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#ifndef VTZERO_OUTPUT_HPP
|
||||||
|
#define VTZERO_OUTPUT_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file output.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains overloads of operator<< for basic vtzero types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vtzero/geometry.hpp>
|
||||||
|
#include <vtzero/types.hpp>
|
||||||
|
|
||||||
|
#include <iosfwd>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/// Overload of the << operator for GeomType
|
||||||
|
template <typename TChar, typename TTraits>
|
||||||
|
std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const GeomType type) {
|
||||||
|
return out << geom_type_name(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overload of the << operator for property_value_type
|
||||||
|
template <typename TChar, typename TTraits>
|
||||||
|
std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const property_value_type type) {
|
||||||
|
return out << property_value_type_name(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overload of the << operator for index_value
|
||||||
|
template <typename TChar, typename TTraits>
|
||||||
|
std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const index_value index) {
|
||||||
|
if (index.valid()) {
|
||||||
|
return out << index.value();
|
||||||
|
}
|
||||||
|
return out << "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overload of the << operator for index_value_pair
|
||||||
|
template <typename TChar, typename TTraits>
|
||||||
|
std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const index_value_pair index_pair) {
|
||||||
|
if (index_pair.valid()) {
|
||||||
|
return out << '[' << index_pair.key() << ',' << index_pair.value() << ']';
|
||||||
|
}
|
||||||
|
return out << "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overload of the << operator for points
|
||||||
|
template <typename TChar, typename TTraits>
|
||||||
|
std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const point p) {
|
||||||
|
return out << '(' << p.x << ',' << p.y << ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_OUTPUT_HPP
|
89
include/vtzero/property.hpp
Normal file
89
include/vtzero/property.hpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#ifndef VTZERO_PROPERTY_HPP
|
||||||
|
#define VTZERO_PROPERTY_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file property.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains the property class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "property_value.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A view of a vector tile property (key and value).
|
||||||
|
*
|
||||||
|
* Doesn't hold any data itself, just views of the key and value.
|
||||||
|
*/
|
||||||
|
class property {
|
||||||
|
|
||||||
|
data_view m_key{};
|
||||||
|
property_value m_value{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default constructor creates an invalid (empty) property.
|
||||||
|
*/
|
||||||
|
constexpr property() noexcept = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a (valid) property from a key and value.
|
||||||
|
*/
|
||||||
|
constexpr property(const data_view key, const property_value value) noexcept :
|
||||||
|
m_key(key),
|
||||||
|
m_value(value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a valid property? Properties are valid if they were
|
||||||
|
* constructed using the non-default constructor.
|
||||||
|
*/
|
||||||
|
constexpr bool valid() const noexcept {
|
||||||
|
return m_key.data() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a valid property? Properties are valid if they were
|
||||||
|
* constructed using the non-default constructor.
|
||||||
|
*/
|
||||||
|
explicit constexpr operator bool() const noexcept {
|
||||||
|
return valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the property key.
|
||||||
|
constexpr data_view key() const noexcept {
|
||||||
|
return m_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the property value.
|
||||||
|
constexpr property_value value() const noexcept {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class property
|
||||||
|
|
||||||
|
/// properties are equal if they contain the same key & value data.
|
||||||
|
inline constexpr bool operator==(const property& lhs, const property& rhs) noexcept {
|
||||||
|
return lhs.key() == rhs.key() && lhs.value() == rhs.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// properties are unequal if they do not contain them same key and value data.
|
||||||
|
inline constexpr bool operator!=(const property& lhs, const property& rhs) noexcept {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_PROPERTY_HPP
|
103
include/vtzero/property_mapper.hpp
Normal file
103
include/vtzero/property_mapper.hpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#ifndef VTZERO_PROPERTY_MAPPER_HPP
|
||||||
|
#define VTZERO_PROPERTY_MAPPER_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file property_mapper.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains the property_mapper class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "builder.hpp"
|
||||||
|
#include "layer.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establishes a mapping between index values of properties of an existing
|
||||||
|
* layer and a new layer. Can be used when copying some features from an
|
||||||
|
* existing layer to a new layer.
|
||||||
|
*/
|
||||||
|
class property_mapper {
|
||||||
|
|
||||||
|
const layer& m_layer;
|
||||||
|
layer_builder& m_layer_builder;
|
||||||
|
|
||||||
|
std::vector<index_value> m_keys;
|
||||||
|
std::vector<index_value> m_values;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the mapping between the specified layers
|
||||||
|
*
|
||||||
|
* @param layer The existing layer from which (some) properties will
|
||||||
|
* be copied.
|
||||||
|
* @param layer_builder The new layer that is being created.
|
||||||
|
*/
|
||||||
|
property_mapper(const layer& layer, layer_builder& layer_builder) :
|
||||||
|
m_layer(layer),
|
||||||
|
m_layer_builder(layer_builder) {
|
||||||
|
m_keys.resize(layer.key_table().size());
|
||||||
|
m_values.resize(layer.value_table().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map the value index of a key.
|
||||||
|
*
|
||||||
|
* @param index The value index of the key in the existing table.
|
||||||
|
* @returns The value index of the same key in the new table.
|
||||||
|
*/
|
||||||
|
index_value map_key(index_value index) {
|
||||||
|
auto& k = m_keys[index.value()];
|
||||||
|
|
||||||
|
if (!k.valid()) {
|
||||||
|
k = m_layer_builder.add_key_without_dup_check(m_layer.key(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map the value index of a value.
|
||||||
|
*
|
||||||
|
* @param index The value index of the value in the existing table.
|
||||||
|
* @returns The value index of the same value in the new table.
|
||||||
|
*/
|
||||||
|
index_value map_value(index_value index) {
|
||||||
|
auto& v = m_values[index.value()];
|
||||||
|
|
||||||
|
if (!v.valid()) {
|
||||||
|
v = m_layer_builder.add_value_without_dup_check(m_layer.value(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map the value indexes of a key/value pair.
|
||||||
|
*
|
||||||
|
* @param idxs The value indexes of the key/value pair in the existing
|
||||||
|
* table.
|
||||||
|
* @returns The value indexes of the same key/value pair in the new
|
||||||
|
* table.
|
||||||
|
*/
|
||||||
|
index_value_pair operator()(index_value_pair idxs) {
|
||||||
|
return {map_key(idxs.key()), map_value(idxs.value())};
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class property_mapper
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_PROPERTY_MAPPER_HPP
|
398
include/vtzero/property_value.hpp
Normal file
398
include/vtzero/property_value.hpp
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
#ifndef VTZERO_PROPERTY_VALUE_HPP
|
||||||
|
#define VTZERO_PROPERTY_VALUE_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file property_value.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains the property_value class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "exception.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
#include <protozero/pbf_message.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A view of a vector tile property value.
|
||||||
|
*
|
||||||
|
* Doesn't hold any data itself.
|
||||||
|
*/
|
||||||
|
class property_value {
|
||||||
|
|
||||||
|
data_view m_value{};
|
||||||
|
|
||||||
|
static bool check_tag_and_type(protozero::pbf_tag_type tag, protozero::pbf_wire_type type) noexcept {
|
||||||
|
static constexpr const std::array<protozero::pbf_wire_type, 7> types{{
|
||||||
|
string_value_type::wire_type,
|
||||||
|
float_value_type::wire_type,
|
||||||
|
double_value_type::wire_type,
|
||||||
|
int_value_type::wire_type,
|
||||||
|
uint_value_type::wire_type,
|
||||||
|
sint_value_type::wire_type,
|
||||||
|
bool_value_type::wire_type,
|
||||||
|
}};
|
||||||
|
|
||||||
|
if (tag < 1 || tag > types.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return types[tag - 1] == type; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
|
||||||
|
}
|
||||||
|
|
||||||
|
static data_view get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, string_value_type /* dummy */) {
|
||||||
|
return value_message.get_view();
|
||||||
|
}
|
||||||
|
|
||||||
|
static float get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, float_value_type /* dummy */) {
|
||||||
|
return value_message.get_float();
|
||||||
|
}
|
||||||
|
|
||||||
|
static double get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, double_value_type /* dummy */) {
|
||||||
|
return value_message.get_double();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, int_value_type /* dummy */) {
|
||||||
|
return value_message.get_int64();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, uint_value_type /* dummy */) {
|
||||||
|
return value_message.get_uint64();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, sint_value_type /* dummy */) {
|
||||||
|
return value_message.get_sint64();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_value_impl(protozero::pbf_message<detail::pbf_value>& value_message, bool_value_type /* dummy */) {
|
||||||
|
return value_message.get_bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename T::type get_value() const {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
protozero::pbf_message<detail::pbf_value> value_message{m_value};
|
||||||
|
|
||||||
|
typename T::type result{};
|
||||||
|
bool has_result = false;
|
||||||
|
while (value_message.next(T::pvtype, T::wire_type)) {
|
||||||
|
result = get_value_impl(value_message, T());
|
||||||
|
has_result = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw type_exception{};
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default constructor creates an invalid (empty) property_value.
|
||||||
|
*/
|
||||||
|
constexpr property_value() noexcept = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a (valid) property_value from a data_view.
|
||||||
|
*/
|
||||||
|
explicit constexpr property_value(const data_view value) noexcept :
|
||||||
|
m_value(value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a valid property value? Property values are valid if they
|
||||||
|
* were constructed using the non-default constructor.
|
||||||
|
*/
|
||||||
|
constexpr bool valid() const noexcept {
|
||||||
|
return m_value.data() != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the type of this property.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
* @throws format_exception if the encoding is invalid
|
||||||
|
*/
|
||||||
|
property_value_type type() const {
|
||||||
|
vtzero_assert(valid());
|
||||||
|
protozero::pbf_message<detail::pbf_value> value_message{m_value};
|
||||||
|
if (value_message.next()) {
|
||||||
|
const auto tag_val = static_cast<protozero::pbf_tag_type>(value_message.tag());
|
||||||
|
if (!check_tag_and_type(tag_val, value_message.wire_type())) {
|
||||||
|
throw format_exception{"illegal property value type"};
|
||||||
|
}
|
||||||
|
return value_message.tag();
|
||||||
|
}
|
||||||
|
throw format_exception{"missing tag value"};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the internal data_view this object was constructed with.
|
||||||
|
*/
|
||||||
|
constexpr data_view data() const noexcept {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get string value of this object.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
* @throws type_exception if the type of this property value is
|
||||||
|
* something other than string.
|
||||||
|
*/
|
||||||
|
data_view string_value() const {
|
||||||
|
return get_value<string_value_type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get float value of this object.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
* @throws type_exception if the type of this property value is
|
||||||
|
* something other than float.
|
||||||
|
*/
|
||||||
|
float float_value() const {
|
||||||
|
return get_value<float_value_type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get double value of this object.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
* @throws type_exception if the type of this property value is
|
||||||
|
* something other than double.
|
||||||
|
*/
|
||||||
|
double double_value() const {
|
||||||
|
return get_value<double_value_type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get int value of this object.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
* @throws type_exception if the type of this property value is
|
||||||
|
* something other than int.
|
||||||
|
*/
|
||||||
|
std::int64_t int_value() const {
|
||||||
|
return get_value<int_value_type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get uint value of this object.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
* @throws type_exception if the type of this property value is
|
||||||
|
* something other than uint.
|
||||||
|
*/
|
||||||
|
std::uint64_t uint_value() const {
|
||||||
|
return get_value<uint_value_type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get sint value of this object.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
* @throws type_exception if the type of this property value is
|
||||||
|
* something other than sint.
|
||||||
|
*/
|
||||||
|
std::int64_t sint_value() const {
|
||||||
|
return get_value<sint_value_type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get bool value of this object.
|
||||||
|
*
|
||||||
|
* @pre @code valid() @endcode
|
||||||
|
* @throws type_exception if the type of this property value is
|
||||||
|
* something other than bool.
|
||||||
|
*/
|
||||||
|
bool bool_value() const {
|
||||||
|
return get_value<bool_value_type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class property_value
|
||||||
|
|
||||||
|
/// property_values are equal if they contain the same data.
|
||||||
|
inline constexpr bool operator==(const property_value lhs, const property_value rhs) noexcept {
|
||||||
|
return lhs.data() == rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// property_values are unequal if they do not contain the same data.
|
||||||
|
inline constexpr bool operator!=(const property_value lhs, const property_value rhs) noexcept {
|
||||||
|
return lhs.data() != rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// property_values are ordered in the same way as the underlying data
|
||||||
|
inline bool operator<(const property_value lhs, const property_value rhs) noexcept {
|
||||||
|
return lhs.data() < rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// property_values are ordered in the same way as the underlying data
|
||||||
|
inline bool operator<=(const property_value lhs, const property_value rhs) noexcept {
|
||||||
|
return lhs.data() <= rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// property_values are ordered in the same way as the underlying data
|
||||||
|
inline bool operator>(const property_value lhs, const property_value rhs) noexcept {
|
||||||
|
return lhs.data() > rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// property_values are ordered in the same way as the underlying data
|
||||||
|
inline bool operator>=(const property_value lhs, const property_value rhs) noexcept {
|
||||||
|
return lhs.data() >= rhs.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the value to a visitor.
|
||||||
|
*
|
||||||
|
* The visitor must have an overloaded call operator taking a single
|
||||||
|
* argument of each of the types data_view, float, double, int64_t,
|
||||||
|
* uint64_t, and bool. All call operators must return the same type which
|
||||||
|
* will be the return type of this function.
|
||||||
|
*/
|
||||||
|
template <typename V>
|
||||||
|
decltype(std::declval<V>()(data_view{})) apply_visitor(V&& visitor, const property_value value) {
|
||||||
|
switch (value.type()) {
|
||||||
|
case property_value_type::string_value:
|
||||||
|
return std::forward<V>(visitor)(value.string_value());
|
||||||
|
case property_value_type::float_value:
|
||||||
|
return std::forward<V>(visitor)(value.float_value());
|
||||||
|
case property_value_type::double_value:
|
||||||
|
return std::forward<V>(visitor)(value.double_value());
|
||||||
|
case property_value_type::int_value:
|
||||||
|
return std::forward<V>(visitor)(value.int_value());
|
||||||
|
case property_value_type::uint_value:
|
||||||
|
return std::forward<V>(visitor)(value.uint_value());
|
||||||
|
case property_value_type::sint_value:
|
||||||
|
return std::forward<V>(visitor)(value.sint_value());
|
||||||
|
default: // case property_value_type::bool_value:
|
||||||
|
return std::forward<V>(visitor)(value.bool_value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <typename TVariant, typename TMapping>
|
||||||
|
struct convert_visitor {
|
||||||
|
|
||||||
|
TVariant operator()(data_view value) const {
|
||||||
|
return TVariant(typename TMapping::string_type(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
TVariant operator()(float value) const {
|
||||||
|
return TVariant(typename TMapping::float_type(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
TVariant operator()(double value) const {
|
||||||
|
return TVariant(typename TMapping::double_type(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
TVariant operator()(int64_t value) const {
|
||||||
|
return TVariant(typename TMapping::int_type(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
TVariant operator()(uint64_t value) const {
|
||||||
|
return TVariant(typename TMapping::uint_type(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
TVariant operator()(bool value) const {
|
||||||
|
return TVariant(typename TMapping::bool_type(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct convert_visitor
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default mapping between the different types of a property_value to
|
||||||
|
* the types needed for a variant. Derive from this class, overwrite
|
||||||
|
* the types you want and use that class as second template parameter
|
||||||
|
* in the convert_property_value class.
|
||||||
|
*/
|
||||||
|
struct property_value_mapping {
|
||||||
|
|
||||||
|
/// mapping for string type
|
||||||
|
using string_type = std::string;
|
||||||
|
|
||||||
|
/// mapping for float type
|
||||||
|
using float_type = float;
|
||||||
|
|
||||||
|
/// mapping for double type
|
||||||
|
using double_type = double;
|
||||||
|
|
||||||
|
/// mapping for int type
|
||||||
|
using int_type = int64_t;
|
||||||
|
|
||||||
|
/// mapping for uint type
|
||||||
|
using uint_type = uint64_t;
|
||||||
|
|
||||||
|
/// mapping for bool type
|
||||||
|
using bool_type = bool;
|
||||||
|
|
||||||
|
}; // struct property_value_mapping
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a property_value to a different (usually variant-based)
|
||||||
|
* class.
|
||||||
|
*
|
||||||
|
* Usage: If you have a variant type like
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* using variant_type = boost::variant<std::string, float, double, int64_t, uint64_t, bool>;
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* you can use
|
||||||
|
* @code
|
||||||
|
* property_value x = ...;
|
||||||
|
* auto v = convert_property_value<variant_type>(x);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* to convert the data.
|
||||||
|
*
|
||||||
|
* Usually your variant type has to support all of the following types:
|
||||||
|
* std::string, float, double, int64_t, uint64_t, and bool. If your type
|
||||||
|
* doesn't, you can add a second template parameter with a struct
|
||||||
|
* containing the mapping between the vtzero types and your types:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* struct mapping : vtzero::property_value_mapping {
|
||||||
|
* using float_type = double; // convert all floats to doubles
|
||||||
|
* using bool_type = int; // convert all bools to ints
|
||||||
|
* // use default types for the rest
|
||||||
|
* // see the class vtzero::property_value_mapping for the defaults
|
||||||
|
* };
|
||||||
|
* property_value x = ...;
|
||||||
|
* auto v = convert_property_value<variant_type, mapping>(x);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @tparam TVariant The variant type to convert to.
|
||||||
|
* @tparam TMapping A struct derived from property_value_mapping with the
|
||||||
|
* mapping for the types.
|
||||||
|
* @param value The property value to convert.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template <typename TVariant, typename TMapping = property_value_mapping>
|
||||||
|
TVariant convert_property_value(const property_value value) {
|
||||||
|
return apply_visitor(detail::convert_visitor<TVariant, TMapping>{}, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_PROPERTY_VALUE_HPP
|
429
include/vtzero/types.hpp
Normal file
429
include/vtzero/types.hpp
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
#ifndef VTZERO_TYPES_HPP
|
||||||
|
#define VTZERO_TYPES_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include <protozero/pbf_reader.hpp>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
// @cond internal
|
||||||
|
// Wrappers for assert() used for testing
|
||||||
|
#ifndef vtzero_assert
|
||||||
|
# define vtzero_assert(x) assert(x)
|
||||||
|
#endif
|
||||||
|
#ifndef vtzero_assert_in_noexcept_function
|
||||||
|
# define vtzero_assert_in_noexcept_function(x) assert(x)
|
||||||
|
#endif
|
||||||
|
// @endcond
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file types.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains the declaration of low-level types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief All parts of the vtzero header-only library are in this namespace.
|
||||||
|
*/
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using data_view class from protozero. See the protozero documentation
|
||||||
|
* on how to change this to use a different implementation.
|
||||||
|
* https://github.com/mapbox/protozero/blob/master/doc/advanced.md#protozero_use_view
|
||||||
|
*/
|
||||||
|
using data_view = protozero::data_view;
|
||||||
|
|
||||||
|
// based on https://github.com/mapbox/vector-tile-spec/blob/master/2.1/vector_tile.proto
|
||||||
|
|
||||||
|
/// The geometry type as specified in the vector tile spec
|
||||||
|
enum class GeomType {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
POINT = 1,
|
||||||
|
LINESTRING = 2,
|
||||||
|
POLYGON = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of a GeomType (for debug output etc.)
|
||||||
|
*/
|
||||||
|
inline const char* geom_type_name(GeomType type) noexcept {
|
||||||
|
static const char* names[] = {
|
||||||
|
"unknown", "point", "linestring", "polygon"
|
||||||
|
};
|
||||||
|
return names[static_cast<int>(type)]; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The property value type as specified in the vector tile spec
|
||||||
|
enum class property_value_type : protozero::pbf_tag_type {
|
||||||
|
string_value = 1,
|
||||||
|
float_value = 2,
|
||||||
|
double_value = 3,
|
||||||
|
int_value = 4,
|
||||||
|
uint_value = 5,
|
||||||
|
sint_value = 6,
|
||||||
|
bool_value = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of a property value type (for debug output etc.)
|
||||||
|
*/
|
||||||
|
inline const char* property_value_type_name(property_value_type type) noexcept {
|
||||||
|
static const char* names[] = {
|
||||||
|
"", "string", "float", "double", "int", "uint", "sint", "bool"
|
||||||
|
};
|
||||||
|
return names[static_cast<int>(type)]; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
enum class pbf_tile : protozero::pbf_tag_type {
|
||||||
|
layers = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class pbf_layer : protozero::pbf_tag_type {
|
||||||
|
name = 1,
|
||||||
|
features = 2,
|
||||||
|
keys = 3,
|
||||||
|
values = 4,
|
||||||
|
extent = 5,
|
||||||
|
version = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class pbf_feature : protozero::pbf_tag_type {
|
||||||
|
id = 1,
|
||||||
|
tags = 2,
|
||||||
|
type = 3,
|
||||||
|
geometry = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
using pbf_value = property_value_type;
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
/// property value type holding a reference to a string
|
||||||
|
struct string_value_type {
|
||||||
|
|
||||||
|
/// the underlying storage type
|
||||||
|
using type = data_view;
|
||||||
|
|
||||||
|
/// @cond internal
|
||||||
|
constexpr static const property_value_type pvtype = property_value_type::string_value;
|
||||||
|
constexpr static const protozero::pbf_wire_type wire_type = protozero::pbf_wire_type::length_delimited;
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/// value
|
||||||
|
data_view value{};
|
||||||
|
|
||||||
|
/// Construct empty string_value_type
|
||||||
|
constexpr string_value_type() noexcept = default;
|
||||||
|
|
||||||
|
/// Construct string_value_type
|
||||||
|
explicit constexpr string_value_type(data_view v) :
|
||||||
|
value(v) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct string_value_type
|
||||||
|
|
||||||
|
/// property value type holding a float
|
||||||
|
struct float_value_type {
|
||||||
|
|
||||||
|
/// the underlying storage type
|
||||||
|
using type = float;
|
||||||
|
|
||||||
|
/// @cond internal
|
||||||
|
constexpr static const property_value_type pvtype = property_value_type::float_value;
|
||||||
|
constexpr static const protozero::pbf_wire_type wire_type = protozero::pbf_wire_type::fixed32;
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/// value
|
||||||
|
float value = 0.0f;
|
||||||
|
|
||||||
|
/// Construct float_value_type with value 0.0
|
||||||
|
constexpr float_value_type() noexcept = default;
|
||||||
|
|
||||||
|
/// Construct float_value_type
|
||||||
|
explicit constexpr float_value_type(float v) :
|
||||||
|
value(v) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct float_value_type
|
||||||
|
|
||||||
|
/// property value type holding a double
|
||||||
|
struct double_value_type {
|
||||||
|
|
||||||
|
/// the underlying storage type
|
||||||
|
using type = double;
|
||||||
|
|
||||||
|
/// @cond internal
|
||||||
|
constexpr static const property_value_type pvtype = property_value_type::double_value;
|
||||||
|
constexpr static const protozero::pbf_wire_type wire_type = protozero::pbf_wire_type::fixed64;
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/// value
|
||||||
|
double value = 0.0;
|
||||||
|
|
||||||
|
/// Construct double_value_type with value 0.0
|
||||||
|
constexpr double_value_type() noexcept = default;
|
||||||
|
|
||||||
|
/// Construct double_value_type
|
||||||
|
explicit constexpr double_value_type(double v) :
|
||||||
|
value(v) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct double_value_type
|
||||||
|
|
||||||
|
/// property value type holding an int
|
||||||
|
struct int_value_type {
|
||||||
|
|
||||||
|
/// the underlying storage type
|
||||||
|
using type = int64_t;
|
||||||
|
|
||||||
|
/// @cond internal
|
||||||
|
constexpr static const property_value_type pvtype = property_value_type::int_value;
|
||||||
|
constexpr static const protozero::pbf_wire_type wire_type = protozero::pbf_wire_type::varint;
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/// value
|
||||||
|
int64_t value = 0;
|
||||||
|
|
||||||
|
/// Construct int_value_type with value 0
|
||||||
|
constexpr int_value_type() noexcept = default;
|
||||||
|
|
||||||
|
/// Construct int_value_type
|
||||||
|
explicit constexpr int_value_type(int64_t v) :
|
||||||
|
value(v) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct int_value_type
|
||||||
|
|
||||||
|
/// property value type holding a uint
|
||||||
|
struct uint_value_type {
|
||||||
|
|
||||||
|
/// the underlying storage type
|
||||||
|
using type = uint64_t;
|
||||||
|
|
||||||
|
/// @cond internal
|
||||||
|
constexpr static const property_value_type pvtype = property_value_type::uint_value;
|
||||||
|
constexpr static const protozero::pbf_wire_type wire_type = protozero::pbf_wire_type::varint;
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/// value
|
||||||
|
uint64_t value = 0;
|
||||||
|
|
||||||
|
/// Construct uint_value_type with value 0
|
||||||
|
constexpr uint_value_type() noexcept = default;
|
||||||
|
|
||||||
|
/// Construct uint_value_type
|
||||||
|
explicit constexpr uint_value_type(uint64_t v) :
|
||||||
|
value(v) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct uint_value_type
|
||||||
|
|
||||||
|
/// property value type holding an sint
|
||||||
|
struct sint_value_type {
|
||||||
|
|
||||||
|
/// the underlying storage type
|
||||||
|
using type = int64_t;
|
||||||
|
|
||||||
|
/// @cond internal
|
||||||
|
constexpr static const property_value_type pvtype = property_value_type::sint_value;
|
||||||
|
constexpr static const protozero::pbf_wire_type wire_type = protozero::pbf_wire_type::varint;
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/// value
|
||||||
|
int64_t value = 0;
|
||||||
|
|
||||||
|
/// Construct sint_value_type with value 0
|
||||||
|
constexpr sint_value_type() noexcept = default;
|
||||||
|
|
||||||
|
/// Construct sint_value_type
|
||||||
|
explicit constexpr sint_value_type(int64_t v) :
|
||||||
|
value(v) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct sint_value_type
|
||||||
|
|
||||||
|
/// property value type holding a bool
|
||||||
|
struct bool_value_type {
|
||||||
|
|
||||||
|
/// the underlying storage type
|
||||||
|
using type = bool;
|
||||||
|
|
||||||
|
/// @cond internal
|
||||||
|
constexpr static const property_value_type pvtype = property_value_type::bool_value;
|
||||||
|
constexpr static const protozero::pbf_wire_type wire_type = protozero::pbf_wire_type::varint;
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
|
/// value
|
||||||
|
bool value = false;
|
||||||
|
|
||||||
|
/// Construct bool_value_type with false value
|
||||||
|
constexpr bool_value_type() noexcept = default;
|
||||||
|
|
||||||
|
/// Construct bool_value_type
|
||||||
|
explicit constexpr bool_value_type(bool v) :
|
||||||
|
value(v) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // struct bool_value_type
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class wraps the uint32_t used for looking up keys/values in the
|
||||||
|
* key/values tables.
|
||||||
|
*/
|
||||||
|
class index_value {
|
||||||
|
|
||||||
|
static const uint32_t invalid_value = std::numeric_limits<uint32_t>::max();
|
||||||
|
|
||||||
|
uint32_t m_value = invalid_value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Default construct to an invalid value.
|
||||||
|
constexpr index_value() noexcept = default;
|
||||||
|
|
||||||
|
/// Construct with the given value.
|
||||||
|
constexpr index_value(uint32_t value) noexcept : // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
|
||||||
|
m_value(value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this index value valid? Index values are valid if they have
|
||||||
|
* been initialized with something other than the default constructor.
|
||||||
|
*/
|
||||||
|
constexpr bool valid() const noexcept {
|
||||||
|
return m_value != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value.
|
||||||
|
*
|
||||||
|
* @pre Must be valid.
|
||||||
|
*/
|
||||||
|
uint32_t value() const noexcept {
|
||||||
|
vtzero_assert_in_noexcept_function(valid());
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class index_value
|
||||||
|
|
||||||
|
/// Index values are equal if their values are.
|
||||||
|
inline bool operator==(const index_value lhs, const index_value rhs) noexcept {
|
||||||
|
return lhs.value() == rhs.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Index values are not equal if their values are not equal.
|
||||||
|
inline bool operator!=(const index_value lhs, const index_value rhs) noexcept {
|
||||||
|
return lhs.value() != rhs.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class holds two index_values, one for a key and one for a value.
|
||||||
|
*/
|
||||||
|
class index_value_pair {
|
||||||
|
|
||||||
|
index_value m_key{};
|
||||||
|
index_value m_value{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Default construct to an invalid value.
|
||||||
|
constexpr index_value_pair() noexcept = default;
|
||||||
|
|
||||||
|
/// Construct with the given values.
|
||||||
|
constexpr index_value_pair(index_value key, index_value value) noexcept :
|
||||||
|
m_key(key),
|
||||||
|
m_value(value) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this index value pair valid? Index values pairs are valid if
|
||||||
|
* both the key and the value index value are valid.
|
||||||
|
*/
|
||||||
|
constexpr bool valid() const noexcept {
|
||||||
|
return m_key.valid() && m_value.valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this index value pair valid? Index values pairs are valid if
|
||||||
|
* both the key and the value index value are valid.
|
||||||
|
*/
|
||||||
|
constexpr explicit operator bool() const noexcept {
|
||||||
|
return valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the key index value.
|
||||||
|
constexpr index_value key() const noexcept {
|
||||||
|
return m_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the value index value.
|
||||||
|
constexpr index_value value() const noexcept {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class index_value_pair
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The geometry class holds a geometry type and a reference to the data
|
||||||
|
* defining this geometry. To actually decode these geometries, use the
|
||||||
|
* decode_point_geometry(), decode_linestring_geometry(), and
|
||||||
|
* decode_polygon_geometry() classes.
|
||||||
|
*/
|
||||||
|
class geometry {
|
||||||
|
|
||||||
|
data_view m_data{};
|
||||||
|
GeomType m_type = GeomType::UNKNOWN;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A forward iterator yielding 32bit unsigned integers with the
|
||||||
|
* geometry encoded according to spec 4.3.
|
||||||
|
*/
|
||||||
|
using const_iterator = protozero::pbf_reader::const_uint32_iterator;
|
||||||
|
|
||||||
|
/// Default construct to an invalid value.
|
||||||
|
constexpr geometry() noexcept = default;
|
||||||
|
|
||||||
|
/// Construct with the given values.
|
||||||
|
constexpr geometry(data_view data, GeomType type) noexcept :
|
||||||
|
m_data(data),
|
||||||
|
m_type(type) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The data of this geometry
|
||||||
|
constexpr data_view data() const noexcept {
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The type of this geometry
|
||||||
|
constexpr GeomType type() const noexcept {
|
||||||
|
return m_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return iterator to the beginning of the data.
|
||||||
|
const_iterator begin() const noexcept {
|
||||||
|
return {m_data.data(), m_data.data() + m_data.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return iterator to one past the end of the data.
|
||||||
|
const_iterator end() const noexcept {
|
||||||
|
return {m_data.data() + m_data.size(), m_data.data() + m_data.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class geometry
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_TYPES_HPP
|
290
include/vtzero/vector_tile.hpp
Normal file
290
include/vtzero/vector_tile.hpp
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
#ifndef VTZERO_VECTOR_TILE_HPP
|
||||||
|
#define VTZERO_VECTOR_TILE_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file vector_tile.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains the vector_tile class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "exception.hpp"
|
||||||
|
#include "layer.hpp"
|
||||||
|
#include "types.hpp"
|
||||||
|
|
||||||
|
#include <protozero/pbf_message.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iterator>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace vtzero {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A vector tile is basically nothing more than an ordered collection
|
||||||
|
* of named layers. For the most efficient way to access the layers,
|
||||||
|
* call next_layer() until it returns an invalid layer:
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* std::string data = ...;
|
||||||
|
* vector_tile tile{data};
|
||||||
|
* while (auto layer = tile.next_layer()) {
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* If you know the index of the layer, you can get it directly with
|
||||||
|
* @code
|
||||||
|
* tile.get_layer(4);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* You can also access the layer by name:
|
||||||
|
* @code
|
||||||
|
* tile.get_layer_by_name("foobar");
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
class vector_tile {
|
||||||
|
|
||||||
|
data_view m_data;
|
||||||
|
protozero::pbf_message<detail::pbf_tile> m_tile_reader;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the vector_tile from a data_view. The vector_tile object
|
||||||
|
* will keep a reference to the data referenced by the data_view. No
|
||||||
|
* copy of the data is created.
|
||||||
|
*/
|
||||||
|
explicit vector_tile(const data_view data) noexcept :
|
||||||
|
m_data(data),
|
||||||
|
m_tile_reader(m_data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the vector_tile from a string. The vector_tile object
|
||||||
|
* will keep a reference to the data referenced by the string. No
|
||||||
|
* copy of the data is created.
|
||||||
|
*/
|
||||||
|
explicit vector_tile(const std::string& data) noexcept :
|
||||||
|
m_data(data.data(), data.size()),
|
||||||
|
m_tile_reader(m_data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct the vector_tile from a ptr and size. The vector_tile
|
||||||
|
* object will keep a reference to the data. No copy of the data is
|
||||||
|
* created.
|
||||||
|
*/
|
||||||
|
vector_tile(const char* data, std::size_t size) noexcept :
|
||||||
|
m_data(data, size),
|
||||||
|
m_tile_reader(m_data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this vector tile empty?
|
||||||
|
*
|
||||||
|
* @returns true if there are no layers in this vector tile, false
|
||||||
|
* otherwise
|
||||||
|
* Complexity: Constant.
|
||||||
|
*/
|
||||||
|
bool empty() const noexcept {
|
||||||
|
return m_data.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of layers in this tile.
|
||||||
|
*
|
||||||
|
* Complexity: Linear in the number of layers.
|
||||||
|
*
|
||||||
|
* @returns the number of layers in this tile
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
*/
|
||||||
|
std::size_t count_layers() const {
|
||||||
|
std::size_t size = 0;
|
||||||
|
|
||||||
|
protozero::pbf_message<detail::pbf_tile> tile_reader{m_data};
|
||||||
|
while (tile_reader.next(detail::pbf_tile::layers,
|
||||||
|
protozero::pbf_wire_type::length_delimited)) {
|
||||||
|
tile_reader.skip();
|
||||||
|
++size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next layer in this tile.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*
|
||||||
|
* @returns layer The next layer or the invalid layer if there are no
|
||||||
|
* more layers.
|
||||||
|
* @throws format_exception if the tile data is ill-formed.
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
*/
|
||||||
|
layer next_layer() {
|
||||||
|
const bool has_next = m_tile_reader.next(detail::pbf_tile::layers,
|
||||||
|
protozero::pbf_wire_type::length_delimited);
|
||||||
|
|
||||||
|
return has_next ? layer{m_tile_reader.get_view()} : layer{};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the layer iterator. The next time next_layer() is called,
|
||||||
|
* it will begin from the first layer again.
|
||||||
|
*
|
||||||
|
* Complexity: Constant.
|
||||||
|
*/
|
||||||
|
void reset_layer() noexcept {
|
||||||
|
m_tile_reader = protozero::pbf_message<detail::pbf_tile>{m_data};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call a function for each layer in this tile.
|
||||||
|
*
|
||||||
|
* @tparam The type of the function. It must take a single argument
|
||||||
|
* of type layer&& and return a bool. If the function returns
|
||||||
|
* false, the iteration will be stopped.
|
||||||
|
* @param func The function to call.
|
||||||
|
* @returns true if the iteration was completed and false otherwise.
|
||||||
|
*/
|
||||||
|
template <typename TFunc>
|
||||||
|
bool for_each_layer(TFunc&& func) const {
|
||||||
|
protozero::pbf_message<detail::pbf_tile> tile_reader{m_data};
|
||||||
|
|
||||||
|
while (tile_reader.next(detail::pbf_tile::layers,
|
||||||
|
protozero::pbf_wire_type::length_delimited)) {
|
||||||
|
if (!std::forward<TFunc>(func)(layer{tile_reader.get_view()})) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the layer with the specified zero-based index.
|
||||||
|
*
|
||||||
|
* Complexity: Linear in the number of layers.
|
||||||
|
*
|
||||||
|
* @returns The specified layer or the invalid layer if index is
|
||||||
|
* larger than the number of layers.
|
||||||
|
* @throws format_exception if the tile data is ill-formed.
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
*/
|
||||||
|
layer get_layer(std::size_t index) const {
|
||||||
|
protozero::pbf_message<detail::pbf_tile> tile_reader{m_data};
|
||||||
|
|
||||||
|
while (tile_reader.next(detail::pbf_tile::layers,
|
||||||
|
protozero::pbf_wire_type::length_delimited)) {
|
||||||
|
if (index == 0) {
|
||||||
|
return layer{tile_reader.get_view()};
|
||||||
|
}
|
||||||
|
tile_reader.skip();
|
||||||
|
--index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return layer{};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the layer with the specified name.
|
||||||
|
*
|
||||||
|
* Complexity: Linear in the number of layers.
|
||||||
|
*
|
||||||
|
* If there are several layers with the same name (which is against
|
||||||
|
* the spec 4.1 "A Vector Tile MUST NOT contain two or more layers
|
||||||
|
* whose name values are byte-for-byte identical.") it is unspecified
|
||||||
|
* which will be returned.
|
||||||
|
*
|
||||||
|
* @returns The specified layer or the invalid layer if there is no
|
||||||
|
* layer with this name.
|
||||||
|
* @throws format_exception if the tile data is ill-formed.
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
*/
|
||||||
|
layer get_layer_by_name(const data_view name) const {
|
||||||
|
protozero::pbf_message<detail::pbf_tile> tile_reader{m_data};
|
||||||
|
|
||||||
|
while (tile_reader.next(detail::pbf_tile::layers,
|
||||||
|
protozero::pbf_wire_type::length_delimited)) {
|
||||||
|
const auto layer_data = tile_reader.get_view();
|
||||||
|
protozero::pbf_message<detail::pbf_layer> layer_reader{layer_data};
|
||||||
|
if (layer_reader.next(detail::pbf_layer::name,
|
||||||
|
protozero::pbf_wire_type::length_delimited)) {
|
||||||
|
if (layer_reader.get_view() == name) {
|
||||||
|
return layer{layer_data};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 4.1 "A layer MUST contain a name field."
|
||||||
|
throw format_exception{"missing name in layer (spec 4.1)"};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return layer{};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the layer with the specified name.
|
||||||
|
*
|
||||||
|
* Complexity: Linear in the number of layers.
|
||||||
|
*
|
||||||
|
* If there are several layers with the same name (which is against
|
||||||
|
* the spec 4.1 "A Vector Tile MUST NOT contain two or more layers
|
||||||
|
* whose name values are byte-for-byte identical.") it is unspecified
|
||||||
|
* which will be returned.
|
||||||
|
*
|
||||||
|
* @returns The specified layer or the invalid layer if there is no
|
||||||
|
* layer with this name.
|
||||||
|
* @throws format_exception if the tile data is ill-formed.
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
*/
|
||||||
|
layer get_layer_by_name(const std::string& name) const {
|
||||||
|
return get_layer_by_name(data_view{name.data(), name.size()});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the layer with the specified name.
|
||||||
|
*
|
||||||
|
* Complexity: Linear in the number of layers.
|
||||||
|
*
|
||||||
|
* If there are several layers with the same name (which is against
|
||||||
|
* the spec 4.1 "A Vector Tile MUST NOT contain two or more layers
|
||||||
|
* whose name values are byte-for-byte identical.") it is unspecified
|
||||||
|
* which will be returned.
|
||||||
|
*
|
||||||
|
* @returns The specified layer or the invalid layer if there is no
|
||||||
|
* layer with this name.
|
||||||
|
* @throws format_exception if the tile data is ill-formed.
|
||||||
|
* @throws any protozero exception if the protobuf encoding is invalid.
|
||||||
|
*/
|
||||||
|
layer get_layer_by_name(const char* name) const {
|
||||||
|
return get_layer_by_name(data_view{name, std::strlen(name)});
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class vector_tile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to determine whether some data could represent a
|
||||||
|
* vector tile. This takes advantage of the fact that the first byte of
|
||||||
|
* a vector tile is always 0x1a. It can't be 100% reliable though, because
|
||||||
|
* some other data could still contain that byte.
|
||||||
|
*
|
||||||
|
* @returns false if this is definitely no vector tile
|
||||||
|
* true if this could be a vector tile
|
||||||
|
*/
|
||||||
|
inline bool is_vector_tile(const data_view data) noexcept {
|
||||||
|
return !data.empty() && data.data()[0] == 0x1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace vtzero
|
||||||
|
|
||||||
|
#endif // VTZERO_VECTOR_TILE_HPP
|
36
include/vtzero/version.hpp
Normal file
36
include/vtzero/version.hpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef VTZERO_VERSION_HPP
|
||||||
|
#define VTZERO_VERSION_HPP
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
|
||||||
|
vtzero - Tiny and fast vector tile decoder and encoder in C++.
|
||||||
|
|
||||||
|
This file is from https://github.com/mapbox/vtzero where you can find more
|
||||||
|
documentation.
|
||||||
|
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file version.hpp
|
||||||
|
*
|
||||||
|
* @brief Contains the version number macros for the vtzero library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// The major version number
|
||||||
|
#define VTZERO_VERSION_MAJOR 1
|
||||||
|
|
||||||
|
/// The minor version number
|
||||||
|
#define VTZERO_VERSION_MINOR 0
|
||||||
|
|
||||||
|
/// The patch number
|
||||||
|
#define VTZERO_VERSION_PATCH 1
|
||||||
|
|
||||||
|
/// The complete version number
|
||||||
|
#define VTZERO_VERSION_CODE \
|
||||||
|
(VTZERO_VERSION_MAJOR * 10000 + VTZERO_VERSION_MINOR * 100 + \
|
||||||
|
VTZERO_VERSION_PATCH)
|
||||||
|
|
||||||
|
/// Version number as string
|
||||||
|
#define VTZERO_VERSION_STRING "1.0.1"
|
||||||
|
|
||||||
|
#endif // VTZERO_VERSION_HPP
|
81
test/CMakeLists.txt
Normal file
81
test/CMakeLists.txt
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# CMake config
|
||||||
|
#
|
||||||
|
# vtzero tests
|
||||||
|
#
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if(Boost_FOUND)
|
||||||
|
message(STATUS "Boost library found: enable testing with boost::variant")
|
||||||
|
add_definitions(-DVTZERO_TEST_WITH_VARIANT)
|
||||||
|
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
|
||||||
|
else()
|
||||||
|
message(STATUS "Boost library not found: disable testing with boost::variant")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include_directories(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/catch")
|
||||||
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
|
|
||||||
|
set(TEST_SOURCES builder
|
||||||
|
builder_linestring
|
||||||
|
builder_point
|
||||||
|
builder_polygon
|
||||||
|
exceptions
|
||||||
|
feature
|
||||||
|
geometry
|
||||||
|
geometry_linestring
|
||||||
|
geometry_point
|
||||||
|
geometry_polygon
|
||||||
|
index
|
||||||
|
layer
|
||||||
|
output
|
||||||
|
point
|
||||||
|
property_map
|
||||||
|
property_value
|
||||||
|
types
|
||||||
|
vector_tile)
|
||||||
|
|
||||||
|
string(REGEX REPLACE "([^;]+)" "t/test_\\1.cpp" _test_sources "${TEST_SOURCES}")
|
||||||
|
|
||||||
|
add_executable(unit-tests test_main.cpp ${_test_sources})
|
||||||
|
|
||||||
|
add_executable(fixture-tests test_main.cpp fixture_tests.cpp)
|
||||||
|
|
||||||
|
add_test(NAME unit-tests
|
||||||
|
COMMAND unit-tests
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
set(_fixtures ${MVT_FIXTURES}/fixtures)
|
||||||
|
if(EXISTS ${_fixtures})
|
||||||
|
message(STATUS "Found test fixtures. Enabled mvt fixture tests.")
|
||||||
|
add_test(NAME fixture-tests
|
||||||
|
COMMAND fixture-tests)
|
||||||
|
|
||||||
|
set_tests_properties(fixture-tests PROPERTIES
|
||||||
|
ENVIRONMENT "FIXTURES_DIR=${_fixtures}")
|
||||||
|
|
||||||
|
if(NOT WIN32)
|
||||||
|
set(_real_world_dir ${MVT_FIXTURES}/real-world)
|
||||||
|
file(GLOB real_world ${_real_world_dir}/bangkok/*
|
||||||
|
${_real_world_dir}/chicago/*
|
||||||
|
${_real_world_dir}/nepal/*
|
||||||
|
${_real_world_dir}/norway/*
|
||||||
|
${_real_world_dir}/sanfrancisco/*
|
||||||
|
${_real_world_dir}/uruguay/*)
|
||||||
|
|
||||||
|
if(NOT "${real_world}" STREQUAL "")
|
||||||
|
execute_process(COMMAND cat ${real_world}
|
||||||
|
OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/real-world-all.mvt)
|
||||||
|
|
||||||
|
add_test(NAME vtzero-show-real-world
|
||||||
|
COMMAND vtzero-show ${CMAKE_BINARY_DIR}/test/real-world-all.mvt)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(WARNING "Disabled mvt fixture tests, because fixtures not found.\n Install them by calling 'git submodule update --init' in ${CMAKE_SOURCE_DIR}.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
11678
test/catch/catch.hpp
Normal file
11678
test/catch/catch.hpp
Normal file
File diff suppressed because it is too large
Load Diff
BIN
test/data/mapbox-streets-v6-14-8714-8017.mvt
Normal file
BIN
test/data/mapbox-streets-v6-14-8714-8017.mvt
Normal file
Binary file not shown.
1003
test/fixture_tests.cpp
Normal file
1003
test/fixture_tests.cpp
Normal file
File diff suppressed because it is too large
Load Diff
37
test/include/test.hpp
Normal file
37
test/include/test.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef TEST_HPP
|
||||||
|
#define TEST_HPP
|
||||||
|
|
||||||
|
#include <catch.hpp>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
// Define vtzero_assert() to throw this error. This allows the tests to
|
||||||
|
// check that the assert fails.
|
||||||
|
struct assert_error : public std::runtime_error {
|
||||||
|
explicit assert_error(const char* what_arg) : std::runtime_error(what_arg) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define vtzero_assert(x) if (!(x)) { throw assert_error{#x}; }
|
||||||
|
|
||||||
|
#define vtzero_assert_in_noexcept_function(x) if (!(x)) { got_an_assert = true; }
|
||||||
|
|
||||||
|
extern bool got_an_assert;
|
||||||
|
|
||||||
|
#define REQUIRE_ASSERT(x) x; REQUIRE(got_an_assert); got_an_assert = false;
|
||||||
|
|
||||||
|
#include <vtzero/output.hpp>
|
||||||
|
|
||||||
|
std::string load_test_tile();
|
||||||
|
|
||||||
|
struct mypoint {
|
||||||
|
int64_t p1;
|
||||||
|
int64_t p2;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline vtzero::point create_vtzero_point(mypoint p) noexcept {
|
||||||
|
return {static_cast<int32_t>(p.p1),
|
||||||
|
static_cast<int32_t>(p.p2)};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TEST_HPP
|
1
test/mvt-fixtures
Submodule
1
test/mvt-fixtures
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 6ff9cefd5996abfdede6375a86bb2238803dabb9
|
408
test/t/test_builder.cpp
Normal file
408
test/t/test_builder.cpp
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/builder.hpp>
|
||||||
|
#include <vtzero/index.hpp>
|
||||||
|
#include <vtzero/output.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct movable_not_copyable {
|
||||||
|
constexpr static bool value = !std::is_copy_constructible<T>::value &&
|
||||||
|
!std::is_copy_assignable<T>::value &&
|
||||||
|
std::is_nothrow_move_constructible<T>::value &&
|
||||||
|
std::is_nothrow_move_assignable<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(movable_not_copyable<vtzero::tile_builder>::value, "tile_builder should be nothrow movable, but not copyable");
|
||||||
|
static_assert(movable_not_copyable<vtzero::point_feature_builder>::value, "point_feature_builder should be nothrow movable, but not copyable");
|
||||||
|
static_assert(movable_not_copyable<vtzero::linestring_feature_builder>::value, "linestring_feature_builder should be nothrow movable, but not copyable");
|
||||||
|
static_assert(movable_not_copyable<vtzero::polygon_feature_builder>::value, "polygon_feature_builder should be nothrow movable, but not copyable");
|
||||||
|
static_assert(movable_not_copyable<vtzero::geometry_feature_builder>::value, "geometry_feature_builder should be nothrow movable, but not copyable");
|
||||||
|
|
||||||
|
TEST_CASE("Create tile from existing layers") {
|
||||||
|
const auto buffer = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{buffer};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
|
||||||
|
SECTION("add_existing_layer(layer)") {
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
tbuilder.add_existing_layer(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("add_existing_layer(data_view)") {
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
tbuilder.add_existing_layer(layer.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
REQUIRE(data == buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Create layer based on existing layer") {
|
||||||
|
const auto buffer = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{buffer};
|
||||||
|
const auto layer = tile.get_layer_by_name("place_label");
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, layer};
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(42);
|
||||||
|
fbuilder.add_point(10, 20);
|
||||||
|
fbuilder.commit();
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
vtzero::vector_tile new_tile{data};
|
||||||
|
const auto new_layer = new_tile.next_layer();
|
||||||
|
REQUIRE(std::string(new_layer.name()) == "place_label");
|
||||||
|
REQUIRE(new_layer.version() == 1);
|
||||||
|
REQUIRE(new_layer.extent() == 4096);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Create layer and add keys/values") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "name"};
|
||||||
|
|
||||||
|
const auto ki1 = lbuilder.add_key_without_dup_check("key1");
|
||||||
|
const auto ki2 = lbuilder.add_key("key2");
|
||||||
|
const auto ki3 = lbuilder.add_key("key1");
|
||||||
|
|
||||||
|
REQUIRE(ki1 != ki2);
|
||||||
|
REQUIRE(ki1 == ki3);
|
||||||
|
|
||||||
|
const auto vi1 = lbuilder.add_value_without_dup_check(vtzero::encoded_property_value{"value1"});
|
||||||
|
vtzero::encoded_property_value value2{"value2"};
|
||||||
|
const auto vi2 = lbuilder.add_value_without_dup_check(vtzero::property_value{value2.data()});
|
||||||
|
|
||||||
|
const auto vi3 = lbuilder.add_value(vtzero::encoded_property_value{"value1"});
|
||||||
|
const auto vi4 = lbuilder.add_value(vtzero::encoded_property_value{19});
|
||||||
|
const auto vi5 = lbuilder.add_value(vtzero::encoded_property_value{19.0});
|
||||||
|
const auto vi6 = lbuilder.add_value(vtzero::encoded_property_value{22});
|
||||||
|
vtzero::encoded_property_value nineteen{19};
|
||||||
|
const auto vi7 = lbuilder.add_value(vtzero::property_value{nineteen.data()});
|
||||||
|
|
||||||
|
REQUIRE(vi1 != vi2);
|
||||||
|
REQUIRE(vi1 == vi3);
|
||||||
|
REQUIRE(vi1 != vi4);
|
||||||
|
REQUIRE(vi1 != vi5);
|
||||||
|
REQUIRE(vi1 != vi6);
|
||||||
|
REQUIRE(vi4 != vi5);
|
||||||
|
REQUIRE(vi4 != vi6);
|
||||||
|
REQUIRE(vi4 == vi7);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Committing a feature succeeds after a geometry was added") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
{ // explicit commit after geometry
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(1);
|
||||||
|
fbuilder.add_point(10, 10);
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // explicit commit after properties
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(2);
|
||||||
|
fbuilder.add_point(10, 10);
|
||||||
|
fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // extra commits or rollbacks are okay but no other calls
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(3);
|
||||||
|
fbuilder.add_point(10, 10);
|
||||||
|
fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
|
||||||
|
fbuilder.commit();
|
||||||
|
|
||||||
|
SECTION("superfluous commit()") {
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
SECTION("superfluous rollback()") {
|
||||||
|
fbuilder.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_id(10), const assert_error&);
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_point(20, 20), const assert_error&);
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_property("x", "y"), const assert_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
|
||||||
|
uint64_t n = 1;
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
REQUIRE(feature.id() == n++);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(n == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Committing a feature fails with assert if no geometry was added") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
SECTION("explicit immediate commit") {
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.commit(), const assert_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("explicit commit after setting id") {
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(2);
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.commit(), const assert_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Rollback feature") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(1);
|
||||||
|
fbuilder.add_point(10, 10);
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // immediate rollback
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(2);
|
||||||
|
fbuilder.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // rollback after setting id
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(3);
|
||||||
|
fbuilder.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // rollback after geometry
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(4);
|
||||||
|
fbuilder.add_point(20, 20);
|
||||||
|
fbuilder.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // rollback after properties
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(5);
|
||||||
|
fbuilder.add_point(20, 20);
|
||||||
|
fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
|
||||||
|
fbuilder.rollback();
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // implicit rollback after geometry
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(6);
|
||||||
|
fbuilder.add_point(10, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // implicit rollback after properties
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(7);
|
||||||
|
fbuilder.add_point(10, 10);
|
||||||
|
fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(8);
|
||||||
|
fbuilder.add_point(30, 30);
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
|
||||||
|
auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == 1);
|
||||||
|
|
||||||
|
feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == 8);
|
||||||
|
|
||||||
|
feature = layer.next_feature();
|
||||||
|
REQUIRE_FALSE(feature);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool vector_tile_equal(const std::string& t1, const std::string& t2) {
|
||||||
|
vtzero::vector_tile vt1{t1};
|
||||||
|
vtzero::vector_tile vt2{t2};
|
||||||
|
|
||||||
|
for (auto l1 = vt1.next_layer(), l2 = vt2.next_layer();
|
||||||
|
l1 && l2;
|
||||||
|
l1 = vt1.next_layer(), l2 = vt2.next_layer()) {
|
||||||
|
if (l1.empty()) {
|
||||||
|
l1 = vt1.next_layer();
|
||||||
|
if (!l1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (l2.empty()) {
|
||||||
|
l2 = vt2.next_layer();
|
||||||
|
if (!l2) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!l1 ||
|
||||||
|
!l2 ||
|
||||||
|
l1.version() != l2.version() ||
|
||||||
|
l1.extent() != l2.extent() ||
|
||||||
|
l1.num_features() != l2.num_features() ||
|
||||||
|
l1.name() != l2.name()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto f1 = l1.next_feature(), f2 = l2.next_feature();
|
||||||
|
f1 && f2;
|
||||||
|
f1 = l1.next_feature(), f2 = l2.next_feature()) {
|
||||||
|
if (f1.id() != f2.id() ||
|
||||||
|
f1.geometry_type() != f2.geometry_type() ||
|
||||||
|
f1.num_properties() != f2.num_properties() ||
|
||||||
|
f1.geometry().data() != f2.geometry().data()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (auto p1 = f1.next_property(), p2 = f2.next_property();
|
||||||
|
p1 && p2;
|
||||||
|
p1 = f1.next_property(), p2 = f2.next_property()) {
|
||||||
|
if (p1.key() != p2.key() || p1.value() != p2.value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Copy tile") {
|
||||||
|
const auto buffer = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{buffer};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, layer};
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
lbuilder.add_feature(feature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
REQUIRE(vector_tile_equal(buffer, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Copy tile using geometry_feature_builder") {
|
||||||
|
const auto buffer = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{buffer};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, layer};
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
vtzero::geometry_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(feature.id());
|
||||||
|
fbuilder.set_geometry(feature.geometry());
|
||||||
|
while (auto property = feature.next_property()) {
|
||||||
|
fbuilder.add_property(property.key(), property.value());
|
||||||
|
}
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
REQUIRE(vector_tile_equal(buffer, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Copy only point geometries using geometry_feature_builder") {
|
||||||
|
const auto buffer = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{buffer};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, layer};
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
vtzero::geometry_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(feature.id());
|
||||||
|
if (feature.geometry().type() == vtzero::GeomType::POINT) {
|
||||||
|
fbuilder.set_geometry(feature.geometry());
|
||||||
|
while (auto property = feature.next_property()) {
|
||||||
|
fbuilder.add_property(property.key(), property.value());
|
||||||
|
}
|
||||||
|
fbuilder.commit();
|
||||||
|
++n;
|
||||||
|
} else {
|
||||||
|
fbuilder.rollback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
REQUIRE(n == 17);
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
vtzero::vector_tile result_tile{data};
|
||||||
|
while (auto layer = result_tile.next_layer()) {
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
++n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(n == 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Build point feature from container with too many points") {
|
||||||
|
|
||||||
|
// fake container pretending to contain too many points
|
||||||
|
struct test_container {
|
||||||
|
|
||||||
|
std::size_t size() const noexcept {
|
||||||
|
return 1ul << 29u;
|
||||||
|
}
|
||||||
|
|
||||||
|
vtzero::point* begin() const noexcept {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
vtzero::point* end() const noexcept {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.set_id(1);
|
||||||
|
|
||||||
|
test_container tc;
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_points_from_container(tc), const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Moving a feature builder is allowed") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
auto fbuilder2 = std::move(fbuilder);
|
||||||
|
vtzero::point_feature_builder fbuilder3{std::move(fbuilder2)};
|
||||||
|
}
|
||||||
|
|
312
test/t/test_builder_linestring.cpp
Normal file
312
test/t/test_builder_linestring.cpp
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/builder.hpp>
|
||||||
|
#include <vtzero/geometry.hpp>
|
||||||
|
#include <vtzero/index.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using ls_type = std::vector<std::vector<vtzero::point>>;
|
||||||
|
|
||||||
|
struct linestring_handler {
|
||||||
|
|
||||||
|
ls_type data;
|
||||||
|
|
||||||
|
void linestring_begin(uint32_t count) {
|
||||||
|
data.emplace_back();
|
||||||
|
data.back().reserve(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_point(const vtzero::point point) {
|
||||||
|
data.back().push_back(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_end() const noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_linestring_builder(bool with_id, bool with_prop) {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
if (with_id) {
|
||||||
|
fbuilder.set_id(17);
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.add_linestring(3);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
fbuilder.set_point(vtzero::point{20, 30});
|
||||||
|
fbuilder.set_point(mypoint{30, 40});
|
||||||
|
|
||||||
|
if (with_prop) {
|
||||||
|
fbuilder.add_property("foo", "bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer.name() == "test");
|
||||||
|
REQUIRE(layer.version() == 2);
|
||||||
|
REQUIRE(layer.extent() == 4096);
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
|
||||||
|
const auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == (with_id ? 17 : 0));
|
||||||
|
|
||||||
|
linestring_handler handler;
|
||||||
|
vtzero::decode_linestring_geometry(feature.geometry(), handler);
|
||||||
|
|
||||||
|
const ls_type result = {{{10, 20}, {20, 30}, {30, 40}}};
|
||||||
|
REQUIRE(handler.data == result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("linestring builder without id/without properties") {
|
||||||
|
test_linestring_builder(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("linestring builder without id/with properties") {
|
||||||
|
test_linestring_builder(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("linestring builder with id/without properties") {
|
||||||
|
test_linestring_builder(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("linestring builder with id/with properties") {
|
||||||
|
test_linestring_builder(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling add_linestring() with bad values throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
SECTION("0") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_linestring(0), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("1") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_linestring(1), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("2^29") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_linestring(1ul << 29u), const assert_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_multilinestring_builder(bool with_id, bool with_prop) {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
if (with_id) {
|
||||||
|
fbuilder.set_id(17);
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.add_linestring(3);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
fbuilder.set_point(vtzero::point{20, 30});
|
||||||
|
fbuilder.set_point(mypoint{30, 40});
|
||||||
|
|
||||||
|
fbuilder.add_linestring(2);
|
||||||
|
fbuilder.set_point(1, 2);
|
||||||
|
fbuilder.set_point(2, 1);
|
||||||
|
|
||||||
|
if (with_prop) {
|
||||||
|
fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer.name() == "test");
|
||||||
|
REQUIRE(layer.version() == 2);
|
||||||
|
REQUIRE(layer.extent() == 4096);
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
|
||||||
|
const auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == (with_id ? 17 : 0));
|
||||||
|
|
||||||
|
linestring_handler handler;
|
||||||
|
vtzero::decode_linestring_geometry(feature.geometry(), handler);
|
||||||
|
|
||||||
|
const ls_type result = {{{10, 20}, {20, 30}, {30, 40}}, {{1, 2}, {2, 1}}};
|
||||||
|
REQUIRE(handler.data == result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("Multilinestring builder without id/without properties") {
|
||||||
|
test_multilinestring_builder(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multilinestring builder without id/with properties") {
|
||||||
|
test_multilinestring_builder(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multilinestring builder with id/without properties") {
|
||||||
|
test_multilinestring_builder(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multilinestring builder with id/with properties") {
|
||||||
|
test_multilinestring_builder(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling add_linestring() twice throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.add_linestring(3);
|
||||||
|
REQUIRE_ASSERT(fbuilder.add_linestring(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling linestring_feature_builder::set_point() throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_point(10, 10), const assert_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling linestring_feature_builder::set_point() with same point throws") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.add_linestring(2);
|
||||||
|
fbuilder.set_point(10, 10);
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_point(10, 10), const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling linestring_feature_builder::set_point() too often throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.add_linestring(2);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
fbuilder.set_point(20, 20);
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_point(30, 20), const assert_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Add linestring from container") {
|
||||||
|
const ls_type points = {{{10, 20}, {20, 30}, {30, 40}}};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
SECTION("using iterators") {
|
||||||
|
fbuilder.add_linestring(points[0].cbegin(), points[0].cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("using iterators and size") {
|
||||||
|
fbuilder.add_linestring(points[0].cbegin(), points[0].cend(), static_cast<uint32_t>(points[0].size()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SECTION("using container directly") {
|
||||||
|
fbuilder.add_linestring_from_container(points[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer);
|
||||||
|
REQUIRE(layer.name() == "test");
|
||||||
|
REQUIRE(layer.version() == 2);
|
||||||
|
REQUIRE(layer.extent() == 4096);
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
|
||||||
|
const auto feature = layer.next_feature();
|
||||||
|
|
||||||
|
linestring_handler handler;
|
||||||
|
vtzero::decode_linestring_geometry(feature.geometry(), handler);
|
||||||
|
|
||||||
|
REQUIRE(handler.data == points);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
TEST_CASE("Add linestring from iterator with wrong count throws assert") {
|
||||||
|
const std::vector<vtzero::point> points = {{10, 20}, {20, 30}, {30, 40}};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_linestring(points.cbegin(),
|
||||||
|
points.cend(),
|
||||||
|
static_cast<uint32_t>(points.size() + 1)), const assert_error&);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("Adding several linestrings with feature rollback in the middle") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(1);
|
||||||
|
fbuilder.add_linestring(2);
|
||||||
|
fbuilder.set_point(10, 10);
|
||||||
|
fbuilder.set_point(20, 20);
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(2);
|
||||||
|
fbuilder.add_linestring(2);
|
||||||
|
fbuilder.set_point(10, 10);
|
||||||
|
fbuilder.set_point(10, 10);
|
||||||
|
fbuilder.commit();
|
||||||
|
} catch (vtzero::geometry_exception&) {
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::linestring_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(3);
|
||||||
|
fbuilder.add_linestring(2);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
fbuilder.set_point(20, 10);
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer);
|
||||||
|
REQUIRE(layer.name() == "test");
|
||||||
|
REQUIRE(layer.num_features() == 2);
|
||||||
|
|
||||||
|
auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == 1);
|
||||||
|
feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == 3);
|
||||||
|
}
|
||||||
|
|
279
test/t/test_builder_point.cpp
Normal file
279
test/t/test_builder_point.cpp
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/builder.hpp>
|
||||||
|
#include <vtzero/geometry.hpp>
|
||||||
|
#include <vtzero/index.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct point_handler {
|
||||||
|
|
||||||
|
std::vector<vtzero::point> data;
|
||||||
|
|
||||||
|
void points_begin(uint32_t count) {
|
||||||
|
data.reserve(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void points_point(const vtzero::point point) {
|
||||||
|
data.push_back(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
void points_end() const noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_point_builder(bool with_id, bool with_prop) {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
if (with_id) {
|
||||||
|
fbuilder.set_id(17);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("add point using coordinates / property using key/value") {
|
||||||
|
fbuilder.add_point(10, 20);
|
||||||
|
if (with_prop) {
|
||||||
|
fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("add point using vtzero::point / property using key/value") {
|
||||||
|
fbuilder.add_point(vtzero::point{10, 20});
|
||||||
|
if (with_prop) {
|
||||||
|
fbuilder.add_property("foo", vtzero::encoded_property_value{22});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("add point using mypoint / property using property") {
|
||||||
|
vtzero::encoded_property_value pv{3.5};
|
||||||
|
vtzero::property p{"foo", vtzero::property_value{pv.data()}};
|
||||||
|
fbuilder.add_point(mypoint{10, 20});
|
||||||
|
if (with_prop) {
|
||||||
|
fbuilder.add_property(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer.name() == "test");
|
||||||
|
REQUIRE(layer.version() == 2);
|
||||||
|
REQUIRE(layer.extent() == 4096);
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
|
||||||
|
const auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == (with_id ? 17 : 0));
|
||||||
|
|
||||||
|
point_handler handler;
|
||||||
|
vtzero::decode_point_geometry(feature.geometry(), handler);
|
||||||
|
|
||||||
|
const std::vector<vtzero::point> result = {{10, 20}};
|
||||||
|
REQUIRE(handler.data == result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Point builder without id/without properties") {
|
||||||
|
test_point_builder(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Point builder without id/with properties") {
|
||||||
|
test_point_builder(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Point builder with id/without properties") {
|
||||||
|
test_point_builder(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Point builder with id/with properties") {
|
||||||
|
test_point_builder(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling add_points() with bad values throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
SECTION("0") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_points(0), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("2^29") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_points(1ul << 29u), const assert_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_multipoint_builder(bool with_id, bool with_prop) {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
if (with_id) {
|
||||||
|
fbuilder.set_id(17);
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.add_points(3);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
fbuilder.set_point(vtzero::point{20, 30});
|
||||||
|
fbuilder.set_point(mypoint{30, 40});
|
||||||
|
|
||||||
|
if (with_prop) {
|
||||||
|
fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer.name() == "test");
|
||||||
|
REQUIRE(layer.version() == 2);
|
||||||
|
REQUIRE(layer.extent() == 4096);
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
|
||||||
|
const auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == (with_id ? 17 : 0));
|
||||||
|
|
||||||
|
point_handler handler;
|
||||||
|
vtzero::decode_point_geometry(feature.geometry(), handler);
|
||||||
|
|
||||||
|
const std::vector<vtzero::point> result = {{10, 20}, {20, 30}, {30, 40}};
|
||||||
|
REQUIRE(handler.data == result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("Multipoint builder without id/without properties") {
|
||||||
|
test_multipoint_builder(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multipoint builder without id/with properties") {
|
||||||
|
test_multipoint_builder(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multipoint builder with id/without properties") {
|
||||||
|
test_multipoint_builder(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multipoint builder with id/with properties") {
|
||||||
|
test_multipoint_builder(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling add_point() and then other geometry functions throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.add_point(10, 20);
|
||||||
|
|
||||||
|
SECTION("add_point()") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_point(10, 20), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("add_points()") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_points(2), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("set_point()") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_point(10, 10), const assert_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling point_feature_builder::set_point() throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_point(10, 10), const assert_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling add_points() and then other geometry functions throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.add_points(2);
|
||||||
|
|
||||||
|
SECTION("add_point()") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_point(10, 20), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("add_points()") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_points(2), const assert_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling point_feature_builder::set_point() too often throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.add_points(2);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
fbuilder.set_point(20, 20);
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_point(30, 20), const assert_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Add points from container") {
|
||||||
|
const std::vector<vtzero::point> points = {{10, 20}, {20, 30}, {30, 40}};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
/* SECTION("using iterators") {
|
||||||
|
fbuilder.add_points(points.cbegin(), points.cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("using iterators and size") {
|
||||||
|
fbuilder.add_points(points.cbegin(), points.cend(), static_cast<uint32_t>(points.size()));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
SECTION("using container directly") {
|
||||||
|
fbuilder.add_points_from_container(points);
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer);
|
||||||
|
REQUIRE(layer.name() == "test");
|
||||||
|
REQUIRE(layer.version() == 2);
|
||||||
|
REQUIRE(layer.extent() == 4096);
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
|
||||||
|
const auto feature = layer.next_feature();
|
||||||
|
|
||||||
|
point_handler handler;
|
||||||
|
vtzero::decode_point_geometry(feature.geometry(), handler);
|
||||||
|
|
||||||
|
REQUIRE(handler.data == points);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
TEST_CASE("Add points from iterator with wrong count throws assert") {
|
||||||
|
const std::vector<vtzero::point> points = {{10, 20}, {20, 30}, {30, 40}};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_points(points.cbegin(),
|
||||||
|
points.cend(),
|
||||||
|
static_cast<uint32_t>(points.size() + 1)), const assert_error&);
|
||||||
|
}*/
|
||||||
|
|
307
test/t/test_builder_polygon.cpp
Normal file
307
test/t/test_builder_polygon.cpp
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/builder.hpp>
|
||||||
|
#include <vtzero/geometry.hpp>
|
||||||
|
#include <vtzero/index.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using polygon_type = std::vector<std::vector<vtzero::point>>;
|
||||||
|
|
||||||
|
struct polygon_handler {
|
||||||
|
|
||||||
|
polygon_type data;
|
||||||
|
|
||||||
|
void ring_begin(uint32_t count) {
|
||||||
|
data.emplace_back();
|
||||||
|
data.back().reserve(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_point(const vtzero::point point) {
|
||||||
|
data.back().push_back(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_end(vtzero::ring_type /* type */) const noexcept {
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_polygon_builder(bool with_id, bool with_prop) {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
if (with_id) {
|
||||||
|
fbuilder.set_id(17);
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.add_ring(4);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
fbuilder.set_point(vtzero::point{20, 30});
|
||||||
|
fbuilder.set_point(mypoint{30, 40});
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
|
||||||
|
if (with_prop) {
|
||||||
|
fbuilder.add_property("foo", "bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer.name() == "test");
|
||||||
|
REQUIRE(layer.version() == 2);
|
||||||
|
REQUIRE(layer.extent() == 4096);
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
|
||||||
|
const auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == (with_id ? 17 : 0));
|
||||||
|
|
||||||
|
polygon_handler handler;
|
||||||
|
vtzero::decode_polygon_geometry(feature.geometry(), handler);
|
||||||
|
|
||||||
|
const polygon_type result = {{{10, 20}, {20, 30}, {30, 40}, {10, 20}}};
|
||||||
|
REQUIRE(handler.data == result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("polygon builder without id/without properties") {
|
||||||
|
test_polygon_builder(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("polygon builder without id/with properties") {
|
||||||
|
test_polygon_builder(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("polygon builder with id/without properties") {
|
||||||
|
test_polygon_builder(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("polygon builder with id/with properties") {
|
||||||
|
test_polygon_builder(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling add_ring() with bad values throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
SECTION("0") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_ring(0), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("1") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_ring(1), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("2") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_ring(2), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("3") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_ring(3), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("2^29") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_ring(1ul << 29u), const assert_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_multipolygon_builder(bool with_id, bool with_prop) {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
if (with_id) {
|
||||||
|
fbuilder.set_id(17);
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.add_ring(4);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
fbuilder.set_point(vtzero::point{20, 30});
|
||||||
|
fbuilder.set_point(mypoint{30, 40});
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
|
||||||
|
fbuilder.add_ring(5);
|
||||||
|
fbuilder.set_point(1, 1);
|
||||||
|
fbuilder.set_point(2, 1);
|
||||||
|
fbuilder.set_point(2, 2);
|
||||||
|
fbuilder.set_point(1, 2);
|
||||||
|
|
||||||
|
if (with_id) {
|
||||||
|
fbuilder.set_point(1, 1);
|
||||||
|
} else {
|
||||||
|
fbuilder.close_ring();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (with_prop) {
|
||||||
|
fbuilder.add_property("foo", vtzero::encoded_property_value{"bar"});
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer.name() == "test");
|
||||||
|
REQUIRE(layer.version() == 2);
|
||||||
|
REQUIRE(layer.extent() == 4096);
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
|
||||||
|
const auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == (with_id ? 17 : 0));
|
||||||
|
|
||||||
|
polygon_handler handler;
|
||||||
|
vtzero::decode_polygon_geometry(feature.geometry(), handler);
|
||||||
|
|
||||||
|
const polygon_type result = {{{10, 20}, {20, 30}, {30, 40}, {10, 20}},
|
||||||
|
{{1, 1}, {2, 1}, {2, 2}, {1, 2}, {1, 1}}};
|
||||||
|
REQUIRE(handler.data == result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("Multipolygon builder without id/without properties") {
|
||||||
|
test_multipolygon_builder(false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multipolygon builder without id/with properties") {
|
||||||
|
test_multipolygon_builder(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multipolygon builder with id/without properties") {
|
||||||
|
test_multipolygon_builder(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Multipolygon builder with id/with properties") {
|
||||||
|
test_multipolygon_builder(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling add_ring() twice throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.add_ring(4);
|
||||||
|
REQUIRE_ASSERT(fbuilder.add_ring(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling polygon_feature_builder::set_point()/close_ring() throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
SECTION("set_point") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_point(10, 10), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("close_ring") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.close_ring(), const assert_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling polygon_feature_builder::set_point()/close_ring() too often throws assert") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.add_ring(4);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
fbuilder.set_point(20, 20);
|
||||||
|
fbuilder.set_point(30, 20);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
|
||||||
|
SECTION("set_point") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_point(50, 20), const assert_error&);
|
||||||
|
}
|
||||||
|
SECTION("close_ring") {
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.close_ring(), const assert_error&);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling polygon_feature_builder::set_point() with same point throws") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.add_ring(4);
|
||||||
|
fbuilder.set_point(10, 10);
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_point(10, 10), const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling polygon_feature_builder::set_point() creating unclosed ring throws") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
fbuilder.add_ring(4);
|
||||||
|
fbuilder.set_point(10, 10);
|
||||||
|
fbuilder.set_point(10, 20);
|
||||||
|
fbuilder.set_point(20, 20);
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.set_point(20, 30), const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Add polygon from container") {
|
||||||
|
const polygon_type points = {{{10, 20}, {20, 30}, {30, 40}, {10, 20}}};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
{
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
SECTION("using iterators") {
|
||||||
|
fbuilder.add_ring(points[0].cbegin(), points[0].cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("using iterators and size") {
|
||||||
|
fbuilder.add_ring(points[0].cbegin(), points[0].cend(), static_cast<uint32_t>(points[0].size()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SECTION("using container directly") {
|
||||||
|
fbuilder.add_ring_from_container(points[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer);
|
||||||
|
REQUIRE(layer.name() == "test");
|
||||||
|
REQUIRE(layer.version() == 2);
|
||||||
|
REQUIRE(layer.extent() == 4096);
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
|
||||||
|
const auto feature = layer.next_feature();
|
||||||
|
|
||||||
|
polygon_handler handler;
|
||||||
|
vtzero::decode_polygon_geometry(feature.geometry(), handler);
|
||||||
|
|
||||||
|
REQUIRE(handler.data == points);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
TEST_CASE("Add polygon from iterator with wrong count throws assert") {
|
||||||
|
const std::vector<vtzero::point> points = {{10, 20}, {20, 30}, {30, 40}, {10, 20}};
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::polygon_feature_builder fbuilder{lbuilder};
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(fbuilder.add_ring(points.cbegin(),
|
||||||
|
points.cend(),
|
||||||
|
static_cast<uint32_t>(points.size() + 1)), const assert_error&);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
42
test/t/test_exceptions.cpp
Normal file
42
test/t/test_exceptions.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/exception.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
TEST_CASE("construct format_exception with const char*") {
|
||||||
|
vtzero::format_exception e{"broken"};
|
||||||
|
REQUIRE(std::string{e.what()} == "broken");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("construct format_exception with const std::string") {
|
||||||
|
vtzero::format_exception e{std::string{"broken"}};
|
||||||
|
REQUIRE(std::string{e.what()} == "broken");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("construct geometry_exception with const char*") {
|
||||||
|
vtzero::geometry_exception e{"broken"};
|
||||||
|
REQUIRE(std::string{e.what()} == "broken");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("construct geometry_exception with std::string") {
|
||||||
|
vtzero::geometry_exception e{std::string{"broken"}};
|
||||||
|
REQUIRE(std::string{e.what()} == "broken");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("construct type_exception") {
|
||||||
|
vtzero::type_exception e;
|
||||||
|
REQUIRE(std::string{e.what()} == "wrong property value type");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("construct version_exception") {
|
||||||
|
vtzero::version_exception e{42};
|
||||||
|
REQUIRE(std::string{e.what()} == "unknown vector tile version: 42");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("construct out_of_range_exception") {
|
||||||
|
vtzero::out_of_range_exception e{99};
|
||||||
|
REQUIRE(std::string{e.what()} == "index out of range: 99");
|
||||||
|
}
|
||||||
|
|
97
test/t/test_feature.cpp
Normal file
97
test/t/test_feature.cpp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/feature.hpp>
|
||||||
|
#include <vtzero/layer.hpp>
|
||||||
|
#include <vtzero/vector_tile.hpp>
|
||||||
|
|
||||||
|
TEST_CASE("default constructed feature") {
|
||||||
|
vtzero::feature feature{};
|
||||||
|
|
||||||
|
REQUIRE_FALSE(feature.valid());
|
||||||
|
REQUIRE_FALSE(feature);
|
||||||
|
REQUIRE(feature.id() == 0);
|
||||||
|
REQUIRE_FALSE(feature.has_id());
|
||||||
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::UNKNOWN);
|
||||||
|
REQUIRE_ASSERT(feature.geometry());
|
||||||
|
REQUIRE(feature.empty());
|
||||||
|
REQUIRE(feature.num_properties() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("read a feature") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.get_layer_by_name("bridge");
|
||||||
|
REQUIRE(layer.valid());
|
||||||
|
|
||||||
|
auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.valid());
|
||||||
|
REQUIRE(feature);
|
||||||
|
REQUIRE(feature.id() == 0);
|
||||||
|
REQUIRE(feature.has_id());
|
||||||
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::LINESTRING);
|
||||||
|
REQUIRE_FALSE(feature.empty());
|
||||||
|
REQUIRE(feature.num_properties() == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("iterate over all properties of a feature") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
auto layer = tile.get_layer_by_name("bridge");
|
||||||
|
auto feature = layer.next_feature();
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
SECTION("external iterator") {
|
||||||
|
while (auto p = feature.next_property()) {
|
||||||
|
++count;
|
||||||
|
if (p.key() == "type") {
|
||||||
|
REQUIRE(p.value().type() == vtzero::property_value_type::string_value);
|
||||||
|
REQUIRE(p.value().string_value() == "primary");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("internal iterator") {
|
||||||
|
feature.for_each_property([&count](const vtzero::property& p) {
|
||||||
|
++count;
|
||||||
|
if (p.key() == "type") {
|
||||||
|
REQUIRE(p.value().type() == vtzero::property_value_type::string_value);
|
||||||
|
REQUIRE(p.value().string_value() == "primary");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(count == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("iterate over some properties of a feature") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
auto layer = tile.get_layer_by_name("bridge");
|
||||||
|
REQUIRE(layer.valid());
|
||||||
|
|
||||||
|
auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.valid());
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
SECTION("external iterator") {
|
||||||
|
while (auto p = feature.next_property()) {
|
||||||
|
++count;
|
||||||
|
if (p.key() == "oneway") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("internal iterator") {
|
||||||
|
feature.for_each_property([&count](const vtzero::property& p) {
|
||||||
|
++count;
|
||||||
|
return p.key() != "oneway";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(count == 2);
|
||||||
|
}
|
||||||
|
|
334
test/t/test_geometry.cpp
Normal file
334
test/t/test_geometry.cpp
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/geometry.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using container = std::vector<uint32_t>;
|
||||||
|
using iterator = container::const_iterator;
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder") {
|
||||||
|
const container g = {};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE(decoder.done());
|
||||||
|
REQUIRE_FALSE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE_THROWS_AS(decoder.next_point(), const assert_error&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with point") {
|
||||||
|
const container g = {9, 50, 34};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(decoder.next_point(), const assert_error&);
|
||||||
|
|
||||||
|
SECTION("trying to get LineTo command") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.next_command(vtzero::detail::CommandId::LINE_TO), const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("trying to get ClosePath command") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.next_command(vtzero::detail::CommandId::CLOSE_PATH), "expected command 7 but got 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("trying to get MoveTo command") {
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE_THROWS_AS(decoder.next_command(vtzero::detail::CommandId::MOVE_TO), const assert_error&);
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(25, 17));
|
||||||
|
|
||||||
|
REQUIRE(decoder.done());
|
||||||
|
REQUIRE_FALSE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with incomplete point") {
|
||||||
|
container g = {9, 50, 34};
|
||||||
|
|
||||||
|
SECTION("half a point") {
|
||||||
|
g.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("missing point") {
|
||||||
|
g.pop_back();
|
||||||
|
g.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), 100};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE_THROWS_AS(decoder.next_point(), const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with multipoint") {
|
||||||
|
const container g = {17, 10, 14, 3, 9};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 2);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(5, 7));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(3, 2));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
|
||||||
|
REQUIRE(decoder.done());
|
||||||
|
REQUIRE_FALSE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with linestring") {
|
||||||
|
const container g = {9, 4, 4, 18, 0, 16, 16, 0};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(2, 2));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.count() == 2);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(2, 10));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(10, 10));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
|
||||||
|
REQUIRE(decoder.done());
|
||||||
|
REQUIRE_FALSE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with linestring with equal points") {
|
||||||
|
const container g = {9, 4, 4, 18, 0, 16, 0, 0};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(2, 2));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.count() == 2);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(2, 10));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(2, 10));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
|
||||||
|
REQUIRE(decoder.done());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with multilinestring") {
|
||||||
|
const container g = {9, 4, 4, 18, 0, 16, 16, 0, 9, 17, 17, 10, 4, 8};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(2, 2));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.count() == 2);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(2, 10));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(10, 10));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(1, 1));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(3, 5));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
|
||||||
|
REQUIRE(decoder.done());
|
||||||
|
REQUIRE_FALSE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with polygon") {
|
||||||
|
const container g = {9, 6, 12, 18, 10, 12, 24, 44, 15};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(3, 6));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.count() == 2);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(8, 12));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(20, 34));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::CLOSE_PATH));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
|
||||||
|
REQUIRE(decoder.done());
|
||||||
|
REQUIRE_FALSE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with polygon with wrong ClosePath count 2") {
|
||||||
|
const container g = {9, 6, 12, 18, 10, 12, 24, 44, 23};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(3, 6));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(8, 12));
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(20, 34));
|
||||||
|
REQUIRE_THROWS_AS(decoder.next_command(vtzero::detail::CommandId::CLOSE_PATH), const vtzero::geometry_exception&);
|
||||||
|
REQUIRE_THROWS_WITH(decoder.next_command(vtzero::detail::CommandId::CLOSE_PATH), "ClosePath command count is not 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with polygon with wrong ClosePath count 0") {
|
||||||
|
const container g = {9, 6, 12, 18, 10, 12, 24, 44, 7};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(3, 6));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(8, 12));
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(20, 34));
|
||||||
|
REQUIRE_THROWS_AS(decoder.next_command(vtzero::detail::CommandId::CLOSE_PATH), const vtzero::geometry_exception&);
|
||||||
|
REQUIRE_THROWS_WITH(decoder.next_command(vtzero::detail::CommandId::CLOSE_PATH), "ClosePath command count is not 1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with multipolygon") {
|
||||||
|
const container g = {9, 0, 0, 26, 20, 0, 0, 20, 19, 0, 15, 9, 22, 2, 26, 18,
|
||||||
|
0, 0, 18, 17, 0, 15, 9, 4, 13, 26, 0, 8, 8, 0, 0, 7, 15};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(0, 0));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.count() == 3);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(10, 0));
|
||||||
|
REQUIRE(decoder.count() == 2);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(10, 10));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(0, 10));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::CLOSE_PATH));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(11, 11));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.count() == 3);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(20, 11));
|
||||||
|
REQUIRE(decoder.count() == 2);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(20, 20));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(11, 20));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::CLOSE_PATH));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(13, 13));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.count() == 3);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(13, 17));
|
||||||
|
REQUIRE(decoder.count() == 2);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(17, 17));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(17, 13));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::CLOSE_PATH));
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
|
||||||
|
REQUIRE(decoder.done());
|
||||||
|
REQUIRE_FALSE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder decoding linestring with int32 overflow in x coordinate") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(1),
|
||||||
|
protozero::encode_zigzag32(std::numeric_limits<int32_t>::max()),
|
||||||
|
protozero::encode_zigzag32(0),
|
||||||
|
vtzero::detail::command_line_to(1),
|
||||||
|
protozero::encode_zigzag32(1),
|
||||||
|
protozero::encode_zigzag32(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(std::numeric_limits<int32_t>::max(), 0));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(std::numeric_limits<int32_t>::min(), 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder decoding linestring with int32 overflow in y coordinate") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(1),
|
||||||
|
protozero::encode_zigzag32(0),
|
||||||
|
protozero::encode_zigzag32(std::numeric_limits<int32_t>::min()),
|
||||||
|
vtzero::detail::command_line_to(1),
|
||||||
|
protozero::encode_zigzag32(-1),
|
||||||
|
protozero::encode_zigzag32(-1)
|
||||||
|
};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::MOVE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(0, std::numeric_limits<int32_t>::min()));
|
||||||
|
REQUIRE(decoder.next_command(vtzero::detail::CommandId::LINE_TO));
|
||||||
|
REQUIRE(decoder.count() == 1);
|
||||||
|
REQUIRE(decoder.next_point() == vtzero::point(-1, std::numeric_limits<int32_t>::max()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with multipoint with a huge count") {
|
||||||
|
const uint32_t huge_value = (1ul << 29u) - 1;
|
||||||
|
const container g = {vtzero::detail::command_move_to(huge_value), 10, 10};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), g.size() / 2};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(decoder.next_command(vtzero::detail::CommandId::MOVE_TO), const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("geometry_decoder with multipoint with not enough points") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(2), 10};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<iterator> decoder{g.cbegin(), g.cend(), 1};
|
||||||
|
REQUIRE(decoder.count() == 0);
|
||||||
|
REQUIRE_FALSE(decoder.done());
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(decoder.next_command(vtzero::detail::CommandId::MOVE_TO), const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
|
160
test/t/test_geometry_linestring.cpp
Normal file
160
test/t/test_geometry_linestring.cpp
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/geometry.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using container = std::vector<uint32_t>;
|
||||||
|
using iterator = container::const_iterator;
|
||||||
|
|
||||||
|
class dummy_geom_handler {
|
||||||
|
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void linestring_begin(const uint32_t /*count*/) noexcept {
|
||||||
|
++value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_point(const vtzero::point /*point*/) noexcept {
|
||||||
|
value += 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void linestring_end() noexcept {
|
||||||
|
value += 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result() const noexcept {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class dummy_geom_handler
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_linestring_geometry() with empty input") {
|
||||||
|
const container g;
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
dummy_geom_handler handler;
|
||||||
|
decoder.decode_linestring(dummy_geom_handler{});
|
||||||
|
REQUIRE(handler.result() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_linestring_geometry() with a valid linestring") {
|
||||||
|
const container g = {9, 4, 4, 18, 0, 16, 16, 0};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
REQUIRE(decoder.decode_linestring(dummy_geom_handler{}) == 10301);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_linestring_geometry() with a valid multilinestring") {
|
||||||
|
const container g = {9, 4, 4, 18, 0, 16, 16, 0, 9, 17, 17, 10, 4, 8};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
dummy_geom_handler handler;
|
||||||
|
decoder.decode_linestring(handler);
|
||||||
|
REQUIRE(handler.result() == 20502);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_linestring_geometry() with a point geometry fails") {
|
||||||
|
const container g = {9, 50, 34}; // this is a point geometry
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
"expected LineTo command (spec 4.3.4.3)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_linestring_geometry() with a polygon geometry fails") {
|
||||||
|
const container g = {9, 6, 12, 18, 10, 12, 24, 44, 15}; // this is a polygon geometry
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
"expected command 1 but got 7");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_linestring_geometry() with something other than MoveTo command") {
|
||||||
|
const container g = {vtzero::detail::command_line_to(3)};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
"expected command 1 but got 2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_linestring_geometry() with a count of 0") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(0)};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
"MoveTo command count is not 1 (spec 4.3.4.3)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_linestring_geometry() with a count of 2") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(2), 10, 20, 20, 10};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
"MoveTo command count is not 1 (spec 4.3.4.3)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_linestring_geometry() with 2nd command not a LineTo") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(1), 3, 4,
|
||||||
|
vtzero::detail::command_move_to(1)};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
"expected command 2 but got 1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_linestring_geometry() with LineTo and 0 count") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(1), 3, 4,
|
||||||
|
vtzero::detail::command_line_to(0)};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_linestring(dummy_geom_handler{}),
|
||||||
|
"LineTo command count is zero (spec 4.3.4.3)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
135
test/t/test_geometry_point.cpp
Normal file
135
test/t/test_geometry_point.cpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/geometry.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using container = std::vector<uint32_t>;
|
||||||
|
using iterator = container::const_iterator;
|
||||||
|
|
||||||
|
class dummy_geom_handler {
|
||||||
|
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void points_begin(const uint32_t /*count*/) noexcept {
|
||||||
|
++value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void points_point(const vtzero::point /*point*/) noexcept {
|
||||||
|
value += 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void points_end() noexcept {
|
||||||
|
value += 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result() const noexcept {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class dummy_geom_handler
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_point() with empty input") {
|
||||||
|
const container g;
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
"expected MoveTo command (spec 4.3.4.2)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_point() with a valid point") {
|
||||||
|
const container g = {9, 50, 34};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
dummy_geom_handler handler;
|
||||||
|
decoder.decode_point(handler);
|
||||||
|
REQUIRE(handler.result() == 10101);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_point() with a valid multipoint") {
|
||||||
|
const container g = {17, 10, 14, 3, 9};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
REQUIRE(decoder.decode_point(dummy_geom_handler{}) == 10201);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_point() with a linestring geometry fails") {
|
||||||
|
const container g = {9, 4, 4, 18, 0, 16, 16, 0}; // this is a linestring geometry
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
"additional data after end of geometry (spec 4.3.4.2)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_point() with a polygon geometry fails") {
|
||||||
|
const container g = {9, 6, 12, 18, 10, 12, 24, 44, 15}; // this is a polygon geometry
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
"additional data after end of geometry (spec 4.3.4.2)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_point() with something other than MoveTo command") {
|
||||||
|
const container g = {vtzero::detail::command_line_to(3)};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
"expected command 1 but got 2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_point() with a count of 0") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(0)};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
"MoveTo command count is zero (spec 4.3.4.2)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_point() with more data then expected") {
|
||||||
|
const container g = {9, 50, 34, 9};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_point(dummy_geom_handler{}),
|
||||||
|
"additional data after end of geometry (spec 4.3.4.2)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
204
test/t/test_geometry_polygon.cpp
Normal file
204
test/t/test_geometry_polygon.cpp
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/geometry.hpp>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using container = std::vector<uint32_t>;
|
||||||
|
using iterator = container::const_iterator;
|
||||||
|
|
||||||
|
class dummy_geom_handler {
|
||||||
|
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void ring_begin(const uint32_t /*count*/) noexcept {
|
||||||
|
++value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_point(const vtzero::point /*point*/) noexcept {
|
||||||
|
value += 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ring_end(vtzero::ring_type /*is_outer*/) noexcept {
|
||||||
|
value += 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result() const noexcept {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // class dummy_geom_handler
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with empty input") {
|
||||||
|
const container g;
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
dummy_geom_handler handler;
|
||||||
|
decoder.decode_polygon(dummy_geom_handler{});
|
||||||
|
REQUIRE(handler.result() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with a valid polygon") {
|
||||||
|
const container g = {9, 6, 12, 18, 10, 12, 24, 44, 15};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
REQUIRE(decoder.decode_polygon(dummy_geom_handler{}) == 10401);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with a duplicate end point") {
|
||||||
|
const container g = {9, 6, 12, 26, 10, 12, 24, 44, 33, 55, 15};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
dummy_geom_handler handler;
|
||||||
|
decoder.decode_polygon(handler);
|
||||||
|
REQUIRE(handler.result() == 10501);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with a valid multipolygon") {
|
||||||
|
const container g = {9, 0, 0, 26, 20, 0, 0, 20, 19, 0, 15, 9, 22, 2, 26, 18,
|
||||||
|
0, 0, 18, 17, 0, 15, 9, 4, 13, 26, 0, 8, 8, 0, 0, 7, 15};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
dummy_geom_handler handler;
|
||||||
|
decoder.decode_polygon(handler);
|
||||||
|
REQUIRE(handler.result() == 31503);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with a point geometry fails") {
|
||||||
|
const container g = {9, 50, 34}; // this is a point geometry
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
"expected LineTo command (spec 4.3.4.4)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with a linestring geometry fails") {
|
||||||
|
const container g = {9, 4, 4, 18, 0, 16, 16, 0}; // this is a linestring geometry
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
"expected ClosePath command (4.3.4.4)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with something other than MoveTo command") {
|
||||||
|
const container g = {vtzero::detail::command_line_to(3)};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
"expected command 1 but got 2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with a count of 0") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(0)};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
"MoveTo command count is not 1 (spec 4.3.4.4)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with a count of 2") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(2), 1, 2, 3, 4};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
"MoveTo command count is not 1 (spec 4.3.4.4)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with 2nd command not a LineTo") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(1), 3, 4,
|
||||||
|
vtzero::detail::command_move_to(1)};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
"expected command 2 but got 1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with LineTo and 0 count") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(1), 3, 4,
|
||||||
|
vtzero::detail::command_line_to(0),
|
||||||
|
vtzero::detail::command_close_path()};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
dummy_geom_handler handler;
|
||||||
|
decoder.decode_polygon(handler);
|
||||||
|
REQUIRE(handler.result() == 10201);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with LineTo and 1 count") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(1), 3, 4,
|
||||||
|
vtzero::detail::command_line_to(1), 5, 6,
|
||||||
|
vtzero::detail::command_close_path()};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
dummy_geom_handler handler;
|
||||||
|
decoder.decode_polygon(handler);
|
||||||
|
REQUIRE(handler.result() == 10301);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() with 3nd command not a ClosePath") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(1), 3, 4,
|
||||||
|
vtzero::detail::command_line_to(2), 4, 5, 6, 7,
|
||||||
|
vtzero::detail::command_line_to(0)};
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
|
||||||
|
SECTION("check exception type") {
|
||||||
|
REQUIRE_THROWS_AS(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
const vtzero::geometry_exception&);
|
||||||
|
}
|
||||||
|
SECTION("check exception message") {
|
||||||
|
REQUIRE_THROWS_WITH(decoder.decode_polygon(dummy_geom_handler{}),
|
||||||
|
"expected command 7 but got 2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Calling decode_polygon_geometry() on polygon with zero area") {
|
||||||
|
const container g = {vtzero::detail::command_move_to(1), 0, 0,
|
||||||
|
vtzero::detail::command_line_to(3), 2, 0, 0, 4, 2, 0,
|
||||||
|
vtzero::detail::command_close_path()};
|
||||||
|
|
||||||
|
vtzero::detail::geometry_decoder<container::const_iterator> decoder{g.begin(), g.end(), g.size() / 2};
|
||||||
|
dummy_geom_handler handler;
|
||||||
|
decoder.decode_polygon(handler);
|
||||||
|
REQUIRE(handler.result() == 10501);
|
||||||
|
}
|
||||||
|
|
343
test/t/test_index.cpp
Normal file
343
test/t/test_index.cpp
Normal file
@ -0,0 +1,343 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/builder.hpp>
|
||||||
|
#include <vtzero/index.hpp>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
TEST_CASE("add keys to layer using key index built into layer") {
|
||||||
|
static constexpr const int max_keys = 100;
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
for (int n = 0; n < max_keys; ++n) {
|
||||||
|
const auto key = std::to_string(n);
|
||||||
|
const auto idx = lbuilder.add_key(key);
|
||||||
|
REQUIRE(n == idx.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int n = 0; n < max_keys; n += 2) {
|
||||||
|
const auto key = std::to_string(n);
|
||||||
|
const auto idx = lbuilder.add_key(key);
|
||||||
|
REQUIRE(n == idx.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("add values to layer using value index built into layer") {
|
||||||
|
static constexpr const int max_values = 100;
|
||||||
|
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
for (int n = 0; n < max_values; ++n) {
|
||||||
|
const auto value = std::to_string(n);
|
||||||
|
const auto idx = lbuilder.add_value(vtzero::encoded_property_value{value});
|
||||||
|
REQUIRE(n == idx.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int n = 0; n < max_values; n += 2) {
|
||||||
|
const auto value = std::to_string(n);
|
||||||
|
const auto idx = lbuilder.add_value(vtzero::encoded_property_value{value});
|
||||||
|
REQUIRE(n == idx.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TIndex>
|
||||||
|
static void test_key_index() {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
TIndex index{lbuilder};
|
||||||
|
|
||||||
|
const auto i1 = index({"foo"});
|
||||||
|
const auto i2 = index({"bar"});
|
||||||
|
const auto i3 = index({"baz"});
|
||||||
|
const auto i4 = index({"foo"});
|
||||||
|
const auto i5 = index({"foo"});
|
||||||
|
const auto i6 = index({""});
|
||||||
|
const auto i7 = index({"bar"});
|
||||||
|
|
||||||
|
REQUIRE(i1 != i2);
|
||||||
|
REQUIRE(i1 != i3);
|
||||||
|
REQUIRE(i1 == i4);
|
||||||
|
REQUIRE(i1 == i5);
|
||||||
|
REQUIRE(i1 != i6);
|
||||||
|
REQUIRE(i1 != i7);
|
||||||
|
REQUIRE(i2 != i3);
|
||||||
|
REQUIRE(i2 != i4);
|
||||||
|
REQUIRE(i2 != i5);
|
||||||
|
REQUIRE(i2 != i6);
|
||||||
|
REQUIRE(i2 == i7);
|
||||||
|
REQUIRE(i3 != i4);
|
||||||
|
REQUIRE(i3 != i5);
|
||||||
|
REQUIRE(i3 != i6);
|
||||||
|
REQUIRE(i3 != i7);
|
||||||
|
REQUIRE(i4 == i5);
|
||||||
|
REQUIRE(i4 != i6);
|
||||||
|
REQUIRE(i4 != i7);
|
||||||
|
REQUIRE(i5 != i6);
|
||||||
|
REQUIRE(i5 != i7);
|
||||||
|
REQUIRE(i6 != i7);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("key index based on std::unordered_map") {
|
||||||
|
test_key_index<vtzero::key_index<std::unordered_map>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("key index based on std::map") {
|
||||||
|
test_key_index<vtzero::key_index<std::map>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TIndex>
|
||||||
|
static void test_value_index_internal() {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
TIndex index{lbuilder};
|
||||||
|
|
||||||
|
const auto i1 = index(vtzero::encoded_property_value{"foo"});
|
||||||
|
const auto i2 = index(vtzero::encoded_property_value{"bar"});
|
||||||
|
const auto i3 = index(vtzero::encoded_property_value{88});
|
||||||
|
const auto i4 = index(vtzero::encoded_property_value{"foo"});
|
||||||
|
const auto i5 = index(vtzero::encoded_property_value{77});
|
||||||
|
const auto i6 = index(vtzero::encoded_property_value{1.5});
|
||||||
|
const auto i7 = index(vtzero::encoded_property_value{"bar"});
|
||||||
|
|
||||||
|
REQUIRE(i1 != i2);
|
||||||
|
REQUIRE(i1 != i3);
|
||||||
|
REQUIRE(i1 == i4);
|
||||||
|
REQUIRE(i1 != i5);
|
||||||
|
REQUIRE(i1 != i6);
|
||||||
|
REQUIRE(i1 != i7);
|
||||||
|
|
||||||
|
REQUIRE(i2 != i3);
|
||||||
|
REQUIRE(i2 != i4);
|
||||||
|
REQUIRE(i2 != i5);
|
||||||
|
REQUIRE(i2 != i6);
|
||||||
|
REQUIRE(i2 == i7);
|
||||||
|
|
||||||
|
REQUIRE(i3 != i4);
|
||||||
|
REQUIRE(i3 != i5);
|
||||||
|
REQUIRE(i3 != i6);
|
||||||
|
REQUIRE(i3 != i7);
|
||||||
|
|
||||||
|
REQUIRE(i4 != i5);
|
||||||
|
REQUIRE(i4 != i6);
|
||||||
|
REQUIRE(i4 != i7);
|
||||||
|
|
||||||
|
REQUIRE(i5 != i6);
|
||||||
|
REQUIRE(i5 != i7);
|
||||||
|
|
||||||
|
REQUIRE(i6 != i7);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("internal value index based on std::unordered_map") {
|
||||||
|
test_value_index_internal<vtzero::value_index_internal<std::unordered_map>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("internal value index based on std::map") {
|
||||||
|
test_value_index_internal<vtzero::value_index_internal<std::map>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("external value index") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
vtzero::value_index<vtzero::string_value_type, std::string, std::map> string_index{lbuilder};
|
||||||
|
vtzero::value_index<vtzero::int_value_type, int, std::unordered_map> int_index{lbuilder};
|
||||||
|
vtzero::value_index<vtzero::sint_value_type, int, std::unordered_map> sint_index{lbuilder};
|
||||||
|
|
||||||
|
const auto i1 = string_index("foo");
|
||||||
|
const auto i2 = string_index("bar");
|
||||||
|
const auto i3 = int_index(6);
|
||||||
|
const auto i4 = sint_index(6);
|
||||||
|
const auto i5 = string_index(std::string{"foo"});
|
||||||
|
const auto i6 = int_index(6);
|
||||||
|
const auto i7 = sint_index(2);
|
||||||
|
const auto i8 = sint_index(5);
|
||||||
|
const auto i9 = sint_index(6);
|
||||||
|
|
||||||
|
REQUIRE(i1 != i2);
|
||||||
|
REQUIRE(i1 != i3);
|
||||||
|
REQUIRE(i1 != i4);
|
||||||
|
REQUIRE(i1 == i5);
|
||||||
|
REQUIRE(i1 != i6);
|
||||||
|
REQUIRE(i1 != i7);
|
||||||
|
REQUIRE(i1 != i7);
|
||||||
|
REQUIRE(i1 != i9);
|
||||||
|
|
||||||
|
REQUIRE(i2 != i3);
|
||||||
|
REQUIRE(i2 != i4);
|
||||||
|
REQUIRE(i2 != i5);
|
||||||
|
REQUIRE(i2 != i6);
|
||||||
|
REQUIRE(i2 != i7);
|
||||||
|
REQUIRE(i2 != i8);
|
||||||
|
REQUIRE(i2 != i9);
|
||||||
|
|
||||||
|
REQUIRE(i3 != i4);
|
||||||
|
REQUIRE(i3 != i5);
|
||||||
|
REQUIRE(i3 == i6);
|
||||||
|
REQUIRE(i3 != i7);
|
||||||
|
REQUIRE(i3 != i8);
|
||||||
|
REQUIRE(i3 != i9);
|
||||||
|
|
||||||
|
REQUIRE(i4 != i5);
|
||||||
|
REQUIRE(i4 != i6);
|
||||||
|
REQUIRE(i4 != i7);
|
||||||
|
REQUIRE(i4 != i8);
|
||||||
|
REQUIRE(i4 == i9);
|
||||||
|
|
||||||
|
REQUIRE(i5 != i6);
|
||||||
|
REQUIRE(i5 != i7);
|
||||||
|
REQUIRE(i5 != i8);
|
||||||
|
REQUIRE(i5 != i9);
|
||||||
|
|
||||||
|
REQUIRE(i6 != i7);
|
||||||
|
REQUIRE(i6 != i8);
|
||||||
|
REQUIRE(i6 != i9);
|
||||||
|
|
||||||
|
REQUIRE(i7 != i8);
|
||||||
|
REQUIRE(i7 != i9);
|
||||||
|
|
||||||
|
REQUIRE(i8 != i9);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("bool value index") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::value_index_bool index{lbuilder};
|
||||||
|
|
||||||
|
const auto i1 = index(false);
|
||||||
|
const auto i2 = index(true);
|
||||||
|
const auto i3 = index(true);
|
||||||
|
const auto i4 = index(false);
|
||||||
|
|
||||||
|
REQUIRE(i1 != i2);
|
||||||
|
REQUIRE(i1 != i3);
|
||||||
|
REQUIRE(i1 == i4);
|
||||||
|
|
||||||
|
REQUIRE(i2 == i3);
|
||||||
|
REQUIRE(i2 != i4);
|
||||||
|
|
||||||
|
REQUIRE(i3 != i4);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("small unsigned int value index") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
vtzero::value_index_small_uint index{lbuilder};
|
||||||
|
|
||||||
|
const auto i1 = index(12);
|
||||||
|
const auto i2 = index(4);
|
||||||
|
const auto i3 = index(0);
|
||||||
|
const auto i4 = index(100);
|
||||||
|
const auto i5 = index(4);
|
||||||
|
const auto i6 = index(12);
|
||||||
|
|
||||||
|
REQUIRE(i1 != i2);
|
||||||
|
REQUIRE(i1 != i3);
|
||||||
|
REQUIRE(i1 != i4);
|
||||||
|
REQUIRE(i1 != i5);
|
||||||
|
REQUIRE(i1 == i6);
|
||||||
|
|
||||||
|
REQUIRE(i2 != i3);
|
||||||
|
REQUIRE(i2 != i4);
|
||||||
|
REQUIRE(i2 == i5);
|
||||||
|
REQUIRE(i2 != i6);
|
||||||
|
|
||||||
|
REQUIRE(i3 != i4);
|
||||||
|
REQUIRE(i3 != i5);
|
||||||
|
REQUIRE(i3 != i6);
|
||||||
|
|
||||||
|
REQUIRE(i4 != i5);
|
||||||
|
REQUIRE(i4 != i6);
|
||||||
|
|
||||||
|
REQUIRE(i5 != i6);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("add features using a key index") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(7);
|
||||||
|
fbuilder.add_point(10, 20);
|
||||||
|
|
||||||
|
SECTION("no index") {
|
||||||
|
fbuilder.add_property("some_key", 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("key index using unordered_map") {
|
||||||
|
vtzero::key_index<std::unordered_map> index{lbuilder};
|
||||||
|
fbuilder.add_property(index("some_key"), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("key index using map") {
|
||||||
|
vtzero::key_index<std::map> index{lbuilder};
|
||||||
|
fbuilder.add_property(index("some_key"), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
// ============
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
REQUIRE(tile.count_layers() == 1);
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == 7);
|
||||||
|
const auto property = feature.next_property();
|
||||||
|
REQUIRE(property.value().int_value() == 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("add features using a value index") {
|
||||||
|
vtzero::tile_builder tbuilder;
|
||||||
|
vtzero::layer_builder lbuilder{tbuilder, "test"};
|
||||||
|
const auto key = lbuilder.add_key("some_key");
|
||||||
|
|
||||||
|
vtzero::point_feature_builder fbuilder{lbuilder};
|
||||||
|
fbuilder.set_id(17);
|
||||||
|
fbuilder.add_point(10, 20);
|
||||||
|
|
||||||
|
SECTION("no index") {
|
||||||
|
fbuilder.add_property(key, vtzero::sint_value_type{12});
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("external value index using unordered_map") {
|
||||||
|
vtzero::value_index<vtzero::sint_value_type, int, std::unordered_map> index{lbuilder};
|
||||||
|
fbuilder.add_property(key, index(12));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("external value index using map") {
|
||||||
|
vtzero::value_index<vtzero::sint_value_type, int, std::map> index{lbuilder};
|
||||||
|
fbuilder.add_property(key, index(12));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("property_value_type index") {
|
||||||
|
vtzero::value_index_internal<std::unordered_map> index{lbuilder};
|
||||||
|
fbuilder.add_property(key, index(vtzero::encoded_property_value{vtzero::sint_value_type{12}}));
|
||||||
|
}
|
||||||
|
|
||||||
|
fbuilder.commit();
|
||||||
|
|
||||||
|
const std::string data = tbuilder.serialize();
|
||||||
|
|
||||||
|
// ============
|
||||||
|
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
REQUIRE(tile.count_layers() == 1);
|
||||||
|
auto layer = tile.next_layer();
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.id() == 17);
|
||||||
|
const auto property = feature.next_property();
|
||||||
|
REQUIRE(property.value().sint_value() == 12);
|
||||||
|
}
|
||||||
|
|
155
test/t/test_layer.cpp
Normal file
155
test/t/test_layer.cpp
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/layer.hpp>
|
||||||
|
#include <vtzero/vector_tile.hpp>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
TEST_CASE("default constructed layer") {
|
||||||
|
vtzero::layer layer{};
|
||||||
|
REQUIRE_FALSE(layer.valid());
|
||||||
|
REQUIRE_FALSE(layer);
|
||||||
|
|
||||||
|
REQUIRE(layer.data() == vtzero::data_view{});
|
||||||
|
REQUIRE_ASSERT(layer.version());
|
||||||
|
REQUIRE_ASSERT(layer.extent());
|
||||||
|
REQUIRE_ASSERT(layer.name());
|
||||||
|
|
||||||
|
REQUIRE(layer.empty());
|
||||||
|
REQUIRE(layer.num_features() == 0);
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(layer.key_table(), const assert_error&);
|
||||||
|
REQUIRE_THROWS_AS(layer.value_table(), const assert_error&);
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(layer.key(0), const assert_error&);
|
||||||
|
REQUIRE_THROWS_AS(layer.value(0), const assert_error&);
|
||||||
|
|
||||||
|
REQUIRE_THROWS_AS(layer.get_feature_by_id(0), const assert_error&);
|
||||||
|
REQUIRE_THROWS_AS(layer.next_feature(), const assert_error&);
|
||||||
|
REQUIRE_ASSERT(layer.reset_feature());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("read a layer") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.get_layer_by_name("bridge");
|
||||||
|
REQUIRE(layer.valid());
|
||||||
|
REQUIRE(layer);
|
||||||
|
|
||||||
|
REQUIRE(layer.version() == 1);
|
||||||
|
REQUIRE(layer.extent() == 4096);
|
||||||
|
REQUIRE(layer.name() == "bridge");
|
||||||
|
|
||||||
|
REQUIRE_FALSE(layer.empty());
|
||||||
|
REQUIRE(layer.num_features() == 2);
|
||||||
|
|
||||||
|
const auto& kt = layer.key_table();
|
||||||
|
REQUIRE(kt.size() == 4);
|
||||||
|
REQUIRE(kt[0] == "class");
|
||||||
|
|
||||||
|
const auto& vt = layer.value_table();
|
||||||
|
REQUIRE(vt.size() == 4);
|
||||||
|
REQUIRE(vt[0].type() == vtzero::property_value_type::string_value);
|
||||||
|
REQUIRE(vt[0].string_value() == "main");
|
||||||
|
REQUIRE(vt[1].type() == vtzero::property_value_type::int_value);
|
||||||
|
REQUIRE(vt[1].int_value() == 0);
|
||||||
|
|
||||||
|
REQUIRE(layer.key(0) == "class");
|
||||||
|
REQUIRE(layer.key(1) == "oneway");
|
||||||
|
REQUIRE(layer.key(2) == "osm_id");
|
||||||
|
REQUIRE(layer.key(3) == "type");
|
||||||
|
REQUIRE_THROWS_AS(layer.key(4), const vtzero::out_of_range_exception&);
|
||||||
|
|
||||||
|
REQUIRE(layer.value(0).string_value() == "main");
|
||||||
|
REQUIRE(layer.value(1).int_value() == 0);
|
||||||
|
REQUIRE(layer.value(2).string_value() == "primary");
|
||||||
|
REQUIRE(layer.value(3).string_value() == "tertiary");
|
||||||
|
REQUIRE_THROWS_AS(layer.value(4), const vtzero::out_of_range_exception&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("access features in a layer by id") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.get_layer_by_name("building");
|
||||||
|
REQUIRE(layer);
|
||||||
|
|
||||||
|
REQUIRE(layer.num_features() == 937);
|
||||||
|
|
||||||
|
const auto feature = layer.get_feature_by_id(122);
|
||||||
|
REQUIRE(feature.id() == 122);
|
||||||
|
REQUIRE(feature.num_properties() == 0);
|
||||||
|
REQUIRE(feature.geometry_type() == vtzero::GeomType::POLYGON);
|
||||||
|
REQUIRE(feature.geometry().type() == vtzero::GeomType::POLYGON);
|
||||||
|
REQUIRE_FALSE(feature.geometry().data().empty());
|
||||||
|
|
||||||
|
REQUIRE_FALSE(layer.get_feature_by_id(844));
|
||||||
|
REQUIRE_FALSE(layer.get_feature_by_id(999999));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("iterate over all features in a layer") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.get_layer_by_name("building");
|
||||||
|
REQUIRE(layer);
|
||||||
|
|
||||||
|
std::size_t count = 0;
|
||||||
|
|
||||||
|
SECTION("external iterator") {
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("internal iterator") {
|
||||||
|
const bool done = layer.for_each_feature([&count](const vtzero::feature& /*feature*/) noexcept {
|
||||||
|
++count;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
REQUIRE(done);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(count == 937);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("iterate over some features in a layer") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.get_layer_by_name("building");
|
||||||
|
REQUIRE(layer);
|
||||||
|
|
||||||
|
uint64_t id_sum = 0;
|
||||||
|
|
||||||
|
SECTION("external iterator") {
|
||||||
|
while (auto feature = layer.next_feature()) {
|
||||||
|
if (feature.id() == 10) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
id_sum += feature.id();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("internal iterator") {
|
||||||
|
const bool done = layer.for_each_feature([&id_sum](const vtzero::feature& feature) noexcept {
|
||||||
|
if (feature.id() == 10) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
id_sum += feature.id();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
REQUIRE_FALSE(done);
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t expected = (10 - 1) * 10 / 2;
|
||||||
|
REQUIRE(id_sum == expected);
|
||||||
|
|
||||||
|
layer.reset_feature();
|
||||||
|
auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature);
|
||||||
|
REQUIRE(feature.id() == 1);
|
||||||
|
}
|
||||||
|
|
42
test/t/test_output.cpp
Normal file
42
test/t/test_output.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::string get_output(T v) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << v;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("output GeomType") {
|
||||||
|
REQUIRE(get_output(vtzero::GeomType::UNKNOWN) == "unknown");
|
||||||
|
REQUIRE(get_output(vtzero::GeomType::POINT) == "point");
|
||||||
|
REQUIRE(get_output(vtzero::GeomType::LINESTRING) == "linestring");
|
||||||
|
REQUIRE(get_output(vtzero::GeomType::POLYGON) == "polygon");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("output property_value_type") {
|
||||||
|
REQUIRE(get_output(vtzero::property_value_type::sint_value) == "sint");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("output index_value") {
|
||||||
|
REQUIRE(get_output(vtzero::index_value{}) == "invalid");
|
||||||
|
REQUIRE(get_output(vtzero::index_value{5}) == "5");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("output index_value_pair") {
|
||||||
|
const auto in = vtzero::index_value{};
|
||||||
|
const auto v2 = vtzero::index_value{2};
|
||||||
|
const auto v5 = vtzero::index_value{5};
|
||||||
|
REQUIRE(get_output(vtzero::index_value_pair{in, v2}) == "invalid");
|
||||||
|
REQUIRE(get_output(vtzero::index_value_pair{v2, v5}) == "[2,5]");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("output point") {
|
||||||
|
REQUIRE(get_output(vtzero::point{}) == "(0,0)");
|
||||||
|
REQUIRE(get_output(vtzero::point{4, 7}) == "(4,7)");
|
||||||
|
}
|
||||||
|
|
25
test/t/test_point.cpp
Normal file
25
test/t/test_point.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/geometry.hpp>
|
||||||
|
|
||||||
|
TEST_CASE("default constructed point") {
|
||||||
|
vtzero::point p{};
|
||||||
|
|
||||||
|
REQUIRE(p.x == 0);
|
||||||
|
REQUIRE(p.y == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("point") {
|
||||||
|
vtzero::point p1{4, 5};
|
||||||
|
vtzero::point p2{5, 4};
|
||||||
|
vtzero::point p3{4, 5};
|
||||||
|
|
||||||
|
REQUIRE(p1.x == 4);
|
||||||
|
REQUIRE(p1.y == 5);
|
||||||
|
|
||||||
|
REQUIRE_FALSE(p1 == p2);
|
||||||
|
REQUIRE(p1 != p2);
|
||||||
|
REQUIRE(p1 == p3);
|
||||||
|
}
|
||||||
|
|
62
test/t/test_property_map.cpp
Normal file
62
test/t/test_property_map.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/builder.hpp>
|
||||||
|
|
||||||
|
#ifdef VTZERO_TEST_WITH_VARIANT
|
||||||
|
# include <boost/variant.hpp>
|
||||||
|
using variant_type = boost::variant<std::string, float, double, int64_t, uint64_t, bool>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
TEST_CASE("property map") {
|
||||||
|
vtzero::tile_builder tile;
|
||||||
|
vtzero::layer_builder layer_points{tile, "points"};
|
||||||
|
{
|
||||||
|
vtzero::point_feature_builder fbuilder{layer_points};
|
||||||
|
fbuilder.set_id(1);
|
||||||
|
fbuilder.add_points(1);
|
||||||
|
fbuilder.set_point(10, 10);
|
||||||
|
fbuilder.add_property("foo", "bar");
|
||||||
|
fbuilder.add_property("x", "y");
|
||||||
|
fbuilder.add_property("abc", "def");
|
||||||
|
fbuilder.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string data = tile.serialize();
|
||||||
|
|
||||||
|
vtzero::vector_tile vt{data};
|
||||||
|
REQUIRE(vt.count_layers() == 1);
|
||||||
|
auto layer = vt.next_layer();
|
||||||
|
REQUIRE(layer.valid());
|
||||||
|
REQUIRE(layer.num_features() == 1);
|
||||||
|
|
||||||
|
const auto feature = layer.next_feature();
|
||||||
|
REQUIRE(feature.valid());
|
||||||
|
REQUIRE(feature.num_properties() == 3);
|
||||||
|
|
||||||
|
#ifdef VTZERO_TEST_WITH_VARIANT
|
||||||
|
SECTION("std::map") {
|
||||||
|
using prop_map_type = std::map<std::string, variant_type>;
|
||||||
|
auto map = vtzero::create_properties_map<prop_map_type>(feature);
|
||||||
|
|
||||||
|
REQUIRE(map.size() == 3);
|
||||||
|
REQUIRE(boost::get<std::string>(map["foo"]) == "bar");
|
||||||
|
REQUIRE(boost::get<std::string>(map["x"]) == "y");
|
||||||
|
REQUIRE(boost::get<std::string>(map["abc"]) == "def");
|
||||||
|
}
|
||||||
|
SECTION("std::unordered_map") {
|
||||||
|
using prop_map_type = std::unordered_map<std::string, variant_type>;
|
||||||
|
auto map = vtzero::create_properties_map<prop_map_type>(feature);
|
||||||
|
|
||||||
|
REQUIRE(map.size() == 3);
|
||||||
|
REQUIRE(boost::get<std::string>(map["foo"]) == "bar");
|
||||||
|
REQUIRE(boost::get<std::string>(map["x"]) == "y");
|
||||||
|
REQUIRE(boost::get<std::string>(map["abc"]) == "def");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
407
test/t/test_property_value.cpp
Normal file
407
test/t/test_property_value.cpp
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/encoded_property_value.hpp>
|
||||||
|
#include <vtzero/property.hpp>
|
||||||
|
#include <vtzero/property_value.hpp>
|
||||||
|
#include <vtzero/types.hpp>
|
||||||
|
|
||||||
|
#ifdef VTZERO_TEST_WITH_VARIANT
|
||||||
|
# include <boost/variant.hpp>
|
||||||
|
using variant_type = boost::variant<std::string, float, double, int64_t, uint64_t, bool>;
|
||||||
|
|
||||||
|
struct variant_mapping : vtzero::property_value_mapping {
|
||||||
|
using float_type = int64_t;
|
||||||
|
using double_type = int64_t;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct visitor_test_void {
|
||||||
|
|
||||||
|
int x = 0;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void operator()(T /*value*/) {
|
||||||
|
x = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(vtzero::data_view /*value*/) {
|
||||||
|
x = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct visitor_test_int {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int operator()(T /*value*/) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int operator()(vtzero::data_view /*value*/) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct visitor_test_to_string {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::string operator()(T value) {
|
||||||
|
return std::to_string(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string operator()(vtzero::data_view value) {
|
||||||
|
return std::string{value.data(), value.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct string_conv {
|
||||||
|
|
||||||
|
std::string s;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
explicit string_conv(T value) :
|
||||||
|
s(std::to_string(value)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator std::string() {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct string_mapping : vtzero::property_value_mapping {
|
||||||
|
using string_type = std::string;
|
||||||
|
using float_type = string_conv;
|
||||||
|
using double_type = string_conv;
|
||||||
|
using int_type = string_conv;
|
||||||
|
using uint_type = string_conv;
|
||||||
|
using bool_type = string_conv;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE("default constructed property_value") {
|
||||||
|
vtzero::property_value pv;
|
||||||
|
REQUIRE_FALSE(pv.valid());
|
||||||
|
REQUIRE(pv.data().data() == nullptr);
|
||||||
|
|
||||||
|
REQUIRE(pv == vtzero::property_value{});
|
||||||
|
REQUIRE_FALSE(pv != vtzero::property_value{});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("empty property_value") {
|
||||||
|
char x[1] = {0};
|
||||||
|
vtzero::data_view dv{x, 0};
|
||||||
|
vtzero::property_value pv{dv};
|
||||||
|
REQUIRE(pv.valid());
|
||||||
|
REQUIRE_THROWS_AS(pv.type(), const vtzero::format_exception&);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("string value") {
|
||||||
|
vtzero::encoded_property_value epv{"foo"};
|
||||||
|
vtzero::property_value pv{epv.data()};
|
||||||
|
REQUIRE(pv.string_value() == "foo");
|
||||||
|
|
||||||
|
visitor_test_void vt;
|
||||||
|
vtzero::apply_visitor(vt, pv);
|
||||||
|
REQUIRE(vt.x == 2);
|
||||||
|
|
||||||
|
const auto result = vtzero::apply_visitor(visitor_test_int{}, pv);
|
||||||
|
REQUIRE(result == 2);
|
||||||
|
|
||||||
|
const auto str = vtzero::apply_visitor(visitor_test_to_string{}, pv);
|
||||||
|
REQUIRE(str == "foo");
|
||||||
|
|
||||||
|
const std::string cs = vtzero::convert_property_value<std::string, string_mapping>(pv);
|
||||||
|
REQUIRE(cs == "foo");
|
||||||
|
|
||||||
|
#ifdef VTZERO_TEST_WITH_VARIANT
|
||||||
|
const auto vari = vtzero::convert_property_value<variant_type>(pv);
|
||||||
|
REQUIRE(boost::get<std::string>(vari) == "foo");
|
||||||
|
const auto conv = vtzero::convert_property_value<variant_type, variant_mapping>(pv);
|
||||||
|
REQUIRE(boost::get<std::string>(conv) == "foo");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("float value") {
|
||||||
|
vtzero::encoded_property_value epv{1.2f};
|
||||||
|
vtzero::property_value pv{epv.data()};
|
||||||
|
REQUIRE(pv.float_value() == Approx(1.2));
|
||||||
|
|
||||||
|
visitor_test_void vt;
|
||||||
|
vtzero::apply_visitor(vt, pv);
|
||||||
|
REQUIRE(vt.x == 1);
|
||||||
|
|
||||||
|
const auto result = vtzero::apply_visitor(visitor_test_int{}, pv);
|
||||||
|
REQUIRE(result == 1);
|
||||||
|
|
||||||
|
const std::string cs = vtzero::convert_property_value<std::string, string_mapping>(pv);
|
||||||
|
REQUIRE(cs == "1.200000");
|
||||||
|
|
||||||
|
#ifdef VTZERO_TEST_WITH_VARIANT
|
||||||
|
const auto vari = vtzero::convert_property_value<variant_type>(pv);
|
||||||
|
REQUIRE(boost::get<float>(vari) == Approx(1.2));
|
||||||
|
const auto conv = vtzero::convert_property_value<variant_type, variant_mapping>(pv);
|
||||||
|
REQUIRE(boost::get<int64_t>(conv) == 1);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("double value") {
|
||||||
|
vtzero::encoded_property_value epv{3.4};
|
||||||
|
vtzero::property_value pv{epv.data()};
|
||||||
|
REQUIRE(pv.double_value() == Approx(3.4));
|
||||||
|
|
||||||
|
const auto result = vtzero::apply_visitor(visitor_test_int{}, pv);
|
||||||
|
REQUIRE(result == 1);
|
||||||
|
|
||||||
|
const std::string cs = vtzero::convert_property_value<std::string, string_mapping>(pv);
|
||||||
|
REQUIRE(cs == "3.400000");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("int value") {
|
||||||
|
vtzero::encoded_property_value epv{vtzero::int_value_type{42}};
|
||||||
|
vtzero::property_value pv{epv.data()};
|
||||||
|
REQUIRE(pv.int_value() == 42);
|
||||||
|
|
||||||
|
const auto str = vtzero::apply_visitor(visitor_test_to_string{}, pv);
|
||||||
|
REQUIRE(str == "42");
|
||||||
|
|
||||||
|
const std::string cs = vtzero::convert_property_value<std::string, string_mapping>(pv);
|
||||||
|
REQUIRE(cs == "42");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("uint value") {
|
||||||
|
vtzero::encoded_property_value epv{vtzero::uint_value_type{99}};
|
||||||
|
vtzero::property_value pv{epv.data()};
|
||||||
|
REQUIRE(pv.uint_value() == 99);
|
||||||
|
|
||||||
|
const auto str = vtzero::apply_visitor(visitor_test_to_string{}, pv);
|
||||||
|
REQUIRE(str == "99");
|
||||||
|
|
||||||
|
const std::string cs = vtzero::convert_property_value<std::string, string_mapping>(pv);
|
||||||
|
REQUIRE(cs == "99");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("sint value") {
|
||||||
|
vtzero::encoded_property_value epv{vtzero::sint_value_type{42}};
|
||||||
|
vtzero::property_value pv{epv.data()};
|
||||||
|
REQUIRE(pv.sint_value() == 42);
|
||||||
|
|
||||||
|
const auto str = vtzero::apply_visitor(visitor_test_to_string{}, pv);
|
||||||
|
REQUIRE(str == "42");
|
||||||
|
|
||||||
|
const std::string cs = vtzero::convert_property_value<std::string, string_mapping>(pv);
|
||||||
|
REQUIRE(cs == "42");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("bool value") {
|
||||||
|
vtzero::encoded_property_value epv{true};
|
||||||
|
vtzero::property_value pv{epv.data()};
|
||||||
|
REQUIRE(pv.bool_value());
|
||||||
|
|
||||||
|
const auto str = vtzero::apply_visitor(visitor_test_to_string{}, pv);
|
||||||
|
REQUIRE(str == "1");
|
||||||
|
|
||||||
|
const std::string cs = vtzero::convert_property_value<std::string, string_mapping>(pv);
|
||||||
|
REQUIRE(cs == "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("property and property_value equality comparisons") {
|
||||||
|
vtzero::encoded_property_value t{true};
|
||||||
|
vtzero::encoded_property_value f{false};
|
||||||
|
vtzero::encoded_property_value v1{vtzero::int_value_type{1}};
|
||||||
|
vtzero::encoded_property_value vs{"foo"};
|
||||||
|
|
||||||
|
REQUIRE(t == t);
|
||||||
|
REQUIRE_FALSE(t != t);
|
||||||
|
REQUIRE_FALSE(t == f);
|
||||||
|
REQUIRE_FALSE(t == v1);
|
||||||
|
REQUIRE_FALSE(t == vs);
|
||||||
|
|
||||||
|
using pv = vtzero::property_value;
|
||||||
|
REQUIRE(pv{t.data()} == pv{t.data()});
|
||||||
|
REQUIRE_FALSE(pv{t.data()} != pv{t.data()});
|
||||||
|
REQUIRE_FALSE(pv{t.data()} == pv{f.data()});
|
||||||
|
REQUIRE_FALSE(pv{t.data()} == pv{v1.data()});
|
||||||
|
REQUIRE_FALSE(pv{t.data()} == pv{vs.data()});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("property and property_value ordering") {
|
||||||
|
using pv = vtzero::property_value;
|
||||||
|
|
||||||
|
vtzero::encoded_property_value t{true};
|
||||||
|
vtzero::encoded_property_value f{false};
|
||||||
|
|
||||||
|
REQUIRE_FALSE(t < f);
|
||||||
|
REQUIRE_FALSE(t <= f);
|
||||||
|
REQUIRE(t > f);
|
||||||
|
REQUIRE(t >= f);
|
||||||
|
|
||||||
|
REQUIRE_FALSE(pv{t.data()} < pv{f.data()});
|
||||||
|
REQUIRE_FALSE(pv{t.data()} <= pv{f.data()});
|
||||||
|
REQUIRE(pv{t.data()} > pv{f.data()});
|
||||||
|
REQUIRE(pv{t.data()} >= pv{f.data()});
|
||||||
|
|
||||||
|
vtzero::encoded_property_value v1{vtzero::int_value_type{22}};
|
||||||
|
vtzero::encoded_property_value v2{vtzero::int_value_type{17}};
|
||||||
|
|
||||||
|
REQUIRE_FALSE(v1 < v2);
|
||||||
|
REQUIRE_FALSE(v1 <= v2);
|
||||||
|
REQUIRE(v1 > v2);
|
||||||
|
REQUIRE(v1 >= v2);
|
||||||
|
|
||||||
|
REQUIRE_FALSE(pv{v1.data()} < pv{v2.data()});
|
||||||
|
REQUIRE_FALSE(pv{v1.data()} <= pv{v2.data()});
|
||||||
|
REQUIRE(pv{v1.data()} > pv{v2.data()});
|
||||||
|
REQUIRE(pv{v1.data()} >= pv{v2.data()});
|
||||||
|
|
||||||
|
vtzero::encoded_property_value vsf{"foo"};
|
||||||
|
vtzero::encoded_property_value vsb{"bar"};
|
||||||
|
vtzero::encoded_property_value vsx{"foobar"};
|
||||||
|
|
||||||
|
REQUIRE_FALSE(vsf < vsb);
|
||||||
|
REQUIRE_FALSE(vsf <= vsb);
|
||||||
|
REQUIRE(vsf > vsb);
|
||||||
|
REQUIRE(vsf >= vsb);
|
||||||
|
|
||||||
|
REQUIRE_FALSE(pv{vsf.data()} < pv{vsb.data()});
|
||||||
|
REQUIRE_FALSE(pv{vsf.data()} <= pv{vsb.data()});
|
||||||
|
REQUIRE(pv{vsf.data()} > pv{vsb.data()});
|
||||||
|
REQUIRE(pv{vsf.data()} >= pv{vsb.data()});
|
||||||
|
|
||||||
|
REQUIRE(vsf < vsx);
|
||||||
|
REQUIRE(vsf <= vsx);
|
||||||
|
REQUIRE_FALSE(vsf > vsx);
|
||||||
|
REQUIRE_FALSE(vsf >= vsx);
|
||||||
|
|
||||||
|
REQUIRE(pv{vsf.data()} < pv{vsx.data()});
|
||||||
|
REQUIRE(pv{vsf.data()} <= pv{vsx.data()});
|
||||||
|
REQUIRE_FALSE(pv{vsf.data()} > pv{vsx.data()});
|
||||||
|
REQUIRE_FALSE(pv{vsf.data()} >= pv{vsx.data()});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default constructed property") {
|
||||||
|
vtzero::property p;
|
||||||
|
REQUIRE_FALSE(p.valid());
|
||||||
|
REQUIRE_FALSE(p);
|
||||||
|
REQUIRE(p.key().data() == nullptr);
|
||||||
|
REQUIRE(p.value().data().data() == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("valid property") {
|
||||||
|
vtzero::data_view k{"key"};
|
||||||
|
vtzero::encoded_property_value epv{"value"};
|
||||||
|
vtzero::property_value pv{epv.data()};
|
||||||
|
|
||||||
|
vtzero::property p{k, pv};
|
||||||
|
REQUIRE(p.key() == "key");
|
||||||
|
REQUIRE(p.value().string_value() == "value");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("create encoded property values from different string types") {
|
||||||
|
const std::string v{"value"};
|
||||||
|
|
||||||
|
vtzero::encoded_property_value epv1{vtzero::string_value_type{"value"}};
|
||||||
|
vtzero::encoded_property_value epv2{"value"};
|
||||||
|
vtzero::encoded_property_value epv3{v};
|
||||||
|
vtzero::encoded_property_value epv4{vtzero::data_view{v}};
|
||||||
|
vtzero::encoded_property_value epv5{"valuexxxxxxxxx", 5};
|
||||||
|
|
||||||
|
REQUIRE(epv1 == epv2);
|
||||||
|
REQUIRE(epv1 == epv3);
|
||||||
|
REQUIRE(epv1 == epv4);
|
||||||
|
REQUIRE(epv1 == epv5);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("create encoded property values from different floating point types") {
|
||||||
|
vtzero::encoded_property_value f1{vtzero::float_value_type{3.2f}};
|
||||||
|
vtzero::encoded_property_value f2{3.2f};
|
||||||
|
vtzero::encoded_property_value d1{vtzero::double_value_type{3.2}};
|
||||||
|
vtzero::encoded_property_value d2{3.2};
|
||||||
|
|
||||||
|
REQUIRE(f1 == f2);
|
||||||
|
REQUIRE(d1 == d2);
|
||||||
|
|
||||||
|
vtzero::property_value pvf{f1.data()};
|
||||||
|
vtzero::property_value pvd{d1.data()};
|
||||||
|
|
||||||
|
REQUIRE(pvf.float_value() == Approx(pvd.double_value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("create encoded property values from different integer types") {
|
||||||
|
vtzero::encoded_property_value i1{vtzero::int_value_type{7}};
|
||||||
|
vtzero::encoded_property_value i2{int64_t(7)};
|
||||||
|
vtzero::encoded_property_value i3{int32_t(7)};
|
||||||
|
vtzero::encoded_property_value i4{int16_t(7)};
|
||||||
|
vtzero::encoded_property_value u1{vtzero::uint_value_type{7}};
|
||||||
|
vtzero::encoded_property_value u2{uint64_t(7)};
|
||||||
|
vtzero::encoded_property_value u3{uint32_t(7)};
|
||||||
|
vtzero::encoded_property_value u4{uint16_t(7)};
|
||||||
|
vtzero::encoded_property_value s1{vtzero::sint_value_type{7}};
|
||||||
|
|
||||||
|
REQUIRE(i1 == i2);
|
||||||
|
REQUIRE(i1 == i3);
|
||||||
|
REQUIRE(i1 == i4);
|
||||||
|
REQUIRE(u1 == u2);
|
||||||
|
REQUIRE(u1 == u3);
|
||||||
|
REQUIRE(u1 == u4);
|
||||||
|
|
||||||
|
REQUIRE_FALSE(i1 == u1);
|
||||||
|
REQUIRE_FALSE(i1 == s1);
|
||||||
|
REQUIRE_FALSE(u1 == s1);
|
||||||
|
|
||||||
|
REQUIRE(i1.hash() == i2.hash());
|
||||||
|
REQUIRE(u1.hash() == u2.hash());
|
||||||
|
|
||||||
|
vtzero::property_value pvi{i1.data()};
|
||||||
|
vtzero::property_value pvu{u1.data()};
|
||||||
|
vtzero::property_value pvs{s1.data()};
|
||||||
|
|
||||||
|
REQUIRE(pvi.int_value() == pvu.uint_value());
|
||||||
|
REQUIRE(pvi.int_value() == pvs.sint_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("create encoded property values from different bool types") {
|
||||||
|
vtzero::encoded_property_value b1{vtzero::bool_value_type{true}};
|
||||||
|
vtzero::encoded_property_value b2{true};
|
||||||
|
|
||||||
|
REQUIRE(b1 == b2);
|
||||||
|
REQUIRE(b1.hash() == b2.hash());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("property equality comparison operator") {
|
||||||
|
std::string k = "key";
|
||||||
|
|
||||||
|
vtzero::encoded_property_value epv1{"value"};
|
||||||
|
vtzero::encoded_property_value epv2{"another value"};
|
||||||
|
vtzero::property_value pv1{epv1.data()};
|
||||||
|
vtzero::property_value pv2{epv2.data()};
|
||||||
|
|
||||||
|
vtzero::property p1{k, pv1};
|
||||||
|
vtzero::property p2{k, pv1};
|
||||||
|
vtzero::property p3{k, pv2};
|
||||||
|
REQUIRE(p1 == p2);
|
||||||
|
REQUIRE_FALSE(p1 == p3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("property inequality comparison operator") {
|
||||||
|
std::string k1 = "key";
|
||||||
|
std::string k2 = "another_key";
|
||||||
|
|
||||||
|
vtzero::encoded_property_value epv1{"value"};
|
||||||
|
vtzero::encoded_property_value epv2{"another value"};
|
||||||
|
vtzero::property_value pv1{epv1.data()};
|
||||||
|
vtzero::property_value pv2{epv2.data()};
|
||||||
|
|
||||||
|
vtzero::property p1{k1, pv1};
|
||||||
|
vtzero::property p2{k1, pv1};
|
||||||
|
vtzero::property p3{k1, pv2};
|
||||||
|
vtzero::property p4{k2, pv2};
|
||||||
|
REQUIRE_FALSE(p1 != p2);
|
||||||
|
REQUIRE(p1 != p3);
|
||||||
|
REQUIRE(p3 != p4);
|
||||||
|
}
|
116
test/t/test_types.cpp
Normal file
116
test/t/test_types.cpp
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/types.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
TEST_CASE("default constructed string_value_type") {
|
||||||
|
vtzero::string_value_type v;
|
||||||
|
REQUIRE(v.value.data() == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("string_value_type with value") {
|
||||||
|
vtzero::string_value_type v{"foo"};
|
||||||
|
REQUIRE(v.value.data()[0] == 'f');
|
||||||
|
REQUIRE(v.value.size() == 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default constructed float_value_type") {
|
||||||
|
vtzero::float_value_type v;
|
||||||
|
REQUIRE(v.value == Approx(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("float_value_type with value") {
|
||||||
|
float x = 2.7f;
|
||||||
|
vtzero::float_value_type v{x};
|
||||||
|
REQUIRE(v.value == Approx(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default constructed double_value_type") {
|
||||||
|
vtzero::double_value_type v;
|
||||||
|
REQUIRE(v.value == Approx(0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("double_value_type with value") {
|
||||||
|
double x = 2.7;
|
||||||
|
vtzero::double_value_type v{x};
|
||||||
|
REQUIRE(v.value == Approx(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default constructed int_value_type") {
|
||||||
|
vtzero::int_value_type v;
|
||||||
|
REQUIRE(v.value == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("int_value_type with value") {
|
||||||
|
vtzero::int_value_type v{123};
|
||||||
|
REQUIRE(v.value == 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default constructed uint_value_type") {
|
||||||
|
vtzero::uint_value_type v;
|
||||||
|
REQUIRE(v.value == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("uint_value_type with value") {
|
||||||
|
vtzero::uint_value_type v{123};
|
||||||
|
REQUIRE(v.value == 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default constructed sint_value_type") {
|
||||||
|
vtzero::sint_value_type v;
|
||||||
|
REQUIRE(v.value == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("sint_value_type with value") {
|
||||||
|
vtzero::sint_value_type v{-14};
|
||||||
|
REQUIRE(v.value == -14);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default constructed bool_value_type") {
|
||||||
|
vtzero::bool_value_type v;
|
||||||
|
REQUIRE_FALSE(v.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("bool_value_type with value") {
|
||||||
|
bool x = true;
|
||||||
|
vtzero::bool_value_type v{x};
|
||||||
|
REQUIRE(v.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("property_value_type names") {
|
||||||
|
REQUIRE(std::string{vtzero::property_value_type_name(vtzero::property_value_type::string_value)} == "string");
|
||||||
|
REQUIRE(std::string{vtzero::property_value_type_name(vtzero::property_value_type::float_value)} == "float");
|
||||||
|
REQUIRE(std::string{vtzero::property_value_type_name(vtzero::property_value_type::double_value)} == "double");
|
||||||
|
REQUIRE(std::string{vtzero::property_value_type_name(vtzero::property_value_type::int_value)} == "int");
|
||||||
|
REQUIRE(std::string{vtzero::property_value_type_name(vtzero::property_value_type::uint_value)} == "uint");
|
||||||
|
REQUIRE(std::string{vtzero::property_value_type_name(vtzero::property_value_type::sint_value)} == "sint");
|
||||||
|
REQUIRE(std::string{vtzero::property_value_type_name(vtzero::property_value_type::bool_value)} == "bool");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default constructed index value") {
|
||||||
|
vtzero::index_value v;
|
||||||
|
REQUIRE_FALSE(v.valid());
|
||||||
|
REQUIRE_ASSERT(v.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("valid index value") {
|
||||||
|
vtzero::index_value v{32};
|
||||||
|
REQUIRE(v.valid());
|
||||||
|
REQUIRE(v.value() == 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default constructed geometry") {
|
||||||
|
vtzero::geometry geom;
|
||||||
|
REQUIRE(geom.type() == vtzero::GeomType::UNKNOWN);
|
||||||
|
REQUIRE(geom.data().empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("GeomType names") {
|
||||||
|
REQUIRE(std::string{vtzero::geom_type_name(vtzero::GeomType::UNKNOWN)} == "unknown");
|
||||||
|
REQUIRE(std::string{vtzero::geom_type_name(vtzero::GeomType::POINT)} == "point");
|
||||||
|
REQUIRE(std::string{vtzero::geom_type_name(vtzero::GeomType::LINESTRING)} == "linestring");
|
||||||
|
REQUIRE(std::string{vtzero::geom_type_name(vtzero::GeomType::POLYGON)} == "polygon");
|
||||||
|
}
|
||||||
|
|
139
test/t/test_vector_tile.cpp
Normal file
139
test/t/test_vector_tile.cpp
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <vtzero/vector_tile.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
TEST_CASE("open a vector tile with string") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
REQUIRE(vtzero::is_vector_tile(data));
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
REQUIRE_FALSE(tile.empty());
|
||||||
|
REQUIRE(tile.count_layers() == 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("open a vector tile with data_view") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
const vtzero::data_view dv{data};
|
||||||
|
vtzero::vector_tile tile{dv};
|
||||||
|
|
||||||
|
REQUIRE_FALSE(tile.empty());
|
||||||
|
REQUIRE(tile.count_layers() == 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("open a vector tile with pointer and size") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data.data(), data.size()};
|
||||||
|
|
||||||
|
REQUIRE_FALSE(tile.empty());
|
||||||
|
REQUIRE(tile.count_layers() == 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("get layer by index") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.get_layer(0);
|
||||||
|
REQUIRE(layer);
|
||||||
|
REQUIRE(layer.name() == "landuse");
|
||||||
|
|
||||||
|
layer = tile.get_layer(1);
|
||||||
|
REQUIRE(layer);
|
||||||
|
REQUIRE(layer.name() == "waterway");
|
||||||
|
|
||||||
|
layer = tile.get_layer(11);
|
||||||
|
REQUIRE(layer);
|
||||||
|
REQUIRE(layer.name() == "waterway_label");
|
||||||
|
|
||||||
|
layer = tile.get_layer(12);
|
||||||
|
REQUIRE_FALSE(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("get layer by name") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
auto layer = tile.get_layer_by_name("landuse");
|
||||||
|
REQUIRE(layer);
|
||||||
|
REQUIRE(layer.name() == "landuse");
|
||||||
|
|
||||||
|
layer = tile.get_layer_by_name(std::string{"road"});
|
||||||
|
REQUIRE(layer);
|
||||||
|
REQUIRE(layer.name() == "road");
|
||||||
|
|
||||||
|
const vtzero::data_view name{"poi_label"};
|
||||||
|
layer = tile.get_layer_by_name(name);
|
||||||
|
REQUIRE(layer);
|
||||||
|
REQUIRE(layer.name() == "poi_label");
|
||||||
|
|
||||||
|
layer = tile.get_layer_by_name("unknown");
|
||||||
|
REQUIRE_FALSE(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("iterate over layers") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
std::vector<std::string> names;
|
||||||
|
|
||||||
|
SECTION("external iterator") {
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
names.emplace_back(layer.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("internal iterator") {
|
||||||
|
const bool done = tile.for_each_layer([&names](const vtzero::layer& layer) {
|
||||||
|
names.emplace_back(layer.name());
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
REQUIRE(done);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(names.size() == 12);
|
||||||
|
|
||||||
|
static std::vector<std::string> expected = {
|
||||||
|
"landuse", "waterway", "water", "barrier_line", "building", "road",
|
||||||
|
"bridge", "place_label", "water_label", "poi_label", "road_label",
|
||||||
|
"waterway_label"
|
||||||
|
};
|
||||||
|
|
||||||
|
REQUIRE(names == expected);
|
||||||
|
|
||||||
|
tile.reset_layer();
|
||||||
|
int num = 0;
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
++num;
|
||||||
|
}
|
||||||
|
REQUIRE(num == 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("iterate over some of the layers") {
|
||||||
|
const auto data = load_test_tile();
|
||||||
|
vtzero::vector_tile tile{data};
|
||||||
|
|
||||||
|
int num_layers = 0;
|
||||||
|
|
||||||
|
SECTION("external iterator") {
|
||||||
|
while (auto layer = tile.next_layer()) {
|
||||||
|
++num_layers;
|
||||||
|
if (layer.name() == "water") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("internal iterator") {
|
||||||
|
const bool done = tile.for_each_layer([&num_layers](const vtzero::layer& layer) noexcept {
|
||||||
|
++num_layers;
|
||||||
|
return layer.name() != "water";
|
||||||
|
});
|
||||||
|
REQUIRE_FALSE(done);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(num_layers == 3);
|
||||||
|
}
|
||||||
|
|
25
test/test_main.cpp
Normal file
25
test/test_main.cpp
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#define CATCH_CONFIG_MAIN
|
||||||
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
#include <test.hpp>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
bool got_an_assert = false;
|
||||||
|
|
||||||
|
std::string load_test_tile() {
|
||||||
|
std::string path{"data/mapbox-streets-v6-14-8714-8017.mvt"};
|
||||||
|
std::ifstream stream{path, std::ios_base::in|std::ios_base::binary};
|
||||||
|
if (!stream.is_open()) {
|
||||||
|
throw std::runtime_error{"could not open: '" + path + "'"};
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string message{std::istreambuf_iterator<char>(stream.rdbuf()),
|
||||||
|
std::istreambuf_iterator<char>()};
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user