From babbda98a6ea1d53a8bc5015ef5dfb313c47186a Mon Sep 17 00:00:00 2001 From: "Daniel J. Hofmann" Date: Fri, 11 Nov 2016 15:50:02 +0100 Subject: [PATCH] Squashed 'third_party/libosmium/' changes from 80df1d6..40c4a48 40c4a48 Release v2.10.0 6addb2e Search for protozero also in the place where libosmium was found. 9c0d4bb Consistently use lhs and rhs as parameter names in operators etc. 6179759 Update change log. 894eb7d Explicitly use size_t to get no narrowing conversions. e549e73 Fix CMake config for index_lookup. ccf0bc2 Fix bug where some compilers deduce the wrong type. fc3507d Cleaned and documented index example and renamed to osmium_index_lookup. d3c3036 Rename serdump example to dump_internal and document it. 0e9822e Also forward set_uid_from_signed() function. 29ef95c Rename build_taglist function because it was to similar to build_tag_list. c088dd0 Move static constant out of class so clang will compile it. 606cdc4 Fix CMake build script: Path for finding libosmium. 65f91fe Removed unused typedef. 07174f5 Use condition_variable to tell producer when thread queue is not full. b35e957 Some code cleanup in XML parser. c703dff Fix problem with MemberMeta. 1ccbbef Refactoring CompressionFactory. 6561bd6 Use our own exception type for map factory errors. 56e31fa Throw not_found error directly instead of using helper function. 28230c3 Test empty value for node location store, reorganize tests. 2ba316c Reorganize and fix projection tests. f949485 Don't run quite as many tests with projections because they are slow. 1bad16d Add information about build environment to benchmark output. 99617bb Change proj_geojson benchmark into mercator benchmark. 553b946 Allow optional parameters on Reader in any order. dcc3d8f Factor out some common code. 004d8cd Fix forwarding constructor. 9702978 Fix metadata check. 6cfb6c4 Faster implementation of decode_dense_nodes without reading metdata. 4ba4638 Add additional read_metadata parameter to other Reader constructors. d005937 Optionally ignore metadata when reading file. 63961da Mark all CRC32 update functions as noexcept. cc4ca75 Refactor set_user() function to speed it up. 38d19dc Update comments with file sizes. OSM has grown... f7d0824 Add new benchmark that shows performance when main thread is busy. 25070dc Use const ref params. 88e8d96 Mark add_user() as deprecated. f58d9db Refactor some low-level collection code to clean up code. 4680def Add example showing how to create OSM data out of thin air. d42fd50 Add an example showing how tags in OSM files can be changed. 49bf5bc Add additional constructors to Builders taking a reference to parent. 7b91d63 Change Builder::add_item() to take a reference instead of pointer. 2957e48 Some cleanup of examples. d0b458d Calculate size of object at compile time. 3fbb6e7 Use explicit cast. 1851f3d Remove a test that depends on math details. f6a0802 Various cleanups of example programs. ba4921f Rename add_user() to set_user(). d7637c9 Various cleanups related to builders. 07827bc Fix add_user(). 9a5b395 Also refactor OSMObjectBuilder like ChangesetBuilder before. b1f423c Use call chaining on the builder. e49473d Get rid of ObjectBuilder class. 67d70b9 Refactor ChangesetBuilder::add_user(). 8199c33 Make ChangesetBuilder derive directly from Builder. 61d1b67 Simplify some code. d38467a Change derivation hierarchy of some builder classes. b52f8af Refactor Builder code. d012bfa Refactored some code setting attributes on objects using builder instead. 6a05f60 Also forward set_removed function from builders. 8d63b7d Return *this from Builder setter functions and test it. 72a1266 Update catch.hpp to newest version. 3424a74 Check GEOS version is <= 3.5. aee9f9d Cleanup test code. aef198c Improved asserts in Buffer. a98b9bf Code cleanup in tests. a150466 Use GDAL/OGR instead of GEOS to test our WKB implementation. b04a525 Refactor test. 39aa932 Refactored test_factory_with_projection so it works without GEOS. 648f43a Remove unused dependency on geos from tile test. f1748ae Add setters to Builder classes forwarding to underlying objects. 8166879 In debug mode check that Builders are correctly called and destructed. 1c4257e Call commit() on buffer only after all builders are destructed. 2618636 Add functions to check availability of relation members. b45a4d9 Mark RelationMember::set_ref() as deprecated. 7886771 Move "basic" and "buffer" tests to "osm" and "memory", respectively. b664685 Use functions instead of macros in location test for faster compile. b4929ac Add more tests for number parser. 02662a7 Merge pull request #171 from lonvia/fix-long-exponentials 5344a6c fix parsing of numbers in e-notiation with many post-comma digits 3aeaff3 Add some typedefs to NodeRefList and memory::Collection. e750665 Add iterators to IdSetSmall and add docs and tests to IdSet classes. 50ecb2a Add more features to IdSetDense, including unset and iterator. e3dec78 Make IdSet virtual base class with two implementations. 8ea0153 Use C array instead of std::array in IdSet and clear explicitly. 3ba9905 Deprecate osmium::index::BoolVector in favour of new IdSet. 453d1ca Add osmium::index::IdSet. c78254e Add function to (temporarily) disable the progress bar. 4d88a9f Better document osmium::io::Header class. 320e3af Look for protozero includes in CMake config. 838d25e Allow optional checking for libosmium version number in CMake config. 6ce60c1 Fix entity_bits static_assert() tests. f054731 Update change log. 77ac4c2 Make sleep duration for full queues much smaller. 7e39c01 Make some entity_bits functions constexpr. 69ea72f Fix ~ operator on entity_bits and more tests for entity bits. dafe4cf Update embedded Catch unit test header to version 1.5.7. a41c832 Fixed parsing of location coordinates starting with decimal dot. 6523bae README cosmetics. 229acac Add tests for some examples. f1e753d Merge pull request #163 from sebastic/executable-not-elf-or-script ccea2d5 Remove executable bit from .cpp files. af77fb4 Changelog formatting fixes. git-subtree-dir: third_party/libosmium git-subtree-split: 40c4a48f88d25edace6f0b9e079c306308c7760b --- CHANGELOG.md | 58 ++- CMakeLists.txt | 6 +- README.md | 4 +- benchmarks/CMakeLists.txt | 3 + benchmarks/download_data.sh | 10 +- benchmarks/osmium_benchmark_count.cpp | 6 +- benchmarks/osmium_benchmark_count_tag.cpp | 6 +- benchmarks/osmium_benchmark_mercator.cpp | 43 ++ benchmarks/run_benchmark_mercator.sh | 22 + benchmarks/setup.sh | 16 +- cmake/FindOsmium.cmake | 62 ++- examples/CMakeLists.txt | 8 +- examples/README.md | 12 + examples/osmium_area_test.cpp | 4 +- examples/osmium_change_tags.cpp | 203 ++++++++ examples/osmium_convert.cpp | 8 +- examples/osmium_create_pois.cpp | 96 ++++ examples/osmium_dump_internal.cpp | 179 +++++++ examples/osmium_filter_discussions.cpp | 2 +- examples/osmium_index.cpp | 260 ---------- examples/osmium_index_lookup.cpp | 333 +++++++++++++ examples/osmium_pub_names.cpp | 0 examples/osmium_road_length.cpp | 0 examples/osmium_serdump.cpp | 206 -------- include/osmium/area/assembler.hpp | 52 +- .../osmium/area/detail/node_ref_segment.hpp | 4 +- include/osmium/area/detail/segment_list.hpp | 2 +- include/osmium/area/detail/vector.hpp | 20 +- include/osmium/builder/attr.hpp | 64 +-- include/osmium/builder/builder.hpp | 105 ++--- include/osmium/builder/osm_object_builder.hpp | 314 +++++++++++-- include/osmium/geom/factory.hpp | 2 +- include/osmium/geom/geos.hpp | 16 +- include/osmium/geom/relations.hpp | 10 +- include/osmium/geom/tile.hpp | 20 +- include/osmium/handler/disk_store.hpp | 5 +- .../handler/node_locations_for_ways.hpp | 2 +- include/osmium/handler/object_relations.hpp | 2 + include/osmium/index/bool_vector.hpp | 41 +- include/osmium/index/detail/vector_map.hpp | 6 +- include/osmium/index/id_set.hpp | 431 +++++++++++++++++ include/osmium/index/index.hpp | 12 +- include/osmium/index/map.hpp | 27 +- include/osmium/index/map/dummy.hpp | 2 +- include/osmium/index/map/sparse_mem_map.hpp | 2 +- include/osmium/index/map/sparse_mem_table.hpp | 4 +- include/osmium/io/compression.hpp | 60 +-- include/osmium/io/detail/input_format.hpp | 23 +- include/osmium/io/detail/o5m_input_format.hpp | 104 ++-- include/osmium/io/detail/opl_input_format.hpp | 8 +- .../osmium/io/detail/opl_parser_functions.hpp | 80 ++-- include/osmium/io/detail/pbf_decoder.hpp | 170 +++++-- include/osmium/io/detail/pbf_input_format.hpp | 10 +- include/osmium/io/detail/queue_util.hpp | 13 +- include/osmium/io/detail/xml_input_format.hpp | 94 ++-- include/osmium/io/file_format.hpp | 5 + include/osmium/io/header.hpp | 79 +++- include/osmium/io/reader.hpp | 63 ++- include/osmium/memory/buffer.hpp | 82 +++- include/osmium/memory/collection.hpp | 47 +- include/osmium/memory/item.hpp | 4 +- include/osmium/osm/area.hpp | 12 +- include/osmium/osm/changeset.hpp | 18 +- include/osmium/osm/crc.hpp | 42 +- include/osmium/osm/entity_bits.hpp | 26 +- include/osmium/osm/location.hpp | 54 ++- include/osmium/osm/node.hpp | 8 +- include/osmium/osm/node_ref_list.hpp | 33 +- include/osmium/osm/object.hpp | 8 + include/osmium/osm/relation.hpp | 17 +- include/osmium/osm/tag.hpp | 19 +- include/osmium/osm/way.hpp | 6 +- include/osmium/relations/collector.hpp | 58 ++- .../osmium/relations/detail/member_meta.hpp | 40 +- include/osmium/thread/queue.hpp | 57 ++- include/osmium/util/progress_bar.hpp | 12 + include/osmium/version.hpp | 4 +- test/CMakeLists.txt | 50 +- test/data-tests/testdata-xml.cpp | 2 +- test/examples/CMakeLists.txt | 21 + test/examples/t/pub_names/CMakeLists.txt | 7 + test/examples/t/pub_names/pubs.osm | 7 + test/examples/t/road_length/CMakeLists.txt | 8 + test/examples/t/road_length/road.osm | 59 +++ test/include/catch.hpp | 50 +- test/t/basic/test_entity_bits.cpp | 32 -- test/t/builder/test_object_builder.cpp | 444 ++++++++++++++++++ test/t/geom/helper.hpp | 15 - test/t/geom/test_crs.cpp | 10 +- test/t/geom/test_exception.cpp | 8 +- test/t/geom/test_factory_with_projection.cpp | 44 +- test/t/geom/test_geojson.cpp | 203 ++++---- test/t/geom/test_geos.cpp | 56 ++- test/t/geom/test_geos_wkb.cpp | 92 ---- test/t/geom/test_ogr.cpp | 132 +++--- test/t/geom/test_ogr_wkb.cpp | 100 ++++ test/t/geom/test_projection.cpp | 139 +++--- test/t/geom/test_tile.cpp | 2 - test/t/geom/test_wkb.cpp | 18 +- test/t/geom/wnl_helper.hpp | 6 +- test/t/index/test_id_set.cpp | 166 +++++++ test/t/index/test_id_to_location.cpp | 187 ++++---- test/t/io/test_compression_factory.cpp | 27 ++ test/t/io/test_reader_with_mock_parser.cpp | 8 +- .../{buffer => memory}/test_buffer_basics.cpp | 0 .../t/{buffer => memory}/test_buffer_node.cpp | 130 +++-- .../{buffer => memory}/test_buffer_purge.cpp | 82 ++-- test/t/{basic => osm}/test_area.cpp | 0 test/t/{basic => osm}/test_box.cpp | 0 test/t/{basic => osm}/test_changeset.cpp | 65 ++- test/t/{basic => osm}/test_crc.cpp | 0 test/t/osm/test_entity_bits.cpp | 62 +++ test/t/{basic => osm}/test_location.cpp | 117 +++-- test/t/{basic => osm}/test_node.cpp | 0 test/t/{basic => osm}/test_node_ref.cpp | 0 .../test_object_comparisons.cpp | 0 test/t/{basic => osm}/test_relation.cpp | 0 test/t/{basic => osm}/test_timestamp.cpp | 0 .../{basic => osm}/test_types_from_string.cpp | 0 test/t/{basic => osm}/test_way.cpp | 2 +- 120 files changed, 4266 insertions(+), 2031 deletions(-) create mode 100644 benchmarks/osmium_benchmark_mercator.cpp create mode 100755 benchmarks/run_benchmark_mercator.sh create mode 100644 examples/osmium_change_tags.cpp create mode 100644 examples/osmium_create_pois.cpp create mode 100644 examples/osmium_dump_internal.cpp delete mode 100644 examples/osmium_index.cpp create mode 100644 examples/osmium_index_lookup.cpp mode change 100755 => 100644 examples/osmium_pub_names.cpp mode change 100755 => 100644 examples/osmium_road_length.cpp delete mode 100644 examples/osmium_serdump.cpp create mode 100644 include/osmium/index/id_set.hpp create mode 100644 test/examples/CMakeLists.txt create mode 100644 test/examples/t/pub_names/CMakeLists.txt create mode 100644 test/examples/t/pub_names/pubs.osm create mode 100644 test/examples/t/road_length/CMakeLists.txt create mode 100644 test/examples/t/road_length/road.osm delete mode 100644 test/t/basic/test_entity_bits.cpp create mode 100644 test/t/builder/test_object_builder.cpp delete mode 100644 test/t/geom/helper.hpp delete mode 100644 test/t/geom/test_geos_wkb.cpp create mode 100644 test/t/geom/test_ogr_wkb.cpp create mode 100644 test/t/index/test_id_set.cpp create mode 100644 test/t/io/test_compression_factory.cpp rename test/t/{buffer => memory}/test_buffer_basics.cpp (100%) rename test/t/{buffer => memory}/test_buffer_node.cpp (50%) rename test/t/{buffer => memory}/test_buffer_purge.cpp (60%) rename test/t/{basic => osm}/test_area.cpp (100%) rename test/t/{basic => osm}/test_box.cpp (100%) rename test/t/{basic => osm}/test_changeset.cpp (73%) rename test/t/{basic => osm}/test_crc.cpp (100%) create mode 100644 test/t/osm/test_entity_bits.cpp rename test/t/{basic => osm}/test_location.cpp (78%) rename test/t/{basic => osm}/test_node.cpp (100%) rename test/t/{basic => osm}/test_node_ref.cpp (100%) rename test/t/{basic => osm}/test_object_comparisons.cpp (100%) rename test/t/{basic => osm}/test_relation.cpp (100%) rename test/t/{basic => osm}/test_timestamp.cpp (100%) rename test/t/{basic => osm}/test_types_from_string.cpp (100%) rename test/t/{basic => osm}/test_way.cpp (98%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d132ef59..25327c8e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,53 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +## [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 @@ -110,7 +157,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). - 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()`). + an area (`osmium::Areas::outer_rings()`, `inner_rings()`). - Debug output optionally prints CRC32 when `add_crc32` file option is set. ### Changed @@ -267,9 +314,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). 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. +- 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 @@ -410,7 +457,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). Doxygen (up to version 1.8.8). This version contains a workaround to fix this. -[unreleased]: https://github.com/osmcode/libosmium/compare/v2.9.0...HEAD +[unreleased]: https://github.com/osmcode/libosmium/compare/v2.10.0...HEAD +[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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 095137b39..21cf98e11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Cover project(libosmium) set(LIBOSMIUM_VERSION_MAJOR 2) -set(LIBOSMIUM_VERSION_MINOR 9) +set(LIBOSMIUM_VERSION_MINOR 10) set(LIBOSMIUM_VERSION_PATCH 0) set(LIBOSMIUM_VERSION @@ -285,6 +285,10 @@ if(BUILD_DATA_TESTS) add_subdirectory(test/data-tests) endif() +if(BUILD_EXAMPLES) + add_subdirectory(test/examples) +endif() + #----------------------------------------------------------------------------- # diff --git a/README.md b/README.md index 68fc2f61b..d526f13b0 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@ http://osmcode.org/libosmium A fast and flexible C++ library for working with OpenStreetMap data. -[![Build Status](https://secure.travis-ci.org/osmcode/libosmium.png)](https://travis-ci.org/osmcode/libosmium) -[![Build status](https://ci.appveyor.com/api/projects/status/mkbg6e6stdgq7c1b?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium) +[![Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium) +[![Build status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium) Libosmium is developed on Linux, but also works on OSX and Windows (with some limitations). diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt index e46c83349..4b76fcbc1 100644 --- a/benchmarks/CMakeLists.txt +++ b/benchmarks/CMakeLists.txt @@ -12,6 +12,7 @@ set(BENCHMARKS count count_tag index_map + mercator static_vs_dynamic_index write_pbf CACHE STRING "Benchmark programs" @@ -37,6 +38,8 @@ foreach(benchmark ${BENCHMARKS}) @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() diff --git a/benchmarks/download_data.sh b/benchmarks/download_data.sh index 8a6a8ff50..be6adb954 100755 --- a/benchmarks/download_data.sh +++ b/benchmarks/download_data.sh @@ -4,9 +4,9 @@ # cd $DATA_DIR -curl --location --output 1_liechtenstein.osm.pbf http://download.geofabrik.de/europe/liechtenstein-latest.osm.pbf # about 1 MB -curl --location --output 2_bremen.osm.pbf http://download.geofabrik.de/europe/germany/bremen-latest.osm.pbf # about 13 MB -curl --location --output 3_sachsen.osm.pbf http://download.geofabrik.de/europe/germany/sachsen-latest.osm.pbf # about 120 MB -curl --location --output 4_germany.osm.pbf http://download.geofabrik.de/europe/germany-latest.osm.pbf # about 2 GB -curl --location --output 5_planet.osm.pbf http://planet.osm.org/pbf/planet-latest.osm.pbf # about 26 GB +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 diff --git a/benchmarks/osmium_benchmark_count.cpp b/benchmarks/osmium_benchmark_count.cpp index 1a16275f0..41f9aa078 100644 --- a/benchmarks/osmium_benchmark_count.cpp +++ b/benchmarks/osmium_benchmark_count.cpp @@ -19,15 +19,15 @@ struct CountHandler : public osmium::handler::Handler { uint64_t ways = 0; uint64_t relations = 0; - void node(osmium::Node&) { + void node(const osmium::Node&) { ++nodes; } - void way(osmium::Way&) { + void way(const osmium::Way&) { ++ways; } - void relation(osmium::Relation&) { + void relation(const osmium::Relation&) { ++relations; } diff --git a/benchmarks/osmium_benchmark_count_tag.cpp b/benchmarks/osmium_benchmark_count_tag.cpp index 6062ecceb..5b82a7c3a 100644 --- a/benchmarks/osmium_benchmark_count_tag.cpp +++ b/benchmarks/osmium_benchmark_count_tag.cpp @@ -18,7 +18,7 @@ struct CountHandler : public osmium::handler::Handler { uint64_t counter = 0; uint64_t all = 0; - void node(osmium::Node& node) { + void node(const osmium::Node& node) { ++all; const char* amenity = node.tags().get_value_by_key("amenity"); if (amenity && !strcmp(amenity, "post_box")) { @@ -26,11 +26,11 @@ struct CountHandler : public osmium::handler::Handler { } } - void way(osmium::Way&) { + void way(const osmium::Way&) { ++all; } - void relation(osmium::Relation&) { + void relation(const osmium::Relation&) { ++all; } diff --git a/benchmarks/osmium_benchmark_mercator.cpp b/benchmarks/osmium_benchmark_mercator.cpp new file mode 100644 index 000000000..091e5c025 --- /dev/null +++ b/benchmarks/osmium_benchmark_mercator.cpp @@ -0,0 +1,43 @@ +/* + + The code in this file is released into the Public Domain. + +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct GeomHandler : public osmium::handler::Handler { + + osmium::geom::WKBFactory 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(); +} + diff --git a/benchmarks/run_benchmark_mercator.sh b/benchmarks/run_benchmark_mercator.sh new file mode 100755 index 000000000..a52072764 --- /dev/null +++ b/benchmarks/run_benchmark_mercator.sh @@ -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 + diff --git a/benchmarks/setup.sh b/benchmarks/setup.sh index 9733bfe1b..f8901c273 100755 --- a/benchmarks/setup.sh +++ b/benchmarks/setup.sh @@ -9,6 +9,10 @@ if [ -z $DATA_DIR ]; then 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` @@ -20,11 +24,17 @@ 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 +grep '^cpu MHz' /proc/cpuinfo | tail -1 +grep '^cpu cores' /proc/cpuinfo | tail -1 +grep '^siblings' /proc/cpuinfo | tail -1 echo "---------------------" echo "MEMORY:" diff --git a/cmake/FindOsmium.cmake b/cmake/FindOsmium.cmake index fba8ffb92..2224e18dc 100644 --- a/cmake/FindOsmium.cmake +++ b/cmake/FindOsmium.cmake @@ -2,8 +2,8 @@ # # FindOsmium.cmake # -# Find the Libosmium headers and, optionally, several components needed for -# different Libosmium functions. +# Find the Libosmium headers and, optionally, several components needed +# for different Libosmium functions. # #---------------------------------------------------------------------- # @@ -18,9 +18,12 @@ # # Then add the following in your CMakeLists.txt: # -# find_package(Osmium REQUIRED COMPONENTS ) +# find_package(Osmium [version] REQUIRED COMPONENTS ) # include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS}) # +# The version number is optional. If it is not set, any version of +# libosmium will do. +# # For the substitute a space separated list of one or more of the # following components: # @@ -51,10 +54,9 @@ # #---------------------------------------------------------------------- -# Look for the header file. -find_path(OSMIUM_INCLUDE_DIR osmium/osm.hpp - PATH_SUFFIXES include - PATHS +# This is the list of directories where we look for osmium and protozero +# includes. +set(_osmium_include_path ../libosmium ~/Library/Frameworks /Library/Frameworks @@ -62,6 +64,22 @@ find_path(OSMIUM_INCLUDE_DIR osmium/osm.hpp /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}") #---------------------------------------------------------------------- @@ -95,17 +113,31 @@ if(Osmium_USE_PBF) find_package(ZLIB) find_package(Threads) - list(APPEND OSMIUM_EXTRA_FIND_VARS ZLIB_FOUND Threads_FOUND) - if(ZLIB_FOUND AND Threads_FOUND) + 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.") @@ -203,7 +235,7 @@ if(Osmium_USE_SPARSEHASH) 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) + if(NOT CMAKE_VERSION VERSION_LESS 3.0) include(CheckTypeSize) set(CMAKE_REQUIRED_INCLUDES ${SPARSEHASH_INCLUDE_DIR}) set(CMAKE_EXTRA_INCLUDE_FILES "google/sparsetable") @@ -253,13 +285,15 @@ endif() # Check that all required libraries are available # #---------------------------------------------------------------------- -if (OSMIUM_EXTRA_FIND_VARS) +if(OSMIUM_EXTRA_FIND_VARS) list(REMOVE_DUPLICATES OSMIUM_EXTRA_FIND_VARS) endif() -# Handle the QUIETLY and REQUIRED arguments and set OSMIUM_FOUND to TRUE if -# all listed variables are TRUE. +# 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}) +find_package_handle_standard_args(Osmium + REQUIRED_VARS OSMIUM_INCLUDE_DIR ${OSMIUM_EXTRA_FIND_VARS} + VERSION_VAR _libosmium_version) unset(OSMIUM_EXTRA_FIND_VARS) #---------------------------------------------------------------------- diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b47cfdcf7..ac0789633 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -10,18 +10,20 @@ message(STATUS "Configuring examples") set(EXAMPLES area_test + change_tags convert count + create_pois debug + dump_internal filter_discussions - index + index_lookup location_cache_create location_cache_use pub_names read read_with_progress road_length - serdump tiles CACHE STRING "Example programs" ) @@ -32,7 +34,7 @@ set(EXAMPLES # Examples depending on wingetopt # #----------------------------------------------------------------------------- -set(GETOPT_EXAMPLES area_test convert index serdump) +set(GETOPT_EXAMPLES area_test convert index_lookup) if(NOT GETOPT_MISSING) foreach(example ${GETOPT_EXAMPLES}) list(APPEND EXAMPLE_LIBS_${example} ${GETOPT_LIBRARY}) diff --git a/examples/README.md b/examples/README.md index e29103292..55bd4063a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -18,12 +18,24 @@ them. ## Still reasonably simple examples +* `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 diff --git a/examples/osmium_area_test.cpp b/examples/osmium_area_test.cpp index 030337454..0c849a7b0 100644 --- a/examples/osmium_area_test.cpp +++ b/examples/osmium_area_test.cpp @@ -106,7 +106,7 @@ int main(int argc, char* argv[]) { // Read options from command line. while (true) { - int c = getopt_long(argc, argv, "hwo", long_options, 0); + const int c = getopt_long(argc, argv, "hwo", long_options, 0); if (c == -1) { break; } @@ -126,7 +126,7 @@ int main(int argc, char* argv[]) { } } - int remaining_args = argc - optind; + const int remaining_args = argc - optind; if (remaining_args != 1) { std::cerr << "Usage: " << argv[0] << " [OPTIONS] OSMFILE\n"; std::exit(1); diff --git a/examples/osmium_change_tags.cpp b/examples/osmium_change_tags.cpp new file mode 100644 index 000000000..a7c19047d --- /dev/null +++ b/examples/osmium_change_tags.cpp @@ -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 // for std::exit +#include // for std::strcmp +#include // for std::exception +#include // for std::cout, std::cerr +#include // for std::string +#include // for std::move + +// Allow any format of input files (XML, PBF, ...) +#include + +// Allow any format of output files (XML, PBF, ...) +#include + +// We want to use the builder interface +#include + +// We want to use the handler interface +#include + +// For osmium::apply() +#include + +// 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 + 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. + 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 node 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 node 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); + } +} + diff --git a/examples/osmium_convert.cpp b/examples/osmium_convert.cpp index 48a0823db..0ced82c1c 100644 --- a/examples/osmium_convert.cpp +++ b/examples/osmium_convert.cpp @@ -67,7 +67,7 @@ int main(int argc, char* argv[]) { // Read options from command line. while (true) { - int c = getopt_long(argc, argv, "dhf:t:", long_options, 0); + const int c = getopt_long(argc, argv, "dhf:t:", long_options, 0); if (c == -1) { break; } @@ -87,7 +87,7 @@ int main(int argc, char* argv[]) { } } - int remaining_args = argc - optind; + const int remaining_args = argc - optind; if (remaining_args > 2) { std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]\n"; std::exit(1); @@ -124,13 +124,13 @@ int main(int argc, char* argv[]) { osmium::io::Reader reader{input_file}; // Get header from input file and change the "generator" setting to - // outselves. + // 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); + 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 diff --git a/examples/osmium_create_pois.cpp b/examples/osmium_create_pois.cpp new file mode 100644 index 000000000..ff79cbbd5 --- /dev/null +++ b/examples/osmium_create_pois.cpp @@ -0,0 +1,96 @@ +/* + + 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 // for std::exit +#include // for std::strcmp +#include // for std::time +#include // for std::exception +#include // for std::cout, std::cerr +#include // for std::string +#include // for std::move + +// Allow any format of output files (XML, PBF, ...) +#include + +// We want to use the builder interface +#include +#include + +// 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]}; + + 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_name, 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); + } +} + diff --git a/examples/osmium_dump_internal.cpp b/examples/osmium_dump_internal.cpp new file mode 100644 index 000000000..dbc50db9a --- /dev/null +++ b/examples/osmium_dump_internal.cpp @@ -0,0 +1,179 @@ +/* + + 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 file, 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 // for errno +#include // for std::strerror +#include // for std::exit +#include // for std::cout, std::cerr +#include // for std::string +#include // for open +#include // for open + +#ifdef _MSC_VER +# include +#endif + +// Allow any format of input files (XML, PBF, ...) +#include + +// The DiskStore handler +#include + +// The ObjectRelations handler +#include + +// The indexes +#include +#include + +using offset_index_type = osmium::index::map::SparseMemArray; +using map_type = osmium::index::multimap::SparseMemArray; + +/** + * Small class wrapping index files, basically making sure errors are handled + * and the files are closed on destruction. + */ +class IndexFile { + + int m_fd; + +public: + + 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); + } + } + + ~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); + } + + // 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()); +} + diff --git a/examples/osmium_filter_discussions.cpp b/examples/osmium_filter_discussions.cpp index 633498199..c2a94e5a2 100644 --- a/examples/osmium_filter_discussions.cpp +++ b/examples/osmium_filter_discussions.cpp @@ -67,7 +67,7 @@ int main(int argc, char* argv[]) { // 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); + 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". diff --git a/examples/osmium_index.cpp b/examples/osmium_index.cpp deleted file mode 100644 index 478b6c6b6..000000000 --- a/examples/osmium_index.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* - - Example program to look at Osmium indexes on disk. - - The code in this example file is released into the Public Domain. - -*/ - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -template -class IndexSearch { - - typedef typename osmium::index::map::DenseFileArray dense_index_type; - typedef typename osmium::index::map::SparseFileArray sparse_index_type; - - int m_fd; - bool m_dense_format; - - void dump_dense() { - dense_index_type index(m_fd); - - for (std::size_t i = 0; i < index.size(); ++i) { - if (index.get(i) != TValue()) { - std::cout << i << " " << index.get(i) << "\n"; - } - } - } - - void dump_sparse() { - sparse_index_type index(m_fd); - - for (auto& element : index) { - std::cout << element.first << " " << element.second << "\n"; - } - } - - bool search_dense(TKey key) { - dense_index_type index(m_fd); - - try { - TValue value = index.get(key); - std::cout << key << " " << value << "\n"; - } catch (...) { - std::cout << key << " not found\n"; - return false; - } - - return true; - } - - bool search_sparse(TKey key) { - typedef typename sparse_index_type::element_type element_type; - sparse_index_type index(m_fd); - - element_type elem {key, TValue()}; - 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; - } - -public: - - IndexSearch(int fd, bool dense_format) : - m_fd(fd), - m_dense_format(dense_format) { - } - - void dump() { - if (m_dense_format) { - dump_dense(); - } else { - dump_sparse(); - } - } - - bool search(TKey key) { - if (m_dense_format) { - return search_dense(key); - } else { - return search_sparse(key); - } - } - - bool search(const std::vector& keys) { - bool found_all = true; - - for (const auto key : keys) { - if (!search(key)) { - found_all = false; - } - } - - return found_all; - } - -}; // class IndexSearch - -enum return_code : int { - okay = 0, - not_found = 1, - error = 2, - fatal = 3 -}; - -class Options { - - std::vector 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 [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' or 'offset')\n" - ; - } - -public: - - Options(int argc, char* argv[]) { - static struct option long_options[] = { - {"array", required_argument, 0, 'a'}, - {"dump", no_argument, 0, 'd'}, - {"help", no_argument, 0, 'h'}, - {"list", required_argument, 0, 'l'}, - {"search", required_argument, 0, 's'}, - {"type", required_argument, 0, 't'}, - {0, 0, 0, 0} - }; - - while (true) { - int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, 0); - 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(return_code::okay); - 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 != "offset") { - std::cerr << "Unknown type '" << m_type << "'. Must be 'location' or 'offset'.\n"; - std::exit(return_code::fatal); - } - break; - default: - std::exit(return_code::fatal); - } - } - - if (m_array_format == m_list_format) { - std::cerr << "Need option --array or --list, but not both\n"; - std::exit(return_code::fatal); - } - - if (m_type.empty()) { - std::cerr << "Need --type argument.\n"; - std::exit(return_code::fatal); - } - - } - - const std::string& filename() const noexcept { - return m_filename; - } - - bool dense_format() const noexcept { - return m_array_format; - } - - bool do_dump() const noexcept { - return m_dump; - } - - const std::vector& search_keys() const noexcept { - return m_ids; - } - - bool type_is(const char* type) const noexcept { - return m_type == type; - } - -}; // class Options - -int main(int argc, char* argv[]) { - std::ios_base::sync_with_stdio(false); - - Options options(argc, argv); - - std::cout << std::fixed << std::setprecision(7); - int fd = open(options.filename().c_str(), O_RDWR); - - bool result_okay = true; - - if (options.type_is("location")) { - IndexSearch is(fd, options.dense_format()); - - if (options.do_dump()) { - is.dump(); - } else { - result_okay = is.search(options.search_keys()); - } - } else { - IndexSearch is(fd, options.dense_format()); - - if (options.do_dump()) { - is.dump(); - } else { - result_okay = is.search(options.search_keys()); - } - } - - std::exit(result_okay ? return_code::okay : return_code::not_found); -} - diff --git a/examples/osmium_index_lookup.cpp b/examples/osmium_index_lookup.cpp new file mode 100644 index 000000000..01d7e36b3 --- /dev/null +++ b/examples/osmium_index_lookup.cpp @@ -0,0 +1,333 @@ +/* + + 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 // for std::all_of, std::equal_range +#include // for std::exit +#include // for open +#include // for getopt_long +#include // for std::cout, std::cerr +#include // for std::unique_ptr +#include // for std::string +#include // for open +#include // for open +#include // for std::vector + +// Disk-based indexes +#include +#include + +// osmium::Location +#include + +// Basic Osmium types +#include + +// Virtual class for disk index access. If offers functions to dump the +// indexes and to search for ids in the index. +template +class IndexAccess { + + int m_fd; + +public: + + 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& 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 +class IndexAccessDense : public IndexAccess { + + using index_type = typename osmium::index::map::DenseFileArray; + +public: + + IndexAccessDense(int fd) : + IndexAccess(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 +class IndexAccessSparse : public IndexAccess { + + using index_type = typename osmium::index::map::SparseFileArray; + +public: + + IndexAccessSparse(int fd) : + IndexAccess(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 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, 0, 'a'}, + {"dump", no_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {"list", required_argument, 0, 'l'}, + {"search", required_argument, 0, 's'}, + {"type", required_argument, 0, 't'}, + {0, 0, 0, 0} + }; + + while (true) { + const int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, 0); + 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& 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 +std::unique_ptr> create(bool dense, int fd) { + std::unique_ptr> ptr; + + if (dense) { + ptr.reset(new IndexAccessDense{fd}); + } else { + ptr.reset(new IndexAccessSparse{fd}); + } + + return ptr; +} + +// Do the actual work: Either dump the index or search in the index. +template +int run(const IndexAccess& 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); + } + + // Depending on the type of index, we have different implementations. + if (options.type_is("location")) { + // index id -> location + const auto index = create(options.dense_format(), fd); + return run(*index, options); + } else if (options.type_is("id")) { + // index id -> id + const auto index = create(options.dense_format(), fd); + return run(*index, options); + } else { + // index id -> offset + const auto index = create(options.dense_format(), fd); + return run(*index, options); + } +} + diff --git a/examples/osmium_pub_names.cpp b/examples/osmium_pub_names.cpp old mode 100755 new mode 100644 diff --git a/examples/osmium_road_length.cpp b/examples/osmium_road_length.cpp old mode 100755 new mode 100644 diff --git a/examples/osmium_serdump.cpp b/examples/osmium_serdump.cpp deleted file mode 100644 index 81a6d0cc5..000000000 --- a/examples/osmium_serdump.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - - This is a small tool to dump the contents of the input file - in serialized format to stdout. - - The code in this example file is released into the Public Domain. - -*/ - -#include -#include -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -# include -#endif - -#include -#include -#include - -#include -#include -#include -#include - -// ============================================================================== -// Choose the following depending on the size of the input OSM files: -// ============================================================================== -// for smaller OSM files (extracts) -typedef osmium::index::map::SparseMemArray offset_index_type; -//typedef osmium::index::map::SparseMapMmap offset_index_type; -//typedef osmium::index::map::SparseMapFile offset_index_type; - -typedef osmium::index::multimap::SparseMemArray map_type; -//typedef osmium::index::multimap::SparseMemMultimap map_type; -//typedef osmium::index::multimap::Hybrid map_type; - -// ============================================================================== -// for very large OSM files (planet) -//typedef osmium::index::map::DenseMmapArray offset_index_type; -// ============================================================================== - -void print_help() { - std::cout << "osmium_serdump OSMFILE DIR\n" \ - << "Serialize content of OSMFILE into data file in DIR.\n" \ - << "\nOptions:\n" \ - << " -h, --help This help message\n"; -} - -int main(int argc, char* argv[]) { - std::ios_base::sync_with_stdio(false); - - static struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {0, 0, 0, 0} - }; - - while (true) { - int c = getopt_long(argc, argv, "h", long_options, 0); - if (c == -1) { - break; - } - - switch (c) { - case 'h': - print_help(); - std::exit(0); - default: - std::exit(2); - } - } - - int remaining_args = argc - optind; - - if (remaining_args != 2) { - std::cerr << "Usage: " << argv[0] << " OSMFILE DIR\n"; - std::exit(2); - } - - std::string dir(argv[optind+1]); -#ifndef _WIN32 - int result = ::mkdir(dir.c_str(), 0777); -#else - int result = mkdir(dir.c_str()); -#endif - if (result == -1 && errno != EEXIST) { - std::cerr << "Problem creating directory '" << dir << "': " << strerror(errno) << "\n"; - std::exit(2); - } - - std::string data_file(dir + "/data.osm.ser"); - 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 << "': " << strerror(errno) << "\n"; - std::exit(2); - } - - offset_index_type node_index; - offset_index_type way_index; - offset_index_type relation_index; - - osmium::handler::DiskStore disk_store_handler(data_fd, node_index, way_index, relation_index); - - map_type map_node2way; - map_type map_node2relation; - map_type map_way2relation; - map_type map_relation2relation; - - osmium::handler::ObjectRelations object_relations_handler(map_node2way, map_node2relation, map_way2relation, map_relation2relation); - - osmium::io::Reader reader(argv[1]); - - while (osmium::memory::Buffer buffer = reader.read()) { - disk_store_handler(buffer); // XXX - osmium::apply(buffer, object_relations_handler); - } - - reader.close(); - - { - std::string index_file(dir + "/nodes.idx"); - int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) { - std::cerr << "Can't open nodes index file '" << index_file << "': " << strerror(errno) << "\n"; - std::exit(2); - } - node_index.dump_as_list(fd); - close(fd); - } - - { - std::string index_file(dir + "/ways.idx"); - int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) { - std::cerr << "Can't open ways index file '" << index_file << "': " << strerror(errno) << "\n"; - std::exit(2); - } - way_index.dump_as_list(fd); - close(fd); - } - - { - std::string index_file(dir + "/relations.idx"); - int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) { - std::cerr << "Can't open relations index file '" << index_file << "': " << strerror(errno) << "\n"; - std::exit(2); - } - relation_index.dump_as_list(fd); - close(fd); - } - - { - map_node2way.sort(); - std::string index_file(dir + "/node2way.map"); - int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) { - std::cerr << "Can't open node->way map file '" << index_file << "': " << strerror(errno) << "\n"; - std::exit(2); - } - map_node2way.dump_as_list(fd); - close(fd); - } - - { - map_node2relation.sort(); - std::string index_file(dir + "/node2rel.map"); - int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) { - std::cerr << "Can't open node->rel map file '" << index_file << "': " << strerror(errno) << "\n"; - std::exit(2); - } - map_node2relation.dump_as_list(fd); - close(fd); - } - - { - map_way2relation.sort(); - std::string index_file(dir + "/way2rel.map"); - int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) { - std::cerr << "Can't open way->rel map file '" << index_file << "': " << strerror(errno) << "\n"; - std::exit(2); - } - map_way2relation.dump_as_list(fd); - close(fd); - } - - { - map_relation2relation.sort(); - std::string index_file(dir + "/rel2rel.map"); - int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (fd < 0) { - std::cerr << "Can't open rel->rel map file '" << index_file << "': " << strerror(errno) << "\n"; - std::exit(2); - } - map_relation2relation.dump_as_list(fd); - close(fd); - } -} - diff --git a/include/osmium/area/assembler.hpp b/include/osmium/area/assembler.hpp index d5bf8d8da..092f4b4c7 100644 --- a/include/osmium/area/assembler.hpp +++ b/include/osmium/area/assembler.hpp @@ -193,12 +193,12 @@ namespace osmium { }; // struct location_to_ring_map - inline bool operator==(const location_to_ring_map& a, const location_to_ring_map& b) noexcept { - return a.location == b.location; + inline bool operator==(const location_to_ring_map& lhs, const location_to_ring_map& rhs) noexcept { + return lhs.location == rhs.location; } - inline bool operator<(const location_to_ring_map& a, const location_to_ring_map& b) noexcept { - return a.location < b.location; + inline bool operator<(const location_to_ring_map& lhs, const location_to_ring_map& rhs) noexcept { + return lhs.location < rhs.location; } } // namespace detail @@ -288,7 +288,7 @@ namespace osmium { } void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Way& way) const { - builder.add_item(&way.tags()); + builder.add_item(way.tags()); } void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set& ways) const { @@ -333,7 +333,7 @@ namespace osmium { } static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) { - osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder); + 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()); @@ -354,7 +354,7 @@ namespace osmium { } if (m_config.keep_type_tag) { - builder.add_item(&relation.tags()); + builder.add_item(relation.tags()); } else { copy_tags_without_type(builder, relation.tags()); } @@ -373,12 +373,12 @@ namespace osmium { if (debug()) { std::cerr << " only one outer way\n"; } - builder.add_item(&(*ways.cbegin())->tags()); + builder.add_item((*ways.cbegin())->tags()); } else { if (debug()) { std::cerr << " multiple outer ways, get common tags\n"; } - osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder); + osmium::builder::TagListBuilder tl_builder{builder}; add_common_tags(tl_builder, ways); } } @@ -386,7 +386,7 @@ namespace osmium { template static void build_ring_from_proto_ring(osmium::builder::AreaBuilder& builder, const detail::ProtoRing& ring) { - TBuilder ring_builder(builder.buffer(), &builder); + TBuilder ring_builder{builder}; ring_builder.add_node_ref(ring.get_node_ref_start()); for (const auto& segment : ring.segments()) { ring_builder.add_node_ref(segment->stop()); @@ -458,8 +458,8 @@ namespace osmium { } detail::NodeRefSegment* get_next_segment(const osmium::Location& location) { - auto it = std::lower_bound(m_locations.begin(), m_locations.end(), slocation{}, [this, &location](const slocation& a, const slocation& b) { - return a.location(m_segment_list, location) < b.location(m_segment_list, location); + auto it = std::lower_bound(m_locations.begin(), m_locations.end(), slocation{}, [this, &location](const slocation& lhs, const slocation& rhs) { + return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); }); assert(it != m_locations.end()); @@ -744,8 +744,8 @@ namespace osmium { m_locations.emplace_back(n, true); } - std::stable_sort(m_locations.begin(), m_locations.end(), [this](const slocation& a, const slocation& b) { - return a.location(m_segment_list) < b.location(m_segment_list); + std::stable_sort(m_locations.begin(), m_locations.end(), [this](const slocation& lhs, const slocation& rhs) { + return lhs.location(m_segment_list) < rhs.location(m_segment_list); }); } @@ -1015,8 +1015,8 @@ namespace osmium { std::vector xrings = create_location_to_ring_map(open_ring_its); - const auto ring_min = std::min_element(xrings.begin(), xrings.end(), [](const location_to_ring_map& a, const location_to_ring_map& b) { - return a.ring().min_segment() < b.ring().min_segment(); + const auto ring_min = std::min_element(xrings.begin(), xrings.end(), [](const location_to_ring_map& lhs, const location_to_ring_map& rhs) { + return lhs.ring().min_segment() < rhs.ring().min_segment(); }); find_inner_outer_complex(); @@ -1068,11 +1068,11 @@ namespace osmium { // Find the candidate with the smallest/largest area const auto chosen_cand = ring_min_is_outer ? - std::min_element(candidates.cbegin(), candidates.cend(), [](const candidate& a, const candidate& b) { - return std::abs(a.sum) < std::abs(b.sum); + std::min_element(candidates.cbegin(), candidates.cend(), [](const candidate& lhs, const candidate& rhs) { + return std::abs(lhs.sum) < std::abs(rhs.sum); }) : - std::max_element(candidates.cbegin(), candidates.cend(), [](const candidate& a, const candidate& b) { - return std::abs(a.sum) < std::abs(b.sum); + std::max_element(candidates.cbegin(), candidates.cend(), [](const candidate& lhs, const candidate& rhs) { + return std::abs(lhs.sum) < std::abs(rhs.sum); }); if (debug()) { @@ -1103,8 +1103,8 @@ namespace osmium { const auto locs = make_range(std::equal_range(m_locations.begin(), m_locations.end(), slocation{}, - [this, &location](const slocation& a, const slocation& b) { - return a.location(m_segment_list, location) < b.location(m_segment_list, location); + [this, &location](const slocation& lhs, const slocation& rhs) { + return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); })); for (auto& loc : locs) { if (!m_segment_list[loc.item].is_done()) { @@ -1267,8 +1267,8 @@ namespace osmium { } for (const auto& location : m_split_locations) { if (m_config.problem_reporter) { - auto it = std::lower_bound(m_locations.cbegin(), m_locations.cend(), slocation{}, [this, &location](const slocation& a, const slocation& b) { - return a.location(m_segment_list, location) < b.location(m_segment_list, location); + auto it = std::lower_bound(m_locations.cbegin(), m_locations.cend(), slocation{}, [this, &location](const slocation& lhs, const slocation& rhs) { + return lhs.location(m_segment_list, location) < rhs.location(m_segment_list, location); }); assert(it != m_locations.cend()); const osmium::object_id_type id = it->node_ref(m_segment_list).ref(); @@ -1362,7 +1362,7 @@ namespace osmium { #endif bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) { - osmium::builder::AreaBuilder builder(out_buffer); + osmium::builder::AreaBuilder builder{out_buffer}; builder.initialize_from_object(way); const bool area_okay = create_rings(); @@ -1382,7 +1382,7 @@ namespace osmium { bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector& members) { m_num_members = members.size(); - osmium::builder::AreaBuilder builder(out_buffer); + osmium::builder::AreaBuilder builder{out_buffer}; builder.initialize_from_object(relation); const bool area_okay = create_rings(); diff --git a/include/osmium/area/detail/node_ref_segment.hpp b/include/osmium/area/detail/node_ref_segment.hpp index b131a4317..1c03d3e52 100644 --- a/include/osmium/area/detail/node_ref_segment.hpp +++ b/include/osmium/area/detail/node_ref_segment.hpp @@ -379,8 +379,8 @@ namespace osmium { sl[2] = {1, s2.first().location() }; sl[3] = {1, s2.second().location()}; - std::sort(sl, sl+4, [](const seg_loc& a, const seg_loc& b) { - return a.location < b.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) { diff --git a/include/osmium/area/detail/segment_list.hpp b/include/osmium/area/detail/segment_list.hpp index a4361e0e1..97d512a10 100644 --- a/include/osmium/area/detail/segment_list.hpp +++ b/include/osmium/area/detail/segment_list.hpp @@ -101,7 +101,7 @@ namespace osmium { * Calculate the number of segments in all the ways together. */ static size_t get_num_segments(const std::vector& members) noexcept { - return std::accumulate(members.cbegin(), members.cend(), 0, [](size_t sum, const osmium::Way* way) { + return std::accumulate(members.cbegin(), members.cend(), static_cast(0), [](size_t sum, const osmium::Way* way) { if (way->nodes().empty()) { return sum; } else { diff --git a/include/osmium/area/detail/vector.hpp b/include/osmium/area/detail/vector.hpp index 44983cc75..fae128069 100644 --- a/include/osmium/area/detail/vector.hpp +++ b/include/osmium/area/detail/vector.hpp @@ -73,18 +73,18 @@ namespace osmium { }; // struct vec // addition - constexpr inline vec operator+(const vec& a, const vec& b) noexcept { - return vec{a.x + b.x, a.y + b.y}; + 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& a, const vec& b) noexcept { - return vec{a.x - b.x, a.y - b.y}; + 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& a, const vec& b) noexcept { - return a.x * b.y - a.y * b.x; + constexpr inline int64_t operator*(const vec& lhs, const vec& rhs) noexcept { + return lhs.x * rhs.y - lhs.y * rhs.x; } // scale vector @@ -98,13 +98,13 @@ namespace osmium { } // equality - constexpr inline bool operator==(const vec& a, const vec& b) noexcept { - return a.x == b.x && a.y == b.y; + 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& a, const vec& b) noexcept { - return !(a == b); + constexpr inline bool operator!=(const vec& lhs, const vec& rhs) noexcept { + return !(lhs == rhs); } template diff --git a/include/osmium/builder/attr.hpp b/include/osmium/builder/attr.hpp index 2a5b69070..8e0b4a3df 100644 --- a/include/osmium/builder/attr.hpp +++ b/include/osmium/builder/attr.hpp @@ -617,7 +617,7 @@ namespace osmium { template inline void add_user(TBuilder& builder, const TArgs&... args) { - builder.add_user(get_user(args...)); + builder.set_user(get_user(args...)); } // ============================================================== @@ -761,11 +761,13 @@ namespace osmium { static_assert(sizeof...(args) > 0, "add_node() must have buffer and at least one additional argument"); static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_node()"); - NodeBuilder builder(buffer); + { + NodeBuilder builder(buffer); - detail::add_basic(builder, args...); - detail::add_user(builder, args...); - detail::add_list(builder, args...); + detail::add_basic(builder, args...); + detail::add_user(builder, args...); + detail::add_list(builder, args...); + } return buffer.commit(); } @@ -782,12 +784,14 @@ namespace osmium { static_assert(sizeof...(args) > 0, "add_way() must have buffer and at least one additional argument"); static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_way()"); - WayBuilder builder(buffer); + { + WayBuilder builder(buffer); - detail::add_basic(builder, args...); - detail::add_user(builder, args...); - detail::add_list(builder, args...); - detail::add_list(builder, args...); + detail::add_basic(builder, args...); + detail::add_user(builder, args...); + detail::add_list(builder, args...); + detail::add_list(builder, args...); + } return buffer.commit(); } @@ -804,12 +808,14 @@ namespace osmium { static_assert(sizeof...(args) > 0, "add_relation() must have buffer and at least one additional argument"); static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_relation()"); - RelationBuilder builder(buffer); + { + RelationBuilder builder(buffer); - detail::add_basic(builder, args...); - detail::add_user(builder, args...); - detail::add_list(builder, args...); - detail::add_list(builder, args...); + detail::add_basic(builder, args...); + detail::add_user(builder, args...); + detail::add_list(builder, args...); + detail::add_list(builder, args...); + } return buffer.commit(); } @@ -826,12 +832,14 @@ namespace osmium { static_assert(sizeof...(args) > 0, "add_changeset() must have buffer and at least one additional argument"); static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_changeset()"); - ChangesetBuilder builder(buffer); + { + ChangesetBuilder builder(buffer); - detail::add_basic(builder, args...); - detail::add_user(builder, args...); - detail::add_list(builder, args...); - detail::add_list(builder, args...); + detail::add_basic(builder, args...); + detail::add_user(builder, args...); + detail::add_list(builder, args...); + detail::add_list(builder, args...); + } return buffer.commit(); } @@ -848,15 +856,17 @@ namespace osmium { static_assert(sizeof...(args) > 0, "add_area() must have buffer and at least one additional argument"); static_assert(detail::are_all_handled_by::value, "Attribute not allowed in add_area()"); - AreaBuilder builder(buffer); + { + AreaBuilder builder(buffer); - detail::add_basic(builder, args...); - detail::add_user(builder, args...); - detail::add_list(builder, args...); + detail::add_basic(builder, args...); + detail::add_user(builder, args...); + detail::add_list(builder, args...); - (void)std::initializer_list{ - (detail::ring_handler::set_value(builder, args), 0)... - }; + (void)std::initializer_list{ + (detail::ring_handler::set_value(builder, args), 0)... + }; + } return buffer.commit(); } diff --git a/include/osmium/builder/builder.hpp b/include/osmium/builder/builder.hpp index 1b274ada5..044da1e9d 100644 --- a/include/osmium/builder/builder.hpp +++ b/include/osmium/builder/builder.hpp @@ -45,6 +45,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { @@ -53,6 +54,10 @@ namespace osmium { */ namespace builder { + /** + * Parent class for individual builder classes. Instantiate one of + * its derived classes. + */ class Builder { osmium::memory::Buffer& m_buffer; @@ -71,20 +76,34 @@ namespace osmium { m_buffer(buffer), m_parent(parent), m_item_offset(buffer.written()) { - m_buffer.reserve_space(size); + 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(m_buffer.data() + m_item_offset); } - public: + unsigned char* reserve_space(size_t size) { + return m_buffer.reserve_space(size); + } /** * Add padding to buffer (if needed) to align data properly. @@ -102,7 +121,7 @@ namespace osmium { void add_padding(bool self = false) { const auto padding = osmium::memory::align_bytes - (size() % osmium::memory::align_bytes); if (padding != osmium::memory::align_bytes) { - std::fill_n(m_buffer.reserve_space(padding), padding, 0); + std::fill_n(reserve_space(padding), padding, 0); if (self) { add_size(padding); } else if (m_parent) { @@ -123,12 +142,6 @@ namespace osmium { return item().byte_size(); } - void add_item(const osmium::memory::Item* item) { - unsigned char* target = m_buffer.reserve_space(item->padded_size()); - std::copy_n(reinterpret_cast(item), item->padded_size(), target); - add_size(item->padded_size()); - } - /** * Reserve space for an object of class T in buffer and return * pointer to it. @@ -136,7 +149,7 @@ namespace osmium { template T* reserve_space_for() { assert(m_buffer.is_aligned()); - return reinterpret_cast(m_buffer.reserve_space(sizeof(T))); + return reinterpret_cast(reserve_space(sizeof(T))); } /** @@ -149,7 +162,7 @@ namespace osmium { * @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 = m_buffer.reserve_space(length); + unsigned char* target = reserve_space(length); std::copy_n(reinterpret_cast(data), length, target); return length; } @@ -170,65 +183,37 @@ namespace osmium { * @returns The number of bytes appended (always 1). */ osmium::memory::item_size_type append_zero() { - *m_buffer.reserve_space(1) = '\0'; + *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 - template - class ObjectBuilder : public Builder { - - static_assert(std::is_base_of::value, "ObjectBuilder can only build objects derived from osmium::memory::Item"); - - public: - - explicit ObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : - Builder(buffer, parent, sizeof(TItem)) { - new (&item()) TItem(); - } - - TItem& object() noexcept { - return static_cast(item()); - } - - /** - * Add user name to buffer. - * - * @param user Pointer to user name. - * @param length Length of user name (without \0 termination). - */ - void add_user(const char* user, const string_size_type length) { - object().set_user_size(length + 1); - add_size(append(user, length) + append_zero()); - add_padding(true); - } - - /** - * Add user name to buffer. - * - * @param user Pointer to \0-terminated user name. - */ - void add_user(const char* user) { - add_user(user, static_cast_with_assert(std::strlen(user))); - } - - /** - * Add user name to buffer. - * - * @param user User name. - */ - void add_user(const std::string& user) { - add_user(user.data(), static_cast_with_assert(user.size())); - } - - }; // class ObjectBuilder - } // namespace builder } // namespace osmium diff --git a/include/osmium/builder/osm_object_builder.hpp b/include/osmium/builder/osm_object_builder.hpp index e7a82988b..b1c7220a7 100644 --- a/include/osmium/builder/osm_object_builder.hpp +++ b/include/osmium/builder/osm_object_builder.hpp @@ -45,6 +45,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { @@ -66,12 +68,18 @@ namespace osmium { namespace builder { - class TagListBuilder : public ObjectBuilder { + class TagListBuilder : public Builder { public: explicit TagListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : - ObjectBuilder(buffer, parent) { + Builder(buffer, parent, sizeof(TagList)) { + new (&item()) TagList(); + } + + explicit TagListBuilder(Builder& parent) : + Builder(parent.buffer(), &parent, sizeof(TagList)) { + new (&item()) TagList(); } ~TagListBuilder() { @@ -169,21 +177,27 @@ namespace osmium { }; // class TagListBuilder template - class NodeRefListBuilder : public ObjectBuilder { + class NodeRefListBuilder : public Builder { public: explicit NodeRefListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : - ObjectBuilder(buffer, parent) { + Builder(buffer, parent, sizeof(T)) { + new (&item()) T(); + } + + explicit NodeRefListBuilder(Builder& parent) : + Builder(parent.buffer(), &parent, sizeof(T)) { + new (&item()) T(); } ~NodeRefListBuilder() { - static_cast(this)->add_padding(); + add_padding(); } void add_node_ref(const NodeRef& node_ref) { - new (static_cast(this)->reserve_space_for()) osmium::NodeRef(node_ref); - static_cast(this)->add_size(sizeof(osmium::NodeRef)); + new (reserve_space_for()) osmium::NodeRef(node_ref); + add_size(sizeof(osmium::NodeRef)); } void add_node_ref(const object_id_type ref, const osmium::Location& location = Location{}) { @@ -196,7 +210,7 @@ namespace osmium { using OuterRingBuilder = NodeRefListBuilder; using InnerRingBuilder = NodeRefListBuilder; - class RelationMemberListBuilder : public ObjectBuilder { + class RelationMemberListBuilder : public Builder { /** * Add role to buffer. @@ -219,7 +233,13 @@ namespace osmium { public: explicit RelationMemberListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : - ObjectBuilder(buffer, parent) { + Builder(buffer, parent, sizeof(RelationMemberList)) { + new (&item()) RelationMemberList(); + } + + explicit RelationMemberListBuilder(Builder& parent) : + Builder(parent.buffer(), &parent, sizeof(RelationMemberList)) { + new (&item()) RelationMemberList(); } ~RelationMemberListBuilder() { @@ -245,7 +265,7 @@ namespace osmium { add_size(sizeof(RelationMember)); add_role(*member, role, role_length); if (full_member) { - add_item(full_member); + add_item(*full_member); } } @@ -281,7 +301,7 @@ namespace osmium { }; // class RelationMemberListBuilder - class ChangesetDiscussionBuilder : public ObjectBuilder { + class ChangesetDiscussionBuilder : public Builder { osmium::ChangesetComment* m_comment = nullptr; @@ -309,7 +329,13 @@ namespace osmium { public: explicit ChangesetDiscussionBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : - ObjectBuilder(buffer, parent) { + Builder(buffer, parent, sizeof(ChangesetDiscussion)) { + new (&item()) ChangesetDiscussion(); + } + + explicit ChangesetDiscussionBuilder(Builder& parent) : + Builder(parent.buffer(), &parent, sizeof(ChangesetDiscussion)) { + new (&item()) ChangesetDiscussion(); } ~ChangesetDiscussionBuilder() { @@ -339,19 +365,101 @@ namespace osmium { }; // class ChangesetDiscussionBuilder - template - class OSMObjectBuilder : public ObjectBuilder { +#define OSMIUM_FORWARD(setter) \ + template \ + type& setter(TArgs&&... args) { \ + object().setter(std::forward(args)...); \ + return static_cast(*this); \ + } + + template + class OSMObjectBuilder : public Builder { + + using type = TDerived; + + constexpr static const 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) : - ObjectBuilder(buffer, parent) { - static_cast(this)->reserve_space_for(); - static_cast(this)->add_size(sizeof(string_size_type)); + 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(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(object().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); + reserve_space(space_needed); + add_size(static_cast(space_needed)); + } + std::copy_n(user, length, object().data() + size_of_object); + std::fill_n(object().data() + size_of_object + length, osmium::memory::padded_length(length + 1) - length, 0); + object().set_user_size(length + 1); + + return static_cast(*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(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(user.size())); + } + + /// @deprecated Use set_user(...) instead. + template + OSMIUM_DEPRECATED void add_user(TArgs&&... args) { + set_user(std::forward(args)...); + } + + OSMIUM_FORWARD(set_id) + OSMIUM_FORWARD(set_visible) + OSMIUM_FORWARD(set_deleted) + 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>& tags) { - osmium::builder::TagListBuilder tl_builder(static_cast(this)->buffer(), this); + osmium::builder::TagListBuilder tl_builder{buffer(), this}; for (const auto& p : tags) { tl_builder.add_tag(p.first, p.second); } @@ -359,19 +467,40 @@ namespace osmium { }; // class OSMObjectBuilder - using NodeBuilder = OSMObjectBuilder; - using RelationBuilder = OSMObjectBuilder; + class NodeBuilder : public OSMObjectBuilder { - class WayBuilder : public OSMObjectBuilder { + using type = NodeBuilder; + + public: + + explicit NodeBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : + OSMObjectBuilder(buffer, parent) { + } + + explicit NodeBuilder(Builder& parent) : + OSMObjectBuilder(parent.buffer(), &parent) { + } + + OSMIUM_FORWARD(set_location) + + }; // class NodeBuilder + + class WayBuilder : public OSMObjectBuilder { + + using type = WayBuilder; public: explicit WayBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : - OSMObjectBuilder(buffer, parent) { + OSMObjectBuilder(buffer, parent) { + } + + explicit WayBuilder(Builder& parent) : + OSMObjectBuilder(parent.buffer(), &parent) { } void add_node_refs(const std::initializer_list& nodes) { - osmium::builder::WayNodeListBuilder builder(buffer(), this); + osmium::builder::WayNodeListBuilder builder{buffer(), this}; for (const auto& node_ref : nodes) { builder.add_node_ref(node_ref); } @@ -379,32 +508,147 @@ namespace osmium { }; // class WayBuilder - class AreaBuilder : public OSMObjectBuilder { + class RelationBuilder : public OSMObjectBuilder { + + using type = RelationBuilder; + + public: + + explicit RelationBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : + OSMObjectBuilder(buffer, parent) { + } + + explicit RelationBuilder(Builder& parent) : + OSMObjectBuilder(parent.buffer(), &parent) { + } + + }; // class RelationBuilder + + class AreaBuilder : public OSMObjectBuilder { + + using type = AreaBuilder; public: explicit AreaBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : - OSMObjectBuilder(buffer, parent) { + OSMObjectBuilder(buffer, parent) { + } + + explicit AreaBuilder(Builder& parent) : + OSMObjectBuilder(parent.buffer(), &parent) { } /** * Initialize area attributes from the attributes of the given object. */ void initialize_from_object(const osmium::OSMObject& source) { - osmium::Area& area = object(); - area.set_id(osmium::object_id_to_area_id(source.id(), source.type())); - area.set_version(source.version()); - area.set_changeset(source.changeset()); - area.set_timestamp(source.timestamp()); - area.set_visible(source.visible()); - area.set_uid(source.uid()); - - add_user(source.user()); + 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 - using ChangesetBuilder = ObjectBuilder; + class ChangesetBuilder : public Builder { + + using type = ChangesetBuilder; + + constexpr static const 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(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(object().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); + reserve_space(space_needed); + add_size(static_cast(space_needed)); + } + std::copy_n(user, length, object().data() + sizeof(Changeset)); + std::fill_n(object().data() + sizeof(Changeset) + length, osmium::memory::padded_length(length + 1) - length, 0); + 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(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(user.size())); + } + + /// @deprecated Use set_user(...) instead. + template + OSMIUM_DEPRECATED void add_user(TArgs&&... args) { + set_user(std::forward(args)...); + } + + }; // class ChangesetBuilder + +#undef OSMIUM_FORWARD } // namespace builder diff --git a/include/osmium/geom/factory.hpp b/include/osmium/geom/factory.hpp index 14c51dfdf..f2db40229 100644 --- a/include/osmium/geom/factory.hpp +++ b/include/osmium/geom/factory.hpp @@ -95,7 +95,7 @@ namespace osmium { return m_message.c_str(); } - }; // struct geometry_error + }; // class geometry_error /** * @brief Everything related to geometry handling. diff --git a/include/osmium/geom/geos.hpp b/include/osmium/geom/geos.hpp index f406076a1..d59e7cebc 100644 --- a/include/osmium/geom/geos.hpp +++ b/include/osmium/geom/geos.hpp @@ -33,12 +33,22 @@ DEALINGS IN THE SOFTWARE. */ +#include +#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 GDAL + * 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`. */ @@ -88,6 +98,7 @@ namespace osmium { namespace detail { + /// @deprecated class GEOSFactoryImpl { std::unique_ptr m_precision_model; @@ -245,6 +256,7 @@ namespace osmium { } // namespace detail + /// @deprecated template using GEOSFactory = GeometryFactory; @@ -254,4 +266,6 @@ namespace osmium { #undef THROW +#endif + #endif // OSMIUM_GEOM_GEOS_HPP diff --git a/include/osmium/geom/relations.hpp b/include/osmium/geom/relations.hpp index 76d452e4e..5e6e773f5 100644 --- a/include/osmium/geom/relations.hpp +++ b/include/osmium/geom/relations.hpp @@ -43,11 +43,11 @@ namespace osmium { /** * Check whether one geometry contains another. */ - inline bool contains(const osmium::Box& a, const osmium::Box& b) { - return ((a.bottom_left().x() >= b.bottom_left().x()) && - (a.top_right().x() <= b.top_right().x()) && - (a.bottom_left().y() >= b.bottom_left().y()) && - (a.top_right().y() <= b.top_right().y())); + inline bool contains(const osmium::Box& lhs, const osmium::Box& rhs) { + 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())); } } // namespace geom diff --git a/include/osmium/geom/tile.hpp b/include/osmium/geom/tile.hpp index 672ae5416..2c3301726 100644 --- a/include/osmium/geom/tile.hpp +++ b/include/osmium/geom/tile.hpp @@ -118,23 +118,23 @@ namespace osmium { }; // struct Tile /// Tiles are equal if all their attributes are equal. - inline bool operator==(const Tile& a, const Tile& b) { - return a.z == b.z && a.x == b.x && a.y == b.y; + inline bool operator==(const Tile& lhs, const Tile& rhs) { + return lhs.z == rhs.z && lhs.x == rhs.x && lhs.y == rhs.y; } - inline bool operator!=(const Tile& a, const Tile& b) { - return ! (a == b); + inline bool operator!=(const Tile& lhs, const Tile& rhs) { + return ! (lhs == rhs); } /** * This defines an arbitrary order on tiles for use in std::map etc. */ - inline bool operator<(const Tile& a, const Tile& b) { - if (a.z < b.z) return true; - if (a.z > b.z) return false; - if (a.x < b.x) return true; - if (a.x > b.x) return false; - return a.y < b.y; + inline bool operator<(const Tile& lhs, const Tile& rhs) { + if (lhs.z < rhs.z) return true; + if (lhs.z > rhs.z) return false; + if (lhs.x < rhs.x) return true; + if (lhs.x > rhs.x) return false; + return lhs.y < rhs.y; } } // namespace geom diff --git a/include/osmium/handler/disk_store.hpp b/include/osmium/handler/disk_store.hpp index 4e955734a..d112ac08b 100644 --- a/include/osmium/handler/disk_store.hpp +++ b/include/osmium/handler/disk_store.hpp @@ -51,6 +51,9 @@ namespace osmium { namespace handler { /** + * Writes OSM data in the Osmium-internal serialized format to disk + * keeping track of object offsets in the indexes given to the + * constructor. * * Note: This handler will only work if either all object IDs are * positive or all object IDs are negative. @@ -95,10 +98,8 @@ namespace osmium { m_offset += relation.byte_size(); } - // XXX void operator()(const osmium::memory::Buffer& buffer) { osmium::io::detail::reliable_write(m_data_fd, buffer.data(), buffer.committed()); - osmium::apply(buffer.begin(), buffer.end(), *this); } diff --git a/include/osmium/handler/node_locations_for_ways.hpp b/include/osmium/handler/node_locations_for_ways.hpp index a490f9e28..61c6de209 100644 --- a/include/osmium/handler/node_locations_for_ways.hpp +++ b/include/osmium/handler/node_locations_for_ways.hpp @@ -165,7 +165,7 @@ namespace osmium { } } if (error && !m_ignore_errors) { - throw osmium::not_found("location for one or more nodes not found in node location index"); + throw osmium::not_found{"location for one or more nodes not found in node location index"}; } } diff --git a/include/osmium/handler/object_relations.hpp b/include/osmium/handler/object_relations.hpp index 279365d08..5f1956d75 100644 --- a/include/osmium/handler/object_relations.hpp +++ b/include/osmium/handler/object_relations.hpp @@ -46,6 +46,8 @@ namespace osmium { namespace handler { /** + * This handler updates the indexes given to the constructor with + * the relations between objects. * * Note: This handler will only work if either all object IDs are * positive or all object IDs are negative. diff --git a/include/osmium/index/bool_vector.hpp b/include/osmium/index/bool_vector.hpp index e6e190e27..2ef80f9eb 100644 --- a/include/osmium/index/bool_vector.hpp +++ b/include/osmium/index/bool_vector.hpp @@ -33,50 +33,15 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include +#include namespace osmium { namespace index { - /** - * Index storing one bit for each Id. The index automatically scales - * with the Ids stored. Default value is 'false'. Storage uses - * std::vector and needs a minimum of memory if the Ids are - * dense. - */ + /// @deprecated Use osmium::index::IdSet instead. template - class BoolVector { - - static_assert(std::is_unsigned::value, "Needs unsigned type"); - - std::vector m_bits; - - public: - - BoolVector() = default; - - BoolVector(const BoolVector&) = default; - BoolVector(BoolVector&&) = default; - BoolVector& operator=(const BoolVector&) = default; - BoolVector& operator=(BoolVector&&) = default; - - ~BoolVector() noexcept = default; - - void set(T id, bool value = true) { - if (m_bits.size() <= id) { - m_bits.resize(id + 1024 * 1024); - } - - m_bits[id] = value; - } - - bool get(T id) const { - return id < m_bits.size() && m_bits[id]; - } - - }; // class BoolVector + using BoolVector = IdSet; } // namespace index diff --git a/include/osmium/index/detail/vector_map.hpp b/include/osmium/index/detail/vector_map.hpp index ac87c2fc9..9f4af1f62 100644 --- a/include/osmium/index/detail/vector_map.hpp +++ b/include/osmium/index/detail/vector_map.hpp @@ -85,11 +85,11 @@ namespace osmium { try { const TValue& value = m_vector.at(id); if (value == osmium::index::empty_value()) { - not_found_error(id); + throw osmium::not_found{id}; } return value; } catch (const std::out_of_range&) { - not_found_error(id); + throw osmium::not_found{id}; } } @@ -180,7 +180,7 @@ namespace osmium { return a.first < b.first; }); if (result == m_vector.end() || result->first != id) { - not_found_error(id); + throw osmium::not_found{id}; } else { return result->second; } diff --git a/include/osmium/index/id_set.hpp b/include/osmium/index/id_set.hpp new file mode 100644 index 000000000..4a894a0ba --- /dev/null +++ b/include/osmium/index/id_set.hpp @@ -0,0 +1,431 @@ +#ifndef OSMIUM_INDEX_ID_SET_HPP +#define OSMIUM_INDEX_ID_SET_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace osmium { + + namespace index { + + /** + * Virtual parent class for IdSets. Use one of the implementations + * provided. + */ + template + class IdSet { + + public: + + virtual ~IdSet() { + } + + /** + * Add the given Id to the set. + */ + virtual void set(T id) = 0; + + /** + * Is the Id in the set? + */ + virtual bool get(T id) const noexcept = 0; + + /** + * Is the set empty? + */ + virtual bool empty() const = 0; + + /** + * Clear the set. + */ + virtual void clear() = 0; + + }; // class IdSet + + template + class IdSetDense; + + /** + * Const_iterator for iterating over a IdSetDense. + */ + template + class IdSetDenseIterator { + + const IdSetDense* m_set; + T m_value; + T m_last; + + void next() noexcept { + while (m_value != m_last && !m_set->get(m_value)) { + const auto cid = IdSetDense::chunk_id(m_value); + assert(cid < m_set->m_data.size()); + if (!m_set->m_data[cid]) { + m_value = (cid + 1) << (IdSetDense::chunk_bits + 3); + } else { + const auto slot = m_set->m_data[cid][IdSetDense::offset(m_value)]; + if (slot == 0) { + m_value += 8; + m_value &= ~0x7; + } else { + ++m_value; + } + } + } + } + + public: + + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using pointer = value_type*; + using reference = value_type&; + + IdSetDenseIterator(const IdSetDense* set, T value, T last) noexcept : + m_set(set), + m_value(value), + m_last(last) { + next(); + } + + IdSetDenseIterator& operator++() noexcept { + if (m_value != m_last) { + ++m_value; + next(); + } + return *this; + } + + IdSetDenseIterator operator++(int) noexcept { + IdSetDenseIterator tmp(*this); + operator++(); + return tmp; + } + + bool operator==(const IdSetDenseIterator& rhs) const noexcept { + return m_set == rhs.m_set && m_value == rhs.m_value; + } + + bool operator!=(const IdSetDenseIterator& rhs) const noexcept { + return ! (*this == rhs); + } + + T operator*() const noexcept { + assert(m_value < m_last); + return m_value; + } + + }; // class IdSetDenseIterator + + /** + * A set of Ids of the given type. Internal storage is in chunks of + * arrays used as bit fields. Internally those chunks will be allocated + * as needed, so it works relatively efficiently with both smaller + * and larger Id sets. If it is not used, no memory is allocated at + * all. + */ + template + class IdSetDense : public IdSet { + + static_assert(std::is_unsigned::value, "Needs unsigned type"); + static_assert(sizeof(T) >= 4, "Needs at least 32bit type"); + + friend class IdSetDenseIterator; + + // This value is a compromise. For node Ids it could be bigger + // which would mean less (but larger) memory allocations. For + // relations Ids it could be smaller, because they would all fit + // into a smaller allocation. + constexpr static const size_t chunk_bits = 22; + constexpr static const size_t chunk_size = 1 << chunk_bits; + + std::vector> m_data; + size_t m_size = 0; + + static size_t chunk_id(T id) noexcept { + return id >> (chunk_bits + 3); + } + + static size_t offset(T id) noexcept { + return (id >> 3) & ((1 << chunk_bits) - 1); + } + + static unsigned char bitmask(T id) noexcept { + return 1 << (id & 0x7); + } + + T last() const noexcept { + return m_data.size() * chunk_size * 8; + } + + unsigned char& get_element(T id) { + const auto cid = chunk_id(id); + if (cid >= m_data.size()) { + m_data.resize(cid + 1); + } + + auto& chunk = m_data[cid]; + if (!chunk) { + chunk.reset(new unsigned char[chunk_size]); + ::memset(chunk.get(), 0, chunk_size); + } + + return chunk[offset(id)]; + } + + public: + + using const_iterator = IdSetDenseIterator; + + IdSetDense() = default; + + /** + * Add the Id to the set if it is not already in there. + * + * @param id The Id to set. + * @returns true if the Id was added, false if it was already set. + */ + bool check_and_set(T id) { + auto& element = get_element(id); + + if ((element & bitmask(id)) == 0) { + element |= bitmask(id); + ++m_size; + return true; + } + + return false; + } + + /** + * Add the given Id to the set. + * + * @param id The Id to set. + */ + void set(T id) override final { + (void)check_and_set(id); + } + + /** + * Remove the given Id from the set. + * + * @param id The Id to set. + */ + void unset(T id) { + auto& element = get_element(id); + + if ((element & bitmask(id)) != 0) { + element &= ~bitmask(id); + --m_size; + } + } + + /** + * Is the Id in the set? + * + * @param id The Id to check. + */ + bool get(T id) const noexcept override final { + if (chunk_id(id) >= m_data.size()) { + return false; + } + auto* r = m_data[chunk_id(id)].get(); + if (!r) { + return false; + } + return (r[offset(id)] & bitmask(id)) != 0; + } + + /** + * Is the set empty? + */ + bool empty() const noexcept override final { + return m_size == 0; + } + + /** + * The number of Ids stored in the set. + */ + size_t size() const noexcept { + return m_size; + } + + /** + * Clear the set. + */ + void clear() override final { + m_data.clear(); + m_size = 0; + } + + IdSetDenseIterator begin() const { + return IdSetDenseIterator{this, 0, last()}; + } + + IdSetDenseIterator end() const { + return IdSetDenseIterator{this, last(), last()}; + } + + }; // class IdSetDense + + /** + * IdSet implementation for small Id sets. It writes the Ids + * into a vector and uses linear search. + */ + template + class IdSetSmall : public IdSet { + + std::vector m_data; + + public: + + /** + * Add the given Id to the set. + */ + void set(T id) override final { + m_data.push_back(id); + } + + /** + * Is the Id in the set? Uses linear search. + * + * @param id The Id to check. + */ + bool get(T id) const noexcept override final { + const auto it = std::find(m_data.cbegin(), m_data.cend(), id); + return it != m_data.cend(); + } + + /** + * Is the Id in the set? Uses a binary search. For larger sets + * this might be more efficient than calling get(), the set + * must be sorted. + * + * @param id The Id to check. + * @pre You must have called sort_unique() before calling this + * or be sure there are no duplicates and the Ids have been + * set in order. + */ + bool get_binary_search(T id) const noexcept { + return std::binary_search(m_data.cbegin(), m_data.cend(), id); + } + + /** + * Is the set empty? + */ + bool empty() const noexcept override final { + return m_data.empty(); + } + + /** + * Clear the set. + */ + void clear() override final { + m_data.clear(); + } + + /** + * Sort the internal vector and remove any duplicates. Call this + * before using size(), get_binary_search() or using an iterator. + */ + void sort_unique() { + std::sort(m_data.begin(), m_data.end()); + const auto last = std::unique(m_data.begin(), m_data.end()); + m_data.erase(last, m_data.end()); + + } + + /** + * The number of Ids stored in the set. + * + * @pre You must have called sort_unique() before calling this + * or be sure there are no duplicates. + */ + size_t size() const noexcept { + return m_data.size(); + } + + /// Iterator type. There is no non-const iterator. + using const_iterator = typename std::vector::const_iterator; + + const_iterator begin() const noexcept { + return m_data.cbegin(); + } + + const_iterator end() const noexcept { + return m_data.cend(); + } + + const_iterator cbegin() const noexcept { + return m_data.cbegin(); + } + + const_iterator cend() const noexcept { + return m_data.cend(); + } + + }; // class IdSetSmall + + template class IdSetType> + class NWRIdSet { + + using id_set_type = IdSetType; + + id_set_type m_sets[3]; + + public: + + id_set_type& operator()(osmium::item_type type) noexcept { + return m_sets[osmium::item_type_to_nwr_index(type)]; + } + + const id_set_type& operator()(osmium::item_type type) const noexcept { + return m_sets[osmium::item_type_to_nwr_index(type)]; + } + + }; // class NWRIdSet + + } // namespace index + +} // namespace osmium + +#endif // OSMIUM_INDEX_ID_SET_HPP diff --git a/include/osmium/index/index.hpp b/include/osmium/index/index.hpp index c3deeadf8..65871f23e 100644 --- a/include/osmium/index/index.hpp +++ b/include/osmium/index/index.hpp @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include @@ -57,6 +56,10 @@ namespace osmium { std::runtime_error(what) { } + explicit not_found(uint64_t id) : + std::runtime_error(std::string{"id "} + std::to_string(id) + " not found") { + } + }; // struct not_found /** @@ -64,13 +67,6 @@ namespace osmium { */ namespace index { - template - OSMIUM_NORETURN void not_found_error(TKey key) { - std::stringstream s; - s << "id " << key << " not found"; - throw not_found(s.str()); - } - /** * Some of the index classes need an "empty" value that can * never appear in real data. This function must return this diff --git a/include/osmium/index/map.hpp b/include/osmium/index/map.hpp index 1d2d5aa34..d26a4aa4d 100644 --- a/include/osmium/index/map.hpp +++ b/include/osmium/index/map.hpp @@ -48,6 +48,18 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + struct map_factory_error : public std::runtime_error { + + explicit map_factory_error(const char* message) : + std::runtime_error(message) { + } + + explicit map_factory_error(const std::string& message) : + std::runtime_error(message) { + } + + }; // struct map_factory_error + namespace index { /** @@ -148,14 +160,14 @@ namespace osmium { // default implementation is empty } - // This function could usually be const in derived classes, + // This function can usually be const in derived classes, // but not always. It could, for instance, sort internal data. // This is why it is not declared const here. virtual void dump_as_list(const int /*fd*/) { throw std::runtime_error("can't dump as list"); } - // This function could usually be const in derived classes, + // This function can usually be const in derived classes, // but not always. It could, for instance, sort internal data. // This is why it is not declared const here. virtual void dump_as_array(const int /*fd*/) { @@ -188,13 +200,6 @@ namespace osmium { MapFactory(MapFactory&&) = delete; MapFactory& operator=(MapFactory&&) = delete; - OSMIUM_NORETURN static void error(const std::string& map_type_name) { - std::string error_message {"Support for map type '"}; - error_message += map_type_name; - error_message += "' not compiled into this binary."; - throw std::runtime_error(error_message); - } - public: static MapFactory& instance() { @@ -226,7 +231,7 @@ namespace osmium { std::vector config = osmium::split_string(config_string, ','); if (config.empty()) { - throw std::runtime_error("Need non-empty map type name."); + throw map_factory_error{"Need non-empty map type name"}; } auto it = m_callbacks.find(config[0]); @@ -234,7 +239,7 @@ namespace osmium { return std::unique_ptr((it->second)(config)); } - error(config[0]); + throw map_factory_error{std::string{"Support for map type '"} + config[0] + "' not compiled into this binary"}; } }; // class MapFactory diff --git a/include/osmium/index/map/dummy.hpp b/include/osmium/index/map/dummy.hpp index 98d082b82..529ad2539 100644 --- a/include/osmium/index/map/dummy.hpp +++ b/include/osmium/index/map/dummy.hpp @@ -63,7 +63,7 @@ namespace osmium { } const TValue get(const TId id) const final { - not_found_error(id); + throw osmium::not_found{id}; } size_t size() const final { diff --git a/include/osmium/index/map/sparse_mem_map.hpp b/include/osmium/index/map/sparse_mem_map.hpp index 1e3c58cc6..43fea3671 100644 --- a/include/osmium/index/map/sparse_mem_map.hpp +++ b/include/osmium/index/map/sparse_mem_map.hpp @@ -79,7 +79,7 @@ namespace osmium { const TValue get(const TId id) const final { auto it = m_elements.find(id); if (it == m_elements.end()) { - not_found_error(id); + throw osmium::not_found{id}; } return it->second; } diff --git a/include/osmium/index/map/sparse_mem_table.hpp b/include/osmium/index/map/sparse_mem_table.hpp index 241a64f97..68f57972f 100644 --- a/include/osmium/index/map/sparse_mem_table.hpp +++ b/include/osmium/index/map/sparse_mem_table.hpp @@ -99,10 +99,10 @@ namespace osmium { const TValue get(const TId id) const final { if (id >= m_elements.size()) { - not_found_error(id); + throw osmium::not_found{id}; } if (m_elements[id] == osmium::index::empty_value()) { - not_found_error(id); + throw osmium::not_found{id}; } return m_elements[id]; } diff --git a/include/osmium/io/compression.hpp b/include/osmium/io/compression.hpp index e7f93bd5e..a38bba64e 100644 --- a/include/osmium/io/compression.hpp +++ b/include/osmium/io/compression.hpp @@ -145,10 +145,11 @@ namespace osmium { private: - using compression_map_type = std::map>; + using callbacks_type = std::tuple; + + using compression_map_type = std::map; compression_map_type m_callbacks; @@ -160,11 +161,17 @@ namespace osmium { CompressionFactory(CompressionFactory&&) = delete; CompressionFactory& operator=(CompressionFactory&&) = delete; - OSMIUM_NORETURN void error(osmium::io::file_compression compression) { - std::string error_message {"Support for compression '"}; + const callbacks_type& find_callbacks(osmium::io::file_compression compression) const { + const auto it = m_callbacks.find(compression); + + if (it != m_callbacks.end()) { + return it->second; + } + + std::string error_message{"Support for compression '"}; error_message += as_string(compression); - error_message += "' not compiled into this binary."; - throw unsupported_file_format_error(error_message); + error_message += "' not compiled into this binary"; + throw unsupported_file_format_error{error_message}; } public: @@ -189,36 +196,21 @@ namespace osmium { } template - std::unique_ptr create_compressor(osmium::io::file_compression compression, TArgs&&... args) { - auto it = m_callbacks.find(compression); - - if (it != m_callbacks.end()) { - return std::unique_ptr(std::get<0>(it->second)(std::forward(args)...)); - } - - error(compression); + std::unique_ptr create_compressor(osmium::io::file_compression compression, TArgs&&... args) const { + const auto callbacks = find_callbacks(compression); + return std::unique_ptr(std::get<0>(callbacks)(std::forward(args)...)); } - std::unique_ptr create_decompressor(osmium::io::file_compression compression, int fd) { - auto it = m_callbacks.find(compression); - - if (it != m_callbacks.end()) { - auto p = std::unique_ptr(std::get<1>(it->second)(fd)); - p->set_file_size(osmium::util::file_size(fd)); - return p; - } - - error(compression); + std::unique_ptr create_decompressor(osmium::io::file_compression compression, int fd) const { + const auto callbacks = find_callbacks(compression); + auto p = std::unique_ptr(std::get<1>(callbacks)(fd)); + p->set_file_size(osmium::util::file_size(fd)); + return p; } - std::unique_ptr create_decompressor(osmium::io::file_compression compression, const char* buffer, size_t size) { - auto it = m_callbacks.find(compression); - - if (it != m_callbacks.end()) { - return std::unique_ptr(std::get<2>(it->second)(buffer, size)); - } - - error(compression); + std::unique_ptr create_decompressor(osmium::io::file_compression compression, const char* buffer, size_t size) const { + const auto callbacks = find_callbacks(compression); + return std::unique_ptr(std::get<2>(callbacks)(buffer, size)); } }; // class CompressionFactory diff --git a/include/osmium/io/detail/input_format.hpp b/include/osmium/io/detail/input_format.hpp index 98081e3c9..67b05a83b 100644 --- a/include/osmium/io/detail/input_format.hpp +++ b/include/osmium/io/detail/input_format.hpp @@ -55,12 +55,17 @@ namespace osmium { namespace detail { + struct reader_options { + osmium::osm_entity_bits::type read_which_entities = osm_entity_bits::all; + osmium::io::read_meta read_metadata = read_meta::yes; + }; + class Parser { future_buffer_queue_type& m_output_queue; std::promise& m_header_promise; queue_wrapper m_input_queue; - osmium::osm_entity_bits::type m_read_types; + reader_options m_options; bool m_header_is_done; protected: @@ -73,11 +78,15 @@ namespace osmium { return m_input_queue.has_reached_end_of_data(); } - osmium::osm_entity_bits::type read_types() const { - return m_read_types; + osmium::osm_entity_bits::type read_types() const noexcept { + return m_options.read_which_entities; } - bool header_is_done() const { + osmium::io::read_meta read_metadata() const noexcept { + return m_options.read_metadata; + } + + bool header_is_done() const noexcept { return m_header_is_done; } @@ -111,11 +120,11 @@ namespace osmium { Parser(future_string_queue_type& input_queue, future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_types) : + osmium::io::detail::reader_options options) : m_output_queue(output_queue), m_header_promise(header_promise), m_input_queue(input_queue), - m_read_types(read_types), + m_options(options), m_header_is_done(false) { } @@ -157,7 +166,7 @@ namespace osmium { using create_parser_type = std::function(future_string_queue_type&, future_buffer_queue_type&, std::promise& header_promise, - osmium::osm_entity_bits::type read_which_entities)>; + osmium::io::detail::reader_options options)>; private: diff --git a/include/osmium/io/detail/o5m_input_format.hpp b/include/osmium/io/detail/o5m_input_format.hpp index d6428790a..de4595e0d 100644 --- a/include/osmium/io/detail/o5m_input_format.hpp +++ b/include/osmium/io/detail/o5m_input_format.hpp @@ -80,7 +80,7 @@ namespace osmium { struct o5m_error : public io_error { explicit o5m_error(const char* what) : - io_error(std::string("o5m format error: ") + what) { + io_error(std::string{"o5m format error: "} + what) { } }; // struct o5m_error @@ -135,7 +135,7 @@ namespace osmium { const char* get(uint64_t index) const { if (m_table.empty() || index == 0 || index > number_of_entries) { - throw o5m_error("reference to non-existing string in table"); + throw o5m_error{"reference to non-existing string in table"}; } auto entry = (current_entry + number_of_entries - index) % number_of_entries; return &m_table[entry * entry_size]; @@ -191,7 +191,7 @@ namespace osmium { static const unsigned char header_magic[] = { 0xff, 0xe0, 0x04, 'o', '5' }; if (std::strncmp(reinterpret_cast(header_magic), m_data, sizeof(header_magic))) { - throw o5m_error("wrong header magic"); + throw o5m_error{"wrong header magic"}; } m_data += sizeof(header_magic); @@ -203,7 +203,7 @@ namespace osmium { } else if (*m_data == 'c') { // o5c change file m_header.set_has_multiple_object_versions(true); } else { - throw o5m_error("wrong header magic"); + throw o5m_error{"wrong header magic"}; } m_data++; @@ -211,7 +211,7 @@ namespace osmium { void check_file_format_version() { if (*m_data != '2') { - throw o5m_error("wrong header magic"); + throw o5m_error{"wrong header magic"}; } m_data++; @@ -219,7 +219,7 @@ namespace osmium { void decode_header() { if (! ensure_bytes_available(7)) { // overall length of header - throw o5m_error("file too short (incomplete header info)"); + throw o5m_error{"file too short (incomplete header info)"}; } check_header_magic(); @@ -260,7 +260,7 @@ namespace osmium { if (**dataptr == 0x00) { // get inline string (*dataptr)++; if (*dataptr == end) { - throw o5m_error("string format error"); + throw o5m_error{"string format error"}; } return *dataptr; } else { // get from reference table @@ -277,7 +277,7 @@ namespace osmium { auto uid = protozero::decode_varint(&data, end); if (data == end) { - throw o5m_error("missing user name"); + throw o5m_error{"missing user name"}; } const char* user = ++data; @@ -290,7 +290,7 @@ namespace osmium { while (*data++) { if (data == end) { - throw o5m_error("no null byte in user name"); + throw o5m_error{"no null byte in user name"}; } } @@ -302,24 +302,24 @@ namespace osmium { return std::make_pair(static_cast_with_assert(uid), user); } - void decode_tags(osmium::builder::Builder* builder, const char** dataptr, const char* const end) { - osmium::builder::TagListBuilder tl_builder(m_buffer, builder); + void decode_tags(osmium::builder::Builder& parent, const char** dataptr, const char* const end) { + osmium::builder::TagListBuilder builder{parent}; - while(*dataptr != end) { + while (*dataptr != end) { bool update_pointer = (**dataptr == 0x00); const char* data = decode_string(dataptr, end); const char* start = data; while (*data++) { if (data == end) { - throw o5m_error("no null byte in tag key"); + throw o5m_error{"no null byte in tag key"}; } } const char* value = data; while (*data++) { if (data == end) { - throw o5m_error("no null byte in tag value"); + throw o5m_error{"no null byte in tag value"}; } } @@ -328,7 +328,7 @@ namespace osmium { *dataptr = data; } - tl_builder.add_tag(start, value); + builder.add_tag(start, value); } } @@ -357,50 +357,46 @@ namespace osmium { } void decode_node(const char* data, const char* const end) { - osmium::builder::NodeBuilder builder(m_buffer); - osmium::Node& node = builder.object(); + osmium::builder::NodeBuilder builder{m_buffer}; - node.set_id(m_delta_id.update(zvarint(&data, end))); + builder.set_id(m_delta_id.update(zvarint(&data, end))); - builder.add_user(decode_info(node, &data, end)); + builder.set_user(decode_info(builder.object(), &data, end)); if (data == end) { // no location, object is deleted - builder.object().set_visible(false); - builder.object().set_location(osmium::Location{}); + builder.set_visible(false); + builder.set_location(osmium::Location{}); } else { auto lon = m_delta_lon.update(zvarint(&data, end)); auto lat = m_delta_lat.update(zvarint(&data, end)); - builder.object().set_location(osmium::Location{lon, lat}); + builder.set_location(osmium::Location{lon, lat}); if (data != end) { - decode_tags(&builder, &data, end); + decode_tags(builder, &data, end); } } - - m_buffer.commit(); } void decode_way(const char* data, const char* const end) { - osmium::builder::WayBuilder builder(m_buffer); - osmium::Way& way = builder.object(); + osmium::builder::WayBuilder builder{m_buffer}; - way.set_id(m_delta_id.update(zvarint(&data, end))); + builder.set_id(m_delta_id.update(zvarint(&data, end))); - builder.add_user(decode_info(way, &data, end)); + builder.set_user(decode_info(builder.object(), &data, end)); if (data == end) { // no reference section, object is deleted - builder.object().set_visible(false); + builder.set_visible(false); } else { auto reference_section_length = protozero::decode_varint(&data, end); if (reference_section_length > 0) { const char* const end_refs = data + reference_section_length; if (end_refs > end) { - throw o5m_error("way nodes ref section too long"); + throw o5m_error{"way nodes ref section too long"}; } - osmium::builder::WayNodeListBuilder wn_builder(m_buffer, &builder); + osmium::builder::WayNodeListBuilder wn_builder{builder}; while (data < end_refs) { wn_builder.add_node_ref(m_delta_way_node_id.update(zvarint(&data, end))); @@ -408,16 +404,14 @@ namespace osmium { } if (data != end) { - decode_tags(&builder, &data, end); + decode_tags(builder, &data, end); } } - - m_buffer.commit(); } osmium::item_type decode_member_type(char c) { if (c < '0' || c > '2') { - throw o5m_error("unknown member type"); + throw o5m_error{"unknown member type"}; } return osmium::nwr_index_to_item_type(c - '0'); } @@ -429,13 +423,13 @@ namespace osmium { auto member_type = decode_member_type(*data++); if (data == end) { - throw o5m_error("missing role"); + throw o5m_error{"missing role"}; } const char* role = data; while (*data++) { if (data == end) { - throw o5m_error("no null byte in role"); + throw o5m_error{"no null byte in role"}; } } @@ -448,30 +442,29 @@ namespace osmium { } void decode_relation(const char* data, const char* const end) { - osmium::builder::RelationBuilder builder(m_buffer); - osmium::Relation& relation = builder.object(); + osmium::builder::RelationBuilder builder{m_buffer}; - relation.set_id(m_delta_id.update(zvarint(&data, end))); + builder.set_id(m_delta_id.update(zvarint(&data, end))); - builder.add_user(decode_info(relation, &data, end)); + builder.set_user(decode_info(builder.object(), &data, end)); if (data == end) { // no reference section, object is deleted - builder.object().set_visible(false); + builder.set_visible(false); } else { auto reference_section_length = protozero::decode_varint(&data, end); if (reference_section_length > 0) { const char* const end_refs = data + reference_section_length; if (end_refs > end) { - throw o5m_error("relation format error"); + throw o5m_error{"relation format error"}; } - osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder); + osmium::builder::RelationMemberListBuilder rml_builder{builder}; while (data < end_refs) { auto delta_id = zvarint(&data, end); if (data == end) { - throw o5m_error("relation member format error"); + throw o5m_error{"relation member format error"}; } auto type_role = decode_role(&data, end); auto i = osmium::item_type_to_nwr_index(type_role.first); @@ -481,11 +474,9 @@ namespace osmium { } if (data != end) { - decode_tags(&builder, &data, end); + decode_tags(builder, &data, end); } } - - m_buffer.commit(); } void decode_bbox(const char* data, const char* const end) { @@ -537,11 +528,11 @@ namespace osmium { try { length = protozero::decode_varint(&m_data, m_end); } catch (const protozero::end_of_buffer_exception&) { - throw o5m_error("premature end of file"); + throw o5m_error{"premature end of file"}; } if (! ensure_bytes_available(length)) { - throw o5m_error("premature end of file"); + throw o5m_error{"premature end of file"}; } switch (ds_type) { @@ -549,18 +540,21 @@ namespace osmium { mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::node) { decode_node(m_data, m_data + length); + m_buffer.commit(); } break; case dataset_type::way: mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::way) { decode_way(m_data, m_data + length); + m_buffer.commit(); } break; case dataset_type::relation: mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::relation) { decode_relation(m_data, m_data + length); + m_buffer.commit(); } break; case dataset_type::bounding_box: @@ -598,8 +592,8 @@ namespace osmium { O5mParser(future_string_queue_type& input_queue, future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_types) : - Parser(input_queue, output_queue, header_promise, read_types), + osmium::io::detail::reader_options options) : + Parser(input_queue, output_queue, header_promise, options), m_header(), m_buffer(buffer_size), m_input(), @@ -625,8 +619,8 @@ namespace osmium { [](future_string_queue_type& input_queue, future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_which_entities) { - return std::unique_ptr(new O5mParser(input_queue, output_queue, header_promise, read_which_entities)); + osmium::io::detail::reader_options options) { + return std::unique_ptr(new O5mParser(input_queue, output_queue, header_promise, options)); }); // dummy function to silence the unused variable warning from above diff --git a/include/osmium/io/detail/opl_input_format.hpp b/include/osmium/io/detail/opl_input_format.hpp index 15a31f38a..1bd8b0763 100644 --- a/include/osmium/io/detail/opl_input_format.hpp +++ b/include/osmium/io/detail/opl_input_format.hpp @@ -82,8 +82,8 @@ namespace osmium { OPLParser(future_string_queue_type& input_queue, future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_types) : - Parser(input_queue, output_queue, header_promise, read_types) { + osmium::io::detail::reader_options options) : + Parser(input_queue, output_queue, header_promise, options) { set_header_value(osmium::io::Header{}); } @@ -137,8 +137,8 @@ namespace osmium { [](future_string_queue_type& input_queue, future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_which_entities) { - return std::unique_ptr(new OPLParser(input_queue, output_queue, header_promise, read_which_entities)); + osmium::io::detail::reader_options options) { + return std::unique_ptr(new OPLParser(input_queue, output_queue, header_promise, options)); }); // dummy function to silence the unused variable warning from above diff --git a/include/osmium/io/detail/opl_parser_functions.hpp b/include/osmium/io/detail/opl_parser_functions.hpp index 97c59275a..ee35b36f3 100644 --- a/include/osmium/io/detail/opl_parser_functions.hpp +++ b/include/osmium/io/detail/opl_parser_functions.hpp @@ -365,9 +365,8 @@ namespace osmium { inline void opl_parse_node(const char** data, osmium::memory::Buffer& buffer) { osmium::builder::NodeBuilder builder{buffer}; - osmium::Node& node = builder.object(); - node.set_id(opl_parse_id(data)); + builder.set_id(opl_parse_id(data)); const char* tags_begin = nullptr; @@ -382,19 +381,19 @@ namespace osmium { ++(*data); switch (c) { case 'v': - node.set_version(opl_parse_version(data)); + builder.set_version(opl_parse_version(data)); break; case 'd': - node.set_visible(opl_parse_visible(data)); + builder.set_visible(opl_parse_visible(data)); break; case 'c': - node.set_changeset(opl_parse_changeset_id(data)); + builder.set_changeset(opl_parse_changeset_id(data)); break; case 't': - node.set_timestamp(opl_parse_timestamp(data)); + builder.set_timestamp(opl_parse_timestamp(data)); break; case 'i': - node.set_uid(opl_parse_uid(data)); + builder.set_uid(opl_parse_uid(data)); break; case 'u': opl_parse_string(data, user); @@ -422,23 +421,20 @@ namespace osmium { } if (location.valid()) { - node.set_location(location); + builder.set_location(location); } - builder.add_user(user); + builder.set_user(user); if (tags_begin) { opl_parse_tags(tags_begin, buffer, &builder); } - - buffer.commit(); } inline void opl_parse_way(const char** data, osmium::memory::Buffer& buffer) { osmium::builder::WayBuilder builder{buffer}; - osmium::Way& way = builder.object(); - way.set_id(opl_parse_id(data)); + builder.set_id(opl_parse_id(data)); const char* tags_begin = nullptr; @@ -455,19 +451,19 @@ namespace osmium { ++(*data); switch (c) { case 'v': - way.set_version(opl_parse_version(data)); + builder.set_version(opl_parse_version(data)); break; case 'd': - way.set_visible(opl_parse_visible(data)); + builder.set_visible(opl_parse_visible(data)); break; case 'c': - way.set_changeset(opl_parse_changeset_id(data)); + builder.set_changeset(opl_parse_changeset_id(data)); break; case 't': - way.set_timestamp(opl_parse_timestamp(data)); + builder.set_timestamp(opl_parse_timestamp(data)); break; case 'i': - way.set_uid(opl_parse_uid(data)); + builder.set_uid(opl_parse_uid(data)); break; case 'u': opl_parse_string(data, user); @@ -488,15 +484,13 @@ namespace osmium { } } - builder.add_user(user); + builder.set_user(user); if (tags_begin) { opl_parse_tags(tags_begin, buffer, &builder); } opl_parse_way_nodes(nodes_begin, nodes_end, buffer, &builder); - - buffer.commit(); } inline void opl_parse_relation_members(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::RelationBuilder* parent_builder = nullptr) { @@ -536,9 +530,8 @@ namespace osmium { inline void opl_parse_relation(const char** data, osmium::memory::Buffer& buffer) { osmium::builder::RelationBuilder builder{buffer}; - osmium::Relation& relation = builder.object(); - relation.set_id(opl_parse_id(data)); + builder.set_id(opl_parse_id(data)); const char* tags_begin = nullptr; @@ -555,19 +548,19 @@ namespace osmium { ++(*data); switch (c) { case 'v': - relation.set_version(opl_parse_version(data)); + builder.set_version(opl_parse_version(data)); break; case 'd': - relation.set_visible(opl_parse_visible(data)); + builder.set_visible(opl_parse_visible(data)); break; case 'c': - relation.set_changeset(opl_parse_changeset_id(data)); + builder.set_changeset(opl_parse_changeset_id(data)); break; case 't': - relation.set_timestamp(opl_parse_timestamp(data)); + builder.set_timestamp(opl_parse_timestamp(data)); break; case 'i': - relation.set_uid(opl_parse_uid(data)); + builder.set_uid(opl_parse_uid(data)); break; case 'u': opl_parse_string(data, user); @@ -588,7 +581,7 @@ namespace osmium { } } - builder.add_user(user); + builder.set_user(user); if (tags_begin) { opl_parse_tags(tags_begin, buffer, &builder); @@ -597,15 +590,12 @@ namespace osmium { if (members_begin != members_end) { opl_parse_relation_members(members_begin, members_end, buffer, &builder); } - - buffer.commit(); } inline void opl_parse_changeset(const char** data, osmium::memory::Buffer& buffer) { osmium::builder::ChangesetBuilder builder{buffer}; - osmium::Changeset& changeset = builder.object(); - changeset.set_id(opl_parse_changeset_id(data)); + builder.set_id(opl_parse_changeset_id(data)); const char* tags_begin = nullptr; @@ -621,19 +611,19 @@ namespace osmium { ++(*data); switch (c) { case 'k': - changeset.set_num_changes(opl_parse_int(data)); + builder.set_num_changes(opl_parse_int(data)); break; case 's': - changeset.set_created_at(opl_parse_timestamp(data)); + builder.set_created_at(opl_parse_timestamp(data)); break; case 'e': - changeset.set_closed_at(opl_parse_timestamp(data)); + builder.set_closed_at(opl_parse_timestamp(data)); break; case 'd': - changeset.set_num_comments(opl_parse_int(data)); + builder.set_num_comments(opl_parse_int(data)); break; case 'i': - changeset.set_uid(opl_parse_uid(data)); + builder.set_uid(opl_parse_uid(data)); break; case 'u': opl_parse_string(data, user); @@ -672,17 +662,17 @@ namespace osmium { } if (location1.valid() && location2.valid()) { - changeset.bounds().extend(location1); - changeset.bounds().extend(location2); + osmium::Box box; + box.extend(location1); + box.extend(location2); + builder.set_bounds(box); } - builder.add_user(user); + builder.set_user(user); if (tags_begin) { opl_parse_tags(tags_begin, buffer, &builder); } - - buffer.commit(); } inline bool opl_parse_line(uint64_t line_count, @@ -702,6 +692,7 @@ namespace osmium { if (read_types & osmium::osm_entity_bits::node) { ++data; opl_parse_node(&data, buffer); + buffer.commit(); return true; } break; @@ -709,6 +700,7 @@ namespace osmium { if (read_types & osmium::osm_entity_bits::way) { ++data; opl_parse_way(&data, buffer); + buffer.commit(); return true; } break; @@ -716,6 +708,7 @@ namespace osmium { if (read_types & osmium::osm_entity_bits::relation) { ++data; opl_parse_relation(&data, buffer); + buffer.commit(); return true; } break; @@ -723,6 +716,7 @@ namespace osmium { if (read_types & osmium::osm_entity_bits::changeset) { ++data; opl_parse_changeset(&data, buffer); + buffer.commit(); return true; } break; diff --git a/include/osmium/io/detail/pbf_decoder.hpp b/include/osmium/io/detail/pbf_decoder.hpp index 5df191db6..5164ce33d 100644 --- a/include/osmium/io/detail/pbf_decoder.hpp +++ b/include/osmium/io/detail/pbf_decoder.hpp @@ -50,6 +50,7 @@ DEALINGS IN THE SOFTWARE. #include // IWYU pragma: export #include #include +#include #include #include #include @@ -94,6 +95,8 @@ namespace osmium { osmium::memory::Buffer m_buffer { initial_buffer_size }; + osmium::io::read_meta m_read_metadata; + void decode_stringtable(const data_view& data) { if (!m_stringtable.empty()) { throw osmium::pbf_error("more than one stringtable in pbf file"); @@ -143,13 +146,19 @@ namespace osmium { case OSMFormat::PrimitiveGroup::repeated_Node_nodes: if (m_read_types & osmium::osm_entity_bits::node) { decode_node(pbf_primitive_group.get_view()); + m_buffer.commit(); } else { pbf_primitive_group.skip(); } break; case OSMFormat::PrimitiveGroup::optional_DenseNodes_dense: if (m_read_types & osmium::osm_entity_bits::node) { - decode_dense_nodes(pbf_primitive_group.get_view()); + if (m_read_metadata == osmium::io::read_meta::yes) { + decode_dense_nodes(pbf_primitive_group.get_view()); + } else { + decode_dense_nodes_without_metadata(pbf_primitive_group.get_view()); + } + m_buffer.commit(); } else { pbf_primitive_group.skip(); } @@ -157,6 +166,7 @@ namespace osmium { case OSMFormat::PrimitiveGroup::repeated_Way_ways: if (m_read_types & osmium::osm_entity_bits::way) { decode_way(pbf_primitive_group.get_view()); + m_buffer.commit(); } else { pbf_primitive_group.skip(); } @@ -164,6 +174,7 @@ namespace osmium { case OSMFormat::PrimitiveGroup::repeated_Relation_relations: if (m_read_types & osmium::osm_entity_bits::relation) { decode_relation(pbf_primitive_group.get_view()); + m_buffer.commit(); } else { pbf_primitive_group.skip(); } @@ -221,9 +232,9 @@ namespace osmium { using kv_type = protozero::iterator_range; - void build_tag_list(osmium::builder::Builder& builder, const kv_type& keys, const kv_type& vals) { + void build_tag_list(osmium::builder::Builder& parent, const kv_type& keys, const kv_type& vals) { if (!keys.empty()) { - osmium::builder::TagListBuilder tl_builder(m_buffer, &builder); + osmium::builder::TagListBuilder builder{parent}; auto kit = keys.begin(); auto vit = vals.begin(); while (kit != keys.end()) { @@ -233,7 +244,7 @@ namespace osmium { } const auto& k = m_stringtable.at(*kit++); const auto& v = m_stringtable.at(*vit++); - tl_builder.add_tag(k.first, k.second, v.first, v.second); + builder.add_tag(k.first, k.second, v.first, v.second); } } } @@ -243,7 +254,7 @@ namespace osmium { } void decode_node(const data_view& data) { - osmium::builder::NodeBuilder builder(m_buffer); + osmium::builder::NodeBuilder builder{m_buffer}; osmium::Node& node = builder.object(); kv_type keys; @@ -266,7 +277,11 @@ namespace osmium { vals = pbf_node.get_packed_uint32(); break; case OSMFormat::Node::optional_Info_info: - user = decode_info(pbf_node.get_view(), builder.object()); + if (m_read_metadata == osmium::io::read_meta::yes) { + user = decode_info(pbf_node.get_view(), builder.object()); + } else { + pbf_node.skip(); + } break; case OSMFormat::Node::required_sint64_lat: lat = pbf_node.get_sint64(); @@ -290,15 +305,13 @@ namespace osmium { )); } - builder.add_user(user.first, user.second); + builder.set_user(user.first, user.second); build_tag_list(builder, keys, vals); - - m_buffer.commit(); } void decode_way(const data_view& data) { - osmium::builder::WayBuilder builder(m_buffer); + osmium::builder::WayBuilder builder{m_buffer}; kv_type keys; kv_type vals; @@ -321,7 +334,11 @@ namespace osmium { vals = pbf_way.get_packed_uint32(); break; case OSMFormat::Way::optional_Info_info: - user = decode_info(pbf_way.get_view(), builder.object()); + if (m_read_metadata == osmium::io::read_meta::yes) { + user = decode_info(pbf_way.get_view(), builder.object()); + } else { + pbf_way.skip(); + } break; case OSMFormat::Way::packed_sint64_refs: refs = pbf_way.get_packed_sint64(); @@ -337,10 +354,10 @@ namespace osmium { } } - builder.add_user(user.first, user.second); + builder.set_user(user.first, user.second); if (!refs.empty()) { - osmium::builder::WayNodeListBuilder wnl_builder(m_buffer, &builder); + osmium::builder::WayNodeListBuilder wnl_builder{builder}; osmium::util::DeltaDecode ref; if (lats.empty()) { for (const auto& ref_value : refs) { @@ -363,12 +380,10 @@ namespace osmium { } build_tag_list(builder, keys, vals); - - m_buffer.commit(); } void decode_relation(const data_view& data) { - osmium::builder::RelationBuilder builder(m_buffer); + osmium::builder::RelationBuilder builder{m_buffer}; kv_type keys; kv_type vals; @@ -391,7 +406,11 @@ namespace osmium { vals = pbf_relation.get_packed_uint32(); break; case OSMFormat::Relation::optional_Info_info: - user = decode_info(pbf_relation.get_view(), builder.object()); + if (m_read_metadata == osmium::io::read_meta::yes) { + user = decode_info(pbf_relation.get_view(), builder.object()); + } else { + pbf_relation.skip(); + } break; case OSMFormat::Relation::packed_int32_roles_sid: roles = pbf_relation.get_packed_int32(); @@ -407,10 +426,10 @@ namespace osmium { } } - builder.add_user(user.first, user.second); + builder.set_user(user.first, user.second); if (!refs.empty()) { - osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder); + osmium::builder::RelationMemberListBuilder rml_builder{builder}; osmium::util::DeltaDecode ref; while (!roles.empty() && !refs.empty() && !types.empty()) { const auto& r = m_stringtable.at(roles.front()); @@ -431,8 +450,84 @@ namespace osmium { } build_tag_list(builder, keys, vals); + } + + void build_tag_list_from_dense_nodes(osmium::builder::NodeBuilder& builder, protozero::pbf_reader::const_int32_iterator& it, protozero::pbf_reader::const_int32_iterator last) { + osmium::builder::TagListBuilder tl_builder{builder}; + while (it != last && *it != 0) { + const auto& k = m_stringtable.at(*it++); + if (it == last) { + throw osmium::pbf_error("PBF format error"); // this is against the spec, keys/vals must come in pairs + } + const auto& v = m_stringtable.at(*it++); + tl_builder.add_tag(k.first, k.second, v.first, v.second); + } + + if (it != last) { + ++it; + } + } + + void decode_dense_nodes_without_metadata(const data_view& data) { + protozero::iterator_range ids; + protozero::iterator_range lats; + protozero::iterator_range lons; + + protozero::iterator_range tags; + + protozero::pbf_message pbf_dense_nodes(data); + while (pbf_dense_nodes.next()) { + switch (pbf_dense_nodes.tag()) { + case OSMFormat::DenseNodes::packed_sint64_id: + ids = pbf_dense_nodes.get_packed_sint64(); + break; + case OSMFormat::DenseNodes::packed_sint64_lat: + lats = pbf_dense_nodes.get_packed_sint64(); + break; + case OSMFormat::DenseNodes::packed_sint64_lon: + lons = pbf_dense_nodes.get_packed_sint64(); + break; + case OSMFormat::DenseNodes::packed_int32_keys_vals: + tags = pbf_dense_nodes.get_packed_int32(); + break; + default: + pbf_dense_nodes.skip(); + } + } + + osmium::util::DeltaDecode dense_id; + osmium::util::DeltaDecode dense_latitude; + osmium::util::DeltaDecode dense_longitude; + + auto tag_it = tags.begin(); + + while (!ids.empty()) { + if (lons.empty() || + lats.empty()) { + // this is against the spec, must have same number of elements + throw osmium::pbf_error("PBF format error"); + } + + osmium::builder::NodeBuilder builder{m_buffer}; + osmium::Node& node = builder.object(); + + node.set_id(dense_id.update(ids.front())); + ids.drop_front(); + + const auto lon = dense_longitude.update(lons.front()); + lons.drop_front(); + const auto lat = dense_latitude.update(lats.front()); + lats.drop_front(); + builder.object().set_location(osmium::Location( + convert_pbf_coordinate(lon), + convert_pbf_coordinate(lat) + )); + + if (tag_it != tags.end()) { + build_tag_list_from_dense_nodes(builder, tag_it, tags.end()); + } + } - m_buffer.commit(); } void decode_dense_nodes(const data_view& data) { @@ -522,7 +617,7 @@ namespace osmium { bool visible = true; - osmium::builder::NodeBuilder builder(m_buffer); + osmium::builder::NodeBuilder builder{m_buffer}; osmium::Node& node = builder.object(); node.set_id(dense_id.update(ids.front())); @@ -569,9 +664,7 @@ namespace osmium { const auto& u = m_stringtable.at(dense_user_sid.update(user_sids.front())); user_sids.drop_front(); - builder.add_user(u.first, u.second); - } else { - builder.add_user(""); + builder.set_user(u.first, u.second); } // even if the node isn't visible, there's still a record @@ -588,31 +681,18 @@ namespace osmium { } if (tag_it != tags.end()) { - osmium::builder::TagListBuilder tl_builder(m_buffer, &builder); - while (tag_it != tags.end() && *tag_it != 0) { - const auto& k = m_stringtable.at(*tag_it++); - if (tag_it == tags.end()) { - throw osmium::pbf_error("PBF format error"); // this is against the spec, keys/vals must come in pairs - } - const auto& v = m_stringtable.at(*tag_it++); - tl_builder.add_tag(k.first, k.second, v.first, v.second); - } - - if (tag_it != tags.end()) { - ++tag_it; - } + build_tag_list_from_dense_nodes(builder, tag_it, tags.end()); } - - m_buffer.commit(); } } public: - PBFPrimitiveBlockDecoder(const data_view& data, osmium::osm_entity_bits::type read_types) : + PBFPrimitiveBlockDecoder(const data_view& data, osmium::osm_entity_bits::type read_types, osmium::io::read_meta read_metadata) : m_data(data), - m_read_types(read_types) { + m_read_types(read_types), + m_read_metadata(read_metadata) { } PBFPrimitiveBlockDecoder(const PBFPrimitiveBlockDecoder&) = delete; @@ -789,12 +869,14 @@ namespace osmium { std::shared_ptr m_input_buffer; osmium::osm_entity_bits::type m_read_types; + osmium::io::read_meta m_read_metadata; public: - PBFDataBlobDecoder(std::string&& input_buffer, osmium::osm_entity_bits::type read_types) : + PBFDataBlobDecoder(std::string&& input_buffer, osmium::osm_entity_bits::type read_types, osmium::io::read_meta read_metadata) : m_input_buffer(std::make_shared(std::move(input_buffer))), - m_read_types(read_types) { + m_read_types(read_types), + m_read_metadata(read_metadata) { } PBFDataBlobDecoder(const PBFDataBlobDecoder&) = default; @@ -807,7 +889,7 @@ namespace osmium { osmium::memory::Buffer operator()() { std::string output; - PBFPrimitiveBlockDecoder decoder(decode_blob(*m_input_buffer, output), m_read_types); + PBFPrimitiveBlockDecoder decoder(decode_blob(*m_input_buffer, output), m_read_types, m_read_metadata); return decoder(); } diff --git a/include/osmium/io/detail/pbf_input_format.hpp b/include/osmium/io/detail/pbf_input_format.hpp index 1253447f3..31e778ab2 100644 --- a/include/osmium/io/detail/pbf_input_format.hpp +++ b/include/osmium/io/detail/pbf_input_format.hpp @@ -180,7 +180,7 @@ namespace osmium { while (const auto size = check_type_and_get_blob_size("OSMData")) { std::string input_buffer = read_from_input_queue_with_check(size); - PBFDataBlobDecoder data_blob_parser{ std::move(input_buffer), read_types() }; + PBFDataBlobDecoder data_blob_parser{std::move(input_buffer), read_types(), read_metadata()}; if (osmium::config::use_pool_threads_for_pbf_parsing()) { send_to_output_queue(osmium::thread::Pool::instance().submit(std::move(data_blob_parser))); @@ -195,8 +195,8 @@ namespace osmium { PBFParser(future_string_queue_type& input_queue, future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_types) : - Parser(input_queue, output_queue, header_promise, read_types), + osmium::io::detail::reader_options options) : + Parser(input_queue, output_queue, header_promise, options), m_input_buffer() { } @@ -221,8 +221,8 @@ namespace osmium { [](future_string_queue_type& input_queue, future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_which_entities) { - return std::unique_ptr(new PBFParser(input_queue, output_queue, header_promise, read_which_entities)); + osmium::io::detail::reader_options options) { + return std::unique_ptr(new PBFParser(input_queue, output_queue, header_promise, options)); }); // dummy function to silence the unused variable warning from above diff --git a/include/osmium/io/detail/queue_util.hpp b/include/osmium/io/detail/queue_util.hpp index bc4702000..021ea7de4 100644 --- a/include/osmium/io/detail/queue_util.hpp +++ b/include/osmium/io/detail/queue_util.hpp @@ -33,10 +33,10 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include +#include #include #include @@ -59,13 +59,6 @@ namespace osmium { */ using future_buffer_queue_type = future_queue_type; - /** - * This type of queue contains OSM file data in the form it is - * stored on disk, ie encoded as XML, PBF, etc. - * The "end of file" is marked by an empty string. - */ - using string_queue_type = osmium::thread::Queue; - /** * This type of queue contains OSM file data in the form it is * stored on disk, ie encoded as XML, PBF, etc. @@ -95,11 +88,11 @@ namespace osmium { add_to_queue(queue, T{}); } - inline bool at_end_of_data(const std::string& data) { + inline bool at_end_of_data(const std::string& data) noexcept { return data.empty(); } - inline bool at_end_of_data(osmium::memory::Buffer& buffer) { + inline bool at_end_of_data(osmium::memory::Buffer& buffer) noexcept { return !buffer; } diff --git a/include/osmium/io/detail/xml_input_format.hpp b/include/osmium/io/detail/xml_input_format.hpp index d8c57d86e..242ef9b83 100644 --- a/include/osmium/io/detail/xml_input_format.hpp +++ b/include/osmium/io/detail/xml_input_format.hpp @@ -80,8 +80,8 @@ namespace osmium { XML_Error error_code; std::string error_string; - explicit xml_error(XML_Parser parser) : - io_error(std::string("XML parsing error at line ") + explicit xml_error(const XML_Parser& parser) : + io_error(std::string{"XML parsing error at line "} + std::to_string(XML_GetCurrentLineNumber(parser)) + ", column " + std::to_string(XML_GetCurrentColumnNumber(parser)) @@ -117,7 +117,7 @@ namespace osmium { } explicit format_version_error(const char* v) : - io_error(std::string("Can not read file with version ") + v), + io_error(std::string{"Can not read file with version "} + v), version(v) { } @@ -201,7 +201,7 @@ namespace osmium { static void entity_declaration_handler(void*, const XML_Char*, int, const XML_Char*, int, const XML_Char*, const XML_Char*, const XML_Char*, const XML_Char*) { - throw osmium::xml_error("XML entities are not supported"); + throw osmium::xml_error{"XML entities are not supported"}; } public: @@ -209,7 +209,7 @@ namespace osmium { explicit ExpatXMLParser(T* callback_object) : m_parser(XML_ParserCreate(nullptr)) { if (!m_parser) { - throw osmium::io_error("Internal error: Can not create parser"); + throw osmium::io_error{"Internal error: Can not create parser"}; } XML_SetUserData(m_parser, callback_object); XML_SetElementHandler(m_parser, start_element_wrapper, end_element_wrapper); @@ -229,7 +229,7 @@ namespace osmium { void operator()(const std::string& data, bool last) { if (XML_Parse(m_parser, data.data(), static_cast_with_assert(data.size()), last) == XML_STATUS_ERROR) { - throw osmium::xml_error(m_parser); + throw osmium::xml_error{m_parser}; } } @@ -271,37 +271,32 @@ namespace osmium { return user; } - void init_changeset(osmium::builder::ChangesetBuilder* builder, const XML_Char** attrs) { - const char* user = ""; - osmium::Changeset& new_changeset = builder->object(); + void init_changeset(osmium::builder::ChangesetBuilder& builder, const XML_Char** attrs) { + osmium::Box box; - osmium::Location min; - osmium::Location max; - check_attributes(attrs, [&min, &max, &user, &new_changeset](const XML_Char* name, const XML_Char* value) { + check_attributes(attrs, [&builder, &box](const XML_Char* name, const XML_Char* value) { if (!std::strcmp(name, "min_lon")) { - min.set_lon(value); + box.bottom_left().set_lon(value); } else if (!std::strcmp(name, "min_lat")) { - min.set_lat(value); + box.bottom_left().set_lat(value); } else if (!std::strcmp(name, "max_lon")) { - max.set_lon(value); + box.top_right().set_lon(value); } else if (!std::strcmp(name, "max_lat")) { - max.set_lat(value); + box.top_right().set_lat(value); } else if (!std::strcmp(name, "user")) { - user = value; + builder.set_user(value); } else { - new_changeset.set_attribute(name, value); + builder.set_attribute(name, value); } }); - new_changeset.bounds().extend(min); - new_changeset.bounds().extend(max); - - builder->add_user(user); + builder.set_bounds(box); } - void get_tag(osmium::builder::Builder* builder, const XML_Char** attrs) { + void get_tag(osmium::builder::Builder& builder, const XML_Char** attrs) { const char* k = ""; const char* v = ""; + check_attributes(attrs, [&k, &v](const XML_Char* name, const XML_Char* value) { if (name[0] == 'k' && name[1] == 0) { k = value; @@ -309,8 +304,9 @@ namespace osmium { v = value; } }); + if (!m_tl_builder) { - m_tl_builder = std::unique_ptr(new osmium::builder::TagListBuilder(m_buffer, builder)); + m_tl_builder.reset(new osmium::builder::TagListBuilder{builder}); } m_tl_builder->add_tag(k, v); } @@ -330,17 +326,17 @@ namespace osmium { if (!std::strcmp(name, "version")) { m_header.set("version", value); if (std::strcmp(value, "0.6")) { - throw osmium::format_version_error(value); + throw osmium::format_version_error{value}; } } else if (!std::strcmp(name, "generator")) { m_header.set("generator", value); } }); if (m_header.get("version") == "") { - throw osmium::format_version_error(); + throw osmium::format_version_error{}; } } else { - throw osmium::xml_error(std::string("Unknown top-level element: ") + element); + throw osmium::xml_error{std::string{"Unknown top-level element: "} + element}; } m_context = context::top; break; @@ -349,8 +345,8 @@ namespace osmium { if (!std::strcmp(element, "node")) { mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::node) { - m_node_builder = std::unique_ptr(new osmium::builder::NodeBuilder(m_buffer)); - m_node_builder->add_user(init_object(m_node_builder->object(), attrs)); + m_node_builder.reset(new osmium::builder::NodeBuilder{m_buffer}); + m_node_builder->set_user(init_object(m_node_builder->object(), attrs)); m_context = context::node; } else { m_context = context::ignored_node; @@ -358,8 +354,8 @@ namespace osmium { } else if (!std::strcmp(element, "way")) { mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::way) { - m_way_builder = std::unique_ptr(new osmium::builder::WayBuilder(m_buffer)); - m_way_builder->add_user(init_object(m_way_builder->object(), attrs)); + m_way_builder.reset(new osmium::builder::WayBuilder{m_buffer}); + m_way_builder->set_user(init_object(m_way_builder->object(), attrs)); m_context = context::way; } else { m_context = context::ignored_way; @@ -367,8 +363,8 @@ namespace osmium { } else if (!std::strcmp(element, "relation")) { mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::relation) { - m_relation_builder = std::unique_ptr(new osmium::builder::RelationBuilder(m_buffer)); - m_relation_builder->add_user(init_object(m_relation_builder->object(), attrs)); + m_relation_builder.reset(new osmium::builder::RelationBuilder{m_buffer}); + m_relation_builder->set_user(init_object(m_relation_builder->object(), attrs)); m_context = context::relation; } else { m_context = context::ignored_relation; @@ -376,8 +372,8 @@ namespace osmium { } else if (!std::strcmp(element, "changeset")) { mark_header_as_done(); if (read_types() & osmium::osm_entity_bits::changeset) { - m_changeset_builder = std::unique_ptr(new osmium::builder::ChangesetBuilder(m_buffer)); - init_changeset(m_changeset_builder.get(), attrs); + m_changeset_builder.reset(new osmium::builder::ChangesetBuilder{m_buffer}); + init_changeset(*m_changeset_builder, attrs); m_context = context::changeset; } else { m_context = context::ignored_changeset; @@ -407,7 +403,7 @@ namespace osmium { m_last_context = context::node; m_context = context::in_object; if (!std::strcmp(element, "tag")) { - get_tag(m_node_builder.get(), attrs); + get_tag(*m_node_builder, attrs); } break; case context::way: @@ -417,7 +413,7 @@ namespace osmium { m_tl_builder.reset(); if (!m_wnl_builder) { - m_wnl_builder = std::unique_ptr(new osmium::builder::WayNodeListBuilder(m_buffer, m_way_builder.get())); + m_wnl_builder.reset(new osmium::builder::WayNodeListBuilder{*m_way_builder}); } NodeRef nr; @@ -433,7 +429,7 @@ namespace osmium { m_wnl_builder->add_node_ref(nr); } else if (!std::strcmp(element, "tag")) { m_wnl_builder.reset(); - get_tag(m_way_builder.get(), attrs); + get_tag(*m_way_builder, attrs); } break; case context::relation: @@ -443,7 +439,7 @@ namespace osmium { m_tl_builder.reset(); if (!m_rml_builder) { - m_rml_builder = std::unique_ptr(new osmium::builder::RelationMemberListBuilder(m_buffer, m_relation_builder.get())); + m_rml_builder.reset(new osmium::builder::RelationMemberListBuilder{*m_relation_builder}); } item_type type = item_type::undefined; @@ -459,15 +455,15 @@ namespace osmium { } }); if (type != item_type::node && type != item_type::way && type != item_type::relation) { - throw osmium::xml_error("Unknown type on relation member"); + throw osmium::xml_error{"Unknown type on relation member"}; } if (ref == 0) { - throw osmium::xml_error("Missing ref on relation member"); + throw osmium::xml_error{"Missing ref on relation member"}; } m_rml_builder->add_member(type, ref, role); } else if (!std::strcmp(element, "tag")) { m_rml_builder.reset(); - get_tag(m_relation_builder.get(), attrs); + get_tag(*m_relation_builder, attrs); } break; case context::changeset: @@ -476,12 +472,12 @@ namespace osmium { m_context = context::discussion; m_tl_builder.reset(); if (!m_changeset_discussion_builder) { - m_changeset_discussion_builder = std::unique_ptr(new osmium::builder::ChangesetDiscussionBuilder(m_buffer, m_changeset_builder.get())); + m_changeset_discussion_builder.reset(new osmium::builder::ChangesetDiscussionBuilder{*m_changeset_builder}); } } else if (!std::strcmp(element, "tag")) { m_context = context::in_object; m_changeset_discussion_builder.reset(); - get_tag(m_changeset_builder.get(), attrs); + get_tag(*m_changeset_builder, attrs); } break; case context::discussion: @@ -632,8 +628,8 @@ namespace osmium { XMLParser(future_string_queue_type& input_queue, future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_types) : - Parser(input_queue, output_queue, header_promise, read_types), + osmium::io::detail::reader_options options) : + Parser(input_queue, output_queue, header_promise, options), m_context(context::root), m_last_context(context::root), m_in_delete_section(false), @@ -657,7 +653,7 @@ namespace osmium { ExpatXMLParser parser(this); while (!input_done()) { - std::string data = get_input(); + const std::string data{get_input()}; parser(data, input_done()); if (read_types() == osmium::osm_entity_bits::nothing && header_is_done()) { break; @@ -680,8 +676,8 @@ namespace osmium { [](future_string_queue_type& input_queue, future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_which_entities) { - return std::unique_ptr(new XMLParser(input_queue, output_queue, header_promise, read_which_entities)); + osmium::io::detail::reader_options options) { + return std::unique_ptr(new XMLParser{input_queue, output_queue, header_promise, options}); }); // dummy function to silence the unused variable warning from above diff --git a/include/osmium/io/file_format.hpp b/include/osmium/io/file_format.hpp index c447cb478..72b4abc7a 100644 --- a/include/osmium/io/file_format.hpp +++ b/include/osmium/io/file_format.hpp @@ -49,6 +49,11 @@ namespace osmium { debug = 6 }; + enum class read_meta { + no = 0, + yes = 1 + }; + // avoid g++ false positive #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wreturn-type" diff --git a/include/osmium/io/header.hpp b/include/osmium/io/header.hpp index 55ff5c6de..abd85f84b 100644 --- a/include/osmium/io/header.hpp +++ b/include/osmium/io/header.hpp @@ -44,17 +44,37 @@ namespace osmium { namespace io { /** - * Meta information from the header of an OSM file. - */ + * Meta information from the header of an OSM file. + * + * The header can contain any number of bounding boxes, although + * usually there is only a single one (or none). PBF files only + * allow a single bounding box, but XML files can have multiple ones, + * although it is unusual and the semantics are unclear, so it is + * discouraged to create files with multiple bounding boxes. + * + * The header contains a flag telling you whether this file can + * contain multiple versions of the same object. This is true for + * history files and for change files, but not for normal OSM data + * files. Not all OSM file formats can distinguish between those + * cases, so the flag might be wrong. + * + * In addition the header can contain any number of key-value pairs + * with additional information. Most often this is used to set the + * "generator", the program that generated the file. Depending on + * the file format some of these key-value pairs are handled + * specially. The the Options parent class for details on how to + * set and get those key-value pairs. + */ class Header : public osmium::util::Options { /// Bounding boxes std::vector m_boxes; /** - * Are there possibly multiple versions of the same object in this stream of objects? - * This is true for history files and for change files, but not for normal OSM files. - */ + * Are there possibly multiple versions of the same object in + * this stream of objects? This should be true for history files + * and for change files, but not for normal OSM data files. + */ bool m_has_multiple_object_versions = false; public: @@ -65,49 +85,76 @@ namespace osmium { Options(values) { } - Header(const Header&) = default; - Header& operator=(const Header&) = default; - - Header(Header&&) = default; - Header& operator=(Header&&) = default; - - ~Header() = default; - + /** + * Get the bounding boxes defined in the header. + */ std::vector& boxes() noexcept { return m_boxes; } + /** + * Get the bounding boxes defined in the header. + */ const std::vector& boxes() const noexcept { return m_boxes; } + /** + * Set all the bounding boxes in the header. + */ Header& boxes(const std::vector& boxes) noexcept { m_boxes = boxes; return *this; } + /** + * Get the first (or only if there is only one) bounding box. + * + * Returns an empty, invalid box if there is none. + */ osmium::Box box() const { - return m_boxes.empty() ? osmium::Box() : m_boxes.front(); + return m_boxes.empty() ? osmium::Box{} : m_boxes.front(); } + /** + * Join up all the bounding boxes in the header into one and return + * it. This method is what you probably want to use unless you want + * to handle the possibly multiple bounding boxes yourself. + * + * Returns an empty, invalid box if there is none. + */ osmium::Box joined_boxes() const { osmium::Box box; for (const auto& b : m_boxes) { - box.extend(b.bottom_left()); - box.extend(b.top_right()); + box.extend(b); } return box; } + /** + * Add the given bounding box to the list of bounding boxes in the + * header. + * + * @returns The header itself to allow chaining. + */ Header& add_box(const osmium::Box& box) { m_boxes.push_back(box); return *this; } + /** + * Can this file contain multiple versions of the same object? + */ bool has_multiple_object_versions() const noexcept { return m_has_multiple_object_versions; } + /** + * Set the flag that tells us whether this file can contain + * multiple versions of the same object? + * + * @returns The header itself to allow chaining. + */ Header& set_has_multiple_object_versions(bool value) noexcept { m_has_multiple_object_versions = value; return *this; diff --git a/include/osmium/io/reader.hpp b/include/osmium/io/reader.hpp index 12f97b8e6..89c8564f3 100644 --- a/include/osmium/io/reader.hpp +++ b/include/osmium/io/reader.hpp @@ -91,7 +91,6 @@ namespace osmium { class Reader { osmium::io::File m_file; - osmium::osm_entity_bits::type m_read_which_entities; enum class status { okay = 0, // normal reading @@ -118,15 +117,25 @@ namespace osmium { size_t m_file_size; + osmium::io::detail::reader_options m_options; + + void set_option(osmium::osm_entity_bits::type value) noexcept { + m_options.read_which_entities = value; + } + + void set_option(osmium::io::read_meta value) noexcept { + m_options.read_metadata = value; + } + // This function will run in a separate thread. static void parser_thread(const osmium::io::File& file, detail::future_string_queue_type& input_queue, detail::future_buffer_queue_type& osmdata_queue, std::promise&& header_promise, - osmium::osm_entity_bits::type read_which_entities) { + osmium::io::detail::reader_options options) { std::promise promise = std::move(header_promise); const auto creator = detail::ParserFactory::instance().get_creator_function(file); - const auto parser = creator(input_queue, osmdata_queue, promise, read_which_entities); + const auto parser = creator(input_queue, osmdata_queue, promise, options); parser->parse(); } @@ -205,15 +214,28 @@ namespace osmium { /** * Create new Reader object. * - * @param file The file we want to open. - * @param read_which_entities Which OSM entities (nodes, ways, relations, and/or changesets) - * should be read from the input file. It can speed the read up - * significantly if objects that are not needed anyway are not - * parsed. + * @param file The file (contains name and format info) to open. + * @param args All further arguments are optional and can appear + * in any order: + * + * * osmium::osm_entities::bits: Which OSM entities (nodes, ways, + * relations, and/or changesets) should be read from the + * input file. It can speed the read up significantly if + * objects that are not needed anyway are not parsed. + * + * * osmium::io::read_meta: Read meta data or not. The default is + * osmium::io::read_meta::yes which means that meta data + * is read normally. If you set this to + * osmium::io::read_meta::no, meta data (like version, uid, + * etc.) is not read possibly speeding up the read. Not all + * file formats use this setting. + * + * @throws osmium::io_error If there was an error. + * @throws std::system_error If the file could not be opened. */ - explicit Reader(const osmium::io::File& file, osmium::osm_entity_bits::type read_which_entities = osmium::osm_entity_bits::all) : + template + explicit Reader(const osmium::io::File& file, TArgs&&... args) : m_file(file.check()), - m_read_which_entities(read_which_entities), m_status(status::okay), m_childpid(0), m_input_queue(detail::get_input_queue_size(), "raw_input"), @@ -227,17 +249,24 @@ namespace osmium { m_header(), m_thread(), m_file_size(m_decompressor->file_size()) { + + (void)std::initializer_list{ + (set_option(args), 0)... + }; + std::promise header_promise; m_header_future = header_promise.get_future(); - m_thread = osmium::thread::thread_handler{parser_thread, std::ref(m_file), std::ref(m_input_queue), std::ref(m_osmdata_queue), std::move(header_promise), read_which_entities}; + m_thread = osmium::thread::thread_handler{parser_thread, std::ref(m_file), std::ref(m_input_queue), std::ref(m_osmdata_queue), std::move(header_promise), m_options}; } - explicit Reader(const std::string& filename, osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all) : - Reader(osmium::io::File(filename), read_types) { + template + explicit Reader(const std::string& filename, TArgs&&... args) : + Reader(osmium::io::File(filename), std::forward(args)...) { } - explicit Reader(const char* filename, osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all) : - Reader(osmium::io::File(filename), read_types) { + template + explicit Reader(const char* filename, TArgs&&... args) : + Reader(osmium::io::File(filename), std::forward(args)...) { } Reader(const Reader&) = delete; @@ -304,7 +333,7 @@ namespace osmium { try { if (m_header_future.valid()) { m_header = m_header_future.get(); - if (m_read_which_entities == osmium::osm_entity_bits::nothing) { + if (m_options.read_which_entities == osmium::osm_entity_bits::nothing) { m_status = status::eof; } } @@ -330,7 +359,7 @@ namespace osmium { osmium::memory::Buffer buffer; if (m_status != status::okay || - m_read_which_entities == osmium::osm_entity_bits::nothing) { + m_options.read_which_entities == osmium::osm_entity_bits::nothing) { throw io_error("Can not read from reader when in status 'closed', 'eof', or 'error'"); } diff --git a/include/osmium/memory/buffer.hpp b/include/osmium/memory/buffer.hpp index 8c246db02..bcf0bd0a4 100644 --- a/include/osmium/memory/buffer.hpp +++ b/include/osmium/memory/buffer.hpp @@ -113,6 +113,9 @@ namespace osmium { size_t m_capacity; size_t m_written; size_t m_committed; +#ifndef NDEBUG + uint8_t m_builder_count{0}; +#endif auto_grow m_auto_grow {auto_grow::no}; std::function m_full; @@ -216,13 +219,28 @@ namespace osmium { ~Buffer() = default; +#ifndef NDEBUG + void increment_builder_count() noexcept { + ++m_builder_count; + } + + void decrement_builder_count() noexcept { + assert(m_builder_count > 0); + --m_builder_count; + } + + uint8_t builder_count() const noexcept { + return m_builder_count; + } +#endif + /** * Return a pointer to data inside the buffer. * * @pre The buffer must be valid. */ unsigned char* data() const noexcept { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return m_data; } @@ -258,7 +276,7 @@ namespace osmium { * @pre The buffer must be valid. */ bool is_aligned() const noexcept { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return (m_written % align_bytes == 0) && (m_committed % align_bytes == 0); } @@ -283,7 +301,7 @@ namespace osmium { * than the difference between committed() and capacity(). */ OSMIUM_DEPRECATED void set_full_callback(std::function full) { - assert(m_data); + assert(m_data && "This must be a valid buffer"); m_full = full; } @@ -292,7 +310,6 @@ namespace osmium { * This works only with internally memory-managed buffers. * If the given size is not larger than the current capacity, * nothing is done. - * Already written but not committed data is discarded. * * @pre The buffer must be valid. * @@ -305,7 +322,7 @@ namespace osmium { * @throws std::bad_alloc if there isn't enough memory available. */ void grow(size_t size) { - assert(m_data); + assert(m_data && "This must be a valid buffer"); if (!m_memory) { throw std::logic_error("Can't grow Buffer if it doesn't use internal memory management."); } @@ -325,15 +342,18 @@ namespace osmium { /** * Mark currently written bytes in the buffer as committed. * - * @pre The buffer must be valid and aligned properly (as indicated + * @pre The buffer must be valid. + * @pre The buffer must be aligned properly (as indicated * by is_aligned(). + * @pre No builder can be open on this buffer. * * @returns Number of committed bytes before this commit. Can be * used as an offset into the buffer to get to the * object being committed by this call. */ size_t commit() { - assert(m_data); + assert(m_data && "This must be a valid buffer"); + assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope"); assert(is_aligned()); const size_t offset = m_committed; @@ -345,9 +365,11 @@ namespace osmium { * Roll back changes in buffer to last committed state. * * @pre The buffer must be valid. + * @pre No builder can be open on this buffer. */ void rollback() { - assert(m_data); + assert(m_data && "This must be a valid buffer"); + assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope"); m_written = m_committed; } @@ -356,9 +378,12 @@ namespace osmium { * * No-op on an invalid buffer. * + * @pre No builder can be open on this buffer. + * * @returns Number of bytes in the buffer before it was cleared. */ size_t clear() { + assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope"); const size_t committed = m_committed; m_written = 0; m_committed = 0; @@ -377,7 +402,7 @@ namespace osmium { */ template T& get(const size_t offset) const { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return *reinterpret_cast(&m_data[offset]); } @@ -415,7 +440,7 @@ namespace osmium { * no callback defined and the buffer isn't auto-growing. */ unsigned char* reserve_space(const size_t size) { - assert(m_data); + assert(m_data && "This must be a valid buffer"); // try to flush the buffer empty first. if (m_written + size > m_capacity && m_full) { m_full(*this); @@ -455,7 +480,7 @@ namespace osmium { */ template T& add_item(const T& item) { - assert(m_data); + assert(m_data && "This must be a valid buffer"); unsigned char* target = reserve_space(item.padded_size()); std::copy_n(reinterpret_cast(&item), item.padded_size(), target); return *reinterpret_cast(target); @@ -465,6 +490,7 @@ namespace osmium { * Add committed contents of the given buffer to this buffer. * * @pre The buffer must be valid. + * @pre No builder can be open on this buffer. * * Note that you have to eventually call commit() to actually * commit this data. @@ -472,7 +498,9 @@ namespace osmium { * @param buffer The source of the copy. Must be valid. */ void add_buffer(const Buffer& buffer) { - assert(m_data && buffer); + assert(m_data && "This must be a valid buffer"); + assert(buffer && "Buffer parameter must be a valid buffer"); + assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope"); unsigned char* target = reserve_space(buffer.committed()); std::copy_n(buffer.data(), buffer.committed(), target); } @@ -482,11 +510,13 @@ namespace osmium { * you can use std::back_inserter. * * @pre The buffer must be valid. + * @pre No builder can be open on this buffer. * * @param item The item to be added. */ void push_back(const osmium::memory::Item& item) { - assert(m_data); + assert(m_data && "This must be a valid buffer"); + assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope"); add_item(item); commit(); } @@ -537,7 +567,7 @@ namespace osmium { */ template t_iterator begin() { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return t_iterator(m_data, m_data + m_committed); } @@ -550,7 +580,7 @@ namespace osmium { * @returns Iterator to first OSMEntity in the buffer. */ iterator begin() { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return iterator(m_data, m_data + m_committed); } @@ -565,7 +595,7 @@ namespace osmium { */ template t_iterator get_iterator(size_t offset) { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return t_iterator(m_data + offset, m_data + m_committed); } @@ -579,7 +609,7 @@ namespace osmium { * buffer. */ iterator get_iterator(size_t offset) { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return iterator(m_data + offset, m_data + m_committed); } @@ -593,7 +623,7 @@ namespace osmium { */ template t_iterator end() { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return t_iterator(m_data + m_committed, m_data + m_committed); } @@ -606,40 +636,40 @@ namespace osmium { * @returns End iterator. */ iterator end() { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return iterator(m_data + m_committed, m_data + m_committed); } template t_const_iterator cbegin() const { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return t_const_iterator(m_data, m_data + m_committed); } const_iterator cbegin() const { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return const_iterator(m_data, m_data + m_committed); } template t_const_iterator get_iterator(size_t offset) const { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return t_const_iterator(m_data + offset, m_data + m_committed); } const_iterator get_iterator(size_t offset) const { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return const_iterator(m_data + offset, m_data + m_committed); } template t_const_iterator cend() const { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return t_const_iterator(m_data + m_committed, m_data + m_committed); } const_iterator cend() const { - assert(m_data); + assert(m_data && "This must be a valid buffer"); return const_iterator(m_data + m_committed, m_data + m_committed); } @@ -698,7 +728,7 @@ namespace osmium { */ template void purge_removed(TCallbackClass* callback) { - assert(m_data); + assert(m_data && "This must be a valid buffer"); if (begin() == end()) { return; } diff --git a/include/osmium/memory/collection.hpp b/include/osmium/memory/collection.hpp index 2a2c04088..fb413ff2d 100644 --- a/include/osmium/memory/collection.hpp +++ b/include/osmium/memory/collection.hpp @@ -46,9 +46,9 @@ namespace osmium { template class CollectionIterator { - // This data_type is either 'unsigned char*' or 'const unsigned char*' depending - // on whether TMember is const. This allows this class to be used as an iterator and - // as a const_iterator. + // This data_type is either 'unsigned char*' or 'const unsigned + // char*' depending on whether TMember is const. This allows this + // class to be used as an iterator and as a const_iterator. using data_type = typename std::conditional::value, const unsigned char*, unsigned char*>::type; data_type m_data; @@ -92,11 +92,11 @@ namespace osmium { return m_data; } - TMember& operator*() const { + TMember& operator*() const noexcept { return *reinterpret_cast(m_data); } - TMember* operator->() const { + TMember* operator->() const noexcept { return reinterpret_cast(m_data); } @@ -118,9 +118,12 @@ namespace osmium { public: - using iterator = CollectionIterator; - using const_iterator = CollectionIterator; - using value_type = TMember; + using value_type = TMember; + using reference = TMember&; + using const_reference = const TMember&; + using iterator = CollectionIterator; + using const_iterator = CollectionIterator; + using size_type = size_t; static constexpr osmium::item_type itemtype = TCollectionItemType; @@ -128,31 +131,45 @@ namespace osmium { Item(sizeof(Collection), TCollectionItemType) { } - bool empty() const { + /** + * Does this collection contain any items? + * + * Complexity: Constant. + */ + bool empty() const noexcept { return sizeof(Collection) == byte_size(); } - iterator begin() { + /** + * Returns the number of items in this collection. + * + * Complexity: Linear in the number of items. + */ + size_type size() const noexcept { + return static_cast(std::distance(begin(), end())); + } + + iterator begin() noexcept { return iterator(data() + sizeof(Collection)); } - iterator end() { + iterator end() noexcept { return iterator(data() + byte_size()); } - const_iterator cbegin() const { + const_iterator cbegin() const noexcept { return const_iterator(data() + sizeof(Collection)); } - const_iterator cend() const { + const_iterator cend() const noexcept { return const_iterator(data() + byte_size()); } - const_iterator begin() const { + const_iterator begin() const noexcept { return cbegin(); } - const_iterator end() const { + const_iterator end() const noexcept { return cend(); } diff --git a/include/osmium/memory/item.hpp b/include/osmium/memory/item.hpp index 2df33c7f5..b72ca4d7c 100644 --- a/include/osmium/memory/item.hpp +++ b/include/osmium/memory/item.hpp @@ -59,9 +59,9 @@ namespace osmium { using item_size_type = uint32_t; // align datastructures to this many bytes - constexpr item_size_type align_bytes = 8; + constexpr const item_size_type align_bytes = 8; - inline std::size_t padded_length(std::size_t length) noexcept { + inline constexpr std::size_t padded_length(std::size_t length) noexcept { return (length + align_bytes - 1) & ~(align_bytes - 1); } diff --git a/include/osmium/osm/area.hpp b/include/osmium/osm/area.hpp index 490fbe95b..d146e97ce 100644 --- a/include/osmium/osm/area.hpp +++ b/include/osmium/osm/area.hpp @@ -50,7 +50,8 @@ DEALINGS IN THE SOFTWARE. namespace osmium { namespace builder { - template class ObjectBuilder; + template + class OSMObjectBuilder; } // namespace builder /** @@ -117,7 +118,8 @@ namespace osmium { */ class Area : public OSMObject { - friend class osmium::builder::ObjectBuilder; + template + friend class osmium::builder::OSMObjectBuilder; Area() : OSMObject(sizeof(Area), osmium::item_type::area) { @@ -130,6 +132,8 @@ namespace osmium { /** * Was this area created from a way? (In contrast to areas * created from a relation and their members.) + * + * Complexity: Constant. */ bool from_way() const noexcept { return (positive_id() & 0x1) == 0; @@ -137,6 +141,8 @@ namespace osmium { /** * Return the Id of the way or relation this area was created from. + * + * Complexity: Constant. */ osmium::object_id_type orig_id() const noexcept { return osmium::area_id_to_object_id(id()); @@ -145,6 +151,8 @@ namespace osmium { /** * Count the number of outer and inner rings of this area. * + * Complexity: Linear in the number of rings. + * * @returns Pair (number outer rings, number inner rings) */ std::pair num_rings() const { diff --git a/include/osmium/osm/changeset.hpp b/include/osmium/osm/changeset.hpp index 8a503caac..828a2c2bc 100644 --- a/include/osmium/osm/changeset.hpp +++ b/include/osmium/osm/changeset.hpp @@ -51,7 +51,7 @@ namespace osmium { namespace builder { class ChangesetDiscussionBuilder; - template class ObjectBuilder; + class ChangesetBuilder; } // namespace builder class Changeset; @@ -129,20 +129,12 @@ namespace osmium { class ChangesetDiscussion : public osmium::memory::Collection { - friend class osmium::builder::ObjectBuilder; - public: - using size_type = size_t; - ChangesetDiscussion() : osmium::memory::Collection() { } - size_type size() const noexcept { - return static_cast(std::distance(begin(), end())); - } - }; // class ChangesetDiscussion static_assert(sizeof(ChangesetDiscussion) % osmium::memory::align_bytes == 0, "Class osmium::ChangesetDiscussion has wrong size to be aligned properly!"); @@ -156,7 +148,7 @@ namespace osmium { */ class Changeset : public osmium::OSMEntity { - friend class osmium::builder::ObjectBuilder; + friend class osmium::builder::ChangesetBuilder; osmium::Box m_bounds; osmium::Timestamp m_created_at; @@ -173,10 +165,14 @@ namespace osmium { OSMEntity(sizeof(Changeset), osmium::item_type::changeset) { } - void set_user_size(string_size_type size) { + void set_user_size(string_size_type size) noexcept { m_user_size = size; } + string_size_type user_size() const noexcept { + return m_user_size; + } + unsigned char* subitems_position() { return data() + osmium::memory::padded_length(sizeof(Changeset) + m_user_size); } diff --git a/include/osmium/osm/crc.hpp b/include/osmium/osm/crc.hpp index 2abeac4a1..bf057fd3a 100644 --- a/include/osmium/osm/crc.hpp +++ b/include/osmium/osm/crc.hpp @@ -100,15 +100,15 @@ namespace osmium { return m_crc; } - void update_bool(const bool value) { + void update_bool(const bool value) noexcept { m_crc.process_byte(value); } - void update_int8(const uint8_t value) { + void update_int8(const uint8_t value) noexcept { m_crc.process_byte(value); } - void update_int16(const uint16_t value) { + void update_int16(const uint16_t value) noexcept { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint16_t)); #else @@ -117,7 +117,7 @@ namespace osmium { #endif } - void update_int32(const uint32_t value) { + void update_int32(const uint32_t value) noexcept { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint32_t)); #else @@ -126,7 +126,7 @@ namespace osmium { #endif } - void update_int64(const uint64_t value) { + void update_int64(const uint64_t value) noexcept { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint64_t)); #else @@ -135,57 +135,57 @@ namespace osmium { #endif } - void update_string(const char* str) { + void update_string(const char* str) noexcept { while (*str) { m_crc.process_byte(*str++); } } - void update(const Timestamp& timestamp) { + void update(const Timestamp& timestamp) noexcept { update_int32(uint32_t(timestamp)); } - void update(const osmium::Location& location) { + void update(const osmium::Location& location) noexcept { update_int32(location.x()); update_int32(location.y()); } - void update(const osmium::Box& box) { + void update(const osmium::Box& box) noexcept { update(box.bottom_left()); update(box.top_right()); } - void update(const NodeRef& node_ref) { + void update(const NodeRef& node_ref) noexcept { update_int64(node_ref.ref()); update(node_ref.location()); } - void update(const NodeRefList& node_refs) { + void update(const NodeRefList& node_refs) noexcept { for (const NodeRef& node_ref : node_refs) { update(node_ref); } } - void update(const TagList& tags) { + void update(const TagList& tags) noexcept { for (const Tag& tag : tags) { update_string(tag.key()); update_string(tag.value()); } } - void update(const osmium::RelationMember& member) { + void update(const osmium::RelationMember& member) noexcept { update_int64(member.ref()); update_int16(uint16_t(member.type())); update_string(member.role()); } - void update(const osmium::RelationMemberList& members) { + void update(const osmium::RelationMemberList& members) noexcept { for (const RelationMember& member : members) { update(member); } } - void update(const osmium::OSMObject& object) { + void update(const osmium::OSMObject& object) noexcept { update_int64(object.id()); update_bool(object.visible()); update_int32(object.version()); @@ -195,22 +195,22 @@ namespace osmium { update(object.tags()); } - void update(const osmium::Node& node) { + void update(const osmium::Node& node) noexcept { update(static_cast(node)); update(node.location()); } - void update(const osmium::Way& way) { + void update(const osmium::Way& way) noexcept { update(static_cast(way)); update(way.nodes()); } - void update(const osmium::Relation& relation) { + void update(const osmium::Relation& relation) noexcept { update(static_cast(relation)); update(relation.members()); } - void update(const osmium::Area& area) { + void update(const osmium::Area& area) noexcept { update(static_cast(area)); for (const auto& subitem : area) { if (subitem.type() == osmium::item_type::outer_ring || @@ -220,7 +220,7 @@ namespace osmium { } } - void update(const osmium::ChangesetDiscussion& discussion) { + void update(const osmium::ChangesetDiscussion& discussion) noexcept { for (const auto& comment : discussion) { update(comment.date()); update_int32(comment.uid()); @@ -229,7 +229,7 @@ namespace osmium { } } - void update(const osmium::Changeset& changeset) { + void update(const osmium::Changeset& changeset) noexcept { update_int64(changeset.id()); update(changeset.created_at()); update(changeset.closed_at()); diff --git a/include/osmium/osm/entity_bits.hpp b/include/osmium/osm/entity_bits.hpp index b8e9ddba8..05afe3b07 100644 --- a/include/osmium/osm/entity_bits.hpp +++ b/include/osmium/osm/entity_bits.hpp @@ -60,7 +60,9 @@ namespace osmium { * assert(! (entities & osmium::osm_entity_bits::changeset)); * @endcode */ - enum type : unsigned char { + enum type : unsigned char { // this should have been an enum class + // but now we can't change it any more + // without breaking lots of code nothing = 0x00, node = 0x01, @@ -75,8 +77,16 @@ namespace osmium { }; // enum type - inline type operator|(const type lhs, const type rhs) noexcept { - return static_cast(static_cast(lhs) | static_cast (rhs)); + inline constexpr type operator|(const type lhs, const type rhs) noexcept { + return static_cast(static_cast(lhs) | static_cast(rhs)); + } + + inline constexpr type operator&(const type lhs, const type rhs) noexcept { + return static_cast(static_cast(lhs) & static_cast(rhs)); + } + + inline constexpr type operator~(const type value) noexcept { + return all & static_cast(~static_cast(value)); } inline type& operator|=(type& lhs, const type rhs) noexcept { @@ -84,14 +94,6 @@ namespace osmium { return lhs; } - inline type operator&(const type lhs, const type rhs) noexcept { - return static_cast(static_cast(lhs) & static_cast (rhs)); - } - - inline type operator~(const type value) noexcept { - return static_cast(~static_cast(value)); - } - inline type operator&=(type& lhs, const type rhs) noexcept { lhs = lhs & rhs; return lhs; @@ -104,7 +106,7 @@ namespace osmium { * changeset. */ inline type from_item_type(osmium::item_type item_type) noexcept { - auto ut = static_cast::type>(item_type); + const auto ut = static_cast::type>(item_type); assert(ut <= 0x05); if (ut == 0) { return nothing; diff --git a/include/osmium/osm/location.hpp b/include/osmium/osm/location.hpp index c5da620cb..d20871759 100644 --- a/include/osmium/osm/location.hpp +++ b/include/osmium/osm/location.hpp @@ -86,23 +86,31 @@ namespace osmium { ++str; } - // there has to be at least one digit - if (*str >= '0' && *str <= '9') { - result = *str - '0'; - ++str; + if (*str != '.') { + // there has to be at least one digit + if (*str >= '0' && *str <= '9') { + result = *str - '0'; + ++str; + } else { + goto error; + } + + // optional additional digits before decimal point + while (*str >= '0' && *str <= '9' && max_digits > 0) { + result = result * 10 + (*str - '0'); + ++str; + --max_digits; + } + + if (max_digits == 0) { + goto error; + } } else { - goto error; - } - - // optional additional digits before decimal point - while (*str >= '0' && *str <= '9' && max_digits > 0) { - result = result * 10 + (*str - '0'); - ++str; - --max_digits; - } - - if (max_digits == 0) { - goto error; + // need at least one digit after decimal dot if there was no + // digit before decimal dot + if (*(str + 1) < '0' || *(str + 1) > '9') { + goto error; + } } // optional decimal point @@ -163,18 +171,20 @@ namespace osmium { } if (scale < 0) { - result = 0; + for (; scale < 0 && result > 0; ++scale) { + result /= 10; + } } else { for (; scale > 0; --scale) { result *= 10; } + } - result = (result + 5) / 10 * sign; + result = (result + 5) / 10 * sign; - if (result > std::numeric_limits::max() || - result < std::numeric_limits::min()) { - goto error; - } + if (result > std::numeric_limits::max() || + result < std::numeric_limits::min()) { + goto error; } *data = str; diff --git a/include/osmium/osm/node.hpp b/include/osmium/osm/node.hpp index 677ffc784..f3df5e961 100644 --- a/include/osmium/osm/node.hpp +++ b/include/osmium/osm/node.hpp @@ -41,12 +41,14 @@ DEALINGS IN THE SOFTWARE. namespace osmium { namespace builder { - template class ObjectBuilder; + template + class OSMObjectBuilder; } // namespace builder class Node : public OSMObject { - friend class osmium::builder::ObjectBuilder; + template + friend class osmium::builder::OSMObjectBuilder; osmium::Location m_location; @@ -62,7 +64,7 @@ namespace osmium { return m_location; } - Node& set_location(const osmium::Location& location) { + Node& set_location(const osmium::Location& location) noexcept { m_location = location; return *this; } diff --git a/include/osmium/osm/node_ref_list.hpp b/include/osmium/osm/node_ref_list.hpp index 6cfdf2295..f430b63a8 100644 --- a/include/osmium/osm/node_ref_list.hpp +++ b/include/osmium/osm/node_ref_list.hpp @@ -52,12 +52,23 @@ namespace osmium { public: + using value_type = NodeRef; + using reference = NodeRef&; + using const_reference = const NodeRef&; + using iterator = NodeRef*; + using const_iterator = const NodeRef*; + using const_reverse_iterator = std::reverse_iterator; + using difference_type = std::ptrdiff_t; + using size_type = std::size_t; + explicit NodeRefList(osmium::item_type itemtype) noexcept : osmium::memory::Item(sizeof(NodeRefList), itemtype) { } /** * Checks whether the collection is empty. + * + * Complexity: Constant. */ bool empty() const noexcept { return sizeof(NodeRefList) == byte_size(); @@ -65,8 +76,10 @@ namespace osmium { /** * Returns the number of NodeRefs in the collection. + * + * Complexity: Constant. */ - size_t size() const noexcept { + size_type size() const noexcept { const auto size_node_refs = byte_size() - sizeof(NodeRefList); assert(size_node_refs % sizeof(NodeRef) == 0); return size_node_refs / sizeof(NodeRef); @@ -75,11 +88,13 @@ namespace osmium { /** * Access specified element. * + * Complexity: Constant. + * * @pre @code n < size() @endcode * * @param n Get the n-th element of the collection. */ - const NodeRef& operator[](size_t n) const noexcept { + const NodeRef& operator[](size_type n) const noexcept { assert(n < size()); const NodeRef* node_ref = &*(cbegin()); return node_ref[n]; @@ -88,6 +103,8 @@ namespace osmium { /** * Access the first element. * + * Complexity: Constant. + * * @pre @code !empty() @endcode */ const NodeRef& front() const noexcept { @@ -98,6 +115,8 @@ namespace osmium { /** * Access the last element. * + * Complexity: Constant. + * * @pre @code !empty() @endcode */ const NodeRef& back() const noexcept { @@ -109,6 +128,8 @@ namespace osmium { * Checks whether the first and last node in the collection have the * same ID. The locations are not checked. * + * Complexity: Constant. + * * @pre @code !empty() @endcode */ bool is_closed() const noexcept { @@ -119,6 +140,8 @@ namespace osmium { * Checks whether the first and last node in the collection have the * same ID. The locations are not checked. * + * Complexity: Constant. + * * @pre @code !empty() @endcode */ bool ends_have_same_id() const noexcept { @@ -129,6 +152,8 @@ namespace osmium { * Checks whether the first and last node in the collection have the * same location. The IDs are not checked. * + * Complexity: Constant. + * * @pre @code !empty() @endcode * @pre @code front().location() && back().location() @endcode */ @@ -137,10 +162,6 @@ namespace osmium { return front().location() == back().location(); } - using iterator = NodeRef*; - using const_iterator = const NodeRef*; - using const_reverse_iterator = std::reverse_iterator; - /// Returns an iterator to the beginning. iterator begin() noexcept { return iterator(data() + sizeof(NodeRefList)); diff --git a/include/osmium/osm/object.hpp b/include/osmium/osm/object.hpp index caa6fbcdd..01fe24908 100644 --- a/include/osmium/osm/object.hpp +++ b/include/osmium/osm/object.hpp @@ -52,11 +52,19 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + namespace builder { + template + class OSMObjectBuilder; + } // namespace builder + /** * OSMObject (Node, Way, Relation, or Area). */ class OSMObject : public osmium::OSMEntity { + template + friend class osmium::builder::OSMObjectBuilder; + object_id_type m_id; bool m_deleted : 1; object_version_type m_version : 31; diff --git a/include/osmium/osm/relation.hpp b/include/osmium/osm/relation.hpp index 2aa9caaf1..8c096807e 100644 --- a/include/osmium/osm/relation.hpp +++ b/include/osmium/osm/relation.hpp @@ -43,11 +43,14 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { namespace builder { - template class ObjectBuilder; + template + class OSMObjectBuilder; + class RelationMemberListBuilder; } // namespace builder @@ -109,7 +112,8 @@ namespace osmium { return m_ref; } - RelationMember& ref(object_id_type ref) noexcept { + /// @deprecated Use set_ref() instead. + OSMIUM_DEPRECATED RelationMember& ref(object_id_type ref) noexcept { m_ref = ref; return *this; } @@ -149,23 +153,18 @@ namespace osmium { public: - using size_type = size_t; - RelationMemberList() : osmium::memory::Collection() { } - size_type size() const noexcept { - return static_cast(std::distance(begin(), end())); - } - }; // class RelationMemberList static_assert(sizeof(RelationMemberList) % osmium::memory::align_bytes == 0, "Class osmium::RelationMemberList has wrong size to be aligned properly!"); class Relation : public OSMObject { - friend class osmium::builder::ObjectBuilder; + template + friend class osmium::builder::OSMObjectBuilder; Relation() noexcept : OSMObject(sizeof(Relation), osmium::item_type::relation) { diff --git a/include/osmium/osm/tag.hpp b/include/osmium/osm/tag.hpp index cd2a913b7..e2537cef4 100644 --- a/include/osmium/osm/tag.hpp +++ b/include/osmium/osm/tag.hpp @@ -86,12 +86,14 @@ namespace osmium { }; // class Tag - inline bool operator==(const Tag& a, const Tag& b) { - return !std::strcmp(a.key(), b.key()) && !std::strcmp(a.value(), b.value()); + inline bool operator==(const Tag& lhs, const Tag& rhs) { + return !std::strcmp(lhs.key(), rhs.key()) && + !std::strcmp(lhs.value(), rhs.value()); } - inline bool operator<(const Tag& a, const Tag& b) { - return (!std::strcmp(a.key(), b.key()) && (std::strcmp(a.value(), b.value()) < 0)) || (std::strcmp(a.key(), b.key()) < 0); + inline bool operator<(const Tag& lhs, const Tag& rhs) { + const auto c = std::strcmp(lhs.key(), rhs.key()); + return (c == 0 ? std::strcmp(lhs.value(), rhs.value()) : c) < 0; } /** @@ -112,19 +114,10 @@ namespace osmium { public: - using size_type = size_t; - TagList() : osmium::memory::Collection() { } - /** - * Returns the number of tags in this tag list. - */ - size_type size() const noexcept { - return static_cast(std::distance(begin(), end())); - } - /** * Get tag value for the given tag key. If the key is not set, returns * the default_value. diff --git a/include/osmium/osm/way.hpp b/include/osmium/osm/way.hpp index f6713fef4..e4415ef6c 100644 --- a/include/osmium/osm/way.hpp +++ b/include/osmium/osm/way.hpp @@ -44,7 +44,8 @@ DEALINGS IN THE SOFTWARE. namespace osmium { namespace builder { - template class ObjectBuilder; + template + class OSMObjectBuilder; } // namespace builder /** @@ -66,7 +67,8 @@ namespace osmium { class Way : public OSMObject { - friend class osmium::builder::ObjectBuilder; + template + friend class osmium::builder::OSMObjectBuilder; Way() noexcept : OSMObject(sizeof(Way), osmium::item_type::way) { diff --git a/include/osmium/relations/collector.hpp b/include/osmium/relations/collector.hpp index b8455b4eb..53f6de90f 100644 --- a/include/osmium/relations/collector.hpp +++ b/include/osmium/relations/collector.hpp @@ -40,6 +40,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include @@ -353,7 +354,7 @@ namespace osmium { member_meta(member.type()).emplace_back(member.ref(), m_relations.size(), n); relation_meta.increment_need_members(); } else { - member.ref(0); // set member id to zero to indicate we are not interested + member.set_ref(0); // set member id to zero to indicate we are not interested } ++n; } @@ -494,12 +495,65 @@ namespace osmium { return m_members_buffer; } + /** + * Is the given member available in the members buffer? + * + * If you also need the offset of the object, use + * get_availability_and_offset() instead, it is more efficient + * that way. + * + * @param type Item type + * @param id Object Id + * @returns True if the object is available, false otherwise. + */ + bool is_available(osmium::item_type type, osmium::object_id_type id) { + const auto range = find_member_meta(type, id); + assert(!range.empty()); + return range.begin()->is_available(); + } + + /** + * Get offset of a member in the members buffer. + * + * @pre The member must be available. If you are not sure, call + * get_availability_and_offset() instead. + * @param type Item type + * @param id Object Id + * @returns The offset of the object in the members buffer. + */ size_t get_offset(osmium::item_type type, osmium::object_id_type id) { const auto range = find_member_meta(type, id); assert(!range.empty()); + assert(range.begin()->is_available()); return range.begin()->buffer_offset(); } + /** + * Checks whether a member is available in the members buffer + * and returns its offset. + * + * If the member is not available, the boolean returned as the + * first element in the pair is false. In that case the offset + * in the second element is undefined. + * + * If the member is available, the boolean returned as the first + * element in the pair is true and the second element of the + * pair contains the offset into the members buffer. + * + * @param type Item type + * @param id Object Id + * @returns Pair of bool (showing availability) and the offset. + */ + std::pair get_availability_and_offset(osmium::item_type type, osmium::object_id_type id) { + const auto range = find_member_meta(type, id); + assert(!range.empty()); + if (range.begin()->is_available()) { + return std::make_pair(true, range.begin()->buffer_offset()); + } else { + return std::make_pair(false, 0); + } + } + template void read_relations(TIter begin, TIter end) { HandlerPass1 handler(*static_cast(this)); @@ -525,7 +579,7 @@ namespace osmium { /** * Decide whether to purge removed members and then do it. * - * Currently the purging is done every thousand calls. + * Currently the purging is done every 10000 calls. * This could probably be improved upon. */ void possibly_purge_removed_members() { diff --git a/include/osmium/relations/detail/member_meta.hpp b/include/osmium/relations/detail/member_meta.hpp index b28dca1d0..7624a60f3 100644 --- a/include/osmium/relations/detail/member_meta.hpp +++ b/include/osmium/relations/detail/member_meta.hpp @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include @@ -71,24 +72,44 @@ namespace osmium { /** * Offset in the buffer where the object is stored. + * + * The default value is one that will never be valid, so it is + * easier to catch problems. */ - size_t m_buffer_offset { 0 }; + size_t m_buffer_offset = std::numeric_limits::max(); + /** + * Has this member been found in the input data. + */ + bool m_available = false; + + /** + * Marks this member as removed. It can not be used any more. + */ bool m_removed = false; public: /** - * Create new MemberMeta. The variant with zeros for relation_pos and - * member_pos is used to create dummy MemberMeta that can be compared - * to the MemberMeta in the vectors using the equal_range algorithm. + * Create new MemberMeta. */ - explicit MemberMeta(osmium::object_id_type member_id, size_t relation_pos=0, size_t member_pos=0) noexcept : + explicit MemberMeta(osmium::object_id_type member_id, size_t relation_pos, size_t member_pos) noexcept : m_member_id(member_id), m_relation_pos(relation_pos), m_member_pos(member_pos) { } + /** + * Create new MemberMeta. This constructor is used to create + * dummy MemberMeta objects that can be compared to the + * MemberMetas in a vector using the equal_range algorithm. + */ + explicit MemberMeta(osmium::object_id_type member_id) noexcept : + m_member_id(member_id), + m_relation_pos(0), + m_member_pos(0) { + } + osmium::object_id_type member_id() const noexcept { return m_member_id; } @@ -107,6 +128,11 @@ namespace osmium { void set_buffer_offset(size_t offset) noexcept { m_buffer_offset = offset; + m_available = true; + } + + bool is_available() const noexcept { + return m_available; } bool removed() const noexcept { @@ -124,8 +150,8 @@ namespace osmium { * Used to sort a vector of MemberMeta objects and to later find * them using binary search. */ - inline bool operator<(const MemberMeta& a, const MemberMeta& b) noexcept { - return a.member_id() < b.member_id(); + inline bool operator<(const MemberMeta& lhs, const MemberMeta& rhs) noexcept { + return lhs.member_id() < rhs.member_id(); } template diff --git a/include/osmium/thread/queue.hpp b/include/osmium/thread/queue.hpp index 6f4f7b1d9..5ae910891 100644 --- a/include/osmium/thread/queue.hpp +++ b/include/osmium/thread/queue.hpp @@ -51,7 +51,7 @@ namespace osmium { namespace thread { - static const std::chrono::milliseconds full_queue_sleep_duration { 10 }; // XXX + static const std::chrono::milliseconds max_wait{10}; /** * A thread-safe queue. @@ -70,9 +70,12 @@ namespace osmium { std::queue m_queue; - /// Used to signal readers when data is available in the queue. + /// Used to signal consumers when data is available in the queue. std::condition_variable m_data_available; + /// Used to signal producers when queue is not full. + std::condition_variable m_space_available; + #ifdef OSMIUM_DEBUG_QUEUE_SIZE /// The largest size the queue has been so far. size_t m_largest_size; @@ -109,7 +112,8 @@ namespace osmium { m_name(name), m_mutex(), m_queue(), - m_data_available() + m_data_available(), + m_space_available() #ifdef OSMIUM_DEBUG_QUEUE_SIZE , m_largest_size(0), @@ -123,13 +127,20 @@ namespace osmium { ~Queue() { #ifdef OSMIUM_DEBUG_QUEUE_SIZE - std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times in " << m_push_counter << " push() calls and was empty " << m_empty_counter << " times in " << m_pop_counter << " pop() calls\n"; + std::cerr << "queue '" << m_name + << "' with max_size=" << m_max_size + << " had largest size " << m_largest_size + << " and was full " << m_full_counter + << " times in " << m_push_counter + << " push() calls and was empty " << m_empty_counter + << " times in " << m_pop_counter + << " pop() calls\n"; #endif } /** - * Push an element onto the queue. If the queue has a max size, this - * call will block if the queue is full. + * Push an element onto the queue. If the queue has a max size, + * this call will block if the queue is full. */ void push(T value) { #ifdef OSMIUM_DEBUG_QUEUE_SIZE @@ -137,13 +148,16 @@ namespace osmium { #endif if (m_max_size) { while (size() >= m_max_size) { - std::this_thread::sleep_for(full_queue_sleep_duration); + std::unique_lock lock{m_mutex}; + m_space_available.wait_for(lock, max_wait, [this] { + return m_queue.size() < m_max_size; + }); #ifdef OSMIUM_DEBUG_QUEUE_SIZE ++m_full_counter; #endif } } - std::lock_guard lock(m_mutex); + std::lock_guard lock{m_mutex}; m_queue.push(std::move(value)); #ifdef OSMIUM_DEBUG_QUEUE_SIZE if (m_largest_size < m_queue.size()) { @@ -157,7 +171,7 @@ namespace osmium { #ifdef OSMIUM_DEBUG_QUEUE_SIZE ++m_pop_counter; #endif - std::unique_lock lock(m_mutex); + std::unique_lock lock{m_mutex}; #ifdef OSMIUM_DEBUG_QUEUE_SIZE if (m_queue.empty()) { ++m_empty_counter; @@ -169,6 +183,10 @@ namespace osmium { if (!m_queue.empty()) { value = std::move(m_queue.front()); m_queue.pop(); + lock.unlock(); + if (m_max_size) { + m_space_available.notify_one(); + } } } @@ -176,25 +194,30 @@ namespace osmium { #ifdef OSMIUM_DEBUG_QUEUE_SIZE ++m_pop_counter; #endif - std::lock_guard lock(m_mutex); - if (m_queue.empty()) { + { + std::lock_guard lock{m_mutex}; + if (m_queue.empty()) { #ifdef OSMIUM_DEBUG_QUEUE_SIZE - ++m_empty_counter; + ++m_empty_counter; #endif - return false; + return false; + } + value = std::move(m_queue.front()); + m_queue.pop(); + } + if (m_max_size) { + m_space_available.notify_one(); } - value = std::move(m_queue.front()); - m_queue.pop(); return true; } bool empty() const { - std::lock_guard lock(m_mutex); + std::lock_guard lock{m_mutex}; return m_queue.empty(); } size_t size() const { - std::lock_guard lock(m_mutex); + std::lock_guard lock{m_mutex}; return m_queue.size(); } diff --git a/include/osmium/util/progress_bar.hpp b/include/osmium/util/progress_bar.hpp index 814aa2c0e..0e528fc64 100644 --- a/include/osmium/util/progress_bar.hpp +++ b/include/osmium/util/progress_bar.hpp @@ -172,6 +172,18 @@ namespace osmium { } } + /** + * Removes the progress bar. Call this before doing any other output. + * The next time update() is called, the progress bar will be visible + * again. + */ + void remove() { + if (m_enable) { + std::cerr << spc() << " \r"; + m_prev_percent = 100 + 1; + } + } + }; // class ProgressBar } // namespace osmium diff --git a/include/osmium/version.hpp b/include/osmium/version.hpp index 6f3b0a368..09c576288 100644 --- a/include/osmium/version.hpp +++ b/include/osmium/version.hpp @@ -34,9 +34,9 @@ DEALINGS IN THE SOFTWARE. */ #define LIBOSMIUM_VERSION_MAJOR 2 -#define LIBOSMIUM_VERSION_MINOR 9 +#define LIBOSMIUM_VERSION_MINOR 10 #define LIBOSMIUM_VERSION_PATCH 0 -#define LIBOSMIUM_VERSION_STRING "2.9.0" +#define LIBOSMIUM_VERSION_STRING "2.10.0" #endif // OSMIUM_VERSION_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6230cde23..051574e08 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -112,12 +112,6 @@ if(NOT Threads_FOUND) set(Threads_FOUND FALSE) endif() -if(GEOS_FOUND AND PROJ_FOUND) - set(GEOS_AND_PROJ_FOUND TRUE) -else() - set(GEOS_AND_PROJ_FOUND FALSE) -endif() - #----------------------------------------------------------------------------- # @@ -127,45 +121,45 @@ endif() add_unit_test(area test_area_id) add_unit_test(area test_node_ref_segment) -add_unit_test(basic test_area) -add_unit_test(basic test_box) -add_unit_test(basic test_changeset) -add_unit_test(basic test_crc) -add_unit_test(basic test_entity_bits) -add_unit_test(basic test_location) -add_unit_test(basic test_node) -add_unit_test(basic test_node_ref) -add_unit_test(basic test_object_comparisons) -add_unit_test(basic test_relation) -add_unit_test(basic test_timestamp) -add_unit_test(basic test_types_from_string) -add_unit_test(basic test_way) +add_unit_test(osm test_area) +add_unit_test(osm test_box) +add_unit_test(osm test_changeset) +add_unit_test(osm test_crc) +add_unit_test(osm test_entity_bits) +add_unit_test(osm test_location) +add_unit_test(osm test_node) +add_unit_test(osm test_node_ref) +add_unit_test(osm test_object_comparisons) +add_unit_test(osm test_relation) +add_unit_test(osm test_timestamp) +add_unit_test(osm test_types_from_string) +add_unit_test(osm test_way) -add_unit_test(buffer test_buffer_basics) -add_unit_test(buffer test_buffer_node) -add_unit_test(buffer test_buffer_purge) +add_unit_test(memory test_buffer_basics) +add_unit_test(memory test_buffer_node) +add_unit_test(memory test_buffer_purge) add_unit_test(builder test_attr) - -add_unit_test(geom test_factory_with_projection - ENABLE_IF ${GEOS_AND_PROJ_FOUND} - LIBS ${GEOS_LIBRARY} ${PROJ_LIBRARY}) +add_unit_test(builder test_object_builder) add_unit_test(geom test_crs ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY}) add_unit_test(geom test_exception) +add_unit_test(geom test_factory_with_projection ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY}) add_unit_test(geom test_geojson) add_unit_test(geom test_geos ENABLE_IF ${GEOS_FOUND} LIBS ${GEOS_LIBRARY}) -add_unit_test(geom test_geos_wkb ENABLE_IF ${GEOS_FOUND} LIBS ${GEOS_LIBRARY}) add_unit_test(geom test_mercator) add_unit_test(geom test_ogr ENABLE_IF ${GDAL_FOUND} LIBS ${GDAL_LIBRARY}) +add_unit_test(geom test_ogr_wkb ENABLE_IF ${GDAL_FOUND} LIBS ${GDAL_LIBRARY}) add_unit_test(geom test_projection ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY}) -add_unit_test(geom test_tile ENABLE_IF ${GEOS_FOUND}) +add_unit_test(geom test_tile) add_unit_test(geom test_wkb) add_unit_test(geom test_wkt) +add_unit_test(index test_id_set) add_unit_test(index test_id_to_location ENABLE_IF ${SPARSEHASH_FOUND}) add_unit_test(index test_file_based_index) +add_unit_test(io test_compression_factory) add_unit_test(io test_bzip2 ENABLE_IF ${BZIP2_FOUND} LIBS ${BZIP2_LIBRARIES}) add_unit_test(io test_file_formats) add_unit_test(io test_reader LIBS "${OSMIUM_XML_LIBRARIES};${OSMIUM_PBF_LIBRARIES}") diff --git a/test/data-tests/testdata-xml.cpp b/test/data-tests/testdata-xml.cpp index 0d2739a3a..eb984f851 100644 --- a/test/data-tests/testdata-xml.cpp +++ b/test/data-tests/testdata-xml.cpp @@ -83,7 +83,7 @@ header_buffer_type parse_xml(std::string input) { osmium::io::detail::add_to_queue(input_queue, std::move(input)); osmium::io::detail::add_to_queue(input_queue, std::string{}); - osmium::io::detail::XMLParser parser{input_queue, output_queue, header_promise, osmium::osm_entity_bits::all}; + osmium::io::detail::XMLParser parser{input_queue, output_queue, header_promise, osmium::io::detail::reader_options{}}; parser.parse(); header_buffer_type result; diff --git a/test/examples/CMakeLists.txt b/test/examples/CMakeLists.txt new file mode 100644 index 000000000..90bf76ea9 --- /dev/null +++ b/test/examples/CMakeLists.txt @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# +# CMake Config +# +# Libosmium example tests +# +#----------------------------------------------------------------------------- + +message(STATUS "Configuring example tests") + +file(GLOB _dirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/t/*) + +foreach(_dir ${_dirs}) + message(STATUS " adding test: ${_dir}") + add_subdirectory("${_dir}") +endforeach() + +message(STATUS "Configuring example tests - done") + + +#----------------------------------------------------------------------------- diff --git a/test/examples/t/pub_names/CMakeLists.txt b/test/examples/t/pub_names/CMakeLists.txt new file mode 100644 index 000000000..9a68ae869 --- /dev/null +++ b/test/examples/t/pub_names/CMakeLists.txt @@ -0,0 +1,7 @@ + +add_test(NAME examples_pub_names + COMMAND osmium_pub_names ${CMAKE_CURRENT_SOURCE_DIR}/pubs.osm) + +set_tests_properties(examples_pub_names PROPERTIES + PASS_REGULAR_EXPRESSION "^Im Holze\n$") + diff --git a/test/examples/t/pub_names/pubs.osm b/test/examples/t/pub_names/pubs.osm new file mode 100644 index 000000000..ee5422695 --- /dev/null +++ b/test/examples/t/pub_names/pubs.osm @@ -0,0 +1,7 @@ + + + + + + + diff --git a/test/examples/t/road_length/CMakeLists.txt b/test/examples/t/road_length/CMakeLists.txt new file mode 100644 index 000000000..6323f0731 --- /dev/null +++ b/test/examples/t/road_length/CMakeLists.txt @@ -0,0 +1,8 @@ + +add_test(NAME examples_road_length + COMMAND osmium_road_length ${CMAKE_CURRENT_SOURCE_DIR}/road.osm) + +set_tests_properties(examples_road_length PROPERTIES + PASS_REGULAR_EXPRESSION "^Length: 0\\.405.*km\n$" +) + diff --git a/test/examples/t/road_length/road.osm b/test/examples/t/road_length/road.osm new file mode 100644 index 000000000..934ef46f7 --- /dev/null +++ b/test/examples/t/road_length/road.osm @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/include/catch.hpp b/test/include/catch.hpp index 879fc5b1d..2e6fe8d39 100644 --- a/test/include/catch.hpp +++ b/test/include/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.5.6 - * Generated: 2016-06-09 19:20:41.460328 + * Catch v1.5.8 + * Generated: 2016-10-26 12:07:30.938259 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. @@ -3223,10 +3223,11 @@ namespace Catch { bool matches( TestCaseInfo const& testCase ) const { // All patterns in a filter must match for the filter to be a match - for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) + for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { if( !(*it)->matches( testCase ) ) return false; - return true; + } + return true; } }; @@ -4719,8 +4720,11 @@ namespace Catch { std::string line; while( std::getline( f, line ) ) { line = trim(line); - if( !line.empty() && !startsWith( line, "#" ) ) - addTestOrTags( config, "\"" + line + "\"," ); + if( !line.empty() && !startsWith( line, "#" ) ) { + if( !startsWith( line, "\"" ) ) + line = "\"" + line + "\""; + addTestOrTags( config, line + "," ); + } } } @@ -5368,7 +5372,10 @@ namespace Catch { ++it ) { matchedTests++; TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); - Catch::cout() << testCaseInfo.name << std::endl; + if( startsWith( testCaseInfo.name, "#" ) ) + Catch::cout() << "\"" << testCaseInfo.name << "\"" << std::endl; + else + Catch::cout() << testCaseInfo.name << std::endl; } return matchedTests; } @@ -6454,7 +6461,7 @@ namespace Catch { namespace Catch { struct RandomNumberGenerator { - typedef int result_type; + typedef std::ptrdiff_t result_type; result_type operator()( result_type n ) const { return std::rand() % n; } @@ -7571,7 +7578,7 @@ namespace Catch { return os; } - Version libraryVersion( 1, 5, 6, "", 0 ); + Version libraryVersion( 1, 5, 8, "", 0 ); } @@ -7802,8 +7809,11 @@ namespace Catch { bool contains( std::string const& s, std::string const& infix ) { return s.find( infix ) != std::string::npos; } + char toLowerCh(char c) { + return static_cast( ::tolower( c ) ); + } void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), ::tolower ); + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); } std::string toLower( std::string const& s ) { std::string lc = s; @@ -8951,9 +8961,10 @@ namespace Catch { break; default: - // Escape control chars - based on contribution by @espenalb in PR #465 + // Escape control chars - based on contribution by @espenalb in PR #465 and + // by @mrpi PR #588 if ( ( c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) - os << "&#x" << std::uppercase << std::hex << static_cast( c ); + os << "&#x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast( c ) << ';'; else os << c; } @@ -9008,13 +9019,20 @@ namespace Catch { : m_tagIsOpen( false ), m_needsNewline( false ), m_os( &Catch::cout() ) - {} + { + // We encode control characters, which requires + // XML 1.1 + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + *m_os << "\n"; + } XmlWriter( std::ostream& os ) : m_tagIsOpen( false ), m_needsNewline( false ), m_os( &os ) - {} + { + *m_os << "\n"; + } ~XmlWriter() { while( !m_tags.empty() ) @@ -9181,7 +9199,7 @@ namespace Catch { virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); + m_xml.startElement( "TestCase" ).writeAttribute( "name", testInfo.name ); if ( m_config->showDurations() == ShowDurations::Always ) m_testCaseTimer.start(); @@ -9243,7 +9261,7 @@ namespace Catch { .writeText( assertionResult.getMessage() ); break; case ResultWas::FatalErrorCondition: - m_xml.scopedElement( "Fatal Error Condition" ) + m_xml.scopedElement( "FatalErrorCondition" ) .writeAttribute( "filename", assertionResult.getSourceInfo().file ) .writeAttribute( "line", assertionResult.getSourceInfo().line ) .writeText( assertionResult.getMessage() ); diff --git a/test/t/basic/test_entity_bits.cpp b/test/t/basic/test_entity_bits.cpp deleted file mode 100644 index 13de94b5f..000000000 --- a/test/t/basic/test_entity_bits.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "catch.hpp" - -#include - -TEST_CASE("entity_bits") { - - SECTION("can_be_set_and_checked") { - osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::node | osmium::osm_entity_bits::way; - REQUIRE(entities == (osmium::osm_entity_bits::node | osmium::osm_entity_bits::way)); - - entities |= osmium::osm_entity_bits::relation; - REQUIRE((entities & osmium::osm_entity_bits::object)); - - entities |= osmium::osm_entity_bits::area; - REQUIRE(entities == osmium::osm_entity_bits::object); - - REQUIRE(! (entities & osmium::osm_entity_bits::changeset)); - - entities &= osmium::osm_entity_bits::node; - REQUIRE((entities & osmium::osm_entity_bits::node)); - REQUIRE(! (entities & osmium::osm_entity_bits::way)); - REQUIRE(entities == osmium::osm_entity_bits::node); - - REQUIRE(osmium::osm_entity_bits::nothing == osmium::osm_entity_bits::from_item_type(osmium::item_type::undefined)); - REQUIRE(osmium::osm_entity_bits::node == osmium::osm_entity_bits::from_item_type(osmium::item_type::node)); - REQUIRE(osmium::osm_entity_bits::way == osmium::osm_entity_bits::from_item_type(osmium::item_type::way)); - REQUIRE(osmium::osm_entity_bits::relation == osmium::osm_entity_bits::from_item_type(osmium::item_type::relation)); - REQUIRE(osmium::osm_entity_bits::changeset == osmium::osm_entity_bits::from_item_type(osmium::item_type::changeset)); - REQUIRE(osmium::osm_entity_bits::area == osmium::osm_entity_bits::from_item_type(osmium::item_type::area)); - } - -} diff --git a/test/t/builder/test_object_builder.cpp b/test/t/builder/test_object_builder.cpp new file mode 100644 index 000000000..5b1256413 --- /dev/null +++ b/test/t/builder/test_object_builder.cpp @@ -0,0 +1,444 @@ + +#include "catch.hpp" + +#include +#include +#include + +TEST_CASE("create objects using builder") { + osmium::memory::Buffer buffer{1024*10}; + std::string user; + + SECTION("complete node with tags") { + SECTION("user length 0") { + user = ""; + } + SECTION("user length 1") { + user = "1"; + } + SECTION("user length 2") { + user = "12"; + } + SECTION("user length 3") { + user = "123"; + } + SECTION("user length 4") { + user = "1234"; + } + SECTION("user length 5") { + user = "12345"; + } + SECTION("user length 6") { + user = "123456"; + } + SECTION("user length 7") { + user = "1234567"; + } + SECTION("user length 8") { + user = "12345678"; + } + SECTION("user length 9") { + user = "123456789"; + } + SECTION("user length 10") { + user = "1234567890"; + } + SECTION("user length 11") { + user = "12345678901"; + } + SECTION("user length 12") { + user = "123456789012"; + } + SECTION("user length 13") { + user = "1234567890123"; + } + SECTION("user length 14") { + user = "12345678901234"; + } + SECTION("user length 15") { + user = "123456789012345"; + } + SECTION("user length 16") { + user = "1234567890123456"; + } + SECTION("user length 17") { + user = "12345678901234567"; + } + SECTION("user length 18") { + user = "123456789012345678"; + } + + osmium::Location loc{1.2, 3.4}; + + { + osmium::builder::NodeBuilder builder{buffer}; + + builder.set_id(17) + .set_visible(true) + .set_version(1) + .set_changeset(123) + .set_uid(555) + .set_timestamp("2015-07-01T00:00:01Z") + .set_location(loc) + .set_user(user); + + builder.add_tags({{"highway", "primary"}, {"oneway", "yes"}}); + } + + const auto& node = buffer.get(buffer.commit()); + + REQUIRE(node.id() == 17); + REQUIRE(node.version() == 1); + REQUIRE(node.changeset() == 123); + REQUIRE(node.uid() == 555); + REQUIRE(node.timestamp() == osmium::Timestamp{"2015-07-01T00:00:01Z"}); + REQUIRE(node.location() == loc); + + REQUIRE(user == node.user()); + + REQUIRE(node.tags().size() == 2); + } + + SECTION("complete way with tags") { + SECTION("user length 0") { + user = ""; + } + SECTION("user length 1") { + user = "1"; + } + SECTION("user length 2") { + user = "12"; + } + SECTION("user length 3") { + user = "123"; + } + SECTION("user length 4") { + user = "1234"; + } + SECTION("user length 5") { + user = "12345"; + } + SECTION("user length 6") { + user = "123456"; + } + SECTION("user length 7") { + user = "1234567"; + } + SECTION("user length 8") { + user = "12345678"; + } + SECTION("user length 9") { + user = "123456789"; + } + SECTION("user length 10") { + user = "1234567890"; + } + SECTION("user length 11") { + user = "12345678901"; + } + SECTION("user length 12") { + user = "123456789012"; + } + SECTION("user length 13") { + user = "1234567890123"; + } + SECTION("user length 14") { + user = "12345678901234"; + } + SECTION("user length 15") { + user = "123456789012345"; + } + SECTION("user length 16") { + user = "1234567890123456"; + } + SECTION("user length 17") { + user = "12345678901234567"; + } + SECTION("user length 18") { + user = "123456789012345678"; + } + + { + osmium::builder::WayBuilder builder{buffer}; + + builder.set_id(17) + .set_visible(true) + .set_version(1) + .set_changeset(123) + .set_uid(555) + .set_timestamp("2015-07-01T00:00:01Z") + .set_user(user); + + builder.add_tags({{"highway", "primary"}, {"oneway", "yes"}}); + } + + const auto& way = buffer.get(buffer.commit()); + + REQUIRE(way.id() == 17); + REQUIRE(way.version() == 1); + REQUIRE(way.changeset() == 123); + REQUIRE(way.uid() == 555); + REQUIRE(way.timestamp() == osmium::Timestamp{"2015-07-01T00:00:01Z"}); + + REQUIRE(user == way.user()); + + REQUIRE(way.tags().size() == 2); + } + + SECTION("complete relation with tags") { + SECTION("user length 0") { + user = ""; + } + SECTION("user length 1") { + user = "1"; + } + SECTION("user length 2") { + user = "12"; + } + SECTION("user length 3") { + user = "123"; + } + SECTION("user length 4") { + user = "1234"; + } + SECTION("user length 5") { + user = "12345"; + } + SECTION("user length 6") { + user = "123456"; + } + SECTION("user length 7") { + user = "1234567"; + } + SECTION("user length 8") { + user = "12345678"; + } + SECTION("user length 9") { + user = "123456789"; + } + SECTION("user length 10") { + user = "1234567890"; + } + SECTION("user length 11") { + user = "12345678901"; + } + SECTION("user length 12") { + user = "123456789012"; + } + SECTION("user length 13") { + user = "1234567890123"; + } + SECTION("user length 14") { + user = "12345678901234"; + } + SECTION("user length 15") { + user = "123456789012345"; + } + SECTION("user length 16") { + user = "1234567890123456"; + } + SECTION("user length 17") { + user = "12345678901234567"; + } + SECTION("user length 18") { + user = "123456789012345678"; + } + + { + osmium::builder::RelationBuilder builder{buffer}; + + builder.set_id(17) + .set_visible(true) + .set_version(1) + .set_changeset(123) + .set_uid(555) + .set_timestamp("2015-07-01T00:00:01Z") + .set_user(user); + + builder.add_tags({{"highway", "primary"}, {"oneway", "yes"}}); + } + + const auto& relation = buffer.get(buffer.commit()); + + REQUIRE(relation.id() == 17); + REQUIRE(relation.version() == 1); + REQUIRE(relation.changeset() == 123); + REQUIRE(relation.uid() == 555); + REQUIRE(relation.timestamp() == osmium::Timestamp{"2015-07-01T00:00:01Z"}); + + REQUIRE(user == relation.user()); + + REQUIRE(relation.tags().size() == 2); + } + + SECTION("complete changeset with tags") { + osmium::Location bl{-1.2, -3.4}; + osmium::Location tr{1.2, 3.4}; + + SECTION("user length 0") { + user = ""; + } + SECTION("user length 1") { + user = "1"; + } + SECTION("user length 2") { + user = "12"; + } + SECTION("user length 3") { + user = "123"; + } + SECTION("user length 4") { + user = "1234"; + } + SECTION("user length 5") { + user = "12345"; + } + SECTION("user length 6") { + user = "123456"; + } + SECTION("user length 7") { + user = "1234567"; + } + SECTION("user length 8") { + user = "12345678"; + } + SECTION("user length 9") { + user = "123456789"; + } + SECTION("user length 10") { + user = "1234567890"; + } + SECTION("user length 11") { + user = "12345678901"; + } + SECTION("user length 12") { + user = "123456789012"; + } + SECTION("user length 13") { + user = "1234567890123"; + } + SECTION("user length 14") { + user = "12345678901234"; + } + SECTION("user length 15") { + user = "123456789012345"; + } + SECTION("user length 16") { + user = "1234567890123456"; + } + SECTION("user length 17") { + user = "12345678901234567"; + } + SECTION("user length 18") { + user = "123456789012345678"; + } + + { + osmium::builder::ChangesetBuilder builder{buffer}; + + builder.set_id(17) + .set_uid(222) + .set_created_at(osmium::Timestamp{"2016-07-03T01:23:45Z"}) + .set_closed_at(osmium::Timestamp{"2016-07-03T01:23:48Z"}) + .set_num_changes(3) + .set_num_comments(2) + .set_bounds(osmium::Box{bl, tr}) + .set_user(user); + } + + const auto& changeset = buffer.get(buffer.commit()); + + REQUIRE(changeset.id() == 17); + REQUIRE(changeset.uid() == 222); + REQUIRE(changeset.created_at() == osmium::Timestamp{"2016-07-03T01:23:45Z"}); + REQUIRE(changeset.closed_at() == osmium::Timestamp{"2016-07-03T01:23:48Z"}); + REQUIRE(changeset.num_changes() == 3); + REQUIRE(changeset.num_comments() == 2); + + const auto& box = changeset.bounds(); + REQUIRE(box.bottom_left() == bl); + REQUIRE(box.top_right() == tr); + + REQUIRE(user == changeset.user()); + } + +} + +TEST_CASE("no call to set_user on node") { + osmium::memory::Buffer buffer{1024*10}; + + { + osmium::builder::NodeBuilder builder{buffer}; + } + + const auto& node = buffer.get(buffer.commit()); + + REQUIRE(*node.user() == '\0'); +} + +TEST_CASE("set_user with length on node") { + osmium::memory::Buffer buffer{1024*10}; + std::string user = "userx"; + + { + osmium::builder::NodeBuilder builder{buffer}; + builder.set_user(user.c_str(), 4); + } + + const auto& node = buffer.get(buffer.commit()); + + REQUIRE(std::string{"user"} == node.user()); +} + +TEST_CASE("no call to set_user on way") { + osmium::memory::Buffer buffer{1024*10}; + + { + osmium::builder::WayBuilder builder{buffer}; + } + + const auto& way = buffer.get(buffer.commit()); + + REQUIRE(*way.user() == '\0'); +} + +TEST_CASE("set_user with length on way") { + osmium::memory::Buffer buffer{1024*10}; + std::string user = "userx"; + + { + osmium::builder::WayBuilder builder{buffer}; + builder.set_user(user.c_str(), 4); + } + + const auto& way = buffer.get(buffer.commit()); + + REQUIRE(std::string{"user"} == way.user()); +} + +TEST_CASE("no call to set_user on changeset") { + osmium::memory::Buffer buffer{1024*10}; + + { + osmium::builder::ChangesetBuilder builder{buffer}; + } + + const auto& changeset = buffer.get(buffer.commit()); + + REQUIRE(*changeset.user() == '\0'); +} + +TEST_CASE("set_user with length on changeset") { + osmium::memory::Buffer buffer{1024*10}; + std::string user = "userx"; + + { + osmium::builder::ChangesetBuilder builder{buffer}; + builder.set_user(user.c_str(), 4); + } + + const auto& changeset = buffer.get(buffer.commit()); + + REQUIRE(std::string{"user"} == changeset.user()); +} + diff --git a/test/t/geom/helper.hpp b/test/t/geom/helper.hpp deleted file mode 100644 index e0cefe6d1..000000000 --- a/test/t/geom/helper.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef TEST_GEOM_HELPER_HPP -#define TEST_GEOM_HELPER_HPP - -#include - -#include - -inline std::string geos_to_wkb(const geos::geom::Geometry* geometry) { - std::stringstream ss; - geos::io::WKBWriter wkb_writer; - wkb_writer.writeHEX(*geometry, ss); - return ss.str(); -} - -#endif // TEST_GEOM_HELPER_HPP diff --git a/test/t/geom/test_crs.cpp b/test/t/geom/test_crs.cpp index c8fbc098f..d8f7c36c3 100644 --- a/test/t/geom/test_crs.cpp +++ b/test/t/geom/test_crs.cpp @@ -5,12 +5,12 @@ #include TEST_CASE("CRS") { - osmium::geom::CRS wgs84{4326}; - osmium::geom::CRS mercator{3857}; + const osmium::geom::CRS wgs84{4326}; + const osmium::geom::CRS mercator{3857}; - osmium::geom::Coordinates c{osmium::geom::deg_to_rad(1.2), osmium::geom::deg_to_rad(3.4)}; - auto ct = osmium::geom::transform(wgs84, mercator, c); - auto c2 = osmium::geom::transform(mercator, wgs84, ct); + const osmium::geom::Coordinates c{osmium::geom::deg_to_rad(1.2), osmium::geom::deg_to_rad(3.4)}; + const auto ct = osmium::geom::transform(wgs84, mercator, c); + const auto c2 = osmium::geom::transform(mercator, wgs84, ct); REQUIRE(c.x == Approx(c2.x)); REQUIRE(c.y == Approx(c2.y)); diff --git a/test/t/geom/test_exception.cpp b/test/t/geom/test_exception.cpp index fe950434b..4122d17c8 100644 --- a/test/t/geom/test_exception.cpp +++ b/test/t/geom/test_exception.cpp @@ -6,11 +6,9 @@ TEST_CASE("Geometry exception") { - SECTION("geometry_error") { - osmium::geometry_error e("some error message", "node", 17); - REQUIRE(e.id() == 17); - REQUIRE(std::string(e.what()) == "some error message (node_id=17)"); - } + osmium::geometry_error e{"some error message", "node", 17}; + REQUIRE(e.id() == 17); + REQUIRE(std::string{e.what()} == "some error message (node_id=17)"); } diff --git a/test/t/geom/test_factory_with_projection.cpp b/test/t/geom/test_factory_with_projection.cpp index 42fc864bb..08efc0314 100644 --- a/test/t/geom/test_factory_with_projection.cpp +++ b/test/t/geom/test_factory_with_projection.cpp @@ -1,41 +1,21 @@ #include "catch.hpp" -#include #include #include #include #include -#include "helper.hpp" - -TEST_CASE("Projection") { - - SECTION("point_mercator") { - osmium::geom::WKTFactory factory(2); - - std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(std::string {"POINT(356222.37 467961.14)"} == wkt); - } - - SECTION("point_epsg_3857") { - osmium::geom::WKTFactory factory(osmium::geom::Projection(3857), 2); - - std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(std::string {"POINT(356222.37 467961.14)"} == wkt); - } - - SECTION("wkb_with_parameter") { - osmium::geom::WKBFactory wkb_factory(osmium::geom::Projection(3857), osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - osmium::geom::GEOSFactory geos_factory(osmium::geom::Projection(3857)); - - std::string wkb = wkb_factory.create_point(osmium::Location(3.2, 4.2)); - std::unique_ptr geos_point = geos_factory.create_point(osmium::Location(3.2, 4.2)); - REQUIRE(geos_to_wkb(geos_point.get()) == wkb); - } - - SECTION("cleanup") { - // trying to make valgrind happy, but there is still a memory leak in proj library - pj_deallocate_grids(); - } +TEST_CASE("Projection using MercatorProjection class to WKT") { + osmium::geom::WKTFactory factory{2}; + const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(wkt == "POINT(356222.37 467961.14)"); } + +TEST_CASE("Projection using Projection class to WKT") { + osmium::geom::WKTFactory factory{osmium::geom::Projection{3857}, 2}; + + const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(wkt == "POINT(356222.37 467961.14)"); +} + diff --git a/test/t/geom/test_geojson.cpp b/test/t/geom/test_geojson.cpp index 725be7fb9..572493655 100644 --- a/test/t/geom/test_geojson.cpp +++ b/test/t/geom/test_geojson.cpp @@ -5,162 +5,143 @@ #include "area_helper.hpp" #include "wnl_helper.hpp" -TEST_CASE("GeoJSON_Geometry") { - -SECTION("point") { +TEST_CASE("GeoJSON point geometry") { osmium::geom::GeoJSONFactory<> factory; - std::string json {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(std::string{"{\"type\":\"Point\",\"coordinates\":[3.2,4.2]}"} == json); + SECTION("point") { + const std::string json{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(std::string{"{\"type\":\"Point\",\"coordinates\":[3.2,4.2]}"} == json); + } + + SECTION("empty_point") { + REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location); + } + } -SECTION("empty_point") { +TEST_CASE("GeoJSON linestring geometry") { osmium::geom::GeoJSONFactory<> factory; + osmium::memory::Buffer buffer{1000}; - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); -} - -SECTION("linestring") { - osmium::geom::GeoJSONFactory<> factory; - - osmium::memory::Buffer buffer(1000); - auto &wnl = create_test_wnl_okay(buffer); - - { - std::string json {factory.create_linestring(wnl)}; + SECTION("linestring, default") { + const auto& wnl = create_test_wnl_okay(buffer); + const std::string json{factory.create_linestring(wnl)}; REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.2,4.2],[3.5,4.7],[3.6,4.9]]}"} == json); } - { - std::string json {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; + SECTION("linestring, unique, backwards") { + const auto& wnl = create_test_wnl_okay(buffer); + const std::string json{factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.6,4.9],[3.5,4.7],[3.2,4.2]]}"} == json); } - { - std::string json {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + SECTION("linestring, all") { + const auto& wnl = create_test_wnl_okay(buffer); + const std::string json{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.2,4.2],[3.5,4.7],[3.5,4.7],[3.6,4.9]]}"} == json); } - { - std::string json {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + SECTION("linestring, all, backwards") { + const auto& wnl = create_test_wnl_okay(buffer); + const std::string json{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.6,4.9],[3.5,4.7],[3.5,4.7],[3.2,4.2]]}"} == json); } -} -SECTION("empty_linestring") { - osmium::geom::GeoJSONFactory<> factory; + SECTION("empty_linestring") { + const auto& wnl = create_test_wnl_empty(buffer); - osmium::memory::Buffer buffer(1000); - auto& wnl = create_test_wnl_empty(buffer); - - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), osmium::geometry_error); -} - -SECTION("linestring_with_two_same_locations") { - osmium::geom::GeoJSONFactory<> factory; - - osmium::memory::Buffer buffer(1000); - auto& wnl = create_test_wnl_same_location(buffer); - - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); - - { - std::string json {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; - REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.5,4.7],[3.5,4.7]]}"} == json); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), osmium::geometry_error); } - { - std::string json {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; - REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.5,4.7],[3.5,4.7]]}"} == json); + SECTION("linestring with two same locations") { + const auto& wnl = create_test_wnl_same_location(buffer); + + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + + { + const std::string json{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.5,4.7],[3.5,4.7]]}"} == json); + } + + { + const std::string json{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + REQUIRE(std::string{"{\"type\":\"LineString\",\"coordinates\":[[3.5,4.7],[3.5,4.7]]}"} == json); + } } + + SECTION("linestring with undefined location") { + const auto& wnl = create_test_wnl_undefined_location(buffer); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); + } + } -SECTION("linestring_with_undefined_location") { +TEST_CASE("GeoJSON area geometry") { osmium::geom::GeoJSONFactory<> factory; + osmium::memory::Buffer buffer{1000}; - osmium::memory::Buffer buffer(1000); - auto& wnl = create_test_wnl_undefined_location(buffer); + SECTION("area_1outer_0inner") { + const osmium::Area& area = create_test_area_1outer_0inner(buffer); - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); -} + REQUIRE(!area.is_multipolygon()); + REQUIRE(std::distance(area.cbegin(), area.cend()) == 2); + REQUIRE(area.subitems().size() == area.num_rings().first); -SECTION("area_1outer_0inner") { - osmium::geom::GeoJSONFactory<> factory; - - osmium::memory::Buffer buffer(1000); - const osmium::Area& area = create_test_area_1outer_0inner(buffer); - - REQUIRE(!area.is_multipolygon()); - REQUIRE(std::distance(area.cbegin(), area.cend()) == 2); - REQUIRE(area.subitems().size() == area.num_rings().first); - - { - std::string json {factory.create_multipolygon(area)}; + std::string json{factory.create_multipolygon(area)}; REQUIRE(std::string{"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[3.2,4.2],[3.5,4.7],[3.6,4.9],[3.2,4.2]]]]}"} == json); } -} -SECTION("area_1outer_1inner") { - osmium::geom::GeoJSONFactory<> factory; + SECTION("area_1outer_1inner") { + const osmium::Area& area = create_test_area_1outer_1inner(buffer); - osmium::memory::Buffer buffer(1000); - const osmium::Area& area = create_test_area_1outer_1inner(buffer); + REQUIRE(!area.is_multipolygon()); + REQUIRE(std::distance(area.cbegin(), area.cend()) == 3); + REQUIRE(area.subitems().size() == area.num_rings().first); + REQUIRE(area.subitems().size() == area.num_rings().second); - REQUIRE(!area.is_multipolygon()); - REQUIRE(std::distance(area.cbegin(), area.cend()) == 3); - REQUIRE(area.subitems().size() == area.num_rings().first); - REQUIRE(area.subitems().size() == area.num_rings().second); - - { - std::string json {factory.create_multipolygon(area)}; + std::string json{factory.create_multipolygon(area)}; REQUIRE(std::string{"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[0.1,0.1],[9.1,0.1],[9.1,9.1],[0.1,9.1],[0.1,0.1]],[[1,1],[8,1],[8,8],[1,8],[1,1]]]]}"} == json); } -} -SECTION("area_2outer_2inner") { - osmium::geom::GeoJSONFactory<> factory; + SECTION("area_2outer_2inner") { + const osmium::Area& area = create_test_area_2outer_2inner(buffer); - osmium::memory::Buffer buffer(1000); - const osmium::Area& area = create_test_area_2outer_2inner(buffer); + REQUIRE(area.is_multipolygon()); + REQUIRE(std::distance(area.cbegin(), area.cend()) == 5); + REQUIRE(area.subitems().size() == area.num_rings().first); + REQUIRE(area.subitems().size() == area.num_rings().second); - REQUIRE(area.is_multipolygon()); - REQUIRE(std::distance(area.cbegin(), area.cend()) == 5); - REQUIRE(area.subitems().size() == area.num_rings().first); - REQUIRE(area.subitems().size() == area.num_rings().second); - - int outer_ring=0; - int inner_ring=0; - for (const auto& outer : area.outer_rings()) { - if (outer_ring == 0) { - REQUIRE(outer.front().ref() == 1); - } else if (outer_ring == 1) { - REQUIRE(outer.front().ref() == 100); - } else { - REQUIRE(false); - } - for (const auto& inner : area.inner_rings(outer)) { - if (outer_ring == 0 && inner_ring == 0) { - REQUIRE(inner.front().ref() == 5); - } else if (outer_ring == 0 && inner_ring == 1) { - REQUIRE(inner.front().ref() == 10); + int outer_ring=0; + int inner_ring=0; + for (const auto& outer : area.outer_rings()) { + if (outer_ring == 0) { + REQUIRE(outer.front().ref() == 1); + } else if (outer_ring == 1) { + REQUIRE(outer.front().ref() == 100); } else { REQUIRE(false); } - ++inner_ring; + for (const auto& inner : area.inner_rings(outer)) { + if (outer_ring == 0 && inner_ring == 0) { + REQUIRE(inner.front().ref() == 5); + } else if (outer_ring == 0 && inner_ring == 1) { + REQUIRE(inner.front().ref() == 10); + } else { + REQUIRE(false); + } + ++inner_ring; + } + inner_ring = 0; + ++outer_ring; } - inner_ring = 0; - ++outer_ring; - } - { - std::string json {factory.create_multipolygon(area)}; + std::string json{factory.create_multipolygon(area)}; REQUIRE(std::string{"{\"type\":\"MultiPolygon\",\"coordinates\":[[[[0.1,0.1],[9.1,0.1],[9.1,9.1],[0.1,9.1],[0.1,0.1]],[[1,1],[4,1],[4,4],[1,4],[1,1]],[[5,5],[5,7],[7,7],[5,5]]],[[[10,10],[11,10],[11,11],[10,11],[10,10]]]]}"} == json); } -} } diff --git a/test/t/geom/test_geos.cpp b/test/t/geom/test_geos.cpp index f74027c24..8e7fac483 100644 --- a/test/t/geom/test_geos.cpp +++ b/test/t/geom/test_geos.cpp @@ -1,6 +1,10 @@ -#include "catch.hpp" #include + +#ifdef OSMIUM_WITH_GEOS + +#include "catch.hpp" + #include #include "area_helper.hpp" @@ -9,7 +13,7 @@ TEST_CASE("GEOS geometry factory - create point") { osmium::geom::GEOSFactory<> factory; - std::unique_ptr point {factory.create_point(osmium::Location(3.2, 4.2))}; + const std::unique_ptr point{factory.create_point(osmium::Location{3.2, 4.2})}; REQUIRE(3.2 == point->getX()); REQUIRE(4.2 == point->getY()); REQUIRE(4326 == point->getSRID()); @@ -18,7 +22,7 @@ TEST_CASE("GEOS geometry factory - create point") { TEST_CASE("GEOS geometry factory - create point in web mercator") { osmium::geom::GEOSFactory factory; - std::unique_ptr point {factory.create_point(osmium::Location(3.2, 4.2))}; + const std::unique_ptr point{factory.create_point(osmium::Location{3.2, 4.2})}; REQUIRE(Approx(356222.3705384755l) == point->getX()); REQUIRE(Approx(467961.143605213l) == point->getY()); REQUIRE(3857 == point->getSRID()); @@ -26,9 +30,9 @@ TEST_CASE("GEOS geometry factory - create point in web mercator") { TEST_CASE("GEOS geometry factory - create point with externally created GEOS factory") { geos::geom::GeometryFactory geos_factory; - osmium::geom::GEOSFactory<> factory(geos_factory); + osmium::geom::GEOSFactory<> factory{geos_factory}; - std::unique_ptr point {factory.create_point(osmium::Location(3.2, 4.2))}; + const std::unique_ptr point{factory.create_point(osmium::Location{3.2, 4.2})}; REQUIRE(3.2 == point->getX()); REQUIRE(4.2 == point->getY()); REQUIRE(0 == point->getSRID()); @@ -37,45 +41,45 @@ TEST_CASE("GEOS geometry factory - create point with externally created GEOS fac TEST_CASE("GEOS geometry factory - can not create from invalid location") { osmium::geom::GEOSFactory<> factory; - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); + REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location); } TEST_CASE("GEOS geometry factory - create linestring") { osmium::geom::GEOSFactory<> factory; - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_okay(buffer); + osmium::memory::Buffer buffer{10000}; + const auto& wnl = create_test_wnl_okay(buffer); SECTION("from way node list") { - std::unique_ptr linestring {factory.create_linestring(wnl)}; + const std::unique_ptr linestring{factory.create_linestring(wnl)}; REQUIRE(3 == linestring->getNumPoints()); - std::unique_ptr p0 = std::unique_ptr(linestring->getPointN(0)); + const auto p0 = std::unique_ptr(linestring->getPointN(0)); REQUIRE(3.2 == p0->getX()); - std::unique_ptr p2 = std::unique_ptr(linestring->getPointN(2)); + const auto p2 = std::unique_ptr(linestring->getPointN(2)); REQUIRE(3.6 == p2->getX()); } SECTION("without duplicates and backwards") { - std::unique_ptr linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; + const std::unique_ptr linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; REQUIRE(3 == linestring->getNumPoints()); - std::unique_ptr p0 = std::unique_ptr(linestring->getPointN(0)); + const auto p0 = std::unique_ptr(linestring->getPointN(0)); REQUIRE(3.6 == p0->getX()); - std::unique_ptr p2 = std::unique_ptr(linestring->getPointN(2)); + const auto p2 = std::unique_ptr(linestring->getPointN(2)); REQUIRE(3.2 == p2->getX()); } SECTION("with duplicates") { - std::unique_ptr linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + const std::unique_ptr linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; REQUIRE(4 == linestring->getNumPoints()); - std::unique_ptr p0 = std::unique_ptr(linestring->getPointN(0)); + const auto p0 = std::unique_ptr(linestring->getPointN(0)); REQUIRE(3.2 == p0->getX()); } SECTION("with duplicates and backwards") { - std::unique_ptr linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + const std::unique_ptr linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; REQUIRE(4 == linestring->getNumPoints()); - std::unique_ptr p0 = std::unique_ptr(linestring->getPointN(0)); + const auto p0 = std::unique_ptr(linestring->getPointN(0)); REQUIRE(3.6 == p0->getX()); } } @@ -83,10 +87,10 @@ TEST_CASE("GEOS geometry factory - create linestring") { TEST_CASE("GEOS geometry factory - create area with one outer and no inner rings") { osmium::geom::GEOSFactory<> factory; - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; const osmium::Area& area = create_test_area_1outer_0inner(buffer); - std::unique_ptr mp {factory.create_multipolygon(area)}; + const std::unique_ptr mp{factory.create_multipolygon(area)}; REQUIRE(1 == mp->getNumGeometries()); const geos::geom::Polygon* p0 = dynamic_cast(mp->getGeometryN(0)); @@ -96,17 +100,17 @@ TEST_CASE("GEOS geometry factory - create area with one outer and no inner rings const geos::geom::LineString* l0e = p0->getExteriorRing(); REQUIRE(4 == l0e->getNumPoints()); - std::unique_ptr l0e_p0 = std::unique_ptr(l0e->getPointN(1)); + const auto l0e_p0 = std::unique_ptr(l0e->getPointN(1)); REQUIRE(3.5 == l0e_p0->getX()); } TEST_CASE("GEOS geometry factory - create area with one outer and one inner ring") { osmium::geom::GEOSFactory<> factory; - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; const osmium::Area& area = create_test_area_1outer_1inner(buffer); - std::unique_ptr mp {factory.create_multipolygon(area)}; + const std::unique_ptr mp{factory.create_multipolygon(area)}; REQUIRE(1 == mp->getNumGeometries()); const geos::geom::Polygon* p0 = dynamic_cast(mp->getGeometryN(0)); @@ -123,10 +127,10 @@ TEST_CASE("GEOS geometry factory - create area with one outer and one inner ring TEST_CASE("GEOS geometry factory - create area with two outer and two inner rings") { osmium::geom::GEOSFactory<> factory; - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; const osmium::Area& area = create_test_area_2outer_2inner(buffer); - std::unique_ptr mp {factory.create_multipolygon(area)}; + const std::unique_ptr mp{factory.create_multipolygon(area)}; REQUIRE(2 == mp->getNumGeometries()); const geos::geom::Polygon* p0 = dynamic_cast(mp->getGeometryN(0)); @@ -144,3 +148,5 @@ TEST_CASE("GEOS geometry factory - create area with two outer and two inner ring REQUIRE(5 == l1e->getNumPoints()); } +#endif + diff --git a/test/t/geom/test_geos_wkb.cpp b/test/t/geom/test_geos_wkb.cpp deleted file mode 100644 index 1fca63bc8..000000000 --- a/test/t/geom/test_geos_wkb.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "catch.hpp" - -#include -#include - -#include "helper.hpp" -#include "area_helper.hpp" -#include "wnl_helper.hpp" - -TEST_CASE("WKB_Geometry_with_GEOS") { - -SECTION("point") { - osmium::geom::WKBFactory<> wkb_factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - osmium::geom::GEOSFactory<> geos_factory; - - std::string wkb {wkb_factory.create_point(osmium::Location(3.2, 4.2))}; - - std::unique_ptr geos_point = geos_factory.create_point(osmium::Location(3.2, 4.2)); - REQUIRE(geos_to_wkb(geos_point.get()) == wkb); -} - - -SECTION("linestring") { - osmium::geom::WKBFactory<> wkb_factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - osmium::geom::GEOSFactory<> geos_factory; - - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_okay(buffer); - - { - std::string wkb = wkb_factory.create_linestring(wnl); - std::unique_ptr geos = geos_factory.create_linestring(wnl); - REQUIRE(geos_to_wkb(geos.get()) == wkb); - } - - { - std::string wkb = wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward); - std::unique_ptr geos = geos_factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward); - REQUIRE(geos_to_wkb(geos.get()) == wkb); - } - - { - std::string wkb = wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::all); - std::unique_ptr geos = geos_factory.create_linestring(wnl, osmium::geom::use_nodes::all); - REQUIRE(geos_to_wkb(geos.get()) == wkb); - } - - { - std::string wkb = wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward); - std::unique_ptr geos = geos_factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward); - REQUIRE(geos_to_wkb(geos.get()) == wkb); - } -} - -SECTION("area_1outer_0inner") { - osmium::geom::WKBFactory<> wkb_factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - osmium::geom::GEOSFactory<> geos_factory; - - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_1outer_0inner(buffer); - - std::string wkb = wkb_factory.create_multipolygon(area); - std::unique_ptr geos = geos_factory.create_multipolygon(area); - REQUIRE(geos_to_wkb(geos.get()) == wkb); -} - -SECTION("area_1outer_1inner") { - osmium::geom::WKBFactory<> wkb_factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - osmium::geom::GEOSFactory<> geos_factory; - - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_1outer_1inner(buffer); - - std::string wkb = wkb_factory.create_multipolygon(area); - std::unique_ptr geos = geos_factory.create_multipolygon(area); - REQUIRE(geos_to_wkb(geos.get()) == wkb); -} - -SECTION("area_2outer_2inner") { - osmium::geom::WKBFactory<> wkb_factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - osmium::geom::GEOSFactory<> geos_factory; - - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_2outer_2inner(buffer); - - std::string wkb = wkb_factory.create_multipolygon(area); - std::unique_ptr geos = geos_factory.create_multipolygon(area); - REQUIRE(geos_to_wkb(geos.get()) == wkb); -} - -} - diff --git a/test/t/geom/test_ogr.cpp b/test/t/geom/test_ogr.cpp index 3490c5759..5e0308247 100644 --- a/test/t/geom/test_ogr.cpp +++ b/test/t/geom/test_ogr.cpp @@ -5,121 +5,115 @@ #include "area_helper.hpp" #include "wnl_helper.hpp" -TEST_CASE("OGR_Geometry") { - -SECTION("point") { +TEST_CASE("OGR point geometry") { osmium::geom::OGRFactory<> factory; - std::unique_ptr point {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(3.2 == point->getX()); - REQUIRE(4.2 == point->getY()); + SECTION("point") { + std::unique_ptr point{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(3.2 == point->getX()); + REQUIRE(4.2 == point->getY()); + } + + SECTION("empty_point") { + REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); + } + } -SECTION("empty_point") { +TEST_CASE("OGR linestring geometry") { osmium::geom::OGRFactory<> factory; + osmium::memory::Buffer buffer{10000}; + const auto& wnl = create_test_wnl_okay(buffer); - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); -} - -SECTION("linestring") { - osmium::geom::OGRFactory<> factory; - - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_okay(buffer); - - { - std::unique_ptr linestring {factory.create_linestring(wnl)}; + SECTION("linestring, default") { + std::unique_ptr linestring{factory.create_linestring(wnl)}; REQUIRE(3 == linestring->getNumPoints()); REQUIRE(3.2 == linestring->getX(0)); REQUIRE(3.6 == linestring->getX(2)); } - { - std::unique_ptr linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; + SECTION("linestring, unique nodes, backwards") { + std::unique_ptr linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; REQUIRE(3 == linestring->getNumPoints()); REQUIRE(3.6 == linestring->getX(0)); REQUIRE(3.2 == linestring->getX(2)); } - { - std::unique_ptr linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + SECTION("linestring, all nodes") { + std::unique_ptr linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; REQUIRE(4 == linestring->getNumPoints()); REQUIRE(3.2 == linestring->getX(0)); } - { - std::unique_ptr linestring {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + SECTION("linestring, all nodes, backwards") { + std::unique_ptr linestring{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; REQUIRE(4 == linestring->getNumPoints()); REQUIRE(3.6 == linestring->getX(0)); } + } -SECTION("area_1outer_0inner") { +TEST_CASE("OGR area geometry") { osmium::geom::OGRFactory<> factory; + osmium::memory::Buffer buffer{10000}; - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_1outer_0inner(buffer); + SECTION("area_1outer_0inner") { + const osmium::Area& area = create_test_area_1outer_0inner(buffer); - std::unique_ptr mp {factory.create_multipolygon(area)}; - REQUIRE(1 == mp->getNumGeometries()); + std::unique_ptr mp {factory.create_multipolygon(area)}; + REQUIRE(1 == mp->getNumGeometries()); - const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); - REQUIRE(p0); - REQUIRE(0 == p0->getNumInteriorRings()); + const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); + REQUIRE(p0); + REQUIRE(0 == p0->getNumInteriorRings()); - const OGRLineString* l0e = p0->getExteriorRing(); - REQUIRE(4 == l0e->getNumPoints()); + const OGRLineString* l0e = p0->getExteriorRing(); + REQUIRE(4 == l0e->getNumPoints()); - REQUIRE(3.5 == l0e->getX(1)); -} + REQUIRE(3.5 == l0e->getX(1)); + } -SECTION("area_1outer_1inner") { - osmium::geom::OGRFactory<> factory; + SECTION("area_1outer_1inner") { + const osmium::Area& area = create_test_area_1outer_1inner(buffer); - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_1outer_1inner(buffer); + std::unique_ptr mp {factory.create_multipolygon(area)}; + REQUIRE(1 == mp->getNumGeometries()); - std::unique_ptr mp {factory.create_multipolygon(area)}; - REQUIRE(1 == mp->getNumGeometries()); + const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); + REQUIRE(p0); + REQUIRE(1 == p0->getNumInteriorRings()); - const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); - REQUIRE(p0); - REQUIRE(1 == p0->getNumInteriorRings()); + const OGRLineString* l0e = p0->getExteriorRing(); + REQUIRE(5 == l0e->getNumPoints()); - const OGRLineString* l0e = p0->getExteriorRing(); - REQUIRE(5 == l0e->getNumPoints()); + const OGRLineString* l0i0 = p0->getInteriorRing(0); + REQUIRE(5 == l0i0->getNumPoints()); + } - const OGRLineString* l0i0 = p0->getInteriorRing(0); - REQUIRE(5 == l0i0->getNumPoints()); -} + SECTION("area_2outer_2inner") { + const osmium::Area& area = create_test_area_2outer_2inner(buffer); -SECTION("area_2outer_2inner") { - osmium::geom::OGRFactory<> factory; + std::unique_ptr mp {factory.create_multipolygon(area)}; + REQUIRE(2 == mp->getNumGeometries()); - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_2outer_2inner(buffer); + const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); + REQUIRE(p0); + REQUIRE(2 == p0->getNumInteriorRings()); - std::unique_ptr mp {factory.create_multipolygon(area)}; - REQUIRE(2 == mp->getNumGeometries()); + const OGRLineString* l0e = p0->getExteriorRing(); + REQUIRE(5 == l0e->getNumPoints()); - const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); - REQUIRE(p0); - REQUIRE(2 == p0->getNumInteriorRings()); + const OGRPolygon* p1 = dynamic_cast(mp->getGeometryRef(1)); + REQUIRE(p1); + REQUIRE(0 == p1->getNumInteriorRings()); - const OGRLineString* l0e = p0->getExteriorRing(); - REQUIRE(5 == l0e->getNumPoints()); - - const OGRPolygon* p1 = dynamic_cast(mp->getGeometryRef(1)); - REQUIRE(p1); - REQUIRE(0 == p1->getNumInteriorRings()); - - const OGRLineString* l1e = p1->getExteriorRing(); - REQUIRE(5 == l1e->getNumPoints()); -} + const OGRLineString* l1e = p1->getExteriorRing(); + REQUIRE(5 == l1e->getNumPoints()); + } } diff --git a/test/t/geom/test_ogr_wkb.cpp b/test/t/geom/test_ogr_wkb.cpp new file mode 100644 index 000000000..548d14389 --- /dev/null +++ b/test/t/geom/test_ogr_wkb.cpp @@ -0,0 +1,100 @@ + +#if __BYTE_ORDER == __LITTLE_ENDIAN + +#include "catch.hpp" + +#include +#include +#include + +#include +#include + +#include "area_helper.hpp" +#include "wnl_helper.hpp" + +std::string to_wkb(const OGRGeometry* geometry) { + std::string buffer; + buffer.resize(geometry->WkbSize()); + + geometry->exportToWkb(wkbNDR, const_cast(reinterpret_cast(buffer.data()))); + + return buffer; +} + +TEST_CASE("compare WKB point against GDAL/OGR") { + osmium::geom::WKBFactory<> wkb_factory{osmium::geom::wkb_type::wkb}; + osmium::geom::OGRFactory<> ogr_factory; + + osmium::Location loc{3.2, 4.2}; + const std::string wkb{wkb_factory.create_point(loc)}; + const std::unique_ptr geometry = ogr_factory.create_point(loc); + REQUIRE(to_wkb(geometry.get()) == wkb); +} + +TEST_CASE("compare WKB linestring against GDAL/OGR") { + osmium::geom::WKBFactory<> wkb_factory{osmium::geom::wkb_type::wkb}; + osmium::geom::OGRFactory<> ogr_factory; + osmium::memory::Buffer buffer{10000}; + + const auto& wnl = create_test_wnl_okay(buffer); + + SECTION("linestring") { + const std::string wkb{wkb_factory.create_linestring(wnl)}; + const std::unique_ptr geometry = ogr_factory.create_linestring(wnl); + REQUIRE(to_wkb(geometry.get()) == wkb); + } + + SECTION("linestring, unique nodes, backwards") { + const std::string wkb{wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; + const std::unique_ptr geometry = ogr_factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward); + REQUIRE(to_wkb(geometry.get()) == wkb); + } + + SECTION("linestring, all nodes, forwards") { + const std::string wkb{wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + const std::unique_ptr geometry = ogr_factory.create_linestring(wnl, osmium::geom::use_nodes::all); + REQUIRE(to_wkb(geometry.get()) == wkb); + } + + SECTION("linestring, all nodes, backwards") { + const std::string wkb{wkb_factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + const std::unique_ptr geometry = ogr_factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward); + REQUIRE(to_wkb(geometry.get()) == wkb); + } + +} + +TEST_CASE("compare WKB area against GDAL/OGR") { + osmium::geom::WKBFactory<> wkb_factory{osmium::geom::wkb_type::wkb}; + osmium::geom::OGRFactory<> ogr_factory; + osmium::memory::Buffer buffer{10000}; + + SECTION("area_1outer_0inner") { + const osmium::Area& area = create_test_area_1outer_0inner(buffer); + + const std::string wkb{wkb_factory.create_multipolygon(area)}; + const std::unique_ptr geometry = ogr_factory.create_multipolygon(area); + REQUIRE(to_wkb(geometry.get()) == wkb); + } + + SECTION("area_1outer_1inner") { + const osmium::Area& area = create_test_area_1outer_1inner(buffer); + + const std::string wkb{wkb_factory.create_multipolygon(area)}; + const std::unique_ptr geometry = ogr_factory.create_multipolygon(area); + REQUIRE(to_wkb(geometry.get()) == wkb); + } + + SECTION("area_2outer_2inner") { + const osmium::Area& area = create_test_area_2outer_2inner(buffer); + + const std::string wkb{wkb_factory.create_multipolygon(area)}; + const std::unique_ptr geometry = ogr_factory.create_multipolygon(area); + REQUIRE(to_wkb(geometry.get()) == wkb); + } + +} + +#endif + diff --git a/test/t/geom/test_projection.cpp b/test/t/geom/test_projection.cpp index 58854109e..14df3bfd4 100644 --- a/test/t/geom/test_projection.cpp +++ b/test/t/geom/test_projection.cpp @@ -6,144 +6,121 @@ #include #include -TEST_CASE("Projection") { - -SECTION("identity_projection") { +TEST_CASE("Indentity Projection") { osmium::geom::IdentityProjection projection; REQUIRE(4326 == projection.epsg()); REQUIRE("+proj=longlat +datum=WGS84 +no_defs" == projection.proj_string()); } -SECTION("project_location_4326") { - osmium::geom::Projection projection(4326); +TEST_CASE("Projection 4326") { + osmium::geom::Projection projection{4326}; REQUIRE(4326 == projection.epsg()); REQUIRE("+init=epsg:4326" == projection.proj_string()); - const osmium::Location loc(1.0, 2.0); - const osmium::geom::Coordinates c {1.0, 2.0}; + const osmium::Location loc{1.0, 2.0}; + const osmium::geom::Coordinates c{1.0, 2.0}; REQUIRE(c == projection(loc)); } -SECTION("project_location_4326_string") { - osmium::geom::Projection projection("+init=epsg:4326"); +TEST_CASE("Projection 4326 from init string") { + osmium::geom::Projection projection{"+init=epsg:4326"}; REQUIRE(-1 == projection.epsg()); REQUIRE("+init=epsg:4326" == projection.proj_string()); - const osmium::Location loc(1.0, 2.0); - const osmium::geom::Coordinates c {1.0, 2.0}; + const osmium::Location loc{1.0, 2.0}; + const osmium::geom::Coordinates c{1.0, 2.0}; REQUIRE(c == projection(loc)); } -SECTION("unknown_projection_string") { - REQUIRE_THROWS_AS(osmium::geom::Projection projection("abc"), osmium::projection_error); +TEST_CASE("Creating projection from unknown init string") { + REQUIRE_THROWS_AS(osmium::geom::Projection projection{"abc"}, osmium::projection_error); } -SECTION("unknown_epsg_code") { - REQUIRE_THROWS_AS(osmium::geom::Projection projection(9999999), osmium::projection_error); +TEST_CASE("Creating projection from unknown EPSG code") { + REQUIRE_THROWS_AS(osmium::geom::Projection projection{9999999}, osmium::projection_error); } -SECTION("project_location_3857") { - osmium::geom::Projection projection(3857); +TEST_CASE("Projection 3857") { + osmium::geom::Projection projection{3857}; REQUIRE(3857 == projection.epsg()); REQUIRE("+init=epsg:3857" == projection.proj_string()); - { - const osmium::Location loc(0.0, 0.0); - const osmium::geom::Coordinates c {0.0, 0.0}; + SECTION("Zero coordinates") { + const osmium::Location loc{0.0, 0.0}; + const osmium::geom::Coordinates c{0.0, 0.0}; REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); } - { - const osmium::Location loc(180.0, 0.0); - const osmium::geom::Coordinates c {20037508.34, 0.0}; + + SECTION("Max longitude") { + const osmium::Location loc{180.0, 0.0}; + const osmium::geom::Coordinates c{20037508.34, 0.0}; REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); } - { - const osmium::Location loc(180.0, 0.0); - const osmium::geom::Coordinates c {20037508.34, 0.0}; + + SECTION("Min longitude") { + const osmium::Location loc{-180.0, 0.0}; + const osmium::geom::Coordinates c{-20037508.34, 0.0}; REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); } - { - const osmium::Location loc(0.0, 85.0511288); - const osmium::geom::Coordinates c {0.0, 20037508.34}; + + SECTION("Max latitude") { + const osmium::Location loc{0.0, 85.0511288}; + const osmium::geom::Coordinates c{0.0, 20037508.34}; REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); } } -SECTION("project_location_mercator") { +TEST_CASE("MercatorProjection") { osmium::geom::MercatorProjection projection; - { - const osmium::Location loc(0.0, 0.0); - const osmium::geom::Coordinates c {0.0, 0.0}; + SECTION("Zero coordinates") { + const osmium::Location loc{0.0, 0.0}; + const osmium::geom::Coordinates c{0.0, 0.0}; REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); } - { - const osmium::Location loc(180.0, 0.0); - const osmium::geom::Coordinates c {20037508.34, 0.0}; + + SECTION("Max longitude") { + const osmium::Location loc{180.0, 0.0}; + const osmium::geom::Coordinates c{20037508.34, 0.0}; REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); } - { - const osmium::Location loc(180.0, 0.0); - const osmium::geom::Coordinates c {20037508.34, 0.0}; + + SECTION("Min longitude") { + const osmium::Location loc{-180.0, 0.0}; + const osmium::geom::Coordinates c{-20037508.34, 0.0}; REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); } - { - const osmium::Location loc(0.0, 85.0511288); - const osmium::geom::Coordinates c {0.0, 20037508.34}; + + SECTION("Max latitude") { + const osmium::Location loc{0.0, 85.0511288}; + const osmium::geom::Coordinates c{0.0, 20037508.34}; REQUIRE(projection(loc).x == Approx(c.x).epsilon(0.1)); REQUIRE(projection(loc).y == Approx(c.y).epsilon(0.1)); } } -SECTION("compare_mercators") { +TEST_CASE("Compare mercator implementations") { osmium::geom::MercatorProjection projection_merc; - osmium::geom::Projection projection_3857(3857); - REQUIRE(3857 == projection_3857.epsg()); - REQUIRE("+init=epsg:3857" == projection_3857.proj_string()); + osmium::geom::Projection projection_3857{3857}; - { - const osmium::Location loc(4.2, 27.3); - REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1)); - REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1)); - } - { - const osmium::Location loc(160.789, -42.42); - REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1)); - REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1)); - } - { - const osmium::Location loc(-0.001, 0.001); - REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1)); - REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1)); - } - { - const osmium::Location loc(-85.2, -85.2); - REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1)); - REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1)); - } -} + SECTION("random coordinates") { + std::random_device rd; + std::mt19937 gen{rd()}; + std::uniform_real_distribution<> dis_x{-180.0, 180.0}; + std::uniform_real_distribution<> dis_y{-90.0, 90.0}; -SECTION("compare_mercators") { - osmium::geom::MercatorProjection projection_merc; - osmium::geom::Projection projection_3857(3857); - - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution<> dis_x(-180.0, 180.0); - std::uniform_real_distribution<> dis_y(-90.0, 90.0); - - for (int n = 0; n < 100000; ++n) { - const osmium::Location loc(dis_x(gen), dis_y(gen)); - REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1)); - REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1)); + for (int n = 0; n < 10000; ++n) { + const osmium::Location loc{dis_x(gen), dis_y(gen)}; + REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1)); + REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1)); + } } -} } diff --git a/test/t/geom/test_tile.cpp b/test/t/geom/test_tile.cpp index 5454fed79..953fcc726 100644 --- a/test/t/geom/test_tile.cpp +++ b/test/t/geom/test_tile.cpp @@ -4,8 +4,6 @@ #include -#include "helper.hpp" - #include "test_tile_data.hpp" TEST_CASE("Tile from x0.0 y0.0 at zoom 0") { diff --git a/test/t/geom/test_wkb.cpp b/test/t/geom/test_wkb.cpp index d4d922868..66dd42e51 100644 --- a/test/t/geom/test_wkb.cpp +++ b/test/t/geom/test_wkb.cpp @@ -10,28 +10,28 @@ TEST_CASE("WKB geometry factory (byte-order-dependant), points") { const osmium::Location loc{3.2, 4.2}; SECTION("point") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex}; const std::string wkb{factory.create_point(loc)}; REQUIRE(wkb == "01010000009A99999999990940CDCCCCCCCCCC1040"); } SECTION("point in web mercator") { - osmium::geom::WKBFactory factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + osmium::geom::WKBFactory factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex}; const std::string wkb{factory.create_point(loc)}; REQUIRE(wkb == "010100000028706E7BF9BD1541B03E0D93E48F1C41"); } SECTION("point in ewkb") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); + osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex}; const std::string wkb{factory.create_point(loc)}; REQUIRE(wkb == "0101000020E61000009A99999999990940CDCCCCCCCCCC1040"); } SECTION("point in ewkb in web mercator") { - osmium::geom::WKBFactory factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); + osmium::geom::WKBFactory factory{osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex}; const std::string wkb{factory.create_point(loc)}; REQUIRE(wkb == "0101000020110F000028706E7BF9BD1541B03E0D93E48F1C41"); @@ -44,7 +44,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") { osmium::memory::Buffer buffer{10000}; SECTION("linestring") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex}; const auto& wnl = create_test_wnl_okay(buffer); { @@ -69,7 +69,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") { } SECTION("linestring as ewkb") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); + osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex}; const auto& wnl = create_test_wnl_okay(buffer); @@ -78,7 +78,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") { } SECTION("linestring with two same locations") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex}; const auto& wnl = create_test_wnl_same_location(buffer); @@ -102,7 +102,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") { } SECTION("linestring with undefined location") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex}; const auto& wnl = create_test_wnl_undefined_location(buffer); @@ -115,7 +115,7 @@ TEST_CASE("WKB geometry factory (byte-order-dependant)") { TEST_CASE("WKB geometry (byte-order-independent)") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex}; SECTION("empty point") { REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location); diff --git a/test/t/geom/wnl_helper.hpp b/test/t/geom/wnl_helper.hpp index 91ac114dd..68de4c3ef 100644 --- a/test/t/geom/wnl_helper.hpp +++ b/test/t/geom/wnl_helper.hpp @@ -6,7 +6,7 @@ using namespace osmium::builder::attr; inline const osmium::WayNodeList& create_test_wnl_okay(osmium::memory::Buffer& buffer) { - auto pos = osmium::builder::add_way_node_list(buffer, _nodes({ + const auto pos = osmium::builder::add_way_node_list(buffer, _nodes({ {1, {3.2, 4.2}}, {3, {3.5, 4.7}}, {4, {3.5, 4.7}}, @@ -25,7 +25,7 @@ inline const osmium::WayNodeList& create_test_wnl_empty(osmium::memory::Buffer& } inline const osmium::WayNodeList& create_test_wnl_same_location(osmium::memory::Buffer& buffer) { - auto pos = osmium::builder::add_way_node_list(buffer, _nodes({ + const auto pos = osmium::builder::add_way_node_list(buffer, _nodes({ {1, {3.5, 4.7}}, {2, {3.5, 4.7}} })); @@ -34,7 +34,7 @@ inline const osmium::WayNodeList& create_test_wnl_same_location(osmium::memory:: } inline const osmium::WayNodeList& create_test_wnl_undefined_location(osmium::memory::Buffer& buffer) { - auto pos = osmium::builder::add_way_node_list(buffer, _nodes({ + const auto pos = osmium::builder::add_way_node_list(buffer, _nodes({ {1, {3.5, 4.7}}, {2, osmium::Location()} })); diff --git a/test/t/index/test_id_set.cpp b/test/t/index/test_id_set.cpp new file mode 100644 index 000000000..4c2444717 --- /dev/null +++ b/test/t/index/test_id_set.cpp @@ -0,0 +1,166 @@ + +#include "catch.hpp" + +#include +#include + +TEST_CASE("Basic functionality of IdSetDense") { + osmium::index::IdSetDense s; + + REQUIRE_FALSE(s.get(17)); + REQUIRE_FALSE(s.get(28)); + REQUIRE(s.empty()); + REQUIRE(s.size() == 0); + + s.set(17); + REQUIRE(s.get(17)); + REQUIRE_FALSE(s.get(28)); + REQUIRE_FALSE(s.empty()); + REQUIRE(s.size() == 1); + + s.set(28); + REQUIRE(s.get(17)); + REQUIRE(s.get(28)); + REQUIRE_FALSE(s.empty()); + REQUIRE(s.size() == 2); + + s.set(17); + REQUIRE(s.get(17)); + REQUIRE(s.size() == 2); + + REQUIRE_FALSE(s.check_and_set(17)); + REQUIRE(s.get(17)); + REQUIRE(s.size() == 2); + + s.unset(17); + REQUIRE_FALSE(s.get(17)); + REQUIRE(s.size() == 1); + + REQUIRE(s.check_and_set(32)); + REQUIRE(s.get(32)); + REQUIRE(s.size() == 2); + + s.clear(); + REQUIRE(s.empty()); + REQUIRE(s.size() == 0); +} + +TEST_CASE("Iterating over IdSetDense") { + osmium::index::IdSetDense s; + s.set(7); + s.set(35); + s.set(35); + s.set(20); + s.set(1LL << 33); + s.set(21); + s.set((1LL << 27) + 13); + + REQUIRE(s.size() == 6); + + auto it = s.begin(); + REQUIRE(it != s.end()); + REQUIRE(*it == 7); + ++it; + REQUIRE(it != s.end()); + REQUIRE(*it == 20); + ++it; + REQUIRE(it != s.end()); + REQUIRE(*it == 21); + ++it; + REQUIRE(it != s.end()); + REQUIRE(*it == 35); + ++it; + REQUIRE(it != s.end()); + REQUIRE(*it == (1LL << 27) + 13); + ++it; + REQUIRE(it != s.end()); + REQUIRE(*it == 1LL << 33); + ++it; + REQUIRE(it == s.end()); +} + +TEST_CASE("Test with larger Ids") { + osmium::index::IdSetDense s; + + const osmium::unsigned_object_id_type start = 25; + const osmium::unsigned_object_id_type end = 100000000; + const osmium::unsigned_object_id_type step = 123456; + + for (osmium::unsigned_object_id_type i = start; i < end; i += step) { + s.set(i); + } + + for (osmium::unsigned_object_id_type i = start; i < end; i += step) { + REQUIRE(s.get(i)); + REQUIRE_FALSE(s.get(i + 1)); + } +} + +TEST_CASE("Large gap") { + osmium::index::IdSetDense s; + + s.set(3); + s.set(1 << 30); + + REQUIRE(s.get(1 << 30)); + REQUIRE_FALSE(s.get(1 << 29)); +} + +TEST_CASE("Basic functionality of IdSetSmall") { + osmium::index::IdSetSmall s; + + REQUIRE_FALSE(s.get(17)); + REQUIRE_FALSE(s.get(28)); + REQUIRE(s.empty()); + + s.set(17); + REQUIRE(s.get(17)); + REQUIRE_FALSE(s.get(28)); + REQUIRE_FALSE(s.empty()); + + s.set(28); + REQUIRE(s.get(17)); + REQUIRE(s.get(28)); + REQUIRE_FALSE(s.empty()); + + s.clear(); + REQUIRE(s.empty()); +} + +TEST_CASE("Iterating over IdSetSmall") { + osmium::index::IdSetSmall s; + s.set(7); + s.set(35); + s.set(35); + s.set(20); + s.set(1LL << 33); + s.set(21); + s.set((1LL << 27) + 13); + + // needs to be called before size() and iterator will work properly + s.sort_unique(); + + REQUIRE(s.size() == 6); + + auto it = s.begin(); + REQUIRE(it != s.end()); + REQUIRE(*it == 7); + ++it; + REQUIRE(it != s.end()); + REQUIRE(*it == 20); + ++it; + REQUIRE(it != s.end()); + REQUIRE(*it == 21); + ++it; + REQUIRE(it != s.end()); + REQUIRE(*it == 35); + ++it; + REQUIRE(it != s.end()); + REQUIRE(*it == (1LL << 27) + 13); + ++it; + REQUIRE(it != s.end()); + REQUIRE(*it == 1LL << 33); + ++it; + REQUIRE(it == s.end()); +} + diff --git a/test/t/index/test_id_to_location.cpp b/test/t/index/test_id_to_location.cpp index 810ef3be9..36fc074b4 100644 --- a/test/t/index/test_id_to_location.cpp +++ b/test/t/index/test_id_to_location.cpp @@ -15,12 +15,14 @@ #include +static_assert(osmium::index::empty_value() == osmium::Location{}, "Empty value for location is wrong"); + template void test_func_all(TIndex& index) { - osmium::unsigned_object_id_type id1 = 12; - osmium::unsigned_object_id_type id2 = 3; - osmium::Location loc1{1.2, 4.5}; - osmium::Location loc2{3.5, -7.2}; + const osmium::unsigned_object_id_type id1 = 12; + const osmium::unsigned_object_id_type id2 = 3; + const osmium::Location loc1{1.2, 4.5}; + const osmium::Location loc2{3.5, -7.2}; REQUIRE_THROWS_AS(index.get(id1), osmium::not_found); @@ -33,14 +35,16 @@ void test_func_all(TIndex& index) { REQUIRE_THROWS_AS(index.get(1), osmium::not_found); REQUIRE_THROWS_AS(index.get(5), osmium::not_found); REQUIRE_THROWS_AS(index.get(100), osmium::not_found); + REQUIRE_THROWS_WITH(index.get(0), "id 0 not found"); + REQUIRE_THROWS_WITH(index.get(1), "id 1 not found"); } template void test_func_real(TIndex& index) { - osmium::unsigned_object_id_type id1 = 12; - osmium::unsigned_object_id_type id2 = 3; - osmium::Location loc1{1.2, 4.5}; - osmium::Location loc2{3.5, -7.2}; + const osmium::unsigned_object_id_type id1 = 12; + const osmium::unsigned_object_id_type id2 = 3; + const osmium::Location loc1{1.2, 4.5}; + const osmium::Location loc2{3.5, -7.2}; index.set(id1, loc1); index.set(id2, loc2); @@ -66,115 +70,116 @@ void test_func_real(TIndex& index) { REQUIRE_THROWS_AS(index.get(100), osmium::not_found); } -TEST_CASE("IdToLocation") { +TEST_CASE("Map Id to location: Dummy") { + using index_type = osmium::index::map::Dummy; - SECTION("Dummy") { - using index_type = osmium::index::map::Dummy; + index_type index1; - index_type index1; + REQUIRE(0 == index1.size()); + REQUIRE(0 == index1.used_memory()); - REQUIRE(0 == index1.size()); - REQUIRE(0 == index1.used_memory()); + test_func_all(index1); - test_func_all(index1); + REQUIRE(0 == index1.size()); + REQUIRE(0 == index1.used_memory()); +} - REQUIRE(0 == index1.size()); - REQUIRE(0 == index1.used_memory()); - } +TEST_CASE("Map Id to location: DenseMemArray") { + using index_type = osmium::index::map::DenseMemArray; - SECTION("DenseMemArray") { - using index_type = osmium::index::map::DenseMemArray; + index_type index1; + index1.reserve(1000); + test_func_all(index1); - index_type index1; - index1.reserve(1000); - test_func_all(index1); - - index_type index2; - index2.reserve(1000); - test_func_real(index2); - } + index_type index2; + index2.reserve(1000); + test_func_real(index2); +} #ifdef __linux__ - SECTION("DenseMmapArray") { - using index_type = osmium::index::map::DenseMmapArray; +TEST_CASE("Map Id to location: DenseMmapArray") { + using index_type = osmium::index::map::DenseMmapArray; - index_type index1; - test_func_all(index1); + index_type index1; + test_func_all(index1); - index_type index2; - test_func_real(index2); - } + index_type index2; + test_func_real(index2); +} #else # pragma message("not running 'DenseMapMmap' test case on this machine") #endif - SECTION("DenseFileArray") { - using index_type = osmium::index::map::DenseFileArray; +TEST_CASE("Map Id to location: DenseFileArray") { + using index_type = osmium::index::map::DenseFileArray; - index_type index1; - test_func_all(index1); + index_type index1; + test_func_all(index1); - index_type index2; - test_func_real(index2); - } + index_type index2; + test_func_real(index2); +} #ifdef OSMIUM_WITH_SPARSEHASH - SECTION("SparseMemTable") { - using index_type = osmium::index::map::SparseMemTable; +TEST_CASE("Map Id to location: SparseMemTable") { + using index_type = osmium::index::map::SparseMemTable; - index_type index1; - test_func_all(index1); + index_type index1; + test_func_all(index1); - index_type index2; - test_func_real(index2); - } + index_type index2; + test_func_real(index2); +} #endif - SECTION("SparseMemMap") { - using index_type = osmium::index::map::SparseMemMap; +TEST_CASE("Map Id to location: SparseMemMap") { + using index_type = osmium::index::map::SparseMemMap; - index_type index1; - test_func_all(index1); - - index_type index2; - test_func_real(index2); - } - - SECTION("SparseMemArray") { - using index_type = osmium::index::map::SparseMemArray; - - index_type index1; - - REQUIRE(0 == index1.size()); - REQUIRE(0 == index1.used_memory()); - - test_func_all(index1); - - REQUIRE(2 == index1.size()); - - index_type index2; - test_func_real(index2); - } - - SECTION("Dynamic map choice") { - using map_type = osmium::index::map::Map; - const auto& map_factory = osmium::index::MapFactory::instance(); - - const std::vector map_type_names = map_factory.map_types(); - REQUIRE(map_type_names.size() >= 5); - - for (const auto& map_type_name : map_type_names) { - std::unique_ptr index1 = map_factory.create_map(map_type_name); - index1->reserve(1000); - test_func_all(*index1); - - std::unique_ptr index2 = map_factory.create_map(map_type_name); - index2->reserve(1000); - test_func_real(*index2); - } - } + index_type index1; + test_func_all(index1); + index_type index2; + test_func_real(index2); +} + +TEST_CASE("Map Id to location: SparseMemArray") { + using index_type = osmium::index::map::SparseMemArray; + + index_type index1; + + REQUIRE(0 == index1.size()); + REQUIRE(0 == index1.used_memory()); + + test_func_all(index1); + + REQUIRE(2 == index1.size()); + + index_type index2; + test_func_real(index2); +} + +TEST_CASE("Map Id to location: Dynamic map choice") { + using map_type = osmium::index::map::Map; + const auto& map_factory = osmium::index::MapFactory::instance(); + + const std::vector map_type_names = map_factory.map_types(); + REQUIRE(map_type_names.size() >= 5); + + REQUIRE_THROWS_AS(map_factory.create_map(""), osmium::map_factory_error); + REQUIRE_THROWS_AS(map_factory.create_map("does not exist"), osmium::map_factory_error); + REQUIRE_THROWS_WITH(map_factory.create_map(""), "Need non-empty map type name"); + REQUIRE_THROWS_WITH(map_factory.create_map("does not exist"), "Support for map type 'does not exist' not compiled into this binary"); + + for (const auto& map_type_name : map_type_names) { + std::unique_ptr index1 = map_factory.create_map(map_type_name); + index1->reserve(1000); + test_func_all(*index1); + + std::unique_ptr index2 = map_factory.create_map(map_type_name); + index2->reserve(1000); + test_func_real(*index2); + } } diff --git a/test/t/io/test_compression_factory.cpp b/test/t/io/test_compression_factory.cpp new file mode 100644 index 000000000..5428d8213 --- /dev/null +++ b/test/t/io/test_compression_factory.cpp @@ -0,0 +1,27 @@ + +#include "catch.hpp" + +#include + +TEST_CASE("Compression factory") { + const auto& factory = osmium::io::CompressionFactory::instance(); + + SECTION("compressor") { + REQUIRE(factory.create_compressor(osmium::io::file_compression::none, -1, osmium::io::fsync::no)); + } + + SECTION("decompressor") { + REQUIRE(factory.create_decompressor(osmium::io::file_compression::none, nullptr, 0)); + } + + SECTION("fail on undefined compression") { + REQUIRE_THROWS_AS({ + factory.create_compressor(osmium::io::file_compression::gzip, -1, osmium::io::fsync::no); + }, osmium::unsupported_file_format_error); + REQUIRE_THROWS_WITH({ + factory.create_compressor(osmium::io::file_compression::gzip, -1, osmium::io::fsync::no); + }, "Support for compression 'gzip' not compiled into this binary"); + } + +} + diff --git a/test/t/io/test_reader_with_mock_parser.cpp b/test/t/io/test_reader_with_mock_parser.cpp index c5c997598..b1076ccad 100644 --- a/test/t/io/test_reader_with_mock_parser.cpp +++ b/test/t/io/test_reader_with_mock_parser.cpp @@ -24,9 +24,9 @@ public: MockParser(osmium::io::detail::future_string_queue_type& input_queue, osmium::io::detail::future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_types, + osmium::io::detail::reader_options options, const std::string& fail_in) : - Parser(input_queue, output_queue, header_promise, read_types), + Parser(input_queue, output_queue, header_promise, options), m_fail_in(fail_in) { } @@ -59,8 +59,8 @@ TEST_CASE("Test Reader using MockParser") { [&](osmium::io::detail::future_string_queue_type& input_queue, osmium::io::detail::future_buffer_queue_type& output_queue, std::promise& header_promise, - osmium::osm_entity_bits::type read_which_entities) { - return std::unique_ptr(new MockParser(input_queue, output_queue, header_promise, read_which_entities, fail_in)); + osmium::io::detail::reader_options options) { + return std::unique_ptr(new MockParser(input_queue, output_queue, header_promise, options, fail_in)); }); SECTION("no failure") { diff --git a/test/t/buffer/test_buffer_basics.cpp b/test/t/memory/test_buffer_basics.cpp similarity index 100% rename from test/t/buffer/test_buffer_basics.cpp rename to test/t/memory/test_buffer_basics.cpp diff --git a/test/t/buffer/test_buffer_node.cpp b/test/t/memory/test_buffer_node.cpp similarity index 50% rename from test/t/buffer/test_buffer_node.cpp rename to test/t/memory/test_buffer_node.cpp index ba2431b80..040cbb80f 100644 --- a/test/t/buffer/test_buffer_node.cpp +++ b/test/t/memory/test_buffer_node.cpp @@ -3,7 +3,7 @@ #include #include -void check_node_1(osmium::Node& node) { +void check_node_1(const osmium::Node& node) { REQUIRE(1 == node.id()); REQUIRE(3 == node.version()); REQUIRE(true == node.visible()); @@ -11,9 +11,9 @@ void check_node_1(osmium::Node& node) { REQUIRE(21 == node.uid()); REQUIRE(123 == uint32_t(node.timestamp())); REQUIRE(osmium::Location(3.5, 4.7) == node.location()); - REQUIRE(std::string("testuser") == node.user()); + REQUIRE(std::string{"testuser"} == node.user()); - for (osmium::memory::Item& item : node) { + for (const osmium::memory::Item& item : node) { REQUIRE(osmium::item_type::tag_list == item.type()); } @@ -22,7 +22,7 @@ void check_node_1(osmium::Node& node) { REQUIRE(0 == std::distance(node.tags().begin(), node.tags().end())); } -void check_node_2(osmium::Node& node) { +void check_node_2(const osmium::Node& node) { REQUIRE(2 == node.id()); REQUIRE(3 == node.version()); REQUIRE(true == node.visible()); @@ -30,9 +30,9 @@ void check_node_2(osmium::Node& node) { REQUIRE(21 == node.uid()); REQUIRE(123 == uint32_t(node.timestamp())); REQUIRE(osmium::Location(3.5, 4.7) == node.location()); - REQUIRE(std::string("testuser") == node.user()); + REQUIRE(std::string{"testuser"} == node.user()); - for (osmium::memory::Item& item : node) { + for (const osmium::memory::Item& item : node) { REQUIRE(osmium::item_type::tag_list == item.type()); } @@ -61,60 +61,52 @@ TEST_CASE("Node in Buffer") { constexpr size_t buffer_size = 10000; unsigned char data[buffer_size]; - osmium::memory::Buffer buffer(data, buffer_size, 0); + osmium::memory::Buffer buffer{data, buffer_size, 0}; SECTION("Add node to buffer") { { // add node 1 - osmium::builder::NodeBuilder node_builder(buffer); - osmium::Node& node = node_builder.object(); - REQUIRE(osmium::item_type::node == node.type()); + osmium::builder::NodeBuilder node_builder{buffer}; - node.set_id(1); - node.set_version(3); - node.set_visible(true); - node.set_changeset(333); - node.set_uid(21); - node.set_timestamp(123); - node.set_location(osmium::Location(3.5, 4.7)); - - node_builder.add_user("testuser"); - - buffer.commit(); + node_builder.set_id(1) + .set_version(3) + .set_visible(true) + .set_changeset(333) + .set_uid(21) + .set_timestamp(123) + .set_location(osmium::Location{3.5, 4.7}) + .set_user("testuser"); } + buffer.commit(); + { // add node 2 - osmium::builder::NodeBuilder node_builder(buffer); - osmium::Node& node = node_builder.object(); - REQUIRE(osmium::item_type::node == node.type()); + osmium::builder::NodeBuilder node_builder{buffer}; - node.set_id(2); - node.set_version(3); - node.set_visible(true); - node.set_changeset(333); - node.set_uid(21); - node.set_timestamp(123); - node.set_location(osmium::Location(3.5, 4.7)); + node_builder.set_id(2) + .set_version(3) + .set_visible(true) + .set_changeset(333) + .set_uid(21) + .set_timestamp(123) + .set_location(osmium::Location{3.5, 4.7}) + .set_user("testuser"); - node_builder.add_user("testuser"); - - { - osmium::builder::TagListBuilder tag_builder(buffer, &node_builder); - tag_builder.add_tag("amenity", "bank"); - tag_builder.add_tag("name", "OSM Savings"); - } - - buffer.commit(); + osmium::builder::TagListBuilder tag_builder{node_builder}; + tag_builder.add_tag("amenity", "bank"); + tag_builder.add_tag("name", "OSM Savings"); } + buffer.commit(); + REQUIRE(2 == std::distance(buffer.begin(), buffer.end())); int item_no = 0; - for (osmium::memory::Item& item : buffer) { + for (const osmium::memory::Item& item : buffer) { REQUIRE(osmium::item_type::node == item.type()); - osmium::Node& node = static_cast(item); + const osmium::Node& node = static_cast(item); switch (item_no) { case 0: @@ -137,24 +129,21 @@ TEST_CASE("Node in Buffer") { { // add node 1 - osmium::builder::NodeBuilder node_builder(buffer); - osmium::Node& node = node_builder.object(); - REQUIRE(osmium::item_type::node == node.type()); + osmium::builder::NodeBuilder node_builder{buffer}; - node.set_id(1); - node.set_version(3); - node.set_visible(true); - node.set_changeset(333); - node.set_uid(21); - node.set_timestamp(123); - node.set_location(osmium::Location(3.5, 4.7)); - - node_builder.add_user("testuser"); - - buffer.commit(); + node_builder.set_id(1) + .set_version(3) + .set_visible(true) + .set_changeset(333) + .set_uid(21) + .set_timestamp(123) + .set_location(osmium::Location{3.5, 4.7}) + .set_user("testuser"); } - osmium::memory::Buffer buffer2(buffer_size, osmium::memory::Buffer::auto_grow::yes); + buffer.commit(); + + osmium::memory::Buffer buffer2{buffer_size, osmium::memory::Buffer::auto_grow::yes}; buffer2.add_buffer(buffer); buffer2.commit(); @@ -169,24 +158,21 @@ TEST_CASE("Node in Buffer") { { // add node 1 - osmium::builder::NodeBuilder node_builder(buffer); - osmium::Node& node = node_builder.object(); - REQUIRE(osmium::item_type::node == node.type()); + osmium::builder::NodeBuilder node_builder{buffer}; - node.set_id(1); - node.set_version(3); - node.set_visible(true); - node.set_changeset(333); - node.set_uid(21); - node.set_timestamp(123); - node.set_location(osmium::Location(3.5, 4.7)); - - node_builder.add_user("testuser"); - - buffer.commit(); + node_builder.set_id(1) + .set_version(3) + .set_visible(true) + .set_changeset(333) + .set_uid(21) + .set_timestamp(123) + .set_location(osmium::Location{3.5, 4.7}) + .set_user("testuser"); } - osmium::memory::Buffer buffer2(buffer_size, osmium::memory::Buffer::auto_grow::yes); + buffer.commit(); + + osmium::memory::Buffer buffer2{buffer_size, osmium::memory::Buffer::auto_grow::yes}; std::copy(buffer.begin(), buffer.end(), std::back_inserter(buffer2)); diff --git a/test/t/buffer/test_buffer_purge.cpp b/test/t/memory/test_buffer_purge.cpp similarity index 60% rename from test/t/buffer/test_buffer_purge.cpp rename to test/t/memory/test_buffer_purge.cpp index a72db1b5c..e61e08d44 100644 --- a/test/t/buffer/test_buffer_purge.cpp +++ b/test/t/memory/test_buffer_purge.cpp @@ -17,9 +17,9 @@ struct CallbackClass { TEST_CASE("Purge data from buffer") { constexpr size_t buffer_size = 10000; + osmium::memory::Buffer buffer{buffer_size}; SECTION("purge empty buffer") { - osmium::memory::Buffer buffer(buffer_size); REQUIRE(std::distance(buffer.begin(), buffer.end()) == 0); CallbackClass callback; @@ -30,15 +30,13 @@ TEST_CASE("Purge data from buffer") { } SECTION("purge buffer with one object but nothing to delete") { - osmium::memory::Buffer buffer(buffer_size); - { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser"); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("testuser"); } buffer.commit(); REQUIRE(std::distance(buffer.begin(), buffer.end()) == 1); - size_t committed = buffer.committed(); + const size_t committed = buffer.committed(); CallbackClass callback; buffer.purge_removed(&callback); @@ -49,12 +47,10 @@ TEST_CASE("Purge data from buffer") { } SECTION("purge buffer with one object which gets deleted") { - osmium::memory::Buffer buffer(buffer_size); - { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser"); - node_builder.object().set_removed(true); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("testuser"); + node_builder.set_removed(true); } buffer.commit(); REQUIRE(std::distance(buffer.begin(), buffer.end()) == 1); @@ -68,21 +64,19 @@ TEST_CASE("Purge data from buffer") { } SECTION("purge buffer with two objects, first gets deleted") { - osmium::memory::Buffer buffer(buffer_size); - { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser"); - node_builder.object().set_removed(true); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("testuser"); + node_builder.set_removed(true); } buffer.commit(); - size_t size1 = buffer.committed(); + const size_t size1 = buffer.committed(); { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser"); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("testuser"); } buffer.commit(); - size_t size2 = buffer.committed() - size1; + const size_t size2 = buffer.committed() - size1; REQUIRE(std::distance(buffer.begin(), buffer.end()) == 2); CallbackClass callback; @@ -94,18 +88,16 @@ TEST_CASE("Purge data from buffer") { } SECTION("purge buffer with two objects, second gets deleted") { - osmium::memory::Buffer buffer(buffer_size); - { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser_longer_name"); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("testuser_longer_name"); } buffer.commit(); size_t size1 = buffer.committed(); { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser"); - node_builder.object().set_removed(true); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("testuser"); + node_builder.set_removed(true); } buffer.commit(); @@ -120,24 +112,22 @@ TEST_CASE("Purge data from buffer") { } SECTION("purge buffer with three objects, middle one gets deleted") { - osmium::memory::Buffer buffer(buffer_size); - { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser_longer_name"); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("testuser_longer_name"); } buffer.commit(); { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser"); - node_builder.object().set_removed(true); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("testuser"); + node_builder.set_removed(true); } buffer.commit(); { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("sn"); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("sn"); } buffer.commit(); @@ -151,26 +141,24 @@ TEST_CASE("Purge data from buffer") { } SECTION("purge buffer with three objects, all get deleted") { - osmium::memory::Buffer buffer(buffer_size); - { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser_longer_name"); - node_builder.object().set_removed(true); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("testuser_longer_name"); + node_builder.set_removed(true); } buffer.commit(); { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("testuser"); - node_builder.object().set_removed(true); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("testuser"); + node_builder.set_removed(true); } buffer.commit(); { - osmium::builder::NodeBuilder node_builder(buffer); - node_builder.add_user("sn"); - node_builder.object().set_removed(true); + osmium::builder::NodeBuilder node_builder{buffer}; + node_builder.set_user("sn"); + node_builder.set_removed(true); } buffer.commit(); diff --git a/test/t/basic/test_area.cpp b/test/t/osm/test_area.cpp similarity index 100% rename from test/t/basic/test_area.cpp rename to test/t/osm/test_area.cpp diff --git a/test/t/basic/test_box.cpp b/test/t/osm/test_box.cpp similarity index 100% rename from test/t/basic/test_box.cpp rename to test/t/osm/test_box.cpp diff --git a/test/t/basic/test_changeset.cpp b/test/t/osm/test_changeset.cpp similarity index 73% rename from test/t/basic/test_changeset.cpp rename to test/t/osm/test_changeset.cpp index 36c4778fe..9be8fbbec 100644 --- a/test/t/basic/test_changeset.cpp +++ b/test/t/osm/test_changeset.cpp @@ -9,7 +9,7 @@ using namespace osmium::builder::attr; TEST_CASE("Build changeset") { - osmium::memory::Buffer buffer(10 * 1000); + osmium::memory::Buffer buffer{10 * 1000}; osmium::builder::add_changeset(buffer, _cid(42), @@ -88,58 +88,57 @@ TEST_CASE("Build changeset") { } TEST_CASE("Create changeset without helper") { - osmium::memory::Buffer buffer(10 * 1000); - osmium::builder::ChangesetBuilder builder(buffer); - - osmium::Changeset& cs1 = builder.object(); - cs1.set_id(42) - .set_created_at(100) - .set_closed_at(200) - .set_num_changes(7) - .set_num_comments(2) - .set_uid(9); - - builder.add_user("user"); + osmium::memory::Buffer buffer{10 * 1000}; { - osmium::builder::TagListBuilder tl_builder(buffer, &builder); - tl_builder.add_tag("key1", "val1"); - tl_builder.add_tag("key2", "val2"); - } + osmium::builder::ChangesetBuilder builder{buffer}; - { - osmium::builder::ChangesetDiscussionBuilder disc_builder(buffer, &builder); + builder.set_id(42) + .set_created_at(100) + .set_closed_at(200) + .set_num_changes(7) + .set_num_comments(2) + .set_uid(9) + .set_user("user"); + + { + osmium::builder::TagListBuilder tl_builder{builder}; + tl_builder.add_tag("key1", "val1"); + tl_builder.add_tag("key2", "val2"); + } + + osmium::builder::ChangesetDiscussionBuilder disc_builder{builder}; disc_builder.add_comment(osmium::Timestamp(300), 10, "user2"); disc_builder.add_comment_text("foo"); disc_builder.add_comment(osmium::Timestamp(400), 9, "user"); disc_builder.add_comment_text("bar"); } - buffer.commit(); + const auto& cs = buffer.get(buffer.commit()); - REQUIRE(42 == cs1.id()); - REQUIRE(9 == cs1.uid()); - REQUIRE(7 == cs1.num_changes()); - REQUIRE(2 == cs1.num_comments()); - REQUIRE(true == cs1.closed()); - REQUIRE(osmium::Timestamp(100) == cs1.created_at()); - REQUIRE(osmium::Timestamp(200) == cs1.closed_at()); - REQUIRE(2 == cs1.tags().size()); - REQUIRE(std::string("user") == cs1.user()); + REQUIRE(42 == cs.id()); + REQUIRE(9 == cs.uid()); + REQUIRE(7 == cs.num_changes()); + REQUIRE(2 == cs.num_comments()); + REQUIRE(true == cs.closed()); + REQUIRE(osmium::Timestamp(100) == cs.created_at()); + REQUIRE(osmium::Timestamp(200) == cs.closed_at()); + REQUIRE(2 == cs.tags().size()); + REQUIRE(std::string("user") == cs.user()); - auto cit = cs1.discussion().begin(); + auto cit = cs.discussion().begin(); - REQUIRE(cit != cs1.discussion().end()); + REQUIRE(cit != cs.discussion().end()); REQUIRE(cit->date() == osmium::Timestamp(300)); REQUIRE(cit->uid() == 10); REQUIRE(std::string("user2") == cit->user()); REQUIRE(std::string("foo") == cit->text()); - REQUIRE(++cit != cs1.discussion().end()); + REQUIRE(++cit != cs.discussion().end()); REQUIRE(cit->date() == osmium::Timestamp(400)); REQUIRE(cit->uid() == 9); REQUIRE(std::string("user") == cit->user()); REQUIRE(std::string("bar") == cit->text()); - REQUIRE(++cit == cs1.discussion().end()); + REQUIRE(++cit == cs.discussion().end()); } diff --git a/test/t/basic/test_crc.cpp b/test/t/osm/test_crc.cpp similarity index 100% rename from test/t/basic/test_crc.cpp rename to test/t/osm/test_crc.cpp diff --git a/test/t/osm/test_entity_bits.cpp b/test/t/osm/test_entity_bits.cpp new file mode 100644 index 000000000..a124fa372 --- /dev/null +++ b/test/t/osm/test_entity_bits.cpp @@ -0,0 +1,62 @@ +#include "catch.hpp" + +#include + +static_assert((osmium::osm_entity_bits::node + |osmium::osm_entity_bits::way + |osmium::osm_entity_bits::relation) + == osmium::osm_entity_bits::nwr, "entity_bits nwr failed"); + +static_assert((osmium::osm_entity_bits::node + |osmium::osm_entity_bits::way + |osmium::osm_entity_bits::relation + |osmium::osm_entity_bits::area) + == osmium::osm_entity_bits::nwra, "entity_bits nwra failed"); + +static_assert((osmium::osm_entity_bits::nwra + |osmium::osm_entity_bits::changeset) + == osmium::osm_entity_bits::all, "entity_bits all failed"); + +static_assert((osmium::osm_entity_bits::all + &osmium::osm_entity_bits::node) + == osmium::osm_entity_bits::node, "entity_bits node failed"); + +static_assert((~osmium::osm_entity_bits::all) == osmium::osm_entity_bits::nothing, "entity_bits nothing is the inverse of all"); +static_assert((~osmium::osm_entity_bits::nothing) == osmium::osm_entity_bits::all, "entity_bits all is the inverse of nothing"); +static_assert((~osmium::osm_entity_bits::changeset) == osmium::osm_entity_bits::nwra, "entity_bits nwra is the inverse of changeset"); + +TEST_CASE("Bitwise 'and' and 'or' on entity bits") { + osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::node | osmium::osm_entity_bits::way; + REQUIRE(entities == (osmium::osm_entity_bits::node | osmium::osm_entity_bits::way)); + + entities |= osmium::osm_entity_bits::relation; + REQUIRE((entities & osmium::osm_entity_bits::object)); + + entities |= osmium::osm_entity_bits::area; + REQUIRE(entities == osmium::osm_entity_bits::object); + + REQUIRE_FALSE((entities & osmium::osm_entity_bits::changeset)); + + entities &= osmium::osm_entity_bits::node; + REQUIRE((entities & osmium::osm_entity_bits::node)); + REQUIRE_FALSE((entities & osmium::osm_entity_bits::way)); + REQUIRE(entities == osmium::osm_entity_bits::node); +} + +TEST_CASE("Bitwise 'not' on entity bits") { + REQUIRE(~osmium::osm_entity_bits::all == osmium::osm_entity_bits::nothing); + REQUIRE(~osmium::osm_entity_bits::nothing == osmium::osm_entity_bits::all); + REQUIRE(~osmium::osm_entity_bits::node == (osmium::osm_entity_bits::way | osmium::osm_entity_bits::relation | osmium::osm_entity_bits::area | osmium::osm_entity_bits::changeset)); + REQUIRE(~osmium::osm_entity_bits::nwr == (osmium::osm_entity_bits::area | osmium::osm_entity_bits::changeset)); + REQUIRE(~osmium::osm_entity_bits::nwra == osmium::osm_entity_bits::changeset); +} + +TEST_CASE("Converting item types to entity bits") { + REQUIRE(osmium::osm_entity_bits::nothing == osmium::osm_entity_bits::from_item_type(osmium::item_type::undefined)); + REQUIRE(osmium::osm_entity_bits::node == osmium::osm_entity_bits::from_item_type(osmium::item_type::node)); + REQUIRE(osmium::osm_entity_bits::way == osmium::osm_entity_bits::from_item_type(osmium::item_type::way)); + REQUIRE(osmium::osm_entity_bits::relation == osmium::osm_entity_bits::from_item_type(osmium::item_type::relation)); + REQUIRE(osmium::osm_entity_bits::changeset == osmium::osm_entity_bits::from_item_type(osmium::item_type::changeset)); + REQUIRE(osmium::osm_entity_bits::area == osmium::osm_entity_bits::from_item_type(osmium::item_type::area)); +} + diff --git a/test/t/basic/test_location.cpp b/test/t/osm/test_location.cpp similarity index 78% rename from test/t/basic/test_location.cpp rename to test/t/osm/test_location.cpp index dc5b37872..7abf77962 100644 --- a/test/t/basic/test_location.cpp +++ b/test/t/osm/test_location.cpp @@ -166,41 +166,44 @@ TEST_CASE("Location hash") { } } -#define CR(s, v, r) { \ - const char* strm = "-" s; \ - const char* strp = strm + 1; \ - REQUIRE(std::atof(strp) == Approx( v / 10000000.0)); \ - REQUIRE(std::atof(strm) == Approx(-v / 10000000.0)); \ - const char** data = &strp; \ - REQUIRE(osmium::detail::string_to_location_coordinate(data) == v); \ - REQUIRE(std::string{*data} == r); \ - data = &strm; \ - REQUIRE(osmium::detail::string_to_location_coordinate(data) == -v); \ - REQUIRE(std::string{*data} == r); \ - } +void C(const char* s, long v, const char* r = "") { + std::string strm{"-"}; + strm += s; + REQUIRE(std::atof(strm.c_str() + 1) == Approx( v / 10000000.0)); + REQUIRE(std::atof(strm.c_str() ) == Approx(-v / 10000000.0)); + const char* x = strm.c_str() + 1; + const char** data = &x; + REQUIRE(osmium::detail::string_to_location_coordinate(data) == v); + REQUIRE(std::string{*data} == r); + x = strm.c_str(); + data = &x; + REQUIRE(osmium::detail::string_to_location_coordinate(data) == -v); + REQUIRE(std::string{*data} == r); +} -#define C(s, v) CR(s, v, "") - -#define F(s) { \ - const char* strm = "-" s; \ - const char* strp = strm + 1; \ - const char** data = &strp; \ - REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); \ - data = &strm; \ - REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); \ - } +void F(const char* s) { + std::string strm{"-"}; + strm += s; + const char* x = strm.c_str(); + const char** data = &x; + REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); + ++x; + data = &x; + REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); +} TEST_CASE("Parsing coordinates from strings") { F("x"); F("."); + F(".e2"); F("--"); F(""); F(" "); F(" 123"); - CR("123 ", 1230000000, " "); - CR("123x", 1230000000, "x"); - CR("1.2x", 12000000, "x"); + C("123 ", 1230000000, " "); + C("123x", 1230000000, "x"); + C("1.2x", 12000000, "x"); C("0", 0); @@ -223,14 +226,19 @@ TEST_CASE("Parsing coordinates from strings") { F("1234"); F("1234."); F("12345678901234567890"); + F("1.1234568111111111111111111111111111111"); + F("112.34568111111111111111111111111111111"); C("0.", 0); + C(".0", 0); C("0.0", 0); C("1.", 10000000); C("1.0", 10000000); C("1.2", 12000000); C("0.1", 1000000); + C(".1", 1000000); C("0.01", 100000); + C(".01", 100000); C("0.001", 10000); C("0.0001", 1000); C("0.00001", 100); @@ -251,6 +259,24 @@ TEST_CASE("Parsing coordinates from strings") { C("179.99999999", 1800000000); C("200.123", 2001230000); + C("8.109E-4" , 8109); + C("8.1090E-4" , 8109); + C("8.10909E-4" , 8109); + C("8.109095E-4" , 8109); + C("8.1090959E-4" , 8109); + C("8.10909598E-4" , 8109); + C("8.109095988E-4" , 8109); + C("8.1090959887E-4" , 8109); + C("8.10909598870E-4" , 8109); + C("8.109095988709E-4" , 8109); + C("8.1090959887098E-4" , 8109); + C("8.10909598870983E-4" , 8109); + C("8.109095988709837E-4" , 8109); + C("81.09095988709837E-4" , 81091); + C("810.9095988709837E-4" , 810910); + C(".8109095988709837E-4" , 811); + C(".08109095988709837E-4" , 81); + C("1e2", 1000000000); C("1e1", 100000000); C("1e0", 10000000); @@ -263,8 +289,10 @@ TEST_CASE("Parsing coordinates from strings") { C("1e-7", 1); C("1.0e2", 1000000000); + C("1.e2", 1000000000); C("1.1e1", 110000000); C("0.1e1", 10000000); + C(".1e1", 10000000); C("1.2e0", 12000000); C("1.9e-1", 1900000); C("2.0e-2", 200000); @@ -291,32 +319,35 @@ TEST_CASE("Parsing coordinates from strings") { F("5.0e2"); F("3e2"); F("1e"); + F("1e-"); + F("1e1234567"); F("0.5e"); F("1e10"); - CR("1e2 ", 1000000000, " "); - CR("1.1e2 ", 1100000000, " "); - CR("1.1e2x", 1100000000, "x"); - CR("1.1e2:", 1100000000, ":"); + C("1e2 ", 1000000000, " "); + C("1.1e2 ", 1100000000, " "); + C("1.1e2x", 1100000000, "x"); + C("1.1e2:", 1100000000, ":"); } -#undef C -#undef CR -#undef F +TEST_CASE("Writing zero coordinate into string") { + std::string buffer; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), 0); + REQUIRE(buffer == "0"); +} -#define CW(v, s) buffer.clear(); \ - osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), v); \ - CHECK(buffer == s); \ - buffer.clear(); \ - osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), -v); \ - CHECK(buffer == "-" s); - -TEST_CASE("Writing coordinates into string") { +void CW(long v, const char* s) { std::string buffer; - osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), 0); - CHECK(buffer == "0"); + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), v); + REQUIRE(buffer == s); + buffer.clear(); + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), -v); + REQUIRE(buffer[0] == '-'); + REQUIRE_FALSE(std::strcmp(buffer.c_str() + 1, s)); +} +TEST_CASE("Writing coordinate into string") { CW( 10000000, "1"); CW( 90000000, "9"); CW( 100000000, "10"); @@ -338,8 +369,6 @@ TEST_CASE("Writing coordinates into string") { CW(1799999999, "179.9999999"); } -#undef CW - TEST_CASE("set lon/lat from string") { osmium::Location loc; loc.set_lon("1.2"); diff --git a/test/t/basic/test_node.cpp b/test/t/osm/test_node.cpp similarity index 100% rename from test/t/basic/test_node.cpp rename to test/t/osm/test_node.cpp diff --git a/test/t/basic/test_node_ref.cpp b/test/t/osm/test_node_ref.cpp similarity index 100% rename from test/t/basic/test_node_ref.cpp rename to test/t/osm/test_node_ref.cpp diff --git a/test/t/basic/test_object_comparisons.cpp b/test/t/osm/test_object_comparisons.cpp similarity index 100% rename from test/t/basic/test_object_comparisons.cpp rename to test/t/osm/test_object_comparisons.cpp diff --git a/test/t/basic/test_relation.cpp b/test/t/osm/test_relation.cpp similarity index 100% rename from test/t/basic/test_relation.cpp rename to test/t/osm/test_relation.cpp diff --git a/test/t/basic/test_timestamp.cpp b/test/t/osm/test_timestamp.cpp similarity index 100% rename from test/t/basic/test_timestamp.cpp rename to test/t/osm/test_timestamp.cpp diff --git a/test/t/basic/test_types_from_string.cpp b/test/t/osm/test_types_from_string.cpp similarity index 100% rename from test/t/basic/test_types_from_string.cpp rename to test/t/osm/test_types_from_string.cpp diff --git a/test/t/basic/test_way.cpp b/test/t/osm/test_way.cpp similarity index 98% rename from test/t/basic/test_way.cpp rename to test/t/osm/test_way.cpp index 005ef3000..21258a4c2 100644 --- a/test/t/basic/test_way.cpp +++ b/test/t/osm/test_way.cpp @@ -68,7 +68,7 @@ TEST_CASE("build way with helpers") { { osmium::builder::WayBuilder builder(buffer); - builder.add_user("username"); + builder.set_user("username"); builder.add_tags({ {"amenity", "restaurant"}, {"name", "Zum goldenen Schwanen"}