diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d132ef5916..25327c8e0e5 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 095137b3975..21cf98e11e4 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 68fc2f61b8f..d526f13b0fe 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 e46c833496d..4b76fcbc172 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 8a6a8ff50d4..be6adb9541d 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 1a16275f0df..41f9aa07890 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 6062eccebc9..5b82a7c3abd 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 00000000000..091e5c025ef --- /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 00000000000..a5207276420 --- /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 9733bfe1bde..f8901c2739b 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 fba8ffb928a..2224e18dc87 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 b47cfdcf7b1..ac078963389 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 e2910329252..55bd4063a6b 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 030337454cf..0c849a7b0e6 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 00000000000..a7c19047df0 --- /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 48a0823db82..0ced82c1c71 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 00000000000..ff79cbbd534 --- /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 00000000000..dbc50db9a6f --- /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 633498199c8..c2a94e5a272 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 478b6c6b605..00000000000 --- 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 00000000000..01d7e36b301 --- /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 81a6d0cc575..00000000000 --- 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 d5bf8d8daae..092f4b4c7b1 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 b131a431756..1c03d3e521e 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 a4361e0e130..97d512a1074 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 44983cc7547..fae12806961 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 2a5b6907048..8e0b4a3df2e 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 1b274ada562..044da1e9dff 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,64 +183,36 @@ 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; } - }; // 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. + * Add a subitem to the object being built. This can be something + * like a TagList or RelationMemberList. */ - void add_user(const char* user) { - add_user(user, static_cast_with_assert(std::strlen(user))); + void add_item(const osmium::memory::Item& item) { + m_buffer.add_item(item); + add_size(item.padded_size()); } /** - * Add user name to buffer. - * - * @param user User name. + * @deprecated Use the version of add_item() taking a + * reference instead. */ - void add_user(const std::string& user) { - add_user(user.data(), static_cast_with_assert(user.size())); + OSMIUM_DEPRECATED void add_item(const osmium::memory::Item* item) { + assert(item); + add_item(*item); } - }; // class ObjectBuilder + }; // class Builder } // namespace builder diff --git a/include/osmium/builder/osm_object_builder.hpp b/include/osmium/builder/osm_object_builder.hpp index e7a82988b88..b1c7220a727 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 14c51dfdf96..f2db402292e 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 f406076a184..d59e7cebce7 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 76d452e4ef9..5e6e773f55e 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 672ae5416db..2c33017266e 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 4e955734a7c..d112ac08b4e 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 a490f9e28eb..61c6de209be 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 279365d08ef..5f1956d7537 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 e6e190e2744..2ef80f9ebf8 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 ac87c2fc918..9f4af1f628b 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 00000000000..4a894a0ba29 --- /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 c3deeadf80c..65871f23e0d 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 1d2d5aa3421..d26a4aa4dcc 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 98d082b8276..529ad2539db 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 1e3c58cc66a..43fea36719b 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 241a64f9794..68f57972fa6 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 e7f93bd5ea0..a38bba64e62 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 98081e3c984..67b05a83b33 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; + } + + osmium::io::read_meta read_metadata() const noexcept { + return m_options.read_metadata; } - bool header_is_done() const { + 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 d6428790a9e..de4595e0d24 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 15a31f38a5f..1bd8b0763d9 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 97c59275a09..ee35b36f397 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 5df191db65e..5164ce33d74 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 1253447f3cf..31e778ab294 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 bc4702000e9..021ea7de4f2 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 d8c57d86e74..242ef9b8388 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 c447cb478b5..72b4abc7aad 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 55ff5c6de77..abd85f84b08 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 12f97b8e66f..89c8564f308 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 8c246db02ca..bcf0bd0a441 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 2a2c0408897..fb413ff2d47 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 2df33c7f5da..b72ca4d7ccf 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 490fbe95b62..d146e97cee6 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 8a503caaca4..828a2c2bcde 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 2abeac4a1d8..bf057fd3a29 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 b8e9ddba830..05afe3b0786 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,21 +77,21 @@ 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 type& operator|=(type& lhs, const type rhs) noexcept { - lhs = lhs | rhs; - return lhs; + inline constexpr type operator&(const type lhs, const type rhs) noexcept { + return static_cast(static_cast(lhs) & static_cast(rhs)); } - 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 value) noexcept { + return all & static_cast(~static_cast(value)); } - 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; } inline type operator&=(type& lhs, const type rhs) noexcept { @@ -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 c5da620cb39..d208717595d 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; - } else { - goto error; - } + 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; - } + // 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; + if (max_digits == 0) { + goto error; + } + } else { + // 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 677ffc78400..f3df5e9618c 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 6cfdf229587..f430b63a8d2 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 caa6fbcdd70..01fe24908b7 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 2aa9caaf152..8c096807ead 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 cd2a913b7a3..e2537cef477 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 f6713fef46f..e4415ef6ca0 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 b8455b4ebe9..53f6de90ffb 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 b28dca1d01c..7624a60f311 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 6f4f7b1d95d..5ae91089110 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 814aa2c0e2b..0e528fc643b 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 6f3b0a368dc..09c576288b5 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 6230cde23da..051574e08c8 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(buffer test_buffer_basics) -add_unit_test(buffer test_buffer_node) -add_unit_test(buffer test_buffer_purge) +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(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 0d2739a3a81..eb984f8517b 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 00000000000..90bf76ea9b4 --- /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 00000000000..9a68ae8692f --- /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 00000000000..ee542269545 --- /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 00000000000..6323f073195 --- /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 00000000000..934ef46f7d7 --- /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 879fc5b1d11..2e6fe8d39cc 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 13de94b5fae..00000000000 --- 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 00000000000..5b1256413f6 --- /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 e0cefe6d1ec..00000000000 --- 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 c8fbc098fb5..d8f7c36c302 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 fe950434b54..4122d17c80c 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 42fc864bb3f..08efc031496 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 using MercatorProjection class to WKT") { + osmium::geom::WKTFactory factory{2}; -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); - } + const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(wkt == "POINT(356222.37 467961.14)"); +} - SECTION("cleanup") { - // trying to make valgrind happy, but there is still a memory leak in proj library - pj_deallocate_grids(); - } +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 725be7fb940..572493655f3 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") { - osmium::geom::GeoJSONFactory<> factory; + SECTION("empty_point") { + REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location); + } - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); } -SECTION("linestring") { +TEST_CASE("GeoJSON linestring geometry") { osmium::geom::GeoJSONFactory<> factory; + osmium::memory::Buffer buffer{1000}; - 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); + } - 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") { + const auto& wnl = create_test_wnl_same_location(buffer); -SECTION("linestring_with_two_same_locations") { - osmium::geom::GeoJSONFactory<> factory; + 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); - 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); + { + 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); + } - { - 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); + } } - { - 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") { - osmium::geom::GeoJSONFactory<> factory; - - osmium::memory::Buffer buffer(1000); - auto& wnl = create_test_wnl_undefined_location(buffer); - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); } -SECTION("area_1outer_0inner") { +TEST_CASE("GeoJSON area geometry") { osmium::geom::GeoJSONFactory<> factory; + osmium::memory::Buffer buffer{1000}; - osmium::memory::Buffer buffer(1000); - const osmium::Area& area = create_test_area_1outer_0inner(buffer); + SECTION("area_1outer_0inner") { + 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); + 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; - osmium::memory::Buffer buffer(1000); - const osmium::Area& area = create_test_area_1outer_1inner(buffer); + SECTION("area_1outer_1inner") { + 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; - 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); - - 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); + SECTION("area_2outer_2inner") { + 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); + + 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 f74027c24b0..8e7fac48356 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 1fca63bc803..00000000000 --- 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 3490c5759d9..5e030824712 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") { - osmium::geom::OGRFactory<> factory; + SECTION("empty_point") { + REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); + } - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); } -SECTION("linestring") { +TEST_CASE("OGR linestring geometry") { osmium::geom::OGRFactory<> factory; + osmium::memory::Buffer buffer{10000}; + const auto& wnl = create_test_wnl_okay(buffer); - 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()); - - REQUIRE(3.5 == l0e->getX(1)); -} + const OGRLineString* l0e = p0->getExteriorRing(); + REQUIRE(4 == l0e->getNumPoints()); -SECTION("area_1outer_1inner") { - osmium::geom::OGRFactory<> factory; - - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_1outer_1inner(buffer); + REQUIRE(3.5 == l0e->getX(1)); + } - std::unique_ptr mp {factory.create_multipolygon(area)}; - REQUIRE(1 == mp->getNumGeometries()); + SECTION("area_1outer_1inner") { + const osmium::Area& area = create_test_area_1outer_1inner(buffer); - const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); - REQUIRE(p0); - REQUIRE(1 == p0->getNumInteriorRings()); + std::unique_ptr mp {factory.create_multipolygon(area)}; + REQUIRE(1 == mp->getNumGeometries()); - const OGRLineString* l0e = p0->getExteriorRing(); - REQUIRE(5 == l0e->getNumPoints()); + const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); + REQUIRE(p0); + REQUIRE(1 == p0->getNumInteriorRings()); - const OGRLineString* l0i0 = p0->getInteriorRing(0); - REQUIRE(5 == l0i0->getNumPoints()); -} + const OGRLineString* l0e = p0->getExteriorRing(); + REQUIRE(5 == l0e->getNumPoints()); -SECTION("area_2outer_2inner") { - osmium::geom::OGRFactory<> factory; + const OGRLineString* l0i0 = p0->getInteriorRing(0); + REQUIRE(5 == l0i0->getNumPoints()); + } - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_2outer_2inner(buffer); + SECTION("area_2outer_2inner") { + const osmium::Area& area = create_test_area_2outer_2inner(buffer); - std::unique_ptr mp {factory.create_multipolygon(area)}; - REQUIRE(2 == mp->getNumGeometries()); + std::unique_ptr mp {factory.create_multipolygon(area)}; + REQUIRE(2 == mp->getNumGeometries()); - const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); - REQUIRE(p0); - REQUIRE(2 == p0->getNumInteriorRings()); + const OGRPolygon* p0 = dynamic_cast(mp->getGeometryRef(0)); + REQUIRE(p0); + REQUIRE(2 == p0->getNumInteriorRings()); - const OGRLineString* l0e = p0->getExteriorRing(); - REQUIRE(5 == l0e->getNumPoints()); + const OGRLineString* l0e = p0->getExteriorRing(); + REQUIRE(5 == l0e->getNumPoints()); - const OGRPolygon* p1 = dynamic_cast(mp->getGeometryRef(1)); - REQUIRE(p1); - REQUIRE(0 == p1->getNumInteriorRings()); + 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 00000000000..548d1438977 --- /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 58854109e42..14df3bfd45e 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()); - - { - 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)); + osmium::geom::Projection projection_3857{3857}; + + 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}; + + 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)); + } } - { - 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("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)); - } -} } diff --git a/test/t/geom/test_tile.cpp b/test/t/geom/test_tile.cpp index 5454fed7909..953fcc7261d 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 d4d9228689f..66dd42e510f 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 91ac114dd12..68de4c3ef9e 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 00000000000..4c2444717e0 --- /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 810ef3be97e..36fc074b4b5 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") { - - SECTION("Dummy") { - using index_type = osmium::index::map::Dummy; +TEST_CASE("Map Id to location: 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()); +} - SECTION("DenseMemArray") { - using index_type = osmium::index::map::DenseMemArray; +TEST_CASE("Map Id to location: 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 index1; + test_func_all(index1); - index_type index2; - test_func_real(index2); - } + index_type index2; + test_func_real(index2); +} - SECTION("SparseMemArray") { - using index_type = osmium::index::map::SparseMemArray; +TEST_CASE("Map Id to location: SparseMemArray") { + using index_type = osmium::index::map::SparseMemArray; - 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(2 == index1.size()); + REQUIRE(2 == index1.size()); - index_type index2; - test_func_real(index2); - } + 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(); +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); + 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); + 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"); - std::unique_ptr index2 = map_factory.create_map(map_type_name); - index2->reserve(1000); - test_func_real(*index2); - } - } + 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 00000000000..5428d8213c4 --- /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 c5c99759831..b1076ccaddc 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 @@ class MockParser : public osmium::io::detail::Parser { 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 ba2431b8072..040cbb80f53 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()); - - 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(); + osmium::builder::NodeBuilder node_builder{buffer}; + + 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()); - - 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.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::NodeBuilder node_builder{buffer}; + + 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"); + + 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()); - - 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(); + osmium::builder::NodeBuilder node_builder{buffer}; + + 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()); - - 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(); + osmium::builder::NodeBuilder node_builder{buffer}; + + 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 a72db1b5c1e..e61e08d44cb 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 36c4778fe94..9be8fbbec31 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::ChangesetDiscussionBuilder disc_builder(buffer, &builder); + osmium::builder::ChangesetBuilder builder{buffer}; + + 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 00000000000..a124fa37228 --- /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 dc5b37872e8..7abf77962c8 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); \ - } - -#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 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); +} + +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 - -#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 zero coordinate into string") { + std::string buffer; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), 0); + REQUIRE(buffer == "0"); +} -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 005ef300061..21258a4c2c8 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"}