[libosmium] 01/06: Imported Upstream version 2.7.0
Sebastiaan Couwenberg
sebastic at moszumanska.debian.org
Wed Jun 1 15:19:20 UTC 2016
This is an automated email from the git hooks/post-receive script.
sebastic pushed a commit to branch master
in repository libosmium.
commit 5a6d9f85bb898ee2ac047ec8732477b15d75fbb9
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date: Wed Jun 1 13:32:09 2016 +0200
Imported Upstream version 2.7.0
---
.travis.yml | 3 +-
CHANGELOG.md | 47 +-
CMakeLists.txt | 4 +-
include/gdalcpp.hpp | 91 +-
include/osmium/area/assembler.hpp | 1530 +++++++++++++++-----
include/osmium/area/detail/node_ref_segment.hpp | 309 ++--
include/osmium/area/detail/proto_ring.hpp | 235 ++-
include/osmium/area/detail/segment_list.hpp | 164 ++-
include/osmium/area/detail/vector.hpp | 120 ++
include/osmium/area/multipolygon_collector.hpp | 26 +-
include/osmium/area/problem_reporter.hpp | 76 +-
include/osmium/area/problem_reporter_exception.hpp | 30 +-
include/osmium/area/problem_reporter_ogr.hpp | 143 +-
include/osmium/area/problem_reporter_stream.hpp | 33 +-
include/osmium/area/stats.hpp | 128 ++
include/osmium/builder/osm_object_builder.hpp | 12 +-
include/osmium/diff_iterator.hpp | 8 +-
include/osmium/dynamic_handler.hpp | 2 +-
include/osmium/geom/factory.hpp | 18 +-
include/osmium/geom/geojson.hpp | 10 +-
include/osmium/geom/geos.hpp | 10 +-
include/osmium/geom/ogr.hpp | 35 +-
include/osmium/geom/rapid_geojson.hpp | 10 +-
include/osmium/geom/wkb.hpp | 13 +-
include/osmium/geom/wkt.hpp | 10 +-
include/osmium/handler/chain.hpp | 2 +-
include/osmium/handler/disk_store.hpp | 2 +-
include/osmium/handler/node_locations_for_ways.hpp | 16 +-
include/osmium/handler/object_relations.hpp | 2 +-
include/osmium/index/detail/mmap_vector_base.hpp | 31 +-
include/osmium/index/detail/mmap_vector_file.hpp | 17 +-
include/osmium/index/detail/vector_map.hpp | 16 +-
include/osmium/index/detail/vector_multimap.hpp | 8 +-
include/osmium/index/map.hpp | 12 +-
include/osmium/index/map/sparse_mem_map.hpp | 2 +-
include/osmium/index/multimap.hpp | 8 +-
include/osmium/index/multimap/hybrid.hpp | 14 +-
.../osmium/index/multimap/sparse_mem_multimap.hpp | 11 +-
include/osmium/io/compression.hpp | 14 +-
include/osmium/io/detail/debug_output_format.hpp | 38 +
include/osmium/io/detail/input_format.hpp | 14 +-
include/osmium/io/detail/opl_output_format.hpp | 38 +-
include/osmium/io/detail/output_format.hpp | 4 +-
include/osmium/io/detail/pbf_decoder.hpp | 24 +-
include/osmium/io/detail/pbf_output_format.hpp | 35 +-
include/osmium/io/detail/protobuf_tags.hpp | 4 +-
include/osmium/io/detail/queue_util.hpp | 19 +-
include/osmium/io/detail/string_table.hpp | 13 +-
include/osmium/io/detail/xml_input_format.hpp | 126 +-
include/osmium/io/detail/xml_output_format.hpp | 30 +-
include/osmium/io/input_iterator.hpp | 20 +-
include/osmium/io/output_iterator.hpp | 8 +-
include/osmium/memory/buffer.hpp | 10 +
include/osmium/memory/collection.hpp | 16 +-
include/osmium/memory/item.hpp | 2 +-
include/osmium/memory/item_iterator.hpp | 110 +-
include/osmium/object_pointer_collection.hpp | 4 +-
include/osmium/osm/area.hpp | 33 +-
include/osmium/osm/changeset.hpp | 18 +-
include/osmium/osm/crc.hpp | 1 +
include/osmium/osm/entity_bits.hpp | 18 +-
include/osmium/osm/location.hpp | 42 +-
include/osmium/osm/object.hpp | 68 +-
include/osmium/osm/object_comparisons.hpp | 26 +-
include/osmium/osm/relation.hpp | 2 +-
include/osmium/osm/tag.hpp | 59 +-
include/osmium/osm/types.hpp | 20 +-
include/osmium/osm/types_from_string.hpp | 18 +-
include/osmium/relations/collector.hpp | 32 +-
include/osmium/relations/detail/member_meta.hpp | 2 +-
include/osmium/relations/detail/relation_meta.hpp | 4 +-
include/osmium/tags/filter.hpp | 18 +-
include/osmium/tags/regex_filter.hpp | 2 +-
include/osmium/thread/pool.hpp | 4 +-
include/osmium/thread/sorted_queue.hpp | 159 --
include/osmium/util/delta.hpp | 11 +-
include/osmium/util/iterator.hpp | 15 +-
include/osmium/util/memory_mapping.hpp | 16 +-
.../{tags/regex_filter.hpp => util/misc.hpp} | 32 +-
include/osmium/util/{iterator.hpp => timer.hpp} | 64 +-
.../osmium/{tags/regex_filter.hpp => version.hpp} | 30 +-
test/CMakeLists.txt | 1 +
test/README | 8 -
test/data-tests/testdata-multipolygon.cpp | 4 +-
test/data-tests/testdata-xml.cpp | 28 +-
test/include/catch.hpp | 382 ++++-
test/t/area/test_node_ref_segment.cpp | 185 ++-
test/t/basic/test_entity_bits.cpp | 1 +
test/t/basic/test_location.cpp | 16 +-
test/t/basic/test_object_comparisons.cpp | 132 +-
test/t/basic/test_types_from_string.cpp | 14 +-
test/t/basic/test_way.cpp | 2 +-
test/t/builder/test_attr.cpp | 4 +-
test/t/geom/test_geojson.cpp | 22 +-
test/t/geom/test_geos.cpp | 4 +
test/t/geom/test_ogr.cpp | 4 +
test/t/index/test_file_based_index.cpp | 155 ++
test/t/index/test_id_to_location.cpp | 10 +
test/t/io/test_writer.cpp | 9 +-
test/t/io/test_writer_with_mock_compression.cpp | 3 +-
test/t/io/test_writer_with_mock_encoder.cpp | 3 +-
test/t/tags/test_tag_list.cpp | 8 +-
test/t/util/test_memory_mapping.cpp | 13 +-
103 files changed, 3894 insertions(+), 1545 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index ac0d270..9da37fa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -154,6 +154,5 @@ before_script:
- CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata"
script:
- - make VERBOSE=1
- - ctest --output-on-failure
+ - make VERBOSE=1 && ctest --output-on-failure
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3946448..d60b548 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,50 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
+## [2.7.0] - 2016-06-01
+
+### Added
+
+- New functions for iterating over specific item types in buffers
+ (`osmium::memory::Buffer::select()`), over specific subitems
+ (`osmium::OSMObject::subitems()`), and for iterating over all rings of
+ an area (`osmium::Areas::outer_rings(`), `inner_rings()`).
+- Debug output optionally prints CRC32 when `add_crc32` file option is set.
+
+### Changed
+
+- XML parser will not allow any XML entities which are usually not used in OSM
+ files anyway. This can help avoiding DOS attacks.
+- Removed SortedQueue implementation which was never used.
+- Also incorporate Locations in NodeRefs into CRC32 checksums. This means
+ all checksums will be different compared to earlier versions of libosmium.
+- The completely new algorithm for assembling multipolygons is much faster,
+ has better error reporting, generates statistics and can build more complex
+ multipolygons correctly. The ProblemReporter classes have changed to make
+ this happen, if you have written your own, you have to fix it.
+- Sparse node location stores are now only sorted if needed, ie. when nodes
+ come in unordered.
+
+### Fixed
+
+- Output operator for Location shows full precision.
+- Undefined behaviour in WKB writer and `types_from_string()` function.
+- Fix unsigned overflow in pool.hpp.
+- OSM objects are now ordered by type (nodes, then ways, then relations),
+ then ID, then version, then timestamp. Ordering by timestamp is normally
+ not necessary, because there can't be two objects with same type, ID, and
+ version but different timestamp. But this can happen when diffs are
+ created from OSM extracts, so we check for this here. This change also
+ makes sure IDs are always ordered by absolute IDs, positives first, so
+ order is 0, 1, -1, 2, -2, ...
+- Data corruption bug fixed in disk based indexes (used for the node
+ location store for instance). This only affected you, if you created
+ and index, closed it, and re-opened it (possibly in a different process)
+ and if there were missing nodes. If you looked up those nodes, you got
+ location (0,0) back instead of an error.
+- Memory corruption bug showing up with GDAL 2.
+
+
## [2.6.1] - 2016-02-22
### Added
@@ -276,7 +320,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.6.1...HEAD
+[unreleased]: https://github.com/osmcode/libosmium/compare/v2.7.0...HEAD
+[2.7.0]: https://github.com/osmcode/libosmium/compare/v2.6.1...v2.7.0
[2.6.1]: https://github.com/osmcode/libosmium/compare/v2.6.0...v2.6.1
[2.6.0]: https://github.com/osmcode/libosmium/compare/v2.5.4...v2.6.0
[2.5.4]: https://github.com/osmcode/libosmium/compare/v2.5.3...v2.5.4
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bc9b12e..5f74fab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,8 +24,8 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Cover
project(libosmium)
set(LIBOSMIUM_VERSION_MAJOR 2)
-set(LIBOSMIUM_VERSION_MINOR 6)
-set(LIBOSMIUM_VERSION_PATCH 1)
+set(LIBOSMIUM_VERSION_MINOR 7)
+set(LIBOSMIUM_VERSION_PATCH 0)
set(LIBOSMIUM_VERSION
"${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}")
diff --git a/include/gdalcpp.hpp b/include/gdalcpp.hpp
index 1502f2f..4f3d480 100644
--- a/include/gdalcpp.hpp
+++ b/include/gdalcpp.hpp
@@ -110,8 +110,12 @@ namespace gdalcpp {
namespace detail {
struct init_wrapper {
+#if GDAL_VERSION_MAJOR >= 2
+ init_wrapper() { GDALAllRegister(); }
+#else
init_wrapper() { OGRRegisterAll(); }
~init_wrapper() { OGRCleanupAll(); }
+#endif
};
struct init_library {
@@ -237,6 +241,8 @@ namespace gdalcpp {
detail::Options m_options;
SRS m_srs;
std::unique_ptr<gdal_dataset_type, gdal_dataset_deleter> m_dataset;
+ uint64_t m_edit_count = 0;
+ uint64_t m_max_edit_count = 0;
public:
@@ -255,6 +261,15 @@ namespace gdalcpp {
}
}
+ ~Dataset() {
+ try {
+ if (m_edit_count > 0) {
+ commit_transaction();
+ }
+ } catch (...) {
+ }
+ }
+
const std::string& driver_name() const {
return m_driver_name;
}
@@ -282,10 +297,14 @@ namespace gdalcpp {
exec(sql.c_str());
}
-
Dataset& start_transaction() {
#if GDAL_VERSION_MAJOR >= 2
m_dataset->StartTransaction();
+#else
+ OGRLayer* layer = m_dataset->GetLayer(0);
+ if (layer) {
+ layer->StartTransaction();
+ }
#endif
return *this;
}
@@ -293,7 +312,38 @@ namespace gdalcpp {
Dataset& commit_transaction() {
#if GDAL_VERSION_MAJOR >= 2
m_dataset->CommitTransaction();
+#else
+ OGRLayer* layer = m_dataset->GetLayer(0);
+ if (layer) {
+ layer->CommitTransaction();
+ }
#endif
+ m_edit_count = 0;
+ return *this;
+ }
+
+ void prepare_edit() {
+ if (m_max_edit_count != 0 && m_edit_count == 0) {
+ start_transaction();
+ }
+ }
+
+ void finalize_edit() {
+ if (m_max_edit_count != 0 && ++m_edit_count > m_max_edit_count) {
+ commit_transaction();
+ }
+ }
+
+ Dataset& enable_auto_transactions(uint64_t edits = 100000) {
+ m_max_edit_count = edits;
+ return *this;
+ }
+
+ Dataset& disable_auto_transactions() {
+ if (m_max_edit_count != 0 && m_edit_count > 0) {
+ commit_transaction();
+ }
+ m_max_edit_count = 0;
return *this;
}
@@ -346,19 +396,32 @@ namespace gdalcpp {
return *this;
}
+ void create_feature(OGRFeature* feature) {
+ dataset().prepare_edit();
+ OGRErr result = m_layer->CreateFeature(feature);
+ if (result != OGRERR_NONE) {
+ throw gdal_error(std::string("creating feature in layer '") + name() + "' failed", result, dataset().driver_name(), dataset().dataset_name());
+ }
+ dataset().finalize_edit();
+ }
+
Layer& start_transaction() {
+#if GDAL_VERSION_MAJOR < 2
OGRErr result = m_layer->StartTransaction();
if (result != OGRERR_NONE) {
throw gdal_error(std::string("starting transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name());
}
+#endif
return *this;
}
Layer& commit_transaction() {
+#if GDAL_VERSION_MAJOR < 2
OGRErr result = m_layer->CommitTransaction();
if (result != OGRERR_NONE) {
throw gdal_error(std::string("committing transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name());
}
+#endif
return *this;
}
@@ -366,36 +429,44 @@ namespace gdalcpp {
class Feature {
+ struct ogr_feature_deleter {
+
+ void operator()(OGRFeature* feature) {
+ OGRFeature::DestroyFeature(feature);
+ }
+
+ }; // struct ogr_feature_deleter
+
Layer& m_layer;
- OGRFeature m_feature;
+ std::unique_ptr<OGRFeature, ogr_feature_deleter> m_feature;
public:
Feature(Layer& layer, std::unique_ptr<OGRGeometry>&& geometry) :
m_layer(layer),
- m_feature(m_layer.get().GetLayerDefn()) {
- OGRErr result = m_feature.SetGeometryDirectly(geometry.release());
+ m_feature(OGRFeature::CreateFeature(m_layer.get().GetLayerDefn())) {
+ if (!m_feature) {
+ throw std::bad_alloc();
+ }
+ OGRErr result = m_feature->SetGeometryDirectly(geometry.release());
if (result != OGRERR_NONE) {
throw gdal_error(std::string("setting feature geometry in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name());
}
}
void add_to_layer() {
- OGRErr result = m_layer.get().CreateFeature(&m_feature);
- if (result != OGRERR_NONE) {
- throw gdal_error(std::string("creating feature in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name());
- }
+ m_layer.create_feature(m_feature.get());
}
template <class T>
Feature& set_field(int n, T&& arg) {
- m_feature.SetField(n, std::forward<T>(arg));
+ m_feature->SetField(n, std::forward<T>(arg));
return *this;
}
template <class T>
Feature& set_field(const char* name, T&& arg) {
- m_feature.SetField(name, std::forward<T>(arg));
+ m_feature->SetField(name, std::forward<T>(arg));
return *this;
}
diff --git a/include/osmium/area/assembler.hpp b/include/osmium/area/assembler.hpp
index a2d1c8e..f964d4b 100644
--- a/include/osmium/area/assembler.hpp
+++ b/include/osmium/area/assembler.hpp
@@ -42,6 +42,9 @@ DEALINGS IN THE SOFTWARE.
#include <set>
#include <string>
#include <map>
+#include <numeric>
+#include <unordered_map>
+#include <unordered_set>
#include <vector>
#include <osmium/builder/osm_object_builder.hpp>
@@ -50,87 +53,232 @@ DEALINGS IN THE SOFTWARE.
#include <osmium/osm/location.hpp>
#include <osmium/osm/relation.hpp>
#include <osmium/tags/filter.hpp>
+#include <osmium/util/compatibility.hpp>
+#include <osmium/util/iterator.hpp>
+#include <osmium/util/timer.hpp>
#include <osmium/area/detail/proto_ring.hpp>
#include <osmium/area/detail/node_ref_segment.hpp>
#include <osmium/area/detail/segment_list.hpp>
#include <osmium/area/problem_reporter.hpp>
+#include <osmium/area/stats.hpp>
namespace osmium {
namespace area {
- using osmium::area::detail::ProtoRing;
-
+ /**
+ * Configuration for osmium::area::Assembler objects. Create this
+ * once, set the options you want and then re-use it every time you
+ * create an Assembler object.
+ */
struct AssemblerConfig {
- osmium::area::ProblemReporter* problem_reporter;
+ /**
+ * Optional pointer to problem reporter.
+ */
+ osmium::area::ProblemReporter* problem_reporter = nullptr;
+
+ /**
+ * Debug level. If this is greater than zero, debug messages will
+ * be printed to stderr. Available levels are 1 to 3. Note that
+ * level 2 and above will generate a lot of messages!
+ */
+ int debug_level = 0;
+
+ /**
+ * The roles of multipolygon members are ignored when assembling
+ * multipolygons, because they are often missing or wrong. If this
+ * is set, the roles are checked after the multipolygons are built
+ * against what the assembly process decided where the inner and
+ * outer rings are. This slows down the processing, so it only
+ * makes sense if you want to get the problem reports.
+ */
+ bool check_roles = false;
+
+ /**
+ * When the assembler can't create an area, usually because its
+ * geometry would be invalid, it will create an "empty" area object
+ * without rings. This allows you to detect where an area was
+ * invalid.
+ *
+ * If this is set to false, invalid areas will simply be discarded.
+ */
+ bool create_empty_areas = true;
+
+ /**
+ * Create areas for (multi)polygons where the tags are on the
+ * relation.
+ *
+ * If this is set to false, those areas will simply be discarded.
+ */
+ bool create_new_style_polygons = true;
+
+ /**
+ * Create areas for (multi)polygons where the tags are on the
+ * outer way(s).
+ *
+ * If this is set to false, those areas will simply be discarded.
+ */
+ bool create_old_style_polygons = true;
+
+ /**
+ * Create areas for polygons created from ways.
+ *
+ * If this is set to false, those areas will simply be discarded.
+ */
+ bool create_way_polygons = true;
+
+ /**
+ * Keep the type tag from multipolygon relations on the area
+ * object. By default this is false, and the type tag will be
+ * removed.
+ */
+ bool keep_type_tag = false;
- // Enables debug output to stderr
- bool debug;
+ AssemblerConfig() noexcept = default;
- explicit AssemblerConfig(osmium::area::ProblemReporter* pr = nullptr, bool d = false) :
+ /**
+ * Constructor
+ * @deprecated Use default constructor and set values afterwards.
+ */
+ explicit AssemblerConfig(osmium::area::ProblemReporter* pr, bool d = false) :
problem_reporter(pr),
- debug(d) {
+ debug_level(d) {
}
/**
* Enable or disable debug output to stderr. This is for Osmium
* developers only.
+ *
+ * @deprecated Set debug_level directly.
*/
void enable_debug_output(bool d = true) {
- debug = d;
+ debug_level = d;
}
}; // struct AssemblerConfig
+ namespace detail {
+
+ using open_ring_its_type = std::list<std::list<detail::ProtoRing>::iterator>;
+
+ struct location_to_ring_map {
+ osmium::Location location;
+ open_ring_its_type::iterator ring_it;
+ bool start;
+
+ location_to_ring_map(const osmium::Location& l, const open_ring_its_type::iterator& r, bool s) noexcept :
+ location(l),
+ ring_it(r),
+ start(s) {
+ }
+
+ explicit location_to_ring_map(const osmium::Location& l) noexcept :
+ location(l),
+ ring_it(),
+ start(false) {
+ }
+
+ const detail::ProtoRing& ring() const noexcept {
+ return **ring_it;
+ }
+
+ }; // 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& a, const location_to_ring_map& b) noexcept {
+ return a.location < b.location;
+ }
+
+ } // namespace detail
+
/**
- * Assembles area objects from multipolygon relations and their
- * members. This is called by the MultipolygonCollector object
- * after all members have been collected.
+ * Assembles area objects from closed ways or multipolygon relations
+ * and their members.
*/
class Assembler {
- const AssemblerConfig m_config;
+ using open_ring_its_type = detail::open_ring_its_type;
+ using location_to_ring_map = detail::location_to_ring_map;
+
+ struct slocation {
+
+ static constexpr const uint32_t invalid_item = 1 << 30;
+
+ uint32_t item : 31;
+ uint32_t reverse : 1;
- // The way segments
+ slocation() noexcept :
+ item(invalid_item),
+ reverse(false) {
+ }
+
+ explicit slocation(uint32_t n, bool r = false) noexcept :
+ item(n),
+ reverse(r) {
+ }
+
+ osmium::Location location(const detail::SegmentList& segment_list) const noexcept {
+ const auto& segment = segment_list[item];
+ return reverse ? segment.second().location() : segment.first().location();
+ }
+
+ const osmium::NodeRef& node_ref(const detail::SegmentList& segment_list) const noexcept {
+ const auto& segment = segment_list[item];
+ return reverse ? segment.second() : segment.first();
+ }
+
+ osmium::Location location(const detail::SegmentList& segment_list, const osmium::Location& default_location) const noexcept {
+ if (item == invalid_item) {
+ return default_location;
+ }
+ return location(segment_list);
+ }
+
+ }; // struct slocation
+
+ // Configuration settings for this Assembler
+ const AssemblerConfig& m_config;
+
+ // List of segments (connection between two nodes)
osmium::area::detail::SegmentList m_segment_list;
- // The rings we are building from the way segments
- std::list<ProtoRing> m_rings;
+ // The rings we are building from the segments
+ std::list<detail::ProtoRing> m_rings;
- std::vector<ProtoRing*> m_outer_rings;
- std::vector<ProtoRing*> m_inner_rings;
+ // All node locations
+ std::vector<slocation> m_locations;
- int m_inner_outer_mismatches { 0 };
+ // All locations where more than two segments start/end
+ std::vector<Location> m_split_locations;
- bool debug() const {
- return m_config.debug;
+ // Statistics
+ area_stats m_stats;
+
+ bool debug() const noexcept {
+ return m_config.debug_level > 1;
}
- /**
- * Checks whether the given NodeRefs have the same location.
- * Uses the actual location for the test, not the id. If both
- * have the same location, but not the same id, a problem
- * point will be added to the list of problem points.
- */
- bool has_same_location(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) {
- if (nr1.location() != nr2.location()) {
+ bool report_ways() const noexcept {
+ if (!m_config.problem_reporter) {
return false;
}
- if (nr1.ref() != nr2.ref()) {
- if (m_config.problem_reporter) {
- m_config.problem_reporter->report_duplicate_node(nr1.ref(), nr2.ref(), nr1.location());
- }
- }
- return true;
+ return m_stats.duplicate_nodes ||
+ m_stats.duplicate_segments ||
+ m_stats.intersections ||
+ m_stats.open_rings ||
+ m_stats.short_ways ||
+ m_stats.touching_rings ||
+ m_stats.ways_in_multiple_rings ||
+ m_stats.wrong_role;
}
void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Way& way) const {
- osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
- for (const osmium::Tag& tag : way.tags()) {
- tl_builder.add_tag(tag.key(), tag.value());
- }
+ builder.add_item(&way.tags());
}
void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set<const osmium::Way*>& ways) const {
@@ -144,13 +292,13 @@ namespace osmium {
}
}
- size_t num_ways = ways.size();
+ const size_t num_ways = ways.size();
for (const auto& t_c : counter) {
if (debug()) {
std::cerr << " tag " << t_c.first << " is used " << t_c.second << " times in " << num_ways << " ways\n";
}
if (t_c.second == num_ways) {
- size_t len = std::strlen(t_c.first.c_str());
+ const size_t len = std::strlen(t_c.first.c_str());
tl_builder.add_tag(t_c.first.c_str(), t_c.first.c_str() + len + 1);
}
}
@@ -169,13 +317,22 @@ namespace osmium {
}; // struct MPFilter
- static MPFilter& filter() {
- static MPFilter filter;
+ static const MPFilter& filter() noexcept {
+ static const MPFilter filter;
return filter;
}
- void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) const {
- const auto count = std::count_if(relation.tags().begin(), relation.tags().end(), filter());
+ static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) {
+ osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
+ for (const osmium::Tag& tag : tags) {
+ if (std::strcmp(tag.key(), "type")) {
+ tl_builder.add_tag(tag.key(), tag.value());
+ }
+ }
+ }
+
+ void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) {
+ const auto count = std::count_if(relation.tags().cbegin(), relation.tags().cend(), filter());
if (debug()) {
std::cerr << " found " << count << " tags on relation (without ignored ones)\n";
@@ -186,29 +343,27 @@ namespace osmium {
std::cerr << " use tags from relation\n";
}
- // write out all tags except type=*
- osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
- for (const osmium::Tag& tag : relation.tags()) {
- if (strcmp(tag.key(), "type")) {
- tl_builder.add_tag(tag.key(), tag.value());
- }
+ if (m_config.keep_type_tag) {
+ builder.add_item(&relation.tags());
+ } else {
+ copy_tags_without_type(builder, relation.tags());
}
} else {
+ ++m_stats.no_tags_on_relation;
if (debug()) {
std::cerr << " use tags from outer ways\n";
}
std::set<const osmium::Way*> ways;
- for (const auto& ring : m_outer_rings) {
- ring->get_ways(ways);
+ for (const auto& ring : m_rings) {
+ if (ring.is_outer()) {
+ ring.get_ways(ways);
+ }
}
if (ways.size() == 1) {
if (debug()) {
std::cerr << " only one outer way\n";
}
- osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
- for (const osmium::Tag& tag : (*ways.begin())->tags()) {
- tl_builder.add_tag(tag.key(), tag.value());
- }
+ builder.add_item(&(*ways.cbegin())->tags());
} else {
if (debug()) {
std::cerr << " multiple outer ways, get common tags\n";
@@ -219,494 +374,1065 @@ namespace osmium {
}
}
+ template <typename TBuilder>
+ static void build_ring_from_proto_ring(osmium::builder::AreaBuilder& builder, const detail::ProtoRing& ring) {
+ TBuilder ring_builder(builder.buffer(), &builder);
+ ring_builder.add_node_ref(ring.get_node_ref_start());
+ for (const auto& segment : ring.segments()) {
+ ring_builder.add_node_ref(segment->stop());
+ }
+ }
+
/**
- * Go through all the rings and find rings that are not closed.
- * Problems are reported through the problem reporter.
- *
- * @returns true if any rings were not closed, false otherwise
+ * Append each outer ring together with its inner rings to the
+ * area in the buffer.
*/
- bool check_for_open_rings() {
- bool open_rings = false;
-
- for (const auto& ring : m_rings) {
- if (!ring.closed()) {
- open_rings = true;
- if (m_config.problem_reporter) {
- m_config.problem_reporter->report_ring_not_closed(ring.get_node_ref_front().location(), ring.get_node_ref_back().location());
+ void add_rings_to_area(osmium::builder::AreaBuilder& builder) const {
+ for (const detail::ProtoRing& ring : m_rings) {
+ if (ring.is_outer()) {
+ build_ring_from_proto_ring<osmium::builder::OuterRingBuilder>(builder, ring);
+ for (const detail::ProtoRing* inner : ring.inner_rings()) {
+ build_ring_from_proto_ring<osmium::builder::InnerRingBuilder>(builder, *inner);
}
}
}
-
- return open_rings;
}
- /**
- * Check whether there are any rings that can be combined with the
- * given ring to one larger ring by appending the other ring to
- * the end of this ring.
- * If the rings can be combined they are and the function returns
- * true.
- */
- bool possibly_combine_rings_back(ProtoRing& ring) {
- const osmium::NodeRef& nr = ring.get_node_ref_back();
-
+ void check_inner_outer_roles() {
if (debug()) {
- std::cerr << " possibly_combine_rings_back()\n";
+ std::cerr << " Checking inner/outer roles\n";
}
- for (auto it = m_rings.begin(); it != m_rings.end(); ++it) {
- if (&*it != &ring && !it->closed()) {
- if (has_same_location(nr, it->get_node_ref_front())) {
+
+ std::unordered_map<const osmium::Way*, const detail::ProtoRing*> way_rings;
+ std::unordered_set<const osmium::Way*> ways_in_multiple_rings;
+
+ for (const detail::ProtoRing& ring : m_rings) {
+ for (const auto& segment : ring.segments()) {
+ assert(segment->way());
+
+ if (!segment->role_empty() && (ring.is_outer() ? !segment->role_outer() : !segment->role_inner())) {
+ ++m_stats.wrong_role;
if (debug()) {
- std::cerr << " ring.last=it->first\n";
+ std::cerr << " Segment " << *segment << " from way " << segment->way()->id() << " has role '" << segment->role_name()
+ << "', but should have role '" << (ring.is_outer() ? "outer" : "inner") << "'\n";
}
- ring.merge_ring(*it, debug());
- m_rings.erase(it);
- return true;
- }
- if (has_same_location(nr, it->get_node_ref_back())) {
- if (debug()) {
- std::cerr << " ring.last=it->last\n";
+ if (m_config.problem_reporter) {
+ if (ring.is_outer()) {
+ m_config.problem_reporter->report_role_should_be_outer(segment->way()->id(), segment->first().location(), segment->second().location());
+ } else {
+ m_config.problem_reporter->report_role_should_be_inner(segment->way()->id(), segment->first().location(), segment->second().location());
+ }
}
- ring.merge_ring_reverse(*it, debug());
- m_rings.erase(it);
- return true;
}
+
+ auto& r = way_rings[segment->way()];
+ if (!r) {
+ r = ˚
+ } else if (r != &ring) {
+ ways_in_multiple_rings.insert(segment->way());
+ }
+
}
}
- return false;
+
+ for (const osmium::Way* way : ways_in_multiple_rings) {
+ ++m_stats.ways_in_multiple_rings;
+ if (debug()) {
+ std::cerr << " Way " << way->id() << " is in multiple rings\n";
+ }
+ if (m_config.problem_reporter) {
+ m_config.problem_reporter->report_way_in_multiple_rings(*way);
+ }
+ }
+
}
- /**
- * Check whether there are any rings that can be combined with the
- * given ring to one larger ring by prepending the other ring to
- * the start of this ring.
- * If the rings can be combined they are and the function returns
- * true.
- */
- bool possibly_combine_rings_front(ProtoRing& ring) {
- const osmium::NodeRef& nr = ring.get_node_ref_front();
+ 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);
+ });
+
+ assert(it != m_locations.end());
+ if (m_segment_list[it->item].is_done()) {
+ ++it;
+ }
+ assert(it != m_locations.end());
+
+ assert(!m_segment_list[it->item].is_done());
+ return &m_segment_list[it->item];
+ }
+
+ class rings_stack_element {
+
+ int32_t m_y;
+ detail::ProtoRing* m_ring_ptr;
+
+ public:
+
+ rings_stack_element(int32_t y, detail::ProtoRing* ring_ptr) :
+ m_y(y),
+ m_ring_ptr(ring_ptr) {
+ }
+
+ int32_t y() const noexcept {
+ return m_y;
+ }
+
+ const detail::ProtoRing& ring() const noexcept {
+ return *m_ring_ptr;
+ }
+
+ detail::ProtoRing* ring_ptr() noexcept {
+ return m_ring_ptr;
+ }
+
+ bool operator==(const rings_stack_element& rhs) const noexcept {
+ return m_ring_ptr == rhs.m_ring_ptr;
+ }
+
+ bool operator<(const rings_stack_element& rhs) const noexcept {
+ return m_y < rhs.m_y;
+ }
+
+ }; // class ring_stack_element
+
+ using rings_stack = std::vector<rings_stack_element>;
+
+ void remove_duplicates(rings_stack& outer_rings) {
+ while (true) {
+ const auto it = std::adjacent_find(outer_rings.begin(), outer_rings.end());
+ if (it == outer_rings.end()) {
+ return;
+ }
+ outer_rings.erase(it, std::next(it, 2));
+ }
+ }
+ detail::ProtoRing* find_enclosing_ring(detail::NodeRefSegment* segment) {
if (debug()) {
- std::cerr << " possibly_combine_rings_front()\n";
+ std::cerr << " Looking for ring enclosing " << *segment << "\n";
}
- for (auto it = m_rings.begin(); it != m_rings.end(); ++it) {
- if (&*it != &ring && !it->closed()) {
- if (has_same_location(nr, it->get_node_ref_back())) {
+
+ const auto location = segment->first().location();
+ const auto end_location = segment->second().location();
+
+ while (segment->first().location() == location) {
+ if (segment == &m_segment_list.back()) {
+ break;
+ }
+ ++segment;
+ }
+
+ int nesting = 0;
+
+ rings_stack outer_rings;
+ while (segment >= &m_segment_list.front()) {
+ if (!segment->is_direction_done()) {
+ --segment;
+ continue;
+ }
+ if (debug()) {
+ std::cerr << " Checking against " << *segment << "\n";
+ }
+ const osmium::Location& a = segment->first().location();
+ const osmium::Location& b = segment->second().location();
+
+ if (segment->first().location() == location) {
+ const int64_t ax = a.x();
+ const int64_t bx = b.x();
+ const int64_t lx = end_location.x();
+ const int64_t ay = a.y();
+ const int64_t by = b.y();
+ const int64_t ly = end_location.y();
+ const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax);
+ if (debug()) {
+ std::cerr << " Segment XXXX z=" << z << "\n";
+ }
+ if (z > 0) {
+ nesting += segment->is_reverse() ? -1 : 1;
if (debug()) {
- std::cerr << " ring.first=it->last\n";
+ std::cerr << " Segment is below (nesting=" << nesting << ")\n";
}
- ring.swap_segments(*it);
- ring.merge_ring(*it, debug());
- m_rings.erase(it);
- return true;
+ if (segment->ring()->is_outer()) {
+ if (debug()) {
+ std::cerr << " Segment belongs to outer ring\n";
+ }
+ outer_rings.emplace_back(a.y(), segment->ring());
+ }
+ }
+ } else if (a.x() <= location.x() && location.x() < b.x()) {
+ if (debug()) {
+ std::cerr << " Is in x range\n";
}
- if (has_same_location(nr, it->get_node_ref_front())) {
+
+ const int64_t ax = a.x();
+ const int64_t bx = b.x();
+ const int64_t lx = location.x();
+ const int64_t ay = a.y();
+ const int64_t by = b.y();
+ const int64_t ly = location.y();
+ const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax);
+
+// std::cerr << "z=" << z << "\n";
+
+ if (z >= 0) {
+ nesting += segment->is_reverse() ? -1 : 1;
if (debug()) {
- std::cerr << " ring.first=it->first\n";
+ std::cerr << " Segment is below (nesting=" << nesting << ")\n";
+ }
+ if (segment->ring()->is_outer()) {
+ if (debug()) {
+ std::cerr << " Segment belongs to outer ring\n";
+ }
+ int32_t y = int32_t(ay + (by - ay) * (lx - ax) / (bx - ax));
+ outer_rings.emplace_back(y, segment->ring());
}
- ring.reverse();
- ring.merge_ring(*it, debug());
- m_rings.erase(it);
- return true;
}
}
+ --segment;
}
- return false;
- }
- void split_off_subring(osmium::area::detail::ProtoRing& ring, osmium::area::detail::ProtoRing::segments_type::iterator it, osmium::area::detail::ProtoRing::segments_type::iterator it_begin, osmium::area::detail::ProtoRing::segments_type::iterator it_end) {
- if (debug()) {
- std::cerr << " subring found at: " << *it << "\n";
+ if (nesting % 2 == 0) {
+ if (debug()) {
+ std::cerr << " Decided that this is an outer ring\n";
+ }
+ return nullptr;
+ } else {
+ if (debug()) {
+ std::cerr << " Decided that this is an inner ring\n";
+ }
+ assert(!outer_rings.empty());
+
+ std::sort(outer_rings.rbegin(), outer_rings.rend());
+ if (debug()) {
+ for (const auto& o : outer_rings) {
+ std::cerr << " y=" << o.y() << " " << o.ring() << "\n";
+ }
+ }
+
+ remove_duplicates(outer_rings);
+ if (debug()) {
+ std::cerr << " after remove duplicates:\n";
+ for (const auto& o : outer_rings) {
+ std::cerr << " y=" << o.y() << " " << o.ring() << "\n";
+ }
+ }
+
+ assert(!outer_rings.empty());
+ return outer_rings.front().ring_ptr();
}
- ProtoRing new_ring(it_begin, it_end);
- ring.remove_segments(it_begin, it_end);
+ }
+
+ bool is_split_location(const osmium::Location& location) const noexcept {
+ return std::find(m_split_locations.cbegin(), m_split_locations.cend(), location) != m_split_locations.cend();
+ }
+
+ uint32_t add_new_ring(slocation& node) {
+ detail::NodeRefSegment* segment = &m_segment_list[node.item];
+ assert(!segment->is_done());
+
if (debug()) {
- std::cerr << " split into two rings:\n";
- std::cerr << " " << new_ring << "\n";
- std::cerr << " " << ring << "\n";
+ std::cerr << " Starting new ring at location " << node.location(m_segment_list) << " with segment " << *segment << "\n";
}
- m_rings.push_back(std::move(new_ring));
- }
- bool has_closed_subring_back(ProtoRing& ring, const NodeRef& nr) {
- if (ring.segments().size() < 3) {
- return false;
+ if (node.reverse) {
+ segment->reverse();
}
- if (debug()) {
- std::cerr << " has_closed_subring_back()\n";
+
+ detail::ProtoRing* outer_ring = nullptr;
+
+ if (segment != &m_segment_list.front()) {
+ outer_ring = find_enclosing_ring(segment);
}
- const auto end = ring.segments().end();
- for (auto it = ring.segments().begin() + 1; it != end - 1; ++it) {
- if (has_same_location(nr, it->first())) {
- split_off_subring(ring, it, it, end);
- return true;
+ segment->mark_direction_done();
+
+ m_rings.emplace_back(segment);
+ detail::ProtoRing* ring = &m_rings.back();
+ if (outer_ring) {
+ if (debug()) {
+ std::cerr << " This is an inner ring. Outer ring is " << *outer_ring << "\n";
}
+ outer_ring->add_inner_ring(ring);
+ ring->set_outer_ring(outer_ring);
+ } else if (debug()) {
+ std::cerr << " This is an outer ring\n";
}
- return false;
- }
- bool has_closed_subring_front(ProtoRing& ring, const NodeRef& nr) {
- if (ring.segments().size() < 3) {
- return false;
+ const osmium::Location& first_location = node.location(m_segment_list);
+ osmium::Location last_location = segment->stop().location();
+
+ uint32_t nodes = 1;
+ while (first_location != last_location) {
+ ++nodes;
+ detail::NodeRefSegment* next_segment = get_next_segment(last_location);
+ next_segment->mark_direction_done();
+ if (next_segment->start().location() != last_location) {
+ next_segment->reverse();
+ }
+ ring->add_segment_back(next_segment);
+ if (debug()) {
+ std::cerr << " Next segment is " << *next_segment << "\n";
+ }
+ last_location = next_segment->stop().location();
}
+
+ ring->fix_direction();
+
if (debug()) {
- std::cerr << " has_closed_subring_front()\n";
+ std::cerr << " Completed ring: " << *ring << "\n";
}
- const auto end = ring.segments().end();
- for (auto it = ring.segments().begin() + 1; it != end - 1; ++it) {
- if (has_same_location(nr, it->second())) {
- split_off_subring(ring, it, ring.segments().begin(), it+1);
- return true;
- }
- }
- return false;
+
+ return nodes;
}
- bool check_for_closed_subring(ProtoRing& ring) {
+ uint32_t add_new_ring_complex(slocation& node) {
+ detail::NodeRefSegment* segment = &m_segment_list[node.item];
+ assert(!segment->is_done());
+
if (debug()) {
- std::cerr << " check_for_closed_subring()\n";
+ std::cerr << " Starting new ring at location " << node.location(m_segment_list) << " with segment " << *segment << "\n";
}
- osmium::area::detail::ProtoRing::segments_type segments(ring.segments().size());
- std::copy(ring.segments().cbegin(), ring.segments().cend(), segments.begin());
- std::sort(segments.begin(), segments.end());
- const auto it = std::adjacent_find(segments.begin(), segments.end(), [this](const osmium::area::detail::NodeRefSegment& s1, const osmium::area::detail::NodeRefSegment& s2) {
- return has_same_location(s1.first(), s2.first());
- });
- if (it == segments.end()) {
- return false;
+ if (node.reverse) {
+ segment->reverse();
+ }
+
+ m_rings.emplace_back(segment);
+ detail::ProtoRing* ring = &m_rings.back();
+
+ const osmium::Location& first_location = node.location(m_segment_list);
+ osmium::Location last_location = segment->stop().location();
+
+ uint32_t nodes = 1;
+ while (first_location != last_location && !is_split_location(last_location)) {
+ ++nodes;
+ detail::NodeRefSegment* next_segment = get_next_segment(last_location);
+ if (next_segment->start().location() != last_location) {
+ next_segment->reverse();
+ }
+ ring->add_segment_back(next_segment);
+ if (debug()) {
+ std::cerr << " Next segment is " << *next_segment << "\n";
+ }
+ last_location = next_segment->stop().location();
}
- const auto r1 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it, it+1);
- assert(r1 != ring.segments().end());
- const auto r2 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it+1, it+2);
- assert(r2 != ring.segments().end());
if (debug()) {
- std::cerr << " found subring in ring " << ring << " at " << it->first() << "\n";
+ if (first_location == last_location) {
+ std::cerr << " Completed ring: " << *ring << "\n";
+ } else {
+ std::cerr << " Completed partial ring: " << *ring << "\n";
+ }
}
- const auto m = std::minmax(r1, r2);
+ return nodes;
+ }
- ProtoRing new_ring(m.first, m.second);
- ring.remove_segments(m.first, m.second);
+ void create_locations_list() {
+ m_locations.reserve(m_segment_list.size() * 2);
- if (debug()) {
- std::cerr << " split ring1=" << new_ring << "\n";
- std::cerr << " split ring2=" << ring << "\n";
+ for (uint32_t n = 0; n < m_segment_list.size(); ++n) {
+ m_locations.emplace_back(n, false);
+ m_locations.emplace_back(n, true);
}
- m_rings.emplace_back(new_ring);
+ 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);
+ });
+ }
- return true;
+ void find_inner_outer_complex(detail::ProtoRing* ring) {
+ detail::ProtoRing* outer_ring = find_enclosing_ring(ring->min_segment());
+ if (outer_ring) {
+ outer_ring->add_inner_ring(ring);
+ ring->set_outer_ring(outer_ring);
+ }
+ ring->fix_direction();
+ ring->mark_direction_done();
}
- void combine_rings_front(const osmium::area::detail::NodeRefSegment& segment, ProtoRing& ring) {
+ void find_inner_outer_complex() {
if (debug()) {
- std::cerr << " => match at front of ring\n";
+ std::cerr << " Finding inner/outer rings\n";
+ }
+ std::vector<detail::ProtoRing*> rings;
+ rings.reserve(m_rings.size());
+ for (auto& ring : m_rings) {
+ if (ring.closed()) {
+ rings.push_back(&ring);
+ }
}
- ring.add_segment_front(segment);
- has_closed_subring_front(ring, segment.first());
- if (possibly_combine_rings_front(ring)) {
- check_for_closed_subring(ring);
+
+ if (rings.empty()) {
+ return;
}
- }
- void combine_rings_back(const osmium::area::detail::NodeRefSegment& segment, ProtoRing& ring) {
+ std::sort(rings.begin(), rings.end(), [](detail::ProtoRing* a, detail::ProtoRing* b) {
+ return a->min_segment() < b->min_segment();
+ });
+
+ rings.front()->fix_direction();
+ rings.front()->mark_direction_done();
if (debug()) {
- std::cerr << " => match at back of ring\n";
+ std::cerr << " First ring is outer: " << *rings.front() << "\n";
}
- ring.add_segment_back(segment);
- has_closed_subring_back(ring, segment.second());
- if (possibly_combine_rings_back(ring)) {
- check_for_closed_subring(ring);
+ for (auto it = std::next(rings.begin()); it != rings.end(); ++it) {
+ if (debug()) {
+ std::cerr << " Checking (at min segment " << *((*it)->min_segment()) << ") ring " << **it << "\n";
+ }
+ find_inner_outer_complex(*it);
+ if (debug()) {
+ std::cerr << " Ring is " << ((*it)->is_outer() ? "OUTER: " : "INNER: ") << **it << "\n";
+ }
}
}
/**
- * Append each outer ring together with its inner rings to the
- * area in the buffer.
+ * Finds all locations where more than two segments meet. If there
+ * are any open rings found along the way, they are reported
+ * and the function returns false.
*/
- void add_rings_to_area(osmium::builder::AreaBuilder& builder) const {
- for (const ProtoRing* ring : m_outer_rings) {
- if (debug()) {
- std::cerr << " ring " << *ring << " is outer\n";
- }
- {
- osmium::builder::OuterRingBuilder ring_builder(builder.buffer(), &builder);
- ring_builder.add_node_ref(ring->get_node_ref_front());
- for (const auto& segment : ring->segments()) {
- ring_builder.add_node_ref(segment.second());
+ bool find_split_locations() {
+ osmium::Location previous_location;
+ for (auto it = m_locations.cbegin(); it != m_locations.cend(); ++it) {
+ const osmium::NodeRef& nr = it->node_ref(m_segment_list);
+ const osmium::Location& loc = nr.location();
+ if (std::next(it) == m_locations.cend() || loc != std::next(it)->location(m_segment_list)) {
+ if (debug()) {
+ std::cerr << " Found open ring at " << nr << "\n";
+ }
+ if (m_config.problem_reporter) {
+ const auto& segment = m_segment_list[it->item];
+ m_config.problem_reporter->report_ring_not_closed(nr, segment.way());
+ }
+ ++m_stats.open_rings;
+ } else {
+ if (loc == previous_location && (m_split_locations.empty() || m_split_locations.back() != previous_location )) {
+ m_split_locations.push_back(previous_location);
+ }
+ ++it;
+ if (it == m_locations.end()) {
+ break;
}
}
- for (ProtoRing* inner : ring->inner_rings()) {
- osmium::builder::InnerRingBuilder ring_builder(builder.buffer(), &builder);
- ring_builder.add_node_ref(inner->get_node_ref_front());
- for (const auto& segment : inner->segments()) {
- ring_builder.add_node_ref(segment.second());
+ previous_location = loc;
+ }
+ return m_stats.open_rings == 0;
+ }
+
+ void create_rings_simple_case() {
+ uint32_t count_remaining = m_segment_list.size();
+ for (slocation& sl : m_locations) {
+ const detail::NodeRefSegment& segment = m_segment_list[sl.item];
+ if (!segment.is_done()) {
+ count_remaining -= add_new_ring(sl);
+ if (count_remaining == 0) {
+ return;
}
}
}
}
- bool add_to_existing_ring(osmium::area::detail::NodeRefSegment segment) {
- int n = 0;
- for (auto& ring : m_rings) {
+ std::vector<location_to_ring_map> create_location_to_ring_map(open_ring_its_type& open_ring_its) {
+ std::vector<location_to_ring_map> xrings;
+ xrings.reserve(open_ring_its.size() * 2);
+
+ for (auto it = open_ring_its.begin(); it != open_ring_its.end(); ++it) {
if (debug()) {
- std::cerr << " check against ring " << n << " " << ring;
+ std::cerr << " Ring: " << **it << "\n";
}
- if (ring.closed()) {
- if (debug()) {
- std::cerr << " => ring CLOSED\n";
- }
- } else {
- if (has_same_location(ring.get_node_ref_back(), segment.first())) {
- combine_rings_back(segment, ring);
- return true;
- }
- if (has_same_location(ring.get_node_ref_back(), segment.second())) {
- segment.swap_locations();
- combine_rings_back(segment, ring);
- return true;
- }
- if (has_same_location(ring.get_node_ref_front(), segment.first())) {
- segment.swap_locations();
- combine_rings_front(segment, ring);
- return true;
- }
- if (has_same_location(ring.get_node_ref_front(), segment.second())) {
- combine_rings_front(segment, ring);
- return true;
- }
+ xrings.emplace_back((*it)->get_node_ref_start().location(), it, true);
+ xrings.emplace_back((*it)->get_node_ref_stop().location(), it, false);
+ }
+
+ std::sort(xrings.begin(), xrings.end());
+
+ return xrings;
+ }
+
+ void merge_two_rings(open_ring_its_type& open_ring_its, const location_to_ring_map& m1, const location_to_ring_map& m2) {
+ auto& r1 = *m1.ring_it;
+ auto& r2 = *m2.ring_it;
+
+ if (r1->get_node_ref_stop().location() == r2->get_node_ref_start().location()) {
+ r1->join_forward(*r2);
+ } else if (r1->get_node_ref_stop().location() == r2->get_node_ref_stop().location()) {
+ r1->join_backward(*r2);
+ } else if (r1->get_node_ref_start().location() == r2->get_node_ref_start().location()) {
+ r1->reverse();
+ r1->join_forward(*r2);
+ } else if (r1->get_node_ref_start().location() == r2->get_node_ref_stop().location()) {
+ r1->reverse();
+ r1->join_backward(*r2);
+ } else {
+ assert(false);
+ }
+
+ m_rings.erase(r2);
+ open_ring_its.remove(r2);
+
+ if (r1->closed()) {
+ open_ring_its.remove(r1);
+ }
+ }
+
+ bool try_to_merge(open_ring_its_type& open_ring_its) {
+ if (open_ring_its.empty()) {
+ return false;
+ }
+
+ if (debug()) {
+ std::cerr << " Trying to merge " << open_ring_its.size() << " open rings\n";
+ }
+
+ std::vector<location_to_ring_map> xrings = create_location_to_ring_map(open_ring_its);
+
+ auto it = xrings.cbegin();
+ while (it != xrings.cend()) {
+ it = std::adjacent_find(it, xrings.cend());
+ if (it == xrings.cend()) {
+ return false;
+ }
+ auto after = std::next(it, 2);
+ if (after == xrings.cend() || after->location != it->location) {
if (debug()) {
- std::cerr << " => no match\n";
+ std::cerr << " Merging two rings\n";
}
+ merge_two_rings(open_ring_its, *it, *std::next(it));
+ return true;
+ }
+ while (it != xrings.cend() && it->location == after->location) {
+ ++it;
}
-
- ++n;
}
+
return false;
}
- void check_inner_outer(ProtoRing& ring) {
- const osmium::NodeRef& min_node = ring.min_node();
+ bool there_are_open_rings() const noexcept {
+ return std::any_of(m_rings.cbegin(), m_rings.cend(), [](const detail::ProtoRing& ring){
+ return !ring.closed();
+ });
+ }
+
+ struct candidate {
+ int64_t sum;
+ std::vector<std::pair<location_to_ring_map, bool>> rings;
+ osmium::Location start_location;
+ osmium::Location stop_location;
+
+ explicit candidate(location_to_ring_map& ring, bool reverse) :
+ sum(ring.ring().sum()),
+ rings(),
+ start_location(ring.ring().get_node_ref_start().location()),
+ stop_location(ring.ring().get_node_ref_stop().location()) {
+ rings.emplace_back(ring, reverse);
+ }
+
+ bool closed() const noexcept {
+ return start_location == stop_location;
+ }
+
+ };
+
+ void find_candidates(std::vector<candidate>& candidates, std::unordered_set<osmium::Location>& loc_done, const std::vector<location_to_ring_map>& xrings, candidate& cand) {
if (debug()) {
- std::cerr << " check_inner_outer min_node=" << min_node << "\n";
+ std::cerr << " find_candidates sum=" << cand.sum << " start=" << cand.start_location << " stop=" << cand.stop_location << "\n";
+ for (const auto& ring : cand.rings) {
+ std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n";
+ }
}
- int count = 0;
- int above = 0;
+ const auto connections = make_range(std::equal_range(xrings.cbegin(),
+ xrings.cend(),
+ location_to_ring_map{cand.stop_location}));
- for (auto it = m_segment_list.begin(); it != m_segment_list.end() && it->first().location().x() <= min_node.location().x(); ++it) {
- if (!ring.contains(*it)) {
+ assert(connections.begin() != connections.end());
+
+ assert(!cand.rings.empty());
+ const detail::ProtoRing* ring_leading_here = &cand.rings.back().first.ring();
+ for (const location_to_ring_map& m : connections) {
+ const detail::ProtoRing& ring = m.ring();
+
+ if (&ring != ring_leading_here) {
if (debug()) {
- std::cerr << " segments for count: " << *it;
+ std::cerr << " next possible connection: " << ring << (m.start ? "" : " reverse") << "\n";
}
- if (it->to_left_of(min_node.location())) {
- ++count;
+
+ candidate c = cand;
+ if (m.start) {
+ c.rings.emplace_back(m, false);
+ c.stop_location = ring.get_node_ref_stop().location();
+ c.sum += ring.sum();
+ } else {
+ c.rings.emplace_back(m, true);
+ c.stop_location = ring.get_node_ref_start().location();
+ c.sum -= ring.sum();
+ }
+ if (c.closed()) {
if (debug()) {
- std::cerr << " counted\n";
+ std::cerr << " found candidate\n";
}
- } else {
+ candidates.push_back(c);
+ } else if (loc_done.count(c.stop_location) == 0) {
if (debug()) {
- std::cerr << " not counted\n";
+ std::cerr << " recurse...\n";
}
- }
- if (it->first().location() == min_node.location()) {
- if (it->second().location().y() > min_node.location().y()) {
- ++above;
+ loc_done.insert(c.stop_location);
+ find_candidates(candidates, loc_done, xrings, c);
+ if (debug()) {
+ std::cerr << " ...back\n";
}
+ } else if (debug()) {
+ std::cerr << " loop found\n";
}
- if (it->second().location() == min_node.location()) {
- if (it->first().location().y() > min_node.location().y()) {
- ++above;
+ }
+ }
+ }
+
+ /**
+ * If there are multiple open rings and mltiple ways to join them,
+ * this function is called. It will take the first open ring and
+ * try recursively all ways of closing it. Of all the candidates
+ * the one with the smallest area is chosen and closed. If it
+ * can't close this ring, an error is reported and the function
+ * returns false.
+ */
+ bool join_connected_rings(open_ring_its_type& open_ring_its) {
+ assert(!open_ring_its.empty());
+
+ if (debug()) {
+ std::cerr << " Trying to merge " << open_ring_its.size() << " open rings\n";
+ }
+
+ std::vector<location_to_ring_map> xrings = create_location_to_ring_map(open_ring_its);
+
+ 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();
+ });
+
+ find_inner_outer_complex();
+ detail::ProtoRing* outer_ring = find_enclosing_ring(ring_min->ring().min_segment());
+ bool ring_min_is_outer = !outer_ring;
+ if (debug()) {
+ std::cerr << " Open ring is " << (ring_min_is_outer ? "outer" : "inner") << " ring\n";
+ }
+ for (auto& ring : m_rings) {
+ ring.reset();
+ }
+
+ candidate cand{*ring_min, false};
+
+ // Locations we have visited while finding candidates, used
+ // to detect loops.
+ std::unordered_set<osmium::Location> loc_done;
+
+ loc_done.insert(cand.stop_location);
+
+ std::vector<candidate> candidates;
+ find_candidates(candidates, loc_done, xrings, cand);
+
+ if (candidates.empty()) {
+ if (debug()) {
+ std::cerr << " Found no candidates\n";
+ }
+ if (!open_ring_its.empty()) {
+ ++m_stats.open_rings;
+ if (m_config.problem_reporter) {
+ for (auto& it : open_ring_its) {
+ m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_start());
+ m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_stop());
}
}
}
+ return false;
}
if (debug()) {
- std::cerr << " count=" << count << " above=" << above << "\n";
+ std::cerr << " Found candidates:\n";
+ for (const auto& cand : candidates) {
+ std::cerr << " sum=" << cand.sum << "\n";
+ for (const auto& ring : cand.rings) {
+ std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n";
+ }
+ }
}
- count += above % 2;
+ // Find the candidate with the smallest/largest area
+ 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::max_element(candidates.cbegin(), candidates.cend(), [](const candidate& a, const candidate& b) {
+ return std::abs(a.sum) < std::abs(b.sum);
+ });
- if (count % 2) {
- ring.set_inner();
+ if (debug()) {
+ std::cerr << " Decided on: sum=" << chosen_cand->sum << "\n";
+ for (const auto& ring : chosen_cand->rings) {
+ std::cerr << " " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n";
+ }
+ }
+
+ // Join all (open) rings in the candidate to get one closed ring.
+ assert(chosen_cand->rings.size() > 1);
+ const auto& first_ring = chosen_cand->rings.front().first;
+ for (auto it = chosen_cand->rings.begin() + 1; it != chosen_cand->rings.end(); ++it) {
+ merge_two_rings(open_ring_its, first_ring, it->first);
}
- }
- void check_inner_outer_roles() {
if (debug()) {
- std::cerr << " check_inner_outer_roles\n";
+ std::cerr << " Merged to " << first_ring.ring() << "\n";
}
- for (const auto ringptr : m_outer_rings) {
- for (const auto& segment : ringptr->segments()) {
- if (!segment.role_outer()) {
- ++m_inner_outer_mismatches;
- if (debug()) {
- std::cerr << " segment " << segment << " from way " << segment.way()->id() << " should have role 'outer'\n";
+ return true;
+ }
+
+ bool create_rings_complex_case() {
+ // First create all the (partial) rings starting at the split locations
+ uint32_t count_remaining = m_segment_list.size();
+ for (const osmium::Location& location : m_split_locations) {
+ 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);
+ }));
+ for (auto& loc : locs) {
+ if (!m_segment_list[loc.item].is_done()) {
+ count_remaining -= add_new_ring_complex(loc);
+ if (count_remaining == 0) {
+ break;
}
- if (m_config.problem_reporter) {
- m_config.problem_reporter->report_role_should_be_outer(segment.way()->id(), segment.first().location(), segment.second().location());
+ }
+ }
+ }
+
+ // Now find all the rest of the rings (ie not starting at split locations)
+ if (count_remaining > 0) {
+ for (slocation& sl : m_locations) {
+ const detail::NodeRefSegment& segment = m_segment_list[sl.item];
+ if (!segment.is_done()) {
+ count_remaining -= add_new_ring_complex(sl);
+ if (count_remaining == 0) {
+ break;
}
}
}
}
- for (const auto ringptr : m_inner_rings) {
- for (const auto& segment : ringptr->segments()) {
- if (!segment.role_inner()) {
- ++m_inner_outer_mismatches;
+
+ // Now all segments are in exactly one (partial) ring.
+
+ // If there are open rings, try to join them to create closed
+ // rings.
+ if (there_are_open_rings()) {
+ ++m_stats.area_really_complex_case;
+
+ open_ring_its_type open_ring_its;
+ for (auto it = m_rings.begin(); it != m_rings.end(); ++it) {
+ if (!it->closed()) {
+ open_ring_its.push_back(it);
+ }
+ }
+
+ while (!open_ring_its.empty()) {
+ if (debug()) {
+ std::cerr << " There are " << open_ring_its.size() << " open rings\n";
+ }
+ while (try_to_merge(open_ring_its));
+
+ if (!open_ring_its.empty()) {
if (debug()) {
- std::cerr << " segment " << segment << " from way " << segment.way()->id() << " should have role 'inner'\n";
+ std::cerr << " After joining obvious cases there are still " << open_ring_its.size() << " open rings\n";
}
- if (m_config.problem_reporter) {
- m_config.problem_reporter->report_role_should_be_inner(segment.way()->id(), segment.first().location(), segment.second().location());
+ if (!join_connected_rings(open_ring_its)) {
+ return false;
}
}
}
+
+ if (debug()) {
+ std::cerr << " Joined all open rings\n";
+ }
}
+
+ // Now all rings are complete.
+
+ find_inner_outer_complex();
+
+ return true;
}
/**
* Create rings from segments.
*/
bool create_rings() {
+ m_stats.nodes += m_segment_list.size();
+
+ // Sort the list of segments (from left to right and bottom
+ // to top).
+ osmium::Timer timer_sort;
m_segment_list.sort();
- m_segment_list.erase_duplicate_segments();
+ timer_sort.stop();
+
+ // Remove duplicate segments. Removal is in pairs, so if there
+ // are two identical segments, they will both be removed. If
+ // there are three, two will be removed and one remains.
+ osmium::Timer timer_dupl;
+ m_stats.duplicate_segments = m_segment_list.erase_duplicate_segments(m_config.problem_reporter);
+ timer_dupl.stop();
+
+ // If there are no segments left at this point, this isn't
+ // a valid area.
+ if (m_segment_list.empty()) {
+ if (debug()) {
+ std::cerr << " No segments left\n";
+ }
+ return false;
+ }
+
+ if (m_config.debug_level >= 3) {
+ std::cerr << "Sorted de-duplicated segment list:\n";
+ for (const auto& s : m_segment_list) {
+ std::cerr << " " << s << "\n";
+ }
+ }
// Now we look for segments crossing each other. If there are
// any, the multipolygon is invalid.
// In the future this could be improved by trying to fix those
// cases.
- if (m_segment_list.find_intersections(m_config.problem_reporter)) {
+ osmium::Timer timer_intersection;
+ m_stats.intersections = m_segment_list.find_intersections(m_config.problem_reporter);
+ timer_intersection.stop();
+
+ if (m_stats.intersections) {
return false;
}
- // Now iterator over all segments and add them to rings. Each segment
- // is tacked on to either end of an existing ring if possible, or a
- // new ring is started with it.
- for (const auto& segment : m_segment_list) {
+ // This creates an ordered list of locations of both endpoints
+ // of all segments with pointers back to the segments. We will
+ // use this list later to quickly find which segment(s) fits
+ // onto a known segment.
+ osmium::Timer timer_locations_list;
+ create_locations_list();
+ timer_locations_list.stop();
+
+ // Find all locations where more than two segments start or
+ // end. We call those "split" locations. If there are any
+ // "spike" segments found while doing this, we know the area
+ // geometry isn't valid and return.
+ osmium::Timer timer_split;
+ if (!find_split_locations()) {
+ return false;
+ }
+ timer_split.stop();
+
+ // Now report all split locations to the problem reporter.
+ m_stats.touching_rings += m_split_locations.size();
+ if (!m_split_locations.empty()) {
if (debug()) {
- std::cerr << " checking segment " << segment << "\n";
+ std::cerr << " Found split locations:\n";
}
- if (!add_to_existing_ring(segment)) {
+ 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);
+ });
+ assert(it != m_locations.cend());
+ const osmium::object_id_type id = it->node_ref(m_segment_list).ref();
+ m_config.problem_reporter->report_touching_ring(id, location);
+ }
if (debug()) {
- std::cerr << " new ring for segment " << segment << "\n";
+ std::cerr << " " << location << "\n";
}
- m_rings.emplace_back(segment);
}
}
- if (debug()) {
- std::cerr << " Rings:\n";
- for (const auto& ring : m_rings) {
- std::cerr << " " << ring;
- if (ring.closed()) {
- std::cerr << " (closed)";
- }
- std::cerr << "\n";
+ // From here on we use two different algorithms depending on
+ // whether there were any split locations or not. If there
+ // are no splits, we use the faster "simple algorithm", if
+ // there are, we use the slower "complex algorithm".
+ osmium::Timer timer_simple_case;
+ osmium::Timer timer_complex_case;
+ if (m_split_locations.empty()) {
+ if (debug()) {
+ std::cerr << " No split locations -> using simple algorithm\n";
}
- }
+ ++m_stats.area_simple_case;
- if (check_for_open_rings()) {
+ timer_simple_case.start();
+ create_rings_simple_case();
+ timer_simple_case.stop();
+ } else {
if (debug()) {
- std::cerr << " not all rings are closed\n";
+ std::cerr << " Found split locations -> using complex algorithm\n";
}
- return false;
+ ++m_stats.area_touching_rings_case;
+
+ timer_complex_case.start();
+ if (!create_rings_complex_case()) {
+ return false;
+ }
+ timer_complex_case.stop();
}
- if (debug()) {
- std::cerr << " Find inner/outer...\n";
+ // If the assembler was so configured, now check whether the
+ // member roles are correctly tagged.
+ if (m_config.check_roles && m_stats.from_relations) {
+ osmium::Timer timer_roles;
+ check_inner_outer_roles();
+ timer_roles.stop();
}
- if (m_rings.size() == 1) {
- m_outer_rings.push_back(&m_rings.front());
+ m_stats.outer_rings = std::count_if(m_rings.cbegin(), m_rings.cend(), [](const detail::ProtoRing& ring){
+ return ring.is_outer();
+ });
+ m_stats.inner_rings = m_rings.size() - m_stats.outer_rings;
+
+#ifdef OSMIUM_WITH_TIMER
+ std::cout << m_stats.nodes << ' ' << m_stats.outer_rings << ' ' << m_stats.inner_rings <<
+ ' ' << timer_sort.elapsed_microseconds() <<
+ ' ' << timer_dupl.elapsed_microseconds() <<
+ ' ' << timer_intersection.elapsed_microseconds() <<
+ ' ' << timer_locations_list.elapsed_microseconds() <<
+ ' ' << timer_split.elapsed_microseconds();
+
+ if (m_split_locations.empty()) {
+ std::cout << ' ' << timer_simple_case.elapsed_microseconds() <<
+ " 0";
} else {
- for (auto& ring : m_rings) {
- check_inner_outer(ring);
- if (ring.outer()) {
- if (!ring.is_cw()) {
- ring.reverse();
- }
- m_outer_rings.push_back(&ring);
- } else {
- if (ring.is_cw()) {
- ring.reverse();
- }
- m_inner_rings.push_back(&ring);
- }
- }
-
- if (m_outer_rings.size() == 1) {
- for (auto inner : m_inner_rings) {
- m_outer_rings.front()->add_inner_ring(inner);
- }
- } else {
- // sort outer rings by size, smallest first
- std::sort(m_outer_rings.begin(), m_outer_rings.end(), [](ProtoRing* a, ProtoRing* b) {
- return a->area() < b->area();
- });
- for (auto inner : m_inner_rings) {
- for (auto outer : m_outer_rings) {
- if (inner->is_in(outer)) {
- outer->add_inner_ring(inner);
- break;
- }
- }
- }
- }
+ std::cout << " 0" <<
+ ' ' << timer_complex_case.elapsed_microseconds();
}
- check_inner_outer_roles();
+ std::cout <<
+# ifdef OSMIUM_AREA_CHECK_INNER_OUTER_ROLES
+ ' ' << timer_roles.elapsed_microseconds() <<
+# else
+ " 0" <<
+# endif
+ '\n';
+#endif
+
+ return true;
+ }
+#ifdef OSMIUM_WITH_TIMER
+ static bool print_header() {
+ std::cout << "nodes outer_rings inner_rings sort dupl intersection locations split simple_case complex_case roles_check\n";
return true;
}
+ static bool init_header() {
+ static bool printed_print_header = print_header();
+ return printed_print_header;
+ }
+#endif
+
+ bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) {
+ osmium::builder::AreaBuilder builder(out_buffer);
+ builder.initialize_from_object(way);
+
+ const bool area_okay = create_rings();
+ if (area_okay || m_config.create_empty_areas) {
+ add_tags_to_area(builder, way);
+ }
+ if (area_okay) {
+ add_rings_to_area(builder);
+ }
+
+ if (report_ways()) {
+ m_config.problem_reporter->report_way(way);
+ }
+
+ return area_okay || m_config.create_empty_areas;
+ }
+
+ bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
+ osmium::builder::AreaBuilder builder(out_buffer);
+ builder.initialize_from_object(relation);
+
+ const bool area_okay = create_rings();
+ if (area_okay || m_config.create_empty_areas) {
+ add_tags_to_area(builder, relation);
+ }
+ if (area_okay) {
+ add_rings_to_area(builder);
+ }
+
+ if (report_ways()) {
+ for (const osmium::Way* way : members) {
+ m_config.problem_reporter->report_way(*way);
+ }
+ }
+
+ return area_okay || m_config.create_empty_areas;
+ }
+
public:
- typedef osmium::area::AssemblerConfig config_type;
+ using config_type = osmium::area::AssemblerConfig;
explicit Assembler(const config_type& config) :
m_config(config),
- m_segment_list(config.debug) {
+ m_segment_list(config.debug_level > 1) {
+#ifdef OSMIUM_WITH_TIMER
+ init_header();
+#endif
}
- ~Assembler() = default;
+ ~Assembler() noexcept = default;
/**
* Assemble an area from the given way.
* The resulting area is put into the out_buffer.
*/
void operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) {
+ if (!m_config.create_way_polygons) {
+ return;
+ }
+
+ if (way.tags().has_tag("area", "no")) {
+ return;
+ }
+
if (m_config.problem_reporter) {
m_config.problem_reporter->set_object(osmium::item_type::way, way.id());
+ m_config.problem_reporter->set_nodes(way.nodes().size());
+ }
+
+ // Ignore (but count) ways without segments.
+ if (way.nodes().size() < 2) {
+ ++m_stats.short_ways;
+ return;
}
if (!way.ends_have_same_id()) {
+ ++m_stats.duplicate_nodes;
if (m_config.problem_reporter) {
m_config.problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location());
}
}
- m_segment_list.extract_segments_from_way(way, "outer");
+ ++m_stats.from_ways;
+ m_stats.duplicate_nodes += m_segment_list.extract_segments_from_way(m_config.problem_reporter, way);
- if (debug()) {
- std::cerr << "\nBuild way id()=" << way.id() << " segments.size()=" << m_segment_list.size() << "\n";
+ if (m_config.debug_level > 0) {
+ std::cerr << "\nAssembling way " << way.id() << " containing " << m_segment_list.size() << " nodes\n";
}
// Now create the Area object and add the attributes and tags
// from the way.
- {
- osmium::builder::AreaBuilder builder(out_buffer);
- builder.initialize_from_object(way);
+ if (create_area(out_buffer, way)) {
+ out_buffer.commit();
+ } else {
+ out_buffer.rollback();
+ }
- if (create_rings()) {
- add_tags_to_area(builder, way);
- add_rings_to_area(builder);
- }
+ if (debug()) {
+ std::cerr << "Done: " << m_stats << "\n";
}
- out_buffer.commit();
}
/**
@@ -714,32 +1440,62 @@ namespace osmium {
* All members are to be found in the in_buffer at the offsets
* given by the members parameter.
* The resulting area is put into the out_buffer.
+ *
+ * @deprecated
+ * This function is deprecated. Use the other form of the function
+ * instead.
+ */
+ OSMIUM_DEPRECATED void operator()(const osmium::Relation& relation, const std::vector<size_t>& members, const osmium::memory::Buffer& in_buffer, osmium::memory::Buffer& out_buffer) {
+ std::vector<const osmium::Way*> ways;
+ for (size_t offset : members) {
+ const osmium::Way& way = in_buffer.get<const osmium::Way>(offset);
+ ways.push_back(&way);
+ }
+ operator()(relation, ways, out_buffer);
+ }
+
+ /**
+ * Assemble an area from the given relation and its members.
+ * The resulting area is put into the out_buffer.
*/
- void operator()(const osmium::Relation& relation, const std::vector<size_t>& members, const osmium::memory::Buffer& in_buffer, osmium::memory::Buffer& out_buffer) {
+ void operator()(const osmium::Relation& relation, const std::vector<const osmium::Way*>& members, osmium::memory::Buffer& out_buffer) {
+ assert(relation.members().size() >= members.size());
+
if (m_config.problem_reporter) {
m_config.problem_reporter->set_object(osmium::item_type::relation, relation.id());
}
- m_segment_list.extract_segments_from_ways(relation, members, in_buffer);
+ if (relation.members().empty()) {
+ ++m_stats.no_way_in_mp_relation;
+ return;
+ }
- if (debug()) {
- std::cerr << "\nBuild relation id()=" << relation.id() << " members.size()=" << members.size() << " segments.size()=" << m_segment_list.size() << "\n";
+ ++m_stats.from_relations;
+ m_stats.duplicate_nodes += m_segment_list.extract_segments_from_ways(m_config.problem_reporter, relation, members);
+ m_stats.member_ways = members.size();
+
+ if (m_stats.member_ways == 1) {
+ ++m_stats.single_way_in_mp_relation;
+ }
+
+ if (m_config.debug_level > 0) {
+ std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << m_segment_list.size() << " nodes\n";
}
- size_t area_offset = out_buffer.committed();
+ const size_t area_offset = out_buffer.committed();
// Now create the Area object and add the attributes and tags
// from the relation.
- {
- osmium::builder::AreaBuilder builder(out_buffer);
- builder.initialize_from_object(relation);
-
- if (create_rings()) {
- add_tags_to_area(builder, relation);
- add_rings_to_area(builder);
+ if (create_area(out_buffer, relation, members)) {
+ if ((m_config.create_new_style_polygons && m_stats.no_tags_on_relation == 0) ||
+ (m_config.create_old_style_polygons && m_stats.no_tags_on_relation != 0)) {
+ out_buffer.commit();
+ } else {
+ out_buffer.rollback();
}
+ } else {
+ out_buffer.rollback();
}
- out_buffer.commit();
const osmium::TagList& area_tags = out_buffer.get<osmium::Area>(area_offset).tags(); // tags of the area we just built
@@ -748,27 +1504,33 @@ namespace osmium {
// just built, add them to a list and later build areas for
// them, too.
std::vector<const osmium::Way*> ways_that_should_be_areas;
- if (m_inner_outer_mismatches == 0) {
- auto memit = relation.members().begin();
- for (size_t offset : members) {
- if (!std::strcmp(memit->role(), "inner")) {
- const osmium::Way& way = in_buffer.get<const osmium::Way>(offset);
+ if (m_stats.wrong_role == 0) {
+ detail::for_each_member(relation, members, [this, &ways_that_should_be_areas, &area_tags](const osmium::RelationMember& member, const osmium::Way& way) {
+ if (!std::strcmp(member.role(), "inner")) {
if (!way.nodes().empty() && way.is_closed() && way.tags().size() > 0) {
- auto d = std::count_if(way.tags().begin(), way.tags().end(), filter());
+ const auto d = std::count_if(way.tags().cbegin(), way.tags().cend(), filter());
if (d > 0) {
- osmium::tags::KeyFilter::iterator way_fi_begin(filter(), way.tags().begin(), way.tags().end());
- osmium::tags::KeyFilter::iterator way_fi_end(filter(), way.tags().end(), way.tags().end());
- osmium::tags::KeyFilter::iterator area_fi_begin(filter(), area_tags.begin(), area_tags.end());
- osmium::tags::KeyFilter::iterator area_fi_end(filter(), area_tags.end(), area_tags.end());
+ osmium::tags::KeyFilter::iterator way_fi_begin(filter(), way.tags().cbegin(), way.tags().cend());
+ osmium::tags::KeyFilter::iterator way_fi_end(filter(), way.tags().cend(), way.tags().cend());
+ osmium::tags::KeyFilter::iterator area_fi_begin(filter(), area_tags.cbegin(), area_tags.cend());
+ osmium::tags::KeyFilter::iterator area_fi_end(filter(), area_tags.cend(), area_tags.cend());
if (!std::equal(way_fi_begin, way_fi_end, area_fi_begin) || d != std::distance(area_fi_begin, area_fi_end)) {
ways_that_should_be_areas.push_back(&way);
+ } else {
+ ++m_stats.inner_with_same_tags;
+ if (m_config.problem_reporter) {
+ m_config.problem_reporter->report_inner_with_same_tags(way);
+ }
}
}
}
}
- ++memit;
- }
+ });
+ }
+
+ if (debug()) {
+ std::cerr << "Done: " << m_stats << "\n";
}
// Now build areas for all ways found in the last step.
@@ -778,6 +1540,14 @@ namespace osmium {
}
}
+ /**
+ * Get statistics from assembler. Call this after running the
+ * assembler to get statistics and data about errors.
+ */
+ const osmium::area::area_stats& stats() const noexcept {
+ return m_stats;
+ }
+
}; // class Assembler
} // namespace area
diff --git a/include/osmium/area/detail/node_ref_segment.hpp b/include/osmium/area/detail/node_ref_segment.hpp
index 7c1555a..bb97520 100644
--- a/include/osmium/area/detail/node_ref_segment.hpp
+++ b/include/osmium/area/detail/node_ref_segment.hpp
@@ -34,11 +34,13 @@ DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
+#include <cassert>
#include <cstdint>
#include <cstring>
#include <iosfwd>
#include <utility>
+#include <osmium/area/detail/vector.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node_ref.hpp>
@@ -53,108 +55,178 @@ namespace osmium {
*/
namespace detail {
+ class ProtoRing;
+
+ enum class role_type : uint8_t {
+ unknown = 0,
+ outer = 1,
+ inner = 2,
+ empty = 3
+ };
+
/**
- * This helper class for the Assembler class models a segment.
- * Segments are the connection between
- * two nodes and they all have their smaller coordinate at the
- * beginning of the segment. Smaller, in this case, means smaller x
- * coordinate, and if they are the same smaller y coordinate.
+ * This helper class for the Assembler class models a segment,
+ * the connection between two nodes.
+ *
+ * Internally segments have their smaller coordinate at the
+ * beginning of the segment. Smaller, in this case, means smaller
+ * x coordinate, and, if they are the same, smaller y coordinate.
*/
class NodeRefSegment {
+ // First node in order described above.
osmium::NodeRef m_first;
- osmium::NodeRef m_second;
- /// Role of the member this segment was from.
- const char* m_role;
+ // Second node in order described above.
+ osmium::NodeRef m_second;
- /// Way this segment was from.
+ // Way this segment was from.
const osmium::Way* m_way;
- public:
+ // The ring this segment is part of. Initially nullptr, this
+ // will be filled in once we know which ring the segment is in.
+ ProtoRing* m_ring;
- void swap_locations() {
- using std::swap;
- swap(m_first, m_second);
- }
+ // The role of this segment from the member role.
+ role_type m_role;
+
+ // Nodes have to be reversed to get the intended order.
+ bool m_reverse = false;
- explicit NodeRefSegment() noexcept :
+ // We found the right direction for this segment in the ring.
+ // (This depends on whether it is an inner or outer ring.)
+ bool m_direction_done = false;
+
+ public:
+
+ NodeRefSegment() noexcept :
m_first(),
m_second(),
- m_role(nullptr),
- m_way(nullptr) {
+ m_way(nullptr),
+ m_ring(nullptr),
+ m_role(role_type::unknown) {
}
- explicit NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, const char* role, const osmium::Way* way) :
+ NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, role_type role = role_type::unknown, const osmium::Way* way = nullptr) noexcept :
m_first(nr1),
m_second(nr2),
- m_role(role),
- m_way(way) {
+ m_way(way),
+ m_ring(nullptr),
+ m_role(role) {
if (nr2.location() < nr1.location()) {
- swap_locations();
+ using std::swap;
+ swap(m_first, m_second);
}
}
- NodeRefSegment(const NodeRefSegment&) = default;
- NodeRefSegment(NodeRefSegment&&) = default;
+ /**
+ * The ring this segment is a part of. nullptr if we don't
+ * have the ring yet.
+ */
+ ProtoRing* ring() const noexcept {
+ return m_ring;
+ }
- NodeRefSegment& operator=(const NodeRefSegment&) = default;
- NodeRefSegment& operator=(NodeRefSegment&&) = default;
+ /**
+ * Returns true if the segment has already been placed in a
+ * ring.
+ */
+ bool is_done() const noexcept {
+ return m_ring != nullptr;
+ }
- ~NodeRefSegment() = default;
+ void set_ring(ProtoRing* ring) noexcept {
+ assert(ring);
+ m_ring = ring;
+ }
- /// Return first NodeRef of Segment according to sorting order (bottom left to top right).
+ bool is_reverse() const noexcept {
+ return m_reverse;
+ }
+
+ void reverse() noexcept {
+ m_reverse = !m_reverse;
+ }
+
+ bool is_direction_done() const noexcept {
+ return m_direction_done;
+ }
+
+ void mark_direction_done() noexcept {
+ m_direction_done = true;
+ }
+
+ void mark_direction_not_done() noexcept {
+ m_direction_done = false;
+ }
+
+ /**
+ * Return first NodeRef of Segment according to sorting
+ * order (bottom left to top right).
+ */
const osmium::NodeRef& first() const noexcept {
return m_first;
}
- /// Return second NodeRef of Segment according to sorting order (bottom left to top right).
+ /**
+ * Return second NodeRef of Segment according to sorting
+ * order (bottom left to top right).
+ */
const osmium::NodeRef& second() const noexcept {
return m_second;
}
- bool to_left_of(const osmium::Location& location) const {
- // std::cerr << "segment " << first() << "--" << second() << " to_left_of(" << location << "\n";
-
- if (first().location() == location || second().location() == location) {
- return false;
- }
-
- const std::pair<osmium::Location, osmium::Location> mm = std::minmax(first().location(), second().location(), [](const osmium::Location a, const osmium::Location b) {
- return a.y() < b.y();
- });
-
- if (mm.first.y() >= location.y() || mm.second.y() < location.y() || first().location().x() > location.x()) {
- // std::cerr << " false\n";
- return false;
- }
+ /**
+ * Return real first NodeRef of Segment.
+ */
+ const osmium::NodeRef& start() const noexcept {
+ return m_reverse ? m_second : m_first;
+ }
- int64_t ax = mm.first.x();
- int64_t bx = mm.second.x();
- int64_t lx = location.x();
- int64_t ay = mm.first.y();
- int64_t by = mm.second.y();
- int64_t ly = location.y();
- return ((bx - ax)*(ly - ay) - (by - ay)*(lx - ax)) <= 0;
+ /**
+ * Return real second NodeRef of Segment.
+ */
+ const osmium::NodeRef& stop() const noexcept {
+ return m_reverse ? m_first : m_second;
}
bool role_outer() const noexcept {
- return !strcmp(m_role, "outer");
+ return m_role == role_type::outer;
}
bool role_inner() const noexcept {
- return !strcmp(m_role, "inner");
+ return m_role == role_type::inner;
+ }
+
+ bool role_empty() const noexcept {
+ return m_role == role_type::empty;
+ }
+
+ const char* role_name() const noexcept {
+ static const char* names[] = { "unknown", "outer", "inner", "empty" };
+ return names[int(m_role)];
}
const osmium::Way* way() const noexcept {
return m_way;
}
+ /**
+ * The "determinant" of this segment. Used for calculating
+ * the winding order or a ring.
+ */
+ int64_t det() const noexcept {
+ const vec a{start()};
+ const vec b{stop()};
+ return a * b;
+ }
+
}; // class NodeRefSegment
/// NodeRefSegments are equal if both their locations are equal
inline bool operator==(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
- return lhs.first().location() == rhs.first().location() && lhs.second().location() == rhs.second().location();
+ return lhs.first().location() == rhs.first().location() &&
+ lhs.second().location() == rhs.second().location();
}
inline bool operator!=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
@@ -162,12 +234,33 @@ namespace osmium {
}
/**
- * NodeRefSegments are "smaller" if they are to the left and down of another
- * segment. The first() location is checked first() and only if they have the
- * same first() location the second() location is taken into account.
+ * A NodeRefSegment is "smaller" if the first point is to the
+ * left and down of the first point of the second segment.
+ * If both first points are the same, the segment with the higher
+ * slope comes first. If the slope is the same, the shorter
+ * segment comes first.
*/
inline bool operator<(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
- return (lhs.first().location() == rhs.first().location() && lhs.second().location() < rhs.second().location()) || lhs.first().location() < rhs.first().location();
+ if (lhs.first().location() == rhs.first().location()) {
+ vec p0{lhs.first().location()};
+ vec p1{lhs.second().location()};
+ vec q0{rhs.first().location()};
+ vec q1{rhs.second().location()};
+ vec p = p1 - p0;
+ vec q = q1 - q0;
+
+ if (p.x == 0 && q.x == 0) {
+ return p.y < q.y;
+ }
+
+ auto a = p.y * q.x;
+ auto b = q.y * p.x;
+ if (a == b) {
+ return p.x < q.x;
+ }
+ return a > b;
+ }
+ return lhs.first().location() < rhs.first().location();
}
inline bool operator>(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
@@ -184,7 +277,10 @@ namespace osmium {
template <typename TChar, typename TTraits>
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const NodeRefSegment& segment) {
- return out << segment.first() << "--" << segment.second();
+ return out << segment.start() << "--" << segment.stop()
+ << "[" << (segment.is_reverse() ? 'R' : '_')
+ << (segment.is_done() ? 'd' : '_')
+ << (segment.is_direction_done() ? 'D' : '_') << "]";
}
inline bool outside_x_range(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
@@ -194,7 +290,7 @@ namespace osmium {
return false;
}
- inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) {
+ inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
const std::pair<int32_t, int32_t> m1 = std::minmax(s1.first().location().y(), s1.second().location().y());
const std::pair<int32_t, int32_t> m2 = std::minmax(s2.first().location().y(), s2.second().location().y());
if (m1.first > m2.second || m2.first > m1.second) {
@@ -210,55 +306,94 @@ namespace osmium {
* might be slightly different than the numerically correct
* location.
*
- * This function uses integer arithmentic as much as possible and
+ * This function uses integer arithmetic as much as possible and
* will not work if the segments are longer than about half the
* planet. This shouldn't happen with real data, so it isn't a big
* problem.
*
- * If the segments touch in one of their endpoints, it doesn't
- * count as an intersection.
+ * If the segments touch in one or both of their endpoints, it
+ * doesn't count as an intersection.
*
* If the segments intersect not in a single point but in multiple
- * points, ie if they overlap, this is NOT detected.
+ * points, ie if they are collinear and overlap, the smallest
+ * of the endpoints that is in the overlapping section is returned.
*
* @returns Undefined osmium::Location if there is no intersection
* or a defined Location if the segments intersect.
*/
- inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) {
- if (s1.first().location() == s2.first().location() ||
- s1.first().location() == s2.second().location() ||
- s1.second().location() == s2.first().location() ||
- s1.second().location() == s2.second().location()) {
+ inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
+ // See http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
+ // for some hints about how the algorithm works.
+ const vec p0{s1.first()};
+ const vec p1{s1.second()};
+ const vec q0{s2.first()};
+ const vec q1{s2.second()};
+
+ if ((p0 == q0 && p1 == q1) ||
+ (p0 == q1 && p1 == q0)) {
+ // segments are the same
return osmium::Location();
}
- int64_t s1ax = s1.first().x();
- int64_t s1ay = s1.first().y();
- int64_t s1bx = s1.second().x();
- int64_t s1by = s1.second().y();
- int64_t s2ax = s2.first().x();
- int64_t s2ay = s2.first().y();
- int64_t s2bx = s2.second().x();
- int64_t s2by = s2.second().y();
-
- int64_t d = (s2by - s2ay) * (s1bx - s1ax) -
- (s2bx - s2ax) * (s1by - s1ay);
+ const vec pd = p1 - p0;
+ const int64_t d = pd * (q1 - q0);
if (d != 0) {
- int64_t na = (s2bx - s2ax) * (s1ay - s2ay) -
- (s2by - s2ay) * (s1ax - s2ax);
+ // segments are not collinear
+
+ if (p0 == q0 || p0 == q1 || p1 == q0 || p1 == q1) {
+ // touching at an end point
+ return osmium::Location();
+ }
+
+ // intersection in a point
- int64_t nb = (s1bx - s1ax) * (s1ay - s2ay) -
- (s1by - s1ay) * (s1ax - s2ax);
+ const int64_t na = (q1.x - q0.x) * (p0.y - q0.y) -
+ (q1.y - q0.y) * (p0.x - q0.x);
+
+ const int64_t nb = (p1.x - p0.x) * (p0.y - q0.y) -
+ (p1.y - p0.y) * (p0.x - q0.x);
if ((d > 0 && na >= 0 && na <= d && nb >= 0 && nb <= d) ||
(d < 0 && na <= 0 && na >= d && nb <= 0 && nb >= d)) {
+ const double ua = double(na) / d;
+ const vec i = p0 + ua * (p1 - p0);
+ return osmium::Location(int32_t(i.x), int32_t(i.y));
+ }
+
+ return osmium::Location();
+ }
- double ua = double(na) / d;
- int32_t ix = int32_t(s1ax + ua*(s1bx - s1ax));
- int32_t iy = int32_t(s1ay + ua*(s1by - s1ay));
+ // segments are collinear
+
+ if (pd * (q0 - p0) == 0) {
+ // segments are on the same line
+
+ struct seg_loc {
+ int segment;
+ osmium::Location location;
+ };
+
+ seg_loc sl[4];
+ sl[0] = {0, s1.first().location() };
+ sl[1] = {0, s1.second().location()};
+ sl[2] = {1, s2.first().location() };
+ sl[3] = {1, s2.second().location()};
+
+ std::sort(sl, sl+4, [](const seg_loc& a, const seg_loc& b) {
+ return a.location < b.location;
+ });
+
+ if (sl[1].location == sl[2].location) {
+ return osmium::Location();
+ }
- return osmium::Location(ix, iy);
+ if (sl[0].segment != sl[1].segment) {
+ if (sl[0].location == sl[1].location) {
+ return sl[2].location;
+ } else {
+ return sl[1].location;
+ }
}
}
diff --git a/include/osmium/area/detail/proto_ring.hpp b/include/osmium/area/detail/proto_ring.hpp
index c4edf40..c71a5d3 100644
--- a/include/osmium/area/detail/proto_ring.hpp
+++ b/include/osmium/area/detail/proto_ring.hpp
@@ -58,214 +58,155 @@ namespace osmium {
public:
- typedef std::vector<NodeRefSegment> segments_type;
+ using segments_type = std::vector<NodeRefSegment*>;
private:
- // segments in this ring
+ // Segments in this ring.
segments_type m_segments;
- bool m_outer {true};
-
- // if this is an outer ring, these point to it's inner rings (if any)
+ // If this is an outer ring, these point to it's inner rings
+ // (if any).
std::vector<ProtoRing*> m_inner;
- public:
-
- explicit ProtoRing(const NodeRefSegment& segment) noexcept :
- m_segments() {
- add_segment_back(segment);
- }
-
- explicit ProtoRing(segments_type::const_iterator sbegin, segments_type::const_iterator send) :
- m_segments(static_cast<size_t>(std::distance(sbegin, send))) {
- std::copy(sbegin, send, m_segments.begin());
- }
-
- bool outer() const noexcept {
- return m_outer;
- }
+ // The smallest segment. Will be kept current whenever a new
+ // segment is added to the ring.
+ NodeRefSegment* m_min_segment;
- void set_inner() noexcept {
- m_outer = false;
- }
+ // If this is an inner ring, points to the outer ring.
+ ProtoRing* m_outer_ring;
- segments_type& segments() noexcept {
- return m_segments;
- }
+ int64_t m_sum;
- const segments_type& segments() const noexcept {
- return m_segments;
- }
-
- void remove_segments(segments_type::iterator sbegin, segments_type::iterator send) {
- m_segments.erase(sbegin, send);
- }
+ public:
- void add_segment_front(const NodeRefSegment& segment) {
- m_segments.insert(m_segments.begin(), segment);
+ explicit ProtoRing(NodeRefSegment* segment) noexcept :
+ m_segments(),
+ m_inner(),
+ m_min_segment(segment),
+ m_outer_ring(nullptr),
+ m_sum(0) {
+ add_segment_back(segment);
}
- void add_segment_back(const NodeRefSegment& segment) {
+ void add_segment_back(NodeRefSegment* segment) {
+ assert(segment);
+ if (*segment < *m_min_segment) {
+ m_min_segment = segment;
+ }
m_segments.push_back(segment);
+ segment->set_ring(this);
+ m_sum += segment->det();
}
- const NodeRefSegment& get_segment_front() const {
- return m_segments.front();
+ NodeRefSegment* min_segment() const noexcept {
+ return m_min_segment;
}
- NodeRefSegment& get_segment_front() {
- return m_segments.front();
+ ProtoRing* outer_ring() const noexcept {
+ return m_outer_ring;
}
- const NodeRef& get_node_ref_front() const {
- return get_segment_front().first();
+ void set_outer_ring(ProtoRing* outer_ring) noexcept {
+ assert(outer_ring);
+ assert(m_inner.empty());
+ m_outer_ring = outer_ring;
}
- const NodeRefSegment& get_segment_back() const {
- return m_segments.back();
+ const std::vector<ProtoRing*>& inner_rings() const noexcept {
+ return m_inner;
}
- NodeRefSegment& get_segment_back() {
- return m_segments.back();
+ void add_inner_ring(ProtoRing* ring) {
+ assert(ring);
+ assert(!m_outer_ring);
+ m_inner.push_back(ring);
}
- const NodeRef& get_node_ref_back() const {
- return get_segment_back().second();
+ bool is_outer() const noexcept {
+ return !m_outer_ring;
}
- bool closed() const {
- return m_segments.front().first().location() == m_segments.back().second().location();
+ const segments_type& segments() const noexcept {
+ return m_segments;
}
- int64_t sum() const {
- int64_t sum = 0;
-
- for (const auto& segment : m_segments) {
- sum += static_cast<int64_t>(segment.first().location().x()) * static_cast<int64_t>(segment.second().location().y()) -
- static_cast<int64_t>(segment.second().location().x()) * static_cast<int64_t>(segment.first().location().y());
- }
+ const NodeRef& get_node_ref_start() const noexcept {
+ return m_segments.front()->start();
+ }
- return sum;
+ const NodeRef& get_node_ref_stop() const noexcept {
+ return m_segments.back()->stop();
}
- bool is_cw() const {
- return sum() <= 0;
+ bool closed() const noexcept {
+ return get_node_ref_start().location() == get_node_ref_stop().location();
}
- int64_t area() const {
- return std::abs(sum()) / 2;
+ void reverse() {
+ std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
+ segment->reverse();
+ });
+ std::reverse(m_segments.begin(), m_segments.end());
+ m_sum = -m_sum;
}
- void swap_segments(ProtoRing& other) {
- using std::swap;
- swap(m_segments, other.m_segments);
+ void mark_direction_done() {
+ std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
+ segment->mark_direction_done();
+ });
}
- void add_inner_ring(ProtoRing* ring) {
- m_inner.push_back(ring);
+ bool is_cw() const noexcept {
+ return m_sum <= 0;
}
- const std::vector<ProtoRing*>& inner_rings() const {
- return m_inner;
+ int64_t sum() const noexcept {
+ return m_sum;
}
- void print(std::ostream& out) const {
- out << "[";
- bool first = true;
- for (const auto& segment : m_segments) {
- if (first) {
- out << segment.first().ref();
- }
- out << ',' << segment.second().ref();
- first = false;
+ void fix_direction() noexcept {
+ if (is_cw() == is_outer()) {
+ reverse();
}
- out << "]";
}
- void reverse() {
- std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment& segment) {
- segment.swap_locations();
+ void reset() {
+ m_inner.clear();
+ m_outer_ring = nullptr;
+ std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
+ segment->mark_direction_not_done();
});
- std::reverse(m_segments.begin(), m_segments.end());
}
- /**
- * Merge other ring to end of this ring.
- */
- void merge_ring(const ProtoRing& other, bool debug) {
- if (debug) {
- std::cerr << " MERGE rings ";
- print(std::cerr);
- std::cerr << " to ";
- other.print(std::cerr);
- std::cerr << "\n";
- }
- m_segments.insert(m_segments.end(), other.m_segments.begin(), other.m_segments.end());
- if (debug) {
- std::cerr << " result ring: ";
- print(std::cerr);
- std::cerr << "\n";
+ void get_ways(std::set<const osmium::Way*>& ways) const {
+ for (const auto& segment : m_segments) {
+ ways.insert(segment->way());
}
}
- void merge_ring_reverse(const ProtoRing& other, bool debug) {
- if (debug) {
- std::cerr << " MERGE rings (reverse) ";
- print(std::cerr);
- std::cerr << " to ";
- other.print(std::cerr);
- std::cerr << "\n";
- }
- size_t n = m_segments.size();
- m_segments.resize(n + other.m_segments.size());
- std::transform(other.m_segments.rbegin(), other.m_segments.rend(), m_segments.begin() + static_cast<segments_type::difference_type>(n), [](NodeRefSegment segment) {
- segment.swap_locations();
- return segment;
- });
- if (debug) {
- std::cerr << " result ring: ";
- print(std::cerr);
- std::cerr << "\n";
+ void join_forward(ProtoRing& other) {
+ for (NodeRefSegment* segment : other.m_segments) {
+ add_segment_back(segment);
}
}
- const NodeRef& min_node() const {
- auto it = std::min_element(m_segments.begin(), m_segments.end());
- if (location_less()(it->first(), it->second())) {
- return it->first();
- } else {
- return it->second();
+ void join_backward(ProtoRing& other) {
+ for (auto it = other.m_segments.rbegin(); it != other.m_segments.rend(); ++it) {
+ (*it)->reverse();
+ add_segment_back(*it);
}
}
- bool is_in(ProtoRing* outer) {
- osmium::Location testpoint = segments().front().first().location();
- bool is_in = false;
-
- for (size_t i = 0, j = outer->segments().size()-1; i < outer->segments().size(); j = i++) {
- if (((outer->segments()[i].first().location().y() > testpoint.y()) != (outer->segments()[j].first().location().y() > testpoint.y())) &&
- (testpoint.x() < (outer->segments()[j].first().location().x() - outer->segments()[i].first().location().x()) * (testpoint.y() - outer->segments()[i].first().location().y()) / (outer->segments()[j].first().location().y() - outer->segments()[i].first().location().y()) + outer->segments()[i].first().location().x()) ) {
- is_in = !is_in;
- }
+ void print(std::ostream& out) const {
+ out << "[";
+ if (!m_segments.empty()) {
+ out << m_segments.front()->start().ref();
}
-
- return is_in;
- }
-
- void get_ways(std::set<const osmium::Way*>& ways) {
for (const auto& segment : m_segments) {
- ways.insert(segment.way());
- }
- }
-
- bool contains(const NodeRefSegment& segment) const {
- for (const auto& s : m_segments) {
- if (s == segment || (s.first() == segment.second() && s.second() == segment.first())) {
- return true;
- }
+ out << ',' << segment->stop().ref();
}
- return false;
+ out << "]-" << (is_outer() ? "OUTER" : "INNER");
}
}; // class ProtoRing
diff --git a/include/osmium/area/detail/segment_list.hpp b/include/osmium/area/detail/segment_list.hpp
index 05e0cd8..d7c5927 100644
--- a/include/osmium/area/detail/segment_list.hpp
+++ b/include/osmium/area/detail/segment_list.hpp
@@ -36,6 +36,7 @@ DEALINGS IN THE SOFTWARE.
#include <algorithm>
#include <cassert>
#include <iostream>
+#include <numeric>
#include <vector>
#include <osmium/area/problem_reporter.hpp>
@@ -53,6 +54,24 @@ namespace osmium {
namespace detail {
/**
+ * Iterate over all relation members and the vector of ways at the
+ * same time and call given function with the relation member and
+ * way as parameter. This takes into account that there might be
+ * non-way members in the relation.
+ */
+ template <typename F>
+ inline void for_each_member(const osmium::Relation& relation, const std::vector<const osmium::Way*> ways, F&& func) {
+ auto way_it = ways.cbegin();
+ for (const osmium::RelationMember& member : relation.members()) {
+ if (member.type() == osmium::item_type::way) {
+ assert(way_it != ways.cend());
+ func(member, **way_it);
+ ++way_it;
+ }
+ }
+ }
+
+ /**
* This is a helper class for the area assembler. It models
* a list of segments.
*/
@@ -64,6 +83,51 @@ namespace osmium {
bool m_debug;
+ static role_type parse_role(const char* role) noexcept {
+ if (role[0] == '\0') {
+ return role_type::empty;
+ } else if (!std::strcmp(role, "outer")) {
+ return role_type::outer;
+ } else if (!std::strcmp(role, "inner")) {
+ return role_type::inner;
+ }
+ return role_type::unknown;
+ }
+
+ /**
+ * Calculate the number of segments in all the ways together.
+ */
+ static size_t get_num_segments(const std::vector<const osmium::Way*>& members) noexcept {
+ return std::accumulate(members.cbegin(), members.cend(), 0, [](size_t sum, const osmium::Way* way) {
+ if (way->nodes().empty()) {
+ return sum;
+ } else {
+ return sum + way->nodes().size() - 1;
+ }
+ });
+ }
+
+ uint32_t extract_segments_from_way_impl(osmium::area::ProblemReporter* problem_reporter, const osmium::Way& way, role_type role) {
+ uint32_t duplicate_nodes = 0;
+
+ osmium::NodeRef previous_nr;
+ for (const osmium::NodeRef& nr : way.nodes()) {
+ if (previous_nr.location()) {
+ if (previous_nr.location() != nr.location()) {
+ m_segments.emplace_back(previous_nr, nr, role, &way);
+ } else {
+ ++duplicate_nodes;
+ if (problem_reporter) {
+ problem_reporter->report_duplicate_node(previous_nr.ref(), nr.ref(), nr.location());
+ }
+ }
+ }
+ previous_nr = nr;
+ }
+
+ return duplicate_nodes;
+ }
+
public:
explicit SegmentList(bool debug) noexcept :
@@ -84,12 +148,31 @@ namespace osmium {
return m_segments.size();
}
+ /// Is the segment list empty?
bool empty() const noexcept {
return m_segments.empty();
}
- typedef slist_type::const_iterator const_iterator;
- typedef slist_type::iterator iterator;
+ using const_iterator = slist_type::const_iterator;
+ using iterator = slist_type::iterator;
+
+ NodeRefSegment& front() {
+ return m_segments.front();
+ }
+
+ NodeRefSegment& back() {
+ return m_segments.back();
+ }
+
+ const NodeRefSegment& operator[](size_t n) const noexcept {
+ assert(n < m_segments.size());
+ return m_segments[n];
+ }
+
+ NodeRefSegment& operator[](size_t n) noexcept {
+ assert(n < m_segments.size());
+ return m_segments[n];
+ }
iterator begin() noexcept {
return m_segments.begin();
@@ -115,11 +198,6 @@ namespace osmium {
m_debug = debug;
}
- /// Clear the list of segments. All segments are removed.
- void clear() {
- m_segments.clear();
- }
-
/// Sort the list of segments.
void sort() {
std::sort(m_segments.begin(), m_segments.end());
@@ -128,32 +206,37 @@ namespace osmium {
/**
* Extract segments from given way and add them to the list.
*
- * Segments connecting two nodes with the same location (ie same
- * node or different node with same location) are removed.
- *
- * XXX should two nodes with same location be reported?
+ * Segments connecting two nodes with the same location (ie
+ * same node or different nodes with same location) are
+ * removed after reporting the duplicate node.
*/
- void extract_segments_from_way(const osmium::Way& way, const char* role) {
- osmium::NodeRef last_nr;
- for (const osmium::NodeRef& nr : way.nodes()) {
- if (last_nr.location() && last_nr.location() != nr.location()) {
- m_segments.emplace_back(last_nr, nr, role, &way);
- }
- last_nr = nr;
+ uint32_t extract_segments_from_way(osmium::area::ProblemReporter* problem_reporter, const osmium::Way& way) {
+ if (way.nodes().empty()) {
+ return 0;
}
+ m_segments.reserve(way.nodes().size() - 1);
+ return extract_segments_from_way_impl(problem_reporter, way, role_type::outer);
}
/**
* Extract all segments from all ways that make up this
* multipolygon relation and add them to the list.
*/
- void extract_segments_from_ways(const osmium::Relation& relation, const std::vector<size_t>& members, const osmium::memory::Buffer& in_buffer) {
- auto member_it = relation.members().begin();
- for (size_t offset : members) {
- const osmium::Way& way = in_buffer.get<const osmium::Way>(offset);
- extract_segments_from_way(way, member_it->role());
- ++member_it;
+ uint32_t extract_segments_from_ways(osmium::area::ProblemReporter* problem_reporter, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
+ assert(relation.members().size() >= members.size());
+
+ size_t num_segments = get_num_segments(members);
+ if (problem_reporter) {
+ problem_reporter->set_nodes(num_segments);
}
+ m_segments.reserve(num_segments);
+
+ uint32_t duplicate_nodes = 0;
+ for_each_member(relation, members, [this, &problem_reporter, &duplicate_nodes](const osmium::RelationMember& member, const osmium::Way& way) {
+ duplicate_nodes += extract_segments_from_way_impl(problem_reporter, way, parse_role(member.role()));
+ });
+
+ return duplicate_nodes;
}
/**
@@ -162,17 +245,33 @@ namespace osmium {
* same segment. So if there are three, for instance, two will
* be removed and one will be left.
*/
- void erase_duplicate_segments() {
+ uint32_t erase_duplicate_segments(osmium::area::ProblemReporter* problem_reporter) {
+ uint32_t duplicate_segments = 0;
+
while (true) {
auto it = std::adjacent_find(m_segments.begin(), m_segments.end());
if (it == m_segments.end()) {
- return;
+ break;
}
if (m_debug) {
std::cerr << " erase duplicate segment: " << *it << "\n";
}
+
+ // Only count and report duplicate segments if they
+ // belong to the same way. Those cases are definitely
+ // wrong. If the duplicate segments belong to
+ // different ways, they could be touching inner rings
+ // which are perfectly okay.
+ if (it->way() == std::next(it)->way()) {
+ ++duplicate_segments;
+ if (problem_reporter) {
+ problem_reporter->report_duplicate_segment(it->first(), it->second());
+ }
+ }
m_segments.erase(it, it+2);
}
+
+ return duplicate_segments;
}
/**
@@ -182,14 +281,14 @@ namespace osmium {
* reported to this object.
* @returns true if there are intersections.
*/
- bool find_intersections(osmium::area::ProblemReporter* problem_reporter) const {
+ uint32_t find_intersections(osmium::area::ProblemReporter* problem_reporter) const {
if (m_segments.empty()) {
- return false;
+ return 0;
}
- bool found_intersections = false;
+ uint32_t found_intersections = 0;
- for (auto it1 = m_segments.begin(); it1 != m_segments.end()-1; ++it1) {
+ for (auto it1 = m_segments.cbegin(); it1 != m_segments.cend()-1; ++it1) {
const NodeRefSegment& s1 = *it1;
for (auto it2 = it1+1; it2 != m_segments.end(); ++it2) {
const NodeRefSegment& s2 = *it2;
@@ -203,12 +302,13 @@ namespace osmium {
if (y_range_overlap(s1, s2)) {
osmium::Location intersection = calculate_intersection(s1, s2);
if (intersection) {
- found_intersections = true;
+ ++found_intersections;
if (m_debug) {
std::cerr << " segments " << s1 << " and " << s2 << " intersecting at " << intersection << "\n";
}
if (problem_reporter) {
- problem_reporter->report_intersection(s1.way()->id(), s1.first().location(), s1.second().location(), s2.way()->id(), s2.first().location(), s2.second().location(), intersection);
+ problem_reporter->report_intersection(s1.way()->id(), s1.first().location(), s1.second().location(),
+ s2.way()->id(), s2.first().location(), s2.second().location(), intersection);
}
}
}
diff --git a/include/osmium/area/detail/vector.hpp b/include/osmium/area/detail/vector.hpp
new file mode 100644
index 0000000..283801a
--- /dev/null
+++ b/include/osmium/area/detail/vector.hpp
@@ -0,0 +1,120 @@
+#ifndef OSMIUM_AREA_DETAIL_VECTOR_HPP
+#define OSMIUM_AREA_DETAIL_VECTOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include <cstdint>
+
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node_ref.hpp>
+
+namespace osmium {
+
+ namespace area {
+
+ namespace detail {
+
+ /**
+ * This helper class models a 2D vector in the mathematical sense.
+ * It uses 64 bit integers internally which has enough precision
+ * for most operations with inputs based on 32 bit locations.
+ */
+ struct vec {
+
+ int64_t x;
+ int64_t y;
+
+ constexpr vec(int64_t a, int64_t b) noexcept :
+ x(a),
+ y(b) {
+ }
+
+ constexpr explicit vec(const osmium::Location& l) noexcept :
+ x(l.x()),
+ y(l.y()) {
+ }
+
+ constexpr explicit vec(const osmium::NodeRef& nr) noexcept :
+ x(nr.x()),
+ y(nr.y()) {
+ }
+
+ }; // struct vec
+
+ // addition
+ constexpr inline vec operator+(const vec& a, const vec& b) noexcept {
+ return vec{a.x + b.x, a.y + b.y};
+ }
+
+ // subtraction
+ constexpr inline vec operator-(const vec& a, const vec& b) noexcept {
+ return vec{a.x - b.x, a.y - b.y};
+ }
+
+ // cross product
+ constexpr inline int64_t operator*(const vec& a, const vec& b) noexcept {
+ return a.x * b.y - a.y * b.x;
+ }
+
+ // scale vector
+ constexpr inline vec operator*(double s, const vec& v) noexcept {
+ return vec{int64_t(s * v.x), int64_t(s * v.y)};
+ }
+
+ // scale vector
+ constexpr inline vec operator*(const vec& v, double s) noexcept {
+ return vec{int64_t(s * v.x), int64_t(s * v.y)};
+ }
+
+ // equality
+ constexpr inline bool operator==(const vec& a, const vec& b) noexcept {
+ return a.x == b.x && a.y == b.y;
+ }
+
+ // inequality
+ constexpr inline bool operator!=(const vec& a, const vec& b) noexcept {
+ return !(a == b);
+ }
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const vec& v) {
+ return out << '(' << v.x << ',' << v.y << ')';
+ }
+
+ } // namespace detail
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_DETAIL_VECTOR_HPP
diff --git a/include/osmium/area/multipolygon_collector.hpp b/include/osmium/area/multipolygon_collector.hpp
index 81a28b2..0f5e4d7 100644
--- a/include/osmium/area/multipolygon_collector.hpp
+++ b/include/osmium/area/multipolygon_collector.hpp
@@ -34,11 +34,11 @@ DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
-#include <cassert>
#include <cstddef>
#include <cstring>
#include <vector>
+#include <osmium/area/stats.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
@@ -74,13 +74,15 @@ namespace osmium {
template <typename TAssembler>
class MultipolygonCollector : public osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false> {
- typedef typename osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false> collector_type;
+ using collector_type = osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false>;
- typedef typename TAssembler::config_type assembler_config_type;
+ using assembler_config_type = typename TAssembler::config_type;
const assembler_config_type m_assembler_config;
osmium::memory::Buffer m_output_buffer;
+ osmium::area::area_stats m_stats;
+
static constexpr size_t initial_output_buffer_size = 1024 * 1024;
static constexpr size_t max_buffer_size_for_flush = 100 * 1024;
@@ -107,6 +109,10 @@ namespace osmium {
m_output_buffer(initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes) {
}
+ const osmium::area::area_stats& stats() const noexcept {
+ return m_stats;
+ }
+
/**
* We are interested in all relations tagged with type=multipolygon
* or type=boundary.
@@ -121,7 +127,7 @@ namespace osmium {
return false;
}
- if ((!strcmp(type, "multipolygon")) || (!strcmp(type, "boundary"))) {
+ if ((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) {
return true;
}
@@ -155,6 +161,7 @@ namespace osmium {
// way is closed and has enough nodes, build simple multipolygon
TAssembler assembler(m_assembler_config);
assembler(way, m_output_buffer);
+ m_stats += assembler.stats();
possibly_flush_output_buffer();
}
} catch (osmium::invalid_location&) {
@@ -164,15 +171,20 @@ namespace osmium {
void complete_relation(osmium::relations::RelationMeta& relation_meta) {
const osmium::Relation& relation = this->get_relation(relation_meta);
- std::vector<size_t> offsets;
+ const osmium::memory::Buffer& buffer = this->members_buffer();
+
+ std::vector<const osmium::Way*> ways;
for (const auto& member : relation.members()) {
if (member.ref() != 0) {
- offsets.push_back(this->get_offset(member.type(), member.ref()));
+ size_t offset = this->get_offset(member.type(), member.ref());
+ ways.push_back(&buffer.get<const osmium::Way>(offset));
}
}
+
try {
TAssembler assembler(m_assembler_config);
- assembler(relation, offsets, this->members_buffer(), m_output_buffer);
+ assembler(relation, ways, m_output_buffer);
+ m_stats += assembler.stats();
possibly_flush_output_buffer();
} catch (osmium::invalid_location&) {
// XXX ignore
diff --git a/include/osmium/area/problem_reporter.hpp b/include/osmium/area/problem_reporter.hpp
index 1dde85b..13fb096 100644
--- a/include/osmium/area/problem_reporter.hpp
+++ b/include/osmium/area/problem_reporter.hpp
@@ -39,6 +39,9 @@ DEALINGS IN THE SOFTWARE.
namespace osmium {
+ class NodeRef;
+ class Way;
+
namespace area {
/**
@@ -62,6 +65,9 @@ namespace osmium {
// ID of the relation/way we are currently working on
osmium::object_id_type m_object_id;
+ // Number of nodes in the area
+ size_t m_nodes;
+
public:
ProblemReporter() = default;
@@ -79,6 +85,10 @@ namespace osmium {
m_object_id = object_id;
}
+ void set_nodes(size_t nodes) noexcept {
+ m_nodes = nodes;
+ }
+
// Disable "unused-parameter" warning, so that the compiler will not complain.
// We can't remove the parameter names, because then doxygen will complain.
#pragma GCC diagnostic push
@@ -87,14 +97,24 @@ namespace osmium {
/**
* Report a duplicate node, ie. two nodes with the same location.
*
- * @param node_id1 ID of the first node.
- * @param node_id2 ID of the second node.
- * @param location Location of both nodes.
+ * @param node_id1 ID of the first node.
+ * @param node_id2 ID of the second node.
+ * @param location Location of both nodes.
*/
virtual void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) {
}
/**
+ * Report a node/location where rings touch. This is often wrong,
+ * but not necessarily so.
+ *
+ * @param node_id ID of the node.
+ * @param location Location of the node.
+ */
+ virtual void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) {
+ }
+
+ /**
* Report an intersection between two segments.
*
* @param way1_id ID of the first involved way.
@@ -110,20 +130,32 @@ namespace osmium {
}
/**
+ * Report a duplicate segments. Two or more segments are directly
+ * on top of each other. This can be a problem, if there is a
+ * spike for instance, or it could be okay, if there are touching
+ * inner rings.
+ *
+ * @param nr1 NodeRef of one end of the segment.
+ * @param nr2 NodeRef of the other end of the segment.
+ */
+ virtual void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) {
+ }
+
+ /**
* Report an open ring.
*
- * @param end1 Location of the first open end.
- * @param end2 Location of the second open end.
+ * @param nr NodeRef of one end of the ring.
+ * @param way Optional pointer to way the end node is in.
*/
- virtual void report_ring_not_closed(osmium::Location end1, osmium::Location end2) {
+ virtual void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) {
}
/**
* Report a segment that should have role "outer", but has a different role.
*
- * @param way_id ID of the way this segment is in.
- * @param seg_start Start of the segment with the wrong role.
- * @param seg_end End of the segment with the wrong role.
+ * @param way_id ID of the way this segment is in.
+ * @param seg_start Start of the segment with the wrong role.
+ * @param seg_end End of the segment with the wrong role.
*/
virtual void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) {
}
@@ -138,6 +170,32 @@ namespace osmium {
virtual void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) {
}
+ /**
+ * Report a way that is in multiple rings.
+ *
+ * @param way The way.
+ */
+ virtual void report_way_in_multiple_rings(const osmium::Way& way) {
+ }
+
+ /**
+ * Report a way with role inner that has the same tags as the
+ * relation or outer ways.
+ *
+ * @param way The way.
+ */
+ virtual void report_inner_with_same_tags(const osmium::Way& way) {
+ }
+
+ /**
+ * In addition to reporting specific problems, this is used to
+ * report all ways belonging to areas having problems.
+ *
+ * @param way The way
+ */
+ virtual void report_way(const osmium::Way& way) {
+ }
+
#pragma GCC diagnostic pop
}; // class ProblemReporter
diff --git a/include/osmium/area/problem_reporter_exception.hpp b/include/osmium/area/problem_reporter_exception.hpp
index abadd96..8acd181 100644
--- a/include/osmium/area/problem_reporter_exception.hpp
+++ b/include/osmium/area/problem_reporter_exception.hpp
@@ -42,6 +42,8 @@ DEALINGS IN THE SOFTWARE.
namespace osmium {
+ class NodeRef;
+
namespace area {
class ProblemReporterException : public ProblemReporterStream {
@@ -62,6 +64,12 @@ namespace osmium {
throw std::runtime_error(m_sstream.str());
}
+ void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
+ m_sstream.str();
+ ProblemReporterStream::report_touching_ring(node_id, location);
+ throw std::runtime_error(m_sstream.str());
+ }
+
void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
m_sstream.str();
@@ -69,9 +77,15 @@ namespace osmium {
throw std::runtime_error(m_sstream.str());
}
- void report_ring_not_closed(osmium::Location end1, osmium::Location end2) override {
+ void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
+ m_sstream.str();
+ ProblemReporterStream::report_duplicate_segment(nr1, nr2);
+ throw std::runtime_error(m_sstream.str());
+ }
+
+ void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
m_sstream.str();
- ProblemReporterStream::report_ring_not_closed(end1, end2);
+ ProblemReporterStream::report_ring_not_closed(nr, way);
throw std::runtime_error(m_sstream.str());
}
@@ -87,6 +101,18 @@ namespace osmium {
throw std::runtime_error(m_sstream.str());
}
+ void report_way_in_multiple_rings(const osmium::Way& way) override {
+ m_sstream.str();
+ ProblemReporterStream::report_way_in_multiple_rings(way);
+ throw std::runtime_error(m_sstream.str());
+ }
+
+ void report_inner_with_same_tags(const osmium::Way& way) override {
+ m_sstream.str();
+ ProblemReporterStream::report_inner_with_same_tags(way);
+ throw std::runtime_error(m_sstream.str());
+ }
+
}; // class ProblemReporterException
} // namespace area
diff --git a/include/osmium/area/problem_reporter_ogr.hpp b/include/osmium/area/problem_reporter_ogr.hpp
index d58fe55..b7704a7 100644
--- a/include/osmium/area/problem_reporter_ogr.hpp
+++ b/include/osmium/area/problem_reporter_ogr.hpp
@@ -42,6 +42,7 @@ DEALINGS IN THE SOFTWARE.
* @attention If you include this file, you'll need to link with `libgdal`.
*/
+#include <cstdint>
#include <memory>
#include <gdalcpp.hpp>
@@ -50,7 +51,10 @@ DEALINGS IN THE SOFTWARE.
#include <osmium/geom/factory.hpp>
#include <osmium/geom/ogr.hpp>
#include <osmium/osm/location.hpp>
+#include <osmium/osm/node_ref.hpp>
+#include <osmium/osm/node_ref_list.hpp>
#include <osmium/osm/types.hpp>
+#include <osmium/osm/way.hpp>
namespace osmium {
@@ -66,26 +70,34 @@ namespace osmium {
gdalcpp::Layer m_layer_perror;
gdalcpp::Layer m_layer_lerror;
+ gdalcpp::Layer m_layer_ways;
+
+ void set_object(gdalcpp::Feature& feature) {
+ const char t[2] = { osmium::item_type_to_char(m_object_type), '\0' };
+ feature.set_field("obj_type", t);
+ feature.set_field("obj_id", int32_t(m_object_id));
+ feature.set_field("nodes", int32_t(m_nodes));
+ }
void write_point(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location location) {
gdalcpp::Feature feature(m_layer_perror, m_ogr_factory.create_point(location));
- feature.set_field("id1", static_cast<double>(id1));
- feature.set_field("id2", static_cast<double>(id2));
- feature.set_field("problem_type", problem_type);
+ set_object(feature);
+ feature.set_field("id1", double(id1));
+ feature.set_field("id2", double(id2));
+ feature.set_field("problem", problem_type);
feature.add_to_layer();
}
void write_line(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location loc1, osmium::Location loc2) {
- std::unique_ptr<OGRPoint> ogr_point1 = m_ogr_factory.create_point(loc1);
- std::unique_ptr<OGRPoint> ogr_point2 = m_ogr_factory.create_point(loc2);
- std::unique_ptr<OGRLineString> ogr_linestring = std::unique_ptr<OGRLineString>(new OGRLineString());
- ogr_linestring->addPoint(ogr_point1.get());
- ogr_linestring->addPoint(ogr_point2.get());
+ auto ogr_linestring = std::unique_ptr<OGRLineString>{new OGRLineString{}};
+ ogr_linestring->addPoint(loc1.lon(), loc1.lat());
+ ogr_linestring->addPoint(loc2.lon(), loc2.lat());
gdalcpp::Feature feature(m_layer_lerror, std::move(ogr_linestring));
+ set_object(feature);
feature.set_field("id1", static_cast<double>(id1));
feature.set_field("id2", static_cast<double>(id2));
- feature.set_field("problem_type", problem_type);
+ feature.set_field("problem", problem_type);
feature.add_to_layer();
}
@@ -93,15 +105,36 @@ namespace osmium {
explicit ProblemReporterOGR(gdalcpp::Dataset& dataset) :
m_layer_perror(dataset, "perrors", wkbPoint),
- m_layer_lerror(dataset, "lerrors", wkbLineString) {
-
- m_layer_perror.add_field("id1", OFTReal, 10);
- m_layer_perror.add_field("id2", OFTReal, 10);
- m_layer_perror.add_field("problem_type", OFTString, 30);
-
- m_layer_lerror.add_field("id1", OFTReal, 10);
- m_layer_lerror.add_field("id2", OFTReal, 10);
- m_layer_lerror.add_field("problem_type", OFTString, 30);
+ m_layer_lerror(dataset, "lerrors", wkbLineString),
+ m_layer_ways(dataset, "ways", wkbLineString) {
+
+ // 64bit integers are not supported in GDAL < 2, so we
+ // are using a workaround here in fields where we expect
+ // node IDs, we use real numbers.
+ m_layer_perror
+ .add_field("obj_type", OFTString, 1)
+ .add_field("obj_id", OFTInteger, 10)
+ .add_field("nodes", OFTInteger, 8)
+ .add_field("id1", OFTReal, 12, 1)
+ .add_field("id2", OFTReal, 12, 1)
+ .add_field("problem", OFTString, 30)
+ ;
+
+ m_layer_lerror
+ .add_field("obj_type", OFTString, 1)
+ .add_field("obj_id", OFTInteger, 10)
+ .add_field("nodes", OFTInteger, 8)
+ .add_field("id1", OFTReal, 12, 1)
+ .add_field("id2", OFTReal, 12, 1)
+ .add_field("problem", OFTString, 30)
+ ;
+
+ m_layer_ways
+ .add_field("obj_type", OFTString, 1)
+ .add_field("obj_id", OFTInteger, 10)
+ .add_field("way_id", OFTInteger, 10)
+ .add_field("nodes", OFTInteger, 8)
+ ;
}
~ProblemReporterOGR() override = default;
@@ -110,24 +143,82 @@ namespace osmium {
write_point("duplicate_node", node_id1, node_id2, location);
}
+ void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
+ write_point("touching_ring", node_id, 0, location);
+ }
+
void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
- write_point("intersection", m_object_id, 0, intersection);
- write_line("intersection", m_object_id, way1_id, way1_seg_start, way1_seg_end);
- write_line("intersection", m_object_id, way2_id, way2_seg_start, way2_seg_end);
+ write_point("intersection", way1_id, way2_id, intersection);
+ write_line("intersection", way1_id, way2_id, way1_seg_start, way1_seg_end);
+ write_line("intersection", way2_id, way1_id, way2_seg_start, way2_seg_end);
}
- void report_ring_not_closed(osmium::Location end1, osmium::Location end2) override {
- write_point("ring_not_closed", m_object_id, 0, end1);
- write_point("ring_not_closed", m_object_id, 0, end2);
+ void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
+ write_line("duplicate_segment", nr1.ref(), nr2.ref(), nr1.location(), nr2.location());
+ }
+
+ void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
+ write_point("ring_not_closed", nr.ref(), way ? way->id() : 0, nr.location());
}
void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
- write_line("role_should_be_outer", m_object_id, way_id, seg_start, seg_end);
+ write_line("role_should_be_outer", way_id, 0, seg_start, seg_end);
}
void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
- write_line("role_should_be_inner", m_object_id, way_id, seg_start, seg_end);
+ write_line("role_should_be_inner", way_id, 0, seg_start, seg_end);
+ }
+
+ void report_way_in_multiple_rings(const osmium::Way& way) override {
+ if (way.nodes().size() < 2) {
+ return;
+ }
+ try {
+ gdalcpp::Feature feature(m_layer_lerror, m_ogr_factory.create_linestring(way));
+ set_object(feature);
+ feature.set_field("id1", int32_t(way.id()));
+ feature.set_field("id2", 0);
+ feature.set_field("problem", "way_in_multiple_rings");
+ feature.add_to_layer();
+ } catch (osmium::geometry_error& e) {
+ // XXX
+ }
+ }
+
+ void report_inner_with_same_tags(const osmium::Way& way) override {
+ if (way.nodes().size() < 2) {
+ return;
+ }
+ try {
+ gdalcpp::Feature feature(m_layer_lerror, m_ogr_factory.create_linestring(way));
+ set_object(feature);
+ feature.set_field("id1", int32_t(way.id()));
+ feature.set_field("id2", 0);
+ feature.set_field("problem", "inner_with_same_tags");
+ feature.add_to_layer();
+ } catch (osmium::geometry_error& e) {
+ // XXX
+ }
+ }
+
+ void report_way(const osmium::Way& way) override {
+ if (way.nodes().empty()) {
+ return;
+ }
+ if (way.nodes().size() == 1) {
+ const auto& first_nr = way.nodes()[0];
+ write_point("single_node_in_way", way.id(), first_nr.ref(), first_nr.location());
+ return;
+ }
+ try {
+ gdalcpp::Feature feature(m_layer_ways, m_ogr_factory.create_linestring(way));
+ set_object(feature);
+ feature.set_field("way_id", int32_t(way.id()));
+ feature.add_to_layer();
+ } catch (osmium::geometry_error& e) {
+ // XXX
+ }
}
}; // class ProblemReporterOGR
diff --git a/include/osmium/area/problem_reporter_stream.hpp b/include/osmium/area/problem_reporter_stream.hpp
index ffd94a6..c601867 100644
--- a/include/osmium/area/problem_reporter_stream.hpp
+++ b/include/osmium/area/problem_reporter_stream.hpp
@@ -38,7 +38,9 @@ DEALINGS IN THE SOFTWARE.
#include <osmium/area/problem_reporter.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
+#include <osmium/osm/node_ref.hpp>
#include <osmium/osm/types.hpp>
+#include <osmium/osm/way.hpp>
namespace osmium {
@@ -57,7 +59,7 @@ namespace osmium {
~ProblemReporterStream() override = default;
void header(const char* msg) {
- *m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << ": ";
+ *m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << " (with " << m_nodes << " nodes): ";
}
void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
@@ -65,6 +67,11 @@ namespace osmium {
*m_out << "node_id1=" << node_id1 << " node_id2=" << node_id2 << " location=" << location << "\n";
}
+ void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
+ header("touching ring");
+ *m_out << "node_id=" << node_id << " location=" << location << "\n";
+ }
+
void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
header("intersection");
@@ -72,9 +79,19 @@ namespace osmium {
<< " way2_id=" << way2_id << " way2_seg_start=" << way2_seg_start << " way2_seg_end=" << way2_seg_end << " intersection=" << intersection << "\n";
}
- void report_ring_not_closed(osmium::Location end1, osmium::Location end2) override {
+ void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
+ header("duplicate segment");
+ *m_out << "node_id1=" << nr1.ref() << " location1=" << nr1.location()
+ << " node_id2=" << nr2.ref() << " location2=" << nr2.location() << "\n";
+ }
+
+ void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
header("ring not closed");
- *m_out << "end1=" << end1 << " end2=" << end2 << "\n";
+ *m_out << "node_id=" << nr.ref() << " location=" << nr.location();
+ if (way) {
+ *m_out << " on way " << way->id();
+ }
+ *m_out << "\n";
}
void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
@@ -87,6 +104,16 @@ namespace osmium {
*m_out << "way_id=" << way_id << " seg_start=" << seg_start << " seg_end=" << seg_end << "\n";
}
+ void report_way_in_multiple_rings(const osmium::Way& way) override {
+ header("way in multiple rings");
+ *m_out << "way_id=" << way.id() << '\n';
+ }
+
+ void report_inner_with_same_tags(const osmium::Way& way) override {
+ header("inner way with same tags as relation or outer");
+ *m_out << "way_id=" << way.id() << '\n';
+ }
+
}; // class ProblemReporterStream
} // namespace area
diff --git a/include/osmium/area/stats.hpp b/include/osmium/area/stats.hpp
new file mode 100644
index 0000000..79cc002
--- /dev/null
+++ b/include/osmium/area/stats.hpp
@@ -0,0 +1,128 @@
+#ifndef OSMIUM_AREA_STATS_HPP
+#define OSMIUM_AREA_STATS_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include <cstdint>
+#include <ostream>
+
+namespace osmium {
+
+ namespace area {
+
+ /**
+ * These statistics are generated by the area assembler code. They
+ * tell the user of the assembler a lot about the objects this area
+ * is made out of, what happened during the assembly, and what errors
+ * there were.
+ */
+ struct area_stats {
+ uint64_t area_really_complex_case = 0; ///< Most difficult case with rings touching in multiple points
+ uint64_t area_simple_case = 0; ///< Simple case, no touching rings
+ uint64_t area_touching_rings_case = 0; ///< More difficult case with touching rings
+ uint64_t duplicate_nodes = 0; ///< Consecutive identical nodes or consecutive nodes with same location
+ uint64_t duplicate_segments = 0; ///< Segments duplicated (going back and forth)
+ uint64_t from_relations = 0; ///< Area created from multipolygon relation
+ uint64_t from_ways = 0; ///< Area created from way
+ uint64_t inner_rings = 0; ///< Number of inner rings
+ uint64_t inner_with_same_tags = 0; ///< Number of inner ways with same tags as area
+ uint64_t intersections = 0; ///< Number of intersections between segments
+ uint64_t member_ways = 0; ///< Number of ways in the area
+ uint64_t no_tags_on_relation = 0; ///< No tags on relation (old-style multipolygon with tags on outer ways)
+ uint64_t no_way_in_mp_relation = 0; ///< Multipolygon relation with no way members
+ uint64_t nodes = 0; ///< Number of nodes in the area
+ uint64_t open_rings = 0; ///< Number of open rings in the area
+ uint64_t outer_rings = 0; ///< Number of outer rings in the area
+ uint64_t short_ways = 0; ///< Number of ways with less than two nodes
+ uint64_t single_way_in_mp_relation = 0; ///< Multipolygon relation containing a single way
+ uint64_t touching_rings = 0; ///< Rings touching in a node
+ uint64_t ways_in_multiple_rings = 0; ///< Different segments of a way ended up in different rings
+ uint64_t wrong_role = 0; ///< Member has wrong role (not "outer", "inner", or empty)
+
+ area_stats& operator+=(const area_stats& other) noexcept {
+ area_really_complex_case += other.area_really_complex_case;
+ area_simple_case += other.area_simple_case;
+ area_touching_rings_case += other.area_touching_rings_case;
+ duplicate_nodes += other.duplicate_nodes;
+ duplicate_segments += other.duplicate_segments;
+ from_relations += other.from_relations;
+ from_ways += other.from_ways;
+ inner_rings += other.inner_rings;
+ inner_with_same_tags += other.inner_with_same_tags;
+ intersections += other.intersections;
+ member_ways += other.member_ways;
+ no_tags_on_relation += other.no_tags_on_relation;
+ no_way_in_mp_relation += other.no_way_in_mp_relation;
+ nodes += other.nodes;
+ open_rings += other.open_rings;
+ outer_rings += other.outer_rings;
+ short_ways += other.short_ways;
+ single_way_in_mp_relation += other.single_way_in_mp_relation;
+ touching_rings += other.touching_rings;
+ ways_in_multiple_rings += other.ways_in_multiple_rings;
+ wrong_role += other.wrong_role;
+ return *this;
+ }
+
+ }; // struct area_stats
+
+ template <typename TChar, typename TTraits>
+ inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const area_stats& s) {
+ return out << " area_really_complex_case=" << s.area_really_complex_case
+ << " area_simple_case=" << s.area_simple_case
+ << " area_touching_rings_case=" << s.area_touching_rings_case
+ << " duplicate_nodes=" << s.duplicate_nodes
+ << " duplicate_segments=" << s.duplicate_segments
+ << " from_relations=" << s.from_relations
+ << " from_ways=" << s.from_ways
+ << " inner_rings=" << s.inner_rings
+ << " inner_with_same_tags=" << s.inner_with_same_tags
+ << " intersections=" << s.intersections
+ << " member_ways=" << s.member_ways
+ << " no_tags_on_relation=" << s.no_tags_on_relation
+ << " no_way_in_mp_relation=" << s.no_way_in_mp_relation
+ << " nodes=" << s.nodes
+ << " open_rings=" << s.open_rings
+ << " outer_rings=" << s.outer_rings
+ << " short_ways=" << s.short_ways
+ << " single_way_in_mp_relation=" << s.single_way_in_mp_relation
+ << " touching_rings=" << s.touching_rings
+ << " ways_in_multiple_rings=" << s.ways_in_multiple_rings
+ << " wrong_role=" << s.wrong_role;
+ }
+
+ } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_STATS_HPP
diff --git a/include/osmium/builder/osm_object_builder.hpp b/include/osmium/builder/osm_object_builder.hpp
index d4b54f8..2ab0e7f 100644
--- a/include/osmium/builder/osm_object_builder.hpp
+++ b/include/osmium/builder/osm_object_builder.hpp
@@ -186,9 +186,9 @@ namespace osmium {
}; // class NodeRefListBuilder
- typedef NodeRefListBuilder<WayNodeList> WayNodeListBuilder;
- typedef NodeRefListBuilder<OuterRing> OuterRingBuilder;
- typedef NodeRefListBuilder<InnerRing> InnerRingBuilder;
+ using WayNodeListBuilder = NodeRefListBuilder<WayNodeList>;
+ using OuterRingBuilder = NodeRefListBuilder<OuterRing>;
+ using InnerRingBuilder = NodeRefListBuilder<InnerRing>;
class RelationMemberListBuilder : public ObjectBuilder<RelationMemberList> {
@@ -353,8 +353,8 @@ namespace osmium {
}; // class OSMObjectBuilder
- typedef OSMObjectBuilder<osmium::Node> NodeBuilder;
- typedef OSMObjectBuilder<osmium::Relation> RelationBuilder;
+ using NodeBuilder = OSMObjectBuilder<osmium::Node>;
+ using RelationBuilder = OSMObjectBuilder<osmium::Relation>;
class WayBuilder : public OSMObjectBuilder<osmium::Way> {
@@ -398,7 +398,7 @@ namespace osmium {
}; // class AreaBuilder
- typedef ObjectBuilder<osmium::Changeset> ChangesetBuilder;
+ using ChangesetBuilder = ObjectBuilder<osmium::Changeset>;
} // namespace builder
diff --git a/include/osmium/diff_iterator.hpp b/include/osmium/diff_iterator.hpp
index 6e0ba4b..0814fef 100644
--- a/include/osmium/diff_iterator.hpp
+++ b/include/osmium/diff_iterator.hpp
@@ -49,7 +49,7 @@ namespace osmium {
* underlying OSMObjects.
*/
template <typename TBasicIterator>
- class DiffIterator : public std::iterator<std::input_iterator_tag, const osmium::DiffObject> {
+ class DiffIterator {
static_assert(std::is_base_of<osmium::OSMObject, typename TBasicIterator::value_type>::value, "TBasicIterator::value_type must derive from osmium::OSMObject");
@@ -76,6 +76,12 @@ namespace osmium {
public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = const osmium::DiffObject;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
DiffIterator(TBasicIterator begin, TBasicIterator end) :
m_prev(begin),
m_curr(begin),
diff --git a/include/osmium/dynamic_handler.hpp b/include/osmium/dynamic_handler.hpp
index e7c9900..b250b57 100644
--- a/include/osmium/dynamic_handler.hpp
+++ b/include/osmium/dynamic_handler.hpp
@@ -143,7 +143,7 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) ->
class DynamicHandler : public osmium::handler::Handler {
- typedef std::unique_ptr<osmium::handler::detail::HandlerWrapperBase> impl_ptr;
+ using impl_ptr = std::unique_ptr<osmium::handler::detail::HandlerWrapperBase>;
impl_ptr m_impl;
public:
diff --git a/include/osmium/geom/factory.hpp b/include/osmium/geom/factory.hpp
index 394228e..6737278 100644
--- a/include/osmium/geom/factory.hpp
+++ b/include/osmium/geom/factory.hpp
@@ -148,7 +148,7 @@ namespace osmium {
/**
* Add all points of an outer or inner ring to a multipolygon.
*/
- void add_points(const osmium::OuterRing& nodes) {
+ void add_points(const osmium::NodeRefList& nodes) {
osmium::Location last_location;
for (const osmium::NodeRef& node_ref : nodes) {
if (last_location != node_ref.location()) {
@@ -182,12 +182,13 @@ namespace osmium {
m_impl(std::forward<TArgs>(args)...) {
}
- typedef TProjection projection_type;
- typedef typename TGeomImpl::point_type point_type;
- typedef typename TGeomImpl::linestring_type linestring_type;
- typedef typename TGeomImpl::polygon_type polygon_type;
- typedef typename TGeomImpl::multipolygon_type multipolygon_type;
- typedef typename TGeomImpl::ring_type ring_type;
+ using projection_type = TProjection;
+
+ using point_type = typename TGeomImpl::point_type;
+ using linestring_type = typename TGeomImpl::linestring_type;
+ using polygon_type = typename TGeomImpl::polygon_type;
+ using multipolygon_type = typename TGeomImpl::multipolygon_type;
+ using ring_type = typename TGeomImpl::ring_type;
int epsg() const {
return m_projection.epsg();
@@ -378,8 +379,8 @@ namespace osmium {
m_impl.multipolygon_start();
for (auto it = area.cbegin(); it != area.cend(); ++it) {
- const osmium::OuterRing& ring = static_cast<const osmium::OuterRing&>(*it);
if (it->type() == osmium::item_type::outer_ring) {
+ auto& ring = static_cast<const osmium::OuterRing&>(*it);
if (num_polygons > 0) {
m_impl.multipolygon_polygon_finish();
}
@@ -390,6 +391,7 @@ namespace osmium {
++num_rings;
++num_polygons;
} else if (it->type() == osmium::item_type::inner_ring) {
+ auto& ring = static_cast<const osmium::InnerRing&>(*it);
m_impl.multipolygon_inner_ring_start();
add_points(ring);
m_impl.multipolygon_inner_ring_finish();
diff --git a/include/osmium/geom/geojson.hpp b/include/osmium/geom/geojson.hpp
index 044a89f..5291257 100644
--- a/include/osmium/geom/geojson.hpp
+++ b/include/osmium/geom/geojson.hpp
@@ -53,11 +53,11 @@ namespace osmium {
public:
- typedef std::string point_type;
- typedef std::string linestring_type;
- typedef std::string polygon_type;
- typedef std::string multipolygon_type;
- typedef std::string ring_type;
+ using point_type = std::string;
+ using linestring_type = std::string;
+ using polygon_type = std::string;
+ using multipolygon_type = std::string;
+ using ring_type = std::string;
GeoJSONFactoryImpl(int precision = 7) :
m_precision(precision) {
diff --git a/include/osmium/geom/geos.hpp b/include/osmium/geom/geos.hpp
index f9026e5..187c048 100644
--- a/include/osmium/geom/geos.hpp
+++ b/include/osmium/geom/geos.hpp
@@ -93,11 +93,11 @@ namespace osmium {
public:
- typedef std::unique_ptr<geos::geom::Point> point_type;
- typedef std::unique_ptr<geos::geom::LineString> linestring_type;
- typedef std::unique_ptr<geos::geom::Polygon> polygon_type;
- typedef std::unique_ptr<geos::geom::MultiPolygon> multipolygon_type;
- typedef std::unique_ptr<geos::geom::LinearRing> ring_type;
+ using point_type = std::unique_ptr<geos::geom::Point>;
+ using linestring_type = std::unique_ptr<geos::geom::LineString>;
+ using polygon_type = std::unique_ptr<geos::geom::Polygon>;
+ using multipolygon_type = std::unique_ptr<geos::geom::MultiPolygon>;
+ using ring_type = std::unique_ptr<geos::geom::LinearRing>;
explicit GEOSFactoryImpl(geos::geom::GeometryFactory& geos_factory) :
m_precision_model(nullptr),
diff --git a/include/osmium/geom/ogr.hpp b/include/osmium/geom/ogr.hpp
index b8e9ef3..45586e2 100644
--- a/include/osmium/geom/ogr.hpp
+++ b/include/osmium/geom/ogr.hpp
@@ -62,18 +62,18 @@ namespace osmium {
public:
- typedef std::unique_ptr<OGRPoint> point_type;
- typedef std::unique_ptr<OGRLineString> linestring_type;
- typedef std::unique_ptr<OGRPolygon> polygon_type;
- typedef std::unique_ptr<OGRMultiPolygon> multipolygon_type;
- typedef std::unique_ptr<OGRLinearRing> ring_type;
+ using point_type = std::unique_ptr<OGRPoint>;
+ using linestring_type = std::unique_ptr<OGRLineString>;
+ using polygon_type = std::unique_ptr<OGRPolygon>;
+ using multipolygon_type = std::unique_ptr<OGRMultiPolygon>;
+ using ring_type = std::unique_ptr<OGRLinearRing>;
private:
- linestring_type m_linestring;
- multipolygon_type m_multipolygon;
- polygon_type m_polygon;
- ring_type m_ring;
+ linestring_type m_linestring{nullptr};
+ multipolygon_type m_multipolygon{nullptr};
+ polygon_type m_polygon{nullptr};
+ ring_type m_ring{nullptr};
public:
@@ -82,13 +82,13 @@ namespace osmium {
/* Point */
point_type make_point(const osmium::geom::Coordinates& xy) const {
- return point_type(new OGRPoint(xy.x, xy.y));
+ return point_type{new OGRPoint{xy.x, xy.y}};
}
/* LineString */
void linestring_start() {
- m_linestring = std::unique_ptr<OGRLineString>(new OGRLineString());
+ m_linestring.reset(new OGRLineString{});
}
void linestring_add_location(const osmium::geom::Coordinates& xy) {
@@ -97,13 +97,14 @@ namespace osmium {
}
linestring_type linestring_finish(size_t /* num_points */) {
+ assert(!!m_linestring);
return std::move(m_linestring);
}
/* Polygon */
void polygon_start() {
- m_ring = std::unique_ptr<OGRLinearRing>(new OGRLinearRing());
+ m_ring.reset(new OGRLinearRing{});
}
void polygon_add_location(const osmium::geom::Coordinates& xy) {
@@ -112,7 +113,7 @@ namespace osmium {
}
polygon_type polygon_finish(size_t /* num_points */) {
- std::unique_ptr<OGRPolygon> polygon = std::unique_ptr<OGRPolygon>(new OGRPolygon());
+ auto polygon = std::unique_ptr<OGRPolygon>{new OGRPolygon{}};
polygon->addRingDirectly(m_ring.release());
return polygon;
}
@@ -120,11 +121,11 @@ namespace osmium {
/* MultiPolygon */
void multipolygon_start() {
- m_multipolygon.reset(new OGRMultiPolygon());
+ m_multipolygon.reset(new OGRMultiPolygon{});
}
void multipolygon_polygon_start() {
- m_polygon.reset(new OGRPolygon());
+ m_polygon.reset(new OGRPolygon{});
}
void multipolygon_polygon_finish() {
@@ -134,7 +135,7 @@ namespace osmium {
}
void multipolygon_outer_ring_start() {
- m_ring.reset(new OGRLinearRing());
+ m_ring.reset(new OGRLinearRing{});
}
void multipolygon_outer_ring_finish() {
@@ -144,7 +145,7 @@ namespace osmium {
}
void multipolygon_inner_ring_start() {
- m_ring.reset(new OGRLinearRing());
+ m_ring.reset(new OGRLinearRing{});
}
void multipolygon_inner_ring_finish() {
diff --git a/include/osmium/geom/rapid_geojson.hpp b/include/osmium/geom/rapid_geojson.hpp
index 87e1311..b54809a 100644
--- a/include/osmium/geom/rapid_geojson.hpp
+++ b/include/osmium/geom/rapid_geojson.hpp
@@ -53,11 +53,11 @@ namespace osmium {
public:
- typedef void point_type;
- typedef void linestring_type;
- typedef void polygon_type;
- typedef void multipolygon_type;
- typedef void ring_type;
+ using point_type = void;
+ using linestring_type = void;
+ using polygon_type = void;
+ using multipolygon_type = void;
+ using ring_type = void;
RapidGeoJSONFactoryImpl(TWriter& writer) :
m_writer(&writer) {
diff --git a/include/osmium/geom/wkb.hpp b/include/osmium/geom/wkb.hpp
index 19c1216..9df1823 100644
--- a/include/osmium/geom/wkb.hpp
+++ b/include/osmium/geom/wkb.hpp
@@ -142,16 +142,17 @@ namespace osmium {
}
void set_size(const size_t offset, const size_t size) {
- *reinterpret_cast<uint32_t*>(&m_data[offset]) = static_cast_with_assert<uint32_t>(size);
+ uint32_t s = static_cast_with_assert<uint32_t>(size);
+ std::copy_n(reinterpret_cast<char*>(&s), sizeof(uint32_t), &m_data[offset]);
}
public:
- typedef std::string point_type;
- typedef std::string linestring_type;
- typedef std::string polygon_type;
- typedef std::string multipolygon_type;
- typedef std::string ring_type;
+ using point_type = std::string;
+ using linestring_type = std::string;
+ using polygon_type = std::string;
+ using multipolygon_type = std::string;
+ using ring_type = std::string;
explicit WKBFactoryImpl(wkb_type wtype = wkb_type::wkb, out_type otype = out_type::binary) :
m_wkb_type(wtype),
diff --git a/include/osmium/geom/wkt.hpp b/include/osmium/geom/wkt.hpp
index ae73b96..57a6625 100644
--- a/include/osmium/geom/wkt.hpp
+++ b/include/osmium/geom/wkt.hpp
@@ -54,11 +54,11 @@ namespace osmium {
public:
- typedef std::string point_type;
- typedef std::string linestring_type;
- typedef std::string polygon_type;
- typedef std::string multipolygon_type;
- typedef std::string ring_type;
+ using point_type = std::string;
+ using linestring_type = std::string;
+ using polygon_type = std::string;
+ using multipolygon_type = std::string;
+ using ring_type = std::string;
WKTFactoryImpl(int precision = 7) :
m_precision(precision) {
diff --git a/include/osmium/handler/chain.hpp b/include/osmium/handler/chain.hpp
index e59eb05..109e353 100644
--- a/include/osmium/handler/chain.hpp
+++ b/include/osmium/handler/chain.hpp
@@ -67,7 +67,7 @@ namespace osmium {
template <typename... THandler>
class ChainHandler : public osmium::handler::Handler {
- typedef std::tuple<THandler&...> handlers_type;
+ using handlers_type = std::tuple<THandler&...>;
handlers_type m_handlers;
template <int N, int SIZE, typename THandlers>
diff --git a/include/osmium/handler/disk_store.hpp b/include/osmium/handler/disk_store.hpp
index b8ab229..4e95573 100644
--- a/include/osmium/handler/disk_store.hpp
+++ b/include/osmium/handler/disk_store.hpp
@@ -57,7 +57,7 @@ namespace osmium {
*/
class DiskStore : public osmium::handler::Handler {
- typedef osmium::index::map::Map<unsigned_object_id_type, size_t> offset_index_type;
+ using offset_index_type = osmium::index::map::Map<unsigned_object_id_type, size_t>;
size_t m_offset = 0;
int m_data_fd;
diff --git a/include/osmium/handler/node_locations_for_ways.hpp b/include/osmium/handler/node_locations_for_ways.hpp
index f62a4db..e836397 100644
--- a/include/osmium/handler/node_locations_for_ways.hpp
+++ b/include/osmium/handler/node_locations_for_ways.hpp
@@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE.
*/
+#include <limits>
#include <type_traits>
#include <osmium/handler.hpp>
@@ -50,7 +51,7 @@ namespace osmium {
namespace handler {
- typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> dummy_type;
+ using dummy_type = osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location>;
/**
* Handler to retrieve locations from nodes and add them to ways.
@@ -69,8 +70,8 @@ namespace osmium {
public:
- typedef TStoragePosIDs index_pos_type;
- typedef TStorageNegIDs index_neg_type;
+ using index_pos_type = TStoragePosIDs;
+ using index_neg_type = TStorageNegIDs;
private:
@@ -80,6 +81,8 @@ namespace osmium {
/// Object that handles the actual storage of the node locations (with negative IDs).
TStorageNegIDs& m_storage_neg;
+ osmium::unsigned_object_id_type m_last_id{0};
+
bool m_ignore_errors {false};
bool m_must_sort {false};
@@ -115,7 +118,11 @@ namespace osmium {
* Store the location of the node in the storage.
*/
void node(const osmium::Node& node) {
- m_must_sort = true;
+ if (node.positive_id() < m_last_id) {
+ m_must_sort = true;
+ }
+ m_last_id = node.positive_id();
+
const osmium::object_id_type id = node.id();
if (id >= 0) {
m_storage_pos.set(static_cast<osmium::unsigned_object_id_type>( id), node.location());
@@ -144,6 +151,7 @@ namespace osmium {
m_storage_pos.sort();
m_storage_neg.sort();
m_must_sort = false;
+ m_last_id = std::numeric_limits<osmium::unsigned_object_id_type>::max();
}
bool error = false;
for (auto& node_ref : way.nodes()) {
diff --git a/include/osmium/handler/object_relations.hpp b/include/osmium/handler/object_relations.hpp
index 4afcf6a..279365d 100644
--- a/include/osmium/handler/object_relations.hpp
+++ b/include/osmium/handler/object_relations.hpp
@@ -52,7 +52,7 @@ namespace osmium {
*/
class ObjectRelations : public osmium::handler::Handler {
- typedef osmium::index::multimap::Multimap<unsigned_object_id_type, unsigned_object_id_type> index_type;
+ using index_type = osmium::index::multimap::Multimap<unsigned_object_id_type, unsigned_object_id_type>;
index_type& m_index_n2w;
index_type& m_index_n2r;
diff --git a/include/osmium/index/detail/mmap_vector_base.hpp b/include/osmium/index/detail/mmap_vector_base.hpp
index aadeef0..f84cc2e 100644
--- a/include/osmium/index/detail/mmap_vector_base.hpp
+++ b/include/osmium/index/detail/mmap_vector_base.hpp
@@ -33,10 +33,13 @@ DEALINGS IN THE SOFTWARE.
*/
+#include <algorithm>
+#include <cassert>
#include <cstddef>
#include <new> // IWYU pragma: keep
#include <stdexcept>
+#include <osmium/index/index.hpp>
#include <osmium/util/memory_mapping.hpp>
namespace osmium {
@@ -63,22 +66,26 @@ namespace osmium {
mmap_vector_base(int fd, size_t capacity, size_t size = 0) :
m_size(size),
m_mapping(capacity, osmium::util::MemoryMapping::mapping_mode::write_shared, fd) {
+ assert(size <= capacity);
+ std::fill(data() + size, data() + capacity, osmium::index::empty_value<T>());
+ shrink_to_fit();
}
explicit mmap_vector_base(size_t capacity = mmap_vector_size_increment) :
m_size(0),
m_mapping(capacity) {
+ std::fill_n(data(), capacity, osmium::index::empty_value<T>());
}
~mmap_vector_base() noexcept = default;
- typedef T value_type;
- typedef T& reference;
- typedef const T& const_reference;
- typedef T* pointer;
- typedef const T* const_pointer;
- typedef T* iterator;
- typedef const T* const_iterator;
+ using value_type = T;
+ using pointer = value_type*;
+ using const_pointer = const value_type*;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using iterator = value_type*;
+ using const_iterator = const value_type*;
void close() {
m_mapping.unmap();
@@ -105,6 +112,7 @@ namespace osmium {
}
T& operator[](size_t n) {
+ assert(n < m_size);
return data()[n];
}
@@ -120,7 +128,9 @@ namespace osmium {
}
void shrink_to_fit() {
- // XXX do something here
+ while (m_size > 0 && data()[m_size - 1] == osmium::index::empty_value<T>()) {
+ --m_size;
+ }
}
void push_back(const T& value) {
@@ -133,7 +143,9 @@ namespace osmium {
void reserve(size_t new_capacity) {
if (new_capacity > capacity()) {
+ size_t old_capacity = capacity();
m_mapping.resize(new_capacity);
+ std::fill(data() + old_capacity, data() + new_capacity, osmium::index::empty_value<T>());
}
}
@@ -141,9 +153,6 @@ namespace osmium {
if (new_size > capacity()) {
reserve(new_size + osmium::detail::mmap_vector_size_increment);
}
- if (new_size > size()) {
- new (data() + size()) T[new_size - size()];
- }
m_size = new_size;
}
diff --git a/include/osmium/index/detail/mmap_vector_file.hpp b/include/osmium/index/detail/mmap_vector_file.hpp
index 666a409..e077c60 100644
--- a/include/osmium/index/detail/mmap_vector_file.hpp
+++ b/include/osmium/index/detail/mmap_vector_file.hpp
@@ -33,6 +33,9 @@ DEALINGS IN THE SOFTWARE.
*/
+#include <algorithm>
+#include <string>
+
#include <osmium/index/detail/mmap_vector_base.hpp>
#include <osmium/index/detail/tmpfile.hpp>
#include <osmium/util/file.hpp>
@@ -48,6 +51,16 @@ namespace osmium {
template <typename T>
class mmap_vector_file : public mmap_vector_base<T> {
+ size_t filesize(int fd) const {
+ size_t size = osmium::util::file_size(fd);
+
+ if (size % sizeof(T) != 0) {
+ throw std::runtime_error("Index file has wrong size (must be multiple of " + std::to_string(sizeof(T)) + ").");
+ }
+
+ return size / sizeof(T);
+ }
+
public:
mmap_vector_file() :
@@ -59,8 +72,8 @@ namespace osmium {
explicit mmap_vector_file(int fd) :
mmap_vector_base<T>(
fd,
- osmium::util::file_size(fd) / sizeof(T),
- osmium::util::file_size(fd) / sizeof(T)) {
+ std::max(osmium::detail::mmap_vector_size_increment, filesize(fd)),
+ filesize(fd)) {
}
~mmap_vector_file() noexcept = default;
diff --git a/include/osmium/index/detail/vector_map.hpp b/include/osmium/index/detail/vector_map.hpp
index 0fa59dd..390a402 100644
--- a/include/osmium/index/detail/vector_map.hpp
+++ b/include/osmium/index/detail/vector_map.hpp
@@ -55,10 +55,10 @@ namespace osmium {
public:
- typedef TValue element_type;
- typedef TVector vector_type;
- typedef typename vector_type::iterator iterator;
- typedef typename vector_type::const_iterator const_iterator;
+ using element_type = TValue;
+ using vector_type = TVector;
+ using iterator = typename vector_type::iterator;
+ using const_iterator = typename vector_type::const_iterator;
VectorBasedDenseMap() :
m_vector() {
@@ -146,10 +146,10 @@ namespace osmium {
public:
- typedef typename std::pair<TId, TValue> element_type;
- typedef TVector<element_type> vector_type;
- typedef typename vector_type::iterator iterator;
- typedef typename vector_type::const_iterator const_iterator;
+ using element_type = typename std::pair<TId, TValue>;
+ using vector_type = TVector<element_type>;
+ using iterator = typename vector_type::iterator;
+ using const_iterator = typename vector_type::const_iterator;
private:
diff --git a/include/osmium/index/detail/vector_multimap.hpp b/include/osmium/index/detail/vector_multimap.hpp
index 35a4cc1..af2ec53 100644
--- a/include/osmium/index/detail/vector_multimap.hpp
+++ b/include/osmium/index/detail/vector_multimap.hpp
@@ -52,10 +52,10 @@ namespace osmium {
public:
- typedef typename std::pair<TId, TValue> element_type;
- typedef TVector<element_type> vector_type;
- typedef typename vector_type::iterator iterator;
- typedef typename vector_type::const_iterator const_iterator;
+ using element_type = typename std::pair<TId, TValue>;
+ using vector_type = TVector<element_type>;
+ using iterator = typename vector_type::iterator;
+ using const_iterator = typename vector_type::const_iterator;
private:
diff --git a/include/osmium/index/map.hpp b/include/osmium/index/map.hpp
index f90a895..c8f7598 100644
--- a/include/osmium/index/map.hpp
+++ b/include/osmium/index/map.hpp
@@ -98,10 +98,10 @@ namespace osmium {
public:
/// The "key" type, usually osmium::unsigned_object_id_type.
- typedef TId key_type;
+ using key_type = TId;
/// The "value" type, usually a Location or size_t.
- typedef TValue value_type;
+ using value_type = TValue;
Map() = default;
@@ -171,10 +171,10 @@ namespace osmium {
public:
- typedef TId id_type;
- typedef TValue value_type;
- typedef osmium::index::map::Map<id_type, value_type> map_type;
- typedef std::function<map_type*(const std::vector<std::string>&)> create_map_func;
+ using id_type = TId;
+ using value_type = TValue;
+ using map_type = osmium::index::map::Map<id_type, value_type>;
+ using create_map_func = std::function<map_type*(const std::vector<std::string>&)>;
private:
diff --git a/include/osmium/index/map/sparse_mem_map.hpp b/include/osmium/index/map/sparse_mem_map.hpp
index 41351c8..95f570b 100644
--- a/include/osmium/index/map/sparse_mem_map.hpp
+++ b/include/osmium/index/map/sparse_mem_map.hpp
@@ -98,7 +98,7 @@ namespace osmium {
}
void dump_as_list(const int fd) final {
- typedef typename std::map<TId, TValue>::value_type t;
+ using t = typename std::map<TId, TValue>::value_type;
std::vector<t> v;
v.reserve(m_elements.size());
std::copy(m_elements.cbegin(), m_elements.cend(), std::back_inserter(v));
diff --git a/include/osmium/index/multimap.hpp b/include/osmium/index/multimap.hpp
index de6aa1e..9361d6a 100644
--- a/include/osmium/index/multimap.hpp
+++ b/include/osmium/index/multimap.hpp
@@ -52,7 +52,7 @@ namespace osmium {
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;
+ using element_type = typename std::pair<TId, TValue>;
Multimap(const Multimap&) = delete;
Multimap& operator=(const Multimap&) = delete;
@@ -65,10 +65,10 @@ namespace osmium {
public:
/// The "key" type, usually osmium::unsigned_object_id_type.
- typedef TId key_type;
+ using key_type = TId;
/// The "value" type, usually a Location or size_t.
- typedef TValue value_type;
+ using value_type = TValue;
Multimap() = default;
@@ -77,7 +77,7 @@ namespace osmium {
/// Set the field with id to value.
virtual void set(const TId id, const TValue value) = 0;
- typedef element_type* iterator;
+ using iterator = element_type*;
// virtual std::pair<iterator, iterator> get_all(const TId id) const = 0;
diff --git a/include/osmium/index/multimap/hybrid.hpp b/include/osmium/index/multimap/hybrid.hpp
index ba94302..06a5f6e 100644
--- a/include/osmium/index/multimap/hybrid.hpp
+++ b/include/osmium/index/multimap/hybrid.hpp
@@ -50,10 +50,10 @@ namespace osmium {
template <typename TId, typename TValue>
class HybridIterator {
- typedef SparseMemArray<TId, TValue> main_map_type;
- typedef SparseMemMultimap<TId, TValue> extra_map_type;
+ using main_map_type = SparseMemArray<TId, TValue>;
+ using extra_map_type = SparseMemMultimap<TId, TValue>;
- typedef typename std::pair<TId, TValue> element_type;
+ using element_type = typename std::pair<TId, TValue>;
typename main_map_type::iterator m_begin_main;
typename main_map_type::iterator m_end_main;
@@ -120,16 +120,16 @@ namespace osmium {
template <typename TId, typename TValue>
class Hybrid : public Multimap<TId, TValue> {
- typedef SparseMemArray<TId, TValue> main_map_type;
- typedef SparseMemMultimap<TId, TValue> extra_map_type;
+ using main_map_type = SparseMemArray<TId, TValue>;
+ using extra_map_type = SparseMemMultimap<TId, TValue>;
main_map_type m_main;
extra_map_type m_extra;
public:
- typedef HybridIterator<TId, TValue> iterator;
- typedef const HybridIterator<TId, TValue> const_iterator;
+ using iterator = HybridIterator<TId, TValue>;
+ using const_iterator = const HybridIterator<TId, TValue>;
Hybrid() :
m_main(),
diff --git a/include/osmium/index/multimap/sparse_mem_multimap.hpp b/include/osmium/index/multimap/sparse_mem_multimap.hpp
index 0859fca..7a67cc7 100644
--- a/include/osmium/index/multimap/sparse_mem_multimap.hpp
+++ b/include/osmium/index/multimap/sparse_mem_multimap.hpp
@@ -63,12 +63,11 @@ namespace osmium {
public:
- typedef typename std::multimap<const TId, TValue> collection_type;
- typedef typename collection_type::iterator iterator;
- typedef typename collection_type::const_iterator const_iterator;
- typedef typename collection_type::value_type value_type;
-
- typedef typename std::pair<TId, TValue> element_type;
+ using collection_type = typename std::multimap<const TId, TValue>;
+ using iterator = typename collection_type::iterator;
+ using const_iterator = typename collection_type::const_iterator;
+ using value_type = typename collection_type::value_type;
+ using element_type = typename std::pair<TId, TValue>;
private:
diff --git a/include/osmium/io/compression.hpp b/include/osmium/io/compression.hpp
index 7d95661..fc14e26 100644
--- a/include/osmium/io/compression.hpp
+++ b/include/osmium/io/compression.hpp
@@ -118,16 +118,16 @@ namespace osmium {
public:
- typedef std::function<osmium::io::Compressor*(int, fsync)> create_compressor_type;
- typedef std::function<osmium::io::Decompressor*(int)> create_decompressor_type_fd;
- typedef std::function<osmium::io::Decompressor*(const char*, size_t)> create_decompressor_type_buffer;
+ using create_compressor_type = std::function<osmium::io::Compressor*(int, fsync)>;
+ using create_decompressor_type_fd = std::function<osmium::io::Decompressor*(int)>;
+ using create_decompressor_type_buffer = std::function<osmium::io::Decompressor*(const char*, size_t)>;
private:
- typedef std::map<const osmium::io::file_compression,
- std::tuple<create_compressor_type,
- create_decompressor_type_fd,
- create_decompressor_type_buffer>> compression_map_type;
+ using compression_map_type = std::map<const osmium::io::file_compression,
+ std::tuple<create_compressor_type,
+ create_decompressor_type_fd,
+ create_decompressor_type_buffer>>;
compression_map_type m_callbacks;
diff --git a/include/osmium/io/detail/debug_output_format.hpp b/include/osmium/io/detail/debug_output_format.hpp
index 3f7537e..b45173c 100644
--- a/include/osmium/io/detail/debug_output_format.hpp
+++ b/include/osmium/io/detail/debug_output_format.hpp
@@ -44,12 +44,15 @@ DEALINGS IN THE SOFTWARE.
#include <thread>
#include <utility>
+#include <boost/crc.hpp>
+
#include <osmium/io/detail/output_format.hpp>
#include <osmium/io/file_format.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/memory/collection.hpp>
#include <osmium/osm/box.hpp>
#include <osmium/osm/changeset.hpp>
+#include <osmium/osm/crc.hpp>
#include <osmium/osm/item_type.hpp>
#include <osmium/osm/location.hpp>
#include <osmium/osm/node.hpp>
@@ -90,6 +93,9 @@ namespace osmium {
/// Output with ANSI colors?
bool use_color;
+ /// Add CRC32 checksum to each object?
+ bool add_crc32;
+
};
/**
@@ -237,6 +243,21 @@ namespace osmium {
*m_out += '\n';
}
+ template <typename T>
+ void write_crc32(const T& object) {
+ write_fieldname("crc32");
+ osmium::CRC<boost::crc_32_type> crc32;
+ crc32.update(object);
+ output_formatted(" %x\n", crc32().checksum());
+ }
+
+ void write_crc32(const osmium::Changeset& object) {
+ write_fieldname("crc32");
+ osmium::CRC<boost::crc_32_type> crc32;
+ crc32.update(object);
+ output_formatted(" %x\n", crc32().checksum());
+ }
+
public:
DebugOutputBlock(osmium::memory::Buffer&& buffer, const debug_output_options& options) :
@@ -274,6 +295,10 @@ namespace osmium {
write_tags(node.tags());
+ if (m_options.add_crc32) {
+ write_crc32(node);
+ }
+
*m_out += '\n';
}
@@ -306,6 +331,10 @@ namespace osmium {
*m_out += '\n';
}
+ if (m_options.add_crc32) {
+ write_crc32(way);
+ }
+
*m_out += '\n';
}
@@ -328,6 +357,10 @@ namespace osmium {
*m_out += '\n';
}
+ if (m_options.add_crc32) {
+ write_crc32(relation);
+ }
+
*m_out += '\n';
}
@@ -386,6 +419,10 @@ namespace osmium {
}
}
+ if (m_options.add_crc32) {
+ write_crc32(changeset);
+ }
+
*m_out += '\n';
}
@@ -414,6 +451,7 @@ namespace osmium {
m_options() {
m_options.add_metadata = file.is_not_false("add_metadata");
m_options.use_color = file.is_true("color");
+ m_options.add_crc32 = file.is_true("add_crc32");
}
DebugOutputFormat(const DebugOutputFormat&) = delete;
diff --git a/include/osmium/io/detail/input_format.hpp b/include/osmium/io/detail/input_format.hpp
index 0d78851..e04e57f 100644
--- a/include/osmium/io/detail/input_format.hpp
+++ b/include/osmium/io/detail/input_format.hpp
@@ -154,18 +154,14 @@ namespace osmium {
public:
- typedef std::function<
- std::unique_ptr<Parser>(
- future_string_queue_type&,
- future_buffer_queue_type&,
- std::promise<osmium::io::Header>& header_promise,
- osmium::osm_entity_bits::type read_which_entities
- )
- > create_parser_type;
+ using create_parser_type = std::function<std::unique_ptr<Parser>(future_string_queue_type&,
+ future_buffer_queue_type&,
+ std::promise<osmium::io::Header>& header_promise,
+ osmium::osm_entity_bits::type read_which_entities)>;
private:
- typedef std::map<osmium::io::file_format, create_parser_type> map_type;
+ using map_type = std::map<osmium::io::file_format, create_parser_type>;
map_type m_callbacks;
diff --git a/include/osmium/io/detail/opl_output_format.hpp b/include/osmium/io/detail/opl_output_format.hpp
index 3fedf7e..427c49e 100644
--- a/include/osmium/io/detail/opl_output_format.hpp
+++ b/include/osmium/io/detail/opl_output_format.hpp
@@ -74,6 +74,9 @@ namespace osmium {
/// Should metadata of objects be added?
bool add_metadata;
+ /// Should node locations be added to ways?
+ bool locations_on_ways;
+
};
/**
@@ -160,13 +163,33 @@ namespace osmium {
*m_out += " N";
bool first = true;
- for (const auto& node_ref : way.nodes()) {
- if (first) {
- first = false;
- } else {
- *m_out += ',';
+ if (m_options.locations_on_ways) {
+ for (const auto& node_ref : way.nodes()) {
+ if (first) {
+ first = false;
+ } else {
+ *m_out += ',';
+ }
+ output_formatted("n%" PRId64 "x", node_ref.ref());
+ if (node_ref.location()) {
+ output_formatted("%.7fy%.7f",
+ node_ref.ref(),
+ node_ref.location().lon_without_check(),
+ node_ref.location().lat_without_check()
+ );
+ } else {
+ *m_out += 'y';
+ }
+ }
+ } else {
+ for (const auto& node_ref : way.nodes()) {
+ if (first) {
+ first = false;
+ } else {
+ *m_out += ',';
+ }
+ output_formatted("n%" PRId64, node_ref.ref());
}
- output_formatted("n%" PRId64, node_ref.ref());
}
*m_out += '\n';
}
@@ -226,7 +249,8 @@ namespace osmium {
OPLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) :
OutputFormat(output_queue),
m_options() {
- m_options.add_metadata = file.is_not_false("add_metadata");
+ m_options.add_metadata = file.is_not_false("add_metadata");
+ m_options.locations_on_ways = file.is_true("locations_on_ways");
}
OPLOutputFormat(const OPLOutputFormat&) = delete;
diff --git a/include/osmium/io/detail/output_format.hpp b/include/osmium/io/detail/output_format.hpp
index 34ca82f..c741218 100644
--- a/include/osmium/io/detail/output_format.hpp
+++ b/include/osmium/io/detail/output_format.hpp
@@ -133,11 +133,11 @@ namespace osmium {
public:
- typedef std::function<osmium::io::detail::OutputFormat*(const osmium::io::File&, future_string_queue_type&)> create_output_type;
+ using create_output_type = std::function<osmium::io::detail::OutputFormat*(const osmium::io::File&, future_string_queue_type&)>;
private:
- typedef std::map<osmium::io::file_format, create_output_type> map_type;
+ using map_type = std::map<osmium::io::file_format, create_output_type>;
map_type m_callbacks;
diff --git a/include/osmium/io/detail/pbf_decoder.hpp b/include/osmium/io/detail/pbf_decoder.hpp
index 43de1c1..1c0d2eb 100644
--- a/include/osmium/io/detail/pbf_decoder.hpp
+++ b/include/osmium/io/detail/pbf_decoder.hpp
@@ -293,6 +293,8 @@ namespace osmium {
kv_type keys;
kv_type vals;
std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> refs;
+ std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> lats;
+ std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> lons;
osm_string_len_type user = { "", 0 };
@@ -314,6 +316,12 @@ namespace osmium {
case OSMFormat::Way::packed_sint64_refs:
refs = pbf_way.get_packed_sint64();
break;
+ case OSMFormat::Way::packed_sint64_lat:
+ lats = pbf_way.get_packed_sint64();
+ break;
+ case OSMFormat::Way::packed_sint64_lon:
+ lons = pbf_way.get_packed_sint64();
+ break;
default:
pbf_way.skip();
}
@@ -324,8 +332,20 @@ namespace osmium {
if (refs.first != refs.second) {
osmium::builder::WayNodeListBuilder wnl_builder(m_buffer, &builder);
osmium::util::DeltaDecode<int64_t> ref;
- while (refs.first != refs.second) {
- wnl_builder.add_node_ref(ref.update(*refs.first++));
+ if (lats.first == lats.second) {
+ while (refs.first != refs.second) {
+ wnl_builder.add_node_ref(ref.update(*refs.first++));
+ }
+ } else {
+ osmium::util::DeltaDecode<int64_t> lon;
+ osmium::util::DeltaDecode<int64_t> lat;
+ while (refs.first != refs.second && lons.first != lons.second && lats.first != lats.second) {
+ wnl_builder.add_node_ref(
+ ref.update(*refs.first++),
+ osmium::Location{convert_pbf_coordinate(lon.update(*lons.first++)),
+ convert_pbf_coordinate(lat.update(*lats.first++))}
+ );
+ }
}
}
diff --git a/include/osmium/io/detail/pbf_output_format.hpp b/include/osmium/io/detail/pbf_output_format.hpp
index 878d7b4..5046087 100644
--- a/include/osmium/io/detail/pbf_output_format.hpp
+++ b/include/osmium/io/detail/pbf_output_format.hpp
@@ -101,6 +101,9 @@ namespace osmium {
/// Should the visible flag be added to all OSM objects?
bool add_visible_flag;
+ /// Should node locations be added to ways?
+ bool locations_on_ways;
+
};
/**
@@ -483,6 +486,7 @@ namespace osmium {
m_options.add_metadata = file.is_not_false("pbf_add_metadata") && file.is_not_false("add_metadata");
m_options.add_historical_information_flag = file.has_multiple_object_versions();
m_options.add_visible_flag = file.has_multiple_object_versions();
+ m_options.locations_on_ways = file.is_true("locations_on_ways");
}
PBFOutputFormat(const PBFOutputFormat&) = delete;
@@ -514,6 +518,10 @@ namespace osmium {
pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "HistoricalInformation");
}
+ if (m_options.locations_on_ways) {
+ pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_optional_features, "LocationsOnWays");
+ }
+
pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_writingprogram, header.get("generator"));
std::string osmosis_replication_timestamp = header.get("osmosis_replication_timestamp");
@@ -571,15 +579,34 @@ namespace osmium {
pbf_way.add_int64(OSMFormat::Way::required_int64_id, way.id());
add_meta(way, pbf_way);
+ const auto& nodes = way.nodes();
+
static auto map_node_ref = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> osmium::object_id_type {
return node_ref->ref();
};
- typedef osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_ref), osmium::object_id_type> it_type;
-
- const auto& nodes = way.nodes();
+ using it_type = osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_ref), osmium::object_id_type>;
it_type first { nodes.cbegin(), nodes.cend(), map_node_ref };
it_type last { nodes.cend(), nodes.cend(), map_node_ref };
pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_refs, first, last);
+
+ if (m_options.locations_on_ways) {
+ static auto map_node_x = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> int64_t {
+ return lonlat2int(node_ref->location().lon_without_check());
+ };
+ static auto map_node_y = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> int64_t {
+ return lonlat2int(node_ref->location().lat_without_check());
+ };
+ using it_type_x = osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_x), int64_t>;
+ using it_type_y = osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_y), int64_t>;
+
+ it_type_x first_x { nodes.cbegin(), nodes.cend(), map_node_x };
+ it_type_x last_x { nodes.cend(), nodes.cend(), map_node_x };
+ pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_lon, first_x, last_x);
+
+ it_type_y first_y { nodes.cbegin(), nodes.cend(), map_node_y };
+ it_type_y last_y { nodes.cend(), nodes.cend(), map_node_y };
+ pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_lat, first_y, last_y);
+ }
}
void relation(const osmium::Relation& relation) {
@@ -599,7 +626,7 @@ namespace osmium {
static auto map_member_ref = [](osmium::RelationMemberList::const_iterator member) noexcept -> osmium::object_id_type {
return member->ref();
};
- typedef osmium::util::DeltaEncodeIterator<osmium::RelationMemberList::const_iterator, decltype(map_member_ref), osmium::object_id_type> it_type;
+ using it_type = osmium::util::DeltaEncodeIterator<osmium::RelationMemberList::const_iterator, decltype(map_member_ref), osmium::object_id_type>;
const auto& members = relation.members();
it_type first { members.cbegin(), members.cend(), map_member_ref };
it_type last { members.cend(), members.cend(), map_member_ref };
diff --git a/include/osmium/io/detail/protobuf_tags.hpp b/include/osmium/io/detail/protobuf_tags.hpp
index bdaabba..3eb7902 100644
--- a/include/osmium/io/detail/protobuf_tags.hpp
+++ b/include/osmium/io/detail/protobuf_tags.hpp
@@ -146,7 +146,9 @@ namespace osmium {
packed_uint32_keys = 2,
packed_uint32_vals = 3,
optional_Info_info = 4,
- packed_sint64_refs = 8
+ packed_sint64_refs = 8,
+ packed_sint64_lat = 9,
+ packed_sint64_lon = 10
};
enum class Relation : protozero::pbf_tag_type {
diff --git a/include/osmium/io/detail/queue_util.hpp b/include/osmium/io/detail/queue_util.hpp
index af84544..bc47020 100644
--- a/include/osmium/io/detail/queue_util.hpp
+++ b/include/osmium/io/detail/queue_util.hpp
@@ -47,6 +47,9 @@ namespace osmium {
namespace detail {
+ template <typename T>
+ using future_queue_type = osmium::thread::Queue<std::future<T>>;
+
/**
* This type of queue contains buffers with OSM data in them.
* The "end of file" is marked by an invalid Buffer.
@@ -54,7 +57,7 @@ namespace osmium {
* transport exceptions. The future also helps with keeping the
* data in order.
*/
- using future_buffer_queue_type = osmium::thread::Queue<std::future<osmium::memory::Buffer>>;
+ using future_buffer_queue_type = future_queue_type<osmium::memory::Buffer>;
/**
* This type of queue contains OSM file data in the form it is
@@ -71,24 +74,24 @@ namespace osmium {
* transport exceptions. The future also helps with keeping the
* data in order.
*/
- using future_string_queue_type = osmium::thread::Queue<std::future<std::string>>;
+ using future_string_queue_type = future_queue_type<std::string>;
template <typename T>
- inline void add_to_queue(osmium::thread::Queue<std::future<T>>& queue, T&& data) {
+ inline void add_to_queue(future_queue_type<T>& queue, T&& data) {
std::promise<T> promise;
queue.push(promise.get_future());
promise.set_value(std::forward<T>(data));
}
template <typename T>
- inline void add_to_queue(osmium::thread::Queue<std::future<T>>& queue, std::exception_ptr&& exception) {
+ inline void add_to_queue(future_queue_type<T>& queue, std::exception_ptr&& exception) {
std::promise<T> promise;
queue.push(promise.get_future());
promise.set_exception(std::move(exception));
}
template <typename T>
- inline void add_end_of_data_to_queue(osmium::thread::Queue<std::future<T>>& queue) {
+ inline void add_end_of_data_to_queue(future_queue_type<T>& queue) {
add_to_queue<T>(queue, T{});
}
@@ -103,14 +106,12 @@ namespace osmium {
template <typename T>
class queue_wrapper {
- using queue_type = osmium::thread::Queue<std::future<T>>;
-
- queue_type& m_queue;
+ future_queue_type<T>& m_queue;
bool m_has_reached_end_of_data;
public:
- explicit queue_wrapper(queue_type& queue) :
+ explicit queue_wrapper(future_queue_type<T>& queue) :
m_queue(queue),
m_has_reached_end_of_data(false) {
}
diff --git a/include/osmium/io/detail/string_table.hpp b/include/osmium/io/detail/string_table.hpp
index 55c6226..b989530 100644
--- a/include/osmium/io/detail/string_table.hpp
+++ b/include/osmium/io/detail/string_table.hpp
@@ -109,15 +109,22 @@ namespace osmium {
return m_chunks.front().c_str() + chunk_len;
}
- class const_iterator : public std::iterator<std::forward_iterator_tag, const char*> {
+ class const_iterator {
+
+ using it_type = std::list<std::string>::const_iterator;
- typedef std::list<std::string>::const_iterator it_type;
it_type m_it;
const it_type m_last;
const char* m_pos;
public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = const char*;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
const_iterator(it_type it, it_type last) :
m_it(it),
m_last(last),
@@ -192,7 +199,7 @@ namespace osmium {
struct StrComp {
bool operator()(const char* lhs, const char* rhs) const {
- return strcmp(lhs, rhs) < 0;
+ return std::strcmp(lhs, rhs) < 0;
}
}; // struct StrComp
diff --git a/include/osmium/io/detail/xml_input_format.hpp b/include/osmium/io/detail/xml_input_format.hpp
index 8cb5efe..0233917 100644
--- a/include/osmium/io/detail/xml_input_format.hpp
+++ b/include/osmium/io/detail/xml_input_format.hpp
@@ -192,6 +192,17 @@ namespace osmium {
static_cast<XMLParser*>(data)->characters(text, len);
}
+ // This handler is called when there are any XML entities
+ // declared in the OSM file. Entities are normally not used,
+ // but they can be misused. See
+ // https://en.wikipedia.org/wiki/Billion_laughs
+ // The handler will just throw an error.
+ 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");
+ }
+
public:
explicit ExpatXMLParser(T* callback_object) :
@@ -202,6 +213,7 @@ namespace osmium {
XML_SetUserData(m_parser, callback_object);
XML_SetElementHandler(m_parser, start_element_wrapper, end_element_wrapper);
XML_SetCharacterDataHandler(m_parser, character_data_wrapper);
+ XML_SetEntityDeclHandler(m_parser, entity_declaration_handler);
}
ExpatXMLParser(const ExpatXMLParser&) = delete;
@@ -240,11 +252,11 @@ namespace osmium {
osmium::Location location;
check_attributes(attrs, [&location, &user, &object](const XML_Char* name, const XML_Char* value) {
- if (!strcmp(name, "lon")) {
+ if (!std::strcmp(name, "lon")) {
location.set_lon(std::atof(value)); // XXX doesn't detect garbage after the number
- } else if (!strcmp(name, "lat")) {
+ } else if (!std::strcmp(name, "lat")) {
location.set_lat(std::atof(value)); // XXX doesn't detect garbage after the number
- } else if (!strcmp(name, "user")) {
+ } else if (!std::strcmp(name, "user")) {
user = value;
} else {
object.set_attribute(name, value);
@@ -265,15 +277,15 @@ namespace osmium {
osmium::Location min;
osmium::Location max;
check_attributes(attrs, [&min, &max, &user, &new_changeset](const XML_Char* name, const XML_Char* value) {
- if (!strcmp(name, "min_lon")) {
+ if (!std::strcmp(name, "min_lon")) {
min.set_lon(atof(value));
- } else if (!strcmp(name, "min_lat")) {
+ } else if (!std::strcmp(name, "min_lat")) {
min.set_lat(atof(value));
- } else if (!strcmp(name, "max_lon")) {
+ } else if (!std::strcmp(name, "max_lon")) {
max.set_lon(atof(value));
- } else if (!strcmp(name, "max_lat")) {
+ } else if (!std::strcmp(name, "max_lat")) {
max.set_lat(atof(value));
- } else if (!strcmp(name, "user")) {
+ } else if (!std::strcmp(name, "user")) {
user = value;
} else {
new_changeset.set_attribute(name, value);
@@ -309,17 +321,17 @@ namespace osmium {
void start_element(const XML_Char* element, const XML_Char** attrs) {
switch (m_context) {
case context::root:
- if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) {
- if (!strcmp(element, "osmChange")) {
+ if (!std::strcmp(element, "osm") || !std::strcmp(element, "osmChange")) {
+ if (!std::strcmp(element, "osmChange")) {
m_header.set_has_multiple_object_versions(true);
}
check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) {
- if (!strcmp(name, "version")) {
+ if (!std::strcmp(name, "version")) {
m_header.set("version", value);
- if (strcmp(value, "0.6")) {
+ if (std::strcmp(value, "0.6")) {
throw osmium::format_version_error(value);
}
- } else if (!strcmp(name, "generator")) {
+ } else if (!std::strcmp(name, "generator")) {
m_header.set("generator", value);
}
});
@@ -333,7 +345,7 @@ namespace osmium {
break;
case context::top:
assert(!m_tl_builder);
- if (!strcmp(element, "node")) {
+ if (!std::strcmp(element, "node")) {
mark_header_as_done();
if (read_types() & osmium::osm_entity_bits::node) {
m_node_builder = std::unique_ptr<osmium::builder::NodeBuilder>(new osmium::builder::NodeBuilder(m_buffer));
@@ -342,7 +354,7 @@ namespace osmium {
} else {
m_context = context::ignored_node;
}
- } else if (!strcmp(element, "way")) {
+ } else if (!std::strcmp(element, "way")) {
mark_header_as_done();
if (read_types() & osmium::osm_entity_bits::way) {
m_way_builder = std::unique_ptr<osmium::builder::WayBuilder>(new osmium::builder::WayBuilder(m_buffer));
@@ -351,7 +363,7 @@ namespace osmium {
} else {
m_context = context::ignored_way;
}
- } else if (!strcmp(element, "relation")) {
+ } else if (!std::strcmp(element, "relation")) {
mark_header_as_done();
if (read_types() & osmium::osm_entity_bits::relation) {
m_relation_builder = std::unique_ptr<osmium::builder::RelationBuilder>(new osmium::builder::RelationBuilder(m_buffer));
@@ -360,7 +372,7 @@ namespace osmium {
} else {
m_context = context::ignored_relation;
}
- } else if (!strcmp(element, "changeset")) {
+ } else if (!std::strcmp(element, "changeset")) {
mark_header_as_done();
if (read_types() & osmium::osm_entity_bits::changeset) {
m_changeset_builder = std::unique_ptr<osmium::builder::ChangesetBuilder>(new osmium::builder::ChangesetBuilder(m_buffer));
@@ -369,50 +381,56 @@ namespace osmium {
} else {
m_context = context::ignored_changeset;
}
- } else if (!strcmp(element, "bounds")) {
+ } else if (!std::strcmp(element, "bounds")) {
osmium::Location min;
osmium::Location max;
check_attributes(attrs, [&min, &max](const XML_Char* name, const XML_Char* value) {
- if (!strcmp(name, "minlon")) {
+ if (!std::strcmp(name, "minlon")) {
min.set_lon(atof(value));
- } else if (!strcmp(name, "minlat")) {
+ } else if (!std::strcmp(name, "minlat")) {
min.set_lat(atof(value));
- } else if (!strcmp(name, "maxlon")) {
+ } else if (!std::strcmp(name, "maxlon")) {
max.set_lon(atof(value));
- } else if (!strcmp(name, "maxlat")) {
+ } else if (!std::strcmp(name, "maxlat")) {
max.set_lat(atof(value));
}
});
osmium::Box box;
box.extend(min).extend(max);
m_header.add_box(box);
- } else if (!strcmp(element, "delete")) {
+ } else if (!std::strcmp(element, "delete")) {
m_in_delete_section = true;
}
break;
case context::node:
m_last_context = context::node;
m_context = context::in_object;
- if (!strcmp(element, "tag")) {
+ if (!std::strcmp(element, "tag")) {
get_tag(m_node_builder.get(), attrs);
}
break;
case context::way:
m_last_context = context::way;
m_context = context::in_object;
- if (!strcmp(element, "nd")) {
+ if (!std::strcmp(element, "nd")) {
m_tl_builder.reset();
if (!m_wnl_builder) {
m_wnl_builder = std::unique_ptr<osmium::builder::WayNodeListBuilder>(new osmium::builder::WayNodeListBuilder(m_buffer, m_way_builder.get()));
}
- check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) {
- if (!strcmp(name, "ref")) {
- m_wnl_builder->add_node_ref(osmium::string_to_object_id(value));
+ NodeRef nr;
+ check_attributes(attrs, [this, &nr](const XML_Char* name, const XML_Char* value) {
+ if (!std::strcmp(name, "ref")) {
+ nr.set_ref(osmium::string_to_object_id(value));
+ } else if (!std::strcmp(name, "lon")) {
+ nr.location().set_lon(std::atof(value)); // XXX doesn't detect garbage after the number
+ } else if (!std::strcmp(name, "lat")) {
+ nr.location().set_lat(std::atof(value)); // XXX doesn't detect garbage after the number
}
});
- } else if (!strcmp(element, "tag")) {
+ m_wnl_builder->add_node_ref(nr);
+ } else if (!std::strcmp(element, "tag")) {
m_wnl_builder.reset();
get_tag(m_way_builder.get(), attrs);
}
@@ -420,7 +438,7 @@ namespace osmium {
case context::relation:
m_last_context = context::relation;
m_context = context::in_object;
- if (!strcmp(element, "member")) {
+ if (!std::strcmp(element, "member")) {
m_tl_builder.reset();
if (!m_rml_builder) {
@@ -431,11 +449,11 @@ namespace osmium {
object_id_type ref = 0;
const char* role = "";
check_attributes(attrs, [&type, &ref, &role](const XML_Char* name, const XML_Char* value) {
- if (!strcmp(name, "type")) {
+ if (!std::strcmp(name, "type")) {
type = char_to_item_type(value[0]);
- } else if (!strcmp(name, "ref")) {
+ } else if (!std::strcmp(name, "ref")) {
ref = osmium::string_to_object_id(value);
- } else if (!strcmp(name, "role")) {
+ } else if (!std::strcmp(name, "role")) {
role = static_cast<const char*>(value);
}
});
@@ -446,37 +464,37 @@ namespace osmium {
throw osmium::xml_error("Missing ref on relation member");
}
m_rml_builder->add_member(type, ref, role);
- } else if (!strcmp(element, "tag")) {
+ } else if (!std::strcmp(element, "tag")) {
m_rml_builder.reset();
get_tag(m_relation_builder.get(), attrs);
}
break;
case context::changeset:
m_last_context = context::changeset;
- if (!strcmp(element, "discussion")) {
+ if (!std::strcmp(element, "discussion")) {
m_context = context::discussion;
m_tl_builder.reset();
if (!m_changeset_discussion_builder) {
m_changeset_discussion_builder = std::unique_ptr<osmium::builder::ChangesetDiscussionBuilder>(new osmium::builder::ChangesetDiscussionBuilder(m_buffer, m_changeset_builder.get()));
}
- } else if (!strcmp(element, "tag")) {
+ } else if (!std::strcmp(element, "tag")) {
m_context = context::in_object;
m_changeset_discussion_builder.reset();
get_tag(m_changeset_builder.get(), attrs);
}
break;
case context::discussion:
- if (!strcmp(element, "comment")) {
+ if (!std::strcmp(element, "comment")) {
m_context = context::comment;
osmium::Timestamp date;
osmium::user_id_type uid = 0;
const char* user = "";
check_attributes(attrs, [&date, &uid, &user](const XML_Char* name, const XML_Char* value) {
- if (!strcmp(name, "date")) {
+ if (!std::strcmp(name, "date")) {
date = osmium::Timestamp(value);
- } else if (!strcmp(name, "uid")) {
+ } else if (!std::strcmp(name, "uid")) {
uid = osmium::string_to_user_id(value);
- } else if (!strcmp(name, "user")) {
+ } else if (!std::strcmp(name, "user")) {
user = static_cast<const char*>(value);
}
});
@@ -484,7 +502,7 @@ namespace osmium {
}
break;
case context::comment:
- if (!strcmp(element, "text")) {
+ if (!std::strcmp(element, "text")) {
m_context = context::comment_text;
}
break;
@@ -510,15 +528,15 @@ namespace osmium {
assert(false); // should never be here
break;
case context::top:
- if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) {
+ if (!std::strcmp(element, "osm") || !std::strcmp(element, "osmChange")) {
mark_header_as_done();
m_context = context::root;
- } else if (!strcmp(element, "delete")) {
+ } else if (!std::strcmp(element, "delete")) {
m_in_delete_section = false;
}
break;
case context::node:
- assert(!strcmp(element, "node"));
+ assert(!std::strcmp(element, "node"));
m_tl_builder.reset();
m_node_builder.reset();
m_buffer.commit();
@@ -526,7 +544,7 @@ namespace osmium {
flush_buffer();
break;
case context::way:
- assert(!strcmp(element, "way"));
+ assert(!std::strcmp(element, "way"));
m_tl_builder.reset();
m_wnl_builder.reset();
m_way_builder.reset();
@@ -535,7 +553,7 @@ namespace osmium {
flush_buffer();
break;
case context::relation:
- assert(!strcmp(element, "relation"));
+ assert(!std::strcmp(element, "relation"));
m_tl_builder.reset();
m_rml_builder.reset();
m_relation_builder.reset();
@@ -544,7 +562,7 @@ namespace osmium {
flush_buffer();
break;
case context::changeset:
- assert(!strcmp(element, "changeset"));
+ assert(!std::strcmp(element, "changeset"));
m_tl_builder.reset();
m_changeset_discussion_builder.reset();
m_changeset_builder.reset();
@@ -553,15 +571,15 @@ namespace osmium {
flush_buffer();
break;
case context::discussion:
- assert(!strcmp(element, "discussion"));
+ assert(!std::strcmp(element, "discussion"));
m_context = context::changeset;
break;
case context::comment:
- assert(!strcmp(element, "comment"));
+ assert(!std::strcmp(element, "comment"));
m_context = context::discussion;
break;
case context::comment_text:
- assert(!strcmp(element, "text"));
+ assert(!std::strcmp(element, "text"));
m_context = context::comment;
m_changeset_discussion_builder->add_comment_text(m_comment_text);
break;
@@ -569,22 +587,22 @@ namespace osmium {
m_context = m_last_context;
break;
case context::ignored_node:
- if (!strcmp(element, "node")) {
+ if (!std::strcmp(element, "node")) {
m_context = context::top;
}
break;
case context::ignored_way:
- if (!strcmp(element, "way")) {
+ if (!std::strcmp(element, "way")) {
m_context = context::top;
}
break;
case context::ignored_relation:
- if (!strcmp(element, "relation")) {
+ if (!std::strcmp(element, "relation")) {
m_context = context::top;
}
break;
case context::ignored_changeset:
- if (!strcmp(element, "changeset")) {
+ if (!std::strcmp(element, "changeset")) {
m_context = context::top;
}
break;
diff --git a/include/osmium/io/detail/xml_output_format.hpp b/include/osmium/io/detail/xml_output_format.hpp
index 09bd6b3..ee58457 100644
--- a/include/osmium/io/detail/xml_output_format.hpp
+++ b/include/osmium/io/detail/xml_output_format.hpp
@@ -86,6 +86,8 @@ namespace osmium {
*/
bool use_change_ops;
+ /// Should node locations be added to ways?
+ bool locations_on_ways;
};
class XMLOutputBlock : public OutputBlock {
@@ -287,9 +289,24 @@ namespace osmium {
*m_out += ">\n";
- for (const auto& node_ref : way.nodes()) {
- write_prefix();
- output_formatted(" <nd ref=\"%" PRId64 "\"/>\n", node_ref.ref());
+ if (m_options.locations_on_ways) {
+ for (const auto& node_ref : way.nodes()) {
+ write_prefix();
+ output_formatted(" <nd ref=\"%" PRId64 "\"", node_ref.ref());
+ if (node_ref.location()) {
+ *m_out += " lat=\"";
+ osmium::util::double2string(std::back_inserter(*m_out), node_ref.location().lat_without_check(), 7);
+ *m_out += "\" lon=\"";
+ osmium::util::double2string(std::back_inserter(*m_out), node_ref.location().lon_without_check(), 7);
+ *m_out += "\"";
+ }
+ *m_out += "/>\n";
+ }
+ } else {
+ for (const auto& node_ref : way.nodes()) {
+ write_prefix();
+ output_formatted(" <nd ref=\"%" PRId64 "\"/>\n", node_ref.ref());
+ }
}
write_tags(way.tags(), prefix_spaces());
@@ -394,9 +411,10 @@ namespace osmium {
XMLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) :
OutputFormat(output_queue),
m_options() {
- m_options.add_metadata = file.is_not_false("add_metadata");
- m_options.use_change_ops = file.is_true("xml_change_format");
- m_options.add_visible_flag = (file.has_multiple_object_versions() || file.is_true("force_visible_flag")) && !m_options.use_change_ops;
+ m_options.add_metadata = file.is_not_false("add_metadata");
+ m_options.use_change_ops = file.is_true("xml_change_format");
+ m_options.add_visible_flag = (file.has_multiple_object_versions() || file.is_true("force_visible_flag")) && !m_options.use_change_ops;
+ m_options.locations_on_ways = file.is_true("locations_on_ways");
}
XMLOutputFormat(const XMLOutputFormat&) = delete;
diff --git a/include/osmium/io/input_iterator.hpp b/include/osmium/io/input_iterator.hpp
index 8be9759..a66f355 100644
--- a/include/osmium/io/input_iterator.hpp
+++ b/include/osmium/io/input_iterator.hpp
@@ -57,7 +57,7 @@ namespace osmium {
static_assert(std::is_base_of<osmium::memory::Item, TItem>::value, "TItem must derive from osmium::buffer::Item");
- typedef typename osmium::memory::Buffer::t_iterator<TItem> item_iterator;
+ using item_iterator = typename osmium::memory::Buffer::t_iterator<TItem>;
TSource* m_source;
std::shared_ptr<osmium::memory::Buffer> m_buffer;
@@ -69,20 +69,20 @@ namespace osmium {
if (!m_buffer || !*m_buffer) { // end of input
m_source = nullptr;
m_buffer.reset();
- m_iter = item_iterator();
+ m_iter = item_iterator{};
return;
}
- m_iter = m_buffer->begin<TItem>();
- } while (m_iter == m_buffer->end<TItem>());
+ m_iter = m_buffer->select<TItem>().begin();
+ } while (m_iter == m_buffer->select<TItem>().end());
}
public:
- typedef std::input_iterator_tag iterator_category;
- typedef TItem value_type;
- typedef ptrdiff_t difference_type;
- typedef TItem* pointer;
- typedef TItem& reference;
+ using iterator_category = std::input_iterator_tag;
+ using value_type = TItem;
+ using difference_type = ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
explicit InputIterator(TSource& source) :
m_source(&source) {
@@ -99,7 +99,7 @@ namespace osmium {
assert(m_buffer);
assert(m_iter);
++m_iter;
- if (m_iter == m_buffer->end<TItem>()) {
+ if (m_iter == m_buffer->select<TItem>().end()) {
update_buffer();
}
return *this;
diff --git a/include/osmium/io/output_iterator.hpp b/include/osmium/io/output_iterator.hpp
index ce050eb..f7025fc 100644
--- a/include/osmium/io/output_iterator.hpp
+++ b/include/osmium/io/output_iterator.hpp
@@ -51,12 +51,18 @@ namespace osmium {
namespace io {
template <typename TDest>
- class OutputIterator : public std::iterator<std::output_iterator_tag, osmium::memory::Item> {
+ class OutputIterator {
TDest* m_destination;
public:
+ using iterator_category = std::output_iterator_tag;
+ using value_type = void;
+ using difference_type = void;
+ using pointer = void;
+ using reference = void;
+
explicit OutputIterator(TDest& destination) :
m_destination(&destination) {
}
diff --git a/include/osmium/memory/buffer.hpp b/include/osmium/memory/buffer.hpp
index 07a31fe..8c246db 100644
--- a/include/osmium/memory/buffer.hpp
+++ b/include/osmium/memory/buffer.hpp
@@ -517,6 +517,16 @@ namespace osmium {
*/
using const_iterator = t_const_iterator<osmium::OSMEntity>;
+ template <typename T>
+ ItemIteratorRange<T> select() {
+ return ItemIteratorRange<T>{m_data, m_data + m_committed};
+ }
+
+ template <typename T>
+ ItemIteratorRange<const T> select() const {
+ return ItemIteratorRange<const T>{m_data, m_data + m_committed};
+ }
+
/**
* Get iterator for iterating over all items of type T in the
* buffer.
diff --git a/include/osmium/memory/collection.hpp b/include/osmium/memory/collection.hpp
index 17ace70..2a2c040 100644
--- a/include/osmium/memory/collection.hpp
+++ b/include/osmium/memory/collection.hpp
@@ -44,17 +44,23 @@ namespace osmium {
namespace memory {
template <typename TMember>
- class CollectionIterator : public std::iterator<std::forward_iterator_tag, TMember> {
+ 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.
- typedef typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type data_type;
+ using data_type = typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type;
data_type m_data;
public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = TMember;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
CollectionIterator() noexcept :
m_data(nullptr) {
}
@@ -112,9 +118,9 @@ namespace osmium {
public:
- typedef CollectionIterator<TMember> iterator;
- typedef CollectionIterator<const TMember> const_iterator;
- typedef TMember value_type;
+ using iterator = CollectionIterator<TMember>;
+ using const_iterator = CollectionIterator<const TMember>;
+ using value_type = TMember;
static constexpr osmium::item_type itemtype = TCollectionItemType;
diff --git a/include/osmium/memory/item.hpp b/include/osmium/memory/item.hpp
index fd404ce..0d550fb 100644
--- a/include/osmium/memory/item.hpp
+++ b/include/osmium/memory/item.hpp
@@ -47,7 +47,7 @@ namespace osmium {
namespace memory {
- typedef uint32_t item_size_type;
+ using item_size_type = uint32_t;
// align datastructures to this many bytes
constexpr item_size_type align_bytes = 8;
diff --git a/include/osmium/memory/item_iterator.hpp b/include/osmium/memory/item_iterator.hpp
index 3886c98..df72999 100644
--- a/include/osmium/memory/item_iterator.hpp
+++ b/include/osmium/memory/item_iterator.hpp
@@ -116,19 +116,19 @@ namespace osmium {
} // namespace detail
template <typename TMember>
- class ItemIterator : public std::iterator<std::forward_iterator_tag, TMember> {
+ class ItemIterator {
static_assert(std::is_base_of<osmium::memory::Item, TMember>::value, "TMember must derive from osmium::memory::Item");
// 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.
- typedef typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type data_type;
+ using data_type = typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type;
data_type m_data;
data_type m_end;
- void advance_to_next_item_of_right_type() {
+ void advance_to_next_item_of_right_type() noexcept {
while (m_data != m_end &&
!detail::type_is_compatible<typename std::remove_const<TMember>::type>(reinterpret_cast<const osmium::memory::Item*>(m_data)->type())) {
m_data = reinterpret_cast<TMember*>(m_data)->next();
@@ -137,23 +137,29 @@ namespace osmium {
public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = TMember;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
ItemIterator() noexcept :
m_data(nullptr),
m_end(nullptr) {
}
- ItemIterator(data_type data, data_type end) :
+ ItemIterator(data_type data, data_type end) noexcept :
m_data(data),
m_end(end) {
advance_to_next_item_of_right_type();
}
template <typename T>
- ItemIterator<T> cast() const {
+ ItemIterator<T> cast() const noexcept {
return ItemIterator<T>(m_data, m_end);
}
- ItemIterator<TMember>& operator++() {
+ ItemIterator<TMember>& operator++() noexcept {
assert(m_data);
assert(m_data != m_end);
m_data = reinterpret_cast<TMember*>(m_data)->next();
@@ -166,45 +172,50 @@ namespace osmium {
* types. Do not use this unless you know what you are
* doing.
*/
- ItemIterator<TMember>& advance_once() {
+ ItemIterator<TMember>& advance_once() noexcept {
assert(m_data);
assert(m_data != m_end);
m_data = reinterpret_cast<TMember*>(m_data)->next();
return *static_cast<ItemIterator<TMember>*>(this);
}
- ItemIterator<TMember> operator++(int) {
+ ItemIterator<TMember> operator++(int) noexcept {
ItemIterator<TMember> tmp(*this);
operator++();
return tmp;
}
- bool operator==(const ItemIterator<TMember>& rhs) const {
+ bool operator==(const ItemIterator<TMember>& rhs) const noexcept {
return m_data == rhs.m_data && m_end == rhs.m_end;
}
- bool operator!=(const ItemIterator<TMember>& rhs) const {
+ bool operator!=(const ItemIterator<TMember>& rhs) const noexcept {
return !(*this == rhs);
}
- unsigned char* data() const {
+ data_type data() noexcept {
+ assert(m_data);
+ return m_data;
+ }
+
+ const unsigned char* data() const noexcept {
assert(m_data);
return m_data;
}
- TMember& operator*() const {
+ TMember& operator*() const noexcept {
assert(m_data);
assert(m_data != m_end);
return *reinterpret_cast<TMember*>(m_data);
}
- TMember* operator->() const {
+ TMember* operator->() const noexcept {
assert(m_data);
assert(m_data != m_end);
return reinterpret_cast<TMember*>(m_data);
}
- explicit operator bool() const {
+ explicit operator bool() const noexcept {
return (m_data != nullptr) && (m_data != m_end);
}
@@ -221,6 +232,77 @@ namespace osmium {
return out;
}
+ template <typename T>
+ class ItemIteratorRange {
+
+ static_assert(std::is_base_of<osmium::memory::Item, T>::value, "Template parameter must derive from osmium::memory::Item");
+
+ // This data_type is either 'unsigned char*' or
+ // 'const unsigned char*' depending on whether T is const.
+ using data_type = typename std::conditional<std::is_const<T>::value, const unsigned char*, unsigned char*>::type;
+
+ data_type m_begin;
+ data_type m_end;
+
+ public:
+
+ using iterator = ItemIterator<T>;
+ using const_iterator = ItemIterator<const T>;
+
+ ItemIteratorRange(data_type first, data_type last) noexcept :
+ m_begin(first),
+ m_end(last) {
+ }
+
+ iterator begin() noexcept {
+ return iterator{m_begin, m_end};
+ }
+
+ iterator end() noexcept {
+ return iterator{m_end, m_end};
+ }
+
+ const_iterator cbegin() const noexcept {
+ return const_iterator{m_begin, m_end};
+ }
+
+ const_iterator cend() const noexcept {
+ return const_iterator{m_end, m_end};
+ }
+
+ const_iterator begin() const noexcept {
+ return cbegin();
+ }
+
+ const_iterator end() const noexcept {
+ return cend();
+ }
+
+ /**
+ * Return the number of items in this range.
+ *
+ * Note that this methods has worst-case complexity O(n) with n
+ * being the number of items in the underlying range.
+ */
+ size_t size() const {
+ if (m_begin == m_end) {
+ return 0;
+ }
+ return std::distance(cbegin(), cend());
+ }
+
+ /**
+ * Is this range empty?
+ *
+ * Note that this methods has worst-case complexity O(n) with n
+ * being the number of items in the underlying range.
+ */
+ bool empty() const {
+ return size() == 0;
+ }
+
+ }; // class ItemIteratorRange
+
} // namespace memory
} // namespace osmium
diff --git a/include/osmium/object_pointer_collection.hpp b/include/osmium/object_pointer_collection.hpp
index 09a5293..ce77bd2 100644
--- a/include/osmium/object_pointer_collection.hpp
+++ b/include/osmium/object_pointer_collection.hpp
@@ -70,8 +70,8 @@ namespace osmium {
public:
- typedef boost::indirect_iterator<std::vector<osmium::OSMObject*>::iterator, osmium::OSMObject> iterator;
- typedef boost::indirect_iterator<std::vector<osmium::OSMObject*>::const_iterator, const osmium::OSMObject> const_iterator;
+ using iterator = boost::indirect_iterator<std::vector<osmium::OSMObject*>::iterator, osmium::OSMObject>;
+ using const_iterator = boost::indirect_iterator<std::vector<osmium::OSMObject*>::const_iterator, const osmium::OSMObject>;
ObjectPointerCollection() noexcept :
m_objects() {
diff --git a/include/osmium/osm/area.hpp b/include/osmium/osm/area.hpp
index ee232f0..858c90d 100644
--- a/include/osmium/osm/area.hpp
+++ b/include/osmium/osm/area.hpp
@@ -43,6 +43,7 @@ DEALINGS IN THE SOFTWARE.
#include <osmium/osm/object.hpp>
#include <osmium/osm/types.hpp>
#include <osmium/osm/node_ref_list.hpp>
+#include <osmium/util/compatibility.hpp>
namespace osmium {
@@ -144,8 +145,8 @@ namespace osmium {
*
* @returns Pair (number outer rings, number inner rings)
*/
- std::pair<int, int> num_rings() const {
- std::pair<int, int> counter { 0, 0 };
+ std::pair<size_t, size_t> num_rings() const {
+ std::pair<size_t, size_t> counter { 0, 0 };
for (auto it = cbegin(); it != cend(); ++it) {
switch (it->type()) {
@@ -185,27 +186,51 @@ namespace osmium {
}
/**
+ * @deprecated Use inner_rings() instead.
+ *
* 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 {
+ OSMIUM_DEPRECATED osmium::memory::ItemIterator<const osmium::InnerRing> inner_ring_cbegin(const osmium::memory::ItemIterator<const osmium::OuterRing>& it) const {
return it.cast<const osmium::InnerRing>();
}
/**
+ * @deprecated Use inner_rings() instead.
+ *
* 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 {
+ OSMIUM_DEPRECATED 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>();
}
+ /**
+ * Return an iterator range for all outer rings.
+ * You can use the usual begin() and end() functions to iterate over
+ * all outer rings.
+ */
+ osmium::memory::ItemIteratorRange<const osmium::OuterRing> outer_rings() const {
+ return subitems<const osmium::OuterRing>();
+ }
+
+ /**
+ * Return an iterator range for all inner rings in the given outer
+ * ring.
+ * You can use the usual begin() and end() functions to iterate over
+ * all outer rings.
+ */
+ osmium::memory::ItemIteratorRange<const osmium::InnerRing> inner_rings(const osmium::OuterRing& outer) const {
+ osmium::memory::ItemIteratorRange<const osmium::OuterRing> outer_range{outer.data(), next()};
+ return osmium::memory::ItemIteratorRange<const osmium::InnerRing>{outer_range.cbegin().data(), std::next(outer_range.cbegin()).data()};
+ }
+
}; // class Area
static_assert(sizeof(Area) % osmium::memory::align_bytes == 0, "Class osmium::Area has wrong size to be aligned properly!");
diff --git a/include/osmium/osm/changeset.hpp b/include/osmium/osm/changeset.hpp
index f59db48..0ec994c 100644
--- a/include/osmium/osm/changeset.hpp
+++ b/include/osmium/osm/changeset.hpp
@@ -131,7 +131,7 @@ namespace osmium {
public:
- typedef size_t size_type;
+ using size_type = size_t;
ChangesetDiscussion() :
osmium::memory::Collection<ChangesetComment, osmium::item_type::changeset_discussion>() {
@@ -369,23 +369,23 @@ namespace osmium {
* @param value Value of the attribute
*/
void set_attribute(const char* attr, const char* value) {
- if (!strcmp(attr, "id")) {
+ if (!std::strcmp(attr, "id")) {
set_id(value);
- } else if (!strcmp(attr, "num_changes")) {
+ } else if (!std::strcmp(attr, "num_changes")) {
set_num_changes(value);
- } else if (!strcmp(attr, "comments_count")) {
+ } else if (!std::strcmp(attr, "comments_count")) {
set_num_comments(value);
- } else if (!strcmp(attr, "created_at")) {
+ } else if (!std::strcmp(attr, "created_at")) {
set_created_at(osmium::Timestamp(value));
- } else if (!strcmp(attr, "closed_at")) {
+ } else if (!std::strcmp(attr, "closed_at")) {
set_closed_at(osmium::Timestamp(value));
- } else if (!strcmp(attr, "uid")) {
+ } else if (!std::strcmp(attr, "uid")) {
set_uid(value);
}
}
- typedef osmium::memory::CollectionIterator<Item> iterator;
- typedef osmium::memory::CollectionIterator<const Item> const_iterator;
+ using iterator = osmium::memory::CollectionIterator<Item>;
+ using const_iterator = osmium::memory::CollectionIterator<const Item>;
iterator begin() {
return iterator(subitems_position());
diff --git a/include/osmium/osm/crc.hpp b/include/osmium/osm/crc.hpp
index ff39996..e7c233a 100644
--- a/include/osmium/osm/crc.hpp
+++ b/include/osmium/osm/crc.hpp
@@ -151,6 +151,7 @@ namespace osmium {
void update(const NodeRef& node_ref) {
update_int64(node_ref.ref());
+ update(node_ref.location());
}
void update(const NodeRefList& node_refs) {
diff --git a/include/osmium/osm/entity_bits.hpp b/include/osmium/osm/entity_bits.hpp
index 50b3e4c..b8e9ddb 100644
--- a/include/osmium/osm/entity_bits.hpp
+++ b/include/osmium/osm/entity_bits.hpp
@@ -33,12 +33,15 @@ DEALINGS IN THE SOFTWARE.
*/
+#include <cassert>
+#include <type_traits>
+
#include <osmium/osm/item_type.hpp>
namespace osmium {
/**
- * @brief Bitfield for OSM entity types.
+ * @brief Bit field for OSM entity types.
*/
namespace osm_entity_bits {
@@ -94,8 +97,19 @@ namespace osmium {
return lhs;
}
+ /**
+ * Get entity_bits from item_type.
+ *
+ * @pre item_type must be undefined, node, way, relation, area, or
+ * changeset.
+ */
inline type from_item_type(osmium::item_type item_type) noexcept {
- return static_cast<osmium::osm_entity_bits::type>(0x1 << (static_cast<uint16_t>(item_type) - 1));
+ auto ut = static_cast<std::underlying_type<osmium::item_type>::type>(item_type);
+ assert(ut <= 0x05);
+ if (ut == 0) {
+ return nothing;
+ }
+ return static_cast<osmium::osm_entity_bits::type>(0x1 << (ut - 1));
}
} // namespace osm_entity_bits
diff --git a/include/osmium/osm/location.hpp b/include/osmium/osm/location.hpp
index 85f4b16..2949a90 100644
--- a/include/osmium/osm/location.hpp
+++ b/include/osmium/osm/location.hpp
@@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE.
#include <cmath>
#include <cstdint>
+#include <functional>
#include <iosfwd>
#include <stdexcept>
#include <string>
@@ -273,13 +274,52 @@ namespace osmium {
template <typename TChar, typename TTraits>
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Location& location) {
if (location) {
- out << '(' << location.lon() << ',' << location.lat() << ')';
+ out << '(';
+ location.as_string(std::ostream_iterator<char>(out), ',');
+ out << ')';
} else {
out << "(undefined,undefined)";
}
return out;
}
+ namespace detail {
+
+ template <int N>
+ inline size_t hash(const osmium::Location& location) noexcept {
+ return location.x() ^ location.y();
+ }
+
+ template <>
+ inline size_t hash<8>(const osmium::Location& location) noexcept {
+ size_t h = location.x();
+ h <<= 32;
+ return h ^ location.y();
+ }
+
+ } // namespace detail
+
} // namespace osmium
+namespace std {
+
+// This pragma is a workaround for a bug in an old libc implementation
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+ template <>
+ struct hash<osmium::Location> {
+ using argument_type = osmium::Location;
+ using result_type = size_t;
+ size_t operator()(const osmium::Location& location) const noexcept {
+ return osmium::detail::hash<sizeof(size_t)>(location);
+ }
+ };
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+} // namespace std
+
#endif // OSMIUM_OSM_LOCATION_HPP
diff --git a/include/osmium/osm/object.hpp b/include/osmium/osm/object.hpp
index 6d1de6f..a373156 100644
--- a/include/osmium/osm/object.hpp
+++ b/include/osmium/osm/object.hpp
@@ -38,6 +38,7 @@ DEALINGS IN THE SOFTWARE.
#include <cstdlib>
#include <cstring>
#include <stdexcept>
+#include <tuple>
#include <osmium/memory/collection.hpp>
#include <osmium/memory/item.hpp>
@@ -49,6 +50,7 @@ DEALINGS IN THE SOFTWARE.
#include <osmium/osm/timestamp.hpp>
#include <osmium/osm/types.hpp>
#include <osmium/osm/types_from_string.hpp>
+#include <osmium/util/misc.hpp>
namespace osmium {
@@ -172,9 +174,9 @@ namespace osmium {
* @returns Reference to object to make calls chainable.
*/
OSMObject& set_visible(const char* visible) {
- if (!strcmp("true", visible)) {
+ if (!std::strcmp("true", visible)) {
set_visible(true);
- } else if (!strcmp("false", visible)) {
+ } else if (!std::strcmp("false", visible)) {
set_visible(false);
} else {
throw std::invalid_argument("Unknown value for visible attribute (allowed is 'true' or 'false')");
@@ -313,23 +315,23 @@ namespace osmium {
* @param value Value of the attribute
*/
void set_attribute(const char* attr, const char* value) {
- if (!strcmp(attr, "id")) {
+ if (!std::strcmp(attr, "id")) {
set_id(value);
- } else if (!strcmp(attr, "version")) {
+ } else if (!std::strcmp(attr, "version")) {
set_version(value);
- } else if (!strcmp(attr, "changeset")) {
+ } else if (!std::strcmp(attr, "changeset")) {
set_changeset(value);
- } else if (!strcmp(attr, "timestamp")) {
+ } else if (!std::strcmp(attr, "timestamp")) {
set_timestamp(osmium::Timestamp(value));
- } else if (!strcmp(attr, "uid")) {
+ } else if (!std::strcmp(attr, "uid")) {
set_uid(value);
- } else if (!strcmp(attr, "visible")) {
+ } else if (!std::strcmp(attr, "visible")) {
set_visible(value);
}
}
- typedef osmium::memory::CollectionIterator<Item> iterator;
- typedef osmium::memory::CollectionIterator<const Item> const_iterator;
+ using iterator = osmium::memory::CollectionIterator<Item>;
+ using const_iterator = osmium::memory::CollectionIterator<const Item>;
iterator begin() {
return iterator(subitems_position());
@@ -355,6 +357,26 @@ namespace osmium {
return cend();
}
+ /**
+ * Get a range of subitems of a specific type.
+ *
+ * @tparam The type (must be derived from osmium::memory::Item.
+ */
+ template <typename T>
+ osmium::memory::ItemIteratorRange<T> subitems() {
+ return osmium::memory::ItemIteratorRange<T>{subitems_position(), next()};
+ }
+
+ /**
+ * Get a range of subitems of a specific type.
+ *
+ * @tparam The type (must be derived from osmium::memory::Item.
+ */
+ template <typename T>
+ osmium::memory::ItemIteratorRange<const T> subitems() const {
+ return osmium::memory::ItemIteratorRange<const T>{subitems_position(), next()};
+ }
+
template <typename T>
using t_iterator = osmium::memory::ItemIterator<T>;
@@ -399,8 +421,8 @@ namespace osmium {
* OSMObjects are equal if their type, id, and version are equal.
*/
inline bool operator==(const OSMObject& lhs, const OSMObject& rhs) noexcept {
- return lhs.type() == rhs.type() &&
- lhs.id() == rhs.id() &&
+ return lhs.type() == rhs.type() &&
+ lhs.id() == rhs.id() &&
lhs.version() == rhs.version();
}
@@ -409,16 +431,22 @@ namespace osmium {
}
/**
- * OSMObjects can be ordered by type, id and version.
- * Note that we use the absolute value of the id for a
- * better ordering of objects with negative id.
+ * OSMObjects can be ordered by type, id, version, and timestamp. Usually
+ * ordering by timestamp is not necessary as there shouldn't be two
+ * objects with the same type, id, and version. But this can happen when
+ * creating diff files from extracts, so we take the timestamp into
+ * account here.
+ *
+ * Note that we use the absolute value of the id for a better ordering
+ * of objects with negative id. If the IDs have the same absolute value,
+ * the positive ID comes first.
+ *
+ * See object_order_type_id_reverse_version if you need a different
+ * ordering.
*/
inline bool operator<(const OSMObject& lhs, const OSMObject& rhs) noexcept {
- if (lhs.type() != rhs.type()) {
- return lhs.type() < rhs.type();
- }
- return (lhs.id() == rhs.id() && lhs.version() < rhs.version()) ||
- lhs.positive_id() < rhs.positive_id();
+ return const_tie(lhs.type(), lhs.positive_id(), lhs.id() < 0, lhs.version(), lhs.timestamp()) <
+ const_tie(rhs.type(), rhs.positive_id(), rhs.id() < 0, rhs.version(), rhs.timestamp());
}
inline bool operator>(const OSMObject& lhs, const OSMObject& rhs) noexcept {
diff --git a/include/osmium/osm/object_comparisons.hpp b/include/osmium/osm/object_comparisons.hpp
index fe3529b..a17d47d 100644
--- a/include/osmium/osm/object_comparisons.hpp
+++ b/include/osmium/osm/object_comparisons.hpp
@@ -34,11 +34,13 @@ DEALINGS IN THE SOFTWARE.
*/
#include <osmium/osm/object.hpp>
+#include <osmium/util/misc.hpp>
namespace osmium {
/**
- * Function object class for comparing OSM objects for equality by type, id, and version.
+ * Function object class for comparing OSM objects for equality by type,
+ * id, and version.
*/
struct object_equal_type_id_version {
@@ -53,8 +55,8 @@ namespace osmium {
}; // struct object_equal_type_id_version
/**
- * Function object class for comparing OSM objects for equality by type and id,
- * ignoring the version.
+ * Function object class for comparing OSM objects for equality by type
+ * and id, ignoring the version.
*/
struct object_equal_type_id {
@@ -70,7 +72,8 @@ namespace osmium {
}; // struct object_equal_type_id
/**
- * Function object class for ordering OSM objects by type, id, and version.
+ * Function object class for ordering OSM objects by type, id, version,
+ * and timestamp.
*/
struct object_order_type_id_version {
@@ -85,18 +88,17 @@ namespace osmium {
}; // struct object_order_type_id_version
/**
- * Function object class for ordering OSM objects by type, id, and reverse version,
- * ie objects are ordered by type and id, but later versions of an object are
- * ordered before earlier versions of the same object.
+ * Function object class for ordering OSM objects by type, id, and
+ * reverse version, timestamp. So objects are ordered by type and id, but
+ * later versions of an object are ordered before earlier versions of the
+ * same object. This is useful when the last version of an object needs
+ * to be used.
*/
struct object_order_type_id_reverse_version {
bool operator()(const osmium::OSMObject& lhs, const osmium::OSMObject& rhs) const noexcept {
- if (lhs.type() != rhs.type()) {
- return lhs.type() < rhs.type();
- }
- return (lhs.id() == rhs.id() && lhs.version() > rhs.version()) ||
- lhs.positive_id() < rhs.positive_id();
+ return const_tie(lhs.type(), lhs.id() < 0, lhs.positive_id(), rhs.version(), rhs.timestamp()) <
+ const_tie(rhs.type(), rhs.id() < 0, rhs.positive_id(), lhs.version(), lhs.timestamp());
}
bool operator()(const osmium::OSMObject* lhs, const osmium::OSMObject* rhs) const noexcept {
diff --git a/include/osmium/osm/relation.hpp b/include/osmium/osm/relation.hpp
index 9c4e69c..1ea0a5e 100644
--- a/include/osmium/osm/relation.hpp
+++ b/include/osmium/osm/relation.hpp
@@ -149,7 +149,7 @@ namespace osmium {
public:
- typedef size_t size_type;
+ using size_type = size_t;
RelationMemberList() :
osmium::memory::Collection<RelationMember, osmium::item_type::relation_member_list>() {
diff --git a/include/osmium/osm/tag.hpp b/include/osmium/osm/tag.hpp
index 3f1a298..d5415f7 100644
--- a/include/osmium/osm/tag.hpp
+++ b/include/osmium/osm/tag.hpp
@@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
+#include <cassert>
#include <cstddef>
#include <cstring>
#include <iosfwd>
@@ -87,7 +88,7 @@ namespace osmium {
}; // class Tag
inline bool operator==(const Tag& a, const Tag& b) {
- return !std::strcmp(a.key(), b.key()) && !strcmp(a.value(), b.value());
+ return !std::strcmp(a.key(), b.key()) && !std::strcmp(a.value(), b.value());
}
inline bool operator<(const Tag& a, const Tag& b) {
@@ -104,32 +105,72 @@ namespace osmium {
class TagList : public osmium::memory::Collection<Tag, osmium::item_type::tag_list> {
+ const_iterator find_key(const char* key) const noexcept {
+ return std::find_if(cbegin(), cend(), [key](const Tag& tag) {
+ return !std::strcmp(tag.key(), key);
+ });
+ }
+
public:
- typedef size_t size_type;
+ using size_type = size_t;
TagList() :
osmium::memory::Collection<Tag, osmium::item_type::tag_list>() {
}
+ /**
+ * Returns the number of tags in this tag list.
+ */
size_type size() const noexcept {
return static_cast<size_type>(std::distance(begin(), end()));
}
+ /**
+ * Get tag value for the given tag key. If the key is not set, returns
+ * the default_value.
+ *
+ * @pre @code key != nullptr @endcode
+ */
const char* get_value_by_key(const char* key, const char* default_value = nullptr) const noexcept {
- auto result = std::find_if(cbegin(), cend(), [key](const Tag& tag) {
- return !strcmp(tag.key(), key);
- });
- if (result == cend()) {
- return default_value;
- }
- return result->value();
+ assert(key);
+ const auto result = find_key(key);
+ return result == cend() ? default_value : result->value();
}
+ /**
+ * Get tag value for the given tag key. If the key is not set, returns
+ * nullptr.
+ *
+ * @pre @code key != nullptr @endcode
+ */
const char* operator[](const char* key) const noexcept {
return get_value_by_key(key);
}
+ /**
+ * Returns true if the tag with the given key is in the tag list.
+ *
+ * @pre @code key != nullptr @endcode
+ */
+ bool has_key(const char* key) const noexcept {
+ assert(key);
+ return find_key(key) != cend();
+ }
+
+ /**
+ * Returns true if the tag with the given key and value is in the
+ * tag list.
+ *
+ * @pre @code key != nullptr && value != nullptr @endcode
+ */
+ bool has_tag(const char* key, const char* value) const noexcept {
+ assert(key);
+ assert(value);
+ const auto result = find_key(key);
+ return result != cend() && !std::strcmp(result->value(), value);
+ }
+
}; // class TagList
static_assert(sizeof(TagList) % osmium::memory::align_bytes == 0, "Class osmium::TagList has wrong size to be aligned properly!");
diff --git a/include/osmium/osm/types.hpp b/include/osmium/osm/types.hpp
index 984dd13..ec46bb2 100644
--- a/include/osmium/osm/types.hpp
+++ b/include/osmium/osm/types.hpp
@@ -38,25 +38,25 @@ DEALINGS IN THE SOFTWARE.
namespace osmium {
/*
- * The following typedefs are chosen so that they can represent all needed
+ * The following types are chosen so that they can represent all needed
* numbers and still be reasonably space efficient. As the OSM database
* needs 64 bit IDs for nodes, this size is used for all object IDs.
*/
- typedef int64_t object_id_type; ///< Type for OSM object (node, way, or relation) IDs.
- typedef uint64_t unsigned_object_id_type; ///< Type for OSM object (node, way, or relation) IDs where we only allow positive IDs.
- typedef uint32_t object_version_type; ///< Type for OSM object version number.
- typedef uint32_t changeset_id_type; ///< Type for OSM changeset IDs.
- typedef uint32_t user_id_type; ///< Type for OSM user IDs.
- typedef int32_t signed_user_id_type; ///< Type for signed OSM user IDs.
- typedef uint32_t num_changes_type; ///< Type for changeset num_changes.
- typedef uint32_t num_comments_type; ///< Type for changeset num_comments.
+ using object_id_type = int64_t; ///< Type for OSM object (node, way, or relation) IDs.
+ using unsigned_object_id_type = uint64_t; ///< Type for OSM object (node, way, or relation) IDs where we only allow positive IDs.
+ using object_version_type = uint32_t; ///< Type for OSM object version number.
+ using changeset_id_type = uint32_t; ///< Type for OSM changeset IDs.
+ using user_id_type = uint32_t; ///< Type for OSM user IDs.
+ using signed_user_id_type = int32_t; ///< Type for signed OSM user IDs.
+ using num_changes_type = uint32_t; ///< Type for changeset num_changes.
+ using num_comments_type = uint32_t; ///< Type for changeset num_comments.
/**
* Size for strings in OSM data such as user names, tag keys, roles, etc.
* In Osmium they can be up to 2^16 bytes long, but OSM usually has lower
* defined limits.
*/
- typedef uint16_t string_size_type;
+ using string_size_type = uint16_t;
// maximum of 256 characters of max 4 bytes each (in UTF-8 encoding)
constexpr const int max_osm_string_length = 256 * 4;
diff --git a/include/osmium/osm/types_from_string.hpp b/include/osmium/osm/types_from_string.hpp
index aed0648..d5da72b 100644
--- a/include/osmium/osm/types_from_string.hpp
+++ b/include/osmium/osm/types_from_string.hpp
@@ -50,7 +50,7 @@ namespace osmium {
/**
* Convert string with object id to object_id_type.
*
- * @pre input must not be nullptr.
+ * @pre @code input != nullptr @endcode
*
* @param input Input string.
*
@@ -70,23 +70,29 @@ namespace osmium {
/**
* Parse string with object type identifier followed by object id. This
- * reads strings like "n1234" and "w10".
+ * reads strings like "n1234" and "w10". If there is no type prefix,
+ * the default_type is returned.
*
- * @pre input must not be nullptr.
+ * @pre @code input != nullptr @endcode
+ * @pre @code types != osmium::osm_entity_bits::nothing @endcode
*
* @param input Input string.
* @param types Allowed types. Must not be osmium::osm_entity_bits::nothing.
+ * @param default_type Type used when there is no type prefix.
*
* @returns std::pair of type and id.
*
* @throws std::range_error if the value is out of range.
*/
- inline std::pair<osmium::item_type, osmium::object_id_type> string_to_object_id(const char* input, osmium::osm_entity_bits::type types) {
+ inline std::pair<osmium::item_type, osmium::object_id_type>
+ string_to_object_id(const char* input,
+ osmium::osm_entity_bits::type types,
+ osmium::item_type default_type = osmium::item_type::undefined) {
assert(input);
assert(types != osmium::osm_entity_bits::nothing);
if (*input != '\0') {
if (std::isdigit(*input)) {
- return std::make_pair(osmium::item_type::undefined, string_to_object_id(input));
+ return std::make_pair(default_type, string_to_object_id(input));
}
osmium::item_type t = osmium::char_to_item_type(*input);
if (osmium::osm_entity_bits::from_item_type(t) & types) {
@@ -126,7 +132,7 @@ namespace osmium {
}
/**
- * Convert string with object version to object_version_type.
+ * Convert string with changeset id to changeset_id_type.
*
* @pre input must not be nullptr.
*
diff --git a/include/osmium/relations/collector.hpp b/include/osmium/relations/collector.hpp
index 7d7d14d..4d2b523 100644
--- a/include/osmium/relations/collector.hpp
+++ b/include/osmium/relations/collector.hpp
@@ -64,13 +64,6 @@ namespace osmium {
namespace detail {
- template <typename R>
- inline typename std::iterator_traits<typename R::iterator>::difference_type count_not_removed(const R& range) {
- return std::count_if(range.begin(), range.end(), [](MemberMeta& mm) {
- return !mm.removed();
- });
- }
-
} // namespace detail
/**
@@ -193,14 +186,14 @@ namespace osmium {
int m_count_complete = 0;
- typedef std::function<void(osmium::memory::Buffer&&)> callback_func_type;
+ using callback_func_type = std::function<void(osmium::memory::Buffer&&)>;
callback_func_type m_callback;
static constexpr size_t initial_buffer_size = 1024 * 1024;
iterator_range<mm_iterator> find_member_meta(osmium::item_type type, osmium::object_id_type id) {
auto& mmv = member_meta(type);
- return iterator_range<mm_iterator>{std::equal_range(mmv.begin(), mmv.end(), MemberMeta(id))};
+ return make_range(std::equal_range(mmv.begin(), mmv.end(), MemberMeta(id)));
}
public:
@@ -313,6 +306,7 @@ namespace osmium {
}
const osmium::Relation& get_relation(size_t offset) const {
+ assert(m_relations_buffer.committed() > offset);
return m_relations_buffer.get<osmium::Relation>(offset);
}
@@ -323,7 +317,15 @@ namespace osmium {
return get_relation(relation_meta.relation_offset());
}
+ /**
+ * Get the relation from a member_meta.
+ */
+ const osmium::Relation& get_relation(const MemberMeta& member_meta) const {
+ return get_relation(m_relations[member_meta.relation_pos()]);
+ }
+
osmium::OSMObject& get_member(size_t offset) const {
+ assert(m_members_buffer.committed() > offset);
return m_members_buffer.get<osmium::OSMObject>(offset);
}
@@ -378,6 +380,12 @@ namespace osmium {
std::sort(m_member_meta[2].begin(), m_member_meta[2].end());
}
+ static typename iterator_range<mm_iterator>::iterator::difference_type count_not_removed(const iterator_range<mm_iterator>& range) {
+ return std::count_if(range.begin(), range.end(), [](MemberMeta& mm) {
+ return !mm.removed();
+ });
+ }
+
/**
* Find this object in the member vectors and add it to all
* relations that need it.
@@ -388,7 +396,7 @@ namespace osmium {
bool find_and_add_object(const osmium::OSMObject& object) {
auto range = find_member_meta(object.type(), object.id());
- if (detail::count_not_removed(range) == 0) {
+ if (count_not_removed(range) == 0) {
// nothing found
return false;
}
@@ -434,12 +442,12 @@ namespace osmium {
// if this is the last time this object was needed
// then mark it as removed
- if (detail::count_not_removed(range) == 1) {
+ if (count_not_removed(range) == 1) {
get_member(range.begin()->buffer_offset()).set_removed(true);
}
for (auto& member_meta : range) {
- if (!member_meta.removed() && relation.id() == get_relation(member_meta.relation_pos()).id()) {
+ if (!member_meta.removed() && relation.id() == get_relation(member_meta).id()) {
member_meta.remove();
break;
}
diff --git a/include/osmium/relations/detail/member_meta.hpp b/include/osmium/relations/detail/member_meta.hpp
index f0e9c36..fb71416 100644
--- a/include/osmium/relations/detail/member_meta.hpp
+++ b/include/osmium/relations/detail/member_meta.hpp
@@ -132,7 +132,7 @@ namespace osmium {
template <typename TChar, typename TTraits>
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const MemberMeta& mm) {
- out << "MemberMeta(member_id=" << mm.member_id() << " relation_pos=" << mm.relation_pos() << " member_pos=" << mm.member_pos() << " buffer_offset=" << mm.buffer_offset() << ")";
+ out << "MemberMeta(member_id=" << mm.member_id() << " relation_pos=" << mm.relation_pos() << " member_pos=" << mm.member_pos() << " buffer_offset=" << mm.buffer_offset() << " removed=" << (mm.removed() ? "yes" : "no") << ")";
return out;
}
diff --git a/include/osmium/relations/detail/relation_meta.hpp b/include/osmium/relations/detail/relation_meta.hpp
index 93aa41c..b71c5a5 100644
--- a/include/osmium/relations/detail/relation_meta.hpp
+++ b/include/osmium/relations/detail/relation_meta.hpp
@@ -117,8 +117,8 @@ namespace osmium {
*/
struct has_all_members {
- typedef RelationMeta& argument_type;
- typedef bool result_type;
+ using argument_type = RelationMeta&;
+ using result_type = bool;
/**
* @returns true if this relation is complete, false otherwise.
diff --git a/include/osmium/tags/filter.hpp b/include/osmium/tags/filter.hpp
index 407992e..7451737 100644
--- a/include/osmium/tags/filter.hpp
+++ b/include/osmium/tags/filter.hpp
@@ -76,8 +76,8 @@ namespace osmium {
template <typename TKey, typename TValue=void, typename TKeyComp=match_key<TKey>, typename TValueComp=match_value<TValue>>
class Filter {
- typedef TKey key_type;
- typedef typename std::conditional<std::is_void<TValue>::value, bool, TValue>::type value_type;
+ using key_type = TKey;
+ using value_type = typename std::conditional<std::is_void<TValue>::value, bool, TValue>::type;
struct Rule {
key_type key;
@@ -106,10 +106,10 @@ namespace osmium {
public:
- typedef Filter<TKey, TValue, TKeyComp, TValueComp> filter_type;
- typedef const osmium::Tag& argument_type;
- typedef bool result_type;
- typedef boost::filter_iterator<filter_type, osmium::TagList::const_iterator> iterator;
+ using filter_type = Filter<TKey, TValue, TKeyComp, TValueComp>;
+ using argument_type = const osmium::Tag&;
+ using result_type = bool;
+ using iterator = boost::filter_iterator<filter_type, osmium::TagList::const_iterator>;
explicit Filter(bool default_result = false) :
m_default_result(default_result) {
@@ -151,9 +151,9 @@ namespace osmium {
}; // class Filter
- typedef Filter<std::string, std::string> KeyValueFilter;
- typedef Filter<std::string> KeyFilter;
- typedef Filter<std::string, void, match_key_prefix> KeyPrefixFilter;
+ using KeyValueFilter = Filter<std::string, std::string>;
+ using KeyFilter = Filter<std::string>;
+ using KeyPrefixFilter = Filter<std::string, void, match_key_prefix>;
} // namespace tags
diff --git a/include/osmium/tags/regex_filter.hpp b/include/osmium/tags/regex_filter.hpp
index 8ea6d60..3df94dd 100644
--- a/include/osmium/tags/regex_filter.hpp
+++ b/include/osmium/tags/regex_filter.hpp
@@ -49,7 +49,7 @@ namespace osmium {
}
}; // struct match_value<std::regex>
- typedef Filter<std::string, std::regex> RegexFilter;
+ using RegexFilter = Filter<std::string, std::regex>;
} // namespace tags
diff --git a/include/osmium/thread/pool.hpp b/include/osmium/thread/pool.hpp
index 207f555..44b6d15 100644
--- a/include/osmium/thread/pool.hpp
+++ b/include/osmium/thread/pool.hpp
@@ -66,7 +66,7 @@ namespace osmium {
}
if (num_threads < 0) {
- num_threads += hardware_concurrency;
+ num_threads += int(hardware_concurrency);
}
if (num_threads < 1) {
@@ -190,7 +190,7 @@ namespace osmium {
template <typename TFunction>
std::future<typename std::result_of<TFunction()>::type> submit(TFunction&& func) {
- typedef typename std::result_of<TFunction()>::type result_type;
+ using result_type = typename std::result_of<TFunction()>::type;
std::packaged_task<result_type()> task(std::forward<TFunction>(func));
std::future<result_type> future_result(task.get_future());
diff --git a/include/osmium/thread/sorted_queue.hpp b/include/osmium/thread/sorted_queue.hpp
deleted file mode 100644
index 5478643..0000000
--- a/include/osmium/thread/sorted_queue.hpp
+++ /dev/null
@@ -1,159 +0,0 @@
-#ifndef OSMIUM_THREAD_SORTED_QUEUE_HPP
-#define OSMIUM_THREAD_SORTED_QUEUE_HPP
-
-/*
-
-This file is part of Osmium (http://osmcode.org/libosmium).
-
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
-
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include <condition_variable>
-#include <cstddef>
-#include <deque>
-#include <mutex>
-
-namespace osmium {
-
- namespace thread {
-
- /**
- * This implements a sorted queue. It is a bit like a priority
- * queue. We have n worker threads pushing items into the queue
- * and one thread pulling them out again "in order". The order
- * is defined by the monotonically increasing "num" parameter
- * to the push() method. The wait_and_pop() and try_pop() methods
- * will only give out the next numbered item. This way several
- * workers can work in their own time on different pieces of
- * some incoming data, but it all gets serialized properly again
- * after the workers have done their work.
- */
- template <typename T>
- class SortedQueue {
-
- typedef typename std::deque<T>::size_type size_type;
-
- mutable std::mutex m_mutex;
- std::deque<T> m_queue;
- std::condition_variable m_data_available;
-
- size_type m_offset;
-
- // this method expects that we already have the lock
- bool empty_intern() const {
- return m_queue.front() == T();
- }
-
- public:
-
- SortedQueue() :
- m_mutex(),
- m_queue(1),
- m_data_available(),
- m_offset(0) {
- }
-
- /**
- * Push an item into the queue.
- *
- * @param value The item to push into the queue.
- * @param num Number to describe ordering for the items.
- * It must increase monotonically.
- */
- void push(T value, size_type num) {
- std::lock_guard<std::mutex> lock(m_mutex);
-
- num -= m_offset;
- if (m_queue.size() <= num + 1) {
- m_queue.resize(num + 2);
- }
- m_queue[num] = std::move(value);
-
- m_data_available.notify_one();
- }
-
- /**
- * Wait until the next item becomes available and make it
- * available through value.
- */
- void wait_and_pop(T& value) {
- std::unique_lock<std::mutex> lock(m_mutex);
-
- m_data_available.wait(lock, [this] {
- return !empty_intern();
- });
- value = std::move(m_queue.front());
- m_queue.pop_front();
- ++m_offset;
- }
-
- /**
- * Get next item if it is available and return true. Or
- * return false otherwise.
- */
- bool try_pop(T& value) {
- std::lock_guard<std::mutex> lock(m_mutex);
-
- if (empty_intern()) {
- return false;
- }
- value = std::move(m_queue.front());
- m_queue.pop_front();
- ++m_offset;
- return true;
- }
-
- /**
- * The queue is empty. This means try_pop() would fail if called.
- * It does not mean that there is nothing on the queue. Because
- * the queue is sorted, it could mean that the next item in the
- * queue is not available, but other items are.
- */
- bool empty() const {
- std::lock_guard<std::mutex> lock(m_mutex);
-
- return empty_intern();
- }
-
- /**
- * Returns the number of items in the queue, regardless of whether
- * they can be accessed. If this is =0 it
- * implies empty()==true, but not the other way around.
- */
- size_t size() const {
- std::lock_guard<std::mutex> lock(m_mutex);
- return m_queue.size();
- }
-
- }; // class SortedQueue
-
- } // namespace thread
-
-} // namespace osmium
-
-#endif // OSMIUM_THREAD_SORTED_QUEUE_HPP
diff --git a/include/osmium/util/delta.hpp b/include/osmium/util/delta.hpp
index 34c4eb2..8fd63a4 100644
--- a/include/osmium/util/delta.hpp
+++ b/include/osmium/util/delta.hpp
@@ -119,7 +119,7 @@ namespace osmium {
}; // class DeltaDecode
template <typename TBaseIterator, typename TTransform, typename TValue, typename TDelta = int64_t>
- class DeltaEncodeIterator : public std::iterator<std::input_iterator_tag, TValue> {
+ class DeltaEncodeIterator {
TBaseIterator m_it;
TBaseIterator m_end;
@@ -129,8 +129,13 @@ namespace osmium {
public:
- using value_type = TValue;
- using delta_type = TDelta;
+ using iterator_category = std::input_iterator_tag;
+ using value_type = TValue;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ using delta_type = TDelta;
DeltaEncodeIterator(TBaseIterator first, TBaseIterator last, TTransform& trans) :
m_it(first),
diff --git a/include/osmium/util/iterator.hpp b/include/osmium/util/iterator.hpp
index 4cef519..15f3f3d 100644
--- a/include/osmium/util/iterator.hpp
+++ b/include/osmium/util/iterator.hpp
@@ -46,7 +46,7 @@ namespace osmium {
iterator_range(P&& p) :
P(std::forward<P>(p)) {
}
-
+/*
It begin() {
return this->first;
}
@@ -54,7 +54,7 @@ namespace osmium {
It end() {
return this->second;
}
-
+*/
It begin() const {
return this->first;
}
@@ -67,7 +67,16 @@ namespace osmium {
return begin() == end();
}
- };
+ }; // struct iterator_range
+
+ /**
+ * Helper function to create iterator_range from std::pair.
+ */
+ template <typename P, typename It = typename P::first_type>
+ inline iterator_range<It> make_range(P&& p) {
+ static_assert(std::is_same<P, std::pair<It, It>>::value, "make_range needs pair of iterators as argument");
+ return iterator_range<It>(std::forward<P>(p));
+ }
} // namespace osmium
diff --git a/include/osmium/util/memory_mapping.hpp b/include/osmium/util/memory_mapping.hpp
index 67e944e..cf28e6a 100644
--- a/include/osmium/util/memory_mapping.hpp
+++ b/include/osmium/util/memory_mapping.hpp
@@ -126,22 +126,18 @@ private:
void make_invalid() noexcept;
#ifdef _WIN32
- typedef DWORD flag_type;
+ using flag_type = DWORD;
#else
- typedef int flag_type;
+ using flag_type = int;
#endif
flag_type get_protection() const noexcept;
flag_type get_flags() const noexcept;
- // A zero-sized mapping is not allowed by the operating system.
- // So if the user asks for a mapping of size 0, we map a full
- // page instead. This way we don't have a special case in the rest
- // of the code.
- static size_t initial_size(size_t size) {
+ static size_t check_size(size_t size) {
if (size == 0) {
- return osmium::util::get_pagesize();
+ throw std::runtime_error("Zero-sized mapping is not allowed.");
}
return size;
}
@@ -550,7 +546,7 @@ inline int osmium::util::MemoryMapping::get_flags() const noexcept {
}
inline osmium::util::MemoryMapping::MemoryMapping(size_t size, mapping_mode mode, int fd, off_t offset) :
- m_size(initial_size(size)),
+ m_size(check_size(size)),
m_offset(offset),
m_fd(resize_fd(fd)),
m_mapping_mode(mode),
@@ -689,7 +685,7 @@ inline void osmium::util::MemoryMapping::make_invalid() noexcept {
}
inline osmium::util::MemoryMapping::MemoryMapping(size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset) :
- m_size(initial_size(size)),
+ m_size(check_size(size)),
m_offset(offset),
m_fd(resize_fd(fd)),
m_mapping_mode(mode),
diff --git a/include/osmium/tags/regex_filter.hpp b/include/osmium/util/misc.hpp
similarity index 73%
copy from include/osmium/tags/regex_filter.hpp
copy to include/osmium/util/misc.hpp
index 8ea6d60..8acecb5 100644
--- a/include/osmium/tags/regex_filter.hpp
+++ b/include/osmium/util/misc.hpp
@@ -1,5 +1,5 @@
-#ifndef OSMIUM_TAGS_REGEX_FILTER_HPP
-#define OSMIUM_TAGS_REGEX_FILTER_HPP
+#ifndef OSMIUM_UTIL_MISC_HPP
+#define OSMIUM_UTIL_MISC_HPP
/*
@@ -33,26 +33,20 @@ DEALINGS IN THE SOFTWARE.
*/
-#include <regex>
-#include <string>
-
-#include <osmium/tags/filter.hpp>
+#include <tuple>
namespace osmium {
- namespace tags {
-
- template <>
- struct match_value<std::regex> {
- bool operator()(const std::regex& rule_value, const char* tag_value) {
- return std::regex_match(tag_value, rule_value);
- }
- }; // struct match_value<std::regex>
-
- typedef Filter<std::string, std::regex> RegexFilter;
-
- } // namespace tags
+ /**
+ * Like std::tie(), but takes its arguments as const references. Used
+ * as a helper function when sorting.
+ */
+ template<typename... Ts>
+ inline std::tuple<const Ts&...>
+ const_tie(const Ts&... args) noexcept {
+ return std::tuple<const Ts&...>(args...);
+ }
} // namespace osmium
-#endif // OSMIUM_TAGS_REGEX_FILTER_HPP
+#endif // OSMIUM_UTIL_MISC_HPP
diff --git a/include/osmium/util/iterator.hpp b/include/osmium/util/timer.hpp
similarity index 63%
copy from include/osmium/util/iterator.hpp
copy to include/osmium/util/timer.hpp
index 4cef519..8cae80a 100644
--- a/include/osmium/util/iterator.hpp
+++ b/include/osmium/util/timer.hpp
@@ -1,5 +1,5 @@
-#ifndef OSMIUM_UTIL_ITERATOR_HPP
-#define OSMIUM_UTIL_ITERATOR_HPP
+#ifndef OSMIUM_UTIL_TIMER_HPP
+#define OSMIUM_UTIL_TIMER_HPP
/*
@@ -33,42 +33,66 @@ DEALINGS IN THE SOFTWARE.
*/
-#include <cstddef>
-#include <utility>
+#include <cstdint>
+
+#ifdef OSMIUM_WITH_TIMER
+
+#include <chrono>
namespace osmium {
- template <typename It, typename P = std::pair<It, It>>
- struct iterator_range : public P {
+ class Timer {
+
+ using clock = std::chrono::high_resolution_clock;
+ std::chrono::time_point<clock> m_start;
+ std::chrono::time_point<clock> m_stop;
+
+ public:
- using iterator = It;
+ Timer() :
+ m_start(clock::now()) {
+ }
- iterator_range(P&& p) :
- P(std::forward<P>(p)) {
+ void start() {
+ m_start = clock::now();
}
- It begin() {
- return this->first;
+ void stop() {
+ m_stop = clock::now();
}
- It end() {
- return this->second;
+ int64_t elapsed_microseconds() const {
+ return std::chrono::duration_cast<std::chrono::microseconds>(m_stop - m_start).count();
}
- It begin() const {
- return this->first;
+ };
+
+} // namespace osmium
+
+#else
+
+namespace osmium {
+
+ class Timer {
+
+ public:
+
+ Timer() = default;
+
+ void start() {
}
- It end() const {
- return this->second;
+ void stop() {
}
- size_t empty() const {
- return begin() == end();
+ int64_t elapsed_microseconds() const {
+ return 0;
}
};
} // namespace osmium
-#endif // OSMIUM_UTIL_ITERATOR_HPP
+#endif
+
+#endif // OSMIUM_UTIL_TIMER_HPP
diff --git a/include/osmium/tags/regex_filter.hpp b/include/osmium/version.hpp
similarity index 71%
copy from include/osmium/tags/regex_filter.hpp
copy to include/osmium/version.hpp
index 8ea6d60..ac2765c 100644
--- a/include/osmium/tags/regex_filter.hpp
+++ b/include/osmium/version.hpp
@@ -1,5 +1,5 @@
-#ifndef OSMIUM_TAGS_REGEX_FILTER_HPP
-#define OSMIUM_TAGS_REGEX_FILTER_HPP
+#ifndef OSMIUM_VERSION_HPP
+#define OSMIUM_VERSION_HPP
/*
@@ -33,26 +33,10 @@ DEALINGS IN THE SOFTWARE.
*/
-#include <regex>
-#include <string>
+#define LIBOSMIUM_VERSION_MAJOR 2
+#define LIBOSMIUM_VERSION_MINOR 6
+#define LIBOSMIUM_VERSION_PATCH 1
-#include <osmium/tags/filter.hpp>
+#define LIBOSMIUM_VERSION_STRING "2.6.1"
-namespace osmium {
-
- namespace tags {
-
- template <>
- struct match_value<std::regex> {
- bool operator()(const std::regex& rule_value, const char* tag_value) {
- return std::regex_match(tag_value, rule_value);
- }
- }; // struct match_value<std::regex>
-
- typedef Filter<std::string, std::regex> RegexFilter;
-
- } // namespace tags
-
-} // namespace osmium
-
-#endif // OSMIUM_TAGS_REGEX_FILTER_HPP
+#endif // OSMIUM_VERSION_HPP
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index cea67ec..241c192 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -134,6 +134,7 @@ add_unit_test(geom test_wkb)
add_unit_test(geom test_wkt)
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_bzip2 ENABLE_IF ${BZIP2_FOUND} LIBS ${BZIP2_LIBRARIES})
add_unit_test(io test_file_formats)
diff --git a/test/README b/test/README
index 8195824..868b533 100644
--- a/test/README
+++ b/test/README
@@ -3,11 +3,3 @@ Osmium uses Catch (https://github.com/philsquared/Catch/) for its unit tests.
Only one header file is needed (catch.hpp) which can be downloaded from
http://builds.catch-lib.net/ and put into the include directory.
-Osmium needs a few changes to catch.hpp, they were patched in. To be able to
-compare with the original version, it is stored in include/catch_orig.hpp.
-
-Changes are:
-* Disable more warnings in GCC
-* CATCH_CONFIG_CPP11_NULLPTR must be set for MSVC
-* Problem with test running in loop: https://github.com/philsquared/Catch/issues/271
-
diff --git a/test/data-tests/testdata-multipolygon.cpp b/test/data-tests/testdata-multipolygon.cpp
index cf4fc52..6a863c8 100644
--- a/test/data-tests/testdata-multipolygon.cpp
+++ b/test/data-tests/testdata-multipolygon.cpp
@@ -156,7 +156,9 @@ int main(int argc, char* argv[]) {
osmium::area::ProblemReporterOGR problem_reporter(dataset);
osmium::area::Assembler::config_type assembler_config(&problem_reporter);
- assembler_config.enable_debug_output();
+ assembler_config.check_roles = true;
+ assembler_config.create_empty_areas = true;
+ assembler_config.debug_level = 2;
osmium::area::MultipolygonCollector<osmium::area::Assembler> collector(assembler_config);
std::cerr << "Pass 1...\n";
diff --git a/test/data-tests/testdata-xml.cpp b/test/data-tests/testdata-xml.cpp
index b5a0e90..01cee29 100644
--- a/test/data-tests/testdata-xml.cpp
+++ b/test/data-tests/testdata-xml.cpp
@@ -336,10 +336,10 @@ TEST_CASE("Reading OSM XML 140") {
reader.close();
int count = 0;
- for (auto it = buffer.begin<osmium::Node>(); it != buffer.end<osmium::Node>(); ++it) {
+ for (const auto& node : buffer.select<osmium::Node>()) {
++count;
- REQUIRE(it->id() == count);
- const osmium::TagList& t = it->tags();
+ REQUIRE(node.id() == count);
+ const osmium::TagList& t = node.tags();
const char* uc = t["unicode_char"];
@@ -411,35 +411,35 @@ TEST_CASE("Reading OSM XML 142") {
reader.close();
int count = 0;
- for (auto it = buffer.begin<osmium::Node>(); it != buffer.end<osmium::Node>(); ++it) {
+ for (const auto& node : buffer.select<osmium::Node>()) {
++count;
- REQUIRE(it->id() == count);
- REQUIRE(it->tags().size() == 1);
- const osmium::Tag& tag = *(it->tags().begin());
+ REQUIRE(node.id() == count);
+ REQUIRE(node.tags().size() == 1);
+ const osmium::Tag& tag = *(node.tags().begin());
switch (count) {
case 1:
- REQUIRE(S_(it->user()) == "user name");
+ REQUIRE(S_(node.user()) == "user name");
REQUIRE(S_(tag.key()) == "key with space");
REQUIRE(S_(tag.value()) == "value with space");
break;
case 2:
- REQUIRE(S_(it->user()) == "line\nfeed");
+ REQUIRE(S_(node.user()) == "line\nfeed");
REQUIRE(S_(tag.key()) == "key with\nlinefeed");
REQUIRE(S_(tag.value()) == "value with\nlinefeed");
break;
case 3:
- REQUIRE(S_(it->user()) == "carriage\rreturn");
+ REQUIRE(S_(node.user()) == "carriage\rreturn");
REQUIRE(S_(tag.key()) == "key with\rcarriage\rreturn");
REQUIRE(S_(tag.value()) == "value with\rcarriage\rreturn");
break;
case 4:
- REQUIRE(S_(it->user()) == "tab\tulator");
+ REQUIRE(S_(node.user()) == "tab\tulator");
REQUIRE(S_(tag.key()) == "key with\ttab");
REQUIRE(S_(tag.value()) == "value with\ttab");
break;
case 5:
- REQUIRE(S_(it->user()) == "unencoded linefeed");
+ REQUIRE(S_(node.user()) == "unencoded linefeed");
REQUIRE(S_(tag.key()) == "key with unencoded linefeed");
REQUIRE(S_(tag.value()) == "value with unencoded linefeed");
break;
@@ -455,8 +455,8 @@ TEST_CASE("Reading OSM XML 142") {
osmium::memory::Buffer buffer = reader.read();
reader.close();
- auto it = buffer.begin<osmium::Relation>();
- REQUIRE(it != buffer.end<osmium::Relation>());
+ auto it = buffer.select<osmium::Relation>().begin();
+ REQUIRE(it != buffer.select<osmium::Relation>().end());
REQUIRE(it->id() == 21);
const auto& members = it->members();
REQUIRE(members.size() == 5);
diff --git a/test/include/catch.hpp b/test/include/catch.hpp
index f042149..2a7146a 100644
--- a/test/include/catch.hpp
+++ b/test/include/catch.hpp
@@ -1,6 +1,6 @@
/*
- * Catch v1.3.3
- * Generated: 2016-01-22 07:51:36.661106
+ * Catch v1.4.0
+ * Generated: 2016-03-15 07:23:12.623111
* ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -62,7 +62,11 @@
#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
-#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#ifdef CATCH_CONFIG_COUNTER
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
@@ -89,7 +93,7 @@
// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
-
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
// ****************
// Note to maintainers: if new toggles are added please document them
// in configuration.md, too
@@ -102,6 +106,10 @@
// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
+#if defined(__cplusplus) && __cplusplus >= 201103L
+# define CATCH_CPP11_OR_GREATER
+#endif
+
#ifdef __clang__
# if __has_feature(cxx_nullptr)
@@ -112,6 +120,10 @@
# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
# endif
+# if defined(CATCH_CPP11_OR_GREATER)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+# endif
+
#endif // __clang__
////////////////////////////////////////////////////////////////////////////////
@@ -136,9 +148,13 @@
// GCC
#ifdef __GNUC__
-#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
-# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#endif
+# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+# endif
+
+# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
+# endif
// - otherwise more recent versions define __cplusplus >= 201103L
// and will get picked up below
@@ -173,13 +189,20 @@
#endif
+// Use __COUNTER__ if the compiler supports it
+#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \
+ ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \
+ ( defined __clang__ && __clang_major__ >= 3 )
+
+#define CATCH_INTERNAL_CONFIG_COUNTER
+
+#endif
+
////////////////////////////////////////////////////////////////////////////////
// C++ language feature support
// catch all support for C++11
-#if defined(__cplusplus) && __cplusplus >= 201103L
-
-# define CATCH_CPP11_OR_GREATER
+#if defined(CATCH_CPP11_OR_GREATER)
# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
@@ -246,6 +269,13 @@
#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
# define CATCH_CONFIG_CPP11_UNIQUE_PTR
#endif
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+# define CATCH_CONFIG_COUNTER
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+#endif
// noexcept support:
#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
@@ -672,24 +702,28 @@ void registerTestCaseFunction
#ifdef CATCH_CONFIG_VARIADIC_MACROS
///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+ static void TestName(); \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
+ static void TestName()
#define INTERNAL_CATCH_TESTCASE( ... ) \
- static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
- static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
namespace{ \
- struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+ struct TestName : ClassName{ \
void test(); \
}; \
- Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
} \
- void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+ void TestName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
@@ -697,24 +731,28 @@ void registerTestCaseFunction
#else
///////////////////////////////////////////////////////////////////////////////
+ #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \
+ static void TestName(); \
+ namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+ static void TestName()
#define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
- static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
- namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
- static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )()
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\
namespace{ \
- struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+ struct TestCaseName : ClassName{ \
void test(); \
}; \
- Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
} \
- void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+ void TestCaseName::test()
+ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
@@ -1287,37 +1325,37 @@ namespace Internal {
template<typename T1, typename T2>
struct Evaluator<T1, T2, IsEqualTo> {
static bool evaluate( T1 const& lhs, T2 const& rhs) {
- return opCast( lhs ) == opCast( rhs );
+ return bool( opCast( lhs ) == opCast( rhs ) );
}
};
template<typename T1, typename T2>
struct Evaluator<T1, T2, IsNotEqualTo> {
static bool evaluate( T1 const& lhs, T2 const& rhs ) {
- return opCast( lhs ) != opCast( rhs );
+ return bool( opCast( lhs ) != opCast( rhs ) );
}
};
template<typename T1, typename T2>
struct Evaluator<T1, T2, IsLessThan> {
static bool evaluate( T1 const& lhs, T2 const& rhs ) {
- return opCast( lhs ) < opCast( rhs );
+ return bool( opCast( lhs ) < opCast( rhs ) );
}
};
template<typename T1, typename T2>
struct Evaluator<T1, T2, IsGreaterThan> {
static bool evaluate( T1 const& lhs, T2 const& rhs ) {
- return opCast( lhs ) > opCast( rhs );
+ return bool( opCast( lhs ) > opCast( rhs ) );
}
};
template<typename T1, typename T2>
struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
static bool evaluate( T1 const& lhs, T2 const& rhs ) {
- return opCast( lhs ) >= opCast( rhs );
+ return bool( opCast( lhs ) >= opCast( rhs ) );
}
};
template<typename T1, typename T2>
struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
static bool evaluate( T1 const& lhs, T2 const& rhs ) {
- return opCast( lhs ) <= opCast( rhs );
+ return bool( opCast( lhs ) <= opCast( rhs ) );
}
};
@@ -2020,13 +2058,14 @@ namespace Catch {
do { \
Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
try { \
+ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
( __catchResult <= expr ).endExpression(); \
} \
catch( ... ) { \
__catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
} \
INTERNAL_CATCH_REACT( __catchResult ) \
- } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+ } while( Catch::isTrue( false && static_cast<bool>(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
@@ -2563,10 +2602,12 @@ namespace Catch {
}
///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
- static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
- namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
- static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature )
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+ static std::string translatorName( signature ); \
+ namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
+ static std::string translatorName( signature )
+
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
// #included from: internal/catch_approx.hpp
#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
@@ -3331,6 +3372,11 @@ namespace Catch {
InLexicographicalOrder,
InRandomOrder
}; };
+ struct UseColour { enum YesOrNo {
+ Auto,
+ Yes,
+ No
+ }; };
class TestSpec;
@@ -3350,7 +3396,7 @@ namespace Catch {
virtual TestSpec const& testSpec() const = 0;
virtual RunTests::InWhatOrder runOrder() const = 0;
virtual unsigned int rngSeed() const = 0;
- virtual bool forceColour() const = 0;
+ virtual UseColour::YesOrNo useColour() const = 0;
};
}
@@ -3439,14 +3485,14 @@ namespace Catch {
noThrow( false ),
showHelp( false ),
showInvisibles( false ),
- forceColour( false ),
filenamesAsTags( false ),
abortAfter( -1 ),
rngSeed( 0 ),
verbosity( Verbosity::Normal ),
warnings( WarnAbout::Nothing ),
showDurations( ShowDurations::DefaultForReporter ),
- runOrder( RunTests::InDeclarationOrder )
+ runOrder( RunTests::InDeclarationOrder ),
+ useColour( UseColour::Auto )
{}
bool listTests;
@@ -3459,7 +3505,6 @@ namespace Catch {
bool noThrow;
bool showHelp;
bool showInvisibles;
- bool forceColour;
bool filenamesAsTags;
int abortAfter;
@@ -3469,6 +3514,7 @@ namespace Catch {
WarnAbout::What warnings;
ShowDurations::OrNot showDurations;
RunTests::InWhatOrder runOrder;
+ UseColour::YesOrNo useColour;
std::string outputFilename;
std::string name;
@@ -3534,7 +3580,7 @@ namespace Catch {
virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
virtual RunTests::InWhatOrder runOrder() const { return m_data.runOrder; }
virtual unsigned int rngSeed() const { return m_data.rngSeed; }
- virtual bool forceColour() const { return m_data.forceColour; }
+ virtual UseColour::YesOrNo useColour() const { return m_data.useColour; }
private:
@@ -3572,6 +3618,8 @@ namespace Catch {
#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
// #included from: ../external/clara.h
+// Version 0.0.1.1
+
// Only use header guard if we are not using an outer namespace
#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
@@ -3596,6 +3644,7 @@ namespace Catch {
#include <string>
#include <vector>
#include <sstream>
+#include <algorithm>
// Use optional outer namespace
#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
@@ -3730,12 +3779,158 @@ namespace Tbc {
#endif // TBC_TEXT_FORMAT_H_INCLUDED
// ----------- end of #include from tbc_text_format.h -----------
-// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h
+// ........... back in clara.h
#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+// ----------- #included from clara_compilers.h -----------
+
+#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CLARA_CONFIG_CPP11_OVERRIDE : is override supported?
+// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+
+// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11
+
+#ifdef __clang__
+
+#if __has_feature(cxx_nullptr)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#if __has_feature(cxx_noexcept)
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1600)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(__cplusplus) && __cplusplus >= 201103L
+
+#define CLARA_CPP11_OR_GREATER
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE
+#endif
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+// noexcept support:
+#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT)
+#define CLARA_NOEXCEPT noexcept
+# define CLARA_NOEXCEPT_IS(x) noexcept(x)
+#else
+#define CLARA_NOEXCEPT throw()
+# define CLARA_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CLARA_CONFIG_CPP11_NULLPTR
+#define CLARA_NULL nullptr
+#else
+#define CLARA_NULL NULL
+#endif
+
+// override support
+#ifdef CLARA_CONFIG_CPP11_OVERRIDE
+#define CLARA_OVERRIDE override
+#else
+#define CLARA_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR
+# define CLARA_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+# define CLARA_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// ----------- end of #include from clara_compilers.h -----------
+// ........... back in clara.h
+
#include <map>
-#include <algorithm>
#include <stdexcept>
#include <memory>
@@ -3762,6 +3957,9 @@ namespace Clara {
const unsigned int consoleWidth = 80;
#endif
+ // Use this to try and stop compiler from warning about unreachable code
+ inline bool isTrue( bool value ) { return value; }
+
using namespace Tbc;
inline bool startsWith( std::string const& str, std::string const& prefix ) {
@@ -3802,16 +4000,17 @@ namespace Clara {
}
template<typename T>
inline void convertInto( bool, T& ) {
- throw std::runtime_error( "Invalid conversion" );
+ if( isTrue( true ) )
+ throw std::runtime_error( "Invalid conversion" );
}
template<typename ConfigT>
struct IArgFunction {
virtual ~IArgFunction() {}
-# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS
IArgFunction() = default;
IArgFunction( IArgFunction const& ) = default;
-# endif
+#endif
virtual void set( ConfigT& config, std::string const& value ) const = 0;
virtual void setFlag( ConfigT& config ) const = 0;
virtual bool takesArg() const = 0;
@@ -3821,11 +4020,11 @@ namespace Clara {
template<typename ConfigT>
class BoundArgFunction {
public:
- BoundArgFunction() : functionObj( CATCH_NULL ) {}
+ BoundArgFunction() : functionObj( CLARA_NULL ) {}
BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
- BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CATCH_NULL ) {}
+ BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {}
BoundArgFunction& operator = ( BoundArgFunction const& other ) {
- IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL;
+ IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL;
delete functionObj;
functionObj = newFunctionObj;
return *this;
@@ -3841,7 +4040,7 @@ namespace Clara {
bool takesArg() const { return functionObj->takesArg(); }
bool isSet() const {
- return functionObj != CATCH_NULL;
+ return functionObj != CLARA_NULL;
}
private:
IArgFunction<ConfigT>* functionObj;
@@ -3949,7 +4148,7 @@ namespace Clara {
std::string data;
};
- void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const {
+ void parseIntoTokens( int argc, char const* const argv[], std::vector<Parser::Token>& tokens ) const {
const std::string doubleDash = "--";
for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
parseIntoTokens( argv[i] , tokens);
@@ -4059,7 +4258,7 @@ namespace Clara {
}
};
- typedef CATCH_AUTO_PTR( Arg ) ArgAutoPtr;
+ typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr;
friend void addOptName( Arg& arg, std::string const& optName )
{
@@ -4135,8 +4334,8 @@ namespace Clara {
m_arg->description = description;
return *this;
}
- ArgBuilder& detail( std::string const& _detail ) {
- m_arg->detail = _detail;
+ ArgBuilder& detail( std::string const& detail ) {
+ m_arg->detail = detail;
return *this;
}
@@ -4219,14 +4418,14 @@ namespace Clara {
maxWidth = (std::max)( maxWidth, it->commands().size() );
for( it = itBegin; it != itEnd; ++it ) {
- Detail::Text usageText( it->commands(), Detail::TextAttributes()
+ Detail::Text usage( it->commands(), Detail::TextAttributes()
.setWidth( maxWidth+indent )
.setIndent( indent ) );
Detail::Text desc( it->description, Detail::TextAttributes()
.setWidth( width - maxWidth - 3 ) );
- for( std::size_t i = 0; i < (std::max)( usageText.size(), desc.size() ); ++i ) {
- std::string usageCol = i < usageText.size() ? usageText[i] : "";
+ for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+ std::string usageCol = i < usage.size() ? usage[i] : "";
os << usageCol;
if( i < desc.size() && !desc[i].empty() )
@@ -4283,13 +4482,13 @@ namespace Clara {
return oss.str();
}
- ConfigT parse( int argc, char const * const * argv ) const {
+ ConfigT parse( int argc, char const* const argv[] ) const {
ConfigT config;
parseInto( argc, argv, config );
return config;
}
- std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const {
+ std::vector<Parser::Token> parseInto( int argc, char const* argv[], ConfigT& config ) const {
std::string processName = argv[0];
std::size_t lastSlash = processName.find_last_of( "/\\" );
if( lastSlash != std::string::npos )
@@ -4471,6 +4670,21 @@ namespace Catch {
? ShowDurations::Always
: ShowDurations::Never;
}
+ inline void setUseColour( ConfigData& config, std::string const& value ) {
+ std::string mode = toLower( value );
+
+ if( mode == "yes" )
+ config.useColour = UseColour::Yes;
+ else if( mode == "no" )
+ config.useColour = UseColour::No;
+ else if( mode == "auto" )
+ config.useColour = UseColour::Auto;
+ else
+ throw std::runtime_error( "colour mode must be one of: auto, yes or no" );
+ }
+ inline void forceColour( ConfigData& config ) {
+ config.useColour = UseColour::Yes;
+ }
inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
std::ifstream f( _filename.c_str() );
if( !f.is_open() )
@@ -4557,7 +4771,7 @@ namespace Catch {
cli["-d"]["--durations"]
.describe( "show test durations" )
- .bind( &setShowDurations, "yes/no" );
+ .bind( &setShowDurations, "yes|no" );
cli["-f"]["--input-file"]
.describe( "load test names to run from a file" )
@@ -4585,8 +4799,12 @@ namespace Catch {
.bind( &setRngSeed, "'time'|number" );
cli["--force-colour"]
- .describe( "force colourised output" )
- .bind( &ConfigData::forceColour );
+ .describe( "force colourised output (deprecated)" )
+ .bind( &forceColour );
+
+ cli["--use-colour"]
+ .describe( "should output be colourised" )
+ .bind( &setUseColour, "yes|no" );
return cli;
}
@@ -5692,6 +5910,11 @@ namespace Catch {
while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
Totals deltaTotals = m_totals.delta( prevTotals );
+ if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) {
+ deltaTotals.assertions.failed++;
+ deltaTotals.testCases.passed--;
+ deltaTotals.testCases.failed++;
+ }
m_totals.testCases += deltaTotals.testCases;
m_reporter->testCaseEnded( TestCaseStats( testInfo,
deltaTotals,
@@ -6083,7 +6306,7 @@ namespace Catch {
Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
}
- int applyCommandLine( int argc, char const* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+ int applyCommandLine( int argc, char const* argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
try {
m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
@@ -6110,13 +6333,16 @@ namespace Catch {
m_config.reset();
}
- int run( int argc, char const* const argv[] ) {
+ int run( int argc, char const* argv[] ) {
int returnCode = applyCommandLine( argc, argv );
if( returnCode == 0 )
returnCode = run();
return returnCode;
}
+ int run( int argc, char* argv[] ) {
+ return run( argc, const_cast<char const**>( argv ) );
+ }
int run() {
if( m_configData.showHelp )
@@ -6815,7 +7041,18 @@ namespace {
IColourImpl* platformColourInstance() {
static Win32ColourImpl s_instance;
- return &s_instance;
+
+ Ptr<IConfig const> config = getCurrentContext().getConfig();
+ UseColour::YesOrNo colourMode = config
+ ? config->useColour()
+ : UseColour::Auto;
+ if( colourMode == UseColour::Auto )
+ colourMode = !isDebuggerActive()
+ ? UseColour::Yes
+ : UseColour::No;
+ return colourMode == UseColour::Yes
+ ? &s_instance
+ : NoColourImpl::instance();
}
} // end anon namespace
@@ -6866,7 +7103,14 @@ namespace {
IColourImpl* platformColourInstance() {
Ptr<IConfig const> config = getCurrentContext().getConfig();
- return (config && config->forceColour()) || isatty(STDOUT_FILENO)
+ UseColour::YesOrNo colourMode = config
+ ? config->useColour()
+ : UseColour::Auto;
+ if( colourMode == UseColour::Auto )
+ colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) )
+ ? UseColour::Yes
+ : UseColour::No;
+ return colourMode == UseColour::Yes
? PosixColourImpl::instance()
: NoColourImpl::instance();
}
@@ -6891,9 +7135,7 @@ namespace Catch {
Colour::~Colour(){ if( !m_moved ) use( None ); }
void Colour::use( Code _colourCode ) {
- static IColourImpl* impl = isDebuggerActive()
- ? NoColourImpl::instance()
- : platformColourInstance();
+ static IColourImpl* impl = platformColourInstance();
impl->use( _colourCode );
}
@@ -7270,7 +7512,7 @@ namespace Catch {
return os;
}
- Version libraryVersion( 1, 3, 3, "", 0 );
+ Version libraryVersion( 1, 4, 0, "", 0 );
}
@@ -9566,7 +9808,7 @@ namespace Catch {
if( totals.testCases.total() == 0 ) {
stream << Colour( Colour::Warning ) << "No tests ran\n";
}
- else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) {
+ else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) {
stream << Colour( Colour::ResultSuccess ) << "All tests passed";
stream << " ("
<< pluralise( totals.assertions.passed, "assertion" ) << " in "
diff --git a/test/t/area/test_node_ref_segment.cpp b/test/t/area/test_node_ref_segment.cpp
index 3261c24..5e33c2b 100644
--- a/test/t/area/test_node_ref_segment.cpp
+++ b/test/t/area/test_node_ref_segment.cpp
@@ -20,91 +20,109 @@ TEST_CASE("NodeRefSegmentClass") {
osmium::NodeRef nr3(3, { 1.2, 3.6 });
osmium::NodeRef nr4(4, { 1.2, 3.7 });
- NodeRefSegment s1(nr1, nr2, nullptr, nullptr);
+ NodeRefSegment s1(nr1, nr2);
REQUIRE(s1.first().ref() == 1);
REQUIRE(s1.second().ref() == 2);
- NodeRefSegment s2(nr2, nr3, nullptr, nullptr);
+ NodeRefSegment s2(nr2, nr3);
REQUIRE(s2.first().ref() == 3);
REQUIRE(s2.second().ref() == 2);
- NodeRefSegment s3(nr3, nr4, nullptr, nullptr);
+ NodeRefSegment s3(nr3, nr4);
REQUIRE(s3.first().ref() == 3);
REQUIRE(s3.second().ref() == 4);
}
SECTION("intersection") {
- NodeRefSegment s1({ 1, {0.0, 0.0}}, { 2, {2.0, 2.0}}, nullptr, nullptr);
- NodeRefSegment s2({ 3, {0.0, 2.0}}, { 4, {2.0, 0.0}}, nullptr, nullptr);
- NodeRefSegment s3({ 5, {2.0, 0.0}}, { 6, {4.0, 2.0}}, nullptr, nullptr);
- NodeRefSegment s4({ 7, {1.0, 0.0}}, { 8, {3.0, 2.0}}, nullptr, nullptr);
- NodeRefSegment s5({ 9, {0.0, 4.0}}, {10, {4.0, 0.0}}, nullptr, nullptr);
- NodeRefSegment s6({11, {0.0, 0.0}}, {12, {1.0, 1.0}}, nullptr, nullptr);
- NodeRefSegment s7({13, {1.0, 1.0}}, {14, {3.0, 3.0}}, nullptr, nullptr);
+ NodeRefSegment s1({ 1, {0.0, 0.0}}, { 2, {2.0, 2.0}});
+ NodeRefSegment s2({ 3, {0.0, 2.0}}, { 4, {2.0, 0.0}});
+ NodeRefSegment s3({ 5, {2.0, 0.0}}, { 6, {4.0, 2.0}});
+ NodeRefSegment s4({ 7, {1.0, 0.0}}, { 8, {3.0, 2.0}});
+ NodeRefSegment s5({ 9, {0.0, 4.0}}, {10, {4.0, 0.0}});
+ NodeRefSegment s6({11, {0.0, 0.0}}, {12, {1.0, 1.0}});
+ NodeRefSegment s7({13, {1.0, 1.0}}, {14, {3.0, 3.0}});
REQUIRE(calculate_intersection(s1, s2) == osmium::Location(1.0, 1.0));
+ REQUIRE(calculate_intersection(s2, s1) == osmium::Location(1.0, 1.0));
+
REQUIRE(calculate_intersection(s1, s3) == osmium::Location());
+ REQUIRE(calculate_intersection(s3, s1) == osmium::Location());
+
REQUIRE(calculate_intersection(s2, s3) == osmium::Location());
+ REQUIRE(calculate_intersection(s3, s2) == osmium::Location());
+
REQUIRE(calculate_intersection(s1, s4) == osmium::Location());
+ REQUIRE(calculate_intersection(s4, s1) == osmium::Location());
+
REQUIRE(calculate_intersection(s1, s5) == osmium::Location(2.0, 2.0));
- REQUIRE(calculate_intersection(s1, s1) == osmium::Location());
- REQUIRE(calculate_intersection(s1, s6) == osmium::Location());
- REQUIRE(calculate_intersection(s1, s7) == osmium::Location());
- }
+ REQUIRE(calculate_intersection(s5, s1) == osmium::Location(2.0, 2.0));
- SECTION("intersection of very long segments") {
- NodeRefSegment s1({ 1, {90.0, 90.0}}, { 2, {-90.0, -90.0}}, nullptr, nullptr);
- NodeRefSegment s2({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr);
- REQUIRE(calculate_intersection(s1, s2) == osmium::Location(0.0, 0.0));
+ REQUIRE(calculate_intersection(s1, s6) == osmium::Location(1.0, 1.0));
+ REQUIRE(calculate_intersection(s6, s1) == osmium::Location(1.0, 1.0));
- NodeRefSegment s3({ 1, {-90.0, -90.0}}, { 2, {90.0, 90.0}}, nullptr, nullptr);
- NodeRefSegment s4({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr);
- REQUIRE(calculate_intersection(s3, s4) == osmium::Location(0.0, 0.0));
+ REQUIRE(calculate_intersection(s1, s7) == osmium::Location(1.0, 1.0));
+ REQUIRE(calculate_intersection(s7, s1) == osmium::Location(1.0, 1.0));
- NodeRefSegment s5({ 1, {-90.0000001, -90.0}}, { 2, {90.0, 90.0}}, nullptr, nullptr);
- NodeRefSegment s6({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr);
- REQUIRE(calculate_intersection(s5, s6) == osmium::Location(0.0, 0.0));
+ REQUIRE(calculate_intersection(s6, s7) == osmium::Location());
+ REQUIRE(calculate_intersection(s7, s6) == osmium::Location());
}
- SECTION("to_left_of") {
- osmium::Location loc { 2.0, 2.0 };
+ SECTION("intersection of collinear segments") {
+ NodeRefSegment s1({ 1, {0.0, 0.0}}, { 2, {2.0, 0.0}}); // *---*
+ NodeRefSegment s2({ 3, {2.0, 0.0}}, { 4, {4.0, 0.0}}); // *---*
+ NodeRefSegment s3({ 5, {0.0, 0.0}}, { 6, {1.0, 0.0}}); // *-*
+ NodeRefSegment s4({ 7, {1.0, 0.0}}, { 8, {2.0, 0.0}}); // *-*
+ NodeRefSegment s5({ 9, {1.0, 0.0}}, {10, {3.0, 0.0}}); // *---*
+ NodeRefSegment s6({11, {0.0, 0.0}}, {12, {4.0, 0.0}}); // *-------*
+ NodeRefSegment s7({13, {0.0, 0.0}}, {14, {5.0, 0.0}}); // *---------*
+ NodeRefSegment s8({13, {1.0, 0.0}}, {14, {5.0, 0.0}}); // *-------*
+ NodeRefSegment s9({13, {3.0, 0.0}}, {14, {4.0, 0.0}}); // *-*
+
+ REQUIRE(calculate_intersection(s1, s1) == osmium::Location());
- REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {4.0, 0.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
- REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
+ REQUIRE(calculate_intersection(s1, s2) == osmium::Location());
+ REQUIRE(calculate_intersection(s2, s1) == osmium::Location());
- REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {3.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {4.0, 3.0}}, nullptr, nullptr).to_left_of(loc) == false);
+ REQUIRE(calculate_intersection(s1, s3) == osmium::Location(1.0, 0.0));
+ REQUIRE(calculate_intersection(s3, s1) == osmium::Location(1.0, 0.0));
- REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {2.0, 0.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {3.0, 1.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {3.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
+ REQUIRE(calculate_intersection(s1, s4) == osmium::Location(1.0, 0.0));
+ REQUIRE(calculate_intersection(s4, s1) == osmium::Location(1.0, 0.0));
- REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
+ REQUIRE(calculate_intersection(s1, s5) == osmium::Location(1.0, 0.0));
+ REQUIRE(calculate_intersection(s5, s1) == osmium::Location(1.0, 0.0));
- REQUIRE(NodeRefSegment({0, {2.0, 0.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {2.0, 0.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
- REQUIRE(NodeRefSegment({0, {2.0, 2.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+ REQUIRE(calculate_intersection(s1, s6) == osmium::Location(2.0, 0.0));
+ REQUIRE(calculate_intersection(s6, s1) == osmium::Location(2.0, 0.0));
- REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 1.0}}, nullptr, nullptr).to_left_of(loc) == false);
- REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {0.0, 1.0}}, nullptr, nullptr).to_left_of(loc) == false);
+ REQUIRE(calculate_intersection(s1, s7) == osmium::Location(2.0, 0.0));
+ REQUIRE(calculate_intersection(s7, s1) == osmium::Location(2.0, 0.0));
- REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {1.0, 3.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 0.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {3.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+ REQUIRE(calculate_intersection(s1, s8) == osmium::Location(1.0, 0.0));
+ REQUIRE(calculate_intersection(s8, s1) == osmium::Location(1.0, 0.0));
- REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {1.0, 2.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {1.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
- REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+ REQUIRE(calculate_intersection(s1, s9) == osmium::Location());
+ REQUIRE(calculate_intersection(s9, s1) == osmium::Location());
- REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 2.0}}, nullptr, nullptr).to_left_of(loc));
- REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+ REQUIRE(calculate_intersection(s5, s6) == osmium::Location(1.0, 0.0));
+ REQUIRE(calculate_intersection(s6, s5) == osmium::Location(1.0, 0.0));
- REQUIRE(NodeRefSegment({0, {0.0, 1.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
- REQUIRE(NodeRefSegment({0, {2.0, 2.0}}, {1, {4.0, 0.0}}, nullptr, nullptr).to_left_of(loc) == false);
+ REQUIRE(calculate_intersection(s7, s8) == osmium::Location(1.0, 0.0));
+ REQUIRE(calculate_intersection(s8, s7) == osmium::Location(1.0, 0.0));
+ }
+
+ SECTION("intersection of very long segments") {
+ NodeRefSegment s1({ 1, {90.0, 90.0}}, { 2, {-90.0, -90.0}});
+ NodeRefSegment s2({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}});
+ REQUIRE(calculate_intersection(s1, s2) == osmium::Location(0.0, 0.0));
+
+ NodeRefSegment s3({ 1, {-90.0, -90.0}}, { 2, {90.0, 90.0}});
+ NodeRefSegment s4({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}});
+ REQUIRE(calculate_intersection(s3, s4) == osmium::Location(0.0, 0.0));
+
+ NodeRefSegment s5({ 1, {-90.00000001, -90.0}}, { 2, {90.0, 90.0}});
+ NodeRefSegment s6({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}});
+ REQUIRE(calculate_intersection(s5, s6) == osmium::Location(0.0, 0.0));
}
SECTION("ordering") {
@@ -127,3 +145,64 @@ TEST_CASE("NodeRefSegmentClass") {
}
+TEST_CASE("Ordering of NodeRefSegments") {
+ osmium::NodeRef nr0(0, { 0.0, 0.0 });
+ osmium::NodeRef nr1(1, { 1.0, 0.0 });
+ osmium::NodeRef nr2(2, { 0.0, 1.0 });
+ osmium::NodeRef nr3(3, { 2.0, 0.0 });
+ osmium::NodeRef nr4(4, { 0.0, 2.0 });
+ osmium::NodeRef nr5(5, { 1.0, 1.0 });
+ osmium::NodeRef nr6(6, { 2.0, 2.0 });
+ osmium::NodeRef nr7(6, { 1.0, 2.0 });
+
+ NodeRefSegment s1(nr0, nr1);
+ NodeRefSegment s2(nr0, nr2);
+ NodeRefSegment s3(nr0, nr3);
+ NodeRefSegment s4(nr0, nr4);
+ NodeRefSegment s5(nr0, nr5);
+ NodeRefSegment s6(nr0, nr6);
+ NodeRefSegment s7(nr0, nr7);
+
+ // s1
+ REQUIRE_FALSE(s1 < s1);
+ REQUIRE(s2 < s1);
+ REQUIRE(s1 < s3);
+ REQUIRE(s4 < s1);
+ REQUIRE(s5 < s1);
+ REQUIRE(s6 < s1);
+ REQUIRE(s7 < s1);
+
+ // s2
+ REQUIRE_FALSE(s2 < s2);
+ REQUIRE(s2 < s3);
+ REQUIRE(s2 < s4);
+ REQUIRE(s2 < s5);
+ REQUIRE(s2 < s6);
+ REQUIRE(s2 < s7);
+
+ // s3
+ REQUIRE_FALSE(s3 < s3);
+ REQUIRE(s4 < s3);
+ REQUIRE(s5 < s3);
+ REQUIRE(s6 < s3);
+ REQUIRE(s7 < s3);
+
+ // s4
+ REQUIRE_FALSE(s4 < s4);
+ REQUIRE(s4 < s5);
+ REQUIRE(s4 < s6);
+ REQUIRE(s4 < s7);
+
+ // s5
+ REQUIRE_FALSE(s5 < s5);
+ REQUIRE(s5 < s6);
+ REQUIRE(s7 < s5);
+
+ // s6
+ REQUIRE_FALSE(s6 < s6);
+ REQUIRE(s7 < s6);
+
+ // s7
+ REQUIRE_FALSE(s7 < s7);
+}
+
diff --git a/test/t/basic/test_entity_bits.cpp b/test/t/basic/test_entity_bits.cpp
index f15068b..13de94b 100644
--- a/test/t/basic/test_entity_bits.cpp
+++ b/test/t/basic/test_entity_bits.cpp
@@ -21,6 +21,7 @@ TEST_CASE("entity_bits") {
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));
diff --git a/test/t/basic/test_location.cpp b/test/t/basic/test_location.cpp
index 3fd8d15..c861722 100644
--- a/test/t/basic/test_location.cpp
+++ b/test/t/basic/test_location.cpp
@@ -137,7 +137,7 @@ TEST_CASE("Location") {
}
SECTION("output_defined") {
- osmium::Location p(-3.2, 47.3);
+ osmium::Location p(-3.20, 47.30);
std::stringstream out;
out << p;
REQUIRE(out.str() == "(-3.2,47.3)");
@@ -152,3 +152,17 @@ TEST_CASE("Location") {
}
+TEST_CASE("Location hash") {
+ if (sizeof(size_t) == 8) {
+ REQUIRE(std::hash<osmium::Location>{}({0, 0}) == 0);
+ REQUIRE(std::hash<osmium::Location>{}({0, 1}) == 1);
+ REQUIRE(std::hash<osmium::Location>{}({1, 0}) == 0x100000000);
+ REQUIRE(std::hash<osmium::Location>{}({1, 1}) == 0x100000001);
+ } else {
+ REQUIRE(std::hash<osmium::Location>{}({0, 0}) == 0);
+ REQUIRE(std::hash<osmium::Location>{}({0, 1}) == 1);
+ REQUIRE(std::hash<osmium::Location>{}({1, 0}) == 1);
+ REQUIRE(std::hash<osmium::Location>{}({1, 1}) == 0);
+ }
+}
+
diff --git a/test/t/basic/test_object_comparisons.cpp b/test/t/basic/test_object_comparisons.cpp
index 461969f..42bcbff 100644
--- a/test/t/basic/test_object_comparisons.cpp
+++ b/test/t/basic/test_object_comparisons.cpp
@@ -1,76 +1,80 @@
#include "catch.hpp"
+#include <algorithm>
+#include <functional>
+#include <vector>
+
#include <osmium/builder/attr.hpp>
#include <osmium/builder/osm_object_builder.hpp>
#include <osmium/osm.hpp>
#include <osmium/osm/object_comparisons.hpp>
-TEST_CASE("Object_Comparisons") {
-
- using namespace osmium::builder::attr;
-
- SECTION("order") {
- osmium::memory::Buffer buffer(10 * 1000);
-
- osmium::builder::add_node(buffer, _id(10), _version(1));
- osmium::builder::add_node(buffer, _id(15), _version(2));
-
- auto it = buffer.begin();
- osmium::Node& node1 = static_cast<osmium::Node&>(*it);
- osmium::Node& node2 = static_cast<osmium::Node&>(*(++it));
-
- REQUIRE(node1 < node2);
- REQUIRE_FALSE(node1 > node2);
- node1.set_id(20);
- node1.set_version(1);
- node2.set_id(20);
- node2.set_version(2);
- REQUIRE(node1 < node2);
- REQUIRE_FALSE(node1 > node2);
- node1.set_id(-10);
- node1.set_version(2);
- node2.set_id(-15);
- node2.set_version(1);
- REQUIRE(node1 < node2);
- REQUIRE_FALSE(node1 > node2);
+using namespace osmium::builder::attr;
+
+TEST_CASE("Node comparisons") {
+
+ osmium::memory::Buffer buffer(10 * 1000);
+ std::vector<std::reference_wrapper<osmium::Node>> nodes;
+
+ SECTION("nodes are ordered by id, version, and timestamp") {
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 0), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 1), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 10), _version(2), _timestamp("2016-01-01T00:01:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 10), _version(3), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:01:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 15), _version(1), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(10000000000ll), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+
+ REQUIRE(std::is_sorted(nodes.cbegin(), nodes.cend()));
}
- SECTION("order_types") {
- osmium::memory::Buffer buffer(10 * 1000);
-
- osmium::builder::add_node(buffer, _id(3), _version(3));
- osmium::builder::add_node(buffer, _id(3), _version(4));
- osmium::builder::add_node(buffer, _id(3), _version(4));
- osmium::builder::add_way(buffer, _id(2), _version(2));
- osmium::builder::add_relation(buffer, _id(1), _version(1));
-
- auto it = buffer.begin();
- const osmium::Node& node1 = static_cast<const osmium::Node&>(*it);
- const osmium::Node& node2 = static_cast<const osmium::Node&>(*(++it));
- const osmium::Node& node3 = static_cast<const osmium::Node&>(*(++it));
- const osmium::Way& way = static_cast<const osmium::Way&>(*(++it));
- const osmium::Relation& relation = static_cast<const osmium::Relation&>(*(++it));
-
- REQUIRE(node1 < node2);
- REQUIRE(node2 < way);
- REQUIRE_FALSE(node2 > way);
- REQUIRE(way < relation);
- REQUIRE(node1 < relation);
-
- REQUIRE(osmium::object_order_type_id_version()(node1, node2));
- REQUIRE(osmium::object_order_type_id_reverse_version()(node2, node1));
- REQUIRE(osmium::object_order_type_id_version()(node1, way));
- REQUIRE(osmium::object_order_type_id_reverse_version()(node1, way));
-
- REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, node2));
- REQUIRE(osmium::object_equal_type_id_version()(node2, node3));
-
- REQUIRE(osmium::object_equal_type_id()(node1, node2));
- REQUIRE(osmium::object_equal_type_id()(node2, node3));
-
- REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, way));
- REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, relation));
- REQUIRE_FALSE(osmium::object_equal_type_id()(node1, relation));
+ SECTION("equal nodes are not different") {
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(1), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(1), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+
+ REQUIRE(nodes[0] == nodes[1]);
+ REQUIRE_FALSE(nodes[0] < nodes[1]);
+ REQUIRE_FALSE(nodes[0] > nodes[1]);
+ }
+
+ SECTION("IDs are ordered by absolute value") {
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 0))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 1))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( -1))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 10))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(-10))));
+
+ REQUIRE(std::is_sorted(nodes.cbegin(), nodes.cend()));
+ }
+
+ SECTION("reverse version ordering") {
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 0), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 1), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 10), _version(3), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 10), _version(2), _timestamp("2016-01-01T00:01:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:01:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 15), _version(1), _timestamp("2016-01-01T00:00:00Z"))));
+ nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(10000000000ll), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+
+ REQUIRE(std::is_sorted(nodes.cbegin(), nodes.cend(), osmium::object_order_type_id_reverse_version{}));
}
}
+
+TEST_CASE("Object comparisons") {
+
+ osmium::memory::Buffer buffer(10 * 1000);
+ std::vector<std::reference_wrapper<osmium::OSMObject>> objects;
+
+ SECTION("types are ordered nodes, then ways, then relations") {
+ objects.emplace_back(buffer.get<osmium::Node>( osmium::builder::add_node( buffer, _id(3))));
+ objects.emplace_back(buffer.get<osmium::Way>( osmium::builder::add_way( buffer, _id(2))));
+ objects.emplace_back(buffer.get<osmium::Relation>(osmium::builder::add_relation(buffer, _id(1))));
+
+ REQUIRE(std::is_sorted(objects.cbegin(), objects.cend()));
+ }
+
+}
+
diff --git a/test/t/basic/test_types_from_string.cpp b/test/t/basic/test_types_from_string.cpp
index 2481ae8..2866b8f 100644
--- a/test/t/basic/test_types_from_string.cpp
+++ b/test/t/basic/test_types_from_string.cpp
@@ -31,9 +31,17 @@ TEST_CASE("set type and ID from string") {
REQUIRE(r_2.first == osmium::item_type::relation);
REQUIRE(r_2.second == -2);
- auto x3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr);
- REQUIRE(x3.first == osmium::item_type::undefined);
- REQUIRE(x3.second == 3);
+ auto d3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr);
+ REQUIRE(d3.first == osmium::item_type::undefined);
+ REQUIRE(d3.second == 3);
+
+ auto u3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr, osmium::item_type::undefined);
+ REQUIRE(u3.first == osmium::item_type::undefined);
+ REQUIRE(u3.second == 3);
+
+ auto n3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr, osmium::item_type::node);
+ REQUIRE(n3.first == osmium::item_type::node);
+ REQUIRE(n3.second == 3);
REQUIRE_THROWS_AS(osmium::string_to_object_id("", osmium::osm_entity_bits::nwr), std::range_error);
REQUIRE_THROWS_AS(osmium::string_to_object_id("n", osmium::osm_entity_bits::nwr), std::range_error);
diff --git a/test/t/basic/test_way.cpp b/test/t/basic/test_way.cpp
index c4fa9d5..005ef30 100644
--- a/test/t/basic/test_way.cpp
+++ b/test/t/basic/test_way.cpp
@@ -46,7 +46,7 @@ TEST_CASE("Build way") {
osmium::CRC<boost::crc_32_type> crc32;
crc32.update(way);
- REQUIRE(crc32().checksum() == 0x7676d0c2);
+ REQUIRE(crc32().checksum() == 0x65f6ba91);
}
TEST_CASE("build closed way") {
diff --git a/test/t/builder/test_attr.cpp b/test/t/builder/test_attr.cpp
index 258ae7a..dedde77 100644
--- a/test/t/builder/test_attr.cpp
+++ b/test/t/builder/test_attr.cpp
@@ -67,14 +67,14 @@ TEST_CASE("create node using builders") {
osmium::builder::add_node(buffer, _id(5), _visible(true));
osmium::builder::add_node(buffer, _id(6), _visible(false));
- auto it = buffer.cbegin<osmium::Node>();
+ auto it = buffer.select<osmium::Node>().cbegin();
REQUIRE_FALSE(it++->visible());
REQUIRE_FALSE(it++->visible());
REQUIRE(it++->visible());
REQUIRE(it++->visible());
REQUIRE(it++->visible());
REQUIRE_FALSE(it++->visible());
- REQUIRE(it == buffer.cend<osmium::Node>());
+ REQUIRE(it == buffer.select<osmium::Node>().cend());
}
SECTION("order of attributes doesn't matter") {
diff --git a/test/t/geom/test_geojson.cpp b/test/t/geom/test_geojson.cpp
index a9e8398..725be7f 100644
--- a/test/t/geom/test_geojson.cpp
+++ b/test/t/geom/test_geojson.cpp
@@ -96,7 +96,7 @@ SECTION("area_1outer_0inner") {
REQUIRE(!area.is_multipolygon());
REQUIRE(std::distance(area.cbegin(), area.cend()) == 2);
- REQUIRE(std::distance(area.cbegin<osmium::OuterRing>(), area.cend<osmium::OuterRing>()) == area.num_rings().first);
+ REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
{
std::string json {factory.create_multipolygon(area)};
@@ -112,8 +112,8 @@ SECTION("area_1outer_1inner") {
REQUIRE(!area.is_multipolygon());
REQUIRE(std::distance(area.cbegin(), area.cend()) == 3);
- REQUIRE(std::distance(area.cbegin<osmium::OuterRing>(), area.cend<osmium::OuterRing>()) == area.num_rings().first);
- REQUIRE(std::distance(area.cbegin<osmium::InnerRing>(), area.cend<osmium::InnerRing>()) == area.num_rings().second);
+ REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
+ REQUIRE(area.subitems<osmium::InnerRing>().size() == area.num_rings().second);
{
std::string json {factory.create_multipolygon(area)};
@@ -129,24 +129,24 @@ SECTION("area_2outer_2inner") {
REQUIRE(area.is_multipolygon());
REQUIRE(std::distance(area.cbegin(), area.cend()) == 5);
- REQUIRE(std::distance(area.cbegin<osmium::OuterRing>(), area.cend<osmium::OuterRing>()) == area.num_rings().first);
- REQUIRE(std::distance(area.cbegin<osmium::InnerRing>(), area.cend<osmium::InnerRing>()) == area.num_rings().second);
+ REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
+ REQUIRE(area.subitems<osmium::InnerRing>().size() == area.num_rings().second);
int outer_ring=0;
int inner_ring=0;
- for (auto it_outer = area.cbegin<osmium::OuterRing>(); it_outer != area.cend<osmium::OuterRing>(); ++it_outer) {
+ for (const auto& outer : area.outer_rings()) {
if (outer_ring == 0) {
- REQUIRE(it_outer->front().ref() == 1);
+ REQUIRE(outer.front().ref() == 1);
} else if (outer_ring == 1) {
- REQUIRE(it_outer->front().ref() == 100);
+ REQUIRE(outer.front().ref() == 100);
} else {
REQUIRE(false);
}
- for (auto it_inner = area.inner_ring_cbegin(it_outer); it_inner != area.inner_ring_cend(it_outer); ++it_inner) {
+ for (const auto& inner : area.inner_rings(outer)) {
if (outer_ring == 0 && inner_ring == 0) {
- REQUIRE(it_inner->front().ref() == 5);
+ REQUIRE(inner.front().ref() == 5);
} else if (outer_ring == 0 && inner_ring == 1) {
- REQUIRE(it_inner->front().ref() == 10);
+ REQUIRE(inner.front().ref() == 10);
} else {
REQUIRE(false);
}
diff --git a/test/t/geom/test_geos.cpp b/test/t/geom/test_geos.cpp
index 8bf11c9..d85a603 100644
--- a/test/t/geom/test_geos.cpp
+++ b/test/t/geom/test_geos.cpp
@@ -89,6 +89,7 @@ TEST_CASE("GEOS geometry factory - create area with one outer and no inner rings
REQUIRE(1 == mp->getNumGeometries());
const geos::geom::Polygon* p0 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(0));
+ REQUIRE(p0);
REQUIRE(0 == p0->getNumInteriorRing());
const geos::geom::LineString* l0e = p0->getExteriorRing();
@@ -108,6 +109,7 @@ TEST_CASE("GEOS geometry factory - create area with one outer and one inner ring
REQUIRE(1 == mp->getNumGeometries());
const geos::geom::Polygon* p0 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(0));
+ REQUIRE(p0);
REQUIRE(1 == p0->getNumInteriorRing());
const geos::geom::LineString* l0e = p0->getExteriorRing();
@@ -127,12 +129,14 @@ TEST_CASE("GEOS geometry factory - create area with two outer and two inner ring
REQUIRE(2 == mp->getNumGeometries());
const geos::geom::Polygon* p0 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(0));
+ REQUIRE(p0);
REQUIRE(2 == p0->getNumInteriorRing());
const geos::geom::LineString* l0e = p0->getExteriorRing();
REQUIRE(5 == l0e->getNumPoints());
const geos::geom::Polygon* p1 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(1));
+ REQUIRE(p1);
REQUIRE(0 == p1->getNumInteriorRing());
const geos::geom::LineString* l1e = p1->getExteriorRing();
diff --git a/test/t/geom/test_ogr.cpp b/test/t/geom/test_ogr.cpp
index 26d34c1..3490c57 100644
--- a/test/t/geom/test_ogr.cpp
+++ b/test/t/geom/test_ogr.cpp
@@ -68,6 +68,7 @@ SECTION("area_1outer_0inner") {
REQUIRE(1 == mp->getNumGeometries());
const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+ REQUIRE(p0);
REQUIRE(0 == p0->getNumInteriorRings());
const OGRLineString* l0e = p0->getExteriorRing();
@@ -86,6 +87,7 @@ SECTION("area_1outer_1inner") {
REQUIRE(1 == mp->getNumGeometries());
const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+ REQUIRE(p0);
REQUIRE(1 == p0->getNumInteriorRings());
const OGRLineString* l0e = p0->getExteriorRing();
@@ -105,12 +107,14 @@ SECTION("area_2outer_2inner") {
REQUIRE(2 == mp->getNumGeometries());
const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+ REQUIRE(p0);
REQUIRE(2 == p0->getNumInteriorRings());
const OGRLineString* l0e = p0->getExteriorRing();
REQUIRE(5 == l0e->getNumPoints());
const OGRPolygon* p1 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(1));
+ REQUIRE(p1);
REQUIRE(0 == p1->getNumInteriorRings());
const OGRLineString* l1e = p1->getExteriorRing();
diff --git a/test/t/index/test_file_based_index.cpp b/test/t/index/test_file_based_index.cpp
new file mode 100644
index 0000000..42cf574
--- /dev/null
+++ b/test/t/index/test_file_based_index.cpp
@@ -0,0 +1,155 @@
+
+#include "catch.hpp"
+
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/index/detail/tmpfile.hpp>
+#include <osmium/util/file.hpp>
+
+#include <osmium/index/map/dense_file_array.hpp>
+#include <osmium/index/map/sparse_file_array.hpp>
+
+#include <osmium/index/node_locations_map.hpp>
+
+TEST_CASE("File based index") {
+
+ int fd = osmium::detail::create_tmp_file();
+
+ REQUIRE(osmium::util::file_size(fd) == 0);
+
+ const osmium::unsigned_object_id_type id1 = 6;
+ const osmium::unsigned_object_id_type id2 = 3;
+ const osmium::Location loc1(1.2, 4.5);
+ const osmium::Location loc2(3.5, -7.2);
+
+ SECTION("dense index") {
+ using index_type = osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location>;
+ constexpr const size_t S = sizeof(index_type::element_type);
+
+ {
+ index_type index(fd);
+
+ REQUIRE(index.size() == 0);
+
+ REQUIRE_THROWS_AS(index.get( 0), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 1), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 3), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 5), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 6), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 7), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+ index.set(id1, loc1);
+ REQUIRE(index.size() == 7);
+
+ index.set(id2, loc2);
+ REQUIRE(index.size() == 7);
+
+ index.sort();
+
+ REQUIRE(loc1 == index.get(id1));
+ REQUIRE(loc2 == index.get(id2));
+
+ REQUIRE_THROWS_AS(index.get( 0), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 1), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 5), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 7), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+ REQUIRE(index.size() == 7);
+ REQUIRE(std::distance(index.cbegin(), index.cend()) == 7);
+
+ REQUIRE(osmium::util::file_size(fd) >= (6 * S));
+ }
+
+ {
+ index_type index(fd);
+ REQUIRE(osmium::util::file_size(fd) >= (6 * S));
+
+ REQUIRE(index.size() == 7);
+
+ REQUIRE(loc1 == index.get(id1));
+ REQUIRE(loc2 == index.get(id2));
+
+ REQUIRE_THROWS_AS(index.get( 0), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 1), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 5), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 7), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+ REQUIRE(index.size() == 7);
+ REQUIRE(std::distance(index.cbegin(), index.cend()) == 7);
+
+ auto it = index.cbegin();
+ REQUIRE(*it++ == osmium::Location{});
+ REQUIRE(*it++ == osmium::Location{});
+ REQUIRE(*it++ == osmium::Location{});
+ REQUIRE(*it++ == loc2);
+ REQUIRE(*it++ == osmium::Location{});
+ REQUIRE(*it++ == osmium::Location{});
+ REQUIRE(*it++ == loc1);
+ REQUIRE(it++ == index.cend());
+ }
+ }
+
+ SECTION("sparse index") {
+ using index_type = osmium::index::map::SparseFileArray<osmium::unsigned_object_id_type, osmium::Location>;
+ constexpr const size_t S = sizeof(index_type::element_type);
+
+ {
+ index_type index(fd);
+
+ REQUIRE(index.size() == 0);
+
+ REQUIRE_THROWS_AS(index.get( 0), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 1), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 3), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 5), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 6), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 7), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+ index.set(id1, loc1);
+ REQUIRE(index.size() == 1);
+
+ index.set(id2, loc2);
+ REQUIRE(index.size() == 2);
+
+ index.sort();
+
+ REQUIRE(loc1 == index.get(id1));
+ REQUIRE(loc2 == index.get(id2));
+
+ REQUIRE_THROWS_AS(index.get( 0), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 1), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 5), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 7), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+ REQUIRE(index.size() == 2);
+ REQUIRE(std::distance(index.cbegin(), index.cend()) == 2);
+
+ REQUIRE(osmium::util::file_size(fd) >= (2 * S));
+ }
+
+ {
+ index_type index(fd);
+ REQUIRE(osmium::util::file_size(fd) >= (2 * S));
+
+ REQUIRE(index.size() == 2);
+
+ REQUIRE(loc1 == index.get(id1));
+ REQUIRE(loc2 == index.get(id2));
+
+ REQUIRE_THROWS_AS(index.get( 0), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 1), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 5), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get( 7), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+ REQUIRE(index.size() == 2);
+ REQUIRE(std::distance(index.cbegin(), index.cend()) == 2);
+ }
+ }
+}
+
diff --git a/test/t/index/test_id_to_location.cpp b/test/t/index/test_id_to_location.cpp
index 4aca238..90cce3b 100644
--- a/test/t/index/test_id_to_location.cpp
+++ b/test/t/index/test_id_to_location.cpp
@@ -29,6 +29,8 @@ void test_func_all(TIndex& index) {
index.sort();
+ REQUIRE_THROWS_AS(index.get(0), osmium::not_found);
+ 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);
}
@@ -48,12 +50,20 @@ void test_func_real(TIndex& index) {
REQUIRE(loc1 == index.get(id1));
REQUIRE(loc2 == index.get(id2));
+ REQUIRE_THROWS_AS(index.get(0), osmium::not_found);
+ 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);
index.clear();
REQUIRE_THROWS_AS(index.get(id1), osmium::not_found);
+ REQUIRE_THROWS_AS(index.get(id2), osmium::not_found);
+
+ REQUIRE_THROWS_AS(index.get(0), osmium::not_found);
+ 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);
}
TEST_CASE("IdToLocation") {
diff --git a/test/t/io/test_writer.cpp b/test/t/io/test_writer.cpp
index b56dfeb..d3c2836 100644
--- a/test/t/io/test_writer.cpp
+++ b/test/t/io/test_writer.cpp
@@ -18,9 +18,9 @@ TEST_CASE("Writer") {
osmium::memory::Buffer buffer = reader.read();
REQUIRE(buffer);
REQUIRE(buffer.committed() > 0);
- auto num = std::distance(buffer.cbegin<osmium::OSMObject>(), buffer.cend<osmium::OSMObject>());
+ auto num = std::distance(buffer.select<osmium::OSMObject>().cbegin(), buffer.select<osmium::OSMObject>().cend());
REQUIRE(num > 0);
- REQUIRE(buffer.cbegin<osmium::OSMObject>()->id() == 1);
+ REQUIRE(buffer.select<osmium::OSMObject>().cbegin()->id() == 1);
std::string filename;
@@ -81,9 +81,8 @@ TEST_CASE("Writer") {
osmium::memory::Buffer buffer_check = reader_check.read();
REQUIRE(buffer_check);
REQUIRE(buffer_check.committed() > 0);
- REQUIRE(std::distance(buffer_check.cbegin<osmium::OSMObject>(), buffer_check.cend<osmium::OSMObject>()) == num);
- REQUIRE(buffer_check.cbegin<osmium::OSMObject>()->id() == 1);
-
+ REQUIRE(buffer_check.select<osmium::OSMObject>().size() == num);
+ REQUIRE(buffer_check.select<osmium::OSMObject>().cbegin()->id() == 1);
}
SECTION("Interrupted writer after open") {
diff --git a/test/t/io/test_writer_with_mock_compression.cpp b/test/t/io/test_writer_with_mock_compression.cpp
index c2d3bbd..10c19c4 100644
--- a/test/t/io/test_writer_with_mock_compression.cpp
+++ b/test/t/io/test_writer_with_mock_compression.cpp
@@ -56,8 +56,7 @@ TEST_CASE("Write with mock compressor") {
osmium::memory::Buffer buffer = reader.read();
REQUIRE(buffer);
REQUIRE(buffer.committed() > 0);
- auto num = std::distance(buffer.cbegin<osmium::OSMObject>(), buffer.cend<osmium::OSMObject>());
- REQUIRE(num > 0);
+ REQUIRE(buffer.select<osmium::OSMObject>().size() > 0);
SECTION("fail on construction") {
diff --git a/test/t/io/test_writer_with_mock_encoder.cpp b/test/t/io/test_writer_with_mock_encoder.cpp
index a43d591..d059f6b 100644
--- a/test/t/io/test_writer_with_mock_encoder.cpp
+++ b/test/t/io/test_writer_with_mock_encoder.cpp
@@ -62,8 +62,7 @@ TEST_CASE("Test Writer with MockOutputFormat") {
osmium::memory::Buffer buffer = reader.read();
REQUIRE(buffer);
REQUIRE(buffer.committed() > 0);
- auto num = std::distance(buffer.cbegin<osmium::OSMObject>(), buffer.cend<osmium::OSMObject>());
- REQUIRE(num > 0);
+ REQUIRE(buffer.select<osmium::OSMObject>().size() > 0);
SECTION("error in header") {
diff --git a/test/t/tags/test_tag_list.cpp b/test/t/tags/test_tag_list.cpp
index 470a200..3bdf5e9 100644
--- a/test/t/tags/test_tag_list.cpp
+++ b/test/t/tags/test_tag_list.cpp
@@ -167,10 +167,16 @@ TEST_CASE("create tag list") {
});
}
- const osmium::TagList& tl = *buffer.begin<osmium::TagList>();
+ const osmium::TagList& tl = *buffer.select<osmium::TagList>().cbegin();
REQUIRE(osmium::item_type::tag_list == tl.type());
REQUIRE(2 == tl.size());
+ REQUIRE(tl.has_key("highway"));
+ REQUIRE_FALSE(tl.has_key("unknown"));
+ REQUIRE(tl.has_tag("highway", "primary"));
+ REQUIRE_FALSE(tl.has_tag("highway", "false"));
+ REQUIRE_FALSE(tl.has_tag("foo", "bar"));
+
auto it = tl.begin();
REQUIRE(std::string("highway") == it->key());
REQUIRE(std::string("primary") == it->value());
diff --git a/test/t/util/test_memory_mapping.cpp b/test/t/util/test_memory_mapping.cpp
index 29893f7..647d2a0 100644
--- a/test/t/util/test_memory_mapping.cpp
+++ b/test/t/util/test_memory_mapping.cpp
@@ -33,15 +33,10 @@ TEST_CASE("anonymous mapping") {
mapping.unmap(); // second unmap is okay
}
- SECTION("memory mapping of zero length should work") {
- osmium::util::MemoryMapping mapping(0, osmium::util::MemoryMapping::mapping_mode::write_private);
- REQUIRE(mapping.get_addr() != nullptr);
-
- REQUIRE(mapping.size() == osmium::util::get_pagesize());
-
- REQUIRE(!!mapping);
- mapping.unmap();
- REQUIRE(!mapping);
+ SECTION("memory mapping of zero length should fail") {
+ REQUIRE_THROWS({
+ osmium::util::MemoryMapping mapping(0, osmium::util::MemoryMapping::mapping_mode::write_private);
+ });
}
SECTION("moving a memory mapping should work") {
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/libosmium.git
More information about the Pkg-grass-devel
mailing list