Squashed 'third_party/libosmium/' content from commit ce865381f

git-subtree-dir: third_party/libosmium
git-subtree-split: ce865381fb752323ff1e66181f5a49b7f500ffa3
This commit is contained in:
Patrick Niklaus 2017-08-30 09:30:27 +00:00
commit 6eb4f090f9
434 changed files with 81367 additions and 0 deletions

7
.codecov.yml Normal file
View File

@ -0,0 +1,7 @@
ignore:
- "include/gdalcpp.hpp"
- "include/protozero"
- "include/utf8"
- "test/include/catch.hpp"

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
*.swp
.ycm_extra_conf.pyc
/_build*
/build
/libosmium-deps
/.vs*

218
.travis.yml Normal file
View File

@ -0,0 +1,218 @@
#-----------------------------------------------------------------------------
#
# Configuration for continuous integration service at travis-ci.org
#
#-----------------------------------------------------------------------------
language: generic
sudo: false
cache:
directories:
- $HOME/.ccache
env:
global:
- CCACHE_TEMPDIR=/tmp/.ccache-temp
- CCACHE_COMPRESS=1
- CASHER_TIME_OUT=1000
matrix:
include:
# 1/ Linux Clang Builds
- os: linux
compiler: linux-clang35-release
addons:
apt:
sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest']
packages: ['clang-3.5', 'cmake', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='clang++-3.5' BUILD_TYPE='Release'
- os: linux
compiler: linux-clang35-dev
addons:
apt:
sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest']
packages: ['clang-3.5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='clang++-3.5' BUILD_TYPE='Dev'
- os: linux
compiler: linux-clang37-release
addons:
apt:
sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest']
packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='clang++-3.7' BUILD_TYPE='Release'
- os: linux
compiler: linux-clang37-dev
addons:
apt:
sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest']
packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='clang++-3.7' BUILD_TYPE='Dev'
- os: linux
compiler: linux-clang38-release
addons:
apt:
sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test', 'boost-latest']
packages: ['clang-3.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='clang++-3.8' BUILD_TYPE='Release'
- os: linux
compiler: linux-clang38-dev
addons:
apt:
sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test', 'boost-latest']
packages: ['clang-3.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='clang++-3.8' BUILD_TYPE='Dev'
# 2/ Linux GCC Builds
- os: linux
compiler: linux-gcc48-release
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
packages: ['g++-4.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Release'
- os: linux
compiler: linux-gcc48-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
packages: ['g++-4.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Dev'
- os: linux
compiler: linux-gcc49-release
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
packages: ['g++-4.9', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='g++-4.9' BUILD_TYPE='Release'
- os: linux
compiler: linux-gcc49-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
packages: ['g++-4.9', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='g++-4.9' BUILD_TYPE='Dev'
- os: linux
compiler: linux-gcc5-release
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
packages: ['g++-5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='g++-5' BUILD_TYPE='Release'
- os: linux
compiler: linux-gcc5-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
packages: ['g++-5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='g++-5' BUILD_TYPE='Dev'
- os: linux
compiler: linux-gcc6-release
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
packages: ['g++-6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='g++-6' BUILD_TYPE='Release'
- os: linux
compiler: linux-gcc6-dev
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
packages: ['g++-6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='g++-6' BUILD_TYPE='Dev'
- os: linux
compiler: linux-gcc6-coverage
addons:
apt:
sources: ['ubuntu-toolchain-r-test', 'boost-latest']
packages: ['g++-6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin']
env: COMPILER='g++-6' BUILD_TYPE='Coverage'
# 3/ OSX Clang Builds
- os: osx
osx_image: xcode6.4
compiler: xcode64-clang-release
env: COMPILER='clang++' BUILD_TYPE='Release'
- os: osx
osx_image: xcode6.4
compiler: xcode64-clang-dev
env: COMPILER='clang++' BUILD_TYPE='Dev'
- os: osx
osx_image: xcode7
compiler: xcode7-clang-release
env: COMPILER='clang++' BUILD_TYPE='Release'
- os: osx
osx_image: xcode7
compiler: xcode7-clang-dev
env: COMPILER='clang++' BUILD_TYPE='Dev'
- os: osx
osx_image: xcode8.3
compiler: xcode8-clang-release
env: COMPILER='clang++' BUILD_TYPE='Release'
- os: osx
osx_image: xcode8.3
compiler: xcode8-clang-dev
env: COMPILER='clang++' BUILD_TYPE='Dev'
install:
- DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
- mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
- git clone --quiet --depth 1 https://github.com/osmcode/osm-testdata.git
- |
if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
brew remove gdal
brew install cmake boost google-sparsehash gdal || true
fi
- cmake --version
before_script:
- cd ${TRAVIS_BUILD_DIR}
- mkdir build && cd build
- CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_WITH_CCACHE=1 -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata"
script:
- make VERBOSE=1 && ctest --output-on-failure
after_success:
- |
if [ "${BUILD_TYPE}" = "Coverage" ]; then
curl -S -f https://codecov.io/bash -o codecov
chmod +x codecov
gcov-${COMPILER#g++-} -p $(find test/CMakeFiles -name '*.o')
./codecov -Z -c -F unit_tests
gcov-${COMPILER#g++-} -p $(find test/data-tests -name '*.o')
./codecov -Z -c -F data_tests
gcov-${COMPILER#g++-} -p $(find examples -name '*.o')
./codecov -Z -c -F examples
fi

53
.ycm_extra_conf.py Normal file
View File

@ -0,0 +1,53 @@
#-----------------------------------------------------------------------------
#
# Configuration for YouCompleteMe Vim plugin
#
# http://valloric.github.io/YouCompleteMe/
#
#-----------------------------------------------------------------------------
from os.path import realpath, dirname
basedir = dirname(realpath(__file__))
# some default flags
# for more information install clang-3.2-doc package and
# check UsersManual.html
flags = [
'-Werror',
'-Wall',
'-Wextra',
'-pedantic',
'-Wno-return-type',
'-Wno-unused-parameter',
'-Wno-unused-variable',
'-std=c++11',
# '-x' and 'c++' also required
# use 'c' for C projects
'-x',
'c++',
# workaround for https://github.com/Valloric/YouCompleteMe/issues/303
# also see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=800618
'-isystem',
'/usr/lib/ycmd/clang_includes/',
# libosmium include dirs
'-I%s/include' % basedir,
'-I%s/test/include' % basedir,
'-I%s/test/data-test/include' % basedir,
# include third party libraries
'-I/usr/include/gdal',
]
# youcompleteme is calling this function to get flags
# You can also set database for flags. Check: JSONCompilationDatabase.html in
# clang-3.2-doc package
def FlagsForFile( filename ):
return {
'flags': flags,
'do_cache': True
}

770
CHANGELOG.md Normal file
View File

@ -0,0 +1,770 @@
# Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] -
### Added
### Changed
### Fixed
## [2.13.1] - 2017-08-25
### Added
- New "blackhole" file format which throws away all data written into it.
Used for benchmarking.
### Changed
- When reading OPL files, CRLF file endings are now handled correctly.
- Reduce the max number of threads allowed for the `Pool` to 32. This should
still be plenty and might help with test failures on some architectures.
### Fixed
- Tests now run correctly independent of git `core.autocrlf` setting.
- Set binary mode for all files on Windows in example code.
- Low-level file functions now set an invalid parameter handler on Windows
to properly handle errors.
- Restore earlier behaviour allowing zero-length mmap. It is important to
allow zero-length memory mapping, because it is possible that such an index
is empty, for instance when one type of object is missing from an input
file as in https://github.com/osmcode/osmium-tool/issues/65. Drawback is
that files must be opened read-write for this to work, even if we only
want to read from them.
- Use Approx() to compare floating point values in tests.
- Fix broken `Item` test on 32 bit platforms.
## [2.13.0] - 2017-08-15
### Added
- New `RelationsManager` class superseeds the `relations::Collector` class.
The new class is much more modular and easier to extend. If you are using
the Collector class, you are encouraged to switch.
- New `MultipolygonManager` based on the `RelationsManager` class superseeds
the `MultipolygonCollector` class. The examples have been changed to use the
new class and all users are encouraged to switch. There is also a
`MultipolygonManagerLegacy` class if you still need old-style multipolygon
support (see below).
- New `FlexMem` index class that works with input files of any size and
stores the index in memory. This should now be used as the default index
for node location stores. Several example programs now use this index.
- New `CallbackBuffer` class, basically a convenient wrapper around the
`Buffer` class with an additional callback function that is called whenever
the buffer is full.
- Introduce new `ItemStash` class for storing OSM objects in memory.
- New `osmium::geom::overlaps()` function to check if two `Box` objects
overlap.
- Add function `IdSet::used_memory()` to get estimate of memory used in the
set.
- New `is_defined()` and `is_undefined()` methods on `Location` class.
- Tests for all provided example programs. (Some tests currently fail
on Windows for the `osmium_index_lookup` program.)
### Changed
- The area `Assembler` now doesn't work with old-style multipolygons (those
are multipolygon relations with the tags on the outer ways(s) instead of
on the relation) any more. Because old-style multipolygons are now (mostly)
gone from the OSM database this is usually what you want. The new
`AssemblerLegacy` class can be used if you actually need support for
old-style multipolygons, for instance if you are working with historical
data. (In that case you also need to use the `MultipolygonManagerLegacy`
class instead of the `MultipolygonManager` class.)
- Changes for consistent ordering of OSM data: OSM data can come in any order,
but usual OSM files are ordered by type, ID, and version. These changes
extend this ordering to negative IDs which are sometimes used for objects
that have not been uploaded to the OSM server yet. The negative IDs are
ordered now before the positive ones, both in order of their absolute value.
This is the same ordering as JOSM uses.
- Multipolygon assembler now checks for three or more overlapping segments
which are always an error and can report them.
- Enable use of user-provided `thread::Pool` instances in `Reader` and
`Writer` for special use cases.
- Growing a `Buffer` will now work with any capacity parameter, it is
always rounded up for proper alignment. Buffer constructor with three
arguments will now check that commmitted is not larger than capacity.
- Updated embedded protozero to 1.5.2.
- Update version of Catch unit test framework to 1.9.7.
- And, as always, lots of small code cleanups and more tests.
### Fixed
- Buffers larger than 2^32 bytes do now work.
- Output coordinate with value of -2^31 correctly.
- Changeset comments with more than 2^16 characters are now allowed. The new
maximum size is 2^32.
- `ChangesetDiscussionBuilder::add_comment_text()` could fail silently instead
of throwing an exception.
- Changeset bounding boxes are now always output to OSM files (any format)
if at least one of the corners is defined. This is needed to handle broken
data from the main OSM database which contains such cases. The OPL reader
has also been fixed to handle this case.
- In the example `osmium_location_cache_create`, the index file written is
always truncated first.
## [2.12.2] - 2017-05-03
### Added
- Add two argument (key, value) overload of `TagMatcher::operator()`.
### Changed
- Detect, report, and remove duplicate ways in multipolygon relations.
- Change EOF behaviour of Reader: The `Reader::read()` function will now
always return an invalid buffer exactly once to signal EOF.
- Update QGIS multipolygon project that is part of the test suite to show
more problem types.
- Copy multipolygon QGIS file for tests to build dir in cmake step.
- Some code cleanups and improved debug output in multipolygon code.
- Refactor I/O code to simplify code.
- Disable some warnings on MSVC.
- Various small code and build script changes.
### Fixed
- Two bugs in area assembler affecting very complex multipolygons and
multipolygons with overlapping or nearly overlapping lines.
- Invalid use of iterators leading to undefined behaviour in area assembler
code.
- Area assembler stats were not correctly counting inner rings that are
areas in their own right.
- Fix a thread problem valgrind found that might or might not be real.
- Read OPL file correctly even if trailing newline in file is missing.
- Include order for `osmium/index/map` headers and
`osmium/index/node_locations_map.hpp` (or
`osmium/handler/node_locations_for_ways.hpp`) doesn't matter any more.
## [2.12.1] - 2017-04-10
### Added
- New `TagsFilter::set_default_result()` function.
### Changed
- Use larger capacity for `Buffer` if necessary for alignment instead of
throwing an exception. Minimum buffer size is now 64 bytes.
- Check order of input data in relations collector. The relations collector
can not deal with history data or a changes file. This was documented as a
requirement, but often lead to problems, because this was ignored by users.
So it now checks that the input data it gets is ordered and throws an
exception otherwise.
- When writing an OSM file, set generator to libosmium if not set by app.
### Fixed
- Infinite loop in `Buffer::reserve_space()`. (Issue #202.)
- `ObjectPointerCollection::unique()` now removes elements at end.
- Tests comparing double using `==` operator.
- Build on Cygwin.
## [2.12.0] - 2017-03-07
### Added
- `TagMatcher` and `TagsFilter` classes for more flexibly matching tags and
selecting objects based on tags. This obsoletes the less flexible classes
based on `osmium::tags::Filter` classes.
- Extended `index::RelationsMap(Stash|Index)` classes to also allow
parent-to-member lookups.
- New `nrw_array` helper class.
- `ObjectPointerCollection::unique()` function.
### Changed
- Area assembler can now detect invalid locations and report them in the
stats and through the problem reporter. If the new config option
`ignore_invalid_locations` is set, the Assembler will pretend they weren't
even referenced in the ways. (Issue #195.)
- `osmium::area::Assembler::operator()` will now return a boolean reporting
whether building of the area(s) was successful.
- Split up area `Assembler` class into three classes: The
`detail::BasicAssembler` is now the parent class. `Assembler` is the child
class for usual use. The new `GeomAssembler` also derives from
`BasicAssembler` and builds areas without taking tags into account at all.
This is to support osm2pgsql which does tag handling itself. (Issue #194.)
- The `Projection` class can do any projection supported by the Proj.4
library. As a special case it now uses our own Mercator projection
functions when the web mercator projection (EPSG 3857) is used. This is
much faster than going through Proj.4.
- Better error messages for low-level file utility functions.
- Mark `build_tag_list*` functions in `builder_helper.hpp` as deprecated. You
should use the functions from `osmium/builder/attr.hpp` instead.
- Improved performance of the `osmium::tags::match_(any|all|none)_of`
functions.
- Improved performance of string comparison in `tags::Filter`.
- Update version of Catch unit test framework to 1.8.1. This meant some
tests had to be updated.
- Use `get_noexcept()` in `NodeLocationsForWays` handler.
- And lots of code and test cleanups...
### Fixed
- Terminate called on full non-auto-growing buffer. (Issue #189.)
- When file formats were used that were not compiled into the binary, it
terminated instead of throwing. (Issue #197.)
- Windows build problem related to including two different winsock versions.
- Windows build problem related to forced build for old Windows versions.
(Issue #196.)
- Clear stream contents in ProblemReporterException correctly.
- Add `-pthread` compiler and linker options on Linux/OSX. This should fix
a problem where some linker versions will not link binaries correctly when
the `--as-needed` option is used.
- The `Filter::count()` method didn't compile at all.
- XML reader doesn't fail on relation member ref=0 any more.
## [2.11.0] - 2017-01-14
### Added
- New index::RelationsMap(Stash|Index) classes implementing an index for
looking up parent relation IDs given a member relation ID.
- Add `get_noexcept()` method to all index maps. For cases where ids are
often not in the index using this can speed up a program considerably.
- New non-const WayNodeList::operator[].
- Default constructed "invalid" Coordinates.
- Tile constructor from web mercator coordinates and some helper
functions for tile arithmetic.
- Tag matcher matching keys using a regex.
- New `envelope()` functions on `NodeRefList`, `Way`, and `Area` returning a
`Box` object with the geometric envelope of the object.
- Add `amenity_list` example.
### Changed
- Replaced the implementation for the web mercator projection using the usual
tan-formula with a polynomial approximation which is much faster and good
enough for OSM data which only has ~1cm resolution anyway. See
https://github.com/osmcode/mercator-projection for all the details and
benchmarks. You can disable this by defining the macro
`OSMIUM_USE_SLOW_MERCATOR_PROJECTION` before including any of the Osmium
headers.
- Removed the outdated `Makefile`. Always use CMake directly to build.
- Refactoring of `osmium::apply()` removing the resursive templates for faster
compile times and allowing rvalue handlers.
- Lots of code and test cleanups and more documentation.
### Fixed
- Handle endianess on FreeBSD properly.
- Fixed doxygen config for reproducible builds.
## [2.10.3] - 2016-11-20
### Changed
- Round out ObjectPointerCollection implementation and test it.
- Updated embedded protozero to 1.4.5.
## [2.10.2] - 2016-11-16
### Changed
- Updated embedded protozero to 1.4.4.
### Fixed
- Buffer overflow in osmium::Buffer.
## [2.10.1] - 2016-11-15
### Changed
- Updated embedded protozero to 1.4.3.
### Fixed
- Made IdSet work on 32bit systems.
- Fixed endianness check for WKB tests.
## [2.10.0] - 2016-11-11
### Added
- The `Reader` can take an additional optional `read_meta` flag. If this is
set to false the PBF input will ignore metadata on OSM objects (like version,
timestamp, uid, ...) which speeds up file reading by 10 to 20%.
- New `IdSet` virtual class with two implementations: `IdSetDense` and
`IdSetSmall`. Used to efficiently store a set of Ids. This is often needed
to track, for instance, which nodes are needed for ways, etc.
- Added more examples and better documented existing examples.
- Add a benchmark "mercator" converting all node locations in a file to
WebMercator and creating geometries in WKB format.
### Changed
- Better queue handling makes I/O faster in some circumstances.
- The `FindOsmium.cmake` CMake script can now check a current enough libosmium
version is found.
- Builders can now be constructed with a reference to parent builder.
- Made builders more robust by adding asserts that will catch common usage
problems.
- Calling `OSMObjectBuilder::add_user()` is now optional, and the method was
renamed to `set_user()`. (`add_user()` is marked as deprecated.)
- Benchmarks now show compiler and compiler options used.
- `Builder::add_item()` now takes a reference instead of pointer (old version
of the function marked as deprecated).
- GEOS support is deprecated. It does not work any more for GEOS 3.6 or newer.
Reason is the changed interface in GEOS 3.6. If there is interest for the
GEOS support, we can add support back in later (but probably using the
GEOS C API which is more stable than the C++ API). Some tests using GEOS
were rewritten to work without it.
- The `BoolVector` has been deprecated in favour of the new `IdSet` classes.
- Lots of code cleanups and improved API documentation in many places.
- The relations collector can now tell you whether a relation member was in
the input data. See the new `is_available()` and
`get_availability_and_offset()` methods.
- Updated embedded Catch unit test header to version 1.5.8.
### Fixed
- Parsing of coordinates starting with decimal dot and coordinates in
scientific notation.
- `~` operator for `entity_bits` doesn't set unused bits any more.
- Progress bar can now be (temporarily) removed, to allow other output.
## [2.9.0] - 2016-09-15
### Added
- Support for reading OPL files.
- For diff output OSM objects in buffers can be marked as only in one or the
other file. The OPL and debug output formats support diff output based on
this.
- Add documentation and range checks to `Tile` struct.
- More documentation.
- More examples and more extensive comments on examples.
- Support for a progress report in `osmium::io::Reader()` and a `ProgressBar`
utility class to use it.
- New `OSMObject::set_timestamp(const char*)` function.
### Changed
- Parse coordinates in scientific notations ourselves.
- Updated included protozero version to 1.4.2.
- Lots of one-argument constructors are now explicit.
- Timestamp parser now uses our own implementation instead of strptime.
This is faster and independant of locale settings.
- More cases of invalid areas with duplicate segments are reported as
errors.
### Fixed
- Fixed a problem limiting cache file sizes on Windows to 32 bit.
- Fixed includes.
- Exception messages for invalid areas do not report "area contains no rings"
any more, but "invalid area".
## [2.8.0] - 2016-08-04
### Added
- EWKT support.
- Track `pop` type calls and queue underruns when `OSMIUM_DEBUG_QUEUE_SIZE`
environment variable is set.
### Changed
- Switched to newest protozero v1.4.0. This should deliver some speedups
when parsing PBF files. This also removes the DeltaEncodeIterator class,
which isn't needed any more.
- Uses `std::unordered_map` instead of `std::map` in PBF string table code
speeding up writing of PBF files considerably.
- Uses less memory when writing PBF files (smaller string table by default).
- Removes dependency on sparsehash and boost program options libraries for
examples.
- Cleaned up threaded queue code.
### Fixed
- A potentially very bad bug was fixed: When there are many and/or long strings
in tag keys and values and/or user names and/or relation roles, the string
table inside a PBF block would overflow. I have never seen this happen for
normal OSM data, but that doesn't mean it can't happen. The result is that
the strings will all be mixed up, keys for values, values for user names or
whatever.
- Automatically set correct SRID when creating WKB and GEOS geometries.
Note that this changes the behaviour of libosmium when creating GEOS
geometries. Before we created them with -1 as SRID unless set otherwise.
Manual setting of the SRID on the GEOSGeometryFactory is now deprecated.
- Allow coordinates of nodes in scientific notation when reading XML files.
This shouldn't be used really, but sometimes you can find them.
## [2.7.2] - 2016-06-08
### Changed
- Much faster output of OSM files in XML, OPL, or debug formats.
### Fixed
- Parsing and output of coordinates now faster and always uses decimal dot
independant of locale setting.
- Do not output empty discussion elements in changeset XML output.
- Data corruption regression in mmap based indexes.
## [2.7.1] - 2016-06-01
### Fixes
- Update version number in version.hpp.
## [2.7.0] - 2016-06-01
### Added
- New functions for iterating over specific item types in buffers
(`osmium::memory::Buffer::select()`), over specific subitems
(`osmium::OSMObject::subitems()`), and for iterating over all rings of
an area (`osmium::Areas::outer_rings()`, `inner_rings()`).
- Debug output optionally prints CRC32 when `add_crc32` file option is set.
### Changed
- XML parser will not allow any XML entities which are usually not used in OSM
files anyway. This can help avoiding DOS attacks.
- Removed SortedQueue implementation which was never used.
- Also incorporate Locations in NodeRefs into CRC32 checksums. This means
all checksums will be different compared to earlier versions of libosmium.
- The completely new algorithm for assembling multipolygons is much faster,
has better error reporting, generates statistics and can build more complex
multipolygons correctly. The ProblemReporter classes have changed to make
this happen, if you have written your own, you have to fix it.
- Sparse node location stores are now only sorted if needed, ie. when nodes
come in unordered.
### Fixed
- Output operator for Location shows full precision.
- Undefined behaviour in WKB writer and `types_from_string()` function.
- Fix unsigned overflow in pool.hpp.
- OSM objects are now ordered by type (nodes, then ways, then relations),
then ID, then version, then timestamp. Ordering by timestamp is normally
not necessary, because there can't be two objects with same type, ID, and
version but different timestamp. But this can happen when diffs are
created from OSM extracts, so we check for this here. This change also
makes sure IDs are always ordered by absolute IDs, positives first, so
order is 0, 1, -1, 2, -2, ...
- Data corruption bug fixed in disk based indexes (used for the node
location store for instance). This only affected you, if you created
and index, closed it, and re-opened it (possibly in a different process)
and if there were missing nodes. If you looked up those nodes, you got
location (0,0) back instead of an error.
- Memory corruption bug showing up with GDAL 2.
## [2.6.1] - 2016-02-22
### Added
- Add `WITH_PROFILING` option to CMake config. When enabled, this sets the
`-fno-omit-frame-pointer` compiler option.
### Changed
- Massive speed improvements when building multipolygons.
- Uses (and includes) new version 1.3.0 of protozero library.
- Removed dependency on Boost Iterator for PBF writer.
- Example program `osmium_area_test` now uses `cerr` instead of `cout` for
debug output.
## [2.6.0] - 2016-02-04
### Added
- The new handler osmium::handler::CheckOrder can be used to check that a
file is properly ordered.
- Add new method to build OSM nodes, ways, relations, changesets, and areas
in buffers that wraps the older Builder classes. The new code is much easier
to use and very flexible. There is no documentation yet, but the tests in
`test/t/builder/test_attr.cpp` can give you an idea how it works.
- Add util class to get memory usage of current process on Linux.
### Changed
- New Buffer memory management speeds up Buffer use, because it doesn't clear
the memory unnecessarily.
### Fixed
- osmium::Box::extend() function now ignores invalid locations.
- Install of external library headers.
- Check way has at least one node before calling `is_closed()` in area
assembler.
- Declaration/definition of some friend functions was in the wrong namespace.
## [2.5.4] - 2015-12-03
### Changed
- Included gdalcpp.hpp header was updated to version 1.1.1.
- Included protozero library was updated to version 1.2.3.
- Workarounds for missing constexpr support in Visual Studio removed. All
constexpr features we need are supported now.
- Some code cleanup after running clang-tidy on the code.
- Re-added `Buffer::value_type` typedef. Turns out it is needed when using
`std::back_inserter` on the Buffer.
### Fixed
- Bugs with Timestamp code on 32 bit platforms. This necessitated
some changes in Timestamp which might lead to changes in user
code.
- Bug in segment intersection code (which appeared on i686 platform).
## [2.5.3] - 2015-11-17
### Added
- `osmium::make_diff_iterator()` helper function.
### Changed
- Deprecated `osmium::Buffer::set_full_callback()`.
- Removed DataFile class which was never used anywhere.
- Removed unused and obscure `Buffer::value_type` typedef.
### Fixed
- Possible overrun in Buffer when using the full-callback.
- Incorrect swapping of Buffer.
## [2.5.2] - 2015-11-06
# Fixed
- Writing data through an OutputIterator was extremly slow due to
lock contention.
## [2.5.1] - 2015-11-05
### Added
- Header `osmium/fwd.hpp` with forward declarations of the most commonly
used Osmium classes.
### Changed
- Moved `osmium/io/overwrite.hpp` to `osmium/io/writer_options.hpp`
If you still include the old file, you'll get a warning.
## [2.5.0] - 2015-11-04
### Added
- Helper functions to make input iterator ranges and output iterators.
- Add support for reading o5m and o5c files.
- Option for osmium::io::Writer to fsync file after writing.
- Lots of internal asserts() and other robustness checks.
### Changed
- Updated included protozero library to version 1.2.0.
- Complete overhaul of the I/O system making it much more robust against
wrong data and failures during I/O operations.
- Speed up PBF writing by running parts of it in parallel.
- OutputIterator doesn't hold an internal buffer any more, but it uses
one in Writer. Calling flush() on the OutputIterator isn't needed any
more.
- Reader now throws when trying to read after eof or an error.
- I/O functions that used to throw `std::runtime_error` now throw
`osmium::io_error` or derived.
- Optional parameters on `osmium::io::Writer` now work in any order.
### Fixed
- PBF reader now decodes locations of invisible nodes properly.
- Invalid Delta encode iterator dereference.
- Lots of includes fixed to include (only) what's used.
- Dangling reference in area assembly code.
## [2.4.1] - 2015-08-29
### Fixed
- CRC calculation of tags and changesets.
## [2.4.0] - 2015-08-29
### Added
- Checks that user names, member roles and tag keys and values are not longer
than 256 * 4 bytes. That is the maximum length 256 Unicode characters
can have in UTF-8 encoding.
- Support for GDAL 2. GDAL 1 still works.
### Changed
- Improved CMake build scripts.
- Updated internal version of Protozero to 1.1.0.
- Removed `toogr*` examples. They are in their own repository now.
See https://github.com/osmcode/osm-gis-export.
- Files about to be memory-mapped (for instance index files) are now set
to binary mode on Windows so the application doesn't have to do this.
### Fixed
- Hanging program when trying to open file with an unknown file format.
- Building problems with old boost versions.
- Initialization errors in PBF writer.
- Bug in byte swap code.
- Output on Windows now always uses binary mode, even when writing to
stdout, so OSM xml and opl files always use LF line endings.
## [2.3.0] - 2015-08-18
### Added
- Allow instantiating osmium::geom::GEOSFactory with existing GEOS factory.
- Low-level functions to support generating a architecture- and endian-
independant CRC from OSM data. This is intended to be uses with boost::crc.
- Add new debug output format. This format is not intended to be read
automatically, but for human consumption. It formats the data nicely.
- Make writing of metadata configurable for XML and OPL output (use
`add_metadata=false` as file option).
### Changed
- Changed `add_user()` and `add_role()` in builders to use string length
without the 0-termination.
- Improved code setting file format from suffix/format argument.
- Memory mapping utility class now supports readonly, private writable or
shared writable operation.
- Allow empty version (0) in PBF files.
- Use utf8cpp header-only lib instead of boost for utf8 decoding. The library
is included in the libosmium distribution.
- New PBF reader and writer based on the protozero. A complete rewrite of the
code for reading and writing OSM PBF files. It doesn't use the Google
protobuf library and it doesn't use the OSMPBF/OSM-Binary library any more.
Instead is uses the protozero lightweight protobuf header library which is
included in the code. Not only does the new code have less dependencies, it
is faster and more robust. https://github.com/mapbox/protozero
### Fixed
- Various smaller bug fixes.
- Add encoding for relation member roles in OPL format.
- Change character encoding to new format in OPL: variable length hex code
between % characters instead of a % followed by 4-digit hex code. This is
necessary because unicode characters can be longer than the 4-digit hex
code.
- XML writer: The linefeed, carriage return, and tab characters are now
escaped properly.
- Reading large XML files could block.
## [2.2.0] - 2015-07-04
### Added
- Conversion functions for some low-level types.
- BoolVector index class.
- `min_op`/`max_op` utility functions.
- More tests here and there.
- Helper methods `is_between()` and `is_visible_at()` to DiffObject.
- GeoJSON factory using the RapidJSON library.
- Support for tile calculations.
- Create simple polygons from ways in geom factories.
- `MemoryMapping` and `TypedMemoryMapping` helper classes.
- `close()` function to `mmap_vector_base` class.
- Function on `Buffer` class to get iterator to specific offset.
- Explicit cast operator from `osmium::Timestamp` to `uint32_t`.
### Changed
- Throw exception on illegal values in functions parsing strings to get ids,
versions, etc.
- Improved error message for geometry exceptions.
### Fixed
- Throw exception from `dump_as_array()` and `dump_as_list()` functions if not
implemented in an index.
- After writing OSM files, program could stall up to a second.
- Dense location store was written out only partially.
- Use `uint64_t` as counter in benchmarks, so there can be no overflows.
- Example programs now read packed XML files, too.
- Refactoring of memory mapping code. Removes leak on Windows.
- Better check for invalid locations.
- Mark `cbegin()` and `cend()` of `mmap_vector_base` as const functions.
## [2.1.0] - 2015-03-31
### Added
- When writing PBF files, sorting the PBF stringtables is now optional.
- More tests and documentation.
### Changed
- Some functions are now declared `noexcept`.
- XML parser fails now if the top-level element is not `osm` or `osmChange`.
### Fixed
- Race condition in PBF reader.
- Multipolygon collector was accessing non-existent NodeRef.
- Doxygen documentation wan't showing all classes/functions due to a bug in
Doxygen (up to version 1.8.8). This version contains a workaround to fix
this.
[unreleased]: https://github.com/osmcode/libosmium/compare/v2.13.1...HEAD
[2.13.1]: https://github.com/osmcode/libosmium/compare/v2.13.0...v2.13.1
[2.13.0]: https://github.com/osmcode/libosmium/compare/v2.12.2...v2.13.0
[2.12.2]: https://github.com/osmcode/libosmium/compare/v2.12.1...v2.12.2
[2.12.1]: https://github.com/osmcode/libosmium/compare/v2.12.0...v2.12.1
[2.12.0]: https://github.com/osmcode/libosmium/compare/v2.11.0...v2.12.0
[2.11.0]: https://github.com/osmcode/libosmium/compare/v2.10.3...v2.11.0
[2.10.3]: https://github.com/osmcode/libosmium/compare/v2.10.2...v2.10.3
[2.10.2]: https://github.com/osmcode/libosmium/compare/v2.10.1...v2.10.2
[2.10.1]: https://github.com/osmcode/libosmium/compare/v2.10.0...v2.10.1
[2.10.0]: https://github.com/osmcode/libosmium/compare/v2.9.0...v2.10.0
[2.9.0]: https://github.com/osmcode/libosmium/compare/v2.8.0...v2.9.0
[2.8.0]: https://github.com/osmcode/libosmium/compare/v2.7.2...v2.8.0
[2.7.2]: https://github.com/osmcode/libosmium/compare/v2.7.1...v2.7.2
[2.7.1]: https://github.com/osmcode/libosmium/compare/v2.7.0...v2.7.1
[2.7.0]: https://github.com/osmcode/libosmium/compare/v2.6.1...v2.7.0
[2.6.1]: https://github.com/osmcode/libosmium/compare/v2.6.0...v2.6.1
[2.6.0]: https://github.com/osmcode/libosmium/compare/v2.5.4...v2.6.0
[2.5.4]: https://github.com/osmcode/libosmium/compare/v2.5.3...v2.5.4
[2.5.3]: https://github.com/osmcode/libosmium/compare/v2.5.2...v2.5.3
[2.5.2]: https://github.com/osmcode/libosmium/compare/v2.5.1...v2.5.2
[2.5.1]: https://github.com/osmcode/libosmium/compare/v2.5.0...v2.5.1
[2.5.0]: https://github.com/osmcode/libosmium/compare/v2.4.1...v2.5.0
[2.4.1]: https://github.com/osmcode/libosmium/compare/v2.4.0...v2.4.1
[2.4.0]: https://github.com/osmcode/libosmium/compare/v2.3.0...v2.4.0
[2.3.0]: https://github.com/osmcode/libosmium/compare/v2.2.0...v2.3.0
[2.2.0]: https://github.com/osmcode/libosmium/compare/v2.1.0...v2.2.0
[2.1.0]: https://github.com/osmcode/libosmium/compare/v2.0.0...v2.1.0

569
CMakeLists.txt Normal file
View File

@ -0,0 +1,569 @@
#-----------------------------------------------------------------------------
#
# CMake Config
#
# Libosmium
#
#-----------------------------------------------------------------------------
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
#-----------------------------------------------------------------------------
#
# Project version
#
#-----------------------------------------------------------------------------
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Coverage"
CACHE STRING
"List of available configuration types"
FORCE)
project(libosmium)
set(LIBOSMIUM_VERSION_MAJOR 2)
set(LIBOSMIUM_VERSION_MINOR 13)
set(LIBOSMIUM_VERSION_PATCH 1)
set(LIBOSMIUM_VERSION
"${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
#-----------------------------------------------------------------------------
#
# Build options
#
# (Change with -DOPTION=VALUE on cmake command line.)
#
#-----------------------------------------------------------------------------
if(CMAKE_BUILD_TYPE STREQUAL "Dev")
set(dev_build ON)
set(data_test_build ON)
else()
set(dev_build OFF)
set(data_test_build OFF)
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
set(data_test_build ON)
endif()
option(BUILD_EXAMPLES "compile example programs" ON)
option(BUILD_TESTING "compile unit tests, please run them with ctest" ON)
option(BUILD_HEADERS "compile every header file on its own" ${dev_build})
option(BUILD_BENCHMARKS "compile benchmark programs" ${dev_build})
option(BUILD_DATA_TESTS "compile data tests, please run them with ctest" ${data_test_build})
option(INSTALL_GDALCPP "also install gdalcpp headers" OFF)
option(INSTALL_PROTOZERO "also install protozero headers" OFF)
option(INSTALL_UTFCPP "also install utfcpp headers" OFF)
option(WITH_PROFILING "add flags needed for profiling" OFF)
#-----------------------------------------------------------------------------
#
# CCache support
#
#-----------------------------------------------------------------------------
option(BUILD_WITH_CCACHE "build using ccache" OFF)
if(BUILD_WITH_CCACHE)
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "CCACHE_CPP2=1 ${CCACHE_PROGRAM}")
# workaround for some clang versions
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
add_definitions(-Qunused-arguments)
endif()
endif()
endif()
#-----------------------------------------------------------------------------
#
# Coverage support
#
#-----------------------------------------------------------------------------
## This leads to all sorts of compile problems, so disable for now
#include(CheckCXXCompilerFlag)
#check_cxx_compiler_flag("-fkeep-inline-functions" HAS_KEEP_INLINE_FUNCTIONS)
#if(HAS_KEEP_INLINE_FUNCTIONS)
# set(extra_coverage_flags_ "-fkeep-inline-functions")
#endif()
set(CMAKE_CXX_FLAGS_COVERAGE
"-g -O0 -fno-inline-functions -fno-inline --coverage ${extra_coverage_flags_}"
CACHE STRING "Flags used by the compiler during coverage builds.")
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
"--coverage"
CACHE STRING "Flags used by the linker during coverage builds.")
if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
if(BUILD_EXAMPLES OR BUILD_HEADERS OR BUILD_BENCHMARKS)
message(WARNING "Coverage builds don't work for anything but the tests")
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "llvm-cov-\\1.\\2"
gcov_ ${CMAKE_CXX_COMPILER_VERSION})
else()
set(gcov_ "gcov")
endif()
find_program(GCOV ${gcov_} DOC "Coverage tool")
find_program(GCOVR "gcovr" DOC "Coverage report tool")
set(coverage_report_dir "${CMAKE_BINARY_DIR}/coverage")
file(MAKE_DIRECTORY ${coverage_report_dir})
add_custom_target(coverage
${GCOVR}
${CMAKE_BINARY_DIR}
--root=${CMAKE_SOURCE_DIR}
--html --html-details
#--verbose
#--keep
'--filter=.*include/osmium.*'
--sort-percentage
--gcov-executable=${GCOV}
--output=${coverage_report_dir}/index.html)
endif()
#-----------------------------------------------------------------------------
#
# Find external dependencies
#
#-----------------------------------------------------------------------------
find_package(Boost 1.38)
mark_as_advanced(CLEAR BOOST_ROOT)
if(Boost_FOUND)
include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
else()
set(BOOST_ROOT "NOT FOUND: please choose" CACHE PATH "")
message(FATAL_ERROR "PLEASE, specify the directory where the Boost library is installed in BOOST_ROOT")
endif()
# set OSMIUM_INCLUDE_DIR so FindOsmium will not set anything different
set(OSMIUM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include")
include_directories(${OSMIUM_INCLUDE_DIR})
find_package(Osmium COMPONENTS io gdal geos proj sparsehash)
# The find_package put the directory where it found the libosmium includes
# into OSMIUM_INCLUDE_DIRS. We remove it again, because we want to make
# sure to use our own include directory already set up above.
list(FIND OSMIUM_INCLUDE_DIRS "${OSMIUM_INCLUDE_DIR}" _own_index)
list(REMOVE_AT OSMIUM_INCLUDE_DIRS ${_own_index})
set(_own_index)
include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS})
if(MSVC)
find_path(GETOPT_INCLUDE_DIR getopt.h)
find_library(GETOPT_LIBRARY NAMES wingetopt)
if(GETOPT_INCLUDE_DIR AND GETOPT_LIBRARY)
include_directories(SYSTEM ${GETOPT_INCLUDE_DIR})
list(APPEND OSMIUM_LIBRARIES ${GETOPT_LIBRARY})
else()
set(GETOPT_MISSING 1)
endif()
endif()
#-----------------------------------------------------------------------------
#
# Decide which C++ version to use (Minimum/default: C++11).
#
#-----------------------------------------------------------------------------
if(NOT MSVC)
if(NOT USE_CPP_VERSION)
if(CYGWIN)
set(USE_CPP_VERSION gnu++11)
else()
set(USE_CPP_VERSION c++11)
endif()
endif()
message(STATUS "Use C++ version: ${USE_CPP_VERSION}")
# following only available from cmake 2.8.12:
# add_compile_options(-std=${USE_CPP_VERSION})
# so using this instead:
add_definitions(-std=${USE_CPP_VERSION})
endif()
#-----------------------------------------------------------------------------
#
# Compiler and Linker flags
#
#-----------------------------------------------------------------------------
if(MSVC)
set(USUAL_COMPILE_OPTIONS "/Ox")
# do not show warnings caused by missing .pdb files for libraries
set(USUAL_LINK_OPTIONS "/debug /ignore:4099")
else()
set(USUAL_COMPILE_OPTIONS "-O3 -g")
set(USUAL_LINK_OPTIONS "")
endif()
if(WIN32)
add_definitions(-DWIN32 -D_WIN32 -DMSWIN32 -DBGDWIN32)
endif()
set(CMAKE_CXX_FLAGS_DEV "${USUAL_COMPILE_OPTIONS}"
CACHE STRING "Flags used by the compiler during developer builds."
FORCE)
set(CMAKE_EXE_LINKER_FLAGS_DEV "${USUAL_LINK_OPTIONS}"
CACHE STRING "Flags used by the linker during developer builds."
FORCE)
mark_as_advanced(
CMAKE_CXX_FLAGS_DEV
CMAKE_EXE_LINKER_FLAGS_DEV
)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${USUAL_COMPILE_OPTIONS}"
CACHE STRING "Flags used by the compiler during RELWITHDEBINFO builds."
FORCE)
set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${USUAL_LINK_OPTIONS}"
CACHE STRING "Flags used by the linker during RELWITHDEBINFO builds."
FORCE)
mark_as_advanced(
CMAKE_CXX_FLAGS_RELWITHDEBINFO
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
)
if(WITH_PROFILING)
add_definitions(-fno-omit-frame-pointer)
endif()
#-----------------------------------------------------------------------------
#
# Build Type
#
#-----------------------------------------------------------------------------
# In 'Dev' mode: compile with very strict warnings and turn them into errors.
if(CMAKE_BUILD_TYPE STREQUAL "Dev")
if(NOT MSVC)
add_definitions(-Werror)
endif()
add_definitions(${OSMIUM_WARNING_OPTIONS})
# add_definitions(${OSMIUM_WARNING_OPTIONS} ${OSMIUM_DRACONIC_CLANG_OPTIONS} -Wno-documentation -Wno-format-nonliteral -Wno-deprecated -Wno-covered-switch-default -Wno-shadow)
endif()
# Force RelWithDebInfo build type if none was given
if(CMAKE_BUILD_TYPE)
set(build_type ${CMAKE_BUILD_TYPE})
else()
set(build_type "RelWithDebInfo")
endif()
set(CMAKE_BUILD_TYPE ${build_type}
CACHE STRING
"Choose the type of build, options are: ${CMAKE_CONFIGURATION_TYPES}."
FORCE)
#-----------------------------------------------------------------------------
#
# Unit and data tests
#
#-----------------------------------------------------------------------------
enable_testing()
if(BUILD_TESTING OR BUILD_DATA_TESTS)
find_program(MEMORYCHECK_COMMAND valgrind)
set(MEMORYCHECK_COMMAND_OPTIONS
"--trace-children=yes --leak-check=full --show-reachable=yes --error-exitcode=1")
set(MEMORYCHECK_SUPPRESSIONS_FILE "${PROJECT_SOURCE_DIR}/test/valgrind.supp")
endif()
if(BUILD_TESTING)
add_subdirectory(test)
endif()
if(BUILD_DATA_TESTS)
add_subdirectory(test/data-tests)
endif()
if(BUILD_EXAMPLES)
add_subdirectory(test/examples)
endif()
#-----------------------------------------------------------------------------
#
# Optional "cppcheck" target that checks C++ code
#
#-----------------------------------------------------------------------------
message(STATUS "Looking for cppcheck")
find_program(CPPCHECK cppcheck)
if(CPPCHECK)
message(STATUS "Looking for cppcheck - found")
set(CPPCHECK_OPTIONS
--language=c++
--quiet
-j4
--inline-suppr
--enable=warning,style,performance,portability,information,missingInclude
--force
-Uassert -DPROTOZERO_STRICT_API -DPROTOZERO_USE_BUILTIN_BSWAP -UPROTOZERO_USE_VIEW)
# cpp doesn't find system includes for some reason, suppress that report
set(CPPCHECK_OPTIONS ${CPPCHECK_OPTIONS} --suppress=missingIncludeSystem)
file(GLOB_RECURSE ALL_INCLUDES include/osmium/*.hpp)
file(GLOB ALL_EXAMPLES examples/*.cpp)
file(GLOB ALL_BENCHMARKS benchmarks/*.cpp)
file(GLOB ALL_UNIT_TESTS test/t/*/test_*.cpp)
file(GLOB ALL_DATA_TESTS test/data-tests/*.cpp)
if(Osmium_DEBUG)
message(STATUS "Checking includes : ${ALL_INCLUDES}")
message(STATUS "Checking example code : ${ALL_EXAMPLES}")
message(STATUS "Checking benchmarks : ${ALL_BENCHMARKS}")
message(STATUS "Checking unit test code: ${ALL_UNIT_TESTS}")
message(STATUS "Checking data test code: ${ALL_DATA_TESTS}")
endif()
set(CPPCHECK_FILES
${ALL_INCLUDES}
${ALL_EXAMPLES}
${ALL_BENCHMARKS}
${ALL_UNIT_TESTS}
${ALL_DATA_TESTS})
add_custom_target(cppcheck
${CPPCHECK}
--std=c++11 ${CPPCHECK_OPTIONS}
-I ${CMAKE_SOURCE_DIR}/include
${CPPCHECK_FILES}
)
else()
message(STATUS "Looking for cppcheck - not found")
message(STATUS " Build target 'cppcheck' will not be available.")
endif()
#-----------------------------------------------------------------------------
#
# Examples, benchmarks and documentation
#
#-----------------------------------------------------------------------------
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
if(BUILD_BENCHMARKS)
add_subdirectory(benchmarks)
endif()
add_subdirectory(doc)
#-----------------------------------------------------------------------------
#
# Headers
#
# This will try to compile include files on their own to detect missing
# include directives and other dependency-related problems. Note that if this
# work, it is not enough to be sure it will compile in production code.
# But if it reports an error we know we are missing something.
#
#-----------------------------------------------------------------------------
if(BUILD_HEADERS)
file(GLOB_RECURSE
ALL_HPPS
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/include"
include/osmium/*.hpp)
file(MAKE_DIRECTORY header_check)
foreach(hpp ${ALL_HPPS})
string(REPLACE ".hpp" "" tmp ${hpp})
string(REPLACE "/" "__" libname ${tmp})
# Create a dummy .cpp file that includes the header file we want to
# check.
set(DUMMYCPP ${CMAKE_BINARY_DIR}/header_check/${libname}.cpp)
file(WRITE ${DUMMYCPP} "#include <${hpp}>\n")
# There is no way in CMake to just compile but not link a C++ file,
# so we pretend to build a library here.
add_library(${libname} STATIC ${DUMMYCPP} include/${hpp})
#### this is better but only supported from cmake 3.0:
###add_library(${libname} OBJECT ${DUMMYCPP} include/${hpp})
endforeach()
endif()
#-----------------------------------------------------------------------------
#
# Optional "clang-tidy" target
#
#-----------------------------------------------------------------------------
message(STATUS "Looking for clang-tidy")
find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-3.9 clang-tidy-3.8 clang-tidy-3.7 clang-tidy-3.6 clang-tidy-3.5)
if(CLANG_TIDY)
message(STATUS "Looking for clang-tidy - found")
if(BUILD_EXAMPLES)
file(GLOB CT_ALL_EXAMPLES examples/*.cpp)
endif()
if(BUILD_TESTING)
file(GLOB CT_ALL_UNIT_TESTS test/t/*/test_*.cpp)
endif()
if(BUILD_HEADERS)
file(GLOB_RECURSE CT_ALL_INCLUDES ${CMAKE_BINARY_DIR}/header_check/osmium__*.cpp)
endif()
if(BUILD_BENCHMARKS)
file(GLOB CT_ALL_BENCHMARKS benchmarks/*.cpp)
endif()
if(BUILD_DATA_TESTS)
file(GLOB CT_ALL_DATA_TESTS test/data-tests/*.cpp)
endif()
if(Osmium_DEBUG)
message(STATUS "Checking example code : ${CT_ALL_EXAMPLES}")
message(STATUS "Checking unit test code: ${CT_ALL_UNIT_TESTS}")
message(STATUS "Checking includes : ${CT_ALL_INCLUDES}")
message(STATUS "Checking benchmarks : ${CT_ALL_BENCHMARKS}")
message(STATUS "Checking data test code: ${CT_ALL_DATA_TESTS}")
endif()
set(CT_CHECK_FILES
${CT_ALL_EXAMPLES}
${CT_ALL_UNIT_TESTS}
${CT_ALL_INCLUDES}
${CT_ALL_BENCHMARKS}
${CT_ALL_DATA_TESTS})
# For a list of check options, see:
# http://clang.llvm.org/extra/clang-tidy/checks/list.html
list(APPEND CT_CHECKS "cert-*"
"-cert-err60-cpp") # even the std lib doesn't do this
# disabled, because it is slow
# list(APPEND CT_CHECKS "clang-analyzer-*")
list(APPEND CT_CHECKS "google-*"
"-google-explicit-constructor"
"-google-readability-casting"
"-google-readability-function")
list(APPEND CT_CHECKS "llvm-*"
"-llvm-include-order")
list(APPEND CT_CHECKS "misc-*"
"-misc-argument-comment")
list(APPEND CT_CHECKS "modernize-*"
"-modernize-make-unique") # not available in C++11
list(APPEND CT_CHECKS "readability-*"
"-readability-identifier-naming"
"-readability-implicit-bool-cast"
"-readability-named-parameter")
string(REPLACE ";" "," ALL_CHECKS "${CT_CHECKS}")
add_custom_target(clang-tidy
${CLANG_TIDY}
-p ${CMAKE_BINARY_DIR}
-header-filter='include/osmium/.*'
-checks="${ALL_CHECKS}"
${CT_CHECK_FILES}
)
else()
message(STATUS "Looking for clang-tidy - not found")
message(STATUS " Build target 'clang-tidy' will not be available.")
endif()
#-----------------------------------------------------------------------------
#
# Installation
#
# External libraries are only installed if the options are set in case they
# are installed from somewhere else.
#
#-----------------------------------------------------------------------------
install(DIRECTORY include/osmium DESTINATION include)
if(INSTALL_GDALCPP)
install(FILES include/gdalcpp.hpp DESTINATION include)
endif()
if(INSTALL_PROTOZERO)
install(DIRECTORY include/protozero DESTINATION include)
endif()
if(INSTALL_UTFCPP)
install(FILES include/utf8.h DESTINATION include)
install(DIRECTORY include/utf8 DESTINATION include)
endif()
#-----------------------------------------------------------------------------
#
# Packaging
#
#-----------------------------------------------------------------------------
set(CPACK_PACKAGE_VERSION_MAJOR ${LIBOSMIUM_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${LIBOSMIUM_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${LIBOSMIUM_VERSION_PATCH})
if(WIN32)
set(CPACK_GENERATOR ZIP)
else()
set(CPACK_GENERATOR TGZ)
endif()
include(CPack)
#-----------------------------------------------------------------------------
#
# Print warnings at the end
#
#-----------------------------------------------------------------------------
if(BUILD_DATA_TESTS AND OSM_TESTDATA STREQUAL "OSM_TESTDATA-NOTFOUND")
message("\n========================== WARNING ==========================")
message("osm-testdata directory not found, data tests were disabled!\n")
message("You can get it from https://github.com/osmcode/osm-testdata")
message("Clone it into the same directory libosmium is in")
message("or set the OSM_TESTDATA cmake variable to its path.")
message("=============================================================\n")
endif()
#-----------------------------------------------------------------------------

12
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,12 @@
Some rules for contributing to this project:
* Please open a separate issue for each problem, question, or comment you have.
Do not re-use existing issues for other topics, even if they are similar. This
keeps issues small and manageable and makes it much easier to follow through
and make sure each problem is taken care of.
* We'd love for you to send pull requests for fixes you have made or new features
you have added. Please read the [notes for developers](NOTES_FOR_DEVELOPERS.md)
beforehand which contains some coding guidelines.

233
EXTERNAL_LICENSES.txt Normal file
View File

@ -0,0 +1,233 @@
==== For protozero from https://github.com/mapbox/protozero
protozero copyright (c) Mapbox.
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 OWNER 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.
==== For protozero from https://github.com/mapbox/protozero
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
==== For utf8.h
Copyright 2006 Nemanja Trifunovic
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

23
LICENSE.txt Normal file
View File

@ -0,0 +1,23 @@
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.

157
NOTES_FOR_DEVELOPERS.md Normal file
View File

@ -0,0 +1,157 @@
# Notes for Developers
Read this if you want to contribute to Libosmium.
## Namespace
All Osmium code MUST be in the `osmium` namespace or one of its sub-namespaces.
## Include-Only
Osmium is a include-only library. You can't compile the library itself. There
is no `libosmium.so` or `libosmium.dll`.
One drawback ist that you can't have static data in classes, because there
is no place to put this data.
All free functions must be declared `inline`.
## Coding Conventions
These coding conventions have been changing over time and some code is still
different.
* All include files have `#ifdef` guards around them, macros are the path name
in all uppercase where the slashes (`/`) have been changed to underscore (`_`).
* Class names begin with uppercase chars and use CamelCase. Smaller helper
classes are usually defined as struct and have lowercase names.
* Macros (and only macros) are all uppercase. Use macros sparingly, usually
a simple (maybe constexpr) inline function is better. Undef macros after use
if possible.
* Macros should only be used for controlling which parts of the code should be
included when compiling or to avoid major code repetitions.
* Variables, attributes, and function names are lowercase with
`underscores_between_words`.
* Class attribute names start with `m_` (member).
* Use `descriptive_variable_names`, exceptions are well-established conventions
like `i` for a loop variable. Iterators are usually called `it`.
* Declare variables where they are first used (C++ style), not at the beginning
of a function (old C style).
* Names from external namespaces (even `std`) are always mentioned explicitly.
Do not use `using` (except for `std::swap`). This way we can't even by
accident pollute the namespace of the code using Osmium.
* Always use the standard swap idiom: `using std::swap; swap(foo, bar);`.
* `#include` directives appear in three "blocks" after the copyright notice.
The blocks are separated by blank lines. First block contains `#include`s for
standard C/C++ includes, second block for any external libs used, third
block for osmium internal includes. Within each block `#include`s are usually
sorted by path name. All `#include`s use `<>` syntax not `""`.
* Names not to be used from outside the library should be in a namespace
called `detail` under the namespace where they would otherwise appear. If
whole include files are never meant to be included from outside they should
be in a subdirectory called `detail`.
* All files have suffix `.hpp`.
* Closing } of all classes and namespaces should have a trailing comment
with the name of the class/namespace.
* All constructors with one (or more arguments if they have a default) should
be declared "explicit" unless there is a reason for them not to be. Document
that reason.
* If a class has any of the special methods (copy/move constructor/assigment,
destructor) it should have all of them, possibly marking them as default or
deleted.
* Typedefs have `names_like_this_type` which end in `_type`. Typedefs should
use the new `using foo_type = bar` syntax instead of the old
`typedef bar foo_type`.
* Template parameters are single uppercase letters or start with uppercase `T`
and use CamelCase.
* Always use `typename` in templates, not `class`: `template <typename T>`.
* The ellipsis in a variadic template never has a space to the left of it and
always has a space to the right: `template <typename... TArgs>` etc.
Keep to the indentation and other styles used in the code.
## C++11
Osmium uses C++11 and you can use its features such as auto, lambdas,
threading, etc. There are a few features we do not use, because even modern
compilers don't support them yet. This list might change as we get more data
about which compilers support which feature and what operating system versions
or distributions have which versions of these compilers installed.
Use `include/osmium/util/compatibility.hpp` if there are compatibility problems
between compilers due to different C++11 support.
## Operating systems
Usually all code must work on Linux, OSX, and Windows. Execptions are allowed
for some minor functionality, but please discuss this first.
When writing code and tests, care must be taken that everything works with the
CR line ending convention used on Linux and OSX and the CRLF line ending used
on Windows. Note that `git` can be run with different settings regarding line
ending rewritings on different machines making things more difficult. Some
files have been "forced" to LF line endings using `.gitattributes` files.
## 32bit systems
Libosmium tries to work on 32bit systems whereever possible. But there are
some parts which will not work on 32bit systems, mainly because the amount
of main memory available is not enough for it to work anyway.
## Checking your code
The Osmium makefiles use pretty draconian warning options for the compiler.
This is good. Code MUST never produce any warnings, even with those settings.
If absolutely necessary pragmas can be used to disable certain warnings in
specific areas of the code.
If the static code checker `cppcheck` is installed, the CMake configuration
will add a new build target `cppcheck` that will check all `.cpp` and `.hpp`
files. Cppcheck finds some bugs that gcc/clang doesn't. But take the result
with a grain of salt, it also sometimes produces wrong warnings.
Set `BUILD_HEADERS=ON` in the CMake config to enable compiling all include
files on their own to check whether dependencies are all okay. All include
files MUST include all other include files they depend on.
Call `cmake/iwyu.sh` to check for proper includes and forward declarations.
This uses the clang-based `include-what-you-use` program. Note that it does
produce some false reports and crashes often. The `osmium.imp` file can be
used to define mappings for iwyu. See the IWYU tool at
<http://code.google.com/p/include-what-you-use/>.
## Testing
There are unit tests using the Catch Unit Test Framework in the `test`
directory, some data tests in `test/osm-testdata` and tests of the examples in
`test/examples`. They are built by the default cmake config. Run `ctest` to
run them. We can always use more tests.
Tests are run automatically using the Travis (Linux/Mac) and Appveyor (Windows)
services. We automatically create coverage reports on Codevoc.io. Note that
the coverage percentages reported are not always accurate, because code that
is not used in tests at all will not necessarily end up in the binary and
the code coverage tool will not know it is there.
[![Travis Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium)
[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium)
[![Coverage Status](https://codecov.io/gh/osmcode/libosmium/branch/master/graph/badge.svg)](https://codecov.io/gh/osmcode/libosmium)
## Documenting the code
All namespaces, classes, functions, attributes, etc. should be documented.
Osmium uses the [Doxygen](http://www.doxygen.org) source code documentation
system. If it is installed, the CMake configuration will add a new build
target, so you can build it with `make doc`.

97
README.md Normal file
View File

@ -0,0 +1,97 @@
# Libosmium
http://osmcode.org/libosmium
A fast and flexible C++ library for working with OpenStreetMap data.
Libosmium works on Linux, Mac OSX and Windows.
[![Travis Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium)
[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium)
[![Coverage Status](https://codecov.io/gh/osmcode/libosmium/branch/master/graph/badge.svg)](https://codecov.io/gh/osmcode/libosmium)
Please see the [Libosmium manual](http://osmcode.org/libosmium/manual.html)
for more details than this README can provide.
## Prerequisites
Because Libosmium uses many C++11 features you need a modern compiler and
standard C++ library. Osmium needs at least GCC 4.8 or clang (LLVM) 3.4.
(Some parts may work with older versions.)
Different parts of Libosmium (and the applications built on top of it) need
different libraries. You DO NOT NEED to install all of them, just install those
you need for your programs.
For details see the [list of
dependencies](http://osmcode.org/libosmium/manual.html#dependencies) in the
manual.
The following external (header-only) libraries are included in the libosmium
repository:
* [gdalcpp](https://github.com/joto/gdalcpp)
* [protozero](https://github.com/mapbox/protozero)
* [utfcpp](http://utfcpp.sourceforge.net/)
## Directories
* benchmarks: Some benchmarks checking different parts of Libosmium.
* cmake: CMake configuration scripts.
* doc: Config for API reference documentation.
* examples: Osmium example applications.
* include: C/C++ include files. All of Libosmium is in those header files
which are needed for building Osmium applications.
* test: Tests (see below).
## Building
Osmium is a header-only library, so there is nothing to build for the
library itself.
But there are some tests and examples that can be build. Libosmium uses
cmake:
mkdir build
cd build
cmake ..
make
This will build the examples and tests. Call `ctest` to run the tests.
For more detals see the
[Building Libosmium](http://osmcode.org/libosmium/manual.html#building-libosmium)
chapter in the manual.
## Testing
See the
[Libosmium Manual](http://osmcode.org/libosmium/manual.html#running-tests)
for instructions.
## Switching from the old Osmium
If you have been using the old version of Osmium at
https://github.com/joto/osmium you might want to read about the [changes
needed](http://osmcode.org/libosmium/manual.html#changes-from-old-versions-of-osmium).
## License
Libosmium is available under the Boost Software License. See LICENSE.txt.
## Authors
Libosmium was mainly written and is maintained by Jochen Topf
(jochen@topf.org). See the git commit log for other authors.

39
appveyor.yml Normal file
View File

@ -0,0 +1,39 @@
#-----------------------------------------------------------------------------
#
# Configuration for continuous integration service at appveyor.com
#
#-----------------------------------------------------------------------------
environment:
matrix:
- config: Dev
autocrlf: true
- config: Dev
autocrlf: false
- config: RelWithDebInfo
autocrlf: true
- config: RelWithDebInfo
autocrlf: false
shallow_clone: true
# Operating system (build VM template)
os: Visual Studio 2015
# scripts that are called at very beginning, before repo cloning
init:
- git config --global core.autocrlf %autocrlf%
- git config --get core.autocrlf
# clone directory
clone_folder: c:\projects\libosmium
platform: x64
build_script:
- build-appveyor.bat
# remove garbage VS messages
# http://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"

53
benchmarks/CMakeLists.txt Normal file
View File

@ -0,0 +1,53 @@
#-----------------------------------------------------------------------------
#
# CMake Config
#
# Libosmium benchmarks
#
#-----------------------------------------------------------------------------
message(STATUS "Configuring benchmarks")
set(BENCHMARKS
count
count_tag
index_map
mercator
static_vs_dynamic_index
write_pbf
CACHE STRING "Benchmark programs"
)
#-----------------------------------------------------------------------------
#
# Configure benchmarks
#
#-----------------------------------------------------------------------------
message(STATUS "Configuring benchmarks - Building these benchmarks:")
foreach(benchmark ${BENCHMARKS})
message(STATUS " - osmium_benchmark_${benchmark}")
add_executable(osmium_benchmark_${benchmark}
"osmium_benchmark_${benchmark}.cpp")
target_link_libraries(osmium_benchmark_${benchmark}
${OSMIUM_IO_LIBRARIES}
${BENCHMARK_LIBS_${benchmark}})
set_pthread_on_target(osmium_benchmark_${benchmark})
configure_file(run_benchmark_${benchmark}.sh
${CMAKE_CURRENT_BINARY_DIR}/run_benchmark_${benchmark}.sh
@ONLY)
endforeach()
string(TOUPPER "${CMAKE_BUILD_TYPE}" _cmake_build_type)
set(_cxx_flags "${CMAKE_CXX_FLAGS_${_cmake_build_type}}")
foreach(file setup run_benchmarks)
configure_file(${file}.sh ${CMAKE_CURRENT_BINARY_DIR}/${file}.sh @ONLY)
endforeach()
#-----------------------------------------------------------------------------
message(STATUS "Configuring benchmarks - done")
#-----------------------------------------------------------------------------

41
benchmarks/README.md Normal file
View File

@ -0,0 +1,41 @@
# Benchmarks
Benchmarks check the performance of different parts of Libosmium.
## Preparations
To run the benchmarks first make a directory for the data files somewhere
(outside the repository) and set the `DATA_DIR` environment variable:
export DATA_DIR=benchmark_data
mkdir $DATA_DIR
Then copy the OSM files you want to do the benchmarks with into this directory.
You can use the `download_data.sh` script to download a selection of OSM files
in different sizes, but you can use a different selection, too. The benchmarks
will use whatever files you have in the `DATA_DIR` directory.
The download script will start the data files names with a number in order of
the size of the file from smallest to largest. You can use the same convention
or use a different one. Benchmarks will be run on the files in alphabetical
order.
The files don't have to be in that directory, you can add soft links from that
directory to the real file locations if that suits you.
## Compiling the benchmarks
To build the benchmarks set the `BUILD_BENCHMARKS` option when configuring with
CMake and run the compilation by calling `make` (or whatever build tool you
are using).
## Running the benchmarks
Go to the build directory and run `benchmarks/run_benchmarks.sh`. You can also
run each benchmark on its own by calling the respective script in the
`benchmarks` directory.
Results of the benchmarks will be printed to stdout, you might want to redirect
them into a file.

12
benchmarks/download_data.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
#
# download_data.sh
#
cd $DATA_DIR
curl --location --output 1_liechtenstein.osm.pbf http://download.geofabrik.de/europe/liechtenstein-latest.osm.pbf # about 2 MB
curl --location --output 2_bremen.osm.pbf http://download.geofabrik.de/europe/germany/bremen-latest.osm.pbf # about 16 MB
curl --location --output 3_sachsen.osm.pbf http://download.geofabrik.de/europe/germany/sachsen-latest.osm.pbf # about 160 MB
curl --location --output 4_germany.osm.pbf http://download.geofabrik.de/europe/germany-latest.osm.pbf # about 3 GB
curl --location --output 5_planet.osm.pbf http://planet.osm.org/pbf/planet-latest.osm.pbf # about 35 GB

View File

@ -0,0 +1,55 @@
/*
The code in this file is released into the Public Domain.
*/
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <string>
#include <osmium/io/any_input.hpp>
#include <osmium/handler.hpp>
#include <osmium/visitor.hpp>
struct CountHandler : public osmium::handler::Handler {
uint64_t nodes = 0;
uint64_t ways = 0;
uint64_t relations = 0;
void node(const osmium::Node&) {
++nodes;
}
void way(const osmium::Way&) {
++ways;
}
void relation(const osmium::Relation&) {
++relations;
}
};
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
std::exit(1);
}
const std::string input_filename{argv[1]};
osmium::io::Reader reader{input_filename};
CountHandler handler;
osmium::apply(reader, handler);
reader.close();
std::cout << "Nodes: " << handler.nodes << "\n";
std::cout << "Ways: " << handler.ways << "\n";
std::cout << "Relations: " << handler.relations << "\n";
}

View File

@ -0,0 +1,56 @@
/*
The code in this file is released into the Public Domain.
*/
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <string>
#include <osmium/io/any_input.hpp>
#include <osmium/handler.hpp>
#include <osmium/visitor.hpp>
struct CountHandler : public osmium::handler::Handler {
uint64_t counter = 0;
uint64_t all = 0;
void node(const osmium::Node& node) {
++all;
const char* amenity = node.tags().get_value_by_key("amenity");
if (amenity && !strcmp(amenity, "post_box")) {
++counter;
}
}
void way(const osmium::Way&) {
++all;
}
void relation(const osmium::Relation&) {
++all;
}
};
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
std::exit(1);
}
const std::string input_filename{argv[1]};
osmium::io::Reader reader{input_filename};
CountHandler handler;
osmium::apply(reader, handler);
reader.close();
std::cout << "r_all=" << handler.all << " r_counter=" << handler.counter << "\n";
}

View File

@ -0,0 +1,41 @@
/*
The code in this file is released into the Public Domain.
*/
#include <cstdlib>
#include <iostream>
#include <string>
#include <osmium/index/map/all.hpp>
#include <osmium/handler/node_locations_for_ways.hpp>
#include <osmium/visitor.hpp>
#include <osmium/io/any_input.hpp>
#include <osmium/handler.hpp>
using index_type = osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>;
using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " OSMFILE FORMAT\n";
std::exit(1);
}
const std::string input_filename{argv[1]};
const std::string location_store{argv[2]};
osmium::io::Reader reader{input_filename};
const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
std::unique_ptr<index_type> index = map_factory.create_map(location_store);
location_handler_type location_handler{*index};
location_handler.ignore_errors();
osmium::apply(reader, location_handler);
reader.close();
}

View File

@ -0,0 +1,43 @@
/*
The code in this file is released into the Public Domain.
*/
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <string>
#include <osmium/io/any_input.hpp>
#include <osmium/handler.hpp>
#include <osmium/visitor.hpp>
#include <osmium/geom/wkb.hpp>
#include <osmium/geom/mercator_projection.hpp>
struct GeomHandler : public osmium::handler::Handler {
osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory;
void node(const osmium::Node& node) {
const std::string geom = factory.create_point(node);
}
};
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
std::exit(1);
}
const std::string input_filename{argv[1]};
osmium::io::Reader reader{input_filename};
GeomHandler handler;
osmium::apply(reader, handler);
reader.close();
}

View File

@ -0,0 +1,137 @@
/*
This benchmarks compares the run time for statically vs. dynamically
configured index maps. You can configure index maps at compile-time using
typedefs or at run-time using polymorphism.
This will read the input file into a buffer and then run the
NodeLocationForWays handler multiple times over the complete data. The
number of runs depends on the size of the input, but is never smaller
than 10.
Do not run this with very large input files! It will need about 10 times
as much RAM as the file size of the input file.
The code in this file is released into the Public Domain.
*/
#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <limits>
#include <string>
#include <osmium/index/map/all.hpp>
#include <osmium/handler/node_locations_for_ways.hpp>
#include <osmium/visitor.hpp>
#include <osmium/io/any_input.hpp>
#include <osmium/handler.hpp>
using static_index_type = osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
const std::string location_store{"sparse_mem_array"};
using dynamic_index_type = osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>;
using static_location_handler_type = osmium::handler::NodeLocationsForWays<static_index_type>;
using dynamic_location_handler_type = osmium::handler::NodeLocationsForWays<dynamic_index_type>;
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
std::exit(1);
}
const std::string input_filename{argv[1]};
osmium::memory::Buffer buffer{osmium::io::read_file(input_filename)};
const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
const auto buffer_size = buffer.committed() / (1024*1024); // buffer size in MBytes
const int runs = std::max(10, static_cast<int>(5000ull / buffer_size));
std::cout << "input: filename=" << input_filename << " buffer_size=" << buffer_size << "MBytes\n";
std::cout << "runs: " << runs << "\n";
double static_min = std::numeric_limits<double>::max();
double static_sum = 0;
double static_max = 0;
double dynamic_min = std::numeric_limits<double>::max();
double dynamic_sum = 0;
double dynamic_max = 0;
for (int i = 0; i < runs; ++i) {
{
// static index
osmium::memory::Buffer tmp_buffer{buffer.committed()};
for (const auto& item : buffer) {
tmp_buffer.add_item(item);
tmp_buffer.commit();
}
static_index_type static_index;
static_location_handler_type static_location_handler{static_index};
const auto start = std::chrono::steady_clock::now();
osmium::apply(tmp_buffer, static_location_handler);
const auto end = std::chrono::steady_clock::now();
const double duration = std::chrono::duration<double, std::milli>(end-start).count();
if (duration < static_min) static_min = duration;
if (duration > static_max) static_max = duration;
static_sum += duration;
}
{
// dynamic index
osmium::memory::Buffer tmp_buffer{buffer.committed()};
for (const auto& item : buffer) {
tmp_buffer.add_item(item);
tmp_buffer.commit();
}
std::unique_ptr<dynamic_index_type> index = map_factory.create_map(location_store);
dynamic_location_handler_type dynamic_location_handler{*index};
dynamic_location_handler.ignore_errors();
const auto start = std::chrono::steady_clock::now();
osmium::apply(tmp_buffer, dynamic_location_handler);
const auto end = std::chrono::steady_clock::now();
const double duration = std::chrono::duration<double, std::milli>(end-start).count();
if (duration < dynamic_min) dynamic_min = duration;
if (duration > dynamic_max) dynamic_max = duration;
dynamic_sum += duration;
}
}
const double static_avg = static_sum/runs;
const double dynamic_avg = dynamic_sum/runs;
std::cout << "static min=" << static_min << "ms avg=" << static_avg << "ms max=" << static_max << "ms\n";
std::cout << "dynamic min=" << dynamic_min << "ms avg=" << dynamic_avg << "ms max=" << dynamic_max << "ms\n";
const double rfactor = 100.0;
const double diff_min = std::round((dynamic_min - static_min) * rfactor) / rfactor;
const double diff_avg = std::round((dynamic_avg - static_avg) * rfactor) / rfactor;
const double diff_max = std::round((dynamic_max - static_max) * rfactor) / rfactor;
const double prfactor = 10.0;
const double percent_min = std::round((100.0 * diff_min / static_min) * prfactor) / prfactor;
const double percent_avg = std::round((100.0 * diff_avg / static_avg) * prfactor) / prfactor;
const double percent_max = std::round((100.0 * diff_max / static_max) * prfactor) / prfactor;
std::cout << "difference:";
std::cout << " min=" << diff_min << "ms (" << percent_min << "%)";
std::cout << " avg=" << diff_avg << "ms (" << percent_avg << "%)";
std::cout << " max=" << diff_max << "ms (" << percent_max << "%)\n";
}

View File

@ -0,0 +1,35 @@
/*
The code in this file is released into the Public Domain.
*/
#include <cstdlib>
#include <iostream>
#include <string>
#include <osmium/io/any_input.hpp>
#include <osmium/io/any_output.hpp>
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " INPUT-FILE OUTPUT-FILE\n";
std::exit(1);
}
std::string input_filename{argv[1]};
std::string output_filename{argv[2]};
osmium::io::Reader reader{input_filename};
osmium::io::File output_file{output_filename, "pbf"};
osmium::io::Header header;
osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow};
while (osmium::memory::Buffer buffer = reader.read()) {
writer(std::move(buffer));
}
writer.close();
reader.close();
}

View File

@ -0,0 +1,22 @@
#!/bin/sh
#
# run_benchmark_count.sh
#
set -e
BENCHMARK_NAME=count
. @CMAKE_BINARY_DIR@/benchmarks/setup.sh
CMD=$OB_DIR/osmium_benchmark_$BENCHMARK_NAME
echo "# file size num mem time cpu_kernel cpu_user cpu_percent cmd options"
for data in $OB_DATA_FILES; do
filename=`basename $data`
filesize=`stat --format="%s" --dereference $data`
for n in $OB_SEQ; do
$OB_TIME_CMD -f "$filename $filesize $n $OB_TIME_FORMAT" $CMD $data 2>&1 >/dev/null | sed -e "s%$DATA_DIR/%%" | sed -e "s%$OB_DIR/%%"
done
done

View File

@ -0,0 +1,22 @@
#!/bin/sh
#
# run_benchmark_count_tag.sh
#
set -e
BENCHMARK_NAME=count_tag
. @CMAKE_BINARY_DIR@/benchmarks/setup.sh
CMD=$OB_DIR/osmium_benchmark_$BENCHMARK_NAME
echo "# file size num mem time cpu_kernel cpu_user cpu_percent cmd options"
for data in $OB_DATA_FILES; do
filename=`basename $data`
filesize=`stat --format="%s" --dereference $data`
for n in $OB_SEQ; do
$OB_TIME_CMD -f "$filename $filesize $n $OB_TIME_FORMAT" $CMD $data 2>&1 >/dev/null | sed -e "s%$DATA_DIR/%%" | sed -e "s%$OB_DIR/%%"
done
done

View File

@ -0,0 +1,27 @@
#!/bin/sh
#
# run_benchmark_index_map.sh
#
set -e
BENCHMARK_NAME=index_map
. @CMAKE_BINARY_DIR@/benchmarks/setup.sh
CMD=$OB_DIR/osmium_benchmark_$BENCHMARK_NAME
#MAPS="sparse_mem_map sparse_mem_table sparse_mem_array sparse_mmap_array sparse_file_array dense_mem_array dense_mmap_array dense_file_array"
MAPS="sparse_mem_map sparse_mem_table sparse_mem_array sparse_mmap_array sparse_file_array"
echo "# file size num mem time cpu_kernel cpu_user cpu_percent cmd options"
for data in $OB_DATA_FILES; do
filename=`basename $data`
filesize=`stat --format="%s" --dereference $data`
for map in $MAPS; do
for n in $OB_SEQ; do
$OB_TIME_CMD -f "$filename $filesize $n $OB_TIME_FORMAT" $CMD $data $map 2>&1 >/dev/null | sed -e "s%$DATA_DIR/%%" | sed -e "s%$OB_DIR/%%"
done
done
done

View File

@ -0,0 +1,22 @@
#!/bin/sh
#
# run_benchmark_mercator.sh
#
set -e
BENCHMARK_NAME=mercator
. @CMAKE_BINARY_DIR@/benchmarks/setup.sh
CMD=$OB_DIR/osmium_benchmark_$BENCHMARK_NAME
echo "# file size num mem time cpu_kernel cpu_user cpu_percent cmd options"
for data in $OB_DATA_FILES; do
filename=`basename $data`
filesize=`stat --format="%s" --dereference $data`
for n in $OB_SEQ; do
$OB_TIME_CMD -f "$filename $filesize $n $OB_TIME_FORMAT" $CMD $data 2>&1 >/dev/null | sed -e "s%$DATA_DIR/%%" | sed -e "s%$OB_DIR/%%"
done
done

View File

@ -0,0 +1,21 @@
#!/bin/sh
#
# run_benchmark_static_vs_dynamic_index.sh
#
set -e
BENCHMARK_NAME=static_vs_dynamic_index
. @CMAKE_BINARY_DIR@/benchmarks/setup.sh
CMD=$OB_DIR/osmium_benchmark_$BENCHMARK_NAME
for data in $OB_DATA_FILES; do
filesize=`stat --format="%s" --dereference $data`
if [ $filesize -lt 500000000 ]; then
echo "========================"
$CMD $data
fi
done

View File

@ -0,0 +1,28 @@
#!/bin/sh
#
# run_benchmark_write_pbf.sh
#
# Will read the input file and after reading it into memory completely,
# write it to /dev/null. Because this will need the time to read *and* write
# the file, it will report the times for reading and writing. You can
# subtract the times needed for the "count" benchmark to (roughly) get the
# write times.
#
set -e
BENCHMARK_NAME=write_pbf
. @CMAKE_BINARY_DIR@/benchmarks/setup.sh
CMD=$OB_DIR/osmium_benchmark_$BENCHMARK_NAME
echo "# file size num mem time cpu_kernel cpu_user cpu_percent cmd options"
for data in $OB_DATA_FILES; do
filename=`basename $data`
filesize=`stat --format="%s" --dereference $data`
for n in $OB_SEQ; do
$OB_TIME_CMD -f "$filename $filesize $n $OB_TIME_FORMAT" $CMD $data /dev/null 2>&1 >/dev/null | sed -e "s%$DATA_DIR/%%" | sed -e "s%$OB_DIR/%%"
done
done

15
benchmarks/run_benchmarks.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/sh
#
# run_benchmarks.sh
#
# Run all benchmarks.
#
set -e
for benchmark in @CMAKE_BINARY_DIR@/benchmarks/run_benchmark_*.sh; do
name=`basename $benchmark`
echo "Running $name..."
$benchmark
done

44
benchmarks/setup.sh Executable file
View File

@ -0,0 +1,44 @@
#!/bin/sh
#
# setup.sh
#
if [ -z $DATA_DIR ]; then
echo "Please set DATA_DIR environment variable before running benchmark"
exit 1
fi
OB_DIR=@CMAKE_BINARY_DIR@/benchmarks
OB_BUILD_TYPE=@CMAKE_BUILD_TYPE@
OB_COMPILER=@CMAKE_CXX_COMPILER@
OB_COMPILER_VERSION=`$OB_COMPILER --version | head -1`
OB_CXXFLAGS="@_cxx_flags@"
OB_RUNS=3
OB_SEQ=`seq -s' ' 1 $OB_RUNS`
OB_TIME_CMD=/usr/bin/time
OB_TIME_FORMAT="%M %e %S %U %P %C"
OB_DATA_FILES=`find -L $DATA_DIR -mindepth 1 -maxdepth 1 -type f | sort`
echo "BENCHMARK: $BENCHMARK_NAME"
echo "---------------------"
echo "BUILD:"
echo "build type\t: $OB_BUILD_TYPE"
echo "compiler\t: $OB_COMPILER"
echo "CXX version\t: $OB_COMPILER_VERSION"
echo "CXX flags\t: $OB_CXXFLAGS"
echo "---------------------"
echo "CPU:"
grep '^model name' /proc/cpuinfo | tail -1
grep '^cpu MHz' /proc/cpuinfo | tail -1
grep '^cpu cores' /proc/cpuinfo | tail -1
grep '^siblings' /proc/cpuinfo | tail -1
echo "---------------------"
echo "MEMORY:"
free
echo "---------------------"
echo "RESULTS:"

126
build-appveyor.bat Normal file
View File

@ -0,0 +1,126 @@
@ECHO OFF
SETLOCAL
SET EL=0
ECHO ~~~~~~ %~f0 ~~~~~~
SET CUSTOM_CMAKE=cmake-3.6.2-win64-x64
::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
SET lodir=%CD%
SET PATH=%lodir%\%CUSTOM_CMAKE%\bin;%PATH%
SET LODEPSDIR=%lodir%\libosmium-deps
SET PROJ_LIB=%LODEPSDIR%\proj\share
SET GDAL_DATA=%LODEPSDIR%\gdal\data
::gdal.dll
SET PATH=%LODEPSDIR%\gdal\lib;%PATH%
::geos.dll
SET PATH=%LODEPSDIR%\geos\lib;%PATH%
::libtiff.dll
SET PATH=%LODEPSDIR%\libtiff\lib;%PATH%
::jpeg.dll
SET PATH=%LODEPSDIR%\jpeg\lib;%PATH%
::libexpat.dll
SET PATH=%LODEPSDIR%\expat\lib;%PATH%
::zlibwapi.dll
SET PATH=%LODEPSDIR%\zlib\lib;%PATH%
::convert backslashes in bzip2 path to forward slashes
::cmake cannot find it otherwise
SET LIBBZIP2=%LODEPSDIR%\bzip2\lib\libbz2.lib
SET LIBBZIP2=%LIBBZIP2:\=/%
IF NOT EXIST cm.7z ECHO downloading cmake %CUSTOM_CMAKE% ... && powershell Invoke-WebRequest https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/%CUSTOM_CMAKE%.7z -OutFile cm.7z
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
IF NOT EXIST lodeps.7z ECHO downloading binary dependencies... && powershell Invoke-WebRequest https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/libosmium-deps-win-14.0-x64.7z -OutFile lodeps.7z
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
IF NOT EXIST %CUSTOM_CMAKE% ECHO extracting cmake... && 7z x cm.7z | %windir%\system32\find "ing archive"
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
IF NOT EXIST %LODEPSDIR% ECHO extracting binary dependencies... && 7z x lodeps.7z | %windir%\system32\find "ing archive"
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
ECHO %LODEPSDIR%
DIR %LODEPSDIR%
::TREE %LODEPSDIR%
::powershell (Get-ChildItem $env:LODEPSDIR\boost\lib -Filter *boost*.dll)[0].BaseName.split('_')[-1]
FOR /F "tokens=1 usebackq" %%i in (`powershell ^(Get-ChildItem %LODEPSDIR%\boost\lib -Filter *boost*.dll^)[0].BaseName.split^('_'^)[-1]`) DO SET BOOST_VERSION=%%i
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
ECHO BOOST_VERSION^: %BOOST_VERSION%
ECHO our own cmake
cmake -version
CD %lodir%\..
IF NOT EXIST osm-testdata ECHO cloning osm-testdata && git clone --depth 1 https://github.com/osmcode/osm-testdata.git
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
CD osm-testdata
git fetch
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
git pull
IF %ERRORLEVEL% NEQ 0 GOTO ERROR
CD %lodir%
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
::http://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings
SET CMAKE_CMD=cmake .. ^
-LA -G "Visual Studio 14 Win64" ^
-DOsmium_DEBUG=TRUE ^
-DCMAKE_BUILD_TYPE=%config% ^
-DBUILD_HEADERS=OFF ^
-DBOOST_ROOT=%LODEPSDIR%\boost ^
-DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib ^
-DBZIP2_LIBRARY_RELEASE=%LIBBZIP2% ^
-DCMAKE_PREFIX_PATH=%LODEPSDIR%\zlib;%LODEPSDIR%\expat;%LODEPSDIR%\bzip2;%LODEPSDIR%\geos;%LODEPSDIR%\gdal;%LODEPSDIR%\proj;%LODEPSDIR%\sparsehash;%LODEPSDIR%\wingetopt
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 libosmium.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% ^
-E testdata-overview
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%

43
build-local.bat Normal file
View File

@ -0,0 +1,43 @@
@ECHO OFF
SETLOCAL
SET EL=0
ECHO ~~~~~~ %~f0 ~~~~~~
ECHO.
ECHO build-local ["config=Dev"]
ECHO default config^: RelWithDebInfo
ECHO.
SET platform=x64
SET config=RelWithDebInfo
:: OVERRIDE PARAMETERS >>>>>>>>
:NEXT-ARG
IF '%1'=='' GOTO ARGS-DONE
ECHO setting %1
SET %1
SHIFT
GOTO NEXT-ARG
:ARGS-DONE
::<<<<< OVERRIDE PARAMETERS
WHERE 7z
IF %ERRORLEVEL% NEQ 0 ECHO 7zip not on PATH && GOTO ERROR
CALL build-appveyor.bat
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%

153
cmake/FindGem.cmake Normal file
View File

@ -0,0 +1,153 @@
# Author thomas.roehr@dfki.de
#
# Version 0.3 2013-07-02
# - rely on `gem content` to find library and header
# - introduce GEM_OS_PKG to allow search via pkgconfig
# Version 0.2 2010-01-14
# - add support for searching for multiple gems
# Version 0.1 2010-12-15
# - support basic search functionality
# - tested to find rice
#
# OUTPUT:
#
# GEM_INCLUDE_DIRS After successful search contains the include directores
#
# GEM_LIBRARIES After successful search contains the full path of each found library
#
#
# Usage:
# set(GEM_DEBUG TRUE)
# find_package(Gem COMPONENTS rice hoe)
# include_directories(${GEM_INCLUDE_DIRS})
# target_link_libraries(${GEM_LIBRARIES}
#
# in case pkg-config should be used to search for the os pkg, set GEM_OS_PKG, i.e.
# set(GEM_OS_PKG TRUE)
#
# Check for how 'gem' should be called
include(FindPackageHandleStandardArgs)
find_program(GEM_EXECUTABLE
NAMES "gem${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}"
"gem${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}"
"gem-${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}"
"gem-${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}"
"gem${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}${RUBY_VERSION_PATCH}"
"gem${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_PATCH}"
"gem-${RUBY_VERSION_MAJOR}${RUBY_VERSION_MINOR}${RUBY_VERSION_PATCH}"
"gem-${RUBY_VERSION_MAJOR}.${RUBY_VERSION_MINOR}.${RUBY_VERSION_PATCH}"
"gem")
# Making backward compatible
if(Gem_DEBUG)
set(GEM_DEBUG TRUE)
endif()
if(NOT GEM_EXECUTABLE)
MESSAGE(FATAL_ERROR "Could not find the gem executable - install 'gem' first")
endif()
if(NOT Gem_FIND_COMPONENTS)
MESSAGE(FATAL_ERROR "If searching for a Gem you have to provide COMPONENTS with the name of the gem")
endif()
foreach(Gem_NAME ${Gem_FIND_COMPONENTS})
set(GEM_${Gem_NAME}_FOUND TRUE)
list(APPEND components_found_vars GEM_${Gem_NAME}_FOUND)
# If the gem is installed as a gem
if(NOT GEM_OS_PKG)
set(GEM_HOME ENV{GEM_HOME})
# Use `gem content <gem-name>` to extract current information about installed gems
# Store the information into ${GEM_LOCAL_INFO}
EXECUTE_PROCESS(COMMAND ${GEM_EXECUTABLE} content ${Gem_NAME}
RESULT_VARIABLE GEM_RUN_RESULT
OUTPUT_VARIABLE GEM_LOCAL_INFO)
if(GEM_RUN_RESULT STREQUAL "0")
list(APPEND FOUND_GEMS ${Gem_NAME})
set(_library_NAME_PATTERN lib${Gem_NAME}.a
lib${Gem_NAME}.so
lib${Gem_NAME}.dylib
${Gem_NAME}.a
${Gem_NAME}.so
${Gem_NAME}.dylib
.*.a
.*.so
.*.dylib
)
set(_header_SUFFIX_PATTERN
.h
.hh
.hpp
)
# Create a list from the output results of the gem command
string(REPLACE "\n" ";" GEM_CONTENT_LIST "${GEM_LOCAL_INFO}")
foreach(_gem_CONTENT_PATH ${GEM_CONTENT_LIST})
# Convert so that only '/' Unix path separator are being using
# needed to do proper regex matching
FILE(TO_CMAKE_PATH ${_gem_CONTENT_PATH} gem_CONTENT_PATH)
# Identify library -- checking for a library in the gems 'lib' (sub)directory
# Search for an existing library, but only within the gems folder
foreach(_library_NAME ${_library_NAME_PATTERN})
STRING(REGEX MATCH ".*${Gem_NAME}.*/lib/.*${_library_NAME}$" GEM_PATH_INFO "${gem_CONTENT_PATH}")
if(NOT "${GEM_PATH_INFO}" STREQUAL "")
list(APPEND GEM_LIBRARIES ${GEM_PATH_INFO})
break()
endif()
endforeach()
# Identify headers
# Checking for available headers in an include directory
foreach(_header_PATTERN ${_header_SUFFIX_PATTERN})
STRING(REGEX MATCH ".*${Gem_NAME}.*/include/.*${_header_PATTERN}$" GEM_PATH_INFO "${gem_CONTENT_PATH}")
if(NOT "${GEM_PATH_INFO}" STREQUAL "")
STRING(REGEX REPLACE "(.*${Gem_NAME}.*/include/).*${_header_PATTERN}$" "\\1" GEM_PATH_INFO "${gem_CONTENT_PATH}")
list(APPEND GEM_INCLUDE_DIRS ${GEM_PATH_INFO})
break()
endif()
endforeach()
endforeach()
else()
set(GEM_${Gem_NAME}_FOUND FALSE)
endif()
else(NOT GEM_OS_PKG)
pkg_check_modules(GEM_PKG ${Gem_NAME})
set(GEM_${GEM_NAME}_FOUND GEM_PKG_FOUND)
set(GEM_INCLUDE_DIRS ${GEM_PKG_INCLUDE_DIRS})
set(GEM_LIBRARIES ${GEM_PKG_LIBRARIES} ${GEM_PKG_STATIC_LIBRARIES})
list(APPEND GEM_LIBRARIES ${GEM_PKG_LDFLAGS} ${GEM_PKG_STATIC_LDFLAGS})
list(APPEND GEM_LIBRARIES ${GEM_PKG_LDFLAGS_OTHER} ${GEM_PKG_STATIC_LDFLAGS_OTHER})
if(GEM_DEBUG)
message(STATUS "GEM_OS_PKG is defined")
message(STATUS "GEM_INCLUDE_DIRS ${GEM_INCLUDE_DIRS}")
message(STATUS "GEM_STATIC_LIBRARY_DIRS ${GEM_PKG_STATIC_LIBRARY_DIRS}")
message(STATUS "GEM_LIBRARY_DIRS ${GEM_PKG_STATIC_LIBRARY_DIRS}")
message(STATUS "GEM_STATIC_LIBRARIES ${GEM_PKG_STATIC_LIBRARIES}")
message(STATUS "GEM_LIBRARIES ${GEM_LIBRARIES}")
endif()
endif()
if(GEM_DEBUG)
message(STATUS "${Gem_NAME} library dir: ${GEM_LIBRARIES}")
message(STATUS "${Gem_NAME} include dir: ${GEM_INCLUDE_DIRS}")
endif()
endforeach()
# Compact the lists
if(DEFINED GEM_LIBRARIES)
LIST(REMOVE_DUPLICATES GEM_LIBRARIES)
endif()
if(DEFINED GEM_INCLUDE_DIRS)
LIST(REMOVE_DUPLICATES GEM_INCLUDE_DIRS)
endif()
find_package_handle_standard_args(GEM
REQUIRED_VARS ${components_found_vars}
FAIL_MESSAGE "Could not find all required gems")

370
cmake/FindOsmium.cmake Normal file
View File

@ -0,0 +1,370 @@
#----------------------------------------------------------------------
#
# FindOsmium.cmake
#
# Find the Libosmium headers and, optionally, several components needed
# for different Libosmium functions.
#
#----------------------------------------------------------------------
#
# 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(Osmium [version] REQUIRED COMPONENTS <XXX>)
# include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS})
#
# The version number is optional. If it is not set, any version of
# libosmium will do.
#
# For the <XXX> substitute a space separated list of one or more of the
# following components:
#
# pbf - include libraries needed for PBF input and output
# xml - include libraries needed for XML input and output
# io - include libraries needed for any type of input/output
# geos - include if you want to use any of the GEOS functions
# gdal - include if you want to use any of the OGR functions
# proj - include if you want to use any of the Proj.4 functions
# sparsehash - include if you use the sparsehash index
#
# You can check for success with something like this:
#
# if(NOT OSMIUM_FOUND)
# message(WARNING "Libosmium not found!\n")
# endif()
#
#----------------------------------------------------------------------
#
# Variables:
#
# OSMIUM_FOUND - True if Osmium found.
# OSMIUM_INCLUDE_DIRS - Where to find include files.
# OSMIUM_XML_LIBRARIES - Libraries needed for XML I/O.
# OSMIUM_PBF_LIBRARIES - Libraries needed for PBF I/O.
# OSMIUM_IO_LIBRARIES - Libraries needed for XML or PBF I/O.
# OSMIUM_LIBRARIES - All libraries Osmium uses somewhere.
#
#----------------------------------------------------------------------
# This is the list of directories where we look for osmium and protozero
# includes.
set(_osmium_include_path
../libosmium
~/Library/Frameworks
/Library/Frameworks
/opt/local # DarwinPorts
/opt
)
# Look for the header file.
find_path(OSMIUM_INCLUDE_DIR osmium/version.hpp
PATH_SUFFIXES include
PATHS ${_osmium_include_path}
)
# Check libosmium version number
if(Osmium_FIND_VERSION)
file(STRINGS "${OSMIUM_INCLUDE_DIR}/osmium/version.hpp" _libosmium_version_define REGEX "#define LIBOSMIUM_VERSION_STRING")
if("${_libosmium_version_define}" MATCHES "#define LIBOSMIUM_VERSION_STRING \"([0-9.]+)\"")
set(_libosmium_version "${CMAKE_MATCH_1}")
else()
set(_libosmium_version "unknown")
endif()
endif()
set(OSMIUM_INCLUDE_DIRS "${OSMIUM_INCLUDE_DIR}")
#----------------------------------------------------------------------
#
# Check for optional components
#
#----------------------------------------------------------------------
if(Osmium_FIND_COMPONENTS)
foreach(_component ${Osmium_FIND_COMPONENTS})
string(TOUPPER ${_component} _component_uppercase)
set(Osmium_USE_${_component_uppercase} TRUE)
endforeach()
endif()
#----------------------------------------------------------------------
# Component 'io' is an alias for 'pbf' and 'xml'
if(Osmium_USE_IO)
set(Osmium_USE_PBF TRUE)
set(Osmium_USE_XML TRUE)
endif()
#----------------------------------------------------------------------
# Component 'ogr' is an alias for 'gdal'
if(Osmium_USE_OGR)
set(Osmium_USE_GDAL TRUE)
endif()
#----------------------------------------------------------------------
# Component 'pbf'
if(Osmium_USE_PBF)
find_package(ZLIB)
find_package(Threads)
message(STATUS "Looking for protozero")
find_path(PROTOZERO_INCLUDE_DIR protozero/version.hpp
PATH_SUFFIXES include
PATHS ${_osmium_include_path}
${OSMIUM_INCLUDE_DIR}
)
if(PROTOZERO_INCLUDE_DIR)
message(STATUS "Looking for protozero - found")
else()
message(STATUS "Looking for protozero - not found")
endif()
list(APPEND OSMIUM_EXTRA_FIND_VARS ZLIB_FOUND Threads_FOUND PROTOZERO_INCLUDE_DIR)
if(ZLIB_FOUND AND Threads_FOUND AND PROTOZERO_INCLUDE_DIR)
list(APPEND OSMIUM_PBF_LIBRARIES
${ZLIB_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
)
if(WIN32)
# This is needed for the ntohl() function
list(APPEND OSMIUM_PBF_LIBRARIES ws2_32)
endif()
list(APPEND OSMIUM_INCLUDE_DIRS
${ZLIB_INCLUDE_DIR}
${PROTOZERO_INCLUDE_DIR}
)
else()
message(WARNING "Osmium: Can not find some libraries for PBF input/output, please install them or configure the paths.")
endif()
endif()
#----------------------------------------------------------------------
# Component 'xml'
if(Osmium_USE_XML)
find_package(EXPAT)
find_package(BZip2)
find_package(ZLIB)
find_package(Threads)
list(APPEND OSMIUM_EXTRA_FIND_VARS EXPAT_FOUND BZIP2_FOUND ZLIB_FOUND Threads_FOUND)
if(EXPAT_FOUND AND BZIP2_FOUND AND ZLIB_FOUND AND Threads_FOUND)
list(APPEND OSMIUM_XML_LIBRARIES
${EXPAT_LIBRARIES}
${BZIP2_LIBRARIES}
${ZLIB_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
)
list(APPEND OSMIUM_INCLUDE_DIRS
${EXPAT_INCLUDE_DIR}
${BZIP2_INCLUDE_DIR}
${ZLIB_INCLUDE_DIR}
)
else()
message(WARNING "Osmium: Can not find some libraries for XML input/output, please install them or configure the paths.")
endif()
endif()
#----------------------------------------------------------------------
list(APPEND OSMIUM_IO_LIBRARIES
${OSMIUM_PBF_LIBRARIES}
${OSMIUM_XML_LIBRARIES}
)
list(APPEND OSMIUM_LIBRARIES
${OSMIUM_IO_LIBRARIES}
)
#----------------------------------------------------------------------
# Component 'geos'
if(Osmium_USE_GEOS)
find_path(GEOS_INCLUDE_DIR geos/geom.h)
find_library(GEOS_LIBRARY NAMES geos)
list(APPEND OSMIUM_EXTRA_FIND_VARS GEOS_INCLUDE_DIR GEOS_LIBRARY)
if(GEOS_INCLUDE_DIR AND GEOS_LIBRARY)
SET(GEOS_FOUND 1)
list(APPEND OSMIUM_LIBRARIES ${GEOS_LIBRARY})
list(APPEND OSMIUM_INCLUDE_DIRS ${GEOS_INCLUDE_DIR})
else()
message(WARNING "Osmium: GEOS library is required but not found, please install it or configure the paths.")
endif()
endif()
#----------------------------------------------------------------------
# Component 'gdal' (alias 'ogr')
if(Osmium_USE_GDAL)
find_package(GDAL)
list(APPEND OSMIUM_EXTRA_FIND_VARS GDAL_FOUND)
if(GDAL_FOUND)
list(APPEND OSMIUM_LIBRARIES ${GDAL_LIBRARIES})
list(APPEND OSMIUM_INCLUDE_DIRS ${GDAL_INCLUDE_DIRS})
else()
message(WARNING "Osmium: GDAL library is required but not found, please install it or configure the paths.")
endif()
endif()
#----------------------------------------------------------------------
# Component 'proj'
if(Osmium_USE_PROJ)
find_path(PROJ_INCLUDE_DIR proj_api.h)
find_library(PROJ_LIBRARY NAMES proj)
list(APPEND OSMIUM_EXTRA_FIND_VARS PROJ_INCLUDE_DIR PROJ_LIBRARY)
if(PROJ_INCLUDE_DIR AND PROJ_LIBRARY)
set(PROJ_FOUND 1)
list(APPEND OSMIUM_LIBRARIES ${PROJ_LIBRARY})
list(APPEND OSMIUM_INCLUDE_DIRS ${PROJ_INCLUDE_DIR})
else()
message(WARNING "Osmium: PROJ.4 library is required but not found, please install it or configure the paths.")
endif()
endif()
#----------------------------------------------------------------------
# Component 'sparsehash'
if(Osmium_USE_SPARSEHASH)
find_path(SPARSEHASH_INCLUDE_DIR google/sparsetable)
list(APPEND OSMIUM_EXTRA_FIND_VARS SPARSEHASH_INCLUDE_DIR)
if(SPARSEHASH_INCLUDE_DIR)
# Find size of sparsetable::size_type. This does not work on older
# CMake versions because they can do this check only in C, not in C++.
if(NOT CMAKE_VERSION VERSION_LESS 3.0)
include(CheckTypeSize)
set(CMAKE_REQUIRED_INCLUDES ${SPARSEHASH_INCLUDE_DIR})
set(CMAKE_EXTRA_INCLUDE_FILES "google/sparsetable")
check_type_size("google::sparsetable<int>::size_type" SPARSETABLE_SIZE_TYPE LANGUAGE CXX)
set(CMAKE_EXTRA_INCLUDE_FILES)
set(CMAKE_REQUIRED_INCLUDES)
else()
set(SPARSETABLE_SIZE_TYPE ${CMAKE_SIZEOF_VOID_P})
endif()
# Sparsetable::size_type must be at least 8 bytes (64bit), otherwise
# OSM object IDs will not fit.
if(SPARSETABLE_SIZE_TYPE GREATER 7)
set(SPARSEHASH_FOUND 1)
add_definitions(-DOSMIUM_WITH_SPARSEHASH=${SPARSEHASH_FOUND})
list(APPEND OSMIUM_INCLUDE_DIRS ${SPARSEHASH_INCLUDE_DIR})
else()
message(WARNING "Osmium: Disabled Google SparseHash library on 32bit system (size_type=${SPARSETABLE_SIZE_TYPE}).")
endif()
else()
message(WARNING "Osmium: Google SparseHash library is required but not found, please install it or configure the paths.")
endif()
endif()
#----------------------------------------------------------------------
list(REMOVE_DUPLICATES OSMIUM_INCLUDE_DIRS)
if(OSMIUM_XML_LIBRARIES)
list(REMOVE_DUPLICATES OSMIUM_XML_LIBRARIES)
endif()
if(OSMIUM_PBF_LIBRARIES)
list(REMOVE_DUPLICATES OSMIUM_PBF_LIBRARIES)
endif()
if(OSMIUM_IO_LIBRARIES)
list(REMOVE_DUPLICATES OSMIUM_IO_LIBRARIES)
endif()
if(OSMIUM_LIBRARIES)
list(REMOVE_DUPLICATES OSMIUM_LIBRARIES)
endif()
#----------------------------------------------------------------------
#
# Check that all required libraries are available
#
#----------------------------------------------------------------------
if(OSMIUM_EXTRA_FIND_VARS)
list(REMOVE_DUPLICATES OSMIUM_EXTRA_FIND_VARS)
endif()
# Handle the QUIETLY and REQUIRED arguments and the optional version check
# and set OSMIUM_FOUND to TRUE if all listed variables are TRUE.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Osmium
REQUIRED_VARS OSMIUM_INCLUDE_DIR ${OSMIUM_EXTRA_FIND_VARS}
VERSION_VAR _libosmium_version)
unset(OSMIUM_EXTRA_FIND_VARS)
#----------------------------------------------------------------------
#
# A function for setting the -pthread option in compilers/linkers
#
#----------------------------------------------------------------------
function(set_pthread_on_target _target)
if(NOT MSVC)
set_target_properties(${_target} PROPERTIES COMPILE_FLAGS "-pthread")
if(NOT APPLE)
set_target_properties(${_target} PROPERTIES LINK_FLAGS "-pthread")
endif()
endif()
endfunction()
#----------------------------------------------------------------------
#
# Add compiler flags
#
#----------------------------------------------------------------------
add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64)
if(MSVC)
add_definitions(-wd4996)
# Disable warning C4068: "unknown pragma" because we want it to ignore
# pragmas for other compilers.
add_definitions(-wd4068)
# Disable warning C4715: "not all control paths return a value" because
# it generates too many false positives.
add_definitions(-wd4715)
# Disable warning C4351: new behavior: elements of array '...' will be
# default initialized. The new behaviour is correct and we don't support
# old compilers anyway.
add_definitions(-wd4351)
# Disable warning C4503: "decorated name length exceeded, name was truncated"
# there are more than 150 of generated names in libosmium longer than 4096 symbols supported in MSVC
add_definitions(-wd4503)
add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN -D_CRT_SECURE_NO_WARNINGS)
endif()
if(APPLE)
# following only available from cmake 2.8.12:
# add_compile_options(-stdlib=libc++)
# so using this instead:
add_definitions(-stdlib=libc++)
set(LDFLAGS ${LDFLAGS} -stdlib=libc++)
endif()
#----------------------------------------------------------------------
# This is a set of recommended warning options that can be added when compiling
# libosmium code.
if(MSVC)
set(OSMIUM_WARNING_OPTIONS "/W3 /wd4514" CACHE STRING "Recommended warning options for libosmium")
else()
set(OSMIUM_WARNING_OPTIONS "-Wall -Wextra -pedantic -Wredundant-decls -Wdisabled-optimization -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wold-style-cast" CACHE STRING "Recommended warning options for libosmium")
endif()
set(OSMIUM_DRACONIC_CLANG_OPTIONS "-Wdocumentation -Wunused-exception-parameter -Wmissing-declarations -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-unused-macros -Wno-exit-time-destructors -Wno-global-constructors -Wno-padded -Wno-switch-enum -Wno-missing-prototypes -Wno-weak-vtables -Wno-cast-align -Wno-float-equal")
if(Osmium_DEBUG)
message(STATUS "OSMIUM_XML_LIBRARIES=" ${OSMIUM_XML_LIBRARIES})
message(STATUS "OSMIUM_PBF_LIBRARIES=" ${OSMIUM_PBF_LIBRARIES})
message(STATUS "OSMIUM_IO_LIBRARIES=" ${OSMIUM_IO_LIBRARIES})
message(STATUS "OSMIUM_LIBRARIES=" ${OSMIUM_LIBRARIES})
message(STATUS "OSMIUM_INCLUDE_DIRS=" ${OSMIUM_INCLUDE_DIRS})
endif()

3
cmake/README Normal file
View File

@ -0,0 +1,3 @@
FindGem.cmake from https://github.com/rock-core/base-cmake

15
cmake/build.bat Normal file
View File

@ -0,0 +1,15 @@
call "%VS120COMNTOOLS%\..\..\VC\vcvarsall.bat" x86_amd64
set CMAKE_PREFIX_PATH=C:\PROJ
set VERSION=Debug
set TESTS=ON
set ALLHPPS=ON
set PREFIX=d:\libs18d
set BOOST_ROOT=d:\boost
cmake .. -G "Visual Studio 12 Win64" -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=%PREFIX% -DBOOST_ROOT=%BOOST_ROOT% -DBoost_USE_STATIC_LIBS=ON -DBUILD_TESTING=%TESTS% -DBUILD_TRY_HPPS=%ALLHPPS$ -T CTP_Nov2013
msbuild /clp:Verbosity=minimal /nologo libosmium.sln /flp1:logfile=build_errors.txt;errorsonly /flp2:logfile=build_warnings.txt;warningsonly
set PATH=%PATH%;%PREFIX%/bin
del test\osm-testdata\*.db
del test\osm-testdata\*.json
if "%TESTS%"=="ON" ctest -VV >build_tests.log

48
cmake/iwyu.sh Executable file
View File

@ -0,0 +1,48 @@
#!/bin/sh
#
# This will run IWYU (Include What You Use) on includes files. The iwyu
# program isn't very reliable and crashes often, but is still useful.
#
# TODO: This script should be integrated with cmake in some way...
#
# If these are set, the wrong compiler is used by iwyu and there will be
# errors about missing includes.
unset CC
unset CXX
cmdline="iwyu -Xiwyu --mapping_file=osmium.imp -std=c++11 -I include"
log=build/iwyu.log
mkdir -p build/check_reports
echo "INCLUDE WHAT YOU USE REPORT:" >$log
allok=yes
for file in `find include/osmium -name \*.hpp | sort`; do
mkdir -p `dirname build/check_reports/$file`
ifile="build/check_reports/${file%.hpp}.iwyu"
$cmdline $file >$ifile 2>&1
if grep -q 'has correct #includes/fwd-decls' ${ifile}; then
echo "\n\033[1m\033[32m========\033[0m \033[1m${file}\033[0m" >>$log
echo "[OK] ${file}"
elif grep -q 'Assertion failed' ${ifile}; then
echo "\n\033[1m======== ${file}\033[0m" >>$log
echo "[--] ${file}"
allok=no
else
echo "\n\033[1m\033[31m========\033[0m \033[1m${file}\033[0m" >>$log
echo "[ ] ${file}"
allok=no
fi
cat $ifile >>$log
done
if [ "$allok" = "yes" ]; then
echo "All files OK"
else
echo "There were errors"
fi

33
doc/CMakeLists.txt Normal file
View File

@ -0,0 +1,33 @@
#-----------------------------------------------------------------------------
#
# CMake Config
#
# Libosmium documentation
#
#-----------------------------------------------------------------------------
message(STATUS "Configuring documentation")
message(STATUS "Looking for doxygen")
find_package(Doxygen)
if(DOXYGEN_FOUND)
message(STATUS "Looking for doxygen - found")
configure_file(header.html ${CMAKE_CURRENT_BINARY_DIR}/header.html @ONLY)
configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
add_custom_target(doc
${DOXYGEN_EXECUTABLE}
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen" VERBATIM
)
else()
message(STATUS "Looking for doxygen - not found")
message(STATUS " Disabled making of documentation.")
endif()
#-----------------------------------------------------------------------------
message(STATUS "Configuring documentation - done")
#-----------------------------------------------------------------------------

2313
doc/Doxyfile.in Normal file

File diff suppressed because it is too large Load Diff

8
doc/README.md Normal file
View File

@ -0,0 +1,8 @@
The `header.html` is created with:
`doxygen -w html header.html footer.html stylesheet.css`
This might have to be run again for newer Doxygen versions. After that add
changes back in.

21
doc/doc.md Normal file
View File

@ -0,0 +1,21 @@
Osmium is a fast and flexible C++ library for working with OpenStreetMap
data.
This is the API documentation that was automatically created from the
source code. For more information about the Osmium Library see
http://osmcode.org/libosmium .
Osmium is free software and available under the Boost Software License.
The source code is available at https://github.com/osmcode/libosmium .
Osmium 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 Osmium use only,
do not depend on it in your code. Do not include any include files in
directories named "detail" directly. Include files in directories called
"experimental" and everything in namespaces called "experimental" is
unsupported and may change at any time regardless of the status of the rest
of the library.

56
doc/header.html Normal file
View File

@ -0,0 +1,56 @@
<!-- HTML header for doxygen 1.8.8-->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<!--BEGIN TITLEAREA-->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<!--BEGIN PROJECT_LOGO-->
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"/></td>
<!--END PROJECT_LOGO-->
<!--BEGIN PROJECT_NAME-->
<td style="padding-left: 0.5em;">
<div id="projectname">$projectname
<!--BEGIN PROJECT_NUMBER-->&#160;<span id="projectnumber">$projectnumber</span><!--END PROJECT_NUMBER-->
</div>
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
</td>
<!--END PROJECT_NAME-->
<!--BEGIN !PROJECT_NAME-->
<!--BEGIN PROJECT_BRIEF-->
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<!--END PROJECT_BRIEF-->
<!--END !PROJECT_NAME-->
<!--BEGIN DISABLE_INDEX-->
<!--BEGIN SEARCHENGINE-->
<td>$searchbox</td>
<!--END SEARCHENGINE-->
<!--END DISABLE_INDEX-->
</tr>
</tbody>
</table>
</div>
<!--END TITLEAREA-->
<!-- end header part -->

22
doc/osmium.css Normal file
View File

@ -0,0 +1,22 @@
body {
font-family: "Droid Sans",Helvetica,Arial,sans-serif;
background-color: #ffffff;
color: #202060;
}
.tabs, .tabs2, .tabs3, .navpath ul, .tablist li {
background-image: none;
}
.tabs, .tabs2, .tabs3 {
border-top: 1px solid #202060;
}
div.contents {
margin: 0px;
padding-top: 10px;
padding-left: 12px;
padding-right: 8px;
}

74
examples/CMakeLists.txt Normal file
View File

@ -0,0 +1,74 @@
#-----------------------------------------------------------------------------
#
# CMake Config
#
# Libosmium examples
#
#-----------------------------------------------------------------------------
message(STATUS "Configuring examples")
set(EXAMPLES
amenity_list
area_test
change_tags
convert
count
create_pois
debug
dump_internal
filter_discussions
index_lookup
location_cache_create
location_cache_use
pub_names
read
read_with_progress
road_length
tiles
CACHE STRING "Example programs"
)
#-----------------------------------------------------------------------------
#
# Examples depending on wingetopt
#
#-----------------------------------------------------------------------------
set(GETOPT_EXAMPLES area_test convert index_lookup)
if(NOT GETOPT_MISSING)
foreach(example ${GETOPT_EXAMPLES})
list(APPEND EXAMPLE_LIBS_${example} ${GETOPT_LIBRARY})
endforeach()
else()
message(STATUS "Configuring examples - Skipping examples because on Visual Studio the wingetopt library is needed and was not found:")
foreach(example ${GETOPT_EXAMPLES})
message(STATUS " - osmium_${example}")
list(REMOVE_ITEM EXAMPLES ${example})
endforeach()
endif()
#-----------------------------------------------------------------------------
#
# Configure examples
#
#-----------------------------------------------------------------------------
message(STATUS "Configuring examples - Building these examples:")
foreach(example ${EXAMPLES})
message(STATUS " - osmium_${example}")
add_executable(osmium_${example} "osmium_${example}.cpp")
set_pthread_on_target(osmium_${example})
target_link_libraries(osmium_${example} ${OSMIUM_IO_LIBRARIES} ${EXAMPLE_LIBS_${example}})
add_test(NAME examples_usage_${example} COMMAND osmium_${example})
set_tests_properties(examples_usage_${example} PROPERTIES
PASS_REGULAR_EXPRESSION "^Usage: "
)
endforeach()
#-----------------------------------------------------------------------------
message(STATUS "Configuring examples - done")
#-----------------------------------------------------------------------------

45
examples/README.md Normal file
View File

@ -0,0 +1,45 @@
# Osmium example programs
The programs in this directory are intended as examples for developers. They
contain extensive comments explaining what's going on. Note that the examples
only cover a small part of what Osmium can do, you should also read the manuals
and API documentation.
All programs can be run without arguments and they will tell you how to use
them.
## Very simple examples
* `osmium_read`
* `osmium_count`
* `osmium_debug`
* `osmium_tiles`
## Still reasonably simple examples
* `osmium_amenity_list`
* `osmium_read_with_progress`
* `osmium_filter_discussions`
* `osmium_convert`
* `osmium_pub_names`
* `osmium_road_length`
## More advanced examples
* `osmium_area_test`
* `osmium_create_pois`
## Even more advanced examples
* `osmium_change_tags`
* `osmium_location_cache_create`
* `osmium_location_cache_use`
* `osmium_dump_internal`
* `osmium_index_lookup`
## License
The code in these example files is released into the Public Domain. Feel free
to copy the code and build on it.

View File

@ -0,0 +1,171 @@
/*
EXAMPLE osmium_amenity_list
Create a list of all amenities in the OSM input data. The type of amenity
(tag value) and, if available, the name is printed. For nodes, the location
is printed, for areas the center location.
DEMONSTRATES USE OF:
* file input
* location indexes and the NodeLocationsForWays handler
* the MultipolygonManager and Assembler to assemble areas (multipolygons)
* your own handler that works with areas (multipolygons)
* accessing tags
* osmium::geom::Coordinates
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
* osmium_debug
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdio> // for std::printf
#include <cstdlib> // for std::exit
#include <iostream> // for std::cerr
#include <string> // for std::string
// For the location index. There are different types of indexes available.
// This will work for all input files keeping the index in memory.
#include <osmium/index/map/flex_mem.hpp>
// For the NodeLocationForWays handler
#include <osmium/handler/node_locations_for_ways.hpp>
// The type of index used. This must match the include file above
using index_type = osmium::index::map::FlexMem<osmium::unsigned_object_id_type, osmium::Location>;
// The location handler always depends on the index type
using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
// For assembling multipolygons
#include <osmium/area/assembler.hpp>
#include <osmium/area/multipolygon_manager.hpp>
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// For osmium::apply()
#include <osmium/visitor.hpp>
// For osmium::geom::Coordinates
#include <osmium/geom/coordinates.hpp>
class AmenityHandler : public osmium::handler::Handler {
// Print info about one amenity to stdout.
void print_amenity(const char* type, const char* name, const osmium::geom::Coordinates& c) {
std::printf("%8.4f,%8.4f %-15s %s\n", c.x, c.y, type, name ? name : "");
}
// Calculate the center point of a NodeRefList.
osmium::geom::Coordinates calc_center(const osmium::NodeRefList& nr_list) {
// Coordinates simply store an X and Y coordinate pair as doubles.
// (Unlike osmium::Location which stores them more efficiently as
// 32 bit integers.) Use Coordinates when you want to do calculations
// or store projected coordinates.
osmium::geom::Coordinates c{0.0, 0.0};
for (const auto& nr : nr_list) {
c.x += nr.lon();
c.y += nr.lat();
}
c.x /= nr_list.size();
c.y /= nr_list.size();
return c;
}
public:
void node(const osmium::Node& node) {
// Getting a tag value can be expensive, because a list of tags has
// to be gone through and each tag has to be checked. So we store the
// result and reuse it.
const char* amenity = node.tags()["amenity"];
if (amenity) {
print_amenity(amenity, node.tags()["name"], node.location());
}
}
void area(const osmium::Area& area) {
const char* amenity = area.tags()["amenity"];
if (amenity) {
// Use the center of the first outer ring. Because we set
// create_empty_areas = false in the assembler config, we can
// be sure there will always be at least one outer ring.
const auto center = calc_center(*area.cbegin<osmium::OuterRing>());
print_amenity(amenity, area.tags()["name"], center);
}
}
}; // class AmenityHandler
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
std::exit(1);
}
// The input file
const osmium::io::File input_file{argv[1]};
// Configuration for the multipolygon assembler. We disable the option to
// create empty areas when invalid multipolygons are encountered. This
// means areas created have a valid geometry and invalid multipolygons
// are simply ignored.
osmium::area::Assembler::config_type assembler_config;
assembler_config.create_empty_areas = false;
// Initialize the MultipolygonManager. Its job is to collect all
// relations and member ways needed for each area. It then calls an
// instance of the osmium::area::Assembler class (with the given config)
// to actually assemble one area.
osmium::area::MultipolygonManager<osmium::area::Assembler> mp_manager{assembler_config};
// We read the input file twice. In the first pass, only relations are
// read and fed into the multipolygon manager.
std::cerr << "Pass 1...\n";
osmium::relations::read_relations(input_file, mp_manager);
std::cerr << "Pass 1 done\n";
// The index storing all node locations.
index_type index;
// The handler that stores all node locations in the index and adds them
// to the ways.
location_handler_type location_handler{index};
// If a location is not available in the index, we ignore it. It might
// not be needed (if it is not part of a multipolygon relation), so why
// create an error?
location_handler.ignore_errors();
// Create our handler.
AmenityHandler data_handler;
// On the second pass we read all objects and run them first through the
// node location handler and then the multipolygon manager. The manager
// will put the areas it has created into the "buffer" which are then
// fed through our handler.
//
// The read_meta::no option disables reading of meta data (such as version
// numbers, timestamps, etc.) which are not needed in this case. Disabling
// this can speed up your program.
std::cerr << "Pass 2...\n";
osmium::io::Reader reader{input_file, osmium::io::read_meta::no};
osmium::apply(reader, location_handler, data_handler, mp_manager.handler([&data_handler](const osmium::memory::Buffer& area_buffer) {
osmium::apply(area_buffer, data_handler);
}));
reader.close();
std::cerr << "Pass 2 done\n";
}

View File

@ -0,0 +1,210 @@
/*
EXAMPLE osmium_area_test
Create multipolygons from OSM data and dump them to stdout in one of two
formats: WKT or using the built-in Dump format.
DEMONSTRATES USE OF:
* file input
* location indexes and the NodeLocationsForWays handler
* the MultipolygonManager and Assembler to assemble areas (multipolygons)
* your own handler that works with areas (multipolygons)
* the WKTFactory to write geometries in WKT format
* the Dump handler
* the DynamicHandler
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
* osmium_debug
* osmium_amenity_list
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdlib> // for std::exit
#include <getopt.h> // for getopt_long
#include <iostream> // for std::cout, std::cerr
// For assembling multipolygons
#include <osmium/area/assembler.hpp>
#include <osmium/area/multipolygon_manager.hpp>
// For the DynamicHandler class
#include <osmium/dynamic_handler.hpp>
// For the WKT factory
#include <osmium/geom/wkt.hpp>
// For the Dump handler
#include <osmium/handler/dump.hpp>
// For the NodeLocationForWays handler
#include <osmium/handler/node_locations_for_ways.hpp>
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// For osmium::apply()
#include <osmium/visitor.hpp>
// For the location index. There are different types of indexes available.
// This will work for all input files keeping the index in memory.
#include <osmium/index/map/flex_mem.hpp>
// The type of index used. This must match the include file above
using index_type = osmium::index::map::FlexMem<osmium::unsigned_object_id_type, osmium::Location>;
// The location handler always depends on the index type
using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
// This handler writes all area geometries out in WKT (Well Known Text) format.
class WKTDump : public osmium::handler::Handler {
// This factory is used to create a geometry in WKT format from OSM
// objects. The template parameter is empty here, because we output WGS84
// coordinates, but could be used for a projection.
osmium::geom::WKTFactory<> m_factory;
public:
// This callback is called by osmium::apply for each area in the data.
void area(const osmium::Area& area) {
try {
std::cout << m_factory.create_multipolygon(area) << "\n";
} catch (const osmium::geometry_error& e) {
std::cout << "GEOMETRY ERROR: " << e.what() << "\n";
}
}
}; // class WKTDump
void print_help() {
std::cout << "osmium_area_test [OPTIONS] OSMFILE\n\n"
<< "Read OSMFILE and build multipolygons from it.\n"
<< "\nOptions:\n"
<< " -h, --help This help message\n"
<< " -w, --dump-wkt Dump area geometries as WKT\n"
<< " -o, --dump-objects Dump area objects\n";
}
int main(int argc, char* argv[]) {
static struct option long_options[] = {
{"help", no_argument, nullptr, 'h'},
{"dump-wkt", no_argument, nullptr, 'w'},
{"dump-objects", no_argument, nullptr, 'o'},
{nullptr, 0, nullptr, 0}
};
// Initialize an empty DynamicHandler. Later it will be associated
// with one of the handlers. You can think of the DynamicHandler as
// a kind of "variant handler" or a "pointer handler" pointing to the
// real handler.
osmium::handler::DynamicHandler handler;
// Read options from command line.
while (true) {
const int c = getopt_long(argc, argv, "hwo", long_options, nullptr);
if (c == -1) {
break;
}
switch (c) {
case 'h':
print_help();
std::exit(0);
case 'w':
handler.set<WKTDump>();
break;
case 'o':
handler.set<osmium::handler::Dump>(std::cout);
break;
default:
std::exit(1);
}
}
const int remaining_args = argc - optind;
if (remaining_args != 1) {
std::cerr << "Usage: " << argv[0] << " [OPTIONS] OSMFILE\n";
std::exit(1);
}
osmium::io::File input_file{argv[optind]};
// Configuration for the multipolygon assembler. Here the default settings
// are used, but you could change multiple settings.
osmium::area::Assembler::config_type assembler_config;
// Set up a filter matching only forests. This will be used to only build
// areas with matching tags.
osmium::TagsFilter filter{false};
filter.add_rule(true, "landuse", "forest");
filter.add_rule(true, "natural", "wood");
// Initialize the MultipolygonManager. Its job is to collect all
// relations and member ways needed for each area. It then calls an
// instance of the osmium::area::Assembler class (with the given config)
// to actually assemble one area. The filter parameter is optional, if
// it is not set, all areas will be built.
osmium::area::MultipolygonManager<osmium::area::Assembler> mp_manager{assembler_config, filter};
// We read the input file twice. In the first pass, only relations are
// read and fed into the multipolygon manager.
std::cerr << "Pass 1...\n";
osmium::relations::read_relations(input_file, mp_manager);
std::cerr << "Pass 1 done\n";
// Output the amount of main memory used so far. All multipolygon relations
// are in memory now.
std::cerr << "Memory:\n";
osmium::relations::print_used_memory(std::cerr, mp_manager.used_memory());
// The index storing all node locations.
index_type index;
// The handler that stores all node locations in the index and adds them
// to the ways.
location_handler_type location_handler{index};
// If a location is not available in the index, we ignore it. It might
// not be needed (if it is not part of a multipolygon relation), so why
// create an error?
location_handler.ignore_errors();
// On the second pass we read all objects and run them first through the
// node location handler and then the multipolygon collector. The collector
// will put the areas it has created into the "buffer" which are then
// fed through our "handler".
std::cerr << "Pass 2...\n";
osmium::io::Reader reader{input_file};
osmium::apply(reader, location_handler, mp_manager.handler([&handler](osmium::memory::Buffer&& buffer) {
osmium::apply(buffer, handler);
}));
reader.close();
std::cerr << "Pass 2 done\n";
// Output the amount of main memory used so far. All complete multipolygon
// relations have been cleaned up.
std::cerr << "Memory:\n";
osmium::relations::print_used_memory(std::cerr, mp_manager.used_memory());
// If there were multipolgyon relations in the input, but some of their
// members are not in the input file (which often happens for extracts)
// this will write the IDs of the incomplete relations to stderr.
std::vector<osmium::object_id_type> incomplete_relations_ids;
mp_manager.for_each_incomplete_relation([&](const osmium::relations::RelationHandle& handle){
incomplete_relations_ids.push_back(handle->id());
});
if (!incomplete_relations_ids.empty()) {
std::cerr << "Warning! Some member ways missing for these multipolygon relations:";
for (const auto id : incomplete_relations_ids) {
std::cerr << " " << id;
}
std::cerr << "\n";
}
}

View File

@ -0,0 +1,203 @@
/*
EXAMPLE osmium_change_tags
An example how tags in OSM files can be removed or changed. Removes
"created_by" tags and changes tag "landuse=forest" into "natural_wood".
DEMONSTRATES USE OF:
* file input and output
* Osmium buffers
* your own handler
* access to tags
* using builders to write data
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
* osmium_pub_names
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdlib> // for std::exit
#include <cstring> // for std::strcmp
#include <exception> // for std::exception
#include <iostream> // for std::cout, std::cerr
#include <string> // for std::string
#include <utility> // for std::move
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// Allow any format of output files (XML, PBF, ...)
#include <osmium/io/any_output.hpp>
// We want to use the builder interface
#include <osmium/builder/osm_object_builder.hpp>
// We want to use the handler interface
#include <osmium/handler.hpp>
// For osmium::apply()
#include <osmium/visitor.hpp>
// The functions in this class will be called for each object in the input
// and will write a (changed) copy of those objects to the given buffer.
class RewriteHandler : public osmium::handler::Handler {
osmium::memory::Buffer& m_buffer;
// Copy attributes common to all OSM objects (nodes, ways, and relations).
template <typename T>
void copy_attributes(T& builder, const osmium::OSMObject& object) {
// The setter functions on the builder object all return the same
// builder object so they can be chained.
builder.set_id(object.id())
.set_version(object.version())
.set_changeset(object.changeset())
.set_timestamp(object.timestamp())
.set_uid(object.uid())
.set_user(object.user());
}
// Copy all tags with two changes:
// * Do not copy "created_by" tags
// * Change "landuse=forest" into "natural=wood"
void copy_tags(osmium::builder::Builder& parent, const osmium::TagList& tags) {
// The TagListBuilder is used to create a list of tags. The parameter
// to create it is a reference to the builder of the object that
// should have those tags.
osmium::builder::TagListBuilder builder{parent};
// Iterate over all tags and build new tags using the new builder
// based on the old ones.
for (const auto& tag : tags) {
if (std::strcmp(tag.key(), "created_by")) {
if (!std::strcmp(tag.key(), "landuse") && !std::strcmp(tag.value(), "forest")) {
// add_tag() can be called with key and value C strings
builder.add_tag("natural", "wood");
} else {
// add_tag() can also be called with an osmium::Tag
builder.add_tag(tag);
}
}
}
}
public:
// Constructor. New data will be added to the given buffer.
explicit RewriteHandler(osmium::memory::Buffer& buffer) :
m_buffer(buffer) {
}
// The node handler is called for each node in the input data.
void node(const osmium::Node& node) {
// Open a new scope, because the NodeBuilder we are creating has to
// be destructed, before we can call commit() below.
{
// To create a node, we need a NodeBuilder object. It will create
// the node in the given buffer.
osmium::builder::NodeBuilder builder{m_buffer};
// Copy common object attributes over to the new node.
copy_attributes(builder, node);
// Copy the location over to the new node.
builder.set_location(node.location());
// Copy (changed) tags.
copy_tags(builder, node.tags());
}
// Once the object is written to the buffer completely, we have to call
// commit().
m_buffer.commit();
}
// The way handler is called for each way in the input data.
void way(const osmium::Way& way) {
{
osmium::builder::WayBuilder builder{m_buffer};
copy_attributes(builder, way);
copy_tags(builder, way.tags());
// Copy the node list over to the new way.
builder.add_item(way.nodes());
}
m_buffer.commit();
}
// The relation handler is called for each relation in the input data.
void relation(const osmium::Relation& relation) {
{
osmium::builder::RelationBuilder builder{m_buffer};
copy_attributes(builder, relation);
copy_tags(builder, relation.tags());
// Copy the relation member list over to the new way.
builder.add_item(relation.members());
}
m_buffer.commit();
}
}; // class RewriteHandler
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " INFILE OUTFILE\n";
std::exit(1);
}
// Get input and output file names from command line.
std::string input_file_name{argv[1]};
std::string output_file_name{argv[2]};
try {
// Initialize Reader
osmium::io::Reader reader{input_file_name};
// Get header from input file and change the "generator" setting to
// ourselves.
osmium::io::Header header = reader.header();
header.set("generator", "osmium_change_tags");
// Initialize Writer using the header from above and tell it that it
// is allowed to overwrite a possibly existing file.
osmium::io::Writer writer{output_file_name, header, osmium::io::overwrite::allow};
// Read in buffers with OSM objects until there are no more.
while (osmium::memory::Buffer input_buffer = reader.read()) {
// Create an empty buffer with the same size as the input buffer.
// We'll copy the changed data into output buffer, the changes
// are small, so the output buffer needs to be about the same size.
// In case it has to be bigger, we allow it to grow automatically
// by adding the auto_grow::yes parameter.
osmium::memory::Buffer output_buffer{input_buffer.committed(), osmium::memory::Buffer::auto_grow::yes};
// Construct a handler as defined above and feed the input buffer
// to it.
RewriteHandler handler{output_buffer};
osmium::apply(input_buffer, handler);
// Write out the contents of the output buffer.
writer(std::move(output_buffer));
}
// Explicitly close the writer and reader. Will throw an exception if
// there is a problem. If you wait for the destructor to close the writer
// and reader, you will not notice the problem, because destructors must
// not throw.
writer.close();
reader.close();
} catch (const std::exception& e) {
// All exceptions used by the Osmium library derive from std::exception.
std::cerr << e.what() << "\n";
std::exit(1);
}
}

155
examples/osmium_convert.cpp Normal file
View File

@ -0,0 +1,155 @@
/*
EXAMPLE osmium_convert
Convert OSM files from one format into another.
DEMONSTRATES USE OF:
* file input and output
* file types
* Osmium buffers
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdlib> // for std::exit
#include <exception> // for std::exception
#include <getopt.h> // for getopt_long
#include <iostream> // for std::cout, std::cerr
#include <string> // for std::string
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// Allow any format of output files (XML, PBF, ...)
#include <osmium/io/any_output.hpp>
void print_help() {
std::cout << "osmium_convert [OPTIONS] [INFILE [OUTFILE]]\n\n" \
<< "If INFILE or OUTFILE is not given stdin/stdout is assumed.\n" \
<< "File format is autodetected from file name suffix.\n" \
<< "Use -f and -t options to force file format.\n" \
<< "\nFile types:\n" \
<< " osm normal OSM file\n" \
<< " osc OSM change file\n" \
<< " osh OSM file with history information\n" \
<< "\nFile format:\n" \
<< " (default) XML encoding\n" \
<< " pbf binary PBF encoding\n" \
<< " opl OPL encoding\n" \
<< "\nFile compression\n" \
<< " gz compressed with gzip\n" \
<< " bz2 compressed with bzip2\n" \
<< "\nOptions:\n" \
<< " -h, --help This help message\n" \
<< " -f, --from-format=FORMAT Input format\n" \
<< " -t, --to-format=FORMAT Output format\n";
}
int main(int argc, char* argv[]) {
static struct option long_options[] = {
{"help", no_argument, nullptr, 'h'},
{"from-format", required_argument, nullptr, 'f'},
{"to-format", required_argument, nullptr, 't'},
{nullptr, 0, nullptr, 0}
};
// Input and output format are empty by default. Later this will mean that
// the format should be taken from the input and output file suffix,
// respectively.
std::string input_format;
std::string output_format;
// Read options from command line.
while (true) {
const int c = getopt_long(argc, argv, "dhf:t:", long_options, nullptr);
if (c == -1) {
break;
}
switch (c) {
case 'h':
print_help();
std::exit(0);
case 'f':
input_format = optarg;
break;
case 't':
output_format = optarg;
break;
default:
std::exit(1);
}
}
const int remaining_args = argc - optind;
if (remaining_args == 0 || remaining_args > 2) {
std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]\n";
std::exit(1);
}
// Get input file name from command line.
std::string input_file_name;
if (remaining_args >= 1) {
input_file_name = argv[optind];
}
// Get output file name from command line.
std::string output_file_name;
if (remaining_args == 2) {
output_file_name = argv[optind+1];
}
// This declares the input and output files using either the suffix of
// the file names or the format in the 2nd argument. It does not yet open
// the files.
const osmium::io::File input_file{input_file_name, input_format};
const osmium::io::File output_file{output_file_name, output_format};
// Input and output files can be OSM data files (without history) or
// OSM history files. History files are detected if they use the '.osh'
// file suffix.
if ( input_file.has_multiple_object_versions() &&
!output_file.has_multiple_object_versions()) {
std::cerr << "Warning! You are converting from an OSM file with (potentially) several versions of the same object to one that is not marked as such.\n";
}
try {
// Initialize Reader
osmium::io::Reader reader{input_file};
// Get header from input file and change the "generator" setting to
// ourselves.
osmium::io::Header header = reader.header();
header.set("generator", "osmium_convert");
// Initialize Writer using the header from above and tell it that it
// is allowed to overwrite a possibly existing file.
osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow};
// Copy the contents from the input to the output file one buffer at
// a time. This is much easier and faster than copying each object
// in the file. Buffers are moved around, so there is no cost for
// copying in memory.
while (osmium::memory::Buffer buffer = reader.read()) {
writer(std::move(buffer));
}
// Explicitly close the writer and reader. Will throw an exception if
// there is a problem. If you wait for the destructor to close the writer
// and reader, you will not notice the problem, because destructors must
// not throw.
writer.close();
reader.close();
} catch (const std::exception& e) {
// All exceptions used by the Osmium library derive from std::exception.
std::cerr << e.what() << "\n";
std::exit(1);
}
}

95
examples/osmium_count.cpp Normal file
View File

@ -0,0 +1,95 @@
/*
EXAMPLE osmium_count
Counts the number of nodes, ways, and relations in the input file.
DEMONSTRATES USE OF:
* OSM file input
* your own handler
* the memory usage utility class
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdint> // for std::uint64_t
#include <cstdlib> // for std::exit
#include <iostream> // for std::cout, std::cerr
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// We want to use the handler interface
#include <osmium/handler.hpp>
// Utility class gives us access to memory usage information
#include <osmium/util/memory.hpp>
// For osmium::apply()
#include <osmium/visitor.hpp>
// Handler derive from the osmium::handler::Handler base class. Usually you
// overwrite functions node(), way(), and relation(). Other functions are
// available, too. Read the API documentation for details.
struct CountHandler : public osmium::handler::Handler {
std::uint64_t nodes = 0;
std::uint64_t ways = 0;
std::uint64_t relations = 0;
// This callback is called by osmium::apply for each node in the data.
void node(const osmium::Node&) noexcept {
++nodes;
}
// This callback is called by osmium::apply for each way in the data.
void way(const osmium::Way&) noexcept {
++ways;
}
// This callback is called by osmium::apply for each relation in the data.
void relation(const osmium::Relation&) noexcept {
++relations;
}
}; // struct CountHandler
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
std::exit(1);
}
// The Reader is initialized here with an osmium::io::File, but could
// also be directly initialized with a file name.
osmium::io::File input_file{argv[1]};
osmium::io::Reader reader{input_file};
// Create an instance of our own CountHandler and push the data from the
// input file through it.
CountHandler handler;
osmium::apply(reader, handler);
// You do not have to close the Reader explicitly, but because the
// destructor can't throw, you will not see any errors otherwise.
reader.close();
std::cout << "Nodes: " << handler.nodes << "\n";
std::cout << "Ways: " << handler.ways << "\n";
std::cout << "Relations: " << handler.relations << "\n";
// Because of the huge amount of OSM data, some Osmium-based programs
// (though not this one) can use huge amounts of data. So checking actual
// memore usage is often useful and can be done easily with this class.
// (Currently only works on Linux, not OSX and Windows.)
osmium::MemoryUsage memory;
std::cout << "\nMemory used: " << memory.peak() << " MBytes\n";
}

View File

@ -0,0 +1,100 @@
/*
EXAMPLE osmium_create_pois
Showing how to create nodes for points of interest out of thin air.
DEMONSTRATES USE OF:
* file output
* Osmium buffers
* using builders to write data
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
* osmium_pub_names
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdlib> // for std::exit
#include <cstring> // for std::strcmp
#include <ctime> // for std::time
#include <exception> // for std::exception
#include <iostream> // for std::cout, std::cerr
#include <string> // for std::string
#include <utility> // for std::move
// Allow any format of output files (XML, PBF, ...)
#include <osmium/io/any_output.hpp>
// We want to use the builder interface
#include <osmium/builder/osm_object_builder.hpp>
#include <osmium/builder/attr.hpp>
// Declare this to use the functions starting with the underscore (_) below.
using namespace osmium::builder::attr;
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OUTFILE\n";
std::exit(1);
}
// Get output file name from command line.
std::string output_file_name{argv[1]};
// If output file name is "-", this means STDOUT. Set the OPL file type
// in this case. Otherwise take the file type from the file name suffix.
osmium::io::File output_file{output_file_name, output_file_name == "-" ? ".opl" : ""};
try {
// Create a buffer where all objects will live. Use a sensible initial
// buffer size and set the buffer to automatically grow if needed.
const size_t initial_buffer_size = 10000;
osmium::memory::Buffer buffer{initial_buffer_size, osmium::memory::Buffer::auto_grow::yes};
// Add nodes to the buffer. This is, of course, only an example.
// You can set any of the attributes and more tags, etc. Ways and
// relations can be added in a similar way.
osmium::builder::add_node(buffer,
_id(-1),
_version(1),
_timestamp(std::time(nullptr)),
_location(osmium::Location{1.23, 3.45}),
_tag("amenity", "post_box")
);
osmium::builder::add_node(buffer,
_id(-2),
_version(1),
_timestamp(std::time(nullptr)),
_location(1.24, 3.46),
_tags({{"amenity", "restaurant"},
{"name", "Chez OSM"}})
);
// Create header and set generator.
osmium::io::Header header;
header.set("generator", "osmium_create_pois");
// Initialize Writer using the header from above and tell it that it
// is allowed to overwrite a possibly existing file.
osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow};
// Write out the contents of the output buffer.
writer(std::move(buffer));
// Explicitly close the writer. Will throw an exception if there is
// a problem. If you wait for the destructor to close the writer, you
// will not notice the problem, because destructors must not throw.
writer.close();
} catch (const std::exception& e) {
// All exceptions used by the Osmium library derive from std::exception.
std::cerr << e.what() << "\n";
std::exit(1);
}
}

84
examples/osmium_debug.cpp Normal file
View File

@ -0,0 +1,84 @@
/*
EXAMPLE osmium_debug
Dump the contents of the input file in a debug format.
DEMONSTRATES USE OF:
* file input reading only some types
* the dump handler
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdlib> // for std::exit
#include <iostream> // for std::cout, std::cerr
#include <string> // for std::string
// The Dump handler
#include <osmium/handler/dump.hpp>
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
int main(int argc, char* argv[]) {
// Speed up output (not Osmium-specific)
std::ios_base::sync_with_stdio(false);
if (argc < 2 || argc > 3) {
std::cerr << "Usage: " << argv[0] << " OSMFILE [TYPES]\n";
std::cerr << "TYPES can be any combination of 'n', 'w', 'r', and 'c' to indicate what types of OSM entities you want (default: all).\n";
std::exit(1);
}
// Default is all entity types: nodes, ways, relations, and changesets
osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all;
// Get entity types from command line if there is a 2nd argument.
if (argc == 3) {
read_types = osmium::osm_entity_bits::nothing;
std::string types = argv[2];
if (types.find('n') != std::string::npos) {
read_types |= osmium::osm_entity_bits::node;
}
if (types.find('w') != std::string::npos) {
read_types |= osmium::osm_entity_bits::way;
}
if (types.find('r') != std::string::npos) {
read_types |= osmium::osm_entity_bits::relation;
}
if (types.find('c') != std::string::npos) {
read_types |= osmium::osm_entity_bits::changeset;
}
}
// Initialize Reader with file name and the types of entities we want to
// read.
osmium::io::Reader reader{argv[1], read_types};
// The file header can contain metadata such as the program that generated
// the file and the bounding box of the data.
osmium::io::Header header = reader.header();
std::cout << "HEADER:\n generator=" << header.get("generator") << "\n";
for (const auto& bbox : header.boxes()) {
std::cout << " bbox=" << bbox << "\n";
}
// Initialize Dump handler.
osmium::handler::Dump dump{std::cout};
// Read from input and send everything to Dump handler.
osmium::apply(reader, dump);
// You do not have to close the Reader explicitly, but because the
// destructor can't throw, you will not see any errors otherwise.
reader.close();
}

View File

@ -0,0 +1,190 @@
/*
EXAMPLE osmium_dump_internal
Reads an OSM file and dumps the internal datastructure to disk including
indexes to find objects and object relations.
Note that this example programm will only work with small and medium sized
OSM files, not with the planet.
You can use the osmium_index example program to inspect the indexes.
DEMONSTRATES USE OF:
* file input
* indexes and maps
* use of the DiskStore handler
* use of the ObjectRelations handler
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
* osmium_road_length
* osmium_location_cache_create
* osmium_location_cache_use
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cerrno> // for errno
#include <cstring> // for std::strerror
#include <cstdlib> // for std::exit
#include <iostream> // for std::cout, std::cerr
#include <string> // for std::string
#include <sys/stat.h> // for open
#include <sys/types.h> // for open
#ifdef _WIN32
# include <io.h> // for _setmode
#endif
#ifdef _MSC_VER
# include <direct.h>
#endif
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// The DiskStore handler
#include <osmium/handler/disk_store.hpp>
// The ObjectRelations handler
#include <osmium/handler/object_relations.hpp>
// The indexes
#include <osmium/index/map/sparse_mem_array.hpp>
#include <osmium/index/multimap/sparse_mem_array.hpp>
using offset_index_type = osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, size_t>;
using map_type = osmium::index::multimap::SparseMemArray<osmium::unsigned_object_id_type, osmium::unsigned_object_id_type>;
/**
* Small class wrapping index files, basically making sure errors are handled
* and the files are closed on destruction.
*/
class IndexFile {
int m_fd;
public:
explicit IndexFile(const std::string& filename) :
m_fd(::open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666)) {
if (m_fd < 0) {
std::cerr << "Can't open index file '" << filename << "': " << std::strerror(errno) << "\n";
std::exit(2);
}
#ifdef _WIN32
_setmode(m_fd, _O_BINARY);
#endif
}
~IndexFile() {
if (m_fd >= 0) {
close(m_fd);
}
}
int fd() const noexcept {
return m_fd;
}
}; // class IndexFile
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " OSMFILE DIR\n";
std::exit(2);
}
const std::string input_file_name{argv[1]};
const std::string output_dir{argv[2]};
// Create output directory. Ignore the error if it already exists.
#ifndef _WIN32
const int result = ::mkdir(output_dir.c_str(), 0777);
#else
const int result = mkdir(output_dir.c_str());
#endif
if (result == -1 && errno != EEXIST) {
std::cerr << "Problem creating directory '" << output_dir << "': " << std::strerror(errno) << "\n";
std::exit(2);
}
// Create the output file which will contain our serialized OSM data
const std::string data_file{output_dir + "/data.osm.ser"};
const int data_fd = ::open(data_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (data_fd < 0) {
std::cerr << "Can't open data file '" << data_file << "': " << std::strerror(errno) << "\n";
std::exit(2);
}
#ifdef _WIN32
_setmode(data_fd, _O_BINARY);
#endif
// These indexes store the offset in the data file where each node, way,
// or relation is stored.
offset_index_type node_index;
offset_index_type way_index;
offset_index_type relation_index;
// This handler will dump the internal data to disk using the given file
// descriptor while updating the indexes.
osmium::handler::DiskStore disk_store_handler{data_fd, node_index, way_index, relation_index};
// These indexes store the mapping from node id to the ids of the ways
// containing this node, and from node/way/relation ids to the ids of the
// relations containing those objects.
map_type map_node2way;
map_type map_node2relation;
map_type map_way2relation;
map_type map_relation2relation;
// This handler will update the map indexes.
osmium::handler::ObjectRelations object_relations_handler{map_node2way, map_node2relation, map_way2relation, map_relation2relation};
// Read OSM data buffer by buffer.
osmium::io::Reader reader{input_file_name};
while (osmium::memory::Buffer buffer = reader.read()) {
// Write buffer to disk and update indexes.
disk_store_handler(buffer);
// Update object relation index maps.
osmium::apply(buffer, object_relations_handler);
}
reader.close();
// Write out node, way, and relation offset indexes to disk.
IndexFile nodes_idx{output_dir + "/nodes.idx"};
node_index.dump_as_list(nodes_idx.fd());
IndexFile ways_idx{output_dir + "/ways.idx"};
way_index.dump_as_list(ways_idx.fd());
IndexFile relations_idx{output_dir + "/relations.idx"};
relation_index.dump_as_list(relations_idx.fd());
// Sort the maps (so later binary search will work on them) and write
// them to disk.
map_node2way.sort();
IndexFile node2way_idx{output_dir + "/node2way.map"};
map_node2way.dump_as_list(node2way_idx.fd());
map_node2relation.sort();
IndexFile node2relation_idx{output_dir + "/node2rel.map"};
map_node2relation.dump_as_list(node2relation_idx.fd());
map_way2relation.sort();
IndexFile way2relation_idx{output_dir + "/way2rel.map"};
map_way2relation.dump_as_list(way2relation_idx.fd());
map_relation2relation.sort();
IndexFile relation2relation_idx{output_dir + "/rel2rel.map"};
map_relation2relation.dump_as_list(relation2relation_idx.fd());
}

View File

@ -0,0 +1,92 @@
/*
EXAMPLE osmium_filter_discussions
Read OSM changesets with discussions from a changeset dump like the one
you get from http://planet.osm.org/planet/discussions-latest.osm.bz2
and write out only those changesets which have discussions (ie comments).
DEMONSTRATES USE OF:
* file input and output
* setting file formats using the osmium::io::File class
* OSM file headers
* input and output iterators
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <algorithm> // for std::copy_if
#include <cstdlib> // for std::exit
#include <iostream> // for std::cout, std::cerr
// We want to read OSM files in XML format
// (other formats don't support full changesets, so only XML is needed here).
#include <osmium/io/xml_input.hpp>
// We want to write OSM files in XML format.
#include <osmium/io/xml_output.hpp>
// We want to use input and output iterators for easy integration with the
// algorithms of the standard library.
#include <osmium/io/input_iterator.hpp>
#include <osmium/io/output_iterator.hpp>
// We want to support any compression (none, gzip, and bzip2).
#include <osmium/io/any_compression.hpp>
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cout << "Usage: " << argv[0] << " INFILE OUTFILE\n";
std::exit(1);
}
// The input file, deduce file format from file suffix.
osmium::io::File input_file{argv[1]};
// The output file, force XML OSM file format.
osmium::io::File output_file{argv[2], "osm"};
// Initialize Reader for the input file.
// Read only changesets (will ignore nodes, ways, and
// relations if there are any).
osmium::io::Reader reader{input_file, osmium::osm_entity_bits::changeset};
// Get the header from the input file.
osmium::io::Header header = reader.header();
// Set the "generator" on the header to ourselves.
header.set("generator", "osmium_filter_discussions");
// Initialize writer for the output file. Use the header from the input
// file for the output file. This will copy over some header information.
// The last parameter will tell the writer that it is allowed to overwrite
// an existing file. Without it, it will refuse to do so.
osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow};
// Create range of input iterators that will iterator over all changesets
// delivered from input file through the "reader".
auto input_range = osmium::io::make_input_iterator_range<osmium::Changeset>(reader);
// Create an output iterator writing through the "writer" object to the
// output file.
auto output_iterator = osmium::io::make_output_iterator(writer);
// Copy all changesets from input to output that have at least one comment.
std::copy_if(input_range.begin(), input_range.end(), output_iterator, [](const osmium::Changeset& changeset) {
return changeset.num_comments() > 0;
});
// Explicitly close the writer and reader. Will throw an exception if
// there is a problem. If you wait for the destructor to close the writer
// and reader, you will not notice the problem, because destructors must
// not throw.
writer.close();
reader.close();
}

View File

@ -0,0 +1,346 @@
/*
EXAMPLE osmium_index
Example program to look at Osmium indexes on disk.
You can use the osmium_dump_internal example program to create the offset
indexes or osmium_location_cache_create to create a node location index.
DEMONSTRATES USE OF:
* access to indexes on disk
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
* osmium_road_length
* osmium_location_cache_create
* osmium_location_cache_use
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <algorithm> // for std::all_of, std::equal_range
#include <cstdlib> // for std::exit
#include <fcntl.h> // for open
#include <getopt.h> // for getopt_long
#include <iostream> // for std::cout, std::cerr
#include <memory> // for std::unique_ptr
#include <string> // for std::string
#include <sys/stat.h> // for open
#include <sys/types.h> // for open
#include <vector> // for std::vector
#ifdef _WIN32
# include <io.h> // for _setmode
#endif
// Disk-based indexes
#include <osmium/index/map/dense_file_array.hpp>
#include <osmium/index/map/sparse_file_array.hpp>
// osmium::Location
#include <osmium/osm/location.hpp>
// Basic Osmium types
#include <osmium/osm/types.hpp>
// Virtual class for disk index access. If offers functions to dump the
// indexes and to search for ids in the index.
template <typename TValue>
class IndexAccess {
int m_fd;
public:
explicit IndexAccess(int fd) :
m_fd(fd) {
}
int fd() const noexcept {
return m_fd;
}
virtual ~IndexAccess() = default;
virtual void dump() const = 0;
virtual bool search(const osmium::unsigned_object_id_type& key) const = 0;
bool search(const std::vector<osmium::unsigned_object_id_type>& keys) const {
return std::all_of(keys.cbegin(), keys.cend(), [this](const osmium::unsigned_object_id_type& key) {
return search(key);
});
}
}; // class IndexAccess
// Implementation of IndexAccess for dense indexes usually used for very large
// extracts or the planet.
template <typename TValue>
class IndexAccessDense : public IndexAccess<TValue> {
using index_type = typename osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, TValue>;
public:
explicit IndexAccessDense(int fd) :
IndexAccess<TValue>(fd) {
}
void dump() const override {
index_type index{this->fd()};
for (std::size_t i = 0; i < index.size(); ++i) {
if (index.get(i) != TValue{}) {
std::cout << i << " " << index.get(i) << "\n";
}
}
}
bool search(const osmium::unsigned_object_id_type& key) const override {
index_type index{this->fd()};
try {
TValue value = index.get(key);
std::cout << key << " " << value << "\n";
} catch (...) {
std::cout << key << " not found\n";
return false;
}
return true;
}
}; // class IndexAccessDense
// Implementation of IndexAccess for sparse indexes usually used for small or
// medium sized extracts or for "multimap" type indexes.
template <typename TValue>
class IndexAccessSparse : public IndexAccess<TValue> {
using index_type = typename osmium::index::map::SparseFileArray<osmium::unsigned_object_id_type, TValue>;
public:
explicit IndexAccessSparse(int fd) :
IndexAccess<TValue>(fd) {
}
void dump() const override {
index_type index{this->fd()};
for (const auto& element : index) {
std::cout << element.first << " " << element.second << "\n";
}
}
bool search(const osmium::unsigned_object_id_type& key) const override {
using element_type = typename index_type::element_type;
index_type index{this->fd()};
element_type elem{key, TValue{}};
const auto positions = std::equal_range(index.begin(),
index.end(),
elem,
[](const element_type& lhs,
const element_type& rhs) {
return lhs.first < rhs.first;
});
if (positions.first == positions.second) {
std::cout << key << " not found\n";
return false;
}
for (auto it = positions.first; it != positions.second; ++it) {
std::cout << it->first << " " << it->second << "\n";
}
return true;
}
}; // class IndexAccessSparse
// This class contains the code to parse the command line arguments, check
// them and present the results to the rest of the program in an easy-to-use
// way.
class Options {
std::vector<osmium::unsigned_object_id_type> m_ids;
std::string m_type;
std::string m_filename;
bool m_dump = false;
bool m_array_format = false;
bool m_list_format = false;
void print_help() {
std::cout << "Usage: osmium_index_lookup [OPTIONS]\n\n"
<< "-h, --help Print this help message\n"
<< "-a, --array=FILE Read given index file in array format\n"
<< "-l, --list=FILE Read given index file in list format\n"
<< "-d, --dump Dump contents of index file to STDOUT\n"
<< "-s, --search=ID Search for given id (Option can appear multiple times)\n"
<< "-t, --type=TYPE Type of value ('location', 'id', or 'offset')\n"
;
}
public:
Options(int argc, char* argv[]) {
if (argc == 1) {
print_help();
std::exit(1);
}
static struct option long_options[] = {
{"array", required_argument, nullptr, 'a'},
{"dump", no_argument, nullptr, 'd'},
{"help", no_argument, nullptr, 'h'},
{"list", required_argument, nullptr, 'l'},
{"search", required_argument, nullptr, 's'},
{"type", required_argument, nullptr, 't'},
{nullptr, 0, nullptr, 0}
};
while (true) {
const int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, nullptr);
if (c == -1) {
break;
}
switch (c) {
case 'a':
m_array_format = true;
m_filename = optarg;
break;
case 'd':
m_dump = true;
break;
case 'h':
print_help();
std::exit(0);
case 'l':
m_list_format = true;
m_filename = optarg;
break;
case 's':
m_ids.push_back(std::atoll(optarg));
break;
case 't':
m_type = optarg;
if (m_type != "location" && m_type != "id" && m_type != "offset") {
std::cerr << "Unknown type '" << m_type
<< "'. Must be 'location', 'id', or 'offset'.\n";
std::exit(2);
}
break;
default:
std::exit(2);
}
}
if (m_array_format == m_list_format) {
std::cerr << "Need option --array or --list, but not both\n";
std::exit(2);
}
if (m_dump == !m_ids.empty()) {
std::cerr << "Need option --dump or --search, but not both\n";
std::exit(2);
}
if (m_type.empty()) {
std::cerr << "Need --type argument.\n";
std::exit(2);
}
}
const char* filename() const noexcept {
return m_filename.c_str();
}
bool dense_format() const noexcept {
return m_array_format;
}
bool do_dump() const noexcept {
return m_dump;
}
const std::vector<osmium::unsigned_object_id_type>& search_keys() const noexcept {
return m_ids;
}
bool type_is(const char* type) const noexcept {
return m_type == type;
}
}; // class Options
// Factory function to create the right IndexAccess-derived class.
template <typename TValue>
std::unique_ptr<IndexAccess<TValue>> create(bool dense, int fd) {
std::unique_ptr<IndexAccess<TValue>> ptr;
if (dense) {
ptr.reset(new IndexAccessDense<TValue>{fd});
} else {
ptr.reset(new IndexAccessSparse<TValue>{fd});
}
return ptr;
}
// Do the actual work: Either dump the index or search in the index.
template <typename TValue>
int run(const IndexAccess<TValue>& index, const Options& options) {
if (options.do_dump()) {
index.dump();
return 0;
} else {
return index.search(options.search_keys()) ? 0 : 1;
}
}
int main(int argc, char* argv[]) {
// Parse command line options.
Options options{argc, argv};
// Open the index file.
const int fd = ::open(options.filename(), O_RDWR);
if (fd < 0) {
std::cerr << "Can not open file '" << options.filename()
<< "': " << std::strerror(errno) << '\n';
std::exit(2);
}
#ifdef _WIN32
_setmode(fd, _O_BINARY);
#endif
try {
// Depending on the type of index, we have different implementations.
if (options.type_is("location")) {
// index id -> location
const auto index = create<osmium::Location>(options.dense_format(), fd);
return run(*index, options);
} else if (options.type_is("id")) {
// index id -> id
const auto index = create<osmium::unsigned_object_id_type>(options.dense_format(), fd);
return run(*index, options);
} else {
// index id -> offset
const auto index = create<std::size_t>(options.dense_format(), fd);
return run(*index, options);
}
} catch(const std::exception& e) {
std::cerr << "Error: " << e.what() << '\n';
std::exit(1);
}
}

View File

@ -0,0 +1,94 @@
/*
EXAMPLE osmium_location_cache_create
Reads nodes from an OSM file and writes out their locations to a cache
file. The cache file can then be read with osmium_location_cache_use.
Warning: The locations cache file will get huge (>32GB) if you are using
the DenseFileArray index even if the input file is small, because
it depends on the *largest* node ID, not the number of nodes.
DEMONSTRATES USE OF:
* file input
* location indexes and the NodeLocationsForWays handler
* location indexes on disk
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
* osmium_road_length
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cerrno> // for errno
#include <cstdlib> // for std::exit
#include <cstring> // for strerror
#include <fcntl.h> // for open
#include <iostream> // for std::cout, std::cerr
#include <string> // for std::string
#include <sys/stat.h> // for open
#include <sys/types.h> // for open
#ifdef _WIN32
# include <io.h> // for _setmode
#endif
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// For the location index. There are different types of index implementation
// available. These implementations put the index on disk. See below.
#include <osmium/index/map/sparse_file_array.hpp>
#include <osmium/index/map/dense_file_array.hpp>
// For the NodeLocationForWays handler
#include <osmium/handler/node_locations_for_ways.hpp>
// For osmium::apply()
#include <osmium/visitor.hpp>
// Chose one of these two. "sparse" is best used for small and medium extracts,
// the "dense" index for large extracts or the whole planet.
using index_type = osmium::index::map::SparseFileArray<osmium::unsigned_object_id_type, osmium::Location>;
//using index_type = osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location>;
// The location handler always depends on the index type
using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n";
std::exit(1);
}
const std::string input_filename{argv[1]};
const std::string cache_filename{argv[2]};
// Construct Reader reading only nodes
osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::node};
// Initialize location index on disk creating a new file.
const int fd = ::open(cache_filename.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1) {
std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n";
std::exit(1);
}
#ifdef _WIN32
_setmode(fd, _O_BINARY);
#endif
index_type index{fd};
// The handler that stores all node locations in the index.
location_handler_type location_handler{index};
// Feed all nodes through the location handler.
osmium::apply(reader, location_handler);
// Explicitly close input so we get notified of any errors.
reader.close();
}

View File

@ -0,0 +1,108 @@
/*
EXAMPLE osmium_location_cache_use
This reads ways from an OSM file and writes out the way node locations
it got from a location cache generated with osmium_location_cache_create.
Warning: The locations cache file will get huge (>32GB) if you are using
the DenseFileArray index even if the input file is small, because
it depends on the *largest* node ID, not the number of nodes.
DEMONSTRATES USE OF:
* file input
* location indexes and the NodeLocationsForWays handler
* location indexes on disk
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
* osmium_road_length
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cerrno> // for errno
#include <cstdlib> // for std::exit
#include <cstring> // for strerror
#include <fcntl.h> // for open
#include <iostream> // for std::cout, std::cerr
#include <string> // for std::string
#include <sys/stat.h> // for open
#include <sys/types.h> // for open
#ifdef _WIN32
# include <io.h> // for _setmode
#endif
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// For the location index. There are different types of index implementation
// available. These implementations put the index on disk. See below.
#include <osmium/index/map/dense_file_array.hpp>
#include <osmium/index/map/sparse_file_array.hpp>
// For the NodeLocationForWays handler
#include <osmium/handler/node_locations_for_ways.hpp>
// For osmium::apply()
#include <osmium/visitor.hpp>
// Chose one of these two. "sparse" is best used for small and medium extracts,
// the "dense" index for large extracts or the whole planet.
using index_type = osmium::index::map::SparseFileArray<osmium::unsigned_object_id_type, osmium::Location>;
//using index_type = osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location>;
// The location handler always depends on the index type
using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
// This handler only implements the way() function which prints out the way
// ID and all nodes IDs and locations in those ways.
struct MyHandler : public osmium::handler::Handler {
void way(const osmium::Way& way) {
std::cout << "way " << way.id() << "\n";
for (const auto& nr : way.nodes()) {
std::cout << " node " << nr.ref() << " " << nr.location() << "\n";
}
}
}; // struct MyHandler
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n";
std::exit(1);
}
const std::string input_filename{argv[1]};
const std::string cache_filename{argv[2]};
// Construct Reader reading only ways
osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::way};
// Initialize location index on disk using an existing file
const int fd = ::open(cache_filename.c_str(), O_RDWR);
if (fd == -1) {
std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n";
return 1;
}
#ifdef _WIN32
_setmode(fd, _O_BINARY);
#endif
index_type index{fd};
// The handler that adds node locations from the index to the ways.
location_handler_type location_handler{index};
// Feed all ways through the location handler and then our own handler.
MyHandler handler;
osmium::apply(reader, location_handler, handler);
// Explicitly close input so we get notified of any errors.
reader.close();
}

View File

@ -0,0 +1,89 @@
/*
EXAMPLE osmium_pub_names
Show the names and addresses of all pubs found in an OSM file.
DEMONSTRATES USE OF:
* file input
* your own handler
* access to tags
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdlib> // for std::exit
#include <cstring> // for std::strncmp
#include <iostream> // for std::cout, std::cerr
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// We want to use the handler interface
#include <osmium/handler.hpp>
// For osmium::apply()
#include <osmium/visitor.hpp>
class NamesHandler : public osmium::handler::Handler {
void output_pubs(const osmium::OSMObject& object) {
const osmium::TagList& tags = object.tags();
if (tags.has_tag("amenity", "pub")) {
// Print name of the pub if it is set.
const char* name = tags["name"];
if (name) {
std::cout << name << "\n";
} else {
std::cout << "pub with unknown name\n";
}
// Iterate over all tags finding those which start with "addr:"
// and print them.
for (const osmium::Tag& tag : tags) {
if (!std::strncmp(tag.key(), "addr:", 5)) {
std::cout << " " << tag.key() << ": " << tag.value() << "\n";
}
}
}
}
public:
// Nodes can be tagged amenity=pub.
void node(const osmium::Node& node) {
output_pubs(node);
}
// Ways can be tagged amenity=pub, too (typically buildings).
void way(const osmium::Way& way) {
output_pubs(way);
}
}; // class NamesHandler
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
std::exit(1);
}
// Construct the handler defined above
NamesHandler names_handler;
// Initialize the reader with the filename from the command line and
// tell it to only read nodes and ways. We are ignoring multipolygon
// relations in this simple example.
osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::node | osmium::osm_entity_bits::way};
// Apply input data to our own handler
osmium::apply(reader, names_handler);
}

42
examples/osmium_read.cpp Normal file
View File

@ -0,0 +1,42 @@
/*
EXAMPLE osmium_read
Reads and discards the contents of the input file.
(It can be used for timing.)
DEMONSTRATES USE OF:
* file input
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdlib> // for std::exit
#include <iostream> // for std::cerr
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
std::exit(1);
}
// The Reader is initialized here with an osmium::io::File, but could
// also be directly initialized with a file name.
osmium::io::File input_file{argv[1]};
osmium::io::Reader reader{input_file};
// OSM data comes in buffers, read until there are no more.
while (osmium::memory::Buffer buffer = reader.read()) {
// do nothing
}
// You do not have to close the Reader explicitly, but because the
// destructor can't throw, you will not see any errors otherwise.
reader.close();
}

View File

@ -0,0 +1,56 @@
/*
EXAMPLE osmium_read_with_progress
Reads the contents of the input file showing a progress bar.
DEMONSTRATES USE OF:
* file input
* ProgressBar utility function
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdlib> // for std::exit
#include <iostream> // for std::cerr
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// Get access to isatty utility function and progress bar utility class.
#include <osmium/util/file.hpp>
#include <osmium/util/progress_bar.hpp>
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
std::exit(1);
}
// The Reader is initialized here with an osmium::io::File, but could
// also be directly initialized with a file name.
osmium::io::File input_file{argv[1]};
osmium::io::Reader reader{input_file};
// Initialize progress bar, enable it only if STDERR is a TTY.
osmium::ProgressBar progress{reader.file_size(), osmium::util::isatty(2)};
// OSM data comes in buffers, read until there are no more.
while (osmium::memory::Buffer buffer = reader.read()) {
// Update progress bar for each buffer.
progress.update(reader.offset());
}
// Progress bar is done.
progress.done();
// You do not have to close the Reader explicitly, but because the
// destructor can't throw, you will not see any errors otherwise.
reader.close();
}

View File

@ -0,0 +1,92 @@
/*
EXAMPLE osmium_road_length
Calculate the length of the road network (everything tagged `highway=*`)
from the given OSM file.
DEMONSTRATES USE OF:
* file input
* location indexes and the NodeLocationsForWays handler
* length calculation on the earth using the haversine function
SIMPLER EXAMPLES you might want to understand first:
* osmium_read
* osmium_count
* osmium_pub_names
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdlib> // for std::exit
#include <iostream> // for std::cout, std::cerr
// Allow any format of input files (XML, PBF, ...)
#include <osmium/io/any_input.hpp>
// For the osmium::geom::haversine::distance() function
#include <osmium/geom/haversine.hpp>
// For osmium::apply()
#include <osmium/visitor.hpp>
// For the location index. There are different types of indexes available.
// This will work for all input files keeping the index in memory.
#include <osmium/index/map/flex_mem.hpp>
// For the NodeLocationForWays handler
#include <osmium/handler/node_locations_for_ways.hpp>
// The type of index used. This must match the include file above
using index_type = osmium::index::map::FlexMem<osmium::unsigned_object_id_type, osmium::Location>;
// The location handler always depends on the index type
using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
// This handler only implements the way() function, we are not interested in
// any other objects.
struct RoadLengthHandler : public osmium::handler::Handler {
double length = 0;
// If the way has a "highway" tag, find its length and add it to the
// overall length.
void way(const osmium::Way& way) {
const char* highway = way.tags()["highway"];
if (highway) {
length += osmium::geom::haversine::distance(way.nodes());
}
}
}; // struct RoadLengthHandler
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
std::exit(1);
}
// Initialize the reader with the filename from the command line and
// tell it to only read nodes and ways.
osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::node | osmium::osm_entity_bits::way};
// The index to hold node locations.
index_type index;
// The location handler will add the node locations to the index and then
// to the ways
location_handler_type location_handler{index};
// Our handler defined above
RoadLengthHandler road_length_handler;
// Apply input data to first the location handler and then our own handler
osmium::apply(reader, location_handler, road_length_handler);
// Output the length. The haversine function calculates it in meters,
// so we first devide by 1000 to get kilometers.
std::cout << "Length: " << road_length_handler.length / 1000 << " km\n";
}

72
examples/osmium_tiles.cpp Normal file
View File

@ -0,0 +1,72 @@
/*
EXAMPLE osmium_tiles
Convert WGS84 longitude and latitude to Mercator coordinates and tile
coordinates.
DEMONSTRATES USE OF:
* the Location and Coordinates classes
* the Mercator projection function
* the Tile class
LICENSE
The code in this example file is released into the Public Domain.
*/
#include <cstdlib> // for std::exit, std::atoi, std::atof
#include <iostream> // for std::cout, std::cerr
// The Location contains a longitude and latitude and is usually used inside
// a node to store its location in the world.
#include <osmium/osm/location.hpp>
// Needed for the Mercator projection function. Osmium supports the Mercator
// projection out of the box, or pretty much any projection using the Proj.4
// library (with the osmium::geom::Projection class).
#include <osmium/geom/mercator_projection.hpp>
// The Tile class handles tile coordinates and zoom levels.
#include <osmium/geom/tile.hpp>
int main(int argc, char* argv[]) {
if (argc != 4) {
std::cerr << "Usage: " << argv[0] << " ZOOM LON LAT\n";
std::exit(1);
}
const int zoom = std::atoi(argv[1]);
if (zoom < 0 || zoom > 30) {
std::cerr << "ERROR: Zoom must be between 0 and 30\n";
std::exit(1);
}
const double lon = std::atof(argv[2]);
const double lat = std::atof(argv[3]);
// Create location from WGS84 coordinates. In Osmium the order of
// coordinate values is always x/longitude first, then y/latitude.
const osmium::Location location{lon, lat};
std::cout << "WGS84: lon=" << lon << " lat=" << lat << "\n";
// A location can store some invalid locations, ie locations outside the
// -180 to 180 and -90 to 90 degree range. This function checks for that.
if (!location.valid()) {
std::cerr << "ERROR: Location is invalid\n";
std::exit(1);
}
// Project the coordinates using a helper function. You can also use the
// osmium::geom::MercatorProjection class.
const osmium::geom::Coordinates c = osmium::geom::lonlat_to_mercator(location);
std::cout << "Mercator: x=" << c.x << " y=" << c.y << "\n";
// Create a tile at this location. This will also internally use the
// Mercator projection and then calculate the tile coordinates.
const osmium::geom::Tile tile{uint32_t(zoom), location};
std::cout << "Tile: zoom=" << tile.z << " x=" << tile.x << " y=" << tile.y << "\n";
}

477
include/gdalcpp.hpp Normal file
View File

@ -0,0 +1,477 @@
#ifndef GDALCPP_HPP
#define GDALCPP_HPP
/*
C++11 wrapper classes for GDAL/OGR.
Version 1.1.1
https://github.com/joto/gdalcpp
Copyright 2015 Jochen Topf <jochen@topf.org>
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
#include <gdal_priv.h>
#include <gdal_version.h>
#include <ogr_api.h>
#include <ogrsf_frmts.h>
namespace gdalcpp {
#if GDAL_VERSION_MAJOR >= 2
using gdal_driver_type = GDALDriver;
using gdal_dataset_type = GDALDataset;
#else
using gdal_driver_type = OGRSFDriver;
using gdal_dataset_type = OGRDataSource;
#endif
/**
* Exception thrown for all errors in this class.
*/
class gdal_error : public std::runtime_error {
std::string m_driver;
std::string m_dataset;
std::string m_layer;
std::string m_field;
OGRErr m_error;
public:
gdal_error(const std::string& message,
OGRErr error,
const std::string& driver = "",
const std::string& dataset = "",
const std::string& layer = "",
const std::string& field = "") :
std::runtime_error(message),
m_driver(driver),
m_dataset(dataset),
m_layer(layer),
m_field(field),
m_error(error) {
}
const std::string& driver() const {
return m_driver;
}
const std::string& dataset() const {
return m_dataset;
}
const std::string& layer() const {
return m_layer;
}
const std::string& field() const {
return m_field;
}
OGRErr error() const {
return m_error;
}
}; // class gdal_error
namespace detail {
struct init_wrapper {
#if GDAL_VERSION_MAJOR >= 2
init_wrapper() { GDALAllRegister(); }
#else
init_wrapper() { OGRRegisterAll(); }
~init_wrapper() { OGRCleanupAll(); }
#endif
};
struct init_library {
init_library() {
static init_wrapper iw;
}
};
class Driver : private init_library {
gdal_driver_type* m_driver;
public:
Driver(const std::string& driver_name) :
init_library(),
#if GDAL_VERSION_MAJOR >= 2
m_driver(GetGDALDriverManager()->GetDriverByName(driver_name.c_str())) {
#else
m_driver(OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(driver_name.c_str())) {
#endif
if (!m_driver) {
throw gdal_error(std::string("unknown driver: '") + driver_name + "'", OGRERR_NONE, driver_name);
}
}
gdal_driver_type& get() const {
return *m_driver;
}
}; // struct Driver
struct Options {
std::vector<std::string> m_options;
std::unique_ptr<const char*[]> m_ptrs;
Options(const std::vector<std::string>& options) :
m_options(options),
m_ptrs(new const char*[options.size()+1]) {
std::transform(m_options.begin(), m_options.end(), m_ptrs.get(), [&](const std::string& s) {
return s.data();
});
m_ptrs[options.size()] = nullptr;
}
char** get() const {
return const_cast<char**>(m_ptrs.get());
}
}; // struct Options
} // namespace detail
class SRS {
OGRSpatialReference m_spatial_reference;
public:
SRS() :
m_spatial_reference() {
auto result = m_spatial_reference.SetWellKnownGeogCS("WGS84");
if (result != OGRERR_NONE) {
throw gdal_error(std::string("can not initialize spatial reference system WGS84"), result);
}
}
explicit SRS(int epsg) :
m_spatial_reference() {
auto result = m_spatial_reference.importFromEPSG(epsg);
if (result != OGRERR_NONE) {
throw gdal_error(std::string("can not initialize spatial reference system for EPSG:") + std::to_string(epsg), result);
}
}
explicit SRS(const char* name) :
m_spatial_reference() {
auto result = m_spatial_reference.importFromProj4(name);
if (result != OGRERR_NONE) {
throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result);
}
}
explicit SRS(const std::string& name) :
m_spatial_reference() {
auto result = m_spatial_reference.importFromProj4(name.c_str());
if (result != OGRERR_NONE) {
throw gdal_error(std::string("can not initialize spatial reference system '") + name + "'", result);
}
}
explicit SRS(const OGRSpatialReference& spatial_reference) :
m_spatial_reference(spatial_reference) {
}
OGRSpatialReference& get() {
return m_spatial_reference;
}
const OGRSpatialReference& get() const {
return m_spatial_reference;
}
}; // class SRS
class Dataset {
struct gdal_dataset_deleter {
void operator()(gdal_dataset_type* ds) {
#if GDAL_VERSION_MAJOR >= 2
GDALClose(ds);
#else
OGRDataSource::DestroyDataSource(ds);
#endif
}
}; // struct gdal_dataset_deleter
std::string m_driver_name;
std::string m_dataset_name;
detail::Options m_options;
SRS m_srs;
std::unique_ptr<gdal_dataset_type, gdal_dataset_deleter> m_dataset;
uint64_t m_edit_count = 0;
uint64_t m_max_edit_count = 0;
public:
Dataset(const std::string& driver_name, const std::string& dataset_name, const SRS& srs = SRS{}, const std::vector<std::string>& options = {}) :
m_driver_name(driver_name),
m_dataset_name(dataset_name),
m_options(options),
m_srs(srs),
#if GDAL_VERSION_MAJOR >= 2
m_dataset(detail::Driver(driver_name).get().Create(dataset_name.c_str(), 0, 0, 0, GDT_Unknown, m_options.get())) {
#else
m_dataset(detail::Driver(driver_name).get().CreateDataSource(dataset_name.c_str(), m_options.get())) {
#endif
if (!m_dataset) {
throw gdal_error(std::string("failed to create dataset '") + dataset_name + "'", OGRERR_NONE, driver_name, dataset_name);
}
}
~Dataset() {
try {
if (m_edit_count > 0) {
commit_transaction();
}
} catch (...) {
}
}
const std::string& driver_name() const {
return m_driver_name;
}
const std::string& dataset_name() const {
return m_dataset_name;
}
gdal_dataset_type& get() const {
return *m_dataset;
}
SRS& srs() {
return m_srs;
}
void exec(const char* sql) {
auto result = m_dataset->ExecuteSQL(sql, nullptr, nullptr);
if (result) {
m_dataset->ReleaseResultSet(result);
}
}
void exec(const std::string& sql) {
exec(sql.c_str());
}
Dataset& start_transaction() {
#if GDAL_VERSION_MAJOR >= 2
m_dataset->StartTransaction();
#else
OGRLayer* layer = m_dataset->GetLayer(0);
if (layer) {
layer->StartTransaction();
}
#endif
return *this;
}
Dataset& commit_transaction() {
#if GDAL_VERSION_MAJOR >= 2
m_dataset->CommitTransaction();
#else
OGRLayer* layer = m_dataset->GetLayer(0);
if (layer) {
layer->CommitTransaction();
}
#endif
m_edit_count = 0;
return *this;
}
void prepare_edit() {
if (m_max_edit_count != 0 && m_edit_count == 0) {
start_transaction();
}
}
void finalize_edit() {
if (m_max_edit_count != 0 && ++m_edit_count > m_max_edit_count) {
commit_transaction();
}
}
Dataset& enable_auto_transactions(uint64_t edits = 100000) {
m_max_edit_count = edits;
return *this;
}
Dataset& disable_auto_transactions() {
if (m_max_edit_count != 0 && m_edit_count > 0) {
commit_transaction();
}
m_max_edit_count = 0;
return *this;
}
}; // class Dataset
class Layer {
detail::Options m_options;
Dataset& m_dataset;
OGRLayer* m_layer;
public:
Layer(Dataset& dataset, const std::string& layer_name, OGRwkbGeometryType type, const std::vector<std::string>& options = {}) :
m_options(options),
m_dataset(dataset),
m_layer(dataset.get().CreateLayer(layer_name.c_str(), &dataset.srs().get(), type, m_options.get())) {
if (!m_layer) {
throw gdal_error(std::string("failed to create layer '") + layer_name + "'", OGRERR_NONE,
dataset.driver_name(), dataset.dataset_name(), layer_name);
}
}
OGRLayer& get() {
return *m_layer;
}
const OGRLayer& get() const {
return *m_layer;
}
Dataset& dataset() const {
return m_dataset;
}
const char* name() const {
return m_layer->GetName();
}
Layer& add_field(const std::string& field_name, OGRFieldType type, int width, int precision=0) {
OGRFieldDefn field(field_name.c_str(), type);
field.SetWidth(width);
field.SetPrecision(precision);
if (m_layer->CreateField(&field) != OGRERR_NONE) {
throw gdal_error(std::string("failed to create field '") + field_name + "' in layer '" + name() + "'", OGRERR_NONE,
m_dataset.driver_name(), m_dataset.dataset_name(), name(), field_name);
}
return *this;
}
void create_feature(OGRFeature* feature) {
dataset().prepare_edit();
OGRErr result = m_layer->CreateFeature(feature);
if (result != OGRERR_NONE) {
throw gdal_error(std::string("creating feature in layer '") + name() + "' failed", result, dataset().driver_name(), dataset().dataset_name());
}
dataset().finalize_edit();
}
Layer& start_transaction() {
#if GDAL_VERSION_MAJOR < 2
OGRErr result = m_layer->StartTransaction();
if (result != OGRERR_NONE) {
throw gdal_error(std::string("starting transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name());
}
#endif
return *this;
}
Layer& commit_transaction() {
#if GDAL_VERSION_MAJOR < 2
OGRErr result = m_layer->CommitTransaction();
if (result != OGRERR_NONE) {
throw gdal_error(std::string("committing transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name());
}
#endif
return *this;
}
}; // class Layer
class Feature {
struct ogr_feature_deleter {
void operator()(OGRFeature* feature) {
OGRFeature::DestroyFeature(feature);
}
}; // struct ogr_feature_deleter
Layer& m_layer;
std::unique_ptr<OGRFeature, ogr_feature_deleter> m_feature;
public:
Feature(Layer& layer, std::unique_ptr<OGRGeometry>&& geometry) :
m_layer(layer),
m_feature(OGRFeature::CreateFeature(m_layer.get().GetLayerDefn())) {
if (!m_feature) {
throw std::bad_alloc();
}
OGRErr result = m_feature->SetGeometryDirectly(geometry.release());
if (result != OGRERR_NONE) {
throw gdal_error(std::string("setting feature geometry in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name());
}
}
void add_to_layer() {
m_layer.create_feature(m_feature.get());
}
template <class T>
Feature& set_field(int n, T&& arg) {
m_feature->SetField(n, std::forward<T>(arg));
return *this;
}
template <class T>
Feature& set_field(const char* name, T&& arg) {
m_feature->SetField(name, std::forward<T>(arg));
return *this;
}
}; // class Feature
} // namespace gdalcpp
#endif // GDALCPP_HPP

View File

@ -0,0 +1,234 @@
#ifndef OSMIUM_AREA_ASSEMBLER_HPP
#define OSMIUM_AREA_ASSEMBLER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cassert>
#include <iostream>
#include <vector>
#include <osmium/area/assembler_config.hpp>
#include <osmium/area/detail/basic_assembler_with_tags.hpp>
#include <osmium/area/detail/segment_list.hpp>
#include <osmium/area/problem_reporter.hpp>
#include <osmium/area/stats.hpp>
#include <osmium/builder/osm_object_builder.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/relation.hpp>
#include <osmium/osm/tag.hpp>
#include <osmium/osm/way.hpp>
namespace osmium {
namespace area {
/**
* Assembles area objects from closed ways or multipolygon relations
* and their members.
*/
class Assembler : public detail::BasicAssemblerWithTags {
bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) {
osmium::builder::AreaBuilder builder{out_buffer};
builder.initialize_from_object(way);
const bool area_okay = create_rings();
if (area_okay || config().create_empty_areas) {
builder.add_item(way.tags());
}
if (area_okay) {
add_rings_to_area(builder);
}
if (report_ways()) {
config().problem_reporter->report_way(way);
}
return area_okay || config().create_empty_areas;
}
bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
set_num_members(members.size());
osmium::builder::AreaBuilder builder{out_buffer};
builder.initialize_from_object(relation);
const bool area_okay = create_rings();
if (area_okay || config().create_empty_areas) {
if (config().keep_type_tag) {
builder.add_item(relation.tags());
} else {
copy_tags_without_type(builder, relation.tags());
}
}
if (area_okay) {
add_rings_to_area(builder);
}
if (report_ways()) {
for (const osmium::Way* way : members) {
config().problem_reporter->report_way(*way);
}
}
return area_okay || config().create_empty_areas;
}
public:
explicit Assembler(const config_type& config) :
detail::BasicAssemblerWithTags(config) {
}
~Assembler() noexcept = default;
/**
* Assemble an area from the given way.
* The resulting area is put into the out_buffer.
*
* @returns false if there was some kind of error building the
* area, true otherwise.
*/
bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) {
if (!config().create_way_polygons) {
return true;
}
if (config().problem_reporter) {
config().problem_reporter->set_object(osmium::item_type::way, way.id());
config().problem_reporter->set_nodes(way.nodes().size());
}
// Ignore (but count) ways without segments.
if (way.nodes().size() < 2) {
++stats().short_ways;
return false;
}
if (!way.ends_have_same_id()) {
++stats().duplicate_nodes;
if (config().problem_reporter) {
config().problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location());
}
}
++stats().from_ways;
stats().invalid_locations = segment_list().extract_segments_from_way(config().problem_reporter,
stats().duplicate_nodes,
way);
if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
return false;
}
if (config().debug_level > 0) {
std::cerr << "\nAssembling way " << way.id() << " containing " << segment_list().size() << " nodes\n";
}
// Now create the Area object and add the attributes and tags
// from the way.
const bool okay = create_area(out_buffer, way);
if (okay) {
out_buffer.commit();
} else {
out_buffer.rollback();
}
if (debug()) {
std::cerr << "Done: " << stats() << "\n";
}
return okay;
}
/**
* Assemble an area from the given relation and its members.
* The resulting area is put into the out_buffer.
*
* @returns false if there was some kind of error building the
* area(s), true otherwise.
*/
bool operator()(const osmium::Relation& relation, const std::vector<const osmium::Way*>& members, osmium::memory::Buffer& out_buffer) {
if (!config().create_new_style_polygons) {
return true;
}
assert(relation.cmembers().size() >= members.size());
if (config().problem_reporter) {
config().problem_reporter->set_object(osmium::item_type::relation, relation.id());
}
if (relation.members().empty()) {
++stats().no_way_in_mp_relation;
return false;
}
++stats().from_relations;
stats().invalid_locations = segment_list().extract_segments_from_ways(config().problem_reporter,
stats().duplicate_nodes,
stats().duplicate_ways,
relation,
members);
if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
return false;
}
stats().member_ways = members.size();
if (stats().member_ways == 1) {
++stats().single_way_in_mp_relation;
}
if (config().debug_level > 0) {
std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << segment_list().size() << " nodes\n";
}
// Now create the Area object and add the attributes and tags
// from the relation.
bool okay = create_area(out_buffer, relation, members);
if (okay) {
out_buffer.commit();
} else {
out_buffer.rollback();
}
return okay;
}
}; // class Assembler
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_ASSEMBLER_HPP

View File

@ -0,0 +1,154 @@
#ifndef OSMIUM_AREA_ASSEMBLER_CONFIG_HPP
#define OSMIUM_AREA_ASSEMBLER_CONFIG_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <osmium/util/compatibility.hpp>
namespace osmium {
namespace area {
class ProblemReporter;
/**
* Configuration for osmium::area::Assembler objects. Create this
* once, set the options you want and then re-use it every time you
* create an Assembler object.
*/
struct AssemblerConfig {
/**
* Optional pointer to problem reporter.
*/
ProblemReporter* problem_reporter = nullptr;
/**
* Debug level. If this is greater than zero, debug messages will
* be printed to stderr. Available levels are 1 to 3. Note that
* level 2 and above will generate a lot of messages!
*/
int debug_level = 0;
/**
* The roles of multipolygon members are ignored when assembling
* multipolygons, because they are often missing or wrong. If this
* is set, the roles are checked after the multipolygons are built
* against what the assembly process decided where the inner and
* outer rings are. This slows down the processing, so it only
* makes sense if you want to get the problem reports.
*/
bool check_roles = false;
/**
* When the assembler can't create an area, usually because its
* geometry would be invalid, it will create an "empty" area object
* without rings. This allows you to detect where an area was
* invalid.
*
* If this is set to false, invalid areas will simply be discarded.
*/
bool create_empty_areas = true;
/**
* Create areas for (multi)polygons where the tags are on the
* relation.
*
* If this is set to false, those areas will simply be discarded.
*/
bool create_new_style_polygons = true;
/**
* Create areas for (multi)polygons where the tags are on the
* outer way(s). This is ignored by the area::Assembler which
* doesn't support old-style multipolygons any more. Use the
* area::AssemblerLegacy if you need this.
*
* If this is set to false, those areas will simply be discarded.
*/
bool create_old_style_polygons = true;
/**
* Create areas for polygons created from ways.
*
* If this is set to false, those areas will simply be discarded.
*/
bool create_way_polygons = true;
/**
* Keep the type tag from multipolygon relations on the area
* object. By default this is false, and the type tag will be
* removed.
*/
bool keep_type_tag = false;
/**
* If there is an invalid location in any of the ways needed for
* assembling the multipolygon, the assembler will normally fail.
* If this is set, the assembler will silently ignore the invalid
* locations pretending them to be not referenced from the ways.
* This will allow some areas to be built, others will now be
* incorrect. This can sometimes be useful to assemble areas
* crossing the boundary of an extract, but you will also get
* geometrically valid but wrong (multi)polygons.
*/
bool ignore_invalid_locations = false;
AssemblerConfig() noexcept = default;
/**
* Constructor
* @deprecated Use default constructor and set values afterwards.
*/
explicit AssemblerConfig(ProblemReporter* pr, bool d = false) :
problem_reporter(pr),
debug_level(d) {
}
/**
* Enable or disable debug output to stderr. This is for Osmium
* developers only.
*
* @deprecated Set debug_level directly.
*/
OSMIUM_DEPRECATED void enable_debug_output(bool d = true) {
debug_level = d;
}
}; // struct AssemblerConfig
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_ASSEMBLER_CONFIG_HPP

View File

@ -0,0 +1,382 @@
#ifndef OSMIUM_AREA_ASSEMBLER_LEGACY_HPP
#define OSMIUM_AREA_ASSEMBLER_LEGACY_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cassert>
#include <cstring>
#include <functional>
#include <iostream>
#include <iterator>
#include <set>
#include <string>
#include <map>
#include <utility>
#include <vector>
#include <osmium/area/assembler_config.hpp>
#include <osmium/area/detail/basic_assembler_with_tags.hpp>
#include <osmium/area/detail/proto_ring.hpp>
#include <osmium/area/detail/segment_list.hpp>
#include <osmium/area/problem_reporter.hpp>
#include <osmium/area/stats.hpp>
#include <osmium/builder/osm_object_builder.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/memory/collection.hpp>
#include <osmium/osm/area.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/relation.hpp>
#include <osmium/osm/tag.hpp>
#include <osmium/osm/way.hpp>
#include <osmium/tags/filter.hpp>
namespace osmium {
namespace area {
/**
* Assembles area objects from closed ways or multipolygon relations
* and their members.
*/
class AssemblerLegacy : public detail::BasicAssemblerWithTags {
void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Way& way) const {
builder.add_item(way.tags());
}
void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set<const osmium::Way*>& ways) const {
std::map<std::string, std::size_t> counter;
for (const osmium::Way* way : ways) {
for (const auto& tag : way->tags()) {
std::string kv{tag.key()};
kv.append(1, '\0');
kv.append(tag.value());
++counter[kv];
}
}
const std::size_t num_ways = ways.size();
for (const auto& t_c : counter) {
if (debug()) {
std::cerr << " tag " << t_c.first << " is used " << t_c.second << " times in " << num_ways << " ways\n";
}
if (t_c.second == num_ways) {
const std::size_t len = std::strlen(t_c.first.c_str());
tl_builder.add_tag(t_c.first.c_str(), t_c.first.c_str() + len + 1);
}
}
}
struct MPFilter : public osmium::tags::KeyFilter {
MPFilter() : osmium::tags::KeyFilter(true) {
add(false, "type");
add(false, "created_by");
add(false, "source");
add(false, "note");
add(false, "test:id");
add(false, "test:section");
}
}; // struct MPFilter
static const MPFilter& filter() noexcept {
static const MPFilter filter;
return filter;
}
void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) {
const auto count = std::count_if(relation.tags().cbegin(), relation.tags().cend(), std::cref(filter()));
if (debug()) {
std::cerr << " found " << count << " tags on relation (without ignored ones)\n";
}
if (count > 0) {
if (debug()) {
std::cerr << " use tags from relation\n";
}
if (config().keep_type_tag) {
builder.add_item(relation.tags());
} else {
copy_tags_without_type(builder, relation.tags());
}
} else {
++stats().no_tags_on_relation;
if (debug()) {
std::cerr << " use tags from outer ways\n";
}
std::set<const osmium::Way*> ways;
for (const auto& ring : rings()) {
if (ring.is_outer()) {
ring.get_ways(ways);
}
}
if (ways.size() == 1) {
if (debug()) {
std::cerr << " only one outer way\n";
}
builder.add_item((*ways.cbegin())->tags());
} else {
if (debug()) {
std::cerr << " multiple outer ways, get common tags\n";
}
osmium::builder::TagListBuilder tl_builder{builder};
add_common_tags(tl_builder, ways);
}
}
}
bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) {
osmium::builder::AreaBuilder builder{out_buffer};
builder.initialize_from_object(way);
const bool area_okay = create_rings();
if (area_okay || config().create_empty_areas) {
add_tags_to_area(builder, way);
}
if (area_okay) {
add_rings_to_area(builder);
}
if (report_ways()) {
config().problem_reporter->report_way(way);
}
return area_okay || config().create_empty_areas;
}
bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
set_num_members(members.size());
osmium::builder::AreaBuilder builder{out_buffer};
builder.initialize_from_object(relation);
const bool area_okay = create_rings();
if (area_okay || config().create_empty_areas) {
add_tags_to_area(builder, relation);
}
if (area_okay) {
add_rings_to_area(builder);
}
if (report_ways()) {
for (const osmium::Way* way : members) {
config().problem_reporter->report_way(*way);
}
}
return area_okay || config().create_empty_areas;
}
public:
explicit AssemblerLegacy(const config_type& config) :
detail::BasicAssemblerWithTags(config) {
}
~AssemblerLegacy() noexcept = default;
/**
* Assemble an area from the given way.
* The resulting area is put into the out_buffer.
*
* @returns false if there was some kind of error building the
* area, true otherwise.
*/
bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) {
if (!config().create_way_polygons) {
return true;
}
if (way.tags().has_tag("area", "no")) {
return true;
}
if (config().problem_reporter) {
config().problem_reporter->set_object(osmium::item_type::way, way.id());
config().problem_reporter->set_nodes(way.nodes().size());
}
// Ignore (but count) ways without segments.
if (way.nodes().size() < 2) {
++stats().short_ways;
return false;
}
if (!way.ends_have_same_id()) {
++stats().duplicate_nodes;
if (config().problem_reporter) {
config().problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location());
}
}
++stats().from_ways;
stats().invalid_locations = segment_list().extract_segments_from_way(config().problem_reporter,
stats().duplicate_nodes,
way);
if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
return false;
}
if (config().debug_level > 0) {
std::cerr << "\nAssembling way " << way.id() << " containing " << segment_list().size() << " nodes\n";
}
// Now create the Area object and add the attributes and tags
// from the way.
const bool okay = create_area(out_buffer, way);
if (okay) {
out_buffer.commit();
} else {
out_buffer.rollback();
}
if (debug()) {
std::cerr << "Done: " << stats() << "\n";
}
return okay;
}
/**
* Assemble an area from the given relation and its members.
* The resulting area is put into the out_buffer.
*
* @returns false if there was some kind of error building the
* area(s), true otherwise.
*/
bool operator()(const osmium::Relation& relation, const std::vector<const osmium::Way*>& members, osmium::memory::Buffer& out_buffer) {
assert(relation.members().size() >= members.size());
if (config().problem_reporter) {
config().problem_reporter->set_object(osmium::item_type::relation, relation.id());
}
if (relation.members().empty()) {
++stats().no_way_in_mp_relation;
return false;
}
++stats().from_relations;
stats().invalid_locations = segment_list().extract_segments_from_ways(config().problem_reporter,
stats().duplicate_nodes,
stats().duplicate_ways,
relation,
members);
if (!config().ignore_invalid_locations && stats().invalid_locations > 0) {
return false;
}
stats().member_ways = members.size();
if (stats().member_ways == 1) {
++stats().single_way_in_mp_relation;
}
if (config().debug_level > 0) {
std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << segment_list().size() << " nodes\n";
}
const std::size_t area_offset = out_buffer.committed();
// Now create the Area object and add the attributes and tags
// from the relation.
bool okay = create_area(out_buffer, relation, members);
if (okay) {
if ((config().create_new_style_polygons && stats().no_tags_on_relation == 0) ||
(config().create_old_style_polygons && stats().no_tags_on_relation != 0)) {
out_buffer.commit();
} else {
out_buffer.rollback();
}
} else {
out_buffer.rollback();
}
const osmium::TagList& area_tags = out_buffer.get<osmium::Area>(area_offset).tags(); // tags of the area we just built
// Find all closed ways that are inner rings and check their
// tags. If they are not the same as the tags of the area we
// just built, add them to a list and later build areas for
// them, too.
std::vector<const osmium::Way*> ways_that_should_be_areas;
if (stats().wrong_role == 0) {
detail::for_each_member(relation, members, [this, &ways_that_should_be_areas, &area_tags](const osmium::RelationMember& member, const osmium::Way& way) {
if (!std::strcmp(member.role(), "inner")) {
if (!way.nodes().empty() && way.is_closed() && way.tags().size() > 0) {
const auto d = std::count_if(way.tags().cbegin(), way.tags().cend(), std::cref(filter()));
if (d > 0) {
osmium::tags::KeyFilter::iterator way_fi_begin(std::cref(filter()), way.tags().cbegin(), way.tags().cend());
osmium::tags::KeyFilter::iterator way_fi_end(std::cref(filter()), way.tags().cend(), way.tags().cend());
osmium::tags::KeyFilter::iterator area_fi_begin(std::cref(filter()), area_tags.cbegin(), area_tags.cend());
osmium::tags::KeyFilter::iterator area_fi_end(std::cref(filter()), area_tags.cend(), area_tags.cend());
if (!std::equal(way_fi_begin, way_fi_end, area_fi_begin) || d != std::distance(area_fi_begin, area_fi_end)) {
ways_that_should_be_areas.push_back(&way);
} else {
++stats().inner_with_same_tags;
if (config().problem_reporter) {
config().problem_reporter->report_inner_with_same_tags(way);
}
}
}
}
}
});
}
if (debug()) {
std::cerr << "Done: " << stats() << "\n";
}
// Now build areas for all ways found in the last step.
for (const osmium::Way* way : ways_that_should_be_areas) {
AssemblerLegacy assembler{config()};
if (!assembler(*way, out_buffer)) {
okay = false;
}
stats() += assembler.stats();
}
return okay;
}
}; // class AssemblerLegacy
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_ASSEMBLER_LEGACY_HPP

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,93 @@
#ifndef OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_WITH_TAGS_HPP
#define OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_WITH_TAGS_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cstring>
#include <osmium/area/assembler_config.hpp>
#include <osmium/area/detail/basic_assembler.hpp>
#include <osmium/area/stats.hpp>
#include <osmium/builder/osm_object_builder.hpp>
#include <osmium/osm/tag.hpp>
namespace osmium {
namespace area {
namespace detail {
class BasicAssemblerWithTags : public detail::BasicAssembler {
protected:
bool report_ways() const noexcept {
if (!config().problem_reporter) {
return false;
}
return stats().duplicate_nodes ||
stats().duplicate_segments ||
stats().intersections ||
stats().open_rings ||
stats().short_ways ||
stats().touching_rings ||
stats().ways_in_multiple_rings ||
stats().wrong_role;
}
static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) {
osmium::builder::TagListBuilder tl_builder{builder};
for (const osmium::Tag& tag : tags) {
if (std::strcmp(tag.key(), "type")) {
tl_builder.add_tag(tag.key(), tag.value());
}
}
}
public:
using config_type = osmium::area::AssemblerConfig;
explicit BasicAssemblerWithTags(const config_type& config) :
BasicAssembler(config) {
}
}; // class BasicAssemblerWithTags
} // namespace detail
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_DETAIL_BASIC_ASSEMBLER_WITH_TAGS_HPP

View File

@ -0,0 +1,389 @@
#ifndef OSMIUM_AREA_DETAIL_NODE_REF_SEGMENT_HPP
#define OSMIUM_AREA_DETAIL_NODE_REF_SEGMENT_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <iosfwd>
#include <utility>
#include <osmium/area/detail/vector.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node_ref.hpp>
namespace osmium {
class Way;
namespace area {
/**
* @brief Namespace for Osmium internal use
*/
namespace detail {
class ProtoRing;
enum class role_type : uint8_t {
unknown = 0,
outer = 1,
inner = 2,
empty = 3
};
/**
* This helper class for the Assembler class models a segment,
* the connection between two nodes.
*
* Internally segments have their smaller coordinate at the
* beginning of the segment. Smaller, in this case, means smaller
* x coordinate, and, if they are the same, smaller y coordinate.
*/
class NodeRefSegment {
// First node in order described above.
osmium::NodeRef m_first;
// Second node in order described above.
osmium::NodeRef m_second;
// Way this segment was from.
const osmium::Way* m_way;
// The ring this segment is part of. Initially nullptr, this
// will be filled in once we know which ring the segment is in.
ProtoRing* m_ring;
// The role of this segment from the member role.
role_type m_role;
// Nodes have to be reversed to get the intended order.
bool m_reverse = false;
// We found the right direction for this segment in the ring.
// (This depends on whether it is an inner or outer ring.)
bool m_direction_done = false;
public:
NodeRefSegment() noexcept :
m_first(),
m_second(),
m_way(nullptr),
m_ring(nullptr),
m_role(role_type::unknown) {
}
NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, role_type role, const osmium::Way* way) noexcept :
m_first(nr1),
m_second(nr2),
m_way(way),
m_ring(nullptr),
m_role(role) {
if (nr2.location() < nr1.location()) {
using std::swap;
swap(m_first, m_second);
}
}
/**
* The ring this segment is a part of. nullptr if we don't
* have the ring yet.
*/
ProtoRing* ring() const noexcept {
return m_ring;
}
/**
* Returns true if the segment has already been placed in a
* ring.
*/
bool is_done() const noexcept {
return m_ring != nullptr;
}
void set_ring(ProtoRing* ring) noexcept {
assert(ring);
m_ring = ring;
}
bool is_reverse() const noexcept {
return m_reverse;
}
void reverse() noexcept {
m_reverse = !m_reverse;
}
bool is_direction_done() const noexcept {
return m_direction_done;
}
void mark_direction_done() noexcept {
m_direction_done = true;
}
void mark_direction_not_done() noexcept {
m_direction_done = false;
}
/**
* Return first NodeRef of Segment according to sorting
* order (bottom left to top right).
*/
const osmium::NodeRef& first() const noexcept {
return m_first;
}
/**
* Return second NodeRef of Segment according to sorting
* order (bottom left to top right).
*/
const osmium::NodeRef& second() const noexcept {
return m_second;
}
/**
* Return real first NodeRef of Segment.
*/
const osmium::NodeRef& start() const noexcept {
return m_reverse ? m_second : m_first;
}
/**
* Return real second NodeRef of Segment.
*/
const osmium::NodeRef& stop() const noexcept {
return m_reverse ? m_first : m_second;
}
bool role_outer() const noexcept {
return m_role == role_type::outer;
}
bool role_inner() const noexcept {
return m_role == role_type::inner;
}
bool role_empty() const noexcept {
return m_role == role_type::empty;
}
const char* role_name() const noexcept {
static const char* names[] = { "unknown", "outer", "inner", "empty" };
return names[int(m_role)];
}
const osmium::Way* way() const noexcept {
return m_way;
}
/**
* The "determinant" of this segment. Used for calculating
* the winding order or a ring.
*/
int64_t det() const noexcept {
const vec a{start()};
const vec b{stop()};
return a * b;
}
}; // class NodeRefSegment
/// NodeRefSegments are equal if both their locations are equal
inline bool operator==(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
return lhs.first().location() == rhs.first().location() &&
lhs.second().location() == rhs.second().location();
}
inline bool operator!=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
return ! (lhs == rhs);
}
/**
* A NodeRefSegment is "smaller" if the first point is to the
* left and down of the first point of the second segment.
* If both first points are the same, the segment with the higher
* slope comes first. If the slope is the same, the shorter
* segment comes first.
*/
inline bool operator<(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
if (lhs.first().location() == rhs.first().location()) {
const vec p0{lhs.first().location()};
const vec p1{lhs.second().location()};
const vec q0{rhs.first().location()};
const vec q1{rhs.second().location()};
const vec p = p1 - p0;
const vec q = q1 - q0;
if (p.x == 0 && q.x == 0) {
return p.y < q.y;
}
const auto a = p.y * q.x;
const auto b = q.y * p.x;
if (a == b) {
return p.x < q.x;
}
return a > b;
}
return lhs.first().location() < rhs.first().location();
}
template <typename TChar, typename TTraits>
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const NodeRefSegment& segment) {
return out << segment.start() << "--" << segment.stop()
<< "[" << (segment.is_reverse() ? 'R' : '_')
<< (segment.is_done() ? 'd' : '_')
<< (segment.is_direction_done() ? 'D' : '_') << "]";
}
inline bool outside_x_range(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
return s1.first().location().x() > s2.second().location().x();
}
inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
const std::pair<int32_t, int32_t> m1 = std::minmax(s1.first().location().y(), s1.second().location().y());
const std::pair<int32_t, int32_t> m2 = std::minmax(s2.first().location().y(), s2.second().location().y());
return !(m1.first > m2.second || m2.first > m1.second);
}
/**
* Calculate the intersection between two NodeRefSegments. The
* result is returned as a Location. Note that because the Location
* uses integers with limited precision internally, the result
* might be slightly different than the numerically correct
* location.
*
* This function uses integer arithmetic as much as possible and
* will not work if the segments are longer than about half the
* planet. This shouldn't happen with real data, so it isn't a big
* problem.
*
* If the segments touch in one or both of their endpoints, it
* doesn't count as an intersection.
*
* If the segments intersect not in a single point but in multiple
* points, ie if they are collinear and overlap, the smallest
* of the endpoints that is in the overlapping section is returned.
*
* @returns Undefined osmium::Location if there is no intersection
* or a defined Location if the segments intersect.
*/
inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
// See http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
// for some hints about how the algorithm works.
const vec p0{s1.first()};
const vec p1{s1.second()};
const vec q0{s2.first()};
const vec q1{s2.second()};
if ((p0 == q0 && p1 == q1) ||
(p0 == q1 && p1 == q0)) {
// segments are the same
return osmium::Location{};
}
const vec pd = p1 - p0;
const int64_t d = pd * (q1 - q0);
if (d != 0) {
// segments are not collinear
if (p0 == q0 || p0 == q1 || p1 == q0 || p1 == q1) {
// touching at an end point
return osmium::Location{};
}
// intersection in a point
const int64_t na = (q1.x - q0.x) * (p0.y - q0.y) -
(q1.y - q0.y) * (p0.x - q0.x);
const int64_t nb = (p1.x - p0.x) * (p0.y - q0.y) -
(p1.y - p0.y) * (p0.x - q0.x);
if ((d > 0 && na >= 0 && na <= d && nb >= 0 && nb <= d) ||
(d < 0 && na <= 0 && na >= d && nb <= 0 && nb >= d)) {
const double ua = double(na) / d;
const vec i = p0 + ua * (p1 - p0);
return osmium::Location{int32_t(i.x), int32_t(i.y)};
}
return osmium::Location{};
}
// segments are collinear
if (pd * (q0 - p0) == 0) {
// segments are on the same line
struct seg_loc {
int segment;
osmium::Location location;
};
seg_loc sl[4];
sl[0] = {0, s1.first().location() };
sl[1] = {0, s1.second().location()};
sl[2] = {1, s2.first().location() };
sl[3] = {1, s2.second().location()};
std::sort(sl, sl+4, [](const seg_loc& lhs, const seg_loc& rhs) {
return lhs.location < rhs.location;
});
if (sl[1].location == sl[2].location) {
return osmium::Location();
}
if (sl[0].segment != sl[1].segment) {
if (sl[0].location == sl[1].location) {
return sl[2].location;
}
return sl[1].location;
}
}
return osmium::Location{};
}
} // namespace detail
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_DETAIL_NODE_REF_SEGMENT_HPP

View File

@ -0,0 +1,243 @@
#ifndef OSMIUM_AREA_DETAIL_PROTO_RING_HPP
#define OSMIUM_AREA_DETAIL_PROTO_RING_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <iostream>
#include <set>
#include <vector>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/area/detail/node_ref_segment.hpp>
namespace osmium {
class Way;
namespace area {
namespace detail {
/**
* A ring in the process of being built by the Assembler object.
*/
class ProtoRing {
public:
using segments_type = std::vector<NodeRefSegment*>;
private:
// Segments in this ring.
segments_type m_segments;
// If this is an outer ring, these point to it's inner rings
// (if any).
std::vector<ProtoRing*> m_inner;
// The smallest segment. Will be kept current whenever a new
// segment is added to the ring.
NodeRefSegment* m_min_segment;
// If this is an inner ring, points to the outer ring.
ProtoRing* m_outer_ring;
#ifdef OSMIUM_DEBUG_RING_NO
static int64_t next_num() noexcept {
static int64_t counter = 0;
return ++counter;
}
int64_t m_num;
#endif
int64_t m_sum;
public:
explicit ProtoRing(NodeRefSegment* segment) noexcept :
m_segments(),
m_inner(),
m_min_segment(segment),
m_outer_ring(nullptr),
#ifdef OSMIUM_DEBUG_RING_NO
m_num(next_num()),
#endif
m_sum(0) {
add_segment_back(segment);
}
void add_segment_back(NodeRefSegment* segment) {
assert(segment);
if (*segment < *m_min_segment) {
m_min_segment = segment;
}
m_segments.push_back(segment);
segment->set_ring(this);
m_sum += segment->det();
}
NodeRefSegment* min_segment() const noexcept {
return m_min_segment;
}
ProtoRing* outer_ring() const noexcept {
return m_outer_ring;
}
void set_outer_ring(ProtoRing* outer_ring) noexcept {
assert(outer_ring);
assert(m_inner.empty());
m_outer_ring = outer_ring;
}
const std::vector<ProtoRing*>& inner_rings() const noexcept {
return m_inner;
}
void add_inner_ring(ProtoRing* ring) {
assert(ring);
assert(!m_outer_ring);
m_inner.push_back(ring);
}
bool is_outer() const noexcept {
return !m_outer_ring;
}
const segments_type& segments() const noexcept {
return m_segments;
}
const NodeRef& get_node_ref_start() const noexcept {
return m_segments.front()->start();
}
const NodeRef& get_node_ref_stop() const noexcept {
return m_segments.back()->stop();
}
bool closed() const noexcept {
return get_node_ref_start().location() == get_node_ref_stop().location();
}
void reverse() {
std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
segment->reverse();
});
std::reverse(m_segments.begin(), m_segments.end());
m_sum = -m_sum;
}
void mark_direction_done() {
std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
segment->mark_direction_done();
});
}
bool is_cw() const noexcept {
return m_sum <= 0;
}
int64_t sum() const noexcept {
return m_sum;
}
void fix_direction() noexcept {
if (is_cw() == is_outer()) {
reverse();
}
}
void reset() {
m_inner.clear();
m_outer_ring = nullptr;
std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
segment->mark_direction_not_done();
});
}
void get_ways(std::set<const osmium::Way*>& ways) const {
for (const auto& segment : m_segments) {
ways.insert(segment->way());
}
}
void join_forward(ProtoRing& other) {
for (NodeRefSegment* segment : other.m_segments) {
add_segment_back(segment);
}
}
void join_backward(ProtoRing& other) {
for (auto it = other.m_segments.rbegin(); it != other.m_segments.rend(); ++it) {
(*it)->reverse();
add_segment_back(*it);
}
}
void print(std::ostream& out) const {
#ifdef OSMIUM_DEBUG_RING_NO
out << "Ring #" << m_num << " [";
#else
out << "Ring [";
#endif
if (!m_segments.empty()) {
out << m_segments.front()->start().ref();
}
for (const auto& segment : m_segments) {
out << ',' << segment->stop().ref();
}
out << "]-" << (is_outer() ? "OUTER" : "INNER");
}
}; // class ProtoRing
template <typename TChar, typename TTraits>
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const ProtoRing& ring) {
ring.print(out);
return out;
}
} // namespace detail
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_DETAIL_PROTO_RING_HPP

View File

@ -0,0 +1,363 @@
#ifndef OSMIUM_AREA_DETAIL_SEGMENT_LIST_HPP
#define OSMIUM_AREA_DETAIL_SEGMENT_LIST_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <iterator>
#include <numeric>
#include <unordered_set>
#include <vector>
#include <osmium/area/detail/node_ref_segment.hpp>
#include <osmium/area/problem_reporter.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/relation.hpp>
#include <osmium/osm/types.hpp>
#include <osmium/osm/way.hpp>
namespace osmium {
namespace area {
namespace detail {
/**
* Iterate over all relation members and the vector of ways at the
* same time and call given function with the relation member and
* way as parameter. This takes into account that there might be
* non-way members in the relation.
*/
template <typename F>
inline void for_each_member(const osmium::Relation& relation, const std::vector<const osmium::Way*>& ways, F&& func) {
auto way_it = ways.cbegin();
for (const osmium::RelationMember& member : relation.members()) {
if (member.type() == osmium::item_type::way) {
assert(way_it != ways.cend());
func(member, **way_it);
++way_it;
}
}
}
/**
* This is a helper class for the area assembler. It models
* a list of segments.
*/
class SegmentList {
using slist_type = std::vector<NodeRefSegment>;
slist_type m_segments;
bool m_debug;
static role_type parse_role(const char* role) noexcept {
if (role[0] == '\0') {
return role_type::empty;
}
if (!std::strcmp(role, "outer")) {
return role_type::outer;
}
if (!std::strcmp(role, "inner")) {
return role_type::inner;
}
return role_type::unknown;
}
/**
* Calculate the number of segments in all the ways together.
*/
static std::size_t get_num_segments(const std::vector<const osmium::Way*>& members) noexcept {
return std::accumulate(members.cbegin(), members.cend(), static_cast<std::size_t>(0), [](std::size_t sum, const osmium::Way* way) {
if (way->nodes().empty()) {
return sum;
}
return sum + way->nodes().size() - 1;
});
}
uint32_t extract_segments_from_way_impl(ProblemReporter* problem_reporter, uint64_t& duplicate_nodes, const osmium::Way& way, role_type role) {
uint32_t invalid_locations = 0;
osmium::NodeRef previous_nr;
for (const osmium::NodeRef& nr : way.nodes()) {
if (!nr.location().valid()) {
++invalid_locations;
if (problem_reporter) {
problem_reporter->report_invalid_location(way.id(), nr.ref());
}
continue;
}
if (previous_nr.location()) {
if (previous_nr.location() != nr.location()) {
m_segments.emplace_back(previous_nr, nr, role, &way);
} else {
++duplicate_nodes;
if (problem_reporter) {
problem_reporter->report_duplicate_node(previous_nr.ref(), nr.ref(), nr.location());
}
}
}
previous_nr = nr;
}
return invalid_locations;
}
public:
explicit SegmentList(bool debug) noexcept :
m_segments(),
m_debug(debug) {
}
~SegmentList() noexcept = default;
SegmentList(const SegmentList&) = delete;
SegmentList(SegmentList&&) = delete;
SegmentList& operator=(const SegmentList&) = delete;
SegmentList& operator=(SegmentList&&) = delete;
/// The number of segments in the list.
std::size_t size() const noexcept {
return m_segments.size();
}
/// Is the segment list empty?
bool empty() const noexcept {
return m_segments.empty();
}
using const_iterator = slist_type::const_iterator;
using iterator = slist_type::iterator;
NodeRefSegment& front() {
return m_segments.front();
}
NodeRefSegment& back() {
return m_segments.back();
}
const NodeRefSegment& operator[](std::size_t n) const noexcept {
assert(n < m_segments.size());
return m_segments[n];
}
NodeRefSegment& operator[](std::size_t n) noexcept {
assert(n < m_segments.size());
return m_segments[n];
}
iterator begin() noexcept {
return m_segments.begin();
}
iterator end() noexcept {
return m_segments.end();
}
const_iterator begin() const noexcept {
return m_segments.begin();
}
const_iterator end() const noexcept {
return m_segments.end();
}
/**
* Enable or disable debug output to stderr. This is used
* for debugging libosmium itself.
*/
void enable_debug_output(bool debug = true) noexcept {
m_debug = debug;
}
/// Sort the list of segments.
void sort() {
std::sort(m_segments.begin(), m_segments.end());
}
/**
* Extract segments from given way and add them to the list.
*
* Segments connecting two nodes with the same location (ie
* same node or different nodes with same location) are
* removed after reporting the duplicate node.
*/
uint32_t extract_segments_from_way(ProblemReporter* problem_reporter, uint64_t& duplicate_nodes, const osmium::Way& way) {
if (way.nodes().empty()) {
return 0;
}
m_segments.reserve(way.nodes().size() - 1);
return extract_segments_from_way_impl(problem_reporter, duplicate_nodes, way, role_type::outer);
}
/**
* Extract all segments from all ways that make up this
* multipolygon relation and add them to the list.
*/
uint32_t extract_segments_from_ways(ProblemReporter* problem_reporter,
uint64_t& duplicate_nodes,
uint64_t& duplicate_ways,
const osmium::Relation& relation,
const std::vector<const osmium::Way*>& members) {
assert(relation.cmembers().size() >= members.size());
const std::size_t num_segments = get_num_segments(members);
if (problem_reporter) {
problem_reporter->set_nodes(num_segments);
}
m_segments.reserve(num_segments);
std::unordered_set<osmium::object_id_type> ids;
ids.reserve(members.size());
uint32_t invalid_locations = 0;
for_each_member(relation, members, [&](const osmium::RelationMember& member, const osmium::Way& way) {
if (ids.count(way.id()) == 0) {
ids.insert(way.id());
const auto role = parse_role(member.role());
invalid_locations += extract_segments_from_way_impl(problem_reporter, duplicate_nodes, way, role);
} else {
++duplicate_ways;
if (problem_reporter) {
problem_reporter->report_duplicate_way(way);
}
}
});
return invalid_locations;
}
/**
* Find duplicate segments (ie same start and end point) in the
* list and remove them. This will always remove pairs of the
* same segment. So if there are three, for instance, two will
* be removed and one will be left.
*/
void erase_duplicate_segments(ProblemReporter* problem_reporter, uint64_t& duplicate_segments, uint64_t& overlapping_segments) {
while (true) {
auto it = std::adjacent_find(m_segments.begin(), m_segments.end());
if (it == m_segments.end()) {
break;
}
if (m_debug) {
std::cerr << " erase duplicate segment: " << *it << "\n";
}
// Only count and report duplicate segments if they
// belong to the same way or if they don't both have
// the role "inner". Those cases are definitely wrong.
// If the duplicate segments belong to different
// "inner" ways, they could be touching inner rings
// which are perfectly okay. Note that for this check
// the role has to be correct in the member data.
if (it->way() == std::next(it)->way() || !it->role_inner() || !std::next(it)->role_inner()) {
++duplicate_segments;
if (problem_reporter) {
problem_reporter->report_duplicate_segment(it->first(), it->second());
}
}
if (it+2 != m_segments.end() && *it == *(it+2)) {
++overlapping_segments;
if (problem_reporter) {
problem_reporter->report_overlapping_segment(it->first(), it->second());
}
}
m_segments.erase(it, it+2);
}
}
/**
* Find intersection between segments.
*
* @param problem_reporter Any intersections found are
* reported to this object.
* @returns true if there are intersections.
*/
uint32_t find_intersections(ProblemReporter* problem_reporter) const {
if (m_segments.empty()) {
return 0;
}
uint32_t found_intersections = 0;
for (auto it1 = m_segments.cbegin(); it1 != m_segments.cend() - 1; ++it1) {
const NodeRefSegment& s1 = *it1;
for (auto it2 = it1+1; it2 != m_segments.end(); ++it2) {
const NodeRefSegment& s2 = *it2;
assert(s1 != s2); // erase_duplicate_segments() should have made sure of that
if (outside_x_range(s2, s1)) {
break;
}
if (y_range_overlap(s1, s2)) {
osmium::Location intersection{calculate_intersection(s1, s2)};
if (intersection) {
++found_intersections;
if (m_debug) {
std::cerr << " segments " << s1 << " and " << s2 << " intersecting at " << intersection << "\n";
}
if (problem_reporter) {
problem_reporter->report_intersection(s1.way()->id(), s1.first().location(), s1.second().location(),
s2.way()->id(), s2.first().location(), s2.second().location(), intersection);
}
}
}
}
}
return found_intersections;
}
}; // class SegmentList
} // namespace detail
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_DETAIL_SEGMENT_LIST_HPP

View File

@ -0,0 +1,121 @@
#ifndef OSMIUM_AREA_DETAIL_VECTOR_HPP
#define OSMIUM_AREA_DETAIL_VECTOR_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cstdint>
#include <iosfwd>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node_ref.hpp>
namespace osmium {
namespace area {
namespace detail {
/**
* This helper class models a 2D vector in the mathematical sense.
* It uses 64 bit integers internally which has enough precision
* for most operations with inputs based on 32 bit locations.
*/
struct vec {
int64_t x;
int64_t y;
constexpr vec(int64_t a, int64_t b) noexcept :
x(a),
y(b) {
}
constexpr explicit vec(const osmium::Location& l) noexcept :
x(l.x()),
y(l.y()) {
}
constexpr explicit vec(const osmium::NodeRef& nr) noexcept :
x(nr.x()),
y(nr.y()) {
}
}; // struct vec
// addition
constexpr inline vec operator+(const vec& lhs, const vec& rhs) noexcept {
return vec{lhs.x + rhs.x, lhs.y + rhs.y};
}
// subtraction
constexpr inline vec operator-(const vec& lhs, const vec& rhs) noexcept {
return vec{lhs.x - rhs.x, lhs.y - rhs.y};
}
// cross product
constexpr inline int64_t operator*(const vec& lhs, const vec& rhs) noexcept {
return lhs.x * rhs.y - lhs.y * rhs.x;
}
// scale vector
constexpr inline vec operator*(double s, const vec& v) noexcept {
return vec{int64_t(s * v.x), int64_t(s * v.y)};
}
// scale vector
constexpr inline vec operator*(const vec& v, double s) noexcept {
return vec{int64_t(s * v.x), int64_t(s * v.y)};
}
// equality
constexpr inline bool operator==(const vec& lhs, const vec& rhs) noexcept {
return lhs.x == rhs.x && lhs.y == rhs.y;
}
// inequality
constexpr inline bool operator!=(const vec& lhs, const vec& rhs) noexcept {
return !(lhs == rhs);
}
template <typename TChar, typename TTraits>
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const vec& v) {
return out << '(' << v.x << ',' << v.y << ')';
}
} // namespace detail
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_DETAIL_VECTOR_HPP

View File

@ -0,0 +1,132 @@
#ifndef OSMIUM_AREA_GEOM_ASSEMBLER_HPP
#define OSMIUM_AREA_GEOM_ASSEMBLER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <osmium/area/assembler_config.hpp>
#include <osmium/area/detail/basic_assembler.hpp>
#include <osmium/area/detail/segment_list.hpp>
#include <osmium/area/stats.hpp>
#include <osmium/builder/osm_object_builder.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/osm/relation.hpp>
#include <osmium/osm/way.hpp>
namespace osmium {
namespace area {
/**
* Assembles area objects from closed ways or multipolygon relations
* and their members. Unlike the Assembler, this one doesn't take
* tags into account at all. And it doesn't interpret all the config
* settings and doesn't do all the checks and error reporting the
* Assembler does.
*
* This class was developed specifically for the need of osm2pgsql.
* Unless you know what you are doing, use the Assembler class instead
* of this class. Contact the Libosmium developers if you want to use
* this class.
*/
class GeomAssembler : public detail::BasicAssembler {
public:
using config_type = osmium::area::AssemblerConfig;
explicit GeomAssembler(const config_type& config) :
detail::BasicAssembler(config) {
}
~GeomAssembler() noexcept = default;
/**
* Assemble an area from the given way.
*
* The resulting area is put into the out_buffer.
*
* @returns false if there was some kind of error building the
* area, true otherwise.
*/
bool operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) {
segment_list().extract_segments_from_way(config().problem_reporter, stats().duplicate_nodes, way);
if (!create_rings()) {
return false;
}
{
osmium::builder::AreaBuilder builder{out_buffer};
builder.initialize_from_object(way);
add_rings_to_area(builder);
}
out_buffer.commit();
return true;
}
/**
* Assemble an area from the given relation and its member ways
* which are in the ways_buffer.
*
* The resulting area is put into the out_buffer.
*
* @returns false if there was some kind of error building the
* area, true otherwise.
*/
bool operator()(const osmium::Relation& relation, const osmium::memory::Buffer& ways_buffer, osmium::memory::Buffer& out_buffer) {
for (const auto& way : ways_buffer.select<osmium::Way>()) {
segment_list().extract_segments_from_way(config().problem_reporter, stats().duplicate_nodes, way);
}
if (!create_rings()) {
return false;
}
{
osmium::builder::AreaBuilder builder{out_buffer};
builder.initialize_from_object(relation);
add_rings_to_area(builder);
}
out_buffer.commit();
return true;
}
}; // class GeomAssembler
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_GEOM_ASSEMBLER_HPP

View File

@ -0,0 +1,210 @@
#ifndef OSMIUM_AREA_MULTIPOLYGON_COLLECTOR_HPP
#define OSMIUM_AREA_MULTIPOLYGON_COLLECTOR_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <vector>
#include <osmium/area/stats.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/relation.hpp>
#include <osmium/osm/tag.hpp>
#include <osmium/osm/way.hpp>
#include <osmium/relations/collector.hpp>
namespace osmium {
namespace relations {
class RelationMeta;
} // namespace relations
/**
* @brief Code related to the building of areas (multipolygons) from relations.
*/
namespace area {
/**
* This class collects all data needed for creating areas from
* relations tagged with type=multipolygon or type=boundary.
* Most of its functionality is derived from the parent class
* osmium::relations::Collector.
*
* The actual assembling of the areas is done by the assembler
* class given as template argument.
*
* @tparam TAssembler Multipolygon Assembler class.
* @pre The Ids of all objects must be unique in the input data.
*/
template <typename TAssembler>
class MultipolygonCollector : public osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false> {
using collector_type = osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false>;
using assembler_config_type = typename TAssembler::config_type;
const assembler_config_type m_assembler_config;
osmium::memory::Buffer m_output_buffer;
area_stats m_stats;
static constexpr size_t initial_output_buffer_size = 1024 * 1024;
static constexpr size_t max_buffer_size_for_flush = 100 * 1024;
void flush_output_buffer() {
if (this->callback()) {
osmium::memory::Buffer buffer{initial_output_buffer_size};
using std::swap;
swap(buffer, m_output_buffer);
this->callback()(std::move(buffer));
}
}
void possibly_flush_output_buffer() {
if (m_output_buffer.committed() > max_buffer_size_for_flush) {
flush_output_buffer();
}
}
public:
explicit MultipolygonCollector(const assembler_config_type& assembler_config) :
collector_type(),
m_assembler_config(assembler_config),
m_output_buffer(initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes) {
}
const area_stats& stats() const noexcept {
return m_stats;
}
/**
* We are interested in all relations tagged with type=multipolygon
* or type=boundary.
*
* Overwritten from the base class.
*/
bool keep_relation(const osmium::Relation& relation) const {
const char* type = relation.tags().get_value_by_key("type");
// ignore relations without "type" tag
if (!type) {
return false;
}
return (!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"));
}
/**
* Overwritten from the base class.
*/
bool keep_member(const osmium::relations::RelationMeta& /*relation_meta*/, const osmium::RelationMember& member) const {
// We are only interested in members of type way.
return member.type() == osmium::item_type::way;
}
/**
* This is called when a way is not in any multipolygon
* relation.
*
* Overwritten from the base class.
*/
void way_not_in_any_relation(const osmium::Way& way) {
// you need at least 4 nodes to make up a polygon
if (way.nodes().size() <= 3) {
return;
}
try {
if (!way.nodes().front().location() || !way.nodes().back().location()) {
throw osmium::invalid_location{"invalid location"};
}
if (way.ends_have_same_location()) {
// way is closed and has enough nodes, build simple multipolygon
TAssembler assembler{m_assembler_config};
assembler(way, m_output_buffer);
m_stats += assembler.stats();
possibly_flush_output_buffer();
}
} catch (const osmium::invalid_location&) {
// XXX ignore
}
}
void complete_relation(osmium::relations::RelationMeta& relation_meta) {
const osmium::Relation& relation = this->get_relation(relation_meta);
const osmium::memory::Buffer& buffer = this->members_buffer();
std::vector<const osmium::Way*> ways;
ways.reserve(relation.members().size());
for (const auto& member : relation.members()) {
if (member.ref() != 0) {
const size_t offset = this->get_offset(member.type(), member.ref());
ways.push_back(&buffer.get<const osmium::Way>(offset));
}
}
try {
TAssembler assembler{m_assembler_config};
assembler(relation, ways, m_output_buffer);
m_stats += assembler.stats();
possibly_flush_output_buffer();
} catch (const osmium::invalid_location&) {
// XXX ignore
}
}
void flush() {
flush_output_buffer();
}
osmium::memory::Buffer read() {
osmium::memory::Buffer buffer{initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes};
using std::swap;
swap(buffer, m_output_buffer);
return buffer;
}
}; // class MultipolygonCollector
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_MULTIPOLYGON_COLLECTOR_HPP

View File

@ -0,0 +1,189 @@
#ifndef OSMIUM_AREA_MULTIPOLYGON_MANAGER_HPP
#define OSMIUM_AREA_MULTIPOLYGON_MANAGER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <vector>
#include <osmium/area/stats.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/relation.hpp>
#include <osmium/osm/tag.hpp>
#include <osmium/osm/way.hpp>
#include <osmium/relations/manager_util.hpp>
#include <osmium/relations/members_database.hpp>
#include <osmium/relations/relations_database.hpp>
#include <osmium/relations/relations_manager.hpp>
#include <osmium/storage/item_stash.hpp>
#include <osmium/tags/taglist.hpp>
#include <osmium/tags/tags_filter.hpp>
namespace osmium {
/**
* @brief Code related to the building of areas (multipolygons) from relations.
*/
namespace area {
/**
* This class collects all data needed for creating areas from
* relations tagged with type=multipolygon or type=boundary.
* Most of its functionality is derived from the parent class
* osmium::relations::Collector.
*
* The actual assembling of the areas is done by the assembler
* class given as template argument.
*
* @tparam TAssembler Multipolygon Assembler class.
* @pre The Ids of all objects must be unique in the input data.
*/
template <typename TAssembler>
class MultipolygonManager : public osmium::relations::RelationsManager<MultipolygonManager<TAssembler>, false, true, false> {
using assembler_config_type = typename TAssembler::config_type;
const assembler_config_type m_assembler_config;
area_stats m_stats;
osmium::TagsFilter m_filter;
public:
/**
* Construct a MultipolygonManager.
*
* @param assembler_config The configuration that will be given to
* any newly constructed area assembler.
* @param filter An optional filter specifying what tags are
* needed on closed ways or multipolygon relations
* to build the area.
*/
explicit MultipolygonManager(const assembler_config_type& assembler_config, const osmium::TagsFilter& filter = osmium::TagsFilter{true}) :
m_assembler_config(assembler_config),
m_filter(filter) {
}
/**
* Access the aggregated statistics generated by the assemblers
* called from the manager.
*/
const area_stats& stats() const noexcept {
return m_stats;
}
/**
* We are interested in all relations tagged with type=multipolygon
* or type=boundary with at least one way member.
*/
bool new_relation(const osmium::Relation& relation) const {
const char* type = relation.tags().get_value_by_key("type");
// ignore relations without "type" tag
if (type == nullptr) {
return false;
}
if (((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) && osmium::tags::match_any_of(relation.tags(), m_filter)) {
return std::any_of(relation.members().cbegin(), relation.members().cend(), [](const RelationMember& member) {
return member.type() == osmium::item_type::way;
});
}
return false;
}
/**
* This is called when a relation is complete, ie. all members
* were found in the input. It will build the area using the
* assembler.
*/
void complete_relation(const osmium::Relation& relation) {
std::vector<const osmium::Way*> ways;
ways.reserve(relation.members().size());
for (const auto& member : relation.members()) {
if (member.ref() != 0) {
ways.push_back(this->get_member_way(member.ref()));
assert(ways.back() != nullptr);
}
}
try {
TAssembler assembler{m_assembler_config};
assembler(relation, ways, this->buffer());
m_stats += assembler.stats();
} catch (const osmium::invalid_location&) {
// XXX ignore
}
}
void after_way(const osmium::Way& way) {
// you need at least 4 nodes to make up a polygon
if (way.nodes().size() <= 3) {
return;
}
try {
if (!way.nodes().front().location() || !way.nodes().back().location()) {
throw osmium::invalid_location{"invalid location"};
}
if (way.ends_have_same_location()) {
if (way.tags().has_tag("area", "no")) {
return;
}
if (osmium::tags::match_none_of(way.tags(), m_filter)) {
return;
}
TAssembler assembler{m_assembler_config};
assembler(way, this->buffer());
m_stats += assembler.stats();
this->possibly_flush();
}
} catch (const osmium::invalid_location&) {
// XXX ignore
}
}
}; // class MultipolygonManager
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_MULTIPOLYGON_MANAGER_HPP

View File

@ -0,0 +1,180 @@
#ifndef OSMIUM_AREA_MULTIPOLYGON_MANAGER_LEGACY_HPP
#define OSMIUM_AREA_MULTIPOLYGON_MANAGER_LEGACY_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <vector>
#include <osmium/area/stats.hpp>
#include <osmium/handler.hpp>
#include <osmium/handler/check_order.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/memory/callback_buffer.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/relation.hpp>
#include <osmium/osm/tag.hpp>
#include <osmium/osm/way.hpp>
#include <osmium/relations/manager_util.hpp>
#include <osmium/relations/members_database.hpp>
#include <osmium/relations/relations_database.hpp>
#include <osmium/relations/relations_manager.hpp>
#include <osmium/storage/item_stash.hpp>
namespace osmium {
/**
* @brief Code related to the building of areas (multipolygons) from relations.
*/
namespace area {
/**
* This class collects all data needed for creating areas from
* relations tagged with type=multipolygon or type=boundary.
* Most of its functionality is derived from the parent class
* osmium::relations::Collector.
*
* The actual assembling of the areas is done by the assembler
* class given as template argument.
*
* @tparam TAssembler Multipolygon Assembler class.
* @pre The Ids of all objects must be unique in the input data.
*/
template <typename TAssembler>
class MultipolygonManagerLegacy : public osmium::relations::RelationsManager<MultipolygonManagerLegacy<TAssembler>, false, true, false> {
using assembler_config_type = typename TAssembler::config_type;
const assembler_config_type m_assembler_config;
area_stats m_stats;
public:
/**
* Construct a MultipolygonManagerLegacy.
*
* @param assembler_config The configuration that will be given to
* any newly constructed area assembler.
*/
explicit MultipolygonManagerLegacy(const assembler_config_type& assembler_config) :
m_assembler_config(assembler_config) {
}
/**
* Access the aggregated statistics generated by the assemblers
* called from the manager.
*/
const area_stats& stats() const noexcept {
return m_stats;
}
/**
* We are interested in all relations tagged with type=multipolygon
* or type=boundary with at least one way member.
*/
bool new_relation(const osmium::Relation& relation) const {
const char* type = relation.tags().get_value_by_key("type");
// ignore relations without "type" tag
if (!type) {
return false;
}
if ((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) {
return std::any_of(relation.members().cbegin(), relation.members().cend(), [](const RelationMember& member) {
return member.type() == osmium::item_type::way;
});
}
return false;
}
/**
* This is called when a relation is complete, ie. all members
* were found in the input. It will build the area using the
* assembler.
*/
void complete_relation(const osmium::Relation& relation) {
std::vector<const osmium::Way*> ways;
ways.reserve(relation.members().size());
for (const auto& member : relation.members()) {
if (member.ref() != 0) {
ways.push_back(this->get_member_way(member.ref()));
assert(ways.back() != nullptr);
}
}
try {
TAssembler assembler{m_assembler_config};
assembler(relation, ways, this->buffer());
m_stats += assembler.stats();
} catch (const osmium::invalid_location&) {
// XXX ignore
}
}
/**
* This is called when a way is not in any multipolygon
* relation.
*/
void way_not_in_any_relation(const osmium::Way& way) {
// you need at least 4 nodes to make up a polygon
if (way.nodes().size() <= 3) {
return;
}
try {
if (!way.nodes().front().location() || !way.nodes().back().location()) {
throw osmium::invalid_location{"invalid location"};
}
if (way.ends_have_same_location()) {
// way is closed and has enough nodes, build simple multipolygon
TAssembler assembler{m_assembler_config};
assembler(way, this->buffer());
m_stats += assembler.stats();
}
} catch (const osmium::invalid_location&) {
// XXX ignore
}
}
}; // class MultipolygonManagerLegacy
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_MULTIPOLYGON_MANAGER_LEGACY_HPP

View File

@ -0,0 +1,238 @@
#ifndef OSMIUM_AREA_PROBLEM_REPORTER_HPP
#define OSMIUM_AREA_PROBLEM_REPORTER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cstddef>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/types.hpp>
namespace osmium {
class NodeRef;
class Way;
namespace area {
/**
* When assembling a multipolygon/area from a multipolygon relation
* or a closed way several problems can be detected. This includes
* intersections between lines, wrong role attributes on relation
* members etc. These problems are reported by the area::Assembler
* class to the ProblemReporter class or one of its child classes.
*
* This is the parent class which does nothing with the reports.
* Child classes are expected to implement different ways of
* reporting the problems.
*/
class ProblemReporter {
protected:
// Type of object we are currently working on
osmium::item_type m_object_type;
// ID of the relation/way we are currently working on
osmium::object_id_type m_object_id;
// Number of nodes in the area
size_t m_nodes;
public:
ProblemReporter() = default;
virtual ~ProblemReporter() = default;
/**
* Set the object the next problem reports will be on.
*
* @param object_type The type of the object.
* @param object_id The ID of the object.
*/
void set_object(osmium::item_type object_type, osmium::object_id_type object_id) noexcept {
m_object_type = object_type;
m_object_id = object_id;
}
void set_nodes(size_t nodes) noexcept {
m_nodes = nodes;
}
// Disable "unused-parameter" warning, so that the compiler will not complain.
// We can't remove the parameter names, because then doxygen will complain.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
/**
* Report a duplicate node, ie. two nodes with the same location.
*
* @param node_id1 ID of the first node.
* @param node_id2 ID of the second node.
* @param location Location of both nodes.
*/
virtual void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) {
}
/**
* Report a node/location where rings touch. This is often wrong,
* but not necessarily so.
*
* @param node_id ID of the node.
* @param location Location of the node.
*/
virtual void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) {
}
/**
* Report an intersection between two segments.
*
* @param way1_id ID of the first involved way.
* @param way1_seg_start Location where the segment of the first way with the intersection starts
* @param way1_seg_end Location where the segment of the first way with the intersection ends
* @param way2_id ID of the second involved way.
* @param way2_seg_start Location where the segment of the second way with the intersection starts
* @param way2_seg_end Location where the segment of the second way with the intersection ends
* @param intersection Location of the intersection. This might be slightly off the correct location due to rounding.
*/
virtual void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) {
}
/**
* Report a duplicate segments. Two or more segments are directly
* on top of each other. This can be a problem, if there is a
* spike for instance, or it could be okay, if there are touching
* inner rings.
*
* @param nr1 NodeRef of one end of the segment.
* @param nr2 NodeRef of the other end of the segment.
*/
virtual void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) {
}
/**
* Report a duplicate segments. Two or more segments are directly
* on top of each other. This can be a problem, if there is a
* spike for instance, or it could be okay, if there are touching
* inner rings.
*
* @param nr1 NodeRef of one end of the segment.
* @param nr2 NodeRef of the other end of the segment.
*/
virtual void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) {
}
/**
* Report an open ring.
*
* @param nr NodeRef of one end of the ring.
* @param way Optional pointer to way the end node is in.
*/
virtual void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) {
}
/**
* Report a segment that should have role "outer", but has a different role.
*
* @param way_id ID of the way this segment is in.
* @param seg_start Start of the segment with the wrong role.
* @param seg_end End of the segment with the wrong role.
*/
virtual void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) {
}
/**
* Report a segment that should have role "inner", but has a different role.
*
* @param way_id ID of the way this segment is in.
* @param seg_start Start of the segment with the wrong role.
* @param seg_end End of the segment with the wrong role.
*/
virtual void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) {
}
/**
* Report a way that is in multiple rings.
*
* @param way The way.
*/
virtual void report_way_in_multiple_rings(const osmium::Way& way) {
}
/**
* Report a way with role inner that has the same tags as the
* relation or outer ways.
*
* @param way The way.
*/
virtual void report_inner_with_same_tags(const osmium::Way& way) {
}
/**
* Report an invalid location in a way.
*
* @param way_id ID of the way the node is in.
* @param node_id ID of the node with the invalid location.
*/
virtual void report_invalid_location(osmium::object_id_type way_id, osmium::object_id_type node_id) {
}
/**
* Report a way that is more than once in a relation.
*
* @param way The way
*/
virtual void report_duplicate_way(const osmium::Way& way) {
}
/**
* In addition to reporting specific problems, this is used to
* report all ways belonging to areas having problems.
*
* @param way The way
*/
virtual void report_way(const osmium::Way& way) {
}
#pragma GCC diagnostic pop
}; // class ProblemReporter
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_PROBLEM_REPORTER_HPP

View File

@ -0,0 +1,141 @@
#ifndef OSMIUM_AREA_PROBLEM_REPORTER_EXCEPTION_HPP
#define OSMIUM_AREA_PROBLEM_REPORTER_EXCEPTION_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <stdexcept>
#include <osmium/area/problem_reporter_stream.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/types.hpp>
namespace osmium {
class NodeRef;
class Way;
namespace area {
class ProblemReporterException : public ProblemReporterStream {
std::stringstream m_sstream;
public:
ProblemReporterException() :
ProblemReporterStream(m_sstream) {
}
~ProblemReporterException() override = default;
void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
m_sstream.str("");
ProblemReporterStream::report_duplicate_node(node_id1, node_id2, location);
throw std::runtime_error{m_sstream.str()};
}
void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
m_sstream.str("");
ProblemReporterStream::report_touching_ring(node_id, location);
throw std::runtime_error{m_sstream.str()};
}
void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
m_sstream.str("");
ProblemReporterStream::report_intersection(way1_id, way1_seg_start, way1_seg_end, way2_id, way2_seg_start, way2_seg_end, intersection);
throw std::runtime_error{m_sstream.str()};
}
void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
m_sstream.str("");
ProblemReporterStream::report_duplicate_segment(nr1, nr2);
throw std::runtime_error{m_sstream.str()};
}
void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
m_sstream.str("");
ProblemReporterStream::report_overlapping_segment(nr1, nr2);
throw std::runtime_error{m_sstream.str()};
}
void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
m_sstream.str("");
ProblemReporterStream::report_ring_not_closed(nr, way);
throw std::runtime_error{m_sstream.str()};
}
void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
m_sstream.str("");
ProblemReporterStream::report_role_should_be_outer(way_id, seg_start, seg_end);
throw std::runtime_error{m_sstream.str()};
}
void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
m_sstream.str("");
ProblemReporterStream::report_role_should_be_inner(way_id, seg_start, seg_end);
throw std::runtime_error{m_sstream.str()};
}
void report_way_in_multiple_rings(const osmium::Way& way) override {
m_sstream.str("");
ProblemReporterStream::report_way_in_multiple_rings(way);
throw std::runtime_error{m_sstream.str()};
}
void report_inner_with_same_tags(const osmium::Way& way) override {
m_sstream.str("");
ProblemReporterStream::report_inner_with_same_tags(way);
throw std::runtime_error{m_sstream.str()};
}
void report_invalid_location(osmium::object_id_type way_id, osmium::object_id_type node_id) override {
m_sstream.str("");
ProblemReporterStream::report_invalid_location(way_id, node_id);
throw std::runtime_error{m_sstream.str()};
}
void report_duplicate_way(const osmium::Way& way) override {
m_sstream.str("");
ProblemReporterStream::report_duplicate_way(way);
throw std::runtime_error{m_sstream.str()};
}
}; // class ProblemReporterException
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_PROBLEM_REPORTER_EXCEPTION_HPP

View File

@ -0,0 +1,250 @@
#ifndef OSMIUM_AREA_PROBLEM_REPORTER_OGR_HPP
#define OSMIUM_AREA_PROBLEM_REPORTER_OGR_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/**
* @file
*
* This file contains code for reporting problems through OGR when
* assembling multipolygons.
*
* @attention If you include this file, you'll need to link with `libgdal`.
*/
#include <memory>
#include <gdalcpp.hpp>
#include <osmium/area/problem_reporter.hpp>
#include <osmium/geom/factory.hpp>
#include <osmium/geom/ogr.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/node_ref_list.hpp>
#include <osmium/osm/types.hpp>
#include <osmium/osm/way.hpp>
namespace osmium {
namespace area {
/**
* Report problems when assembling areas by adding them to
* layers in an OGR datasource.
*/
class ProblemReporterOGR : public ProblemReporter {
osmium::geom::OGRFactory<> m_ogr_factory;
gdalcpp::Layer m_layer_perror;
gdalcpp::Layer m_layer_lerror;
gdalcpp::Layer m_layer_ways;
void set_object(gdalcpp::Feature& feature) {
const char t[2] = {osmium::item_type_to_char(m_object_type), '\0'};
feature.set_field("obj_type", t);
feature.set_field("obj_id", int32_t(m_object_id));
feature.set_field("nodes", int32_t(m_nodes));
}
void write_point(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location location) {
gdalcpp::Feature feature{m_layer_perror, m_ogr_factory.create_point(location)};
set_object(feature);
feature.set_field("id1", double(id1));
feature.set_field("id2", double(id2));
feature.set_field("problem", problem_type);
feature.add_to_layer();
}
void write_line(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location loc1, osmium::Location loc2) {
auto ogr_linestring = std::unique_ptr<OGRLineString>{new OGRLineString{}};
ogr_linestring->addPoint(loc1.lon(), loc1.lat());
ogr_linestring->addPoint(loc2.lon(), loc2.lat());
gdalcpp::Feature feature{m_layer_lerror, std::move(ogr_linestring)};
set_object(feature);
feature.set_field("id1", static_cast<double>(id1));
feature.set_field("id2", static_cast<double>(id2));
feature.set_field("problem", problem_type);
feature.add_to_layer();
}
public:
explicit ProblemReporterOGR(gdalcpp::Dataset& dataset) :
m_layer_perror(dataset, "perrors", wkbPoint),
m_layer_lerror(dataset, "lerrors", wkbLineString),
m_layer_ways(dataset, "ways", wkbLineString) {
// 64bit integers are not supported in GDAL < 2, so we
// are using a workaround here in fields where we expect
// node IDs, we use real numbers.
m_layer_perror
.add_field("obj_type", OFTString, 1)
.add_field("obj_id", OFTInteger, 10)
.add_field("nodes", OFTInteger, 8)
.add_field("id1", OFTReal, 12, 1)
.add_field("id2", OFTReal, 12, 1)
.add_field("problem", OFTString, 30)
;
m_layer_lerror
.add_field("obj_type", OFTString, 1)
.add_field("obj_id", OFTInteger, 10)
.add_field("nodes", OFTInteger, 8)
.add_field("id1", OFTReal, 12, 1)
.add_field("id2", OFTReal, 12, 1)
.add_field("problem", OFTString, 30)
;
m_layer_ways
.add_field("obj_type", OFTString, 1)
.add_field("obj_id", OFTInteger, 10)
.add_field("way_id", OFTInteger, 10)
.add_field("nodes", OFTInteger, 8)
;
}
~ProblemReporterOGR() override = default;
void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
write_point("duplicate_node", node_id1, node_id2, location);
}
void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
write_point("touching_ring", node_id, 0, location);
}
void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
write_point("intersection", way1_id, way2_id, intersection);
write_line("intersection", way1_id, way2_id, way1_seg_start, way1_seg_end);
write_line("intersection", way2_id, way1_id, way2_seg_start, way2_seg_end);
}
void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
write_line("duplicate_segment", nr1.ref(), nr2.ref(), nr1.location(), nr2.location());
}
void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
write_line("overlapping_segment", nr1.ref(), nr2.ref(), nr1.location(), nr2.location());
}
void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
write_point("ring_not_closed", nr.ref(), way ? way->id() : 0, nr.location());
}
void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
write_line("role_should_be_outer", way_id, 0, seg_start, seg_end);
}
void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
write_line("role_should_be_inner", way_id, 0, seg_start, seg_end);
}
void report_way_in_multiple_rings(const osmium::Way& way) override {
if (way.nodes().size() < 2) {
return;
}
try {
gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)};
set_object(feature);
feature.set_field("id1", int32_t(way.id()));
feature.set_field("id2", 0);
feature.set_field("problem", "way_in_multiple_rings");
feature.add_to_layer();
} catch (const osmium::geometry_error&) {
// XXX
}
}
void report_inner_with_same_tags(const osmium::Way& way) override {
if (way.nodes().size() < 2) {
return;
}
try {
gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)};
set_object(feature);
feature.set_field("id1", int32_t(way.id()));
feature.set_field("id2", 0);
feature.set_field("problem", "inner_with_same_tags");
feature.add_to_layer();
} catch (const osmium::geometry_error&) {
// XXX
}
}
void report_duplicate_way(const osmium::Way& way) override {
if (way.nodes().size() < 2) {
return;
}
try {
gdalcpp::Feature feature{m_layer_lerror, m_ogr_factory.create_linestring(way)};
set_object(feature);
feature.set_field("id1", int32_t(way.id()));
feature.set_field("id2", 0);
feature.set_field("problem", "duplicate_way");
feature.add_to_layer();
} catch (const osmium::geometry_error&) {
// XXX
}
}
void report_way(const osmium::Way& way) override {
if (way.nodes().empty()) {
return;
}
if (way.nodes().size() == 1) {
const auto& first_nr = way.nodes()[0];
write_point("single_node_in_way", way.id(), first_nr.ref(), first_nr.location());
return;
}
try {
gdalcpp::Feature feature{m_layer_ways, m_ogr_factory.create_linestring(way)};
set_object(feature);
feature.set_field("way_id", int32_t(way.id()));
feature.add_to_layer();
} catch (const osmium::geometry_error&) {
// XXX
}
}
}; // class ProblemReporterOGR
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_PROBLEM_REPORTER_OGR_HPP

View File

@ -0,0 +1,139 @@
#ifndef OSMIUM_AREA_PROBLEM_REPORTER_STREAM_HPP
#define OSMIUM_AREA_PROBLEM_REPORTER_STREAM_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <ostream>
#include <osmium/area/problem_reporter.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/types.hpp>
#include <osmium/osm/way.hpp>
namespace osmium {
namespace area {
class ProblemReporterStream : public ProblemReporter {
std::ostream* m_out;
public:
explicit ProblemReporterStream(std::ostream& out) :
m_out(&out) {
}
~ProblemReporterStream() override = default;
void header(const char* msg) {
*m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << " (with " << m_nodes << " nodes): ";
}
void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
header("duplicate node");
*m_out << "node_id1=" << node_id1 << " node_id2=" << node_id2 << " location=" << location << "\n";
}
void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
header("touching ring");
*m_out << "node_id=" << node_id << " location=" << location << "\n";
}
void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
header("intersection");
*m_out << "way1_id=" << way1_id << " way1_seg_start=" << way1_seg_start << " way1_seg_end=" << way1_seg_end
<< " way2_id=" << way2_id << " way2_seg_start=" << way2_seg_start << " way2_seg_end=" << way2_seg_end << " intersection=" << intersection << "\n";
}
void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
header("duplicate segment");
*m_out << "node_id1=" << nr1.ref() << " location1=" << nr1.location()
<< " node_id2=" << nr2.ref() << " location2=" << nr2.location() << "\n";
}
void report_overlapping_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
header("overlapping segment");
*m_out << "node_id1=" << nr1.ref() << " location1=" << nr1.location()
<< " node_id2=" << nr2.ref() << " location2=" << nr2.location() << "\n";
}
void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
header("ring not closed");
*m_out << "node_id=" << nr.ref() << " location=" << nr.location();
if (way) {
*m_out << " on way " << way->id();
}
*m_out << "\n";
}
void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
header("role should be outer");
*m_out << "way_id=" << way_id << " seg_start=" << seg_start << " seg_end=" << seg_end << "\n";
}
void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
header("role should be inner");
*m_out << "way_id=" << way_id << " seg_start=" << seg_start << " seg_end=" << seg_end << "\n";
}
void report_way_in_multiple_rings(const osmium::Way& way) override {
header("way in multiple rings");
*m_out << "way_id=" << way.id() << '\n';
}
void report_inner_with_same_tags(const osmium::Way& way) override {
header("inner way with same tags as relation or outer");
*m_out << "way_id=" << way.id() << '\n';
}
void report_invalid_location(osmium::object_id_type way_id, osmium::object_id_type node_id) override {
header("invalid location");
*m_out << "way_id=" << way_id << " node_id=" << node_id << '\n';
}
void report_duplicate_way(const osmium::Way& way) override {
header("duplicate way");
*m_out << "way_id=" << way.id() << '\n';
}
}; // class ProblemReporterStream
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_PROBLEM_REPORTER_STREAM_HPP

View File

@ -0,0 +1,135 @@
#ifndef OSMIUM_AREA_STATS_HPP
#define OSMIUM_AREA_STATS_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cstdint>
#include <ostream>
namespace osmium {
namespace area {
/**
* These statistics are generated by the area assembler code. They
* tell the user of the assembler a lot about the objects this area
* is made out of, what happened during the assembly, and what errors
* there were.
*/
struct area_stats {
uint64_t area_really_complex_case = 0; ///< Most difficult case with rings touching in multiple points
uint64_t area_simple_case = 0; ///< Simple case, no touching rings
uint64_t area_touching_rings_case = 0; ///< More difficult case with touching rings
uint64_t duplicate_nodes = 0; ///< Consecutive identical nodes or consecutive nodes with same location
uint64_t duplicate_segments = 0; ///< Segments duplicated (going back and forth)
uint64_t duplicate_ways = 0; ///< Ways that are in relation more than once
uint64_t from_relations = 0; ///< Area created from multipolygon relation
uint64_t from_ways = 0; ///< Area created from way
uint64_t inner_rings = 0; ///< Number of inner rings
uint64_t inner_with_same_tags = 0; ///< Number of inner ways with same tags as area
uint64_t intersections = 0; ///< Number of intersections between segments
uint64_t member_ways = 0; ///< Number of ways in the area
uint64_t no_tags_on_relation = 0; ///< No tags on relation (old-style multipolygon with tags on outer ways)
uint64_t no_way_in_mp_relation = 0; ///< Multipolygon relation with no way members
uint64_t nodes = 0; ///< Number of nodes in the area
uint64_t open_rings = 0; ///< Number of open rings in the area
uint64_t outer_rings = 0; ///< Number of outer rings in the area
uint64_t overlapping_segments = 0; ///< Three or more segments with same end points
uint64_t short_ways = 0; ///< Number of ways with less than two nodes
uint64_t single_way_in_mp_relation = 0; ///< Multipolygon relation containing a single way
uint64_t touching_rings = 0; ///< Rings touching in a node
uint64_t ways_in_multiple_rings = 0; ///< Different segments of a way ended up in different rings
uint64_t wrong_role = 0; ///< Member has wrong role (not "outer", "inner", or empty)
uint64_t invalid_locations = 0; ///< Invalid location found
area_stats& operator+=(const area_stats& other) noexcept {
area_really_complex_case += other.area_really_complex_case;
area_simple_case += other.area_simple_case;
area_touching_rings_case += other.area_touching_rings_case;
duplicate_nodes += other.duplicate_nodes;
duplicate_segments += other.duplicate_segments;
duplicate_ways += other.duplicate_ways;
from_relations += other.from_relations;
from_ways += other.from_ways;
inner_rings += other.inner_rings;
inner_with_same_tags += other.inner_with_same_tags;
intersections += other.intersections;
member_ways += other.member_ways;
no_tags_on_relation += other.no_tags_on_relation;
no_way_in_mp_relation += other.no_way_in_mp_relation;
nodes += other.nodes;
open_rings += other.open_rings;
outer_rings += other.outer_rings;
short_ways += other.short_ways;
single_way_in_mp_relation += other.single_way_in_mp_relation;
touching_rings += other.touching_rings;
ways_in_multiple_rings += other.ways_in_multiple_rings;
wrong_role += other.wrong_role;
invalid_locations += invalid_locations;
return *this;
}
}; // struct area_stats
template <typename TChar, typename TTraits>
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const area_stats& s) {
return out << " area_really_complex_case=" << s.area_really_complex_case
<< " area_simple_case=" << s.area_simple_case
<< " area_touching_rings_case=" << s.area_touching_rings_case
<< " duplicate_nodes=" << s.duplicate_nodes
<< " duplicate_segments=" << s.duplicate_segments
<< " duplicate_ways=" << s.duplicate_ways
<< " from_relations=" << s.from_relations
<< " from_ways=" << s.from_ways
<< " inner_rings=" << s.inner_rings
<< " inner_with_same_tags=" << s.inner_with_same_tags
<< " intersections=" << s.intersections
<< " member_ways=" << s.member_ways
<< " no_tags_on_relation=" << s.no_tags_on_relation
<< " no_way_in_mp_relation=" << s.no_way_in_mp_relation
<< " nodes=" << s.nodes
<< " open_rings=" << s.open_rings
<< " outer_rings=" << s.outer_rings
<< " short_ways=" << s.short_ways
<< " single_way_in_mp_relation=" << s.single_way_in_mp_relation
<< " touching_rings=" << s.touching_rings
<< " ways_in_multiple_rings=" << s.ways_in_multiple_rings
<< " wrong_role=" << s.wrong_role
<< " invalid_locations=" << s.invalid_locations;
}
} // namespace area
} // namespace osmium
#endif // OSMIUM_AREA_STATS_HPP

View File

@ -0,0 +1,922 @@
#ifndef OSMIUM_BUILDER_ATTR_HPP
#define OSMIUM_BUILDER_ATTR_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cstddef>
#include <cstdint>
#include <ctime>
#include <initializer_list>
#include <iterator>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <osmium/builder/builder.hpp>
#include <osmium/builder/osm_object_builder.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/osm/changeset.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/object.hpp>
#include <osmium/osm/relation.hpp>
#include <osmium/osm/timestamp.hpp>
#include <osmium/osm/types.hpp>
namespace osmium {
namespace builder {
namespace detail {
#ifdef _MSC_VER
// workaround for bug in MSVC
template <typename THandler, typename... TTypes>
struct is_handled_by;
template <typename THandler>
struct is_handled_by<THandler> {
static constexpr bool value = false;
};
template <typename THandler, typename T, typename... TRest>
struct is_handled_by<THandler, T, TRest...> {
static constexpr bool value = std::is_base_of<typename T::handler, THandler>::value ||
is_handled_by<THandler, TRest...>::value;
};
template <typename THandler, typename... TTypes>
struct are_all_handled_by;
template <typename THandler, typename T>
struct are_all_handled_by<THandler, T> {
static constexpr bool value = std::is_base_of<typename T::handler, THandler>::value;
};
template <typename THandler, typename T, typename... TRest>
struct are_all_handled_by<THandler, T, TRest...> {
static constexpr bool value = std::is_base_of<typename T::handler, THandler>::value &&
are_all_handled_by<THandler, TRest...>::value;
};
#else
// True if Predicate matches for none of the types Ts
template <template<typename> class Predicate, typename... Ts>
struct static_none_of : std::is_same<std::tuple<std::false_type, typename Predicate<Ts>::type...>,
std::tuple<typename Predicate<Ts>::type..., std::false_type>>
{};
// True if Predicate matches for all of the types Ts
template <template<typename> class Predicate, typename... Ts>
struct static_all_of : std::is_same<std::tuple<std::true_type, typename Predicate<Ts>::type...>,
std::tuple<typename Predicate<Ts>::type..., std::true_type>>
{};
// True if THandler is derived from the handler for at least one of the types in TTypes
template <typename THandler, typename... TTypes>
struct is_handled_by {
template <typename T>
using HasHandler = std::is_base_of<typename T::handler, THandler>;
static constexpr bool value = !static_none_of<HasHandler, TTypes...>::value;
};
// True if THandler is derived from the handlers of all the types in TTypes
template <typename THandler, typename... TTypes>
struct are_all_handled_by {
template <typename T>
using HasHandler = std::is_base_of<typename T::handler, THandler>;
static constexpr bool value = static_all_of<HasHandler, TTypes...>::value;
};
#endif
// Wraps any type, so that we can derive from it
template <typename TType>
struct type_wrapper {
using type = TType;
TType value;
constexpr explicit type_wrapper(const TType& v) :
value(v) {
}
}; // struct type_wrapper
// Small wrapper for begin/end iterator
template <typename TType>
struct iterator_wrapper {
using type = TType;
TType first;
TType last;
constexpr iterator_wrapper(TType begin, TType end) :
first(begin),
last(end) {}
constexpr TType begin() const {
return first;
}
constexpr TType end() const {
return last;
}
}; // struct iterator_wrapper
struct entity_handler {};
struct object_handler;
struct node_handler;
struct tags_handler;
struct nodes_handler;
struct members_handler;
struct changeset_handler;
struct discussion_handler;
struct ring_handler;
} // namespace detail
#define OSMIUM_ATTRIBUTE(_handler, _name, _type) \
struct _name : public osmium::builder::detail::type_wrapper<_type> { \
using handler = osmium::builder::detail::_handler;
#define OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(_handler, _name, _type) \
OSMIUM_ATTRIBUTE(_handler, _name, _type) \
constexpr explicit _name(std::add_const<_type>::type& value) : \
type_wrapper(value) {} \
}
#define OSMIUM_ATTRIBUTE_ITER(_handler, _name) \
template <typename TIterator> \
struct _name : public osmium::builder::detail::iterator_wrapper<TIterator> { \
using handler = osmium::builder::detail::_handler; \
constexpr _name(TIterator first, TIterator last) : \
osmium::builder::detail::iterator_wrapper<TIterator>(first, last) {} \
}
namespace attr {
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(object_handler, _id, osmium::object_id_type);
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(object_handler, _version, osmium::object_version_type);
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(entity_handler, _uid, osmium::user_id_type);
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(entity_handler, _cid, osmium::changeset_id_type);
OSMIUM_ATTRIBUTE(object_handler, _deleted, bool)
constexpr explicit _deleted(bool value = true) noexcept :
type_wrapper(value) {}
};
OSMIUM_ATTRIBUTE(object_handler, _visible, bool)
constexpr explicit _visible(bool value = true) noexcept :
type_wrapper(value) {}
};
OSMIUM_ATTRIBUTE(object_handler, _timestamp, osmium::Timestamp)
constexpr explicit _timestamp(const osmium::Timestamp& value) noexcept :
type_wrapper(value) {}
constexpr explicit _timestamp(time_t value) noexcept :
type_wrapper(osmium::Timestamp{value}) {}
constexpr explicit _timestamp(uint32_t value) noexcept :
type_wrapper(osmium::Timestamp{value}) {}
explicit _timestamp(const char* value) :
type_wrapper(osmium::Timestamp{value}) {}
explicit _timestamp(const std::string& value) :
type_wrapper(osmium::Timestamp{value}) {}
};
OSMIUM_ATTRIBUTE(node_handler, _location, osmium::Location)
constexpr explicit _location(const osmium::Location& value) noexcept :
type_wrapper(value) {}
explicit _location(double lat, double lon) :
type_wrapper(osmium::Location{lat, lon}) {}
};
OSMIUM_ATTRIBUTE(entity_handler, _user, const char*)
constexpr explicit _user(const char* val) noexcept :
type_wrapper(val) {}
explicit _user(const std::string& val) noexcept :
type_wrapper(val.c_str()) {}
};
using pair_of_cstrings = std::pair<const char* const, const char* const>;
using pair_of_strings = std::pair<const std::string&, const std::string&>;
class member_type {
osmium::item_type m_type;
osmium::object_id_type m_ref;
const char* m_role;
public:
constexpr member_type(osmium::item_type type, osmium::object_id_type ref, const char* role = "") noexcept :
m_type(type),
m_ref(ref),
m_role(role) {
}
constexpr osmium::item_type type() const noexcept {
return m_type;
}
constexpr osmium::object_id_type ref() const noexcept {
return m_ref;
}
constexpr const char* role() const noexcept {
return m_role;
}
}; // class member_type
class member_type_string {
osmium::item_type m_type;
osmium::object_id_type m_ref;
std::string m_role;
public:
member_type_string(osmium::item_type type, osmium::object_id_type ref, std::string&& role) :
m_type(type),
m_ref(ref),
m_role(std::move(role)) {
}
osmium::item_type type() const noexcept {
return m_type;
}
osmium::object_id_type ref() const noexcept {
return m_ref;
}
const char* role() const noexcept {
return m_role.c_str();
}
}; // class member_type_string
class comment_type {
osmium::Timestamp m_date;
osmium::user_id_type m_uid;
const char* m_user;
const char* m_text;
public:
constexpr comment_type(osmium::Timestamp date, osmium::user_id_type uid, const char* user, const char* text) noexcept :
m_date(date),
m_uid(uid),
m_user(user),
m_text(text) {
}
constexpr osmium::Timestamp date() const noexcept {
return m_date;
}
constexpr osmium::user_id_type uid() const noexcept {
return m_uid;
}
constexpr const char* user() const noexcept {
return m_user;
}
constexpr const char* text() const noexcept {
return m_text;
}
}; // class comment_type
namespace detail {
OSMIUM_ATTRIBUTE_ITER(tags_handler, tags_from_iterator_pair);
OSMIUM_ATTRIBUTE_ITER(nodes_handler, nodes_from_iterator_pair);
OSMIUM_ATTRIBUTE_ITER(members_handler, members_from_iterator_pair);
OSMIUM_ATTRIBUTE_ITER(discussion_handler, comments_from_iterator_pair);
OSMIUM_ATTRIBUTE_ITER(ring_handler, outer_ring_from_iterator_pair);
OSMIUM_ATTRIBUTE_ITER(ring_handler, inner_ring_from_iterator_pair);
} // namespace detail
OSMIUM_ATTRIBUTE(tags_handler, _tag, pair_of_cstrings)
explicit _tag(const pair_of_cstrings& value) noexcept :
type_wrapper(value) {}
explicit _tag(const std::pair<const char* const, const char*>& value) :
type_wrapper(pair_of_cstrings{value.first, value.second}) {}
explicit _tag(const std::pair<const char*, const char* const>& value) :
type_wrapper(pair_of_cstrings{value.first, value.second}) {}
explicit _tag(const std::pair<const char*, const char*>& value) :
type_wrapper(pair_of_cstrings{value.first, value.second}) {}
explicit _tag(const pair_of_strings& value) :
type_wrapper(std::make_pair(value.first.c_str(), value.second.c_str())) {}
explicit _tag(const char* key, const char* val) :
type_wrapper(std::make_pair(key, val)) {}
explicit _tag(const std::string& key, const std::string& val) :
type_wrapper(std::make_pair(key.c_str(), val.c_str())) {}
};
template <typename TTagIterator>
inline constexpr detail::tags_from_iterator_pair<TTagIterator> _tags(TTagIterator first, TTagIterator last) {
return detail::tags_from_iterator_pair<TTagIterator>(first, last);
}
template <typename TContainer>
inline detail::tags_from_iterator_pair<typename TContainer::const_iterator> _tags(const TContainer& container) {
return detail::tags_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
}
using tag_ilist = std::initializer_list<std::pair<const char*, const char*>>;
inline detail::tags_from_iterator_pair<tag_ilist::const_iterator> _tags(const tag_ilist& container) {
return detail::tags_from_iterator_pair<tag_ilist::const_iterator>(std::begin(container), std::end(container));
}
OSMIUM_ATTRIBUTE(nodes_handler, _node, osmium::NodeRef)
constexpr explicit _node(osmium::object_id_type value) noexcept :
type_wrapper(NodeRef{value}) {}
constexpr explicit _node(const NodeRef& value) noexcept :
type_wrapper(value) {}
};
template <typename TIdIterator>
inline constexpr detail::nodes_from_iterator_pair<TIdIterator> _nodes(TIdIterator first, TIdIterator last) {
return detail::nodes_from_iterator_pair<TIdIterator>(first, last);
}
template <typename TContainer>
inline detail::nodes_from_iterator_pair<typename TContainer::const_iterator> _nodes(const TContainer& container) {
return detail::nodes_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
}
using object_id_ilist = std::initializer_list<osmium::object_id_type>;
inline detail::nodes_from_iterator_pair<object_id_ilist::const_iterator> _nodes(const object_id_ilist& container) {
return detail::nodes_from_iterator_pair<object_id_ilist::const_iterator>(std::begin(container), std::end(container));
}
using node_ref_ilist = std::initializer_list<osmium::NodeRef>;
inline detail::nodes_from_iterator_pair<node_ref_ilist::const_iterator> _nodes(const node_ref_ilist& container) {
return detail::nodes_from_iterator_pair<node_ref_ilist::const_iterator>(std::begin(container), std::end(container));
}
OSMIUM_ATTRIBUTE(members_handler, _member, member_type)
constexpr explicit _member(const member_type& value) noexcept :
type_wrapper(value) {}
constexpr explicit _member(osmium::item_type type, osmium::object_id_type id) noexcept :
type_wrapper({type, id}) {}
constexpr explicit _member(osmium::item_type type, osmium::object_id_type id, const char* role) noexcept :
type_wrapper({type, id, role}) {}
explicit _member(osmium::item_type type, osmium::object_id_type id, const std::string& role) noexcept :
type_wrapper({type, id, role.c_str()}) {}
explicit _member(const osmium::RelationMember& member) noexcept :
type_wrapper({member.type(), member.ref(), member.role()}) {}
};
template <typename TMemberIterator>
inline constexpr detail::members_from_iterator_pair<TMemberIterator> _members(TMemberIterator first, TMemberIterator last) {
return detail::members_from_iterator_pair<TMemberIterator>(first, last);
}
template <typename TContainer>
inline detail::members_from_iterator_pair<typename TContainer::const_iterator> _members(const TContainer& container) {
return detail::members_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
}
using member_ilist = std::initializer_list<member_type>;
inline detail::members_from_iterator_pair<member_ilist::const_iterator> _members(const member_ilist& container) {
return detail::members_from_iterator_pair<member_ilist::const_iterator>(std::begin(container), std::end(container));
}
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _num_changes, osmium::num_changes_type);
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _num_comments, osmium::num_comments_type);
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _created_at, osmium::Timestamp);
OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR(changeset_handler, _closed_at, osmium::Timestamp);
OSMIUM_ATTRIBUTE(discussion_handler, _comment, comment_type)
constexpr explicit _comment(const comment_type& value) noexcept :
type_wrapper(value) {}
explicit _comment(const osmium::ChangesetComment& comment) noexcept :
type_wrapper({comment.date(), comment.uid(), comment.user(), comment.text()}) {}
};
template <typename TCommentIterator>
inline constexpr detail::comments_from_iterator_pair<TCommentIterator> _comments(TCommentIterator first, TCommentIterator last) {
return detail::comments_from_iterator_pair<TCommentIterator>(first, last);
}
template <typename TContainer>
inline detail::comments_from_iterator_pair<typename TContainer::const_iterator> _comments(const TContainer& container) {
return detail::comments_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
}
using comment_ilist = std::initializer_list<comment_type>;
inline detail::comments_from_iterator_pair<comment_ilist::const_iterator> _comments(const comment_ilist& container) {
return detail::comments_from_iterator_pair<comment_ilist::const_iterator>(std::begin(container), std::end(container));
}
template <typename TIdIterator>
inline constexpr detail::outer_ring_from_iterator_pair<TIdIterator> _outer_ring(TIdIterator first, TIdIterator last) {
return detail::outer_ring_from_iterator_pair<TIdIterator>(first, last);
}
template <typename TContainer>
inline detail::outer_ring_from_iterator_pair<typename TContainer::const_iterator> _outer_ring(const TContainer& container) {
return detail::outer_ring_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
}
using object_id_ilist = std::initializer_list<osmium::object_id_type>;
inline detail::outer_ring_from_iterator_pair<object_id_ilist::const_iterator> _outer_ring(const object_id_ilist& container) {
return detail::outer_ring_from_iterator_pair<object_id_ilist::const_iterator>(std::begin(container), std::end(container));
}
using node_ref_ilist = std::initializer_list<osmium::NodeRef>;
inline detail::outer_ring_from_iterator_pair<node_ref_ilist::const_iterator> _outer_ring(const node_ref_ilist& container) {
return detail::outer_ring_from_iterator_pair<node_ref_ilist::const_iterator>(std::begin(container), std::end(container));
}
template <typename TIdIterator>
inline constexpr detail::inner_ring_from_iterator_pair<TIdIterator> _inner_ring(TIdIterator first, TIdIterator last) {
return detail::inner_ring_from_iterator_pair<TIdIterator>(first, last);
}
template <typename TContainer>
inline detail::inner_ring_from_iterator_pair<typename TContainer::const_iterator> _inner_ring(const TContainer& container) {
return detail::inner_ring_from_iterator_pair<typename TContainer::const_iterator>(std::begin(container), std::end(container));
}
using object_id_ilist = std::initializer_list<osmium::object_id_type>;
inline detail::inner_ring_from_iterator_pair<object_id_ilist::const_iterator> _inner_ring(const object_id_ilist& container) {
return detail::inner_ring_from_iterator_pair<object_id_ilist::const_iterator>(std::begin(container), std::end(container));
}
using node_ref_ilist = std::initializer_list<osmium::NodeRef>;
inline detail::inner_ring_from_iterator_pair<node_ref_ilist::const_iterator> _inner_ring(const node_ref_ilist& container) {
return detail::inner_ring_from_iterator_pair<node_ref_ilist::const_iterator>(std::begin(container), std::end(container));
}
} // namespace attr
#undef OSMIUM_ATTRIBUTE_ITER
#undef OSMIUM_ATTRIBUTE_WITH_CONSTRUCTOR
#undef OSMIUM_ATTRIBUTE
namespace detail {
struct changeset_handler : public entity_handler {
template <typename TDummy>
static void set_value(osmium::Changeset&, const TDummy&) noexcept {
}
static void set_value(osmium::Changeset& changeset, attr::_cid id) noexcept {
changeset.set_id(id.value);
}
static void set_value(osmium::Changeset& changeset, attr::_num_changes num_changes) noexcept {
changeset.set_num_changes(num_changes.value);
}
static void set_value(osmium::Changeset& changeset, attr::_num_comments num_comments) noexcept {
changeset.set_num_comments(num_comments.value);
}
static void set_value(osmium::Changeset& changeset, attr::_created_at created_at) noexcept {
changeset.set_created_at(created_at.value);
}
static void set_value(osmium::Changeset& changeset, attr::_closed_at closed_at) noexcept {
changeset.set_closed_at(closed_at.value);
}
static void set_value(osmium::Changeset& changeset, attr::_uid uid) noexcept {
changeset.set_uid(uid.value);
}
};
struct object_handler : public entity_handler {
template <typename TDummy>
static void set_value(osmium::OSMObject&, const TDummy&) noexcept {
}
static void set_value(osmium::OSMObject& object, attr::_id id) noexcept {
object.set_id(id.value);
}
static void set_value(osmium::OSMObject& object, attr::_version version) noexcept {
object.set_version(version.value);
}
static void set_value(osmium::OSMObject& object, attr::_visible visible) noexcept {
object.set_visible(visible.value);
}
static void set_value(osmium::OSMObject& object, attr::_deleted deleted) noexcept {
object.set_deleted(deleted.value);
}
static void set_value(osmium::OSMObject& object, attr::_timestamp timestamp) noexcept {
object.set_timestamp(timestamp.value);
}
static void set_value(osmium::OSMObject& object, attr::_cid changeset) noexcept {
object.set_changeset(changeset.value);
}
static void set_value(osmium::OSMObject& object, attr::_uid uid) noexcept {
object.set_uid(uid.value);
}
}; // object_handler
struct node_handler : public object_handler {
using object_handler::set_value;
static void set_value(osmium::Node& node, attr::_location location) noexcept {
node.set_location(location.value);
}
}; // node_handler
template <typename THandler, typename TBuilder, typename... TArgs>
inline void add_basic(TBuilder& builder, const TArgs&... args) noexcept {
(void)std::initializer_list<int>{
(THandler::set_value(builder.object(), args), 0)...
};
}
// ==============================================================
template <typename... TArgs>
inline constexpr const char* get_user(const attr::_user& user, const TArgs&...) noexcept {
return user.value;
}
inline constexpr const char* get_user() noexcept {
return "";
}
template <typename TFirst, typename... TRest>
inline constexpr typename std::enable_if<!std::is_same<attr::_user, TFirst>::value, const char*>::type
get_user(const TFirst&, const TRest&... args) noexcept {
return get_user(args...);
}
template <typename TBuilder, typename... TArgs>
inline void add_user(TBuilder& builder, const TArgs&... args) {
builder.set_user(get_user(args...));
}
// ==============================================================
struct tags_handler {
template <typename TDummy>
static void set_value(TagListBuilder&, const TDummy&) noexcept {
}
static void set_value(TagListBuilder& builder, const attr::_tag& tag) {
builder.add_tag(tag.value);
}
template <typename TIterator>
static void set_value(TagListBuilder& builder, const attr::detail::tags_from_iterator_pair<TIterator>& tags) {
for (const auto& tag : tags) {
builder.add_tag(tag);
}
}
}; // struct tags_handler
struct nodes_handler {
template <typename TDummy>
static void set_value(WayNodeListBuilder&, const TDummy&) noexcept {
}
static void set_value(WayNodeListBuilder& builder, const attr::_node& node_ref) {
builder.add_node_ref(node_ref.value);
}
template <typename TIterator>
static void set_value(WayNodeListBuilder& builder, const attr::detail::nodes_from_iterator_pair<TIterator>& nodes) {
for (const auto& ref : nodes) {
builder.add_node_ref(ref);
}
}
}; // struct nodes_handler
struct members_handler {
template <typename TDummy>
static void set_value(RelationMemberListBuilder&, const TDummy&) noexcept {
}
static void set_value(RelationMemberListBuilder& builder, const attr::_member& member) {
builder.add_member(member.value.type(), member.value.ref(), member.value.role());
}
template <typename TIterator>
static void set_value(RelationMemberListBuilder& builder, const attr::detail::members_from_iterator_pair<TIterator>& members) {
for (const auto& member : members) {
builder.add_member(member.type(), member.ref(), member.role());
}
}
}; // struct members_handler
struct discussion_handler {
template <typename TDummy>
static void set_value(ChangesetDiscussionBuilder&, const TDummy&) noexcept {
}
static void set_value(ChangesetDiscussionBuilder& builder, const attr::_comment& comment) {
builder.add_comment(comment.value.date(), comment.value.uid(), comment.value.user());
builder.add_comment_text(comment.value.text());
}
template <typename TIterator>
static void set_value(ChangesetDiscussionBuilder& builder, const attr::detail::comments_from_iterator_pair<TIterator>& comments) {
for (const auto& comment : comments) {
builder.add_comment(comment.date(), comment.uid(), comment.user());
builder.add_comment_text(comment.text());
}
}
}; // struct discussion_handler
struct ring_handler {
template <typename TDummy>
static void set_value(AreaBuilder&, const TDummy&) noexcept {
}
template <typename TIterator>
static void set_value(AreaBuilder& parent, const attr::detail::outer_ring_from_iterator_pair<TIterator>& nodes) {
OuterRingBuilder builder(parent.buffer(), &parent);
for (const auto& ref : nodes) {
builder.add_node_ref(ref);
}
}
template <typename TIterator>
static void set_value(AreaBuilder& parent, const attr::detail::inner_ring_from_iterator_pair<TIterator>& nodes) {
InnerRingBuilder builder(parent.buffer(), &parent);
for (const auto& ref : nodes) {
builder.add_node_ref(ref);
}
}
}; // struct ring_handler
// ==============================================================
template <typename TBuilder, typename THandler, typename... TArgs>
inline typename std::enable_if<!is_handled_by<THandler, TArgs...>::value>::type
add_list(osmium::builder::Builder&, const TArgs&...) noexcept {
}
template <typename TBuilder, typename THandler, typename... TArgs>
inline typename std::enable_if<is_handled_by<THandler, TArgs...>::value>::type
add_list(osmium::builder::Builder& parent, const TArgs&... args) {
TBuilder builder(parent.buffer(), &parent);
(void)std::initializer_list<int>{
(THandler::set_value(builder, args), 0)...
};
}
struct any_node_handlers : public node_handler, public tags_handler {};
struct any_way_handlers : public object_handler, public tags_handler, public nodes_handler {};
struct any_relation_handlers : public object_handler, public tags_handler, public members_handler {};
struct any_area_handlers : public object_handler, public tags_handler, public ring_handler {};
struct any_changeset_handlers : public changeset_handler, public tags_handler, public discussion_handler {};
} // namespace detail
/**
* Create a node using the given arguments and add it to the given buffer.
*
* @param buffer The buffer to which the node will be added.
* @param args The attributes of the node.
* @returns The position in the buffer where this node was added.
*/
template <typename... TArgs>
inline size_t add_node(osmium::memory::Buffer& buffer, const TArgs&... args) {
static_assert(sizeof...(args) > 0, "add_node() must have buffer and at least one additional argument");
static_assert(detail::are_all_handled_by<detail::any_node_handlers, TArgs...>::value, "Attribute not allowed in add_node()");
{
NodeBuilder builder(buffer);
detail::add_basic<detail::node_handler>(builder, args...);
detail::add_user(builder, args...);
detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
}
return buffer.commit();
}
/**
* Create a way using the given arguments and add it to the given buffer.
*
* @param buffer The buffer to which the way will be added.
* @param args The attributes of the way.
* @returns The position in the buffer where this way was added.
*/
template <typename... TArgs>
inline size_t add_way(osmium::memory::Buffer& buffer, const TArgs&... args) {
static_assert(sizeof...(args) > 0, "add_way() must have buffer and at least one additional argument");
static_assert(detail::are_all_handled_by<detail::any_way_handlers, TArgs...>::value, "Attribute not allowed in add_way()");
{
WayBuilder builder(buffer);
detail::add_basic<detail::object_handler>(builder, args...);
detail::add_user(builder, args...);
detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
detail::add_list<WayNodeListBuilder, detail::nodes_handler>(builder, args...);
}
return buffer.commit();
}
/**
* Create a relation using the given arguments and add it to the given buffer.
*
* @param buffer The buffer to which the relation will be added.
* @param args The attributes of the relation.
* @returns The position in the buffer where this relation was added.
*/
template <typename... TArgs>
inline size_t add_relation(osmium::memory::Buffer& buffer, const TArgs&... args) {
static_assert(sizeof...(args) > 0, "add_relation() must have buffer and at least one additional argument");
static_assert(detail::are_all_handled_by<detail::any_relation_handlers, TArgs...>::value, "Attribute not allowed in add_relation()");
{
RelationBuilder builder(buffer);
detail::add_basic<detail::object_handler>(builder, args...);
detail::add_user(builder, args...);
detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
detail::add_list<RelationMemberListBuilder, detail::members_handler>(builder, args...);
}
return buffer.commit();
}
/**
* Create a changeset using the given arguments and add it to the given buffer.
*
* @param buffer The buffer to which the changeset will be added.
* @param args The attributes of the changeset.
* @returns The position in the buffer where this changeset was added.
*/
template <typename... TArgs>
inline size_t add_changeset(osmium::memory::Buffer& buffer, const TArgs&... args) {
static_assert(sizeof...(args) > 0, "add_changeset() must have buffer and at least one additional argument");
static_assert(detail::are_all_handled_by<detail::any_changeset_handlers, TArgs...>::value, "Attribute not allowed in add_changeset()");
{
ChangesetBuilder builder(buffer);
detail::add_basic<detail::changeset_handler>(builder, args...);
detail::add_user(builder, args...);
detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
detail::add_list<ChangesetDiscussionBuilder, detail::discussion_handler>(builder, args...);
}
return buffer.commit();
}
/**
* Create a area using the given arguments and add it to the given buffer.
*
* @param buffer The buffer to which the area will be added.
* @param args The attributes of the area.
* @returns The position in the buffer where this area was added.
*/
template <typename... TArgs>
inline size_t add_area(osmium::memory::Buffer& buffer, const TArgs&... args) {
static_assert(sizeof...(args) > 0, "add_area() must have buffer and at least one additional argument");
static_assert(detail::are_all_handled_by<detail::any_area_handlers, TArgs...>::value, "Attribute not allowed in add_area()");
{
AreaBuilder builder(buffer);
detail::add_basic<detail::object_handler>(builder, args...);
detail::add_user(builder, args...);
detail::add_list<TagListBuilder, detail::tags_handler>(builder, args...);
(void)std::initializer_list<int>{
(detail::ring_handler::set_value(builder, args), 0)...
};
}
return buffer.commit();
}
/**
* Create a WayNodeList using the given arguments and add it to the given buffer.
*
* @param buffer The buffer to which the list will be added.
* @param args The contents of the list.
* @returns The position in the buffer where this list was added.
*/
template <typename... TArgs>
inline size_t add_way_node_list(osmium::memory::Buffer& buffer, const TArgs&... args) {
static_assert(sizeof...(args) > 0, "add_way_node_list() must have buffer and at least one additional argument");
static_assert(detail::are_all_handled_by<detail::nodes_handler, TArgs...>::value, "Attribute not allowed in add_way_node_list()");
{
WayNodeListBuilder builder(buffer);
(void)std::initializer_list<int>{
(detail::nodes_handler::set_value(builder, args), 0)...
};
}
return buffer.commit();
}
/**
* Create a TagList using the given arguments and add it to the given buffer.
*
* @param buffer The buffer to which the list will be added.
* @param args The contents of the list.
* @returns The position in the buffer where this list was added.
*/
template <typename... TArgs>
inline size_t add_tag_list(osmium::memory::Buffer& buffer, const TArgs&... args) {
static_assert(sizeof...(args) > 0, "add_tag_list() must have buffer and at least one additional argument");
static_assert(detail::are_all_handled_by<detail::tags_handler, TArgs...>::value, "Attribute not allowed in add_tag_list()");
{
TagListBuilder builder(buffer);
(void)std::initializer_list<int>{
(detail::tags_handler::set_value(builder, args), 0)...
};
}
return buffer.commit();
}
} // namespace builder
} // namespace osmium
#endif // OSMIUM_BUILDER_ATTR_HPP

View File

@ -0,0 +1,235 @@
#ifndef OSMIUM_BUILDER_BUILDER_HPP
#define OSMIUM_BUILDER_BUILDER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <osmium/memory/buffer.hpp>
#include <osmium/memory/item.hpp>
#include <osmium/util/compatibility.hpp>
namespace osmium {
/**
* @brief Classes for building OSM objects and other items in buffers
*/
namespace builder {
/**
* Parent class for individual builder classes. Instantiate one of
* its derived classes.
*/
class Builder {
osmium::memory::Buffer& m_buffer;
Builder* m_parent;
std::size_t m_item_offset;
Builder(const Builder&) = delete;
Builder(Builder&&) = delete;
Builder& operator=(const Builder&) = delete;
Builder& operator=(Builder&&) = delete;
protected:
explicit Builder(osmium::memory::Buffer& buffer, Builder* parent, osmium::memory::item_size_type size) :
m_buffer(buffer),
m_parent(parent),
m_item_offset(buffer.written()) {
reserve_space(size);
assert(buffer.is_aligned());
if (m_parent) {
assert(m_buffer.builder_count() == 1 && "Only one sub-builder can be open at any time.");
m_parent->add_size(size);
} else {
assert(m_buffer.builder_count() == 0 && "Only one builder can be open at any time.");
}
#ifndef NDEBUG
m_buffer.increment_builder_count();
#endif
}
#ifdef NDEBUG
~Builder() = default;
#else
~Builder() noexcept {
m_buffer.decrement_builder_count();
}
#endif
osmium::memory::Item& item() const {
return *reinterpret_cast<osmium::memory::Item*>(m_buffer.data() + m_item_offset);
}
unsigned char* reserve_space(std::size_t size) {
return m_buffer.reserve_space(size);
}
/**
* Add padding to buffer (if needed) to align data properly.
*
* This calculates how many padding bytes are needed and adds
* as many zero bytes to the buffer. It also adds this number
* to the size of the current item (if the "self" param is
* true) and recursively to all the parent items.
*
* @param self If true add number of padding bytes to size
* of current item. Size is always added to
* parent item (if any).
*
*/
void add_padding(bool self = false) {
// We know the padding is only a very small number, so it will
// always fit.
const auto padding = static_cast<osmium::memory::item_size_type>(osmium::memory::align_bytes - (size() % osmium::memory::align_bytes));
if (padding != osmium::memory::align_bytes) {
std::fill_n(reserve_space(padding), padding, 0);
if (self) {
add_size(padding);
} else if (m_parent) {
m_parent->add_size(padding);
assert(m_parent->size() % osmium::memory::align_bytes == 0);
}
}
}
void add_size(osmium::memory::item_size_type size) {
item().add_size(size);
if (m_parent) {
m_parent->add_size(size);
}
}
uint32_t size() const noexcept {
return item().byte_size();
}
/**
* Reserve space for an object of class T in buffer and return
* pointer to it.
*/
template <typename T>
T* reserve_space_for() {
assert(m_buffer.is_aligned());
return reinterpret_cast<T*>(reserve_space(sizeof(T)));
}
/**
* Append data to buffer.
*
* @param data Pointer to data.
* @param length Length of data in bytes. If data is a
* \0-terminated string, length must contain the
* \0 byte.
* @returns The number of bytes appended (length).
*/
osmium::memory::item_size_type append(const char* data, const osmium::memory::item_size_type length) {
unsigned char* target = reserve_space(length);
std::copy_n(reinterpret_cast<const unsigned char*>(data), length, target);
return length;
}
/**
* Append data to buffer and append an additional \0.
*
* @param data Pointer to data.
* @param length Length of data in bytes.
* @returns The number of bytes appended (length + 1).
*/
osmium::memory::item_size_type append_with_zero(const char* data, const osmium::memory::item_size_type length) {
unsigned char* target = reserve_space(length + 1);
std::copy_n(reinterpret_cast<const unsigned char*>(data), length, target);
target[length] = '\0';
return length + 1;
}
/**
* Append \0-terminated string to buffer.
*
* @param str \0-terminated string.
* @returns The number of bytes appended (strlen(str) + 1).
*/
osmium::memory::item_size_type append(const char* str) {
return append(str, static_cast<osmium::memory::item_size_type>(std::strlen(str) + 1));
}
/**
* Append '\0' to the buffer.
*
* @deprecated Use append_with_zero() instead.
*
* @returns The number of bytes appended (always 1).
*/
OSMIUM_DEPRECATED osmium::memory::item_size_type append_zero() {
*reserve_space(1) = '\0';
return 1;
}
public:
/// Return the buffer this builder is using.
osmium::memory::Buffer& buffer() noexcept {
return m_buffer;
}
/**
* Add a subitem to the object being built. This can be something
* like a TagList or RelationMemberList.
*/
void add_item(const osmium::memory::Item& item) {
m_buffer.add_item(item);
add_size(item.padded_size());
}
/**
* @deprecated Use the version of add_item() taking a
* reference instead.
*/
OSMIUM_DEPRECATED void add_item(const osmium::memory::Item* item) {
assert(item);
add_item(*item);
}
}; // class Builder
} // namespace builder
} // namespace osmium
#endif // OSMIUM_BUILDER_BUILDER_HPP

View File

@ -0,0 +1,120 @@
#ifndef OSMIUM_BUILDER_BUILDER_HELPER_HPP
#define OSMIUM_BUILDER_BUILDER_HELPER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cstddef>
#include <initializer_list>
#include <functional>
#include <map>
#include <utility>
#include <osmium/builder/osm_object_builder.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/util/compatibility.hpp>
namespace osmium {
class NodeRef;
class TagList;
class WayNodeList;
namespace builder {
/**
* @deprecated
* Use osmium::builder::add_way_node_list() instead.
*/
OSMIUM_DEPRECATED inline const osmium::WayNodeList& build_way_node_list(osmium::memory::Buffer& buffer, const std::initializer_list<osmium::NodeRef>& nodes) {
const size_t pos = buffer.committed();
{
osmium::builder::WayNodeListBuilder wnl_builder(buffer);
for (const auto& node_ref : nodes) {
wnl_builder.add_node_ref(node_ref);
}
}
buffer.commit();
return buffer.get<const osmium::WayNodeList>(pos);
}
/**
* @deprecated
* Use osmium::builder::add_tag_list() instead.
*/
OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list(osmium::memory::Buffer& buffer, const std::initializer_list<std::pair<const char*, const char*>>& tags) {
const size_t pos = buffer.committed();
{
osmium::builder::TagListBuilder tl_builder(buffer);
for (const auto& p : tags) {
tl_builder.add_tag(p.first, p.second);
}
}
buffer.commit();
return buffer.get<const osmium::TagList>(pos);
}
/**
* @deprecated
* Use osmium::builder::add_tag_list() instead.
*/
OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list_from_map(osmium::memory::Buffer& buffer, const std::map<const char*, const char*>& tags) {
const size_t pos = buffer.committed();
{
osmium::builder::TagListBuilder tl_builder(buffer);
for (const auto& p : tags) {
tl_builder.add_tag(p.first, p.second);
}
}
buffer.commit();
return buffer.get<const osmium::TagList>(pos);
}
/**
* @deprecated
* Use osmium::builder::add_tag_list() instead.
*/
OSMIUM_DEPRECATED inline const osmium::TagList& build_tag_list_from_func(osmium::memory::Buffer& buffer, std::function<void(osmium::builder::TagListBuilder&)> func) {
const size_t pos = buffer.committed();
{
osmium::builder::TagListBuilder tl_builder(buffer);
func(tl_builder);
}
buffer.commit();
return buffer.get<const osmium::TagList>(pos);
}
} // namespace builder
} // namespace osmium
#endif // OSMIUM_BUILDER_BUILDER_HELPER_HPP

View File

@ -0,0 +1,680 @@
#ifndef OSMIUM_BUILDER_OSM_OBJECT_BUILDER_HPP
#define OSMIUM_BUILDER_OSM_OBJECT_BUILDER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <initializer_list>
#include <limits>
#include <new>
#include <stdexcept>
#include <string>
#include <utility>
#include <osmium/builder/builder.hpp>
#include <osmium/memory/item.hpp>
#include <osmium/osm/area.hpp>
#include <osmium/osm/box.hpp>
#include <osmium/osm/changeset.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/object.hpp>
#include <osmium/osm/relation.hpp>
#include <osmium/osm/tag.hpp>
#include <osmium/osm/timestamp.hpp>
#include <osmium/osm/types.hpp>
#include <osmium/osm/way.hpp>
#include <osmium/util/cast.hpp>
#include <osmium/util/compatibility.hpp>
namespace osmium {
namespace memory {
class Buffer;
} // namespace memory
namespace builder {
class TagListBuilder : public Builder {
public:
explicit TagListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
Builder(buffer, parent, sizeof(TagList)) {
new (&item()) TagList{};
}
explicit TagListBuilder(Builder& parent) :
Builder(parent.buffer(), &parent, sizeof(TagList)) {
new (&item()) TagList{};
}
~TagListBuilder() {
add_padding();
}
/**
* Add tag to buffer.
*
* @param key Tag key (0-terminated string).
* @param value Tag value (0-terminated string).
*/
void add_tag(const char* key, const char* value) {
if (std::strlen(key) > osmium::max_osm_string_length) {
throw std::length_error{"OSM tag key is too long"};
}
if (std::strlen(value) > osmium::max_osm_string_length) {
throw std::length_error{"OSM tag value is too long"};
}
add_size(append(key));
add_size(append(value));
}
/**
* Add tag to buffer.
*
* @param key Pointer to tag key.
* @param key_length Length of key (not including the \0 byte).
* @param value Pointer to tag value.
* @param value_length Length of value (not including the \0 byte).
*/
void add_tag(const char* key, const std::size_t key_length, const char* value, const std::size_t value_length) {
if (key_length > osmium::max_osm_string_length) {
throw std::length_error{"OSM tag key is too long"};
}
if (value_length > osmium::max_osm_string_length) {
throw std::length_error{"OSM tag value is too long"};
}
add_size(append_with_zero(key, osmium::memory::item_size_type(key_length)));
add_size(append_with_zero(value, osmium::memory::item_size_type(value_length)));
}
/**
* Add tag to buffer.
*
* @param key Tag key.
* @param value Tag value.
*/
void add_tag(const std::string& key, const std::string& value) {
if (key.size() > osmium::max_osm_string_length) {
throw std::length_error{"OSM tag key is too long"};
}
if (value.size() > osmium::max_osm_string_length) {
throw std::length_error{"OSM tag value is too long"};
}
add_size(append(key.data(), osmium::memory::item_size_type(key.size()) + 1));
add_size(append(value.data(), osmium::memory::item_size_type(value.size()) + 1));
}
/**
* Add tag to buffer.
*
* @param tag Tag.
*/
void add_tag(const osmium::Tag& tag) {
add_size(append(tag.key()));
add_size(append(tag.value()));
}
/**
* Add tag to buffer.
*
* @param tag Pair of key/value 0-terminated strings.
*/
void add_tag(const std::pair<const char* const, const char* const>& tag) {
add_tag(tag.first, tag.second);
}
void add_tag(const std::pair<const char* const, const char*>& tag) {
add_tag(tag.first, tag.second);
}
void add_tag(const std::pair<const char*, const char* const>& tag) {
add_tag(tag.first, tag.second);
}
void add_tag(const std::pair<const char*, const char*>& tag) {
add_tag(tag.first, tag.second);
}
/**
* Add tag to buffer.
*
* @param tag Pair of std::string references.
*/
void add_tag(const std::pair<const std::string&, const std::string&>& tag) {
add_tag(tag.first, tag.second);
}
}; // class TagListBuilder
template <typename T>
class NodeRefListBuilder : public Builder {
public:
explicit NodeRefListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
Builder(buffer, parent, sizeof(T)) {
new (&item()) T{};
}
explicit NodeRefListBuilder(Builder& parent) :
Builder(parent.buffer(), &parent, sizeof(T)) {
new (&item()) T{};
}
~NodeRefListBuilder() {
add_padding();
}
void add_node_ref(const NodeRef& node_ref) {
new (reserve_space_for<osmium::NodeRef>()) osmium::NodeRef{node_ref};
add_size(sizeof(osmium::NodeRef));
}
void add_node_ref(const object_id_type ref, const osmium::Location& location = Location{}) {
add_node_ref(NodeRef{ref, location});
}
}; // class NodeRefListBuilder
using WayNodeListBuilder = NodeRefListBuilder<WayNodeList>;
using OuterRingBuilder = NodeRefListBuilder<OuterRing>;
using InnerRingBuilder = NodeRefListBuilder<InnerRing>;
class RelationMemberListBuilder : public Builder {
/**
* Add role to buffer.
*
* @param member Relation member object where the length of the role
* will be set.
* @param role The role.
* @param length Length of role (without \0 termination).
* @throws std:length_error If role is longer than osmium::max_osm_string_length
*/
void add_role(osmium::RelationMember& member, const char* role, const std::size_t length) {
if (length > osmium::max_osm_string_length) {
throw std::length_error{"OSM relation member role is too long"};
}
member.set_role_size(osmium::string_size_type(length) + 1);
add_size(append_with_zero(role, osmium::memory::item_size_type(length)));
add_padding(true);
}
public:
explicit RelationMemberListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
Builder(buffer, parent, sizeof(RelationMemberList)) {
new (&item()) RelationMemberList{};
}
explicit RelationMemberListBuilder(Builder& parent) :
Builder(parent.buffer(), &parent, sizeof(RelationMemberList)) {
new (&item()) RelationMemberList{};
}
~RelationMemberListBuilder() {
add_padding();
}
/**
* Add a member to the relation.
*
* @param type The type (node, way, or relation).
* @param ref The ID of the member.
* @param role The role of the member.
* @param role_length Length of the role (without \0 termination).
* @param full_member Optional pointer to the member object. If it
* is available a copy will be added to the
* relation.
* @throws std:length_error If role_length is greater than
* osmium::max_osm_string_length
*/
void add_member(osmium::item_type type, object_id_type ref, const char* role, const std::size_t role_length, const osmium::OSMObject* full_member = nullptr) {
osmium::RelationMember* member = reserve_space_for<osmium::RelationMember>();
new (member) osmium::RelationMember{ref, type, full_member != nullptr};
add_size(sizeof(RelationMember));
add_role(*member, role, role_length);
if (full_member) {
add_item(*full_member);
}
}
/**
* Add a member to the relation.
*
* @param type The type (node, way, or relation).
* @param ref The ID of the member.
* @param role The role of the member (\0 terminated string).
* @param full_member Optional pointer to the member object. If it
* is available a copy will be added to the
* relation.
* @throws std:length_error If role is longer than osmium::max_osm_string_length
*/
void add_member(osmium::item_type type, object_id_type ref, const char* role, const osmium::OSMObject* full_member = nullptr) {
add_member(type, ref, role, std::strlen(role), full_member);
}
/**
* Add a member to the relation.
*
* @param type The type (node, way, or relation).
* @param ref The ID of the member.
* @param role The role of the member.
* @param full_member Optional pointer to the member object. If it
* is available a copy will be added to the
* relation.
* @throws std:length_error If role is longer than osmium::max_osm_string_length
*/
void add_member(osmium::item_type type, object_id_type ref, const std::string& role, const osmium::OSMObject* full_member = nullptr) {
add_member(type, ref, role.data(), role.size(), full_member);
}
}; // class RelationMemberListBuilder
class ChangesetDiscussionBuilder : public Builder {
osmium::ChangesetComment* m_comment = nullptr;
void add_user(osmium::ChangesetComment& comment, const char* user, const std::size_t length) {
if (length > osmium::max_osm_string_length) {
throw std::length_error{"OSM user name is too long"};
}
comment.set_user_size(osmium::string_size_type(length) + 1);
add_size(append_with_zero(user, osmium::memory::item_size_type(length)));
}
void add_text(osmium::ChangesetComment& comment, const char* text, const std::size_t length) {
if (length > std::numeric_limits<osmium::changeset_comment_size_type>::max() - 1) {
throw std::length_error{"OSM changeset comment is too long"};
}
comment.set_text_size(osmium::changeset_comment_size_type(length) + 1);
add_size(append_with_zero(text, osmium::memory::item_size_type(length)));
add_padding(true);
}
public:
explicit ChangesetDiscussionBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
Builder(buffer, parent, sizeof(ChangesetDiscussion)) {
new (&item()) ChangesetDiscussion{};
}
explicit ChangesetDiscussionBuilder(Builder& parent) :
Builder(parent.buffer(), &parent, sizeof(ChangesetDiscussion)) {
new (&item()) ChangesetDiscussion{};
}
~ChangesetDiscussionBuilder() {
assert(!m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!");
add_padding();
}
void add_comment(osmium::Timestamp date, osmium::user_id_type uid, const char* user) {
assert(!m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!");
m_comment = reserve_space_for<osmium::ChangesetComment>();
new (m_comment) osmium::ChangesetComment{date, uid};
add_size(sizeof(ChangesetComment));
add_user(*m_comment, user, std::strlen(user));
}
void add_comment_text(const char* text) {
assert(m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!");
osmium::ChangesetComment& comment = *m_comment;
m_comment = nullptr;
add_text(comment, text, std::strlen(text));
}
void add_comment_text(const std::string& text) {
assert(m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!");
osmium::ChangesetComment& comment = *m_comment;
m_comment = nullptr;
add_text(comment, text.c_str(), text.size());
}
}; // class ChangesetDiscussionBuilder
#define OSMIUM_FORWARD(setter) \
template <typename... TArgs> \
type& setter(TArgs&&... args) { \
object().setter(std::forward<TArgs>(args)...); \
return static_cast<type&>(*this); \
}
template <typename TDerived, typename T>
class OSMObjectBuilder : public Builder {
using type = TDerived;
constexpr static const std::size_t min_size_for_user = osmium::memory::padded_length(sizeof(string_size_type) + 1);
public:
explicit OSMObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
Builder(buffer, parent, sizeof(T) + min_size_for_user) {
new (&item()) T{};
add_size(min_size_for_user);
std::fill_n(object().data() + sizeof(T), min_size_for_user, 0);
object().set_user_size(1);
}
/**
* Get a reference to the object buing built.
*
* Note that this reference will be invalidated by every action
* on the builder that might make the buffer grow. This includes
* calls to set_user() and any time a new sub-builder is created.
*/
T& object() noexcept {
return static_cast<T&>(item());
}
/**
* Get a const reference to the object buing built.
*
* Note that this reference will be invalidated by every action
* on the builder that might make the buffer grow. This includes
* calls to set_user() and any time a new sub-builder is created.
*/
const T& cobject() const noexcept {
return static_cast<const T&>(item());
}
/**
* Set user name.
*
* @param user Pointer to user name.
* @param length Length of user name (without \0 termination).
*/
TDerived& set_user(const char* user, const string_size_type length) {
const auto size_of_object = sizeof(T) + sizeof(string_size_type);
assert(cobject().user_size() == 1 && (size() <= size_of_object + osmium::memory::padded_length(1))
&& "set_user() must be called at most once and before any sub-builders");
const auto available_space = min_size_for_user - sizeof(string_size_type) - 1;
if (length > available_space) {
const auto space_needed = osmium::memory::padded_length(length - available_space);
std::fill_n(reserve_space(space_needed), space_needed, 0);
add_size(static_cast<uint32_t>(space_needed));
}
std::copy_n(user, length, object().data() + size_of_object);
object().set_user_size(length + 1);
return static_cast<TDerived&>(*this);
}
/**
* Set user name.
*
* @param user Pointer to \0-terminated user name.
*/
TDerived& set_user(const char* user) {
return set_user(user, static_cast_with_assert<string_size_type>(std::strlen(user)));
}
/**
* Set user name.
*
* @param user User name.
*/
TDerived& set_user(const std::string& user) {
return set_user(user.data(), static_cast_with_assert<string_size_type>(user.size()));
}
/// @deprecated Use set_user(...) instead.
template <typename... TArgs>
OSMIUM_DEPRECATED void add_user(TArgs&&... args) {
set_user(std::forward<TArgs>(args)...);
}
OSMIUM_FORWARD(set_id)
OSMIUM_FORWARD(set_visible)
OSMIUM_FORWARD(set_deleted)
OSMIUM_FORWARD(set_version)
OSMIUM_FORWARD(set_changeset)
OSMIUM_FORWARD(set_uid)
OSMIUM_FORWARD(set_uid_from_signed)
OSMIUM_FORWARD(set_timestamp)
OSMIUM_FORWARD(set_attribute)
OSMIUM_FORWARD(set_removed)
void add_tags(const std::initializer_list<std::pair<const char*, const char*>>& tags) {
osmium::builder::TagListBuilder tl_builder{buffer(), this};
for (const auto& p : tags) {
tl_builder.add_tag(p.first, p.second);
}
}
}; // class OSMObjectBuilder
class NodeBuilder : public OSMObjectBuilder<NodeBuilder, Node> {
using type = NodeBuilder;
public:
explicit NodeBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
OSMObjectBuilder<NodeBuilder, Node>(buffer, parent) {
}
explicit NodeBuilder(Builder& parent) :
OSMObjectBuilder<NodeBuilder, Node>(parent.buffer(), &parent) {
}
OSMIUM_FORWARD(set_location)
}; // class NodeBuilder
class WayBuilder : public OSMObjectBuilder<WayBuilder, Way> {
using type = WayBuilder;
public:
explicit WayBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
OSMObjectBuilder<WayBuilder, Way>(buffer, parent) {
}
explicit WayBuilder(Builder& parent) :
OSMObjectBuilder<WayBuilder, Way>(parent.buffer(), &parent) {
}
void add_node_refs(const std::initializer_list<osmium::NodeRef>& nodes) {
osmium::builder::WayNodeListBuilder builder{buffer(), this};
for (const auto& node_ref : nodes) {
builder.add_node_ref(node_ref);
}
}
}; // class WayBuilder
class RelationBuilder : public OSMObjectBuilder<RelationBuilder, Relation> {
using type = RelationBuilder;
public:
explicit RelationBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
OSMObjectBuilder<RelationBuilder, Relation>(buffer, parent) {
}
explicit RelationBuilder(Builder& parent) :
OSMObjectBuilder<RelationBuilder, Relation>(parent.buffer(), &parent) {
}
}; // class RelationBuilder
class AreaBuilder : public OSMObjectBuilder<AreaBuilder, Area> {
using type = AreaBuilder;
public:
explicit AreaBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
OSMObjectBuilder<AreaBuilder, Area>(buffer, parent) {
}
explicit AreaBuilder(Builder& parent) :
OSMObjectBuilder<AreaBuilder, Area>(parent.buffer(), &parent) {
}
/**
* Initialize area attributes from the attributes of the given object.
*/
void initialize_from_object(const osmium::OSMObject& source) {
set_id(osmium::object_id_to_area_id(source.id(), source.type()));
set_version(source.version());
set_changeset(source.changeset());
set_timestamp(source.timestamp());
set_visible(source.visible());
set_uid(source.uid());
set_user(source.user());
}
}; // class AreaBuilder
class ChangesetBuilder : public Builder {
using type = ChangesetBuilder;
constexpr static const std::size_t min_size_for_user = osmium::memory::padded_length(1);
public:
explicit ChangesetBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
Builder(buffer, parent, sizeof(Changeset) + min_size_for_user) {
new (&item()) Changeset{};
add_size(min_size_for_user);
std::fill_n(object().data() + sizeof(Changeset), min_size_for_user, 0);
object().set_user_size(1);
}
/**
* Get a reference to the changeset buing built.
*
* Note that this reference will be invalidated by every action
* on the builder that might make the buffer grow. This includes
* calls to set_user() and any time a new sub-builder is created.
*/
Changeset& object() noexcept {
return static_cast<Changeset&>(item());
}
/**
* Get a const reference to the changeset buing built.
*
* Note that this reference will be invalidated by every action
* on the builder that might make the buffer grow. This includes
* calls to set_user() and any time a new sub-builder is created.
*/
const Changeset& cobject() const noexcept {
return static_cast<const Changeset&>(item());
}
OSMIUM_FORWARD(set_id)
OSMIUM_FORWARD(set_uid)
OSMIUM_FORWARD(set_uid_from_signed)
OSMIUM_FORWARD(set_created_at)
OSMIUM_FORWARD(set_closed_at)
OSMIUM_FORWARD(set_num_changes)
OSMIUM_FORWARD(set_num_comments)
OSMIUM_FORWARD(set_attribute)
OSMIUM_FORWARD(set_removed)
// @deprecated Use set_bounds() instead.
OSMIUM_DEPRECATED osmium::Box& bounds() noexcept {
return object().bounds();
}
ChangesetBuilder& set_bounds(const osmium::Box& box) noexcept {
object().bounds() = box;
return *this;
}
/**
* Set user name.
*
* @param user Pointer to user name.
* @param length Length of user name (without \0 termination).
*/
ChangesetBuilder& set_user(const char* user, const string_size_type length) {
assert(cobject().user_size() == 1 && (size() <= sizeof(Changeset) + osmium::memory::padded_length(1))
&& "set_user() must be called at most once and before any sub-builders");
const auto available_space = min_size_for_user - 1;
if (length > available_space) {
const auto space_needed = osmium::memory::padded_length(length - available_space);
std::fill_n(reserve_space(space_needed), space_needed, 0);
add_size(static_cast<uint32_t>(space_needed));
}
std::copy_n(user, length, object().data() + sizeof(Changeset));
object().set_user_size(length + 1);
return *this;
}
/**
* Set user name.
*
* @param user Pointer to \0-terminated user name.
*/
ChangesetBuilder& set_user(const char* user) {
return set_user(user, static_cast_with_assert<string_size_type>(std::strlen(user)));
}
/**
* Set user name.
*
* @param user User name.
*/
ChangesetBuilder& set_user(const std::string& user) {
return set_user(user.data(), static_cast_with_assert<string_size_type>(user.size()));
}
/// @deprecated Use set_user(...) instead.
template <typename... TArgs>
OSMIUM_DEPRECATED void add_user(TArgs&&... args) {
set_user(std::forward<TArgs>(args)...);
}
}; // class ChangesetBuilder
#undef OSMIUM_FORWARD
} // namespace builder
} // namespace osmium
#endif // OSMIUM_BUILDER_OSM_OBJECT_BUILDER_HPP

View File

@ -0,0 +1,66 @@
#ifndef OSMIUM_DIFF_HANDLER_HPP
#define OSMIUM_DIFF_HANDLER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <osmium/osm/diff_object.hpp>
namespace osmium {
/**
* @brief Osmium diff handlers provide access to differences between OSM object versions
*/
namespace diff_handler {
class DiffHandler {
public:
DiffHandler() = default;
void node(const osmium::DiffNode&) const noexcept {
}
void way(const osmium::DiffWay&) const noexcept {
}
void relation(const osmium::DiffRelation&) const noexcept {
}
}; // class DiffHandler
} // namespace diff_handler
} // namespace osmium
#endif // OSMIUM_DIFF_HANDLER_HPP

View File

@ -0,0 +1,143 @@
#ifndef OSMIUM_DIFF_ITERATOR_HPP
#define OSMIUM_DIFF_ITERATOR_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cassert>
#include <cstddef>
#include <iterator>
#include <type_traits>
#include <utility>
#include <osmium/osm/diff_object.hpp>
namespace osmium {
class OSMObject;
/**
* An input iterator wrapping any iterator over OSMObjects. When
* dereferenced it will yield DiffObject objects pointing to the
* underlying OSMObjects.
*/
template <typename TBasicIterator>
class DiffIterator {
static_assert(std::is_base_of<osmium::OSMObject, typename TBasicIterator::value_type>::value, "TBasicIterator::value_type must derive from osmium::OSMObject");
TBasicIterator m_prev;
TBasicIterator m_curr;
TBasicIterator m_next;
const TBasicIterator m_end;
mutable osmium::DiffObject m_diff;
void set_diff() const noexcept {
assert(m_curr != m_end);
const bool use_curr_for_prev = m_prev->type() != m_curr->type() || m_prev->id() != m_curr->id();
const bool use_curr_for_next = m_next == m_end || m_next->type() != m_curr->type() || m_next->id() != m_curr->id();
m_diff = std::move(osmium::DiffObject{
*(use_curr_for_prev ? m_curr : m_prev),
*m_curr,
*(use_curr_for_next ? m_curr : m_next)
});
}
public:
using iterator_category = std::input_iterator_tag;
using value_type = const osmium::DiffObject;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
DiffIterator(TBasicIterator begin, TBasicIterator end) :
m_prev(begin),
m_curr(begin),
m_next(begin == end ? begin : ++begin),
m_end(std::move(end)),
m_diff() {
}
DiffIterator& operator++() {
m_prev = std::move(m_curr);
m_curr = m_next;
if (m_next != m_end) {
++m_next;
}
return *this;
}
DiffIterator operator++(int) {
DiffIterator tmp{*this};
operator++();
return tmp;
}
bool operator==(const DiffIterator& rhs) const noexcept {
return m_curr == rhs.m_curr && m_end == rhs.m_end;
}
bool operator!=(const DiffIterator& rhs) const noexcept {
return !(*this == rhs);
}
reference operator*() const noexcept {
set_diff();
return m_diff;
}
pointer operator->() const noexcept {
set_diff();
return &m_diff;
}
}; // class DiffIterator
/**
* Create a DiffIterator based on the given iterators.
*/
template <typename TBasicIterator>
inline DiffIterator<TBasicIterator> make_diff_iterator(TBasicIterator begin,
TBasicIterator end) {
return DiffIterator<TBasicIterator>{begin, end};
}
} // namespace osmium
#endif // OSMIUM_DIFF_ITERATOR_HPP

View File

@ -0,0 +1,104 @@
#ifndef OSMIUM_DIFF_VISITOR_HPP
#define OSMIUM_DIFF_VISITOR_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <osmium/diff_iterator.hpp>
#include <osmium/io/input_iterator.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/osm/diff_object.hpp>
#include <osmium/osm/item_type.hpp>
namespace osmium {
namespace detail {
template <typename THandler>
inline void apply_diff_iterator_recurse(const osmium::DiffObject& diff, THandler& handler) {
switch (diff.type()) {
case osmium::item_type::node:
handler.node(static_cast<const osmium::DiffNode&>(diff));
break;
case osmium::item_type::way:
handler.way(static_cast<const osmium::DiffWay&>(diff));
break;
case osmium::item_type::relation:
handler.relation(static_cast<const osmium::DiffRelation&>(diff));
break;
default:
throw osmium::unknown_type{};
}
}
template <typename THandler, typename... TRest>
inline void apply_diff_iterator_recurse(const osmium::DiffObject& diff, THandler& handler, TRest&... more) {
apply_diff_iterator_recurse(diff, handler);
apply_diff_iterator_recurse(diff, more...);
}
} // namespace detail
template <typename TIterator, typename... THandlers>
inline void apply_diff(TIterator it, TIterator end, THandlers&... handlers) {
using diff_iterator = osmium::DiffIterator<TIterator>;
diff_iterator dit{it, end};
diff_iterator dend{end, end};
for (; dit != dend; ++dit) {
detail::apply_diff_iterator_recurse(*dit, handlers...);
}
}
class OSMObject;
template <typename TSource, typename... THandlers>
inline void apply_diff(TSource& source, THandlers&... handlers) {
apply_diff(osmium::io::InputIterator<TSource, osmium::OSMObject>{source},
osmium::io::InputIterator<TSource, osmium::OSMObject>{},
handlers...);
}
template <typename... THandlers>
inline void apply_diff(osmium::memory::Buffer& buffer, THandlers&... handlers) {
apply_diff(buffer.begin(), buffer.end(), handlers...);
}
template <typename... THandlers>
inline void apply_diff(const osmium::memory::Buffer& buffer, THandlers&... handlers) {
apply_diff(buffer.cbegin(), buffer.cend(), handlers...);
}
} // namespace osmium
#endif // OSMIUM_DIFF_VISITOR_HPP

View File

@ -0,0 +1,195 @@
#ifndef OSMIUM_DYNAMIC_HANDLER_HPP
#define OSMIUM_DYNAMIC_HANDLER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <memory>
#include <utility>
#include <osmium/handler.hpp>
namespace osmium {
class Node;
class Way;
class Relation;
class Area;
class Changeset;
namespace handler {
namespace detail {
class HandlerWrapperBase {
public:
virtual ~HandlerWrapperBase() = default;
virtual void node(const osmium::Node&) {
}
virtual void way(const osmium::Way&) {
}
virtual void relation(const osmium::Relation&) {
}
virtual void area(const osmium::Area&) {
}
virtual void changeset(const osmium::Changeset&) {
}
virtual void flush() {
}
}; // class HandlerWrapperBase
// The following uses trick from
// http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence
// to either call handler style functions or visitor style operator().
#define OSMIUM_DYNAMIC_HANDLER_DISPATCH(_name_, _type_) \
template <typename THandler> \
auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, int) -> decltype(handler._name_(object), void()) { \
handler._name_(object); \
} \
template <typename THandler> \
auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> decltype(handler(object), void()) { \
handler(object); \
}
OSMIUM_DYNAMIC_HANDLER_DISPATCH(node, Node)
OSMIUM_DYNAMIC_HANDLER_DISPATCH(way, Way)
OSMIUM_DYNAMIC_HANDLER_DISPATCH(relation, Relation)
OSMIUM_DYNAMIC_HANDLER_DISPATCH(changeset, Changeset)
OSMIUM_DYNAMIC_HANDLER_DISPATCH(area, Area)
template <typename THandler>
auto flush_dispatch(THandler& handler, int) -> decltype(handler.flush(), void()) {
handler.flush();
}
template <typename THandler>
void flush_dispatch(THandler&, long) {
}
template <typename THandler>
class HandlerWrapper : public HandlerWrapperBase {
THandler m_handler;
public:
template <typename... TArgs>
explicit HandlerWrapper(TArgs&&... args) :
m_handler(std::forward<TArgs>(args)...) {
}
void node(const osmium::Node& node) final {
node_dispatch(m_handler, node, 0);
}
void way(const osmium::Way& way) final {
way_dispatch(m_handler, way, 0);
}
void relation(const osmium::Relation& relation) final {
relation_dispatch(m_handler, relation, 0);
}
void area(const osmium::Area& area) final {
area_dispatch(m_handler, area, 0);
}
void changeset(const osmium::Changeset& changeset) final {
changeset_dispatch(m_handler, changeset, 0);
}
void flush() final {
flush_dispatch(m_handler, 0);
}
}; // class HandlerWrapper
} // namespace detail
class DynamicHandler : public osmium::handler::Handler {
using impl_ptr = std::unique_ptr<osmium::handler::detail::HandlerWrapperBase>;
impl_ptr m_impl;
public:
DynamicHandler() :
m_impl(new osmium::handler::detail::HandlerWrapperBase) {
}
template <typename THandler, typename... TArgs>
void set(TArgs&&... args) {
m_impl.reset(new osmium::handler::detail::HandlerWrapper<THandler>{std::forward<TArgs>(args)...});
}
void node(const osmium::Node& node) {
m_impl->node(node);
}
void way(const osmium::Way& way) {
m_impl->way(way);
}
void relation(const osmium::Relation& relation) {
m_impl->relation(relation);
}
void area(const osmium::Area& area) {
m_impl->area(area);
}
void changeset(const osmium::Changeset& changeset) {
m_impl->changeset(changeset);
}
void flush() {
m_impl->flush();
}
}; // class DynamicHandler
} // namespace handler
} // namespace osmium
#endif // OSMIUM_DYNAMIC_HANDLER_HPP

View File

@ -0,0 +1,138 @@
#ifndef OSMIUM_EXPERIMENTAL_FLEX_READER_HPP
#define OSMIUM_EXPERIMENTAL_FLEX_READER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <string>
#include <utility>
#include <vector>
#include <osmium/area/assembler.hpp>
#include <osmium/area/multipolygon_collector.hpp>
#include <osmium/handler/node_locations_for_ways.hpp> // IWYU pragma: keep
#include <osmium/io/file.hpp>
#include <osmium/io/header.hpp>
#include <osmium/io/reader.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/osm/entity_bits.hpp>
#include <osmium/visitor.hpp>
namespace osmium {
/**
* @brief Experimental code that is not "officially" supported.
*/
namespace experimental {
template <typename TLocationHandler>
class FlexReader {
bool m_with_areas;
osmium::osm_entity_bits::type m_entities;
TLocationHandler& m_location_handler;
osmium::io::Reader m_reader;
osmium::area::Assembler::config_type m_assembler_config;
osmium::area::MultipolygonCollector<osmium::area::Assembler> m_collector;
public:
explicit FlexReader(const osmium::io::File& file, TLocationHandler& location_handler, osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nwr) :
m_with_areas((entities & osmium::osm_entity_bits::area) != 0),
m_entities((entities & ~osmium::osm_entity_bits::area) | (m_with_areas ? osmium::osm_entity_bits::node | osmium::osm_entity_bits::way : osmium::osm_entity_bits::nothing)),
m_location_handler(location_handler),
m_reader(file, m_entities),
m_assembler_config(),
m_collector(m_assembler_config)
{
m_location_handler.ignore_errors();
if (m_with_areas) {
osmium::io::Reader reader{file, osmium::osm_entity_bits::relation};
m_collector.read_relations(reader);
reader.close();
}
}
explicit FlexReader(const std::string& filename, TLocationHandler& location_handler, osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nwr) :
FlexReader(osmium::io::File(filename), location_handler, entities) {
}
explicit FlexReader(const char* filename, TLocationHandler& location_handler, osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::nwr) :
FlexReader(osmium::io::File(filename), location_handler, entities) {
}
osmium::memory::Buffer read() {
osmium::memory::Buffer buffer = m_reader.read();
if (buffer) {
if (m_with_areas) {
std::vector<osmium::memory::Buffer> area_buffers;
osmium::apply(buffer, m_location_handler, m_collector.handler([&area_buffers](osmium::memory::Buffer&& area_buffer) {
area_buffers.push_back(std::move(area_buffer));
}));
for (const osmium::memory::Buffer& b : area_buffers) {
buffer.add_buffer(b);
buffer.commit();
}
} else if (m_entities & (osmium::osm_entity_bits::node | osmium::osm_entity_bits::way)) {
osmium::apply(buffer, m_location_handler);
}
}
return buffer;
}
osmium::io::Header header() {
return m_reader.header();
}
void close() {
return m_reader.close();
}
bool eof() const {
return m_reader.eof();
}
const osmium::area::MultipolygonCollector<osmium::area::Assembler>& collector() const {
return m_collector;
}
}; // class FlexReader
} // namespace experimental
} // namespace osmium
#endif // OSMIUM_EXPERIMENTAL_FLEX_READER_HPP

70
include/osmium/fwd.hpp Normal file
View File

@ -0,0 +1,70 @@
#ifndef OSMIUM_FWD_HPP
#define OSMIUM_FWD_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/**
*
* @file
*
* This file contains forward declarations for commonly used Osmium classes.
*
*/
namespace osmium {
class Area;
class Box;
class Changeset;
class ChangesetComment;
class ChangesetDiscussion;
class InnerRing;
class Location;
class Node;
class NodeRef;
class NodeRefList;
class OSMEntity;
class OSMObject;
class OuterRing;
class Relation;
class RelationMemberList;
class Segment;
class Tag;
class TagList;
class Timestamp;
class Way;
class WayNodeList;
} // namespace osmium
#endif // OSMIUM_FWD_HPP

View File

@ -0,0 +1,158 @@
#ifndef OSMIUM_GEOM_COORDINATES_HPP
#define OSMIUM_GEOM_COORDINATES_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cmath>
#include <iosfwd>
#include <limits>
#include <string>
#include <osmium/osm/location.hpp>
#include <osmium/util/double.hpp>
namespace osmium {
namespace geom {
struct Coordinates {
double x;
double y;
/**
* Default constructor creates invalid coordinates.
*/
Coordinates() noexcept :
x(std::numeric_limits<double>::quiet_NaN()),
y(std::numeric_limits<double>::quiet_NaN()) {
}
/**
* Create Coordinates from doubles. If any of them is NaN, the
* coordinates are invalid.
*/
explicit Coordinates(double cx, double cy) noexcept :
x(cx),
y(cy) {
}
/**
* Create Coordinates from a Location. Will throw
* osmium::invalid_location if the location is not valid.
*/
Coordinates(const osmium::Location& location) :
x(location.lon()),
y(location.lat()) {
}
/**
* Coordinates are invalid if they have been default constructed.
*/
bool valid() const noexcept {
return !std::isnan(x) && !std::isnan(y);
}
/**
* Convert coordinates to text and append to given string. If the
* coordinate is invalid, the fixed string "invalid" will be
* added to the string.
*
* @param s String to append the coordinates to.
* @param infix Character to print between the two coordinates.
* @param precision Number of digits after the decimal point the
* coordinate will be rounded to.
*/
void append_to_string(std::string& s, const char infix, int precision) const {
if (valid()) {
osmium::util::double2string(s, x, precision);
s += infix;
osmium::util::double2string(s, y, precision);
} else {
s.append("invalid");
}
}
/**
* Convert coordinates to text and append to given string. If the
* coordinate is invalid, the fixed string "invalid" will be
* added to the string between the prefix and suffix characters.
*
* @param s String to append the coordinates to.
* @param prefix Character to print before the first coordinate.
* @param infix Character to print between the two coordinates.
* @param suffix Character to print after the second coordinate.
* @param precision Number of digits after the decimal point the
* coordinate will be rounded to.
*/
void append_to_string(std::string& s, const char prefix, const char infix, const char suffix, int precision) const {
s += prefix;
append_to_string(s, infix, precision);
s += suffix;
}
}; // struct coordinates
/**
* Check whether two Coordinates are equal. Invalid coordinates are
* equal to other invalid coordinates but not equal to any valid
* coordinates.
*
* Because this is comparing floating point values, it might not give
* the right result if the coordinates have been the result of some
* calculation that introduced rounding errors.
*/
inline bool operator==(const Coordinates& lhs, const Coordinates& rhs) noexcept {
if (!lhs.valid() && !rhs.valid()) {
return true;
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wfloat-equal"
return lhs.x == rhs.x && lhs.y == rhs.y;
#pragma GCC diagnostic pop
}
inline bool operator!=(const Coordinates& lhs, const Coordinates& rhs) noexcept {
return ! operator==(lhs, rhs);
}
template <typename TChar, typename TTraits>
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const Coordinates& c) {
return out << '(' << c.x << ',' << c.y << ')';
}
} // namespace geom
} // namespace osmium
#endif // OSMIUM_GEOM_COORDINATES_HPP

View File

@ -0,0 +1,428 @@
#ifndef OSMIUM_GEOM_FACTORY_HPP
#define OSMIUM_GEOM_FACTORY_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cstddef>
#include <stdexcept>
#include <string>
#include <utility>
#include <osmium/geom/coordinates.hpp>
#include <osmium/memory/collection.hpp>
#include <osmium/memory/item.hpp>
#include <osmium/osm/area.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/node_ref_list.hpp>
#include <osmium/osm/types.hpp>
#include <osmium/osm/way.hpp>
namespace osmium {
/**
* Exception thrown when an invalid geometry is encountered. An example
* would be a linestring with less than two points.
*/
class geometry_error : public std::runtime_error {
std::string m_message;
osmium::object_id_type m_id;
public:
explicit geometry_error(const std::string& message, const char* object_type = "", osmium::object_id_type id = 0) :
std::runtime_error(message),
m_message(message),
m_id(id) {
if (m_id != 0) {
m_message += " (";
m_message += object_type;
m_message += "_id=";
m_message += std::to_string(m_id);
m_message += ")";
}
}
void set_id(const char* object_type, osmium::object_id_type id) {
if (m_id == 0 && id != 0) {
m_message += " (";
m_message += object_type;
m_message += "_id=";
m_message += std::to_string(id);
m_message += ")";
}
m_id = id;
}
osmium::object_id_type id() const noexcept {
return m_id;
}
const char* what() const noexcept override {
return m_message.c_str();
}
}; // class geometry_error
/**
* @brief Everything related to geometry handling.
*/
namespace geom {
/**
* Which nodes of a way to use for a linestring.
*/
enum class use_nodes : bool {
unique = true, ///< Remove consecutive nodes with same location.
all = false ///< Use all nodes.
}; // enum class use_nodes
/**
* Which direction the linestring created from a way
* should have.
*/
enum class direction : bool {
backward = true, ///< Linestring has reverse direction.
forward = false ///< Linestring has same direction as way.
}; // enum class direction
/**
* This pseudo projection just returns its WGS84 input unchanged.
* Used as a template parameter if a real projection is not needed.
*/
class IdentityProjection {
public:
Coordinates operator()(osmium::Location location) const {
return Coordinates{location.lon(), location.lat()};
}
int epsg() const noexcept {
return 4326;
}
std::string proj_string() const {
return "+proj=longlat +datum=WGS84 +no_defs";
}
}; // class IdentityProjection
/**
* Geometry factory.
*/
template <typename TGeomImpl, typename TProjection = IdentityProjection>
class GeometryFactory {
/**
* Add all points of an outer or inner ring to a multipolygon.
*/
void add_points(const osmium::NodeRefList& nodes) {
osmium::Location last_location;
for (const osmium::NodeRef& node_ref : nodes) {
if (last_location != node_ref.location()) {
last_location = node_ref.location();
m_impl.multipolygon_add_location(m_projection(last_location));
}
}
}
TProjection m_projection;
TGeomImpl m_impl;
public:
GeometryFactory<TGeomImpl, TProjection>() :
m_projection(),
m_impl(m_projection.epsg()) {
}
/**
* Constructor for default initialized projection.
*/
template <typename... TArgs>
explicit GeometryFactory<TGeomImpl, TProjection>(TArgs&&... args) :
m_projection(),
m_impl(m_projection.epsg(), std::forward<TArgs>(args)...) {
}
/**
* Constructor for explicitly initialized projection. Note that the
* projection is moved into the GeometryFactory.
*/
template <typename... TArgs>
explicit GeometryFactory<TGeomImpl, TProjection>(TProjection&& projection, TArgs&&... args) :
m_projection(std::move(projection)),
m_impl(m_projection.epsg(), std::forward<TArgs>(args)...) {
}
using projection_type = TProjection;
using point_type = typename TGeomImpl::point_type;
using linestring_type = typename TGeomImpl::linestring_type;
using polygon_type = typename TGeomImpl::polygon_type;
using multipolygon_type = typename TGeomImpl::multipolygon_type;
using ring_type = typename TGeomImpl::ring_type;
int epsg() const noexcept {
return m_projection.epsg();
}
std::string proj_string() const {
return m_projection.proj_string();
}
/* Point */
point_type create_point(const osmium::Location& location) const {
return m_impl.make_point(m_projection(location));
}
point_type create_point(const osmium::Node& node) {
try {
return create_point(node.location());
} catch (osmium::geometry_error& e) {
e.set_id("node", node.id());
throw;
}
}
point_type create_point(const osmium::NodeRef& node_ref) {
try {
return create_point(node_ref.location());
} catch (osmium::geometry_error& e) {
e.set_id("node", node_ref.ref());
throw;
}
}
/* LineString */
void linestring_start() {
m_impl.linestring_start();
}
template <typename TIter>
size_t fill_linestring(TIter it, TIter end) {
size_t num_points = 0;
for (; it != end; ++it, ++num_points) {
m_impl.linestring_add_location(m_projection(it->location()));
}
return num_points;
}
template <typename TIter>
size_t fill_linestring_unique(TIter it, TIter end) {
size_t num_points = 0;
osmium::Location last_location;
for (; it != end; ++it) {
if (last_location != it->location()) {
last_location = it->location();
m_impl.linestring_add_location(m_projection(last_location));
++num_points;
}
}
return num_points;
}
linestring_type linestring_finish(size_t num_points) {
return m_impl.linestring_finish(num_points);
}
linestring_type create_linestring(const osmium::WayNodeList& wnl, use_nodes un = use_nodes::unique, direction dir = direction::forward) {
linestring_start();
size_t num_points = 0;
if (un == use_nodes::unique) {
osmium::Location last_location;
switch (dir) {
case direction::forward:
num_points = fill_linestring_unique(wnl.cbegin(), wnl.cend());
break;
case direction::backward:
num_points = fill_linestring_unique(wnl.crbegin(), wnl.crend());
break;
}
} else {
switch (dir) {
case direction::forward:
num_points = fill_linestring(wnl.cbegin(), wnl.cend());
break;
case direction::backward:
num_points = fill_linestring(wnl.crbegin(), wnl.crend());
break;
}
}
if (num_points < 2) {
throw osmium::geometry_error{"need at least two points for linestring"};
}
return linestring_finish(num_points);
}
linestring_type create_linestring(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir = direction::forward) {
try {
return create_linestring(way.nodes(), un, dir);
} catch (osmium::geometry_error& e) {
e.set_id("way", way.id());
throw;
}
}
/* Polygon */
void polygon_start() {
m_impl.polygon_start();
}
template <typename TIter>
size_t fill_polygon(TIter it, TIter end) {
size_t num_points = 0;
for (; it != end; ++it, ++num_points) {
m_impl.polygon_add_location(m_projection(it->location()));
}
return num_points;
}
template <typename TIter>
size_t fill_polygon_unique(TIter it, TIter end) {
size_t num_points = 0;
osmium::Location last_location;
for (; it != end; ++it) {
if (last_location != it->location()) {
last_location = it->location();
m_impl.polygon_add_location(m_projection(last_location));
++num_points;
}
}
return num_points;
}
polygon_type polygon_finish(size_t num_points) {
return m_impl.polygon_finish(num_points);
}
polygon_type create_polygon(const osmium::WayNodeList& wnl, use_nodes un = use_nodes::unique, direction dir = direction::forward) {
polygon_start();
size_t num_points = 0;
if (un == use_nodes::unique) {
osmium::Location last_location;
switch (dir) {
case direction::forward:
num_points = fill_polygon_unique(wnl.cbegin(), wnl.cend());
break;
case direction::backward:
num_points = fill_polygon_unique(wnl.crbegin(), wnl.crend());
break;
}
} else {
switch (dir) {
case direction::forward:
num_points = fill_polygon(wnl.cbegin(), wnl.cend());
break;
case direction::backward:
num_points = fill_polygon(wnl.crbegin(), wnl.crend());
break;
}
}
if (num_points < 4) {
throw osmium::geometry_error{"need at least four points for polygon"};
}
return polygon_finish(num_points);
}
polygon_type create_polygon(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir = direction::forward) {
try {
return create_polygon(way.nodes(), un, dir);
} catch (osmium::geometry_error& e) {
e.set_id("way", way.id());
throw;
}
}
/* MultiPolygon */
multipolygon_type create_multipolygon(const osmium::Area& area) {
try {
size_t num_polygons = 0;
size_t num_rings = 0;
m_impl.multipolygon_start();
for (const auto& item : area) {
if (item.type() == osmium::item_type::outer_ring) {
auto& ring = static_cast<const osmium::OuterRing&>(item);
if (num_polygons > 0) {
m_impl.multipolygon_polygon_finish();
}
m_impl.multipolygon_polygon_start();
m_impl.multipolygon_outer_ring_start();
add_points(ring);
m_impl.multipolygon_outer_ring_finish();
++num_rings;
++num_polygons;
} else if (item.type() == osmium::item_type::inner_ring) {
auto& ring = static_cast<const osmium::InnerRing&>(item);
m_impl.multipolygon_inner_ring_start();
add_points(ring);
m_impl.multipolygon_inner_ring_finish();
++num_rings;
}
}
// if there are no rings, this area is invalid
if (num_rings == 0) {
throw osmium::geometry_error{"invalid area"};
}
m_impl.multipolygon_polygon_finish();
return m_impl.multipolygon_finish();
} catch (osmium::geometry_error& e) {
e.set_id("area", area.id());
throw;
}
}
}; // class GeometryFactory
} // namespace geom
} // namespace osmium
#endif // OSMIUM_GEOM_FACTORY_HPP

View File

@ -0,0 +1,161 @@
#ifndef OSMIUM_GEOM_GEOJSON_HPP
#define OSMIUM_GEOM_GEOJSON_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cassert>
#include <cstddef>
#include <string>
#include <utility>
#include <osmium/geom/coordinates.hpp>
#include <osmium/geom/factory.hpp>
namespace osmium {
namespace geom {
namespace detail {
class GeoJSONFactoryImpl {
std::string m_str;
int m_precision;
public:
using point_type = std::string;
using linestring_type = std::string;
using polygon_type = std::string;
using multipolygon_type = std::string;
using ring_type = std::string;
GeoJSONFactoryImpl(int /* srid */, int precision = 7) :
m_precision(precision) {
}
/* Point */
// { "type": "Point", "coordinates": [100.0, 0.0] }
point_type make_point(const osmium::geom::Coordinates& xy) const {
std::string str {"{\"type\":\"Point\",\"coordinates\":"};
xy.append_to_string(str, '[', ',', ']', m_precision);
str += "}";
return str;
}
/* LineString */
// { "type": "LineString", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] }
void linestring_start() {
m_str = "{\"type\":\"LineString\",\"coordinates\":[";
}
void linestring_add_location(const osmium::geom::Coordinates& xy) {
xy.append_to_string(m_str, '[', ',', ']', m_precision);
m_str += ',';
}
linestring_type linestring_finish(size_t /* num_points */) {
assert(!m_str.empty());
std::string str;
using std::swap;
swap(str, m_str);
str.back() = ']';
str += "}";
return str;
}
/* MultiPolygon */
void multipolygon_start() {
m_str = "{\"type\":\"MultiPolygon\",\"coordinates\":[";
}
void multipolygon_polygon_start() {
m_str += '[';
}
void multipolygon_polygon_finish() {
m_str += "],";
}
void multipolygon_outer_ring_start() {
m_str += '[';
}
void multipolygon_outer_ring_finish() {
assert(!m_str.empty());
m_str.back() = ']';
}
void multipolygon_inner_ring_start() {
m_str += ",[";
}
void multipolygon_inner_ring_finish() {
assert(!m_str.empty());
m_str.back() = ']';
}
void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
xy.append_to_string(m_str, '[', ',', ']', m_precision);
m_str += ',';
}
multipolygon_type multipolygon_finish() {
assert(!m_str.empty());
std::string str;
using std::swap;
swap(str, m_str);
str.back() = ']';
str += "}";
return str;
}
}; // class GeoJSONFactoryImpl
} // namespace detail
template <typename TProjection = IdentityProjection>
using GeoJSONFactory = GeometryFactory<osmium::geom::detail::GeoJSONFactoryImpl, TProjection>;
} // namespace geom
} // namespace osmium
#endif // OSMIUM_GEOM_GEOJSON_HPP

View File

@ -0,0 +1,271 @@
#ifndef OSMIUM_GEOM_GEOS_HPP
#define OSMIUM_GEOM_GEOS_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <geos/version.h>
#if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && (GEOS_VERSION_MAJOR < 3 || (GEOS_VERSION_MAJOR == 3 && GEOS_VERSION_MINOR <= 5))
#define OSMIUM_WITH_GEOS
/**
* @file
*
* This file contains code for conversion of OSM geometries into GEOS
* geometries.
*
* Note that everything in this file is deprecated and only works up to
* GEOS 3.5. It uses the GEOS C++ API which the GEOS project does not consider
* to be a stable, external API. We probably should have used the GEOS C API
* instead.
*
* @attention If you include this file, you'll need to link with `libgeos`.
*/
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <geos/geom/Coordinate.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/CoordinateSequenceFactory.h>
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/LinearRing.h>
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/Point.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/PrecisionModel.h>
#include <geos/util/GEOSException.h>
#include <osmium/geom/factory.hpp>
#include <osmium/geom/coordinates.hpp>
#include <osmium/util/compatibility.hpp>
// MSVC doesn't support throw_with_nested yet
#ifdef _MSC_VER
# define THROW throw
#else
# include <exception>
# define THROW std::throw_with_nested
#endif
namespace osmium {
struct geos_geometry_error : public geometry_error {
explicit geos_geometry_error(const char* message) :
geometry_error(std::string{"geometry creation failed in GEOS library: "} + message) {
}
}; // struct geos_geometry_error
namespace geom {
namespace detail {
/// @deprecated
class GEOSFactoryImpl {
std::unique_ptr<const geos::geom::PrecisionModel> m_precision_model;
std::unique_ptr<geos::geom::GeometryFactory> m_our_geos_factory;
geos::geom::GeometryFactory* m_geos_factory;
std::unique_ptr<geos::geom::CoordinateSequence> m_coordinate_sequence;
std::vector<std::unique_ptr<geos::geom::LinearRing>> m_rings;
std::vector<std::unique_ptr<geos::geom::Polygon>> m_polygons;
public:
using point_type = std::unique_ptr<geos::geom::Point>;
using linestring_type = std::unique_ptr<geos::geom::LineString>;
using polygon_type = std::unique_ptr<geos::geom::Polygon>;
using multipolygon_type = std::unique_ptr<geos::geom::MultiPolygon>;
using ring_type = std::unique_ptr<geos::geom::LinearRing>;
explicit GEOSFactoryImpl(int /* srid */, geos::geom::GeometryFactory& geos_factory) :
m_precision_model(nullptr),
m_our_geos_factory(nullptr),
m_geos_factory(&geos_factory) {
}
/**
* @deprecated Do not set SRID explicitly. It will be set to the
* correct value automatically.
*/
OSMIUM_DEPRECATED explicit GEOSFactoryImpl(int /* srid */, int srid) :
m_precision_model(new geos::geom::PrecisionModel),
m_our_geos_factory(new geos::geom::GeometryFactory{m_precision_model.get(), srid}),
m_geos_factory(m_our_geos_factory.get()) {
}
explicit GEOSFactoryImpl(int srid) :
m_precision_model(new geos::geom::PrecisionModel),
m_our_geos_factory(new geos::geom::GeometryFactory{m_precision_model.get(), srid}),
m_geos_factory(m_our_geos_factory.get()) {
}
/* Point */
point_type make_point(const osmium::geom::Coordinates& xy) const {
try {
return point_type{m_geos_factory->createPoint(geos::geom::Coordinate{xy.x, xy.y})};
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
/* LineString */
void linestring_start() {
try {
m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast<std::size_t>(0), 2));
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
void linestring_add_location(const osmium::geom::Coordinates& xy) {
try {
m_coordinate_sequence->add(geos::geom::Coordinate{xy.x, xy.y});
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
linestring_type linestring_finish(std::size_t /* num_points */) {
try {
return linestring_type{m_geos_factory->createLineString(m_coordinate_sequence.release())};
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
/* MultiPolygon */
void multipolygon_start() {
m_polygons.clear();
}
void multipolygon_polygon_start() {
m_rings.clear();
}
void multipolygon_polygon_finish() {
try {
assert(!m_rings.empty());
auto inner_rings = new std::vector<geos::geom::Geometry*>;
std::transform(std::next(m_rings.begin(), 1), m_rings.end(), std::back_inserter(*inner_rings), [](std::unique_ptr<geos::geom::LinearRing>& r) {
return r.release();
});
m_polygons.emplace_back(m_geos_factory->createPolygon(m_rings[0].release(), inner_rings));
m_rings.clear();
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
void multipolygon_outer_ring_start() {
try {
m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast<std::size_t>(0), 2));
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
void multipolygon_outer_ring_finish() {
try {
m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release()));
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
void multipolygon_inner_ring_start() {
try {
m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast<std::size_t>(0), 2));
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
void multipolygon_inner_ring_finish() {
try {
m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release()));
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
try {
m_coordinate_sequence->add(geos::geom::Coordinate{xy.x, xy.y});
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
multipolygon_type multipolygon_finish() {
try {
auto polygons = new std::vector<geos::geom::Geometry*>;
std::transform(m_polygons.begin(), m_polygons.end(), std::back_inserter(*polygons), [](std::unique_ptr<geos::geom::Polygon>& p) {
return p.release();
});
m_polygons.clear();
return multipolygon_type{m_geos_factory->createMultiPolygon(polygons)};
} catch (const geos::util::GEOSException& e) {
THROW(osmium::geos_geometry_error(e.what()));
}
}
}; // class GEOSFactoryImpl
} // namespace detail
/// @deprecated
template <typename TProjection = IdentityProjection>
using GEOSFactory = GeometryFactory<osmium::geom::detail::GEOSFactoryImpl, TProjection>;
} // namespace geom
} // namespace osmium
#undef THROW
#endif
#endif // OSMIUM_GEOM_GEOS_HPP

View File

@ -0,0 +1,96 @@
#ifndef OSMIUM_GEOM_HAVERSINE_HPP
#define OSMIUM_GEOM_HAVERSINE_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cmath>
#include <iterator>
#include <osmium/geom/coordinates.hpp>
#include <osmium/geom/util.hpp>
#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/way.hpp>
namespace osmium {
namespace geom {
/**
* @brief Functions to calculate arc distance on Earth using the haversine formula.
*
* See http://en.wikipedia.org/wiki/Haversine_formula
*
* Implementation derived from
* http://blog.julien.cayzac.name/2008/10/arc-and-distance-between-two-points-on.html
*/
namespace haversine {
/// @brief Earth's quadratic mean radius for WGS84
constexpr const double EARTH_RADIUS_IN_METERS = 6372797.560856;
/**
* Calculate distance in meters between two sets of coordinates.
*
* @pre @code c1.valid() && c2.valid() @endcode
*/
inline double distance(const osmium::geom::Coordinates& c1, const osmium::geom::Coordinates& c2) {
double lonh = sin(deg_to_rad(c1.x - c2.x) * 0.5);
lonh *= lonh;
double lath = sin(deg_to_rad(c1.y - c2.y) * 0.5);
lath *= lath;
const double tmp = cos(deg_to_rad(c1.y)) * cos(deg_to_rad(c2.y));
return 2.0 * EARTH_RADIUS_IN_METERS * asin(sqrt(lath + tmp*lonh));
}
/**
* Calculate length of way.
*/
inline double distance(const osmium::WayNodeList& wnl) {
double sum_length = 0;
for (auto it = wnl.begin(); it != wnl.end(); ++it) {
if (std::next(it) != wnl.end()) {
sum_length += distance(it->location(), std::next(it)->location());
}
}
return sum_length;
}
} // namespace haversine
} // namespace geom
} // namespace osmium
#endif // OSMIUM_GEOM_HAVERSINE_HPP

View File

@ -0,0 +1,162 @@
#ifndef OSMIUM_GEOM_MERCATOR_PROJECTION_HPP
#define OSMIUM_GEOM_MERCATOR_PROJECTION_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cmath>
#include <string>
#include <osmium/geom/coordinates.hpp>
#include <osmium/geom/util.hpp>
#include <osmium/osm/location.hpp>
namespace osmium {
namespace geom {
namespace detail {
constexpr double earth_radius_for_epsg3857 = 6378137.0;
constexpr double max_coordinate_epsg3857 = 20037508.34;
constexpr inline double lon_to_x(double lon) {
return earth_radius_for_epsg3857 * deg_to_rad(lon);
}
inline double lat_to_y_with_tan(double lat) { // not constexpr because math functions aren't
return earth_radius_for_epsg3857 * std::log(std::tan(osmium::geom::PI/4 + deg_to_rad(lat)/2));
}
#ifdef OSMIUM_USE_SLOW_MERCATOR_PROJECTION
inline double lat_to_y(double lat) {
return lat_to_y_with_tan(lat);
}
#else
// This is a much faster implementation than the canonical
// implementation using the tan() function. For details
// see https://github.com/osmcode/mercator-projection .
inline double lat_to_y(double lat) { // not constexpr because math functions aren't
if (lat < -78.0 || lat > 78.0) {
return lat_to_y_with_tan(lat);
}
return earth_radius_for_epsg3857 *
((((((((((-3.1112583378460085319e-23 * lat +
2.0465852743943268009e-19) * lat +
6.4905282018672673884e-18) * lat +
-1.9685447939983315591e-14) * lat +
-2.2022588158115104182e-13) * lat +
5.1617537365509453239e-10) * lat +
2.5380136069803016519e-9) * lat +
-5.1448323697228488745e-6) * lat +
-9.4888671473357768301e-6) * lat +
1.7453292518154191887e-2) * lat)
/
((((((((((-1.9741136066814230637e-22 * lat +
-1.258514031244679556e-20) * lat +
4.8141483273572351796e-17) * lat +
8.6876090870176172185e-16) * lat +
-2.3298743439377541768e-12) * lat +
-1.9300094785736130185e-11) * lat +
4.3251609106864178231e-8) * lat +
1.7301944508516974048e-7) * lat +
-3.4554675198786337842e-4) * lat +
-5.4367203601085991108e-4) * lat + 1.0);
}
#endif
constexpr inline double x_to_lon(double x) {
return rad_to_deg(x) / earth_radius_for_epsg3857;
}
inline double y_to_lat(double y) { // not constexpr because math functions aren't
return rad_to_deg(2 * std::atan(std::exp(y / earth_radius_for_epsg3857)) - osmium::geom::PI/2);
}
} // namespace detail
/**
* The maximum latitude that can be projected with the Web Mercator
* (EPSG:3857) projection.
*/
constexpr double MERCATOR_MAX_LAT = 85.0511288;
/**
* Convert the coordinates from WGS84 lon/lat to web mercator.
*
* @pre @code c.valid() @endcode
*/
inline Coordinates lonlat_to_mercator(const Coordinates& c) {
return Coordinates{detail::lon_to_x(c.x), detail::lat_to_y(c.y)};
}
/**
* Convert the coordinates from web mercator to WGS84 lon/lat.
*
* @pre @code c.valid() @endcode
*/
inline Coordinates mercator_to_lonlat(const Coordinates& c) {
return Coordinates{detail::x_to_lon(c.x), detail::y_to_lat(c.y)};
}
/**
* Functor that does projection from WGS84 (EPSG:4326) to "Web
* Mercator" (EPSG:3857)
*/
class MercatorProjection {
public:
MercatorProjection() {
}
Coordinates operator()(osmium::Location location) const {
return Coordinates{detail::lon_to_x(location.lon()), detail::lat_to_y(location.lat())};
}
int epsg() const noexcept {
return 3857;
}
std::string proj_string() const {
return "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs";
}
}; // class MercatorProjection
} // namespace geom
} // namespace osmium
#endif // OSMIUM_GEOM_MERCATOR_PROJECTION_HPP

180
include/osmium/geom/ogr.hpp Normal file
View File

@ -0,0 +1,180 @@
#ifndef OSMIUM_GEOM_OGR_HPP
#define OSMIUM_GEOM_OGR_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/**
* @file
*
* This file contains code for conversion of OSM geometries into OGR
* geometries.
*
* @attention If you include this file, you'll need to link with `libgdal`.
*/
#include <cassert>
#include <cstddef>
#include <memory>
#include <utility>
#include <ogr_geometry.h>
#include <osmium/geom/coordinates.hpp>
#include <osmium/geom/factory.hpp>
namespace osmium {
namespace geom {
namespace detail {
class OGRFactoryImpl {
public:
using point_type = std::unique_ptr<OGRPoint>;
using linestring_type = std::unique_ptr<OGRLineString>;
using polygon_type = std::unique_ptr<OGRPolygon>;
using multipolygon_type = std::unique_ptr<OGRMultiPolygon>;
using ring_type = std::unique_ptr<OGRLinearRing>;
private:
linestring_type m_linestring{nullptr};
multipolygon_type m_multipolygon{nullptr};
polygon_type m_polygon{nullptr};
ring_type m_ring{nullptr};
public:
explicit OGRFactoryImpl(int /* srid */) {
}
/* Point */
point_type make_point(const osmium::geom::Coordinates& xy) const {
return point_type{new OGRPoint{xy.x, xy.y}};
}
/* LineString */
void linestring_start() {
m_linestring.reset(new OGRLineString{});
}
void linestring_add_location(const osmium::geom::Coordinates& xy) {
assert(!!m_linestring);
m_linestring->addPoint(xy.x, xy.y);
}
linestring_type linestring_finish(size_t /* num_points */) {
assert(!!m_linestring);
return std::move(m_linestring);
}
/* Polygon */
void polygon_start() {
m_ring.reset(new OGRLinearRing{});
}
void polygon_add_location(const osmium::geom::Coordinates& xy) {
assert(!!m_ring);
m_ring->addPoint(xy.x, xy.y);
}
polygon_type polygon_finish(size_t /* num_points */) {
auto polygon = std::unique_ptr<OGRPolygon>{new OGRPolygon{}};
polygon->addRingDirectly(m_ring.release());
return polygon;
}
/* MultiPolygon */
void multipolygon_start() {
m_multipolygon.reset(new OGRMultiPolygon{});
}
void multipolygon_polygon_start() {
m_polygon.reset(new OGRPolygon{});
}
void multipolygon_polygon_finish() {
assert(!!m_multipolygon);
assert(!!m_polygon);
m_multipolygon->addGeometryDirectly(m_polygon.release());
}
void multipolygon_outer_ring_start() {
m_ring.reset(new OGRLinearRing{});
}
void multipolygon_outer_ring_finish() {
assert(!!m_polygon);
assert(!!m_ring);
m_polygon->addRingDirectly(m_ring.release());
}
void multipolygon_inner_ring_start() {
m_ring.reset(new OGRLinearRing{});
}
void multipolygon_inner_ring_finish() {
assert(!!m_polygon);
assert(!!m_ring);
m_polygon->addRingDirectly(m_ring.release());
}
void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
assert(!!m_polygon);
assert(!!m_ring);
m_ring->addPoint(xy.x, xy.y);
}
multipolygon_type multipolygon_finish() {
assert(!!m_multipolygon);
return std::move(m_multipolygon);
}
}; // class OGRFactoryImpl
} // namespace detail
template <typename TProjection = IdentityProjection>
using OGRFactory = GeometryFactory<osmium::geom::detail::OGRFactoryImpl, TProjection>;
} // namespace geom
} // namespace osmium
#endif // OSMIUM_GEOM_OGR_HPP

View File

@ -0,0 +1,193 @@
#ifndef OSMIUM_GEOM_PROJECTION_HPP
#define OSMIUM_GEOM_PROJECTION_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/**
* @file
*
* This file contains code for projecting OSM locations to arbitrary
* coordinate reference systems. It is based on the Proj.4 library.
*
* @attention If you include this file, you'll need to link with `libproj`.
*/
#include <memory>
#include <string>
#include <proj_api.h>
#include <osmium/geom/coordinates.hpp>
#include <osmium/geom/mercator_projection.hpp>
#include <osmium/geom/util.hpp>
#include <osmium/osm/location.hpp>
namespace osmium {
namespace geom {
/**
* C++ wrapper for a Coordinate Reference System of the proj library.
*/
class CRS {
struct ProjCRSDeleter {
void operator()(void* crs) {
pj_free(crs);
}
}; // struct ProjCRSDeleter
std::unique_ptr<void, ProjCRSDeleter> m_crs;
public:
explicit CRS(const char* crs) :
m_crs(pj_init_plus(crs), ProjCRSDeleter()) {
if (!m_crs) {
throw osmium::projection_error{std::string{"creation of CRS failed: "} + pj_strerrno(*pj_get_errno_ref())};
}
}
explicit CRS(const std::string& crs) :
CRS(crs.c_str()) {
}
explicit CRS(int epsg) :
CRS(std::string{"+init=epsg:"} + std::to_string(epsg)) {
}
/**
* Get underlying projPJ handle from proj library.
*/
projPJ get() const noexcept {
return m_crs.get();
}
bool is_latlong() const noexcept {
return pj_is_latlong(m_crs.get()) != 0;
}
bool is_geocent() const noexcept {
return pj_is_geocent(m_crs.get()) != 0;
}
}; // class CRS
/**
* Transform coordinates from one CRS into another. Wraps the same
* function of the proj library.
*
* Coordinates have to be in radians and are produced in radians.
*
* @throws osmium::projection_error if the projection fails
*/
// cppcheck-suppress passedByValue (because c is small and we want to change it)
inline Coordinates transform(const CRS& src, const CRS& dest, Coordinates c) {
const int result = pj_transform(src.get(), dest.get(), 1, 1, &c.x, &c.y, nullptr);
if (result != 0) {
throw osmium::projection_error{std::string{"projection failed: "} + pj_strerrno(result)};
}
return c;
}
/**
* Functor that does projection from WGS84 (EPSG:4326) to the given
* CRS.
*
* If this Projection is initialized with the constructor taking
* an integer with the epsg code 4326, no projection is done. If it
* is initialized with epsg code 3857 the Osmium-internal
* implementation of the Mercator projection is used, otherwise this
* falls back to using the proj.4 library. Note that this "magic" does
* not work if you use any of the constructors taking a string.
*/
class Projection {
int m_epsg;
std::string m_proj_string;
CRS m_crs_wgs84{4326};
CRS m_crs_user;
public:
explicit Projection(const std::string& proj_string) :
m_epsg(-1),
m_proj_string(proj_string),
m_crs_user(proj_string) {
}
explicit Projection(const char* proj_string) :
m_epsg(-1),
m_proj_string(proj_string),
m_crs_user(proj_string) {
}
explicit Projection(int epsg) :
m_epsg(epsg),
m_proj_string(std::string{"+init=epsg:"} + std::to_string(epsg)),
m_crs_user(epsg) {
}
Coordinates operator()(osmium::Location location) const {
if (m_epsg == 4326) {
return Coordinates{location.lon(), location.lat()};
} else if (m_epsg == 3857) {
return Coordinates{detail::lon_to_x(location.lon()),
detail::lat_to_y(location.lat())};
}
Coordinates c{transform(m_crs_wgs84, m_crs_user, Coordinates{deg_to_rad(location.lon()),
deg_to_rad(location.lat())})};
if (m_crs_user.is_latlong()) {
c.x = rad_to_deg(c.x);
c.y = rad_to_deg(c.y);
}
return c;
}
int epsg() const noexcept {
return m_epsg;
}
std::string proj_string() const {
return m_proj_string;
}
}; // class Projection
} // namespace geom
} // namespace osmium
#endif // OSMIUM_GEOM_PROJECTION_HPP

View File

@ -0,0 +1,192 @@
#ifndef OSMIUM_GEOM_RAPID_GEOJSON_HPP
#define OSMIUM_GEOM_RAPID_GEOJSON_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cstddef>
#include <osmium/geom/coordinates.hpp>
#include <osmium/geom/factory.hpp>
namespace osmium {
namespace geom {
namespace detail {
/**
* A geometry factory implementation that can be used with the
* RapidJSON (https://github.com/miloyip/rapidjson) JSON writer.
*/
template <typename TWriter>
class RapidGeoJSONFactoryImpl {
TWriter* m_writer;
public:
using point_type = void;
using linestring_type = void;
using polygon_type = void;
using multipolygon_type = void;
using ring_type = void;
RapidGeoJSONFactoryImpl(int /* srid */, TWriter& writer) :
m_writer(&writer) {
}
/* Point */
// { "type": "Point", "coordinates": [100.0, 0.0] }
point_type make_point(const osmium::geom::Coordinates& xy) const {
m_writer->String("geometry");
m_writer->StartObject();
m_writer->String("type");
m_writer->String("Point");
m_writer->String("coordinates");
m_writer->StartArray();
m_writer->Double(xy.x);
m_writer->Double(xy.y);
m_writer->EndArray();
m_writer->EndObject();
}
/* LineString */
// { "type": "LineString", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] }
void linestring_start() {
m_writer->String("geometry");
m_writer->StartObject();
m_writer->String("type");
m_writer->String("LineString");
m_writer->String("coordinates");
m_writer->StartArray();
}
void linestring_add_location(const osmium::geom::Coordinates& xy) {
m_writer->StartArray();
m_writer->Double(xy.x);
m_writer->Double(xy.y);
m_writer->EndArray();
}
linestring_type linestring_finish(size_t /* num_points */) {
m_writer->EndArray();
m_writer->EndObject();
}
/* Polygon */
// { "type": "Polygon", "coordinates": [[[100.0, 0.0], [101.0, 1.0]]] }
void polygon_start() {
m_writer->String("geometry");
m_writer->StartObject();
m_writer->String("type");
m_writer->String("Polygon");
m_writer->String("coordinates");
m_writer->StartArray();
m_writer->StartArray();
}
void polygon_add_location(const osmium::geom::Coordinates& xy) {
m_writer->StartArray();
m_writer->Double(xy.x);
m_writer->Double(xy.y);
m_writer->EndArray();
}
polygon_type polygon_finish(size_t /* num_points */) {
m_writer->EndArray();
m_writer->EndArray();
m_writer->EndObject();
}
/* MultiPolygon */
void multipolygon_start() {
m_writer->String("geometry");
m_writer->StartObject();
m_writer->String("type");
m_writer->String("MultiPolygon");
m_writer->String("coordinates");
m_writer->StartArray();
}
void multipolygon_polygon_start() {
m_writer->StartArray();
}
void multipolygon_polygon_finish() {
m_writer->EndArray();
}
void multipolygon_outer_ring_start() {
m_writer->StartArray();
}
void multipolygon_outer_ring_finish() {
m_writer->EndArray();
}
void multipolygon_inner_ring_start() {
m_writer->StartArray();
}
void multipolygon_inner_ring_finish() {
m_writer->EndArray();
}
void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
m_writer->StartArray();
m_writer->Double(xy.x);
m_writer->Double(xy.y);
m_writer->EndArray();
}
multipolygon_type multipolygon_finish() {
m_writer->EndArray();
m_writer->EndObject();
}
}; // class RapidGeoJSONFactoryImpl
} // namespace detail
template <typename TWriter, typename TProjection = IdentityProjection>
using RapidGeoJSONFactory = GeometryFactory<detail::RapidGeoJSONFactoryImpl<TWriter>, TProjection>;
} // namespace geom
} // namespace osmium
#endif // OSMIUM_GEOM_RAPID_GEOJSON_HPP

View File

@ -0,0 +1,67 @@
#ifndef OSMIUM_GEOM_RELATIONS_HPP
#define OSMIUM_GEOM_RELATIONS_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <osmium/osm/box.hpp>
#include <osmium/osm/location.hpp>
namespace osmium {
namespace geom {
/**
* Check whether one geometry contains another.
*/
inline bool contains(const osmium::Box& lhs, const osmium::Box& rhs) noexcept {
return ((lhs.bottom_left().x() >= rhs.bottom_left().x()) &&
(lhs.top_right().x() <= rhs.top_right().x()) &&
(lhs.bottom_left().y() >= rhs.bottom_left().y()) &&
(lhs.top_right().y() <= rhs.top_right().y()));
}
/**
* Check whether one geometry overlaps another.
*/
inline bool overlaps(const osmium::Box& lhs, const osmium::Box& rhs) noexcept {
return ((lhs.bottom_left().x() <= rhs.top_right().x()) &&
(lhs.bottom_left().y() <= rhs.top_right().y()) &&
(rhs.bottom_left().x() <= lhs.top_right().x()) &&
(rhs.bottom_left().y() <= lhs.top_right().y()));
}
} // namespace geom
} // namespace osmium
#endif // OSMIUM_GEOM_RELATIONS_HPP

Some files were not shown because too many files have changed in this diff Show More