This is an automated email from the git hooks/post-receive script. sebastic pushed a commit to branch master in repository libosmium.
commit 25f004d9a7a19697585c38b683a35b12651af114 Author: Bas Couwenberg <sebas...@xs4all.nl> Date: Tue Mar 31 22:41:11 2015 +0200 Imported Upstream version 2.1.0 --- .gitignore | 2 - .ycm_extra_conf.py | 10 ++- CHANGELOG.md | 31 +++++++ CMakeLists.txt | 2 +- doc/Doxyfile.in | 6 +- include/osmium/area/multipolygon_collector.hpp | 2 +- include/osmium/builder/builder.hpp | 3 +- include/osmium/handler/node_locations_for_ways.hpp | 6 +- include/osmium/index/map.hpp | 3 +- include/osmium/index/multimap.hpp | 3 +- include/osmium/io/detail/pbf_input_format.hpp | 51 ++++++++---- include/osmium/io/detail/pbf_output_format.hpp | 36 +++++++- include/osmium/io/detail/pbf_stringtable.hpp | 42 ++++++---- include/osmium/io/detail/xml_input_format.hpp | 20 ++++- include/osmium/io/reader.hpp | 2 +- include/osmium/memory/item.hpp | 3 +- include/osmium/osm/area.hpp | 39 +++++++-- include/osmium/osm/box.hpp | 77 ++++++++++++++---- include/osmium/osm/changeset.hpp | 3 +- include/osmium/osm/node_ref_list.hpp | 95 ++++++++++++++++------ include/osmium/osm/way.hpp | 6 +- test/data-tests/testdata-xml.cpp | 95 +++++++++++++++++++++- test/t/basic/test_box.cpp | 16 +++- 23 files changed, 434 insertions(+), 119 deletions(-) diff --git a/.gitignore b/.gitignore index 36118e1..5013903 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ -core *.swp -build* .ycm_extra_conf.pyc diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py index b0f9d3e..2b87306 100644 --- a/.ycm_extra_conf.py +++ b/.ycm_extra_conf.py @@ -6,6 +6,10 @@ # #----------------------------------------------------------------------------- +from os.path import realpath, dirname + +basedir = dirname(realpath(__file__)) + # some default flags # for more information install clang-3.2-doc package and # check UsersManual.html @@ -26,9 +30,9 @@ flags = [ 'c++', # libosmium include dirs -'-Iinclude', -'-Itest/include', -'-Itest/data-test/include', +'-I%s/include' % basedir, +'-I%s/test/include' % basedir, +'-I%s/test/data-test/include' % basedir, # include third party libraries '-I/usr/include/gdal', diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4c345a4 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ + +# Change Log + +All notable changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/). + +## [unreleased] - + +## [2.1.0] - 2015-03-31 + +### Added + +- When writing PBF files, sorting the PBF stringtables is now optional. +- More tests and documentation. + +### Changed + +- Some functions are now declared `noexcept`. +- XML parser fails now if the top-level element is not `osm` or `osmChange`. + +### Fixed + +- Race condition in PBF reader. +- Multipolygon collector was accessing non-existent NodeRef. +- Doxygen documentation wan't showing all classes/functions due to a bug in + Doxygen (up to version 1.8.8). This version contains a workaround to fix + this. + +[unreleased]: https://github.com/osmcode/libosmium/compare/v2.1.0...HEAD +[2.1.0]: https://github.com/osmcode/libosmium/compare/v2.0.0...v2.1.0 + diff --git a/CMakeLists.txt b/CMakeLists.txt index 00d48d4..a277765 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev" project(libosmium) set(LIBOSMIUM_VERSION_MAJOR 2) -set(LIBOSMIUM_VERSION_MINOR 0) +set(LIBOSMIUM_VERSION_MINOR 1) set(LIBOSMIUM_VERSION_PATCH 0) set(LIBOSMIUM_VERSION diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 6535259..6bc0185 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -694,7 +694,7 @@ CITE_BIB_FILES = # messages are off. # The default value is: NO. -QUIET = NO +QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES @@ -860,7 +860,7 @@ IMAGE_PATH = # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. -INPUT_FILTER = +INPUT_FILTER = "grep -v static_assert" # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the @@ -876,7 +876,7 @@ FILTER_PATTERNS = # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. -FILTER_SOURCE_FILES = NO +FILTER_SOURCE_FILES = YES # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and diff --git a/include/osmium/area/multipolygon_collector.hpp b/include/osmium/area/multipolygon_collector.hpp index 0bd10f7..84a5262 100644 --- a/include/osmium/area/multipolygon_collector.hpp +++ b/include/osmium/area/multipolygon_collector.hpp @@ -142,7 +142,7 @@ namespace osmium { * Overwritten from the base class. */ void way_not_in_any_relation(const osmium::Way& way) { - if (way.ends_have_same_location() && way.nodes().size() > 3) { + if (way.nodes().size() > 3 && way.ends_have_same_location()) { // way is closed and has enough nodes, build simple multipolygon try { TAssembler assembler(m_assembler_config); diff --git a/include/osmium/builder/builder.hpp b/include/osmium/builder/builder.hpp index bf9cdcc..6606025 100644 --- a/include/osmium/builder/builder.hpp +++ b/include/osmium/builder/builder.hpp @@ -171,8 +171,7 @@ namespace osmium { template <class TItem> class ObjectBuilder : public Builder { - static_assert(std::is_base_of<osmium::memory::Item, TItem>::value, - "ObjectBuilder can only build objects derived from osmium::memory::Item"); + static_assert(std::is_base_of<osmium::memory::Item, TItem>::value, "ObjectBuilder can only build objects derived from osmium::memory::Item"); public: diff --git a/include/osmium/handler/node_locations_for_ways.hpp b/include/osmium/handler/node_locations_for_ways.hpp index d1224a0..9b9fcbf 100644 --- a/include/osmium/handler/node_locations_for_ways.hpp +++ b/include/osmium/handler/node_locations_for_ways.hpp @@ -63,11 +63,9 @@ namespace osmium { template <class TStoragePosIDs, class TStorageNegIDs = dummy_type> class NodeLocationsForWays : public osmium::handler::Handler { - static_assert(std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, TStoragePosIDs>::value, - "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>"); + static_assert(std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, TStoragePosIDs>::value, "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>"); - static_assert(std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, TStorageNegIDs>::value, - "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>"); + static_assert(std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, TStorageNegIDs>::value, "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>"); public: diff --git a/include/osmium/index/map.hpp b/include/osmium/index/map.hpp index a9fe0c3..7b44b8e 100644 --- a/include/osmium/index/map.hpp +++ b/include/osmium/index/map.hpp @@ -84,8 +84,7 @@ namespace osmium { template <typename TId, typename TValue> class Map { - static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value, - "TId template parameter for class Map must be unsigned integral type"); + static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value, "TId template parameter for class Map must be unsigned integral type"); Map(const Map&) = delete; Map& operator=(const Map&) = delete; diff --git a/include/osmium/index/multimap.hpp b/include/osmium/index/multimap.hpp index e5c6b4f..c817b6f 100644 --- a/include/osmium/index/multimap.hpp +++ b/include/osmium/index/multimap.hpp @@ -50,8 +50,7 @@ namespace osmium { template <typename TId, typename TValue> class Multimap { - static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value, - "TId template parameter for class Multimap must be unsigned integral type"); + static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value, "TId template parameter for class Multimap must be unsigned integral type"); typedef typename std::pair<TId, TValue> element_type; diff --git a/include/osmium/io/detail/pbf_input_format.hpp b/include/osmium/io/detail/pbf_input_format.hpp index 8b997ec..ac69cbd 100644 --- a/include/osmium/io/detail/pbf_input_format.hpp +++ b/include/osmium/io/detail/pbf_input_format.hpp @@ -84,8 +84,9 @@ namespace osmium { class PBFInputFormat : public osmium::io::detail::InputFormat { bool m_use_thread_pool; + bool m_eof { false }; queue_type m_queue; - std::atomic<bool> m_done; + std::atomic<bool> m_quit_input_thread; std::thread m_reader; osmium::thread::Queue<std::string>& m_input_queue; std::string m_input_buffer; @@ -164,11 +165,20 @@ namespace osmium { } ++n; - if (m_done) { + if (m_quit_input_thread) { return; } } - m_done = true; + + // Send an empty buffer to signal the reader that we are + // done. + std::promise<osmium::memory::Buffer> promise; + m_queue.push(promise.get_future()); + promise.set_value(osmium::memory::Buffer{}); + } + + void signal_input_thread_to_quit() { + m_quit_input_thread = true; } public: @@ -184,7 +194,7 @@ namespace osmium { osmium::io::detail::InputFormat(file, read_which_entities), m_use_thread_pool(osmium::config::use_pool_threads_for_pbf_parsing()), m_queue(20, "pbf_parser_results"), // XXX - m_done(false), + m_quit_input_thread(false), m_input_queue(input_queue), m_input_buffer() { GOOGLE_PROTOBUF_VERIFY_VERSION; @@ -199,30 +209,37 @@ namespace osmium { } ~PBFInputFormat() { - m_done = true; + signal_input_thread_to_quit(); if (m_reader.joinable()) { m_reader.join(); } } /** - * Returns the next buffer with OSM data read from the PBF file. - * Blocks if data is not available yet. + * Returns the next buffer with OSM data read from the PBF + * file. Blocks if data is not available yet. * Returns an empty buffer at end of input. */ osmium::memory::Buffer read() override { - if (!m_done || !m_queue.empty()) { - std::future<osmium::memory::Buffer> buffer_future; - m_queue.wait_and_pop(buffer_future); - try { - return buffer_future.get(); - } catch (...) { - m_done = true; - throw; - } + osmium::memory::Buffer buffer; + if (m_eof) { + return buffer; } - return osmium::memory::Buffer(); + std::future<osmium::memory::Buffer> buffer_future; + m_queue.wait_and_pop(buffer_future); + + try { + buffer = std::move(buffer_future.get()); + if (!buffer) { + m_eof = true; + } + return buffer; + } catch (...) { + m_eof = true; + signal_input_thread_to_quit(); + throw; + } } }; // class PBFInputFormat diff --git a/include/osmium/io/detail/pbf_output_format.hpp b/include/osmium/io/detail/pbf_output_format.hpp index 73bc616..acce67d 100644 --- a/include/osmium/io/detail/pbf_output_format.hpp +++ b/include/osmium/io/detail/pbf_output_format.hpp @@ -295,6 +295,14 @@ namespace osmium { bool m_use_compression {true}; /** + * Should the string tables in the data blocks be sorted? + * + * Not sorting the string tables makes writing PBF files + * slightly faster. + */ + bool m_sort_stringtables { true }; + + /** * While the .osm.pbf-format is able to carry all meta information, it is * also able to omit this information to reduce size. */ @@ -340,6 +348,21 @@ namespace osmium { ///// Blob writing ///// + void delta_encode_string_ids() { + if (pbf_nodes && pbf_nodes->has_dense()) { + OSMPBF::DenseNodes* dense = pbf_nodes->mutable_dense(); + + if (dense->has_denseinfo()) { + OSMPBF::DenseInfo* denseinfo = dense->mutable_denseinfo(); + + for (int i=0, l=denseinfo->user_sid_size(); i<l; ++i) { + auto user_sid = denseinfo->user_sid(i); + denseinfo->set_user_sid(i, m_delta_user_sid.update(user_sid)); + } + } + } + } + /** * Before a PrimitiveBlock gets serialized, all interim StringTable-ids needs to be * mapped to the associated real StringTable ids. This is done in this function. @@ -518,11 +541,13 @@ namespace osmium { pbf_primitive_block.set_granularity(location_granularity()); pbf_primitive_block.set_date_granularity(date_granularity()); - // store the interim StringTable into the protobuf object - string_table.store_stringtable(pbf_primitive_block.mutable_stringtable()); + string_table.store_stringtable(pbf_primitive_block.mutable_stringtable(), m_sort_stringtables); - // map all interim string ids to real ids - map_string_ids(); + if (m_sort_stringtables) { + map_string_ids(); + } else { + delta_encode_string_ids(); + } std::promise<std::string> promise; m_output_queue.push(promise.get_future()); @@ -743,6 +768,9 @@ namespace osmium { if (file.get("pbf_compression") == "none" || file.get("pbf_compression") == "false") { m_use_compression = false; } + if (file.get("pbf_sort_stringtables") == "false") { + m_sort_stringtables = false; + } if (file.get("pbf_add_metadata") == "false") { m_should_add_metadata = false; } diff --git a/include/osmium/io/detail/pbf_stringtable.hpp b/include/osmium/io/detail/pbf_stringtable.hpp index c56d549..e3d6fe3 100644 --- a/include/osmium/io/detail/pbf_stringtable.hpp +++ b/include/osmium/io/detail/pbf_stringtable.hpp @@ -145,30 +145,44 @@ namespace osmium { * implementation) is that the string table is sorted first by reverse count (ie descending) * and then by reverse lexicographic order. */ - void store_stringtable(OSMPBF::StringTable* st) { + void store_stringtable(OSMPBF::StringTable* st, bool sort) { // add empty StringTable entry at index 0 // StringTable index 0 is reserved as delimiter in the densenodes key/value list // this line also ensures that there's always a valid StringTable st->add_s(""); - std::multimap<string_info, std::string> sortedbycount; + if (sort) { + std::multimap<string_info, std::string> sortedbycount; - m_id2id_map.resize(m_size+1); + m_id2id_map.resize(m_size+1); - std::transform(m_strings.begin(), m_strings.end(), - std::inserter(sortedbycount, sortedbycount.begin()), - [](const std::pair<std::string, string_info>& p) { - return std::pair<string_info, std::string>(p.second, p.first); - }); + std::transform(m_strings.begin(), m_strings.end(), + std::inserter(sortedbycount, sortedbycount.begin()), + [](const std::pair<std::string, string_info>& p) { + return std::pair<string_info, std::string>(p.second, p.first); + }); - string_id_type n=0; + string_id_type n=0; - for (const auto& mapping : sortedbycount) { - // add the string of the current item to the pbf StringTable - st->add_s(mapping.second); + for (const auto& mapping : sortedbycount) { + // add the string of the current item to the pbf StringTable + st->add_s(mapping.second); - // store the mapping from the interim-id to the real id - m_id2id_map[mapping.first.interim_id] = ++n; + // store the mapping from the interim-id to the real id + m_id2id_map[mapping.first.interim_id] = ++n; + } + } else { + std::vector<std::pair<string_id_type, const char*>> sortedbyid; + sortedbyid.reserve(m_strings.size()); + + for (const auto& p : m_strings) { + sortedbyid.emplace_back(p.second.interim_id, p.first.c_str()); + } + + std::sort(sortedbyid.begin(), sortedbyid.end()); + for (const auto& mapping : sortedbyid) { + st->add_s(mapping.second); + } } } diff --git a/include/osmium/io/detail/xml_input_format.hpp b/include/osmium/io/detail/xml_input_format.hpp index 96003d8..c03f84d 100644 --- a/include/osmium/io/detail/xml_input_format.hpp +++ b/include/osmium/io/detail/xml_input_format.hpp @@ -74,8 +74,8 @@ namespace osmium { /** * Exception thrown when the XML parser failed. The exception contains - * information about the place where the error happened and the type of - * error. + * (if available) information about the place where the error happened + * and the type of error. */ struct xml_error : public io_error { @@ -84,7 +84,7 @@ namespace osmium { XML_Error error_code; std::string error_string; - xml_error(XML_Parser parser) : + explicit xml_error(XML_Parser parser) : io_error(std::string("XML parsing error at line ") + std::to_string(XML_GetCurrentLineNumber(parser)) + ", column " @@ -97,8 +97,20 @@ namespace osmium { error_string(XML_ErrorString(error_code)) { } + explicit xml_error(const std::string& message) : + io_error(message), + line(0), + column(0), + error_code(), + error_string(message) { + } + }; // struct xml_error + /** + * Exception thrown when an OSM XML files contains no version attribute + * on the 'osm' element or if the version is unknown. + */ struct format_version_error : public io_error { std::string version; @@ -434,6 +446,8 @@ namespace osmium { if (m_header.get("version") == "") { throw osmium::format_version_error(); } + } else { + throw osmium::xml_error(std::string("Unknown top-level element: ") + element); } m_context = context::top; break; diff --git a/include/osmium/io/reader.hpp b/include/osmium/io/reader.hpp index 0bb4f1c..8d24652 100644 --- a/include/osmium/io/reader.hpp +++ b/include/osmium/io/reader.hpp @@ -260,7 +260,7 @@ namespace osmium { // m_input->read() can return an invalid buffer to signal EOF, // or a valid buffer with or without data. A valid buffer - // without data is not an error, it just means we have to get + // without data is not an error, it just means we have to // keep getting the next buffer until there is one with data. while (true) { osmium::memory::Buffer buffer = m_input->read(); diff --git a/include/osmium/memory/item.hpp b/include/osmium/memory/item.hpp index df15d01..58d63df 100644 --- a/include/osmium/memory/item.hpp +++ b/include/osmium/memory/item.hpp @@ -55,8 +55,7 @@ namespace osmium { template <typename T> inline T padded_length(T length) noexcept { - static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value, - "Template parameter must be unsigned integral type"); + static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value, "Template parameter must be unsigned integral type"); return (length + align_bytes - 1) & ~(align_bytes - 1); } diff --git a/include/osmium/osm/area.hpp b/include/osmium/osm/area.hpp index afd127e..3e129d0 100644 --- a/include/osmium/osm/area.hpp +++ b/include/osmium/osm/area.hpp @@ -53,12 +53,14 @@ namespace osmium { /** * An outer ring of an Area. */ - class OuterRing : public NodeRefList<osmium::item_type::outer_ring> { + class OuterRing : public NodeRefList { public: + static constexpr osmium::item_type itemtype = osmium::item_type::outer_ring; + OuterRing(): - NodeRefList<osmium::item_type::outer_ring>() { + NodeRefList(itemtype) { } }; // class OuterRing @@ -68,12 +70,14 @@ namespace osmium { /** * An inner ring of an Area. */ - class InnerRing : public NodeRefList<osmium::item_type::inner_ring> { + class InnerRing : public NodeRefList { public: + static constexpr osmium::item_type itemtype = osmium::item_type::inner_ring; + InnerRing(): - NodeRefList<osmium::item_type::inner_ring>() { + NodeRefList(itemtype) { } }; // class InnerRing @@ -87,7 +91,7 @@ namespace osmium { * @param type Type of object (way or relation) * @returns Area id */ - inline osmium::object_id_type object_id_to_area_id(osmium::object_id_type id, osmium::item_type type) { + inline osmium::object_id_type object_id_to_area_id(osmium::object_id_type id, osmium::item_type type) noexcept { osmium::object_id_type area_id = std::abs(id) * 2; if (type == osmium::item_type::relation) { ++area_id; @@ -101,7 +105,7 @@ namespace osmium { * @param id Area id * @returns Way or Relation id. */ - inline osmium::object_id_type area_id_to_object_id(osmium::object_id_type id) { + inline osmium::object_id_type area_id_to_object_id(osmium::object_id_type id) noexcept { return id / 2; } @@ -131,15 +135,17 @@ namespace osmium { /** * Return the Id of the way or relation this area was created from. */ - osmium::object_id_type orig_id() const { + osmium::object_id_type orig_id() const noexcept { return osmium::area_id_to_object_id(id()); } /** * Count the number of outer and inner rings of this area. + * + * @returns Pair (number outer rings, number inner rings) */ std::pair<int, int> num_rings() const { - std::pair<int, int> counter; + std::pair<int, int> counter { 0, 0 }; for (auto it = cbegin(); it != cend(); ++it) { switch (it->type()) { @@ -170,16 +176,31 @@ namespace osmium { } /** - * Is this area a multipolygon, ie. has it more than one outer ring? + * Check whether this area is a multipolygon, ie. whether it has more + * than one outer ring? */ bool is_multipolygon() const { return num_rings().first > 1; } + /** + * Get iterator for iterating over all inner rings in a specified outer + * ring. + * + * @param it Iterator specifying outer ring. + * @returns Iterator to first inner ring in specified outer ring. + */ osmium::memory::ItemIterator<const osmium::InnerRing> inner_ring_cbegin(const osmium::memory::ItemIterator<const osmium::OuterRing>& it) const { return it.cast<const osmium::InnerRing>(); } + /** + * Get iterator for iterating over all inner rings in a specified outer + * ring. + * + * @param it Iterator specifying outer ring. + * @returns Iterator one past last inner ring in specified outer ring. + */ osmium::memory::ItemIterator<const osmium::InnerRing> inner_ring_cend(const osmium::memory::ItemIterator<const osmium::OuterRing>& it) const { return std::next(it).cast<const osmium::InnerRing>(); } diff --git a/include/osmium/osm/box.hpp b/include/osmium/osm/box.hpp index 37baf7e..631f919 100644 --- a/include/osmium/osm/box.hpp +++ b/include/osmium/osm/box.hpp @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include <cassert> #include <iosfwd> #include <osmium/util/compatibility.hpp> @@ -41,7 +42,10 @@ DEALINGS IN THE SOFTWARE. namespace osmium { /** - * Bounding box. + * Bounding box. A box is defined by two locations (bottom left location + * and top right location) or, alternatively by four coordinates (minx, + * miny, maxx, and maxy). If both locations are undefined, the box is + * undefined, too. */ class Box { @@ -59,14 +63,33 @@ namespace osmium { m_top_right() { } + /** + * Create box from minimum and maximum coordinates. + * + * @pre @code minx <= maxx && miny <= maxy @endcode + */ Box(double minx, double miny, double maxx, double maxy) : m_bottom_left(minx, miny), m_top_right(maxx, maxy) { + assert(minx <= maxx && miny <= maxy); } + /** + * Create box from bottom left and top right locations. + * + * @pre Either both locations must be defined or neither. + * @pre If both locations are defined, the + * bottom left location must actually be to the left and below + * the top right location. Same coordinates for bottom/top or + * left/right are also okay. + */ Box(const osmium::Location& bottom_left, const osmium::Location& top_right) : m_bottom_left(bottom_left), m_top_right(top_right) { + assert( + (!!bottom_left && !!top_right) || + (bottom_left.x() <= top_right.x() && bottom_left.y() <= top_right.y()) + ); } Box(const Box&) = default; @@ -76,8 +99,13 @@ namespace osmium { ~Box() = default; /** - * Extend this bounding box by the given location. If the - * location is undefined, the bounding box is unchanged. + * Extend this bounding box by the specified location. If the + * location is undefined, the bounding box is unchanged. If + * the box is undefined it will only contain the location after + * this call. + * + * @param location The location we want to extend the box by. + * @returns A reference to this box. */ Box& extend(const Location& location) noexcept { if (location) { @@ -103,8 +131,11 @@ namespace osmium { } /** - * Extend this bounding box by the given box. If the - * box is undefined, the bounding box is unchanged. + * Extend this bounding box by the specified box. If the + * specified box is undefined, the bounding box is unchanged. + * + * @param box The box to extend by. + * @returns A reference to this box. */ Box& extend(const Box& box) noexcept { extend(box.bottom_left()); @@ -113,14 +144,14 @@ namespace osmium { } /** - * Box are defined, ie. contains defined coordinates. + * Box is defined, ie. contains defined locations. */ explicit constexpr operator bool() const noexcept { return static_cast<bool>(m_bottom_left); } /** - * Box are valid, ie. defined and inside usual bounds + * Box is valid, ie. defined and inside usual bounds * (-180<=lon<=180, -90<=lat<=90). */ OSMIUM_CONSTEXPR bool valid() const noexcept { @@ -128,37 +159,43 @@ namespace osmium { } /** - * Bottom-left location. + * Access bottom-left location. */ OSMIUM_CONSTEXPR Location bottom_left() const noexcept { return m_bottom_left; } /** - * Bottom-left location. + * Access bottom-left location. */ Location& bottom_left() noexcept { return m_bottom_left; } /** - * Top-right location. + * Access top-right location. */ OSMIUM_CONSTEXPR Location top_right() const noexcept { return m_top_right; } /** - * Top-right location. + * Access top-right location. */ Location& top_right() noexcept { return m_top_right; } /** - * Is the location inside the box? + * Check whether the location is inside the box. + * + * @pre Location must be defined. + * @pre Box must be defined. */ - bool contains(const osmium::Location& location) const { + bool contains(const osmium::Location& location) const noexcept { + assert(bottom_left()); + assert(top_right()); + assert(location); return location.x() >= bottom_left().x() && location.y() >= bottom_left().y() && location.x() <= top_right().x() && location.y() <= top_right().y(); } @@ -166,7 +203,7 @@ namespace osmium { /** * Calculate size of the box in square degrees. * - * @throws osmium::invalid_location unless all coordinates are valid + * @throws osmium::invalid_location unless all coordinates are valid. */ double size() const { return (m_top_right.lon() - m_bottom_left.lon()) * @@ -176,14 +213,19 @@ namespace osmium { }; // class Box /** - * Boxes are equal if both locations are equal. + * Boxes are equal if both locations are equal. Undefined boxes will + * compare equal. */ inline OSMIUM_CONSTEXPR bool operator==(const Box& lhs, const Box& rhs) noexcept { - return lhs.bottom_left() == rhs.bottom_left() && lhs.top_right() == rhs.top_right(); + return lhs.bottom_left() == rhs.bottom_left() && + lhs.top_right() == rhs.top_right(); } /** - * Output a box to a stream. + * Output a box to a stream. The format is "(LON, LAT, LON, LAT)" or + * "(undefined)" if the box is undefined. + * + * @returns Reference to basic_ostream given as first parameter. */ template <typename TChar, typename TTraits> inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Box& box) { @@ -202,6 +244,7 @@ namespace osmium { } return out; } + } // namespace osmium #endif // OSMIUM_OSM_BOX_HPP diff --git a/include/osmium/osm/changeset.hpp b/include/osmium/osm/changeset.hpp index cc1c52d..0ab4e9b 100644 --- a/include/osmium/osm/changeset.hpp +++ b/include/osmium/osm/changeset.hpp @@ -298,8 +298,7 @@ namespace osmium { }; // class Changeset - static_assert(sizeof(Changeset) % osmium::memory::align_bytes == 0, - "Class osmium::Changeset has wrong size to be aligned properly!"); + static_assert(sizeof(Changeset) % osmium::memory::align_bytes == 0, "Class osmium::Changeset has wrong size to be aligned properly!"); /** * Changesets are equal if their IDs are equal. diff --git a/include/osmium/osm/node_ref_list.hpp b/include/osmium/osm/node_ref_list.hpp index f45bf6b..f0dfedb 100644 --- a/include/osmium/osm/node_ref_list.hpp +++ b/include/osmium/osm/node_ref_list.hpp @@ -44,51 +44,92 @@ DEALINGS IN THE SOFTWARE. namespace osmium { /** - * A vector of NodeRef objects. Usually this is not instatiated directly, + * A vector of NodeRef objects. Usually this is not instantiated directly, * but one of its subclasses are used. */ - template <osmium::item_type TItemType> class NodeRefList : public osmium::memory::Item { public: - static constexpr osmium::item_type itemtype = TItemType; - - NodeRefList() noexcept : - osmium::memory::Item(sizeof(NodeRefList), TItemType) { + NodeRefList(osmium::item_type itemtype) noexcept : + osmium::memory::Item(sizeof(NodeRefList), itemtype) { } + /** + * Checks whether the node list is empty. + */ bool empty() const noexcept { return sizeof(NodeRefList) == byte_size(); } + /** + * Returns the number of nodes in the list. + */ size_t size() const noexcept { - assert((osmium::memory::Item::byte_size() - sizeof(NodeRefList)) % sizeof(NodeRef) == 0); - return (osmium::memory::Item::byte_size() - sizeof(NodeRefList)) / sizeof(NodeRef); - } - - const NodeRef& operator[](size_t n) const { + auto size_node_refs = osmium::memory::Item::byte_size() - sizeof(NodeRefList); + assert(size_node_refs % sizeof(NodeRef) == 0); + return size_node_refs / sizeof(NodeRef); + } + + /** + * Access specified element. + * + * @param n Get this element of the list. + * @pre @code n < size() @endcode + */ + const NodeRef& operator[](size_t n) const noexcept { + assert(n < size()); const NodeRef* node_ref = &*(cbegin()); return node_ref[n]; } - const NodeRef& front() const { + /** + * Access the first element. + * + * @pre @code !empty() @endcode + */ + const NodeRef& front() const noexcept { + assert(!empty()); return operator[](0); } - const NodeRef& back() const { + /** + * Access the last element. + * + * @pre @code !empty() @endcode + */ + const NodeRef& back() const noexcept { + assert(!empty()); return operator[](size()-1); } - bool is_closed() const { + /** + * Checks whether the first and last node in the list have the same ID. + * + * @pre @code !empty() @endcode + */ + bool is_closed() const noexcept { return front().ref() == back().ref(); } - bool ends_have_same_id() const { + /** + * Checks whether the first and last node in the list have the same ID. + * + * @pre @code !empty() @endcode + */ + bool ends_have_same_id() const noexcept { return front().ref() == back().ref(); } + /** + * Checks whether the first and last node in the list have the same + * location. The ID is not checked. + * + * @pre @code !empty() @endcode + * @pre @code front().location() && back().location() @endcode + */ bool ends_have_same_location() const { + assert(front().location() && back().location()); return front().location() == back().location(); } @@ -96,35 +137,43 @@ namespace osmium { typedef const NodeRef* const_iterator; typedef std::reverse_iterator<const NodeRef*> const_reverse_iterator; - iterator begin() { + /// Returns an iterator to the beginning. + iterator begin() noexcept { return iterator(data() + sizeof(NodeRefList)); } - iterator end() { + /// Returns an iterator to the end. + iterator end() noexcept { return iterator(data() + byte_size()); } - const_iterator cbegin() const { + /// Returns an iterator to the beginning. + const_iterator cbegin() const noexcept { return const_iterator(data() + sizeof(NodeRefList)); } - const_iterator cend() const { + /// Returns an iterator to the end. + const_iterator cend() const noexcept { return const_iterator(data() + byte_size()); } - const_iterator begin() const { + /// Returns an iterator to the beginning. + const_iterator begin() const noexcept { return cbegin(); } - const_iterator end() const { + /// Returns an iterator to the end. + const_iterator end() const noexcept { return cend(); } - const_reverse_iterator crbegin() const { + /// Returns a reverse_iterator to the beginning. + const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } - const_reverse_iterator crend() const { + /// Returns a reverse_iterator to the end. + const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } diff --git a/include/osmium/osm/way.hpp b/include/osmium/osm/way.hpp index 6d3e2de..3c5f1f6 100644 --- a/include/osmium/osm/way.hpp +++ b/include/osmium/osm/way.hpp @@ -49,12 +49,14 @@ namespace osmium { /** * List of node references (id and location) in a way. */ - class WayNodeList : public NodeRefList<osmium::item_type::way_node_list> { + class WayNodeList : public NodeRefList { public: + static constexpr osmium::item_type itemtype = osmium::item_type::way_node_list; + WayNodeList(): - NodeRefList<osmium::item_type::way_node_list>() { + NodeRefList(itemtype) { } }; // class WayNodeList diff --git a/test/data-tests/testdata-xml.cpp b/test/data-tests/testdata-xml.cpp index 94af6f9..1969171 100644 --- a/test/data-tests/testdata-xml.cpp +++ b/test/data-tests/testdata-xml.cpp @@ -285,7 +285,6 @@ TEST_CASE("Reading OSM XML 121") { }, osmium::gzip_error); } -#if 0 SECTION("Using Reader") { REQUIRE_THROWS_AS({ osmium::io::Reader reader(filename("121-truncated_gzip_file", "osm.gz")); @@ -294,12 +293,104 @@ TEST_CASE("Reading OSM XML 121") { reader.close(); }, osmium::gzip_error); } -#endif } // ============================================= +TEST_CASE("Reading OSM XML 122") { + + SECTION("Direct") { + REQUIRE_THROWS_AS( { + read_xml("122-no_osm_element"); + }, osmium::xml_error); + } + + SECTION("Using Reader") { + REQUIRE_THROWS_AS({ + osmium::io::Reader reader(filename("122-no_osm_element")); + osmium::io::Header header = reader.header(); + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + }, osmium::xml_error); + } + +} + +// ============================================= + +TEST_CASE("Reading OSM XML 140") { + + SECTION("Using Reader") { + osmium::io::Reader reader(filename("140-unicode")); + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + + int count = 0; + for (auto it = buffer.begin<osmium::Node>(); it != buffer.end<osmium::Node>(); ++it) { + ++count; + REQUIRE(it->id() == count); + const osmium::TagList& t = it->tags(); + + const char* uc = t["unicode_char"]; + + auto len = atoi(t["unicode_utf8_length"]); + REQUIRE(len == strlen(uc)); + + REQUIRE(!strcmp(uc, t["unicode_xml"])); + + switch (count) { + case 1: + REQUIRE(!strcmp(uc, u8"a")); + break; + case 2: + REQUIRE(!strcmp(uc, u8"\u00e4")); + break; + case 3: + REQUIRE(!strcmp(uc, u8"\u30dc")); + break; + case 4: + REQUIRE(!strcmp(uc, u8"\U0001d11e")); + break; + case 5: + REQUIRE(!strcmp(uc, u8"\U0001f6eb")); + break; + default: + REQUIRE(false); // should not be here + } + } + REQUIRE(count == 5); + } + +} + + +// ============================================= + +TEST_CASE("Reading OSM XML 141") { + + SECTION("Using Reader") { + osmium::io::Reader reader(filename("141-entities")); + osmium::memory::Buffer buffer = reader.read(); + reader.close(); + REQUIRE(buffer.committed() > 0); + REQUIRE(buffer.get<osmium::memory::Item>(0).type() == osmium::item_type::node); + + const osmium::Node& node = buffer.get<osmium::Node>(0); + const osmium::TagList& tags = node.tags(); + + REQUIRE(!strcmp(tags["less-than"], "<")); + REQUIRE(!strcmp(tags["greater-than"], ">")); + REQUIRE(!strcmp(tags["apostrophe"], "'")); + REQUIRE(!strcmp(tags["ampersand"], "&")); + REQUIRE(!strcmp(tags["quote"], "\"")); + } + +} + + +// ============================================= + TEST_CASE("Reading OSM XML 200") { SECTION("Direct") { diff --git a/test/t/basic/test_box.cpp b/test/t/basic/test_box.cpp index 73a45f4..6ebf95c 100644 --- a/test/t/basic/test_box.cpp +++ b/test/t/basic/test_box.cpp @@ -25,12 +25,18 @@ SECTION("instantiation_and_extend_with_undefined") { SECTION("instantiation_and_extend") { osmium::Box b; - b.extend(osmium::Location(1.2, 3.4)); + osmium::Location loc1 { 1.2, 3.4 }; + b.extend(loc1); REQUIRE(!!b); REQUIRE(!!b.bottom_left()); REQUIRE(!!b.top_right()); - b.extend(osmium::Location(3.4, 4.5)); - b.extend(osmium::Location(5.6, 7.8)); + REQUIRE(b.contains(loc1)); + + osmium::Location loc2 { 3.4, 4.5 }; + osmium::Location loc3 { 5.6, 7.8 }; + + b.extend(loc2); + b.extend(loc3); REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4)); REQUIRE(b.top_right() == osmium::Location(5.6, 7.8)); @@ -38,6 +44,10 @@ SECTION("instantiation_and_extend") { b.extend(osmium::Location()); REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4)); REQUIRE(b.top_right() == osmium::Location(5.6, 7.8)); + + REQUIRE(b.contains(loc1)); + REQUIRE(b.contains(loc2)); + REQUIRE(b.contains(loc3)); } SECTION("output_defined") { -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/libosmium.git _______________________________________________ Pkg-grass-devel mailing list Pkg-grass-devel@lists.alioth.debian.org http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel