From 4e78329317ff0784cfb8a3441e99ee5f4d0742bd Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 17 Mar 2016 10:26:37 +0100 Subject: [PATCH 01/43] started range analysis --- include/engine/plugins/range_analysis.hpp | 136 +++++++++++++++++ .../engine/routing_algorithms/one_to_many.hpp | 138 ++++++++++++++++++ include/engine/search_engine.hpp | 5 +- src/engine/engine.cpp | 2 + 4 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 include/engine/plugins/range_analysis.hpp create mode 100644 include/engine/routing_algorithms/one_to_many.hpp diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp new file mode 100644 index 00000000000..7820be2da9e --- /dev/null +++ b/include/engine/plugins/range_analysis.hpp @@ -0,0 +1,136 @@ +// +// Created by robin on 3/10/16. +// + +#ifndef OSRM_TEST_PLUGIN_HPP +#define OSRM_TEST_PLUGIN_HPP + +#include "engine/plugins/plugin_base.hpp" +#include "engine/search_engine.hpp" +#include "engine/object_encoder.hpp" + +#include "util/make_unique.hpp" +#include "util/string_util.hpp" +#include "util/simple_logger.hpp" +#include "osrm/json_container.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +template class RangeAnalysis final : public BasePlugin +{ + + struct NodeBucket + { + NodeID predecessor; + EdgeWeight distance; + NodeBucket(const NodeID predecessor, const EdgeWeight distance) + : predecessor(predecessor), distance(distance) + { + } + }; + using ResultMap = std::unordered_map; + + private: + std::string temp_string; + std::string descriptor_string; + std::unique_ptr> search_engine_ptr; + DataFacadeT *facade; + ResultMap resultMap; + + public: + explicit RangeAnalysis(DataFacadeT *facade) : descriptor_string("range"), facade(facade) + { + search_engine_ptr = util::make_unique>(facade); + } + + virtual ~RangeAnalysis() {} + + const std::string GetDescriptor() const override final { return descriptor_string; } + + Status HandleRequest(const RouteParameters &routeParameters, + util::json::Object &json_result) override final + { + /* Check if valid + * + */ + if (routeParameters.coordinates.size() != 1) + { + json_result.values["status_message"] = "Number of Coordinates should be 1"; + return Status::Error; + } + if (!routeParameters.coordinates.front().IsValid()) + { + json_result.values["status_message"] = "Coordinate is invalid"; + return Status::Error; + } + + const auto &input_bearings = routeParameters.bearings; + if (input_bearings.size() > 0 && + routeParameters.coordinates.size() != input_bearings.size()) + { + json_result.values["status_message"] = + "Number of bearings does not match number of coordinate"; + return Status::Error; + } + + /** Interesting code starts here + * + */ + + // auto number_of_results = static_cast(routeParameters.num_results); + const int bearing = input_bearings.size() > 0 ? input_bearings.front().first : 0; + const int range = + input_bearings.size() > 0 + ? (input_bearings.front().second ? *input_bearings.front().second : 10) + : 180; + + std::vector phantomNodeVector; + auto phantomNodePair = facade->NearestPhantomNodeWithAlternativeFromBigComponent( + routeParameters.coordinates.front(), bearing, range); + + phantomNodeVector.push_back(phantomNodePair); + auto snapped_source_phantoms = snapPhantomNodes(phantomNodeVector); + // resultMap = search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); + search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); + + std::string temp_string; + json_result.values["title"] = "This is a Test-Plugin"; + + temp_string = std::to_string(routeParameters.coordinates.size()); + json_result.values["location_count"] = temp_string; + temp_string = std::to_string(resultMap.size()); + json_result.values["Result-Map"] = temp_string; + + for (auto it = resultMap.begin(); it != resultMap.end(); ++it) + { + + util::SimpleLogger().Write() << "Node-ID: " << it->first; + NodeBucket n = it->second; + util::SimpleLogger().Write() << "Predecessor:" << n.predecessor; + util::SimpleLogger().Write() << "Distance: " << n.distance; + } + // util::json::Array json_coordinate; + // util::json::Object result; + // json_coordinate.values.push_back(node.location.lat / COORDINATE_PRECISION); + // json_coordinate.values.push_back(node.location.lon / COORDINATE_PRECISION); + // result.values["mapped coordinate"] = json_coordinate; + // result.values["name"] = facade->get_name_for_id(node.name_id); + return Status::Ok; + } +}; +} +} +} +#endif // OSRM_TEST_PLUGIN_HPP diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp new file mode 100644 index 00000000000..70eb2ba74b5 --- /dev/null +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -0,0 +1,138 @@ +// +// Created by robin on 3/16/16. +// + +#ifndef OSRM_ONE_TO_MANY_HPP +#define OSRM_ONE_TO_MANY_HPP + +#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/search_engine_data.hpp" +#include "util/typedefs.hpp" +#include "util/simple_logger.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace routing_algorithms +{ + +template +class OneToManyRouting final + : public BasicRoutingInterface> +{ + using super = BasicRoutingInterface>; + using QueryHeap = SearchEngineData::QueryHeap; + using PredecessorMap = std::unordered_map; + using DistanceMap = std::unordered_map; + SearchEngineData &engine_working_data; + + + public: + OneToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) + : super(facade), engine_working_data(engine_working_data) + { + } + + ~OneToManyRouting() {} + + + void operator()(PhantomNode &phantomSource, const int distance) const + { + + PredecessorMap predecessorMap; + DistanceMap distanceMap; + engine_working_data.InitializeOrClearFirstThreadLocalStorage( + super::facade->GetNumberOfNodes()); + + QueryHeap &query_heap = *(engine_working_data.forward_heap_1); + + query_heap.Clear(); + // insert target(s) at distance 0 + if (SPECIAL_NODEID != phantomSource.forward_node_id) + { + query_heap.Insert(phantomSource.forward_node_id, + -phantomSource.GetForwardWeightPlusOffset(), + phantomSource.forward_node_id); + } + if (SPECIAL_NODEID != phantomSource.reverse_node_id) + { + query_heap.Insert(phantomSource.reverse_node_id, + -phantomSource.GetReverseWeightPlusOffset(), + phantomSource.reverse_node_id); + } + + // explore search space + while (!query_heap.Empty()) + { + ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); + } + for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) + { + util::SimpleLogger().Write() << "Node-ID: " << it->first; + util::SimpleLogger().Write() << "Predecessor:" << it->second; + util::SimpleLogger().Write() << "Distance: " << distanceMap[it->first]; +// resultMap[it->first].insert(std::make_pair(it->second, distanceMap[it->first])); +// resultMap[it->first].emplace_back(it->second, distanceMap[it->first]); + } + } + + void ForwardRoutingStep(QueryHeap &query_heap, + const int distance, + PredecessorMap &predecessorMap, + DistanceMap &distanceMap) const + { + const NodeID node = query_heap.DeleteMin(); + const int source_distance = query_heap.GetKey(node); + for (auto edge : super::facade->GetAdjacentEdgeRange(node)) + { + const auto &data = super::facade->GetEdgeData(edge); + const NodeID to = super::facade->GetTarget(edge); + const int edge_weight = data.distance; + BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); + + if (query_heap.WasInserted(to)) + { + if (query_heap.GetKey(to) + edge_weight < source_distance) + { + continue; + } + } + const int to_distance = source_distance + edge_weight; + + if (to_distance > distance) + { + continue; + } + + // New Node discovered -> Add to Heap + Node Info Storage + if (!query_heap.WasInserted(to)) + { + query_heap.Insert(to, to_distance, node); + predecessorMap[to] = node; + distanceMap[to] = to_distance; + } + // Found a shorter Path -> Update distance + else if (to_distance < query_heap.GetKey(to)) + { + // new parent + query_heap.GetData(to).parent = node; + query_heap.DecreaseKey(to, to_distance); + + predecessorMap[to] = node; + distanceMap[to] = to_distance; + } + } + } +}; +} +} +} +#endif // OSRM_ONE_TO_MANY_HPP diff --git a/include/engine/search_engine.hpp b/include/engine/search_engine.hpp index c15b8535fc2..afb78c293f2 100644 --- a/include/engine/search_engine.hpp +++ b/include/engine/search_engine.hpp @@ -7,6 +7,7 @@ #include "engine/routing_algorithms/map_matching.hpp" #include "engine/routing_algorithms/shortest_path.hpp" #include "engine/routing_algorithms/direct_shortest_path.hpp" +#include "engine/routing_algorithms/one_to_many.hpp" #include @@ -27,12 +28,14 @@ template class SearchEngine routing_algorithms::AlternativeRouting alternative_path; routing_algorithms::ManyToManyRouting distance_table; routing_algorithms::MapMatching map_matching; + routing_algorithms::OneToManyRouting oneToMany; explicit SearchEngine(DataFacadeT *facade) : facade(facade), shortest_path(facade, engine_working_data), direct_shortest_path(facade, engine_working_data), alternative_path(facade, engine_working_data), - distance_table(facade, engine_working_data), map_matching(facade, engine_working_data) + distance_table(facade, engine_working_data), map_matching(facade, engine_working_data), + oneToMany(facade, engine_working_data) { static_assert(!std::is_pointer::value, "don't instantiate with ptr type"); static_assert(std::is_object::value, diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 14bd19ecffe..a12d6ce9669 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -4,6 +4,7 @@ #include "engine/plugins/distance_table.hpp" #include "engine/plugins/hello_world.hpp" +#include "engine/plugins/range_analysis.hpp" #include "engine/plugins/nearest.hpp" #include "engine/plugins/timestamp.hpp" #include "engine/plugins/trip.hpp" @@ -65,6 +66,7 @@ Engine::Engine(EngineConfig &config) RegisterPlugin( new plugins::RoundTripPlugin(query_data_facade, config.max_locations_trip)); RegisterPlugin(new plugins::TilePlugin(query_data_facade)); + RegisterPlugin(new plugins::RangeAnalysis(query_data_facade)); } void Engine::RegisterPlugin(plugins::BasePlugin *raw_plugin_ptr) From 6025b4651b45be309b72e9625b8be0084a8b7abd Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 17 Mar 2016 11:51:33 +0100 Subject: [PATCH 02/43] first implementation of routing algo with pretty json-print --- include/engine/plugins/range_analysis.hpp | 62 ++++++++++--------- .../engine/routing_algorithms/one_to_many.hpp | 17 ++--- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp index 7820be2da9e..ba380cdcf4a 100644 --- a/include/engine/plugins/range_analysis.hpp +++ b/include/engine/plugins/range_analysis.hpp @@ -31,23 +31,16 @@ namespace plugins template class RangeAnalysis final : public BasePlugin { - struct NodeBucket - { - NodeID predecessor; - EdgeWeight distance; - NodeBucket(const NodeID predecessor, const EdgeWeight distance) - : predecessor(predecessor), distance(distance) - { - } - }; - using ResultMap = std::unordered_map; + using PredecessorMap = std::unordered_map; + using DistanceMap = std::unordered_map; private: std::string temp_string; std::string descriptor_string; std::unique_ptr> search_engine_ptr; DataFacadeT *facade; - ResultMap resultMap; + PredecessorMap predecessorMap; + DistanceMap distanceMap; public: explicit RangeAnalysis(DataFacadeT *facade) : descriptor_string("range"), facade(facade) @@ -102,31 +95,40 @@ template class RangeAnalysis final : public BasePlugin phantomNodeVector.push_back(phantomNodePair); auto snapped_source_phantoms = snapPhantomNodes(phantomNodeVector); - // resultMap = search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); - search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); + search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000, predecessorMap, + distanceMap); - std::string temp_string; - json_result.values["title"] = "This is a Test-Plugin"; + BOOST_ASSERT(predecessorMap.size() == distanceMap.size()); - temp_string = std::to_string(routeParameters.coordinates.size()); - json_result.values["location_count"] = temp_string; - temp_string = std::to_string(resultMap.size()); - json_result.values["Result-Map"] = temp_string; + std::string temp_string; + json_result.values["title"] = "Range Analysis"; - for (auto it = resultMap.begin(); it != resultMap.end(); ++it) + util::json::Array data; + for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) { + util::json::Object object; - util::SimpleLogger().Write() << "Node-ID: " << it->first; - NodeBucket n = it->second; - util::SimpleLogger().Write() << "Predecessor:" << n.predecessor; - util::SimpleLogger().Write() << "Distance: " << n.distance; + util::json::Object source; + FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); + source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; + source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; + object.values["Source"] = std::move(source); + + util::json::Object predecessor; + FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); + predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; + predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; + object.values["Predecessor"] = std::move(predecessor); + + util::json::Object distance; + object.values["distance_from_start"] = distanceMap[it->first]; + + data.values.push_back(object); } - // util::json::Array json_coordinate; - // util::json::Object result; - // json_coordinate.values.push_back(node.location.lat / COORDINATE_PRECISION); - // json_coordinate.values.push_back(node.location.lon / COORDINATE_PRECISION); - // result.values["mapped coordinate"] = json_coordinate; - // result.values["name"] = facade->get_name_for_id(node.name_id); + temp_string = std::to_string(distanceMap.size()); + json_result.values["Nodes Found"] = temp_string;; + json_result.values["Range-Analysis"] = std::move(data); + return Status::Ok; } }; diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp index 70eb2ba74b5..436c573973a 100644 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -34,7 +34,6 @@ class OneToManyRouting final using DistanceMap = std::unordered_map; SearchEngineData &engine_working_data; - public: OneToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) : super(facade), engine_working_data(engine_working_data) @@ -43,12 +42,12 @@ class OneToManyRouting final ~OneToManyRouting() {} - - void operator()(PhantomNode &phantomSource, const int distance) const + void operator()(PhantomNode &phantomSource, + const int distance, + PredecessorMap &predecessorMap, + DistanceMap &distanceMap) const { - PredecessorMap predecessorMap; - DistanceMap distanceMap; engine_working_data.InitializeOrClearFirstThreadLocalStorage( super::facade->GetNumberOfNodes()); @@ -74,14 +73,6 @@ class OneToManyRouting final { ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); } - for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) - { - util::SimpleLogger().Write() << "Node-ID: " << it->first; - util::SimpleLogger().Write() << "Predecessor:" << it->second; - util::SimpleLogger().Write() << "Distance: " << distanceMap[it->first]; -// resultMap[it->first].insert(std::make_pair(it->second, distanceMap[it->first])); -// resultMap[it->first].emplace_back(it->second, distanceMap[it->first]); - } } void ForwardRoutingStep(QueryHeap &query_heap, From f552336790b5f07adfb1fc69f83b8a4cc2b8d42e Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 23 Mar 2016 12:20:58 +0100 Subject: [PATCH 03/43] added testing --- features/bicycle/range.feature | 21 ++++ features/step_definitions/range.rb | 114 ++++++++++++++++++ features/support/route.rb | 8 ++ include/engine/plugins/range_analysis.hpp | 6 +- .../engine/routing_algorithms/one_to_many.hpp | 5 +- 5 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 features/bicycle/range.feature create mode 100644 features/step_definitions/range.rb diff --git a/features/bicycle/range.feature b/features/bicycle/range.feature new file mode 100644 index 00000000000..8b0b958c341 --- /dev/null +++ b/features/bicycle/range.feature @@ -0,0 +1,21 @@ +@range +Feature: Getting the range limited by Distance + + Background: + Given the profile "bicycle" + + Scenario: Range + Given the node map + | a | | b | + | | | | + | | | c | + + And the ways + | nodes | + | ab | + | ac | + + When I request range I should get + |source | node | pred | + | a | b | a | + | a | c | a | diff --git a/features/step_definitions/range.rb b/features/step_definitions/range.rb new file mode 100644 index 00000000000..9537158ee38 --- /dev/null +++ b/features/step_definitions/range.rb @@ -0,0 +1,114 @@ +When /^I request range I should get$/ do |table| + reprocess + actual = [] + OSRMLoader.load(self,"#{contracted_file}.osrm") do + table.hashes.each_with_index do |row,ri| + source = find_node_by_name row['source'] #a + raise "*** unknown in-node '#{row['source']}" unless source + + node = find_node_by_name row['node'] #b + raise "*** unknown in-node '#{row['node']}" unless node + + pred = find_node_by_name row['pred'] #a + raise "*** unknown in-node '#{row['pred']}" unless pred + + # dist = row['distance'] + # raise "*** unknown in-node '#{row['distance']}" unless dist + + #print(source_node) + response = request_range source, @query_params + if response.code == "200" && response.body.empty? == false + json = JSON.parse response.body + # print(json) + if json['status'] == 200 + size = json['Nodes found'] + range = json['Range-Analysis'] + end + end + + #print(size) + # got = {'source' => row['source'], + # 'node' => p1, + # 'pred' => p2} + # 'distance' => retDis} + + # print(range) + ok = false + for coordinate in range; + p1 = Array.new + p2 = Array.new + + + p1 << coordinate['p1']['lat'] + p1 << coordinate['p1']['lon'] + p2 << coordinate['p2']['lat'] + p2 << coordinate['p2']['lon'] + + got = {'source' => row['source'], + 'node' => p1, + 'pred' => p2} + + print(p1) + print(p2) + print("fuu"); + + if FuzzyMatch.match_location(p1, node) && FuzzyMatch.match_location(p2, pred) || ok + print("asdasdasd") + key = 'node' + got[key] = row[key] + key = 'pred' + got[key] = row[key] + ok = true + else + key = 'node' + row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + key = 'pred' + row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + end + end + + # row.keys.each do |key| + # if key=='node' + # if FuzzyMatch.match_location p1, node + # got[key] = row[key] + # ok = true + # else + # row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + # end + # end + # if key=='pred' + # if FuzzyMatch.match_location p2, pred + # got[key] = row[key] + # ok = true + # else + # row[key] = "#{row[key]} [#{pred.lat},#{pred.lon}]" + # end + # end + # if key=='distance' + # if retDis == dist + # got[key] = row[key] + # else + # row[key] = dist + # ok = false + # end + # end + # end + + # end + + + # print(p1) + # print(p2) + # print(node) + + + + unless ok + failed = { :attempt => 'range', :query => @query, :response => response } + log_fail row,got,[failed] + end + actual << got + end + end + table.diff! actual +end diff --git a/features/support/route.rb b/features/support/route.rb index a0deacb8f86..83e549b219c 100644 --- a/features/support/route.rb +++ b/features/support/route.rb @@ -64,6 +64,14 @@ def request_nearest node, user_params params << ["loc", "#{node.lat},#{node.lon}"] return request_path "nearest", params + end + +def request_range node, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + params << ["loc", "#{node.lat},#{node.lon}"] + + return request_path "range", params end def request_table waypoints, user_params diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp index ba380cdcf4a..04533cbe318 100644 --- a/include/engine/plugins/range_analysis.hpp +++ b/include/engine/plugins/range_analysis.hpp @@ -103,6 +103,8 @@ template class RangeAnalysis final : public BasePlugin std::string temp_string; json_result.values["title"] = "Range Analysis"; + json_result.values["start"] = routeParameters.coordinates[0].lat; + util::json::Array data; for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) { @@ -112,13 +114,13 @@ template class RangeAnalysis final : public BasePlugin FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; - object.values["Source"] = std::move(source); + object.values["p1"] = std::move(source); util::json::Object predecessor; FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; - object.values["Predecessor"] = std::move(predecessor); + object.values["p2"] = std::move(predecessor); util::json::Object distance; object.values["distance_from_start"] = distanceMap[it->first]; diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp index 436c573973a..4ab26b9db9c 100644 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -52,6 +52,7 @@ class OneToManyRouting final super::facade->GetNumberOfNodes()); QueryHeap &query_heap = *(engine_working_data.forward_heap_1); + QueryHeap &backward_heap = *(engine_working_data.reverse_heap_1); query_heap.Clear(); // insert target(s) at distance 0 @@ -68,10 +69,12 @@ class OneToManyRouting final phantomSource.reverse_node_id); } + util::SimpleLogger().Write() << phantomSource; // explore search space while (!query_heap.Empty()) { - ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); +// ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); + ForwardRoutingStep(query_heap, backward_heap, ) } } From e887242d3c74a74c8de4e5c845181f20b4335162 Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 17 Mar 2016 10:26:37 +0100 Subject: [PATCH 04/43] started range analysis --- include/engine/plugins/range_analysis.hpp | 136 +++++++++++++++++ .../engine/routing_algorithms/one_to_many.hpp | 138 ++++++++++++++++++ src/engine/engine.cpp | 8 +- 3 files changed, 276 insertions(+), 6 deletions(-) create mode 100644 include/engine/plugins/range_analysis.hpp create mode 100644 include/engine/routing_algorithms/one_to_many.hpp diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp new file mode 100644 index 00000000000..7820be2da9e --- /dev/null +++ b/include/engine/plugins/range_analysis.hpp @@ -0,0 +1,136 @@ +// +// Created by robin on 3/10/16. +// + +#ifndef OSRM_TEST_PLUGIN_HPP +#define OSRM_TEST_PLUGIN_HPP + +#include "engine/plugins/plugin_base.hpp" +#include "engine/search_engine.hpp" +#include "engine/object_encoder.hpp" + +#include "util/make_unique.hpp" +#include "util/string_util.hpp" +#include "util/simple_logger.hpp" +#include "osrm/json_container.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +template class RangeAnalysis final : public BasePlugin +{ + + struct NodeBucket + { + NodeID predecessor; + EdgeWeight distance; + NodeBucket(const NodeID predecessor, const EdgeWeight distance) + : predecessor(predecessor), distance(distance) + { + } + }; + using ResultMap = std::unordered_map; + + private: + std::string temp_string; + std::string descriptor_string; + std::unique_ptr> search_engine_ptr; + DataFacadeT *facade; + ResultMap resultMap; + + public: + explicit RangeAnalysis(DataFacadeT *facade) : descriptor_string("range"), facade(facade) + { + search_engine_ptr = util::make_unique>(facade); + } + + virtual ~RangeAnalysis() {} + + const std::string GetDescriptor() const override final { return descriptor_string; } + + Status HandleRequest(const RouteParameters &routeParameters, + util::json::Object &json_result) override final + { + /* Check if valid + * + */ + if (routeParameters.coordinates.size() != 1) + { + json_result.values["status_message"] = "Number of Coordinates should be 1"; + return Status::Error; + } + if (!routeParameters.coordinates.front().IsValid()) + { + json_result.values["status_message"] = "Coordinate is invalid"; + return Status::Error; + } + + const auto &input_bearings = routeParameters.bearings; + if (input_bearings.size() > 0 && + routeParameters.coordinates.size() != input_bearings.size()) + { + json_result.values["status_message"] = + "Number of bearings does not match number of coordinate"; + return Status::Error; + } + + /** Interesting code starts here + * + */ + + // auto number_of_results = static_cast(routeParameters.num_results); + const int bearing = input_bearings.size() > 0 ? input_bearings.front().first : 0; + const int range = + input_bearings.size() > 0 + ? (input_bearings.front().second ? *input_bearings.front().second : 10) + : 180; + + std::vector phantomNodeVector; + auto phantomNodePair = facade->NearestPhantomNodeWithAlternativeFromBigComponent( + routeParameters.coordinates.front(), bearing, range); + + phantomNodeVector.push_back(phantomNodePair); + auto snapped_source_phantoms = snapPhantomNodes(phantomNodeVector); + // resultMap = search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); + search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); + + std::string temp_string; + json_result.values["title"] = "This is a Test-Plugin"; + + temp_string = std::to_string(routeParameters.coordinates.size()); + json_result.values["location_count"] = temp_string; + temp_string = std::to_string(resultMap.size()); + json_result.values["Result-Map"] = temp_string; + + for (auto it = resultMap.begin(); it != resultMap.end(); ++it) + { + + util::SimpleLogger().Write() << "Node-ID: " << it->first; + NodeBucket n = it->second; + util::SimpleLogger().Write() << "Predecessor:" << n.predecessor; + util::SimpleLogger().Write() << "Distance: " << n.distance; + } + // util::json::Array json_coordinate; + // util::json::Object result; + // json_coordinate.values.push_back(node.location.lat / COORDINATE_PRECISION); + // json_coordinate.values.push_back(node.location.lon / COORDINATE_PRECISION); + // result.values["mapped coordinate"] = json_coordinate; + // result.values["name"] = facade->get_name_for_id(node.name_id); + return Status::Ok; + } +}; +} +} +} +#endif // OSRM_TEST_PLUGIN_HPP diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp new file mode 100644 index 00000000000..70eb2ba74b5 --- /dev/null +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -0,0 +1,138 @@ +// +// Created by robin on 3/16/16. +// + +#ifndef OSRM_ONE_TO_MANY_HPP +#define OSRM_ONE_TO_MANY_HPP + +#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/search_engine_data.hpp" +#include "util/typedefs.hpp" +#include "util/simple_logger.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace routing_algorithms +{ + +template +class OneToManyRouting final + : public BasicRoutingInterface> +{ + using super = BasicRoutingInterface>; + using QueryHeap = SearchEngineData::QueryHeap; + using PredecessorMap = std::unordered_map; + using DistanceMap = std::unordered_map; + SearchEngineData &engine_working_data; + + + public: + OneToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) + : super(facade), engine_working_data(engine_working_data) + { + } + + ~OneToManyRouting() {} + + + void operator()(PhantomNode &phantomSource, const int distance) const + { + + PredecessorMap predecessorMap; + DistanceMap distanceMap; + engine_working_data.InitializeOrClearFirstThreadLocalStorage( + super::facade->GetNumberOfNodes()); + + QueryHeap &query_heap = *(engine_working_data.forward_heap_1); + + query_heap.Clear(); + // insert target(s) at distance 0 + if (SPECIAL_NODEID != phantomSource.forward_node_id) + { + query_heap.Insert(phantomSource.forward_node_id, + -phantomSource.GetForwardWeightPlusOffset(), + phantomSource.forward_node_id); + } + if (SPECIAL_NODEID != phantomSource.reverse_node_id) + { + query_heap.Insert(phantomSource.reverse_node_id, + -phantomSource.GetReverseWeightPlusOffset(), + phantomSource.reverse_node_id); + } + + // explore search space + while (!query_heap.Empty()) + { + ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); + } + for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) + { + util::SimpleLogger().Write() << "Node-ID: " << it->first; + util::SimpleLogger().Write() << "Predecessor:" << it->second; + util::SimpleLogger().Write() << "Distance: " << distanceMap[it->first]; +// resultMap[it->first].insert(std::make_pair(it->second, distanceMap[it->first])); +// resultMap[it->first].emplace_back(it->second, distanceMap[it->first]); + } + } + + void ForwardRoutingStep(QueryHeap &query_heap, + const int distance, + PredecessorMap &predecessorMap, + DistanceMap &distanceMap) const + { + const NodeID node = query_heap.DeleteMin(); + const int source_distance = query_heap.GetKey(node); + for (auto edge : super::facade->GetAdjacentEdgeRange(node)) + { + const auto &data = super::facade->GetEdgeData(edge); + const NodeID to = super::facade->GetTarget(edge); + const int edge_weight = data.distance; + BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); + + if (query_heap.WasInserted(to)) + { + if (query_heap.GetKey(to) + edge_weight < source_distance) + { + continue; + } + } + const int to_distance = source_distance + edge_weight; + + if (to_distance > distance) + { + continue; + } + + // New Node discovered -> Add to Heap + Node Info Storage + if (!query_heap.WasInserted(to)) + { + query_heap.Insert(to, to_distance, node); + predecessorMap[to] = node; + distanceMap[to] = to_distance; + } + // Found a shorter Path -> Update distance + else if (to_distance < query_heap.GetKey(to)) + { + // new parent + query_heap.GetData(to).parent = node; + query_heap.DecreaseKey(to, to_distance); + + predecessorMap[to] = node; + distanceMap[to] = to_distance; + } + } + } +}; +} +} +} +#endif // OSRM_ONE_TO_MANY_HPP diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 9207c44c016..00d56ab17d1 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -7,8 +7,8 @@ #include "engine/plugins/nearest.hpp" #include "engine/plugins/trip.hpp" #include "engine/plugins/viaroute.hpp" -#include "engine/plugins/tile.hpp" #include "engine/plugins/match.hpp" +#include "engine/plugins/tile.hpp" #include "engine/datafacade/datafacade_base.hpp" #include "engine/datafacade/internal_datafacade.hpp" @@ -133,11 +133,7 @@ Engine::Engine(EngineConfig &config) } else { - if (!config.storage_config.IsValid()) - { - throw util::exception("Invalid file paths given!"); - } - query_data_facade = util::make_unique(config.storage_config); + return_code = plugin_iterator->second->HandleRequest(route_parameters, json_result); } // Register plugins From f9d9d9c990c91ffe8cb412cb67ab351aba56fb7e Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 17 Mar 2016 11:51:33 +0100 Subject: [PATCH 05/43] first implementation of routing algo with pretty json-print --- include/engine/plugins/range_analysis.hpp | 62 ++++++++++--------- .../engine/routing_algorithms/one_to_many.hpp | 17 ++--- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp index 7820be2da9e..ba380cdcf4a 100644 --- a/include/engine/plugins/range_analysis.hpp +++ b/include/engine/plugins/range_analysis.hpp @@ -31,23 +31,16 @@ namespace plugins template class RangeAnalysis final : public BasePlugin { - struct NodeBucket - { - NodeID predecessor; - EdgeWeight distance; - NodeBucket(const NodeID predecessor, const EdgeWeight distance) - : predecessor(predecessor), distance(distance) - { - } - }; - using ResultMap = std::unordered_map; + using PredecessorMap = std::unordered_map; + using DistanceMap = std::unordered_map; private: std::string temp_string; std::string descriptor_string; std::unique_ptr> search_engine_ptr; DataFacadeT *facade; - ResultMap resultMap; + PredecessorMap predecessorMap; + DistanceMap distanceMap; public: explicit RangeAnalysis(DataFacadeT *facade) : descriptor_string("range"), facade(facade) @@ -102,31 +95,40 @@ template class RangeAnalysis final : public BasePlugin phantomNodeVector.push_back(phantomNodePair); auto snapped_source_phantoms = snapPhantomNodes(phantomNodeVector); - // resultMap = search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); - search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); + search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000, predecessorMap, + distanceMap); - std::string temp_string; - json_result.values["title"] = "This is a Test-Plugin"; + BOOST_ASSERT(predecessorMap.size() == distanceMap.size()); - temp_string = std::to_string(routeParameters.coordinates.size()); - json_result.values["location_count"] = temp_string; - temp_string = std::to_string(resultMap.size()); - json_result.values["Result-Map"] = temp_string; + std::string temp_string; + json_result.values["title"] = "Range Analysis"; - for (auto it = resultMap.begin(); it != resultMap.end(); ++it) + util::json::Array data; + for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) { + util::json::Object object; - util::SimpleLogger().Write() << "Node-ID: " << it->first; - NodeBucket n = it->second; - util::SimpleLogger().Write() << "Predecessor:" << n.predecessor; - util::SimpleLogger().Write() << "Distance: " << n.distance; + util::json::Object source; + FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); + source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; + source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; + object.values["Source"] = std::move(source); + + util::json::Object predecessor; + FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); + predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; + predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; + object.values["Predecessor"] = std::move(predecessor); + + util::json::Object distance; + object.values["distance_from_start"] = distanceMap[it->first]; + + data.values.push_back(object); } - // util::json::Array json_coordinate; - // util::json::Object result; - // json_coordinate.values.push_back(node.location.lat / COORDINATE_PRECISION); - // json_coordinate.values.push_back(node.location.lon / COORDINATE_PRECISION); - // result.values["mapped coordinate"] = json_coordinate; - // result.values["name"] = facade->get_name_for_id(node.name_id); + temp_string = std::to_string(distanceMap.size()); + json_result.values["Nodes Found"] = temp_string;; + json_result.values["Range-Analysis"] = std::move(data); + return Status::Ok; } }; diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp index 70eb2ba74b5..436c573973a 100644 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -34,7 +34,6 @@ class OneToManyRouting final using DistanceMap = std::unordered_map; SearchEngineData &engine_working_data; - public: OneToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) : super(facade), engine_working_data(engine_working_data) @@ -43,12 +42,12 @@ class OneToManyRouting final ~OneToManyRouting() {} - - void operator()(PhantomNode &phantomSource, const int distance) const + void operator()(PhantomNode &phantomSource, + const int distance, + PredecessorMap &predecessorMap, + DistanceMap &distanceMap) const { - PredecessorMap predecessorMap; - DistanceMap distanceMap; engine_working_data.InitializeOrClearFirstThreadLocalStorage( super::facade->GetNumberOfNodes()); @@ -74,14 +73,6 @@ class OneToManyRouting final { ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); } - for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) - { - util::SimpleLogger().Write() << "Node-ID: " << it->first; - util::SimpleLogger().Write() << "Predecessor:" << it->second; - util::SimpleLogger().Write() << "Distance: " << distanceMap[it->first]; -// resultMap[it->first].insert(std::make_pair(it->second, distanceMap[it->first])); -// resultMap[it->first].emplace_back(it->second, distanceMap[it->first]); - } } void ForwardRoutingStep(QueryHeap &query_heap, From 9059de061f3d4fdda7fa8bdf4116ae6b1bb3156e Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 23 Mar 2016 12:20:58 +0100 Subject: [PATCH 06/43] added testing --- features/bicycle/range.feature | 21 ++ features/step_definitions/range.rb | 114 +++++++++++ features/support/route.rb | 190 ++++++++++++++++++ include/engine/plugins/range_analysis.hpp | 6 +- .../engine/routing_algorithms/one_to_many.hpp | 5 +- 5 files changed, 333 insertions(+), 3 deletions(-) create mode 100644 features/bicycle/range.feature create mode 100644 features/step_definitions/range.rb create mode 100644 features/support/route.rb diff --git a/features/bicycle/range.feature b/features/bicycle/range.feature new file mode 100644 index 00000000000..8b0b958c341 --- /dev/null +++ b/features/bicycle/range.feature @@ -0,0 +1,21 @@ +@range +Feature: Getting the range limited by Distance + + Background: + Given the profile "bicycle" + + Scenario: Range + Given the node map + | a | | b | + | | | | + | | | c | + + And the ways + | nodes | + | ab | + | ac | + + When I request range I should get + |source | node | pred | + | a | b | a | + | a | c | a | diff --git a/features/step_definitions/range.rb b/features/step_definitions/range.rb new file mode 100644 index 00000000000..9537158ee38 --- /dev/null +++ b/features/step_definitions/range.rb @@ -0,0 +1,114 @@ +When /^I request range I should get$/ do |table| + reprocess + actual = [] + OSRMLoader.load(self,"#{contracted_file}.osrm") do + table.hashes.each_with_index do |row,ri| + source = find_node_by_name row['source'] #a + raise "*** unknown in-node '#{row['source']}" unless source + + node = find_node_by_name row['node'] #b + raise "*** unknown in-node '#{row['node']}" unless node + + pred = find_node_by_name row['pred'] #a + raise "*** unknown in-node '#{row['pred']}" unless pred + + # dist = row['distance'] + # raise "*** unknown in-node '#{row['distance']}" unless dist + + #print(source_node) + response = request_range source, @query_params + if response.code == "200" && response.body.empty? == false + json = JSON.parse response.body + # print(json) + if json['status'] == 200 + size = json['Nodes found'] + range = json['Range-Analysis'] + end + end + + #print(size) + # got = {'source' => row['source'], + # 'node' => p1, + # 'pred' => p2} + # 'distance' => retDis} + + # print(range) + ok = false + for coordinate in range; + p1 = Array.new + p2 = Array.new + + + p1 << coordinate['p1']['lat'] + p1 << coordinate['p1']['lon'] + p2 << coordinate['p2']['lat'] + p2 << coordinate['p2']['lon'] + + got = {'source' => row['source'], + 'node' => p1, + 'pred' => p2} + + print(p1) + print(p2) + print("fuu"); + + if FuzzyMatch.match_location(p1, node) && FuzzyMatch.match_location(p2, pred) || ok + print("asdasdasd") + key = 'node' + got[key] = row[key] + key = 'pred' + got[key] = row[key] + ok = true + else + key = 'node' + row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + key = 'pred' + row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + end + end + + # row.keys.each do |key| + # if key=='node' + # if FuzzyMatch.match_location p1, node + # got[key] = row[key] + # ok = true + # else + # row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + # end + # end + # if key=='pred' + # if FuzzyMatch.match_location p2, pred + # got[key] = row[key] + # ok = true + # else + # row[key] = "#{row[key]} [#{pred.lat},#{pred.lon}]" + # end + # end + # if key=='distance' + # if retDis == dist + # got[key] = row[key] + # else + # row[key] = dist + # ok = false + # end + # end + # end + + # end + + + # print(p1) + # print(p2) + # print(node) + + + + unless ok + failed = { :attempt => 'range', :query => @query, :response => response } + log_fail row,got,[failed] + end + actual << got + end + end + table.diff! actual +end diff --git a/features/support/route.rb b/features/support/route.rb new file mode 100644 index 00000000000..83e549b219c --- /dev/null +++ b/features/support/route.rb @@ -0,0 +1,190 @@ +require 'net/http' + +HOST = "http://127.0.0.1:#{OSRM_PORT}" +DESTINATION_REACHED = 15 #OSRM instruction code + +def request_path service, params + uri = "#{HOST}/" + service + response = send_request uri, params + return response +end + +def request_url path + uri = URI.parse"#{HOST}/#{path}" + @query = uri.to_s + Timeout.timeout(OSRM_TIMEOUT) do + Net::HTTP.get_response uri + end +rescue Errno::ECONNREFUSED => e + raise "*** osrm-routed is not running." +rescue Timeout::Error + raise "*** osrm-routed did not respond." +end + +# Overwriters the default values in defaults. +# e.g. [[a, 1], [b, 2]], [[a, 5], [d, 10]] => [[a, 5], [b, 2], [d, 10]] +def overwrite_params defaults, other + merged = [] + defaults.each do |k,v| + idx = other.index { |p| p[0] == k } + if idx == nil then + merged << [k, v] + else + merged << [k, other[idx][1]] + end + end + other.each do |k,v| + if merged.index { |pair| pair[0] == k} == nil then + merged << [k, v] + end + end + + return merged +end + +def request_route waypoints, bearings, user_params + raise "*** number of bearings does not equal the number of waypoints" unless bearings.size == 0 || bearings.size == waypoints.size + + defaults = [['output','json'], ['instructions',true], ['alt',false]] + params = overwrite_params defaults, user_params + encoded_waypoint = waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } + if bearings.size > 0 + encoded_bearings = bearings.map { |b| ["b", b.to_s]} + parasm = params.concat encoded_waypoint.zip(encoded_bearings).flatten! 1 + else + params = params.concat encoded_waypoint + end + + return request_path "viaroute", params +end + +def request_nearest node, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + params << ["loc", "#{node.lat},#{node.lon}"] + + return request_path "nearest", params + end + +def request_range node, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + params << ["loc", "#{node.lat},#{node.lon}"] + + return request_path "range", params +end + +def request_table waypoints, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + params = params.concat waypoints.map{ |w| [w[:type],"#{w[:coord].lat},#{w[:coord].lon}"] } + + return request_path "table", params +end + +def request_trip waypoints, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + params = params.concat waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } + + return request_path "trip", params +end + +def request_matching waypoints, timestamps, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + encoded_waypoint = waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } + if timestamps.size > 0 + encoded_timestamps = timestamps.map { |t| ["t", t.to_s]} + parasm = params.concat encoded_waypoint.zip(encoded_timestamps).flatten! 1 + else + params = params.concat encoded_waypoint + end + + return request_path "match", params +end + +def got_route? response + if response.code == "200" && !response.body.empty? + json = JSON.parse response.body + if json['status'] == 200 + return way_list( json['route_instructions']).empty? == false + end + end + return false +end + +def route_status response + if response.code == "200" && !response.body.empty? + json = JSON.parse response.body + return json['status'] + else + "HTTP #{response.code}" + end +end + +def extract_instruction_list instructions, index, postfix=nil + if instructions + instructions.reject { |r| r[0].to_s=="#{DESTINATION_REACHED}" }. + map { |r| r[index] }. + map { |r| (r=="" || r==nil) ? '""' : "#{r}#{postfix}" }. + join(',') + end +end + +def way_list instructions + extract_instruction_list instructions, 1 +end + +def compass_list instructions + extract_instruction_list instructions, 6 +end + +def bearing_list instructions + extract_instruction_list instructions, 7 +end + +def turn_list instructions + if instructions + types = { + 0 => :none, + 1 => :straight, + 2 => :slight_right, + 3 => :right, + 4 => :sharp_right, + 5 => :u_turn, + 6 => :sharp_left, + 7 => :left, + 8 => :slight_left, + 9 => :via, + 10 => :head, + 11 => :enter_roundabout, + 12 => :leave_roundabout, + 13 => :stay_roundabout, + 14 => :start_end_of_street, + 15 => :destination, + 16 => :name_changes, + 17 => :enter_contraflow, + 18 => :leave_contraflow + } + # replace instructions codes with strings + # "11-3" (enter roundabout and leave a 3rd exit) gets converted to "enter_roundabout-3" + instructions.map do |r| + r[0].to_s.gsub(/^\d*/) do |match| + types[match.to_i].to_s + end + end.join(',') + end +end + +def mode_list instructions + extract_instruction_list instructions, 8 +end + +def time_list instructions + extract_instruction_list instructions, 4, "s" +end + +def distance_list instructions + extract_instruction_list instructions, 2, "m" +end diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp index ba380cdcf4a..04533cbe318 100644 --- a/include/engine/plugins/range_analysis.hpp +++ b/include/engine/plugins/range_analysis.hpp @@ -103,6 +103,8 @@ template class RangeAnalysis final : public BasePlugin std::string temp_string; json_result.values["title"] = "Range Analysis"; + json_result.values["start"] = routeParameters.coordinates[0].lat; + util::json::Array data; for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) { @@ -112,13 +114,13 @@ template class RangeAnalysis final : public BasePlugin FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; - object.values["Source"] = std::move(source); + object.values["p1"] = std::move(source); util::json::Object predecessor; FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; - object.values["Predecessor"] = std::move(predecessor); + object.values["p2"] = std::move(predecessor); util::json::Object distance; object.values["distance_from_start"] = distanceMap[it->first]; diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp index 436c573973a..4ab26b9db9c 100644 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -52,6 +52,7 @@ class OneToManyRouting final super::facade->GetNumberOfNodes()); QueryHeap &query_heap = *(engine_working_data.forward_heap_1); + QueryHeap &backward_heap = *(engine_working_data.reverse_heap_1); query_heap.Clear(); // insert target(s) at distance 0 @@ -68,10 +69,12 @@ class OneToManyRouting final phantomSource.reverse_node_id); } + util::SimpleLogger().Write() << phantomSource; // explore search space while (!query_heap.Empty()) { - ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); +// ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); + ForwardRoutingStep(query_heap, backward_heap, ) } } From bf036de3b3f8e1e44e53f74e5bdbe4071c413c92 Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 13 Apr 2016 10:47:57 +0200 Subject: [PATCH 07/43] changes --- features/bicycle/range.feature | 13 +- features/step_definitions/range.rb | 26 +- features/support/route.rb | 2 +- include/engine/plugins/isochrone.hpp | 310 ++++++++++++++++++ include/engine/plugins/range_analysis.hpp | 140 -------- .../engine/routing_algorithms/one_to_many.hpp | 132 -------- src/engine/engine.cpp | 2 + 7 files changed, 333 insertions(+), 292 deletions(-) create mode 100644 include/engine/plugins/isochrone.hpp delete mode 100644 include/engine/plugins/range_analysis.hpp delete mode 100644 include/engine/routing_algorithms/one_to_many.hpp diff --git a/features/bicycle/range.feature b/features/bicycle/range.feature index 8b0b958c341..10fc8603f00 100644 --- a/features/bicycle/range.feature +++ b/features/bicycle/range.feature @@ -1,21 +1,18 @@ -@range +@bycicle Feature: Getting the range limited by Distance Background: - Given the profile "bicycle" + Given the profile "car" Scenario: Range Given the node map - | a | | b | - | | | | - | | | c | + | a | b | c | And the ways | nodes | | ab | - | ac | + | bc | When I request range I should get |source | node | pred | - | a | b | a | - | a | c | a | + | a | b | c | \ No newline at end of file diff --git a/features/step_definitions/range.rb b/features/step_definitions/range.rb index 9537158ee38..175341d036e 100644 --- a/features/step_definitions/range.rb +++ b/features/step_definitions/range.rb @@ -19,20 +19,21 @@ response = request_range source, @query_params if response.code == "200" && response.body.empty? == false json = JSON.parse response.body - # print(json) + print(json) + print("\n") if json['status'] == 200 - size = json['Nodes found'] + size = json['Nodes Found'] range = json['Range-Analysis'] end end - #print(size) + # print(size + "\n") # got = {'source' => row['source'], # 'node' => p1, # 'pred' => p2} # 'distance' => retDis} - # print(range) + print(range) ok = false for coordinate in range; p1 = Array.new @@ -48,22 +49,25 @@ 'node' => p1, 'pred' => p2} - print(p1) - print(p2) - print("fuu"); + # print("test\n") + # print(p1) + # print("\n"); + # print(node) + # print("\n"); + # print("\n"); - if FuzzyMatch.match_location(p1, node) && FuzzyMatch.match_location(p2, pred) || ok - print("asdasdasd") + + if FuzzyMatch.match_location(p1, node) && FuzzyMatch.match_location(p2, pred) key = 'node' got[key] = row[key] key = 'pred' got[key] = row[key] ok = true - else + elsif !FuzzyMatch.match_location(p1, node) && !FuzzyMatch.match_location(p2, pred) key = 'node' row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" key = 'pred' - row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + row[key] = "#{row[key]} [#{pred.lat},#{pred.lon}]" end end diff --git a/features/support/route.rb b/features/support/route.rb index 83e549b219c..6e3d1b17ae4 100644 --- a/features/support/route.rb +++ b/features/support/route.rb @@ -71,7 +71,7 @@ def request_range node, user_params params = overwrite_params defaults, user_params params << ["loc", "#{node.lat},#{node.lon}"] - return request_path "range", params + return request_path "isochrone", params end def request_table waypoints, user_params diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp new file mode 100644 index 00000000000..63bb2e9562b --- /dev/null +++ b/include/engine/plugins/isochrone.hpp @@ -0,0 +1,310 @@ +// +// Created by robin on 4/6/16. +// + +#ifndef ISOCHRONE_HPP +#define ISOCHRONE_HPP + +#include "engine/plugins/plugin_base.hpp" +#include "engine/search_engine.hpp" +#include "engine/datafacade/internal_datafacade.hpp" + +#include "util/binary_heap.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/dynamic_graph.hpp" +#include "util/fingerprint.hpp" +#include "util/graph_loader.hpp" +#include "util/make_unique.hpp" +#include "util/routed_options.hpp" +#include "util/static_graph.hpp" +#include "util/string_util.hpp" +#include "util/simple_logger.hpp" +#include "util/typedefs.hpp" + +#include "osrm/json_container.hpp" +#include "osrm/coordinate.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ +struct SimpleEdgeData +{ + SimpleEdgeData() : weight(INVALID_EDGE_WEIGHT), real(false) {} + SimpleEdgeData(unsigned weight_, bool real_) : weight(weight_), real(real_) {} + unsigned weight; + bool real; +}; + +using SimpleGraph = util::StaticGraph; +using SimpleEdge = SimpleGraph::InputEdge; +using Datafacade = + osrm::engine::datafacade::InternalDataFacade; + +template class Isochrone final : public BasePlugin +{ + + private: + std::string temp_string; + std::string descriptor_string; + std::unique_ptr> search_engine_ptr; + std::shared_ptr graph; +// std::unique_ptr datafacade; + DataFacadeT *facade; + std::string path; + std::vector coordinate_list; + std::map distance_map; + std::map predecessor_map; + std::vector graph_edge_list; + std::size_t number_of_nodes; + + + void deleteFileIfExists(const std::string &file_name) + { + if (boost::filesystem::exists(file_name)) + { + boost::filesystem::remove(file_name); + } + } + + std::size_t loadGraph(const std::string &path, + std::vector &coordinate_list, + std::vector &graph_edge_list) + { + + std::ifstream input_stream(path, std::ifstream::in | std::ifstream::binary); + if (!input_stream.is_open()) + { + throw util::exception("Cannot open osrm file"); + } + + // load graph data + std::vector edge_list; + std::vector traffic_light_node_list; + std::vector barrier_node_list; + + auto number_of_nodes = util::loadNodesFromFile(input_stream, barrier_node_list, + traffic_light_node_list, coordinate_list); + + util::loadEdgesFromFile(input_stream, edge_list); + + traffic_light_node_list.clear(); + traffic_light_node_list.shrink_to_fit(); + + // Building an node-based graph + for (const auto &input_edge : edge_list) + { + if (input_edge.source == input_edge.target) + { + continue; + } + + // forward edge + graph_edge_list.emplace_back(input_edge.source, input_edge.target, input_edge.weight, + input_edge.forward); + // backward edge + graph_edge_list.emplace_back(input_edge.target, input_edge.source, input_edge.weight, + input_edge.backward); + } + + return number_of_nodes; + } + + public: + explicit Isochrone(DataFacadeT *facade, const std::string path) : descriptor_string("isochrone"), facade(facade), path(path) + { + number_of_nodes = loadGraph(path, coordinate_list, graph_edge_list); + + tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); + graph = std::make_shared(number_of_nodes, graph_edge_list); + graph_edge_list.clear(); + graph_edge_list.shrink_to_fit(); +// { +// std::unordered_map server_paths; +// server_paths["base"] = base; +// osrm::util::populate_base_path(server_paths); +// datafacade = std::unique_ptr(new Datafacade(server_paths)); +// } + } + + virtual ~Isochrone() {} + + const std::string GetDescriptor() const override final { return descriptor_string; } + + Status HandleRequest(const RouteParameters &routeParameters, + util::json::Object &json_result) override final + { + RouteParameters a = routeParameters; + util::json::Object o = json_result; + + auto phantomnodes = facade->NearestPhantomNodes( + {static_cast(routeParameters.coordinates.front().lat), + static_cast(routeParameters.coordinates.front().lon)}, + 1); + + std::sort(phantomnodes.begin(), phantomnodes.end(), + [&](const osrm::engine::PhantomNodeWithDistance &a, + const osrm::engine::PhantomNodeWithDistance &b) + { + return a.distance > b.distance; + }); + auto phantom = phantomnodes[0]; + NodeID source = 0; + + double sourceLat = phantom.phantom_node.location.lat / osrm::COORDINATE_PRECISION; + double sourceLon = phantom.phantom_node.location.lon / osrm::COORDINATE_PRECISION; + + if (facade->EdgeIsCompressed(phantom.phantom_node.forward_node_id)) + { + std::vector forward_id_vector; + facade->GetUncompressedGeometry(phantom.phantom_node.forward_packed_geometry_id, + forward_id_vector); + source = forward_id_vector[phantom.phantom_node.fwd_segment_position]; + } + else + { + source = phantom.phantom_node.forward_packed_geometry_id; + } + + util::SimpleLogger().Write() << "Node-Coord" << sourceLat << " " << sourceLon; + + // Init complete + + struct HeapData + { + NodeID parent; + /* explicit */ HeapData(NodeID p) : parent(p) {} + }; + + using QueryHeap = osrm::util::BinaryHeap>; + + QueryHeap heap(number_of_nodes); + + util::SimpleLogger().Write() << "asdasdasd"; + heap.Insert(source, -phantom.phantom_node.GetForwardWeightPlusOffset(), source); + + // value is in metres + const int MAX = 1000; + + std::unordered_set edgepoints; + std::unordered_set insidepoints; + std::vector border; + + { + // Standard Dijkstra search, terminating when path length > MAX + while (!heap.Empty()) + { + const NodeID source = heap.DeleteMin(); + const std::int32_t distance = heap.GetKey(source); + + for (const auto current_edge : graph->GetAdjacentEdgeRange(source)) + { + const auto target = graph->GetTarget(current_edge); + if (target != SPECIAL_NODEID) + { + const auto data = graph->GetEdgeData(current_edge); + if (data.real) + { + int to_distance = distance + data.weight; + util::SimpleLogger().Write() << "I am Node " << target; + util::SimpleLogger().Write() << "Distance to Source: " << to_distance; + if (to_distance > MAX) + { + edgepoints.insert(target); + // distance_map[target] = + // to_distance; + // predecessor_map[target] = source; + } + else if (!heap.WasInserted(target)) + { + heap.Insert(target, to_distance, source); + insidepoints.insert(source); + distance_map[target] = to_distance; + predecessor_map[target] = source; + } + else if (to_distance < heap.GetKey(target)) + { + heap.GetData(target).parent = source; + heap.DecreaseKey(target, to_distance); + distance_map[target] = to_distance; + predecessor_map[target] = source; + } + } + } + } + } + } + util::SimpleLogger().Write(); + util::SimpleLogger().Write() << "Inside-Points"; + util::SimpleLogger().Write() << insidepoints.size(); + for (auto p : insidepoints) + { + double lon = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; + double lat = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; + auto pre = predecessor_map[p]; + auto d = distance_map[p]; + util::SimpleLogger().Write() << "Lat: " << lat; + util::SimpleLogger().Write() << "Lon: " << lon; + util::SimpleLogger().Write() << "Source: " << p << " Predecessor: " << pre + << " Distance " << d; + // util::SimpleLogger().Write(); + } + + util::SimpleLogger().Write() << "Edgepoints"; + util::SimpleLogger().Write() << edgepoints.size(); + // for (auto p : edgepoints) + // { + // double lon = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; + // double lat = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; + // auto pre = predecessor_map[p]; + // auto d = distance_map[p]; + //// util::SimpleLogger().Write() << "Lat: " << lat; + //// util::SimpleLogger().Write() << "Lon: " << lon; + // util::SimpleLogger().Write() << "Predecessor: " << pre << "Distance " << d; + // util::SimpleLogger().Write(); + // } + + util::json::Array data; + for (auto p : insidepoints) + { + util::json::Object object; + + util::json::Object source; + source.values["lat"] = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; + source.values["lon"] = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; + object.values["p1"] = std::move(source); + + util::json::Object predecessor; + auto pre = predecessor_map[p]; + predecessor.values["lat"] = coordinate_list[pre].lat / osrm::COORDINATE_PRECISION; + predecessor.values["lon"] = coordinate_list[pre].lon / osrm::COORDINATE_PRECISION; + object.values["p2"] = std::move(predecessor); + + util::json::Object distance; + object.values["distance_from_start"] = distance_map[p]; + + data.values.push_back(object); + } + + json_result.values["Range-Analysis"] = std::move(data); + return Status::Ok; + } +}; +} +} +} + +#endif // ISOCHRONE_HPP diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp deleted file mode 100644 index 04533cbe318..00000000000 --- a/include/engine/plugins/range_analysis.hpp +++ /dev/null @@ -1,140 +0,0 @@ -// -// Created by robin on 3/10/16. -// - -#ifndef OSRM_TEST_PLUGIN_HPP -#define OSRM_TEST_PLUGIN_HPP - -#include "engine/plugins/plugin_base.hpp" -#include "engine/search_engine.hpp" -#include "engine/object_encoder.hpp" - -#include "util/make_unique.hpp" -#include "util/string_util.hpp" -#include "util/simple_logger.hpp" -#include "osrm/json_container.hpp" - -#include - -#include -#include -#include -#include - -namespace osrm -{ -namespace engine -{ -namespace plugins -{ - -template class RangeAnalysis final : public BasePlugin -{ - - using PredecessorMap = std::unordered_map; - using DistanceMap = std::unordered_map; - - private: - std::string temp_string; - std::string descriptor_string; - std::unique_ptr> search_engine_ptr; - DataFacadeT *facade; - PredecessorMap predecessorMap; - DistanceMap distanceMap; - - public: - explicit RangeAnalysis(DataFacadeT *facade) : descriptor_string("range"), facade(facade) - { - search_engine_ptr = util::make_unique>(facade); - } - - virtual ~RangeAnalysis() {} - - const std::string GetDescriptor() const override final { return descriptor_string; } - - Status HandleRequest(const RouteParameters &routeParameters, - util::json::Object &json_result) override final - { - /* Check if valid - * - */ - if (routeParameters.coordinates.size() != 1) - { - json_result.values["status_message"] = "Number of Coordinates should be 1"; - return Status::Error; - } - if (!routeParameters.coordinates.front().IsValid()) - { - json_result.values["status_message"] = "Coordinate is invalid"; - return Status::Error; - } - - const auto &input_bearings = routeParameters.bearings; - if (input_bearings.size() > 0 && - routeParameters.coordinates.size() != input_bearings.size()) - { - json_result.values["status_message"] = - "Number of bearings does not match number of coordinate"; - return Status::Error; - } - - /** Interesting code starts here - * - */ - - // auto number_of_results = static_cast(routeParameters.num_results); - const int bearing = input_bearings.size() > 0 ? input_bearings.front().first : 0; - const int range = - input_bearings.size() > 0 - ? (input_bearings.front().second ? *input_bearings.front().second : 10) - : 180; - - std::vector phantomNodeVector; - auto phantomNodePair = facade->NearestPhantomNodeWithAlternativeFromBigComponent( - routeParameters.coordinates.front(), bearing, range); - - phantomNodeVector.push_back(phantomNodePair); - auto snapped_source_phantoms = snapPhantomNodes(phantomNodeVector); - search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000, predecessorMap, - distanceMap); - - BOOST_ASSERT(predecessorMap.size() == distanceMap.size()); - - std::string temp_string; - json_result.values["title"] = "Range Analysis"; - - json_result.values["start"] = routeParameters.coordinates[0].lat; - - util::json::Array data; - for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) - { - util::json::Object object; - - util::json::Object source; - FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); - source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; - source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; - object.values["p1"] = std::move(source); - - util::json::Object predecessor; - FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); - predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; - predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; - object.values["p2"] = std::move(predecessor); - - util::json::Object distance; - object.values["distance_from_start"] = distanceMap[it->first]; - - data.values.push_back(object); - } - temp_string = std::to_string(distanceMap.size()); - json_result.values["Nodes Found"] = temp_string;; - json_result.values["Range-Analysis"] = std::move(data); - - return Status::Ok; - } -}; -} -} -} -#endif // OSRM_TEST_PLUGIN_HPP diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp deleted file mode 100644 index 4ab26b9db9c..00000000000 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ /dev/null @@ -1,132 +0,0 @@ -// -// Created by robin on 3/16/16. -// - -#ifndef OSRM_ONE_TO_MANY_HPP -#define OSRM_ONE_TO_MANY_HPP - -#include "engine/routing_algorithms/routing_base.hpp" -#include "engine/search_engine_data.hpp" -#include "util/typedefs.hpp" -#include "util/simple_logger.hpp" - -#include - -#include -#include -#include -#include - -namespace osrm -{ -namespace engine -{ -namespace routing_algorithms -{ - -template -class OneToManyRouting final - : public BasicRoutingInterface> -{ - using super = BasicRoutingInterface>; - using QueryHeap = SearchEngineData::QueryHeap; - using PredecessorMap = std::unordered_map; - using DistanceMap = std::unordered_map; - SearchEngineData &engine_working_data; - - public: - OneToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) - : super(facade), engine_working_data(engine_working_data) - { - } - - ~OneToManyRouting() {} - - void operator()(PhantomNode &phantomSource, - const int distance, - PredecessorMap &predecessorMap, - DistanceMap &distanceMap) const - { - - engine_working_data.InitializeOrClearFirstThreadLocalStorage( - super::facade->GetNumberOfNodes()); - - QueryHeap &query_heap = *(engine_working_data.forward_heap_1); - QueryHeap &backward_heap = *(engine_working_data.reverse_heap_1); - - query_heap.Clear(); - // insert target(s) at distance 0 - if (SPECIAL_NODEID != phantomSource.forward_node_id) - { - query_heap.Insert(phantomSource.forward_node_id, - -phantomSource.GetForwardWeightPlusOffset(), - phantomSource.forward_node_id); - } - if (SPECIAL_NODEID != phantomSource.reverse_node_id) - { - query_heap.Insert(phantomSource.reverse_node_id, - -phantomSource.GetReverseWeightPlusOffset(), - phantomSource.reverse_node_id); - } - - util::SimpleLogger().Write() << phantomSource; - // explore search space - while (!query_heap.Empty()) - { -// ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); - ForwardRoutingStep(query_heap, backward_heap, ) - } - } - - void ForwardRoutingStep(QueryHeap &query_heap, - const int distance, - PredecessorMap &predecessorMap, - DistanceMap &distanceMap) const - { - const NodeID node = query_heap.DeleteMin(); - const int source_distance = query_heap.GetKey(node); - for (auto edge : super::facade->GetAdjacentEdgeRange(node)) - { - const auto &data = super::facade->GetEdgeData(edge); - const NodeID to = super::facade->GetTarget(edge); - const int edge_weight = data.distance; - BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); - - if (query_heap.WasInserted(to)) - { - if (query_heap.GetKey(to) + edge_weight < source_distance) - { - continue; - } - } - const int to_distance = source_distance + edge_weight; - - if (to_distance > distance) - { - continue; - } - - // New Node discovered -> Add to Heap + Node Info Storage - if (!query_heap.WasInserted(to)) - { - query_heap.Insert(to, to_distance, node); - predecessorMap[to] = node; - distanceMap[to] = to_distance; - } - // Found a shorter Path -> Update distance - else if (to_distance < query_heap.GetKey(to)) - { - // new parent - query_heap.GetData(to).parent = node; - query_heap.DecreaseKey(to, to_distance); - - predecessorMap[to] = node; - distanceMap[to] = to_distance; - } - } - } -}; -} -} -} -#endif // OSRM_ONE_TO_MANY_HPP diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 00d56ab17d1..169af892588 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -5,6 +5,7 @@ #include "engine/plugins/table.hpp" #include "engine/plugins/nearest.hpp" +#include "engine/plugins/timestamp.hpp" #include "engine/plugins/trip.hpp" #include "engine/plugins/viaroute.hpp" #include "engine/plugins/match.hpp" @@ -16,6 +17,7 @@ #include "storage/shared_barriers.hpp" #include "util/make_unique.hpp" +#include "util/routed_options.hpp" #include "util/simple_logger.hpp" #include From a8d560d686a49a45bbe9288edec01b176ad9792b Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 14 Apr 2016 11:22:12 +0200 Subject: [PATCH 08/43] helloworld-isochrone with new API --- include/engine/api/isochrone_api.hpp | 31 ++ include/engine/api/isochrone_parameters.hpp | 28 ++ include/engine/engine.hpp | 4 + include/engine/plugins/isochrone.hpp | 289 +----------------- include/osrm/isochrone_parameters.hpp | 15 + include/osrm/osrm.hpp | 39 ++- include/osrm/osrm_fwd.hpp | 1 + .../api/isochrone_parameter_grammar.hpp | 47 +++ include/server/service/isochrone_service.hpp | 36 +++ src/engine/engine.cpp | 15 +- src/engine/plugins/isochrone.cpp | 25 ++ src/osrm/osrm.cpp | 5 + src/server/api/parameters_parser.cpp | 8 + src/server/service/isochrone_service.cpp | 76 +++++ src/server/service_handler.cpp | 2 + 15 files changed, 319 insertions(+), 302 deletions(-) create mode 100644 include/engine/api/isochrone_api.hpp create mode 100644 include/engine/api/isochrone_parameters.hpp create mode 100644 include/osrm/isochrone_parameters.hpp create mode 100644 include/server/api/isochrone_parameter_grammar.hpp create mode 100644 include/server/service/isochrone_service.hpp create mode 100644 src/engine/plugins/isochrone.cpp create mode 100644 src/server/service/isochrone_service.cpp diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp new file mode 100644 index 00000000000..ae5f7f5722f --- /dev/null +++ b/include/engine/api/isochrone_api.hpp @@ -0,0 +1,31 @@ +// +// Created by robin on 4/13/16. +// + +#ifndef ENGINE_API_ISOCHRONE_HPP +#define ENGINE_API_ISOCHRONE_HPP + +#include "engine/api/base_api.hpp" +#include "engine/api/isochrone_parameters.hpp" + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +class IsochroneAPI final : public BaseAPI +{ + public: + const IsochroneParameters ¶meters; + + IsochroneAPI(const datafacade::BaseDataFacade &facade_, const IsochroneParameters ¶meters_) + : BaseAPI(facade_, parameters_), parameters(parameters_) + { + } +}; +} +} +} +#endif // ENGINE_API_ISOCHRONE_HPP diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp new file mode 100644 index 00000000000..d05361b024c --- /dev/null +++ b/include/engine/api/isochrone_parameters.hpp @@ -0,0 +1,28 @@ +// +// Created by robin on 4/13/16. +// + +#ifndef ENGINE_API_ISOCHRONE_PARAMETERS_HPP +#define ENGINE_API_ISOCHRONE_PARAMETERS_HPP + +#include "engine/api/base_parameters.hpp" + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +struct IsochroneParameters : public BaseParameters +{ + unsigned number_of_results = 1; + + bool IsValid() const { + return BaseParameters::IsValid() && number_of_results >= 1; + } +}; +} +} +} +#endif // ENGINE_API_ISOCHRONE_PARAMETERS_HPP diff --git a/include/engine/engine.hpp b/include/engine/engine.hpp index 027320ba339..447b4f4adf7 100644 --- a/include/engine/engine.hpp +++ b/include/engine/engine.hpp @@ -32,6 +32,7 @@ struct NearestParameters; struct TripParameters; struct MatchParameters; struct TileParameters; +struct IsochroneParameters; } namespace plugins { @@ -41,6 +42,7 @@ class NearestPlugin; class TripPlugin; class MatchPlugin; class TilePlugin; +class IsochronePlugin; } // End fwd decls @@ -69,6 +71,7 @@ class Engine final Status Trip(const api::TripParameters ¶meters, util::json::Object &result); Status Match(const api::MatchParameters ¶meters, util::json::Object &result); Status Tile(const api::TileParameters ¶meters, std::string &result); + Status Isochrone(const api::IsochroneParameters ¶meters, util::json::Object &result); private: std::unique_ptr lock; @@ -79,6 +82,7 @@ class Engine final std::unique_ptr trip_plugin; std::unique_ptr match_plugin; std::unique_ptr tile_plugin; + std::unique_ptr isochrone_plugin; std::unique_ptr query_data_facade; }; diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index 63bb2e9562b..25bbc9580b4 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -6,33 +6,8 @@ #define ISOCHRONE_HPP #include "engine/plugins/plugin_base.hpp" -#include "engine/search_engine.hpp" -#include "engine/datafacade/internal_datafacade.hpp" - -#include "util/binary_heap.hpp" -#include "util/coordinate_calculation.hpp" -#include "util/dynamic_graph.hpp" -#include "util/fingerprint.hpp" -#include "util/graph_loader.hpp" -#include "util/make_unique.hpp" -#include "util/routed_options.hpp" -#include "util/static_graph.hpp" -#include "util/string_util.hpp" -#include "util/simple_logger.hpp" -#include "util/typedefs.hpp" - +#include "engine/api/isochrone_parameters.hpp" #include "osrm/json_container.hpp" -#include "osrm/coordinate.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include namespace osrm { @@ -40,268 +15,14 @@ namespace engine { namespace plugins { -struct SimpleEdgeData -{ - SimpleEdgeData() : weight(INVALID_EDGE_WEIGHT), real(false) {} - SimpleEdgeData(unsigned weight_, bool real_) : weight(weight_), real(real_) {} - unsigned weight; - bool real; -}; -using SimpleGraph = util::StaticGraph; -using SimpleEdge = SimpleGraph::InputEdge; -using Datafacade = - osrm::engine::datafacade::InternalDataFacade; - -template class Isochrone final : public BasePlugin +class IsochronePlugin final : public BasePlugin { - - private: - std::string temp_string; - std::string descriptor_string; - std::unique_ptr> search_engine_ptr; - std::shared_ptr graph; -// std::unique_ptr datafacade; - DataFacadeT *facade; - std::string path; - std::vector coordinate_list; - std::map distance_map; - std::map predecessor_map; - std::vector graph_edge_list; - std::size_t number_of_nodes; - - - void deleteFileIfExists(const std::string &file_name) - { - if (boost::filesystem::exists(file_name)) - { - boost::filesystem::remove(file_name); - } - } - - std::size_t loadGraph(const std::string &path, - std::vector &coordinate_list, - std::vector &graph_edge_list) - { - - std::ifstream input_stream(path, std::ifstream::in | std::ifstream::binary); - if (!input_stream.is_open()) - { - throw util::exception("Cannot open osrm file"); - } - - // load graph data - std::vector edge_list; - std::vector traffic_light_node_list; - std::vector barrier_node_list; - - auto number_of_nodes = util::loadNodesFromFile(input_stream, barrier_node_list, - traffic_light_node_list, coordinate_list); - - util::loadEdgesFromFile(input_stream, edge_list); - - traffic_light_node_list.clear(); - traffic_light_node_list.shrink_to_fit(); - - // Building an node-based graph - for (const auto &input_edge : edge_list) - { - if (input_edge.source == input_edge.target) - { - continue; - } - - // forward edge - graph_edge_list.emplace_back(input_edge.source, input_edge.target, input_edge.weight, - input_edge.forward); - // backward edge - graph_edge_list.emplace_back(input_edge.target, input_edge.source, input_edge.weight, - input_edge.backward); - } - - return number_of_nodes; - } - public: - explicit Isochrone(DataFacadeT *facade, const std::string path) : descriptor_string("isochrone"), facade(facade), path(path) - { - number_of_nodes = loadGraph(path, coordinate_list, graph_edge_list); - - tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); - graph = std::make_shared(number_of_nodes, graph_edge_list); - graph_edge_list.clear(); - graph_edge_list.shrink_to_fit(); -// { -// std::unordered_map server_paths; -// server_paths["base"] = base; -// osrm::util::populate_base_path(server_paths); -// datafacade = std::unique_ptr(new Datafacade(server_paths)); -// } - } - - virtual ~Isochrone() {} - - const std::string GetDescriptor() const override final { return descriptor_string; } - - Status HandleRequest(const RouteParameters &routeParameters, - util::json::Object &json_result) override final - { - RouteParameters a = routeParameters; - util::json::Object o = json_result; - - auto phantomnodes = facade->NearestPhantomNodes( - {static_cast(routeParameters.coordinates.front().lat), - static_cast(routeParameters.coordinates.front().lon)}, - 1); - - std::sort(phantomnodes.begin(), phantomnodes.end(), - [&](const osrm::engine::PhantomNodeWithDistance &a, - const osrm::engine::PhantomNodeWithDistance &b) - { - return a.distance > b.distance; - }); - auto phantom = phantomnodes[0]; - NodeID source = 0; - - double sourceLat = phantom.phantom_node.location.lat / osrm::COORDINATE_PRECISION; - double sourceLon = phantom.phantom_node.location.lon / osrm::COORDINATE_PRECISION; - - if (facade->EdgeIsCompressed(phantom.phantom_node.forward_node_id)) - { - std::vector forward_id_vector; - facade->GetUncompressedGeometry(phantom.phantom_node.forward_packed_geometry_id, - forward_id_vector); - source = forward_id_vector[phantom.phantom_node.fwd_segment_position]; - } - else - { - source = phantom.phantom_node.forward_packed_geometry_id; - } - - util::SimpleLogger().Write() << "Node-Coord" << sourceLat << " " << sourceLon; - - // Init complete - - struct HeapData - { - NodeID parent; - /* explicit */ HeapData(NodeID p) : parent(p) {} - }; - - using QueryHeap = osrm::util::BinaryHeap>; - - QueryHeap heap(number_of_nodes); - - util::SimpleLogger().Write() << "asdasdasd"; - heap.Insert(source, -phantom.phantom_node.GetForwardWeightPlusOffset(), source); - - // value is in metres - const int MAX = 1000; - - std::unordered_set edgepoints; - std::unordered_set insidepoints; - std::vector border; - - { - // Standard Dijkstra search, terminating when path length > MAX - while (!heap.Empty()) - { - const NodeID source = heap.DeleteMin(); - const std::int32_t distance = heap.GetKey(source); - - for (const auto current_edge : graph->GetAdjacentEdgeRange(source)) - { - const auto target = graph->GetTarget(current_edge); - if (target != SPECIAL_NODEID) - { - const auto data = graph->GetEdgeData(current_edge); - if (data.real) - { - int to_distance = distance + data.weight; - util::SimpleLogger().Write() << "I am Node " << target; - util::SimpleLogger().Write() << "Distance to Source: " << to_distance; - if (to_distance > MAX) - { - edgepoints.insert(target); - // distance_map[target] = - // to_distance; - // predecessor_map[target] = source; - } - else if (!heap.WasInserted(target)) - { - heap.Insert(target, to_distance, source); - insidepoints.insert(source); - distance_map[target] = to_distance; - predecessor_map[target] = source; - } - else if (to_distance < heap.GetKey(target)) - { - heap.GetData(target).parent = source; - heap.DecreaseKey(target, to_distance); - distance_map[target] = to_distance; - predecessor_map[target] = source; - } - } - } - } - } - } - util::SimpleLogger().Write(); - util::SimpleLogger().Write() << "Inside-Points"; - util::SimpleLogger().Write() << insidepoints.size(); - for (auto p : insidepoints) - { - double lon = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; - double lat = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; - auto pre = predecessor_map[p]; - auto d = distance_map[p]; - util::SimpleLogger().Write() << "Lat: " << lat; - util::SimpleLogger().Write() << "Lon: " << lon; - util::SimpleLogger().Write() << "Source: " << p << " Predecessor: " << pre - << " Distance " << d; - // util::SimpleLogger().Write(); - } - - util::SimpleLogger().Write() << "Edgepoints"; - util::SimpleLogger().Write() << edgepoints.size(); - // for (auto p : edgepoints) - // { - // double lon = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; - // double lat = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; - // auto pre = predecessor_map[p]; - // auto d = distance_map[p]; - //// util::SimpleLogger().Write() << "Lat: " << lat; - //// util::SimpleLogger().Write() << "Lon: " << lon; - // util::SimpleLogger().Write() << "Predecessor: " << pre << "Distance " << d; - // util::SimpleLogger().Write(); - // } - - util::json::Array data; - for (auto p : insidepoints) - { - util::json::Object object; - - util::json::Object source; - source.values["lat"] = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; - source.values["lon"] = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; - object.values["p1"] = std::move(source); - - util::json::Object predecessor; - auto pre = predecessor_map[p]; - predecessor.values["lat"] = coordinate_list[pre].lat / osrm::COORDINATE_PRECISION; - predecessor.values["lon"] = coordinate_list[pre].lon / osrm::COORDINATE_PRECISION; - object.values["p2"] = std::move(predecessor); - - util::json::Object distance; - object.values["distance_from_start"] = distance_map[p]; - - data.values.push_back(object); - } + explicit IsochronePlugin(datafacade::BaseDataFacade &facade); - json_result.values["Range-Analysis"] = std::move(data); - return Status::Ok; - } + Status HandleRequest(const api::IsochroneParameters ¶ms, + util::json::Object &json_result); }; } } diff --git a/include/osrm/isochrone_parameters.hpp b/include/osrm/isochrone_parameters.hpp new file mode 100644 index 00000000000..03e141210af --- /dev/null +++ b/include/osrm/isochrone_parameters.hpp @@ -0,0 +1,15 @@ +// +// Created by robin on 4/14/16. +// + +#ifndef GLOBAL_ISOCHRONE_PARAMETERS_HPP +#define GLOBAL_ISOCHRONE_PARAMETERS_HPP + +#include "engine/api/isochrone_parameters.hpp" + +namespace osrm +{ +using engine::api::IsochroneParameters; +} + +#endif // GLOBAL_ISOCHRONE_PARAMETERS_HPP diff --git a/include/osrm/osrm.hpp b/include/osrm/osrm.hpp index 1de8610a34f..20606765069 100644 --- a/include/osrm/osrm.hpp +++ b/include/osrm/osrm.hpp @@ -44,22 +44,23 @@ using engine::api::NearestParameters; using engine::api::TripParameters; using engine::api::MatchParameters; using engine::api::TileParameters; +using engine::api::IsochroneParameters; -/** - * Represents a Open Source Routing Machine with access to its services. - * - * This represents an Open Source Routing Machine (OSRM) instance, with the services: - * - * - Route: shortest path queries for coordinates - * - Table: distance tables for coordinates - * - Nearest: nearest street segment for coordinate - * - Trip: shortest round trip between coordinates - * - Match: snaps noisy coordinate traces to the road network - * - Tile: vector tiles with internal graph representation - * - * All services take service-specific parameters, fill a JSON object, and return a status code. - */ -class OSRM final + /** + * Represents a Open Source Routing Machine with access to its services. + * + * This represents an Open Source Routing Machine (OSRM) instance, with the services: + * + * - Route: shortest path queries for coordinates + * - Table: distance tables for coordinates + * - Nearest: nearest street segment for coordinate + * - Trip: shortest round trip between coordinates + * - Match: snaps noisy coordinate traces to the road network + * - Tile: vector tiles with internal graph representation + * + * All services take service-specific parameters, fill a JSON object, and return a status code. + */ + class OSRM final { public: /** @@ -130,6 +131,14 @@ class OSRM final */ Status Tile(const TileParameters ¶meters, std::string &result); + /** + * Isocrhone for given distance + * \param parameters nearest query specific parameters + * \return Status indicating success for the query or failure + * \see Status, NearestParameters and json::Object + */ + Status Isochrone(const IsochroneParameters ¶meters, json::Object &result); + private: std::unique_ptr engine_; }; diff --git a/include/osrm/osrm_fwd.hpp b/include/osrm/osrm_fwd.hpp index f0f1f17af62..9f436b0b755 100644 --- a/include/osrm/osrm_fwd.hpp +++ b/include/osrm/osrm_fwd.hpp @@ -52,6 +52,7 @@ struct NearestParameters; struct TripParameters; struct MatchParameters; struct TileParameters; +struct IsochroneParameters; } // ns api class Engine; diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp new file mode 100644 index 00000000000..d8c0693de2b --- /dev/null +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -0,0 +1,47 @@ +// +// Created by robin on 4/13/16. +// + +#ifndef OSRM_ISOCHRONE_PARAMETER_GRAMMAR_HPP +#define OSRM_ISOCHRONE_PARAMETER_GRAMMAR_HPP + +#include "engine/api/isochrone_parameters.hpp" +#include "server/api/base_parameters_grammar.hpp" + +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; + +struct IsochroneParametersGrammar final : public BaseParametersGrammar +{ + using Iterator = std::string::iterator; + + IsochroneParametersGrammar() : BaseParametersGrammar(root_rule, parameters) + { + const auto set_number = [this](const unsigned number) + { + parameters.number_of_results = number; + }; + isochrone_rule = (qi::lit("number=") > qi::uint_)[set_number]; + root_rule = + query_rule > -qi::lit(".json") > -(qi::lit("?") > (isochrone_rule | base_rule) % '&'); + } + + engine::api::IsochroneParameters parameters; + + private: + qi::rule root_rule; + qi::rule isochrone_rule; +}; +} +} +} + +#endif // OSRM_ISOCHRONE_PARAMETER_GRAMMAR_HPP diff --git a/include/server/service/isochrone_service.hpp b/include/server/service/isochrone_service.hpp new file mode 100644 index 00000000000..77de052943f --- /dev/null +++ b/include/server/service/isochrone_service.hpp @@ -0,0 +1,36 @@ +// +// Created by robin on 4/14/16. +// + +#ifndef SERVER_SERVICE_ISOCHRONE_SERVICE_HPP +#define SERVER_SERVICE_ISOCHRONE_SERVICE_HPP + +#include "server/service/base_service.hpp" + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class IsochroneService final : public BaseService +{ + public: + IsochroneService(OSRM &routing_machine) : BaseService(routing_machine) {} + + engine::Status RunQuery(std::string &query, ResultT &result) final override; + + unsigned GetVersion() final override { return 1; } +}; +} +} +} +#endif // SERVER_SERVICE_ISOCHRONE_SERVICE_HPP diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 169af892588..984d45504e1 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -5,11 +5,11 @@ #include "engine/plugins/table.hpp" #include "engine/plugins/nearest.hpp" -#include "engine/plugins/timestamp.hpp" #include "engine/plugins/trip.hpp" #include "engine/plugins/viaroute.hpp" #include "engine/plugins/match.hpp" #include "engine/plugins/tile.hpp" +#include "engine/plugins/isochrone.hpp" #include "engine/datafacade/datafacade_base.hpp" #include "engine/datafacade/internal_datafacade.hpp" @@ -17,7 +17,6 @@ #include "storage/shared_barriers.hpp" #include "util/make_unique.hpp" -#include "util/routed_options.hpp" #include "util/simple_logger.hpp" #include @@ -135,7 +134,12 @@ Engine::Engine(EngineConfig &config) } else { - return_code = plugin_iterator->second->HandleRequest(route_parameters, json_result); + if (!config.storage_config.IsValid()) + { + throw util::exception("Invalid file paths given!"); + } + query_data_facade = + util::make_unique(config.storage_config); } // Register plugins @@ -147,6 +151,7 @@ Engine::Engine(EngineConfig &config) trip_plugin = create(*query_data_facade, config.max_locations_trip); match_plugin = create(*query_data_facade, config.max_locations_map_matching); tile_plugin = create(*query_data_facade); + isochrone_plugin = create(*query_data_facade); } // make sure we deallocate the unique ptr at a position where we know the size of the plugins @@ -183,6 +188,10 @@ Status Engine::Tile(const api::TileParameters ¶ms, std::string &result) { return RunQuery(lock, *query_data_facade, params, *tile_plugin, result); } +Status Engine::Isochrone(const api::IsochroneParameters ¶ms, util::json::Object &result) { + return RunQuery(lock, *query_data_facade, params, *isochrone_plugin, result); +} + } // engine ns } // osrm ns diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp new file mode 100644 index 00000000000..22b57594d6d --- /dev/null +++ b/src/engine/plugins/isochrone.cpp @@ -0,0 +1,25 @@ +// +// Created by robin on 4/13/16. +// + +#include "engine/plugins/isochrone.hpp" +#include "engine/api/isochrone_parameters.hpp" +#include "engine/api/isochrone_api.hpp" + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ +IsochronePlugin::IsochronePlugin(datafacade::BaseDataFacade &facade) : BasePlugin{facade} {} + +Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, + util::json::Object &json_result) +{ + + return Status::Ok; +} +} +} +} diff --git a/src/osrm/osrm.cpp b/src/osrm/osrm.cpp index 9a69e2e1451..9a4b2575c1a 100644 --- a/src/osrm/osrm.cpp +++ b/src/osrm/osrm.cpp @@ -4,6 +4,7 @@ #include "engine/api/nearest_parameters.hpp" #include "engine/api/trip_parameters.hpp" #include "engine/api/match_parameters.hpp" +#include "engine/api/isochrone_parameters.hpp" #include "engine/engine.hpp" #include "engine/status.hpp" #include "engine/engine_config.hpp" @@ -50,5 +51,9 @@ engine::Status OSRM::Tile(const engine::api::TileParameters ¶ms, std::string { return engine_->Tile(params, result); } +engine::Status OSRM::Isochrone(const engine::api::IsochroneParameters ¶ms, json::Object &result) +{ + return engine_->Isochrone(params, result); +} } // ns osrm diff --git a/src/server/api/parameters_parser.cpp b/src/server/api/parameters_parser.cpp index 1d4485a0379..faf984b836a 100644 --- a/src/server/api/parameters_parser.cpp +++ b/src/server/api/parameters_parser.cpp @@ -6,6 +6,7 @@ #include "server/api/table_parameter_grammar.hpp" #include "server/api/tile_parameter_grammar.hpp" #include "server/api/trip_parameter_grammar.hpp" +#include "server/api/isochrone_parameter_grammar.hpp" #include @@ -95,6 +96,13 @@ boost::optional parseParameters(std::string::iterat { return detail::parseParameters(iter, end); } +template <> +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) +{ + return detail::parseParameters( + iter, end); +} } // ns api } // ns server diff --git a/src/server/service/isochrone_service.cpp b/src/server/service/isochrone_service.cpp new file mode 100644 index 00000000000..7be0a6cdf2a --- /dev/null +++ b/src/server/service/isochrone_service.cpp @@ -0,0 +1,76 @@ +// +// Created by robin on 4/14/16. +// + +#include "server/service/isochrone_service.hpp" +#include "server/service/utils.hpp" + +#include "engine/api/isochrone_parameters.hpp" +#include "server/api/parameters_parser.hpp" + +#include "util/json_container.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +namespace +{ +std::string getWrongOptionHelp(const engine::api::IsochroneParameters ¶meters) +{ + std::string help; + + const auto coord_size = parameters.coordinates.size(); + + const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints", + parameters.hints, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings", + parameters.bearings, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses", + parameters.radiuses, coord_size, help); + + if (!param_size_mismatch && parameters.coordinates.size() < 2) + { + help = "Number of coordinates needs to be at least two."; + } + + return help; +} +} // anon. ns + +engine::Status IsochroneService::RunQuery(std::string &query, ResultT &result) +{ + result = util::json::Object(); + auto &json_result = result.get(); + + auto query_iterator = query.begin(); + auto parameters = api::parseParameters(query_iterator, query.end()); + if (!parameters || query_iterator != query.end()) + { + const auto position = std::distance(query.begin(), query_iterator); + json_result.values["code"] = "InvalidQuery"; + json_result.values["message"] = + "Query string malformed close to position " + std::to_string(position); + return engine::Status::Error; + } + BOOST_ASSERT(parameters); + + if (!parameters->IsValid()) + { + json_result.values["code"] = "InvalidOptions"; + json_result.values["message"] = getWrongOptionHelp(*parameters); + return engine::Status::Error; + } + BOOST_ASSERT(parameters->IsValid()); + + return BaseService::routing_machine.Isochrone(*parameters, json_result); +} +} +} +} diff --git a/src/server/service_handler.cpp b/src/server/service_handler.cpp index 98ba6a61703..58740d22922 100644 --- a/src/server/service_handler.cpp +++ b/src/server/service_handler.cpp @@ -6,6 +6,7 @@ #include "server/service/trip_service.hpp" #include "server/service/match_service.hpp" #include "server/service/tile_service.hpp" +#include "server/service/isochrone_service.hpp" #include "server/api/parsed_url.hpp" #include "util/json_util.hpp" @@ -23,6 +24,7 @@ ServiceHandler::ServiceHandler(osrm::EngineConfig &config) : routing_machine(con service_map["trip"] = util::make_unique(routing_machine); service_map["match"] = util::make_unique(routing_machine); service_map["tile"] = util::make_unique(routing_machine); + service_map["isochrone"] = util::make_unique(routing_machine); } engine::Status ServiceHandler::RunQuery(api::ParsedURL parsed_url, From ab4e259a86b86b13bbd7c40a5e891a7b8f331a4d Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 17 Mar 2016 10:26:37 +0100 Subject: [PATCH 09/43] started range analysis --- include/engine/plugins/range_analysis.hpp | 136 +++++++++++++++++ .../engine/routing_algorithms/one_to_many.hpp | 138 ++++++++++++++++++ include/engine/search_engine.hpp | 50 +++++++ src/engine/engine.cpp | 1 + 4 files changed, 325 insertions(+) create mode 100644 include/engine/plugins/range_analysis.hpp create mode 100644 include/engine/routing_algorithms/one_to_many.hpp create mode 100644 include/engine/search_engine.hpp diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp new file mode 100644 index 00000000000..7820be2da9e --- /dev/null +++ b/include/engine/plugins/range_analysis.hpp @@ -0,0 +1,136 @@ +// +// Created by robin on 3/10/16. +// + +#ifndef OSRM_TEST_PLUGIN_HPP +#define OSRM_TEST_PLUGIN_HPP + +#include "engine/plugins/plugin_base.hpp" +#include "engine/search_engine.hpp" +#include "engine/object_encoder.hpp" + +#include "util/make_unique.hpp" +#include "util/string_util.hpp" +#include "util/simple_logger.hpp" +#include "osrm/json_container.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +template class RangeAnalysis final : public BasePlugin +{ + + struct NodeBucket + { + NodeID predecessor; + EdgeWeight distance; + NodeBucket(const NodeID predecessor, const EdgeWeight distance) + : predecessor(predecessor), distance(distance) + { + } + }; + using ResultMap = std::unordered_map; + + private: + std::string temp_string; + std::string descriptor_string; + std::unique_ptr> search_engine_ptr; + DataFacadeT *facade; + ResultMap resultMap; + + public: + explicit RangeAnalysis(DataFacadeT *facade) : descriptor_string("range"), facade(facade) + { + search_engine_ptr = util::make_unique>(facade); + } + + virtual ~RangeAnalysis() {} + + const std::string GetDescriptor() const override final { return descriptor_string; } + + Status HandleRequest(const RouteParameters &routeParameters, + util::json::Object &json_result) override final + { + /* Check if valid + * + */ + if (routeParameters.coordinates.size() != 1) + { + json_result.values["status_message"] = "Number of Coordinates should be 1"; + return Status::Error; + } + if (!routeParameters.coordinates.front().IsValid()) + { + json_result.values["status_message"] = "Coordinate is invalid"; + return Status::Error; + } + + const auto &input_bearings = routeParameters.bearings; + if (input_bearings.size() > 0 && + routeParameters.coordinates.size() != input_bearings.size()) + { + json_result.values["status_message"] = + "Number of bearings does not match number of coordinate"; + return Status::Error; + } + + /** Interesting code starts here + * + */ + + // auto number_of_results = static_cast(routeParameters.num_results); + const int bearing = input_bearings.size() > 0 ? input_bearings.front().first : 0; + const int range = + input_bearings.size() > 0 + ? (input_bearings.front().second ? *input_bearings.front().second : 10) + : 180; + + std::vector phantomNodeVector; + auto phantomNodePair = facade->NearestPhantomNodeWithAlternativeFromBigComponent( + routeParameters.coordinates.front(), bearing, range); + + phantomNodeVector.push_back(phantomNodePair); + auto snapped_source_phantoms = snapPhantomNodes(phantomNodeVector); + // resultMap = search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); + search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); + + std::string temp_string; + json_result.values["title"] = "This is a Test-Plugin"; + + temp_string = std::to_string(routeParameters.coordinates.size()); + json_result.values["location_count"] = temp_string; + temp_string = std::to_string(resultMap.size()); + json_result.values["Result-Map"] = temp_string; + + for (auto it = resultMap.begin(); it != resultMap.end(); ++it) + { + + util::SimpleLogger().Write() << "Node-ID: " << it->first; + NodeBucket n = it->second; + util::SimpleLogger().Write() << "Predecessor:" << n.predecessor; + util::SimpleLogger().Write() << "Distance: " << n.distance; + } + // util::json::Array json_coordinate; + // util::json::Object result; + // json_coordinate.values.push_back(node.location.lat / COORDINATE_PRECISION); + // json_coordinate.values.push_back(node.location.lon / COORDINATE_PRECISION); + // result.values["mapped coordinate"] = json_coordinate; + // result.values["name"] = facade->get_name_for_id(node.name_id); + return Status::Ok; + } +}; +} +} +} +#endif // OSRM_TEST_PLUGIN_HPP diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp new file mode 100644 index 00000000000..70eb2ba74b5 --- /dev/null +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -0,0 +1,138 @@ +// +// Created by robin on 3/16/16. +// + +#ifndef OSRM_ONE_TO_MANY_HPP +#define OSRM_ONE_TO_MANY_HPP + +#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/search_engine_data.hpp" +#include "util/typedefs.hpp" +#include "util/simple_logger.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace routing_algorithms +{ + +template +class OneToManyRouting final + : public BasicRoutingInterface> +{ + using super = BasicRoutingInterface>; + using QueryHeap = SearchEngineData::QueryHeap; + using PredecessorMap = std::unordered_map; + using DistanceMap = std::unordered_map; + SearchEngineData &engine_working_data; + + + public: + OneToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) + : super(facade), engine_working_data(engine_working_data) + { + } + + ~OneToManyRouting() {} + + + void operator()(PhantomNode &phantomSource, const int distance) const + { + + PredecessorMap predecessorMap; + DistanceMap distanceMap; + engine_working_data.InitializeOrClearFirstThreadLocalStorage( + super::facade->GetNumberOfNodes()); + + QueryHeap &query_heap = *(engine_working_data.forward_heap_1); + + query_heap.Clear(); + // insert target(s) at distance 0 + if (SPECIAL_NODEID != phantomSource.forward_node_id) + { + query_heap.Insert(phantomSource.forward_node_id, + -phantomSource.GetForwardWeightPlusOffset(), + phantomSource.forward_node_id); + } + if (SPECIAL_NODEID != phantomSource.reverse_node_id) + { + query_heap.Insert(phantomSource.reverse_node_id, + -phantomSource.GetReverseWeightPlusOffset(), + phantomSource.reverse_node_id); + } + + // explore search space + while (!query_heap.Empty()) + { + ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); + } + for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) + { + util::SimpleLogger().Write() << "Node-ID: " << it->first; + util::SimpleLogger().Write() << "Predecessor:" << it->second; + util::SimpleLogger().Write() << "Distance: " << distanceMap[it->first]; +// resultMap[it->first].insert(std::make_pair(it->second, distanceMap[it->first])); +// resultMap[it->first].emplace_back(it->second, distanceMap[it->first]); + } + } + + void ForwardRoutingStep(QueryHeap &query_heap, + const int distance, + PredecessorMap &predecessorMap, + DistanceMap &distanceMap) const + { + const NodeID node = query_heap.DeleteMin(); + const int source_distance = query_heap.GetKey(node); + for (auto edge : super::facade->GetAdjacentEdgeRange(node)) + { + const auto &data = super::facade->GetEdgeData(edge); + const NodeID to = super::facade->GetTarget(edge); + const int edge_weight = data.distance; + BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); + + if (query_heap.WasInserted(to)) + { + if (query_heap.GetKey(to) + edge_weight < source_distance) + { + continue; + } + } + const int to_distance = source_distance + edge_weight; + + if (to_distance > distance) + { + continue; + } + + // New Node discovered -> Add to Heap + Node Info Storage + if (!query_heap.WasInserted(to)) + { + query_heap.Insert(to, to_distance, node); + predecessorMap[to] = node; + distanceMap[to] = to_distance; + } + // Found a shorter Path -> Update distance + else if (to_distance < query_heap.GetKey(to)) + { + // new parent + query_heap.GetData(to).parent = node; + query_heap.DecreaseKey(to, to_distance); + + predecessorMap[to] = node; + distanceMap[to] = to_distance; + } + } + } +}; +} +} +} +#endif // OSRM_ONE_TO_MANY_HPP diff --git a/include/engine/search_engine.hpp b/include/engine/search_engine.hpp new file mode 100644 index 00000000000..afb78c293f2 --- /dev/null +++ b/include/engine/search_engine.hpp @@ -0,0 +1,50 @@ +#ifndef SEARCH_ENGINE_HPP +#define SEARCH_ENGINE_HPP + +#include "engine/search_engine_data.hpp" +#include "engine/routing_algorithms/alternative_path.hpp" +#include "engine/routing_algorithms/many_to_many.hpp" +#include "engine/routing_algorithms/map_matching.hpp" +#include "engine/routing_algorithms/shortest_path.hpp" +#include "engine/routing_algorithms/direct_shortest_path.hpp" +#include "engine/routing_algorithms/one_to_many.hpp" + +#include + +namespace osrm +{ +namespace engine +{ + +template class SearchEngine +{ + private: + DataFacadeT *facade; + SearchEngineData engine_working_data; + + public: + routing_algorithms::ShortestPathRouting shortest_path; + routing_algorithms::DirectShortestPathRouting direct_shortest_path; + routing_algorithms::AlternativeRouting alternative_path; + routing_algorithms::ManyToManyRouting distance_table; + routing_algorithms::MapMatching map_matching; + routing_algorithms::OneToManyRouting oneToMany; + + explicit SearchEngine(DataFacadeT *facade) + : facade(facade), shortest_path(facade, engine_working_data), + direct_shortest_path(facade, engine_working_data), + alternative_path(facade, engine_working_data), + distance_table(facade, engine_working_data), map_matching(facade, engine_working_data), + oneToMany(facade, engine_working_data) + { + static_assert(!std::is_pointer::value, "don't instantiate with ptr type"); + static_assert(std::is_object::value, + "don't instantiate with void, function, or reference"); + } + + ~SearchEngine() {} +}; +} +} + +#endif // SEARCH_ENGINE_HPP diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 9207c44c016..a13996f5bb2 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -9,6 +9,7 @@ #include "engine/plugins/viaroute.hpp" #include "engine/plugins/tile.hpp" #include "engine/plugins/match.hpp" +#include "engine/plugins/tile.hpp" #include "engine/datafacade/datafacade_base.hpp" #include "engine/datafacade/internal_datafacade.hpp" From fc1b6964b9bf02b7f5ff594d92ce3413ec05dde7 Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 17 Mar 2016 11:51:33 +0100 Subject: [PATCH 10/43] first implementation of routing algo with pretty json-print --- include/engine/plugins/range_analysis.hpp | 62 ++++++++++--------- .../engine/routing_algorithms/one_to_many.hpp | 17 ++--- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp index 7820be2da9e..ba380cdcf4a 100644 --- a/include/engine/plugins/range_analysis.hpp +++ b/include/engine/plugins/range_analysis.hpp @@ -31,23 +31,16 @@ namespace plugins template class RangeAnalysis final : public BasePlugin { - struct NodeBucket - { - NodeID predecessor; - EdgeWeight distance; - NodeBucket(const NodeID predecessor, const EdgeWeight distance) - : predecessor(predecessor), distance(distance) - { - } - }; - using ResultMap = std::unordered_map; + using PredecessorMap = std::unordered_map; + using DistanceMap = std::unordered_map; private: std::string temp_string; std::string descriptor_string; std::unique_ptr> search_engine_ptr; DataFacadeT *facade; - ResultMap resultMap; + PredecessorMap predecessorMap; + DistanceMap distanceMap; public: explicit RangeAnalysis(DataFacadeT *facade) : descriptor_string("range"), facade(facade) @@ -102,31 +95,40 @@ template class RangeAnalysis final : public BasePlugin phantomNodeVector.push_back(phantomNodePair); auto snapped_source_phantoms = snapPhantomNodes(phantomNodeVector); - // resultMap = search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); - search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000); + search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000, predecessorMap, + distanceMap); - std::string temp_string; - json_result.values["title"] = "This is a Test-Plugin"; + BOOST_ASSERT(predecessorMap.size() == distanceMap.size()); - temp_string = std::to_string(routeParameters.coordinates.size()); - json_result.values["location_count"] = temp_string; - temp_string = std::to_string(resultMap.size()); - json_result.values["Result-Map"] = temp_string; + std::string temp_string; + json_result.values["title"] = "Range Analysis"; - for (auto it = resultMap.begin(); it != resultMap.end(); ++it) + util::json::Array data; + for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) { + util::json::Object object; - util::SimpleLogger().Write() << "Node-ID: " << it->first; - NodeBucket n = it->second; - util::SimpleLogger().Write() << "Predecessor:" << n.predecessor; - util::SimpleLogger().Write() << "Distance: " << n.distance; + util::json::Object source; + FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); + source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; + source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; + object.values["Source"] = std::move(source); + + util::json::Object predecessor; + FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); + predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; + predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; + object.values["Predecessor"] = std::move(predecessor); + + util::json::Object distance; + object.values["distance_from_start"] = distanceMap[it->first]; + + data.values.push_back(object); } - // util::json::Array json_coordinate; - // util::json::Object result; - // json_coordinate.values.push_back(node.location.lat / COORDINATE_PRECISION); - // json_coordinate.values.push_back(node.location.lon / COORDINATE_PRECISION); - // result.values["mapped coordinate"] = json_coordinate; - // result.values["name"] = facade->get_name_for_id(node.name_id); + temp_string = std::to_string(distanceMap.size()); + json_result.values["Nodes Found"] = temp_string;; + json_result.values["Range-Analysis"] = std::move(data); + return Status::Ok; } }; diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp index 70eb2ba74b5..436c573973a 100644 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -34,7 +34,6 @@ class OneToManyRouting final using DistanceMap = std::unordered_map; SearchEngineData &engine_working_data; - public: OneToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) : super(facade), engine_working_data(engine_working_data) @@ -43,12 +42,12 @@ class OneToManyRouting final ~OneToManyRouting() {} - - void operator()(PhantomNode &phantomSource, const int distance) const + void operator()(PhantomNode &phantomSource, + const int distance, + PredecessorMap &predecessorMap, + DistanceMap &distanceMap) const { - PredecessorMap predecessorMap; - DistanceMap distanceMap; engine_working_data.InitializeOrClearFirstThreadLocalStorage( super::facade->GetNumberOfNodes()); @@ -74,14 +73,6 @@ class OneToManyRouting final { ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); } - for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) - { - util::SimpleLogger().Write() << "Node-ID: " << it->first; - util::SimpleLogger().Write() << "Predecessor:" << it->second; - util::SimpleLogger().Write() << "Distance: " << distanceMap[it->first]; -// resultMap[it->first].insert(std::make_pair(it->second, distanceMap[it->first])); -// resultMap[it->first].emplace_back(it->second, distanceMap[it->first]); - } } void ForwardRoutingStep(QueryHeap &query_heap, From fddfd0b6b37147a9302e95b7fd461aec960c63e3 Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 23 Mar 2016 12:20:58 +0100 Subject: [PATCH 11/43] added testing --- features/bicycle/range.feature | 21 ++++ features/step_definitions/range.rb | 114 ++++++++++++++++++ include/engine/plugins/range_analysis.hpp | 6 +- .../engine/routing_algorithms/one_to_many.hpp | 5 +- 4 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 features/bicycle/range.feature create mode 100644 features/step_definitions/range.rb diff --git a/features/bicycle/range.feature b/features/bicycle/range.feature new file mode 100644 index 00000000000..8b0b958c341 --- /dev/null +++ b/features/bicycle/range.feature @@ -0,0 +1,21 @@ +@range +Feature: Getting the range limited by Distance + + Background: + Given the profile "bicycle" + + Scenario: Range + Given the node map + | a | | b | + | | | | + | | | c | + + And the ways + | nodes | + | ab | + | ac | + + When I request range I should get + |source | node | pred | + | a | b | a | + | a | c | a | diff --git a/features/step_definitions/range.rb b/features/step_definitions/range.rb new file mode 100644 index 00000000000..9537158ee38 --- /dev/null +++ b/features/step_definitions/range.rb @@ -0,0 +1,114 @@ +When /^I request range I should get$/ do |table| + reprocess + actual = [] + OSRMLoader.load(self,"#{contracted_file}.osrm") do + table.hashes.each_with_index do |row,ri| + source = find_node_by_name row['source'] #a + raise "*** unknown in-node '#{row['source']}" unless source + + node = find_node_by_name row['node'] #b + raise "*** unknown in-node '#{row['node']}" unless node + + pred = find_node_by_name row['pred'] #a + raise "*** unknown in-node '#{row['pred']}" unless pred + + # dist = row['distance'] + # raise "*** unknown in-node '#{row['distance']}" unless dist + + #print(source_node) + response = request_range source, @query_params + if response.code == "200" && response.body.empty? == false + json = JSON.parse response.body + # print(json) + if json['status'] == 200 + size = json['Nodes found'] + range = json['Range-Analysis'] + end + end + + #print(size) + # got = {'source' => row['source'], + # 'node' => p1, + # 'pred' => p2} + # 'distance' => retDis} + + # print(range) + ok = false + for coordinate in range; + p1 = Array.new + p2 = Array.new + + + p1 << coordinate['p1']['lat'] + p1 << coordinate['p1']['lon'] + p2 << coordinate['p2']['lat'] + p2 << coordinate['p2']['lon'] + + got = {'source' => row['source'], + 'node' => p1, + 'pred' => p2} + + print(p1) + print(p2) + print("fuu"); + + if FuzzyMatch.match_location(p1, node) && FuzzyMatch.match_location(p2, pred) || ok + print("asdasdasd") + key = 'node' + got[key] = row[key] + key = 'pred' + got[key] = row[key] + ok = true + else + key = 'node' + row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + key = 'pred' + row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + end + end + + # row.keys.each do |key| + # if key=='node' + # if FuzzyMatch.match_location p1, node + # got[key] = row[key] + # ok = true + # else + # row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + # end + # end + # if key=='pred' + # if FuzzyMatch.match_location p2, pred + # got[key] = row[key] + # ok = true + # else + # row[key] = "#{row[key]} [#{pred.lat},#{pred.lon}]" + # end + # end + # if key=='distance' + # if retDis == dist + # got[key] = row[key] + # else + # row[key] = dist + # ok = false + # end + # end + # end + + # end + + + # print(p1) + # print(p2) + # print(node) + + + + unless ok + failed = { :attempt => 'range', :query => @query, :response => response } + log_fail row,got,[failed] + end + actual << got + end + end + table.diff! actual +end diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp index ba380cdcf4a..04533cbe318 100644 --- a/include/engine/plugins/range_analysis.hpp +++ b/include/engine/plugins/range_analysis.hpp @@ -103,6 +103,8 @@ template class RangeAnalysis final : public BasePlugin std::string temp_string; json_result.values["title"] = "Range Analysis"; + json_result.values["start"] = routeParameters.coordinates[0].lat; + util::json::Array data; for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) { @@ -112,13 +114,13 @@ template class RangeAnalysis final : public BasePlugin FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; - object.values["Source"] = std::move(source); + object.values["p1"] = std::move(source); util::json::Object predecessor; FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; - object.values["Predecessor"] = std::move(predecessor); + object.values["p2"] = std::move(predecessor); util::json::Object distance; object.values["distance_from_start"] = distanceMap[it->first]; diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp index 436c573973a..4ab26b9db9c 100644 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -52,6 +52,7 @@ class OneToManyRouting final super::facade->GetNumberOfNodes()); QueryHeap &query_heap = *(engine_working_data.forward_heap_1); + QueryHeap &backward_heap = *(engine_working_data.reverse_heap_1); query_heap.Clear(); // insert target(s) at distance 0 @@ -68,10 +69,12 @@ class OneToManyRouting final phantomSource.reverse_node_id); } + util::SimpleLogger().Write() << phantomSource; // explore search space while (!query_heap.Empty()) { - ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); +// ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); + ForwardRoutingStep(query_heap, backward_heap, ) } } From 2035618ed51c852c9b85b53fb47a7a517c2ba5b6 Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 17 Mar 2016 10:26:37 +0100 Subject: [PATCH 12/43] started range analysis --- src/engine/engine.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index a13996f5bb2..00d56ab17d1 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -7,7 +7,6 @@ #include "engine/plugins/nearest.hpp" #include "engine/plugins/trip.hpp" #include "engine/plugins/viaroute.hpp" -#include "engine/plugins/tile.hpp" #include "engine/plugins/match.hpp" #include "engine/plugins/tile.hpp" @@ -134,11 +133,7 @@ Engine::Engine(EngineConfig &config) } else { - if (!config.storage_config.IsValid()) - { - throw util::exception("Invalid file paths given!"); - } - query_data_facade = util::make_unique(config.storage_config); + return_code = plugin_iterator->second->HandleRequest(route_parameters, json_result); } // Register plugins From be23a0dd3e0dc46f59db8053ebc7c6275f005aa3 Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 17 Mar 2016 11:51:33 +0100 Subject: [PATCH 13/43] first implementation of routing algo with pretty json-print --- include/engine/plugins/range_analysis.hpp | 16 ++++++++++++++++ .../engine/routing_algorithms/one_to_many.hpp | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp index 04533cbe318..f0d9432ca90 100644 --- a/include/engine/plugins/range_analysis.hpp +++ b/include/engine/plugins/range_analysis.hpp @@ -97,6 +97,7 @@ template class RangeAnalysis final : public BasePlugin auto snapped_source_phantoms = snapPhantomNodes(phantomNodeVector); search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000, predecessorMap, distanceMap); +<<<<<<< HEAD BOOST_ASSERT(predecessorMap.size() == distanceMap.size()); @@ -104,6 +105,13 @@ template class RangeAnalysis final : public BasePlugin json_result.values["title"] = "Range Analysis"; json_result.values["start"] = routeParameters.coordinates[0].lat; +======= + + BOOST_ASSERT(predecessorMap.size() == distanceMap.size()); + + std::string temp_string; + json_result.values["title"] = "Range Analysis"; +>>>>>>> first implementation of routing algo with pretty json-print util::json::Array data; for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) @@ -114,13 +122,21 @@ template class RangeAnalysis final : public BasePlugin FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; +<<<<<<< HEAD object.values["p1"] = std::move(source); +======= + object.values["Source"] = std::move(source); +>>>>>>> first implementation of routing algo with pretty json-print util::json::Object predecessor; FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; +<<<<<<< HEAD object.values["p2"] = std::move(predecessor); +======= + object.values["Predecessor"] = std::move(predecessor); +>>>>>>> first implementation of routing algo with pretty json-print util::json::Object distance; object.values["distance_from_start"] = distanceMap[it->first]; diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp index 4ab26b9db9c..fd1988f34fc 100644 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -73,8 +73,12 @@ class OneToManyRouting final // explore search space while (!query_heap.Empty()) { +<<<<<<< HEAD // ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); ForwardRoutingStep(query_heap, backward_heap, ) +======= + ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); +>>>>>>> first implementation of routing algo with pretty json-print } } From 06e316198ad7d04c6be737d3218de759e3b99fb9 Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 23 Mar 2016 12:20:58 +0100 Subject: [PATCH 14/43] added testing --- features/support/route.rb | 190 ++++++++++++++++++ include/engine/plugins/range_analysis.hpp | 11 + .../engine/routing_algorithms/one_to_many.hpp | 5 + 3 files changed, 206 insertions(+) create mode 100644 features/support/route.rb diff --git a/features/support/route.rb b/features/support/route.rb new file mode 100644 index 00000000000..83e549b219c --- /dev/null +++ b/features/support/route.rb @@ -0,0 +1,190 @@ +require 'net/http' + +HOST = "http://127.0.0.1:#{OSRM_PORT}" +DESTINATION_REACHED = 15 #OSRM instruction code + +def request_path service, params + uri = "#{HOST}/" + service + response = send_request uri, params + return response +end + +def request_url path + uri = URI.parse"#{HOST}/#{path}" + @query = uri.to_s + Timeout.timeout(OSRM_TIMEOUT) do + Net::HTTP.get_response uri + end +rescue Errno::ECONNREFUSED => e + raise "*** osrm-routed is not running." +rescue Timeout::Error + raise "*** osrm-routed did not respond." +end + +# Overwriters the default values in defaults. +# e.g. [[a, 1], [b, 2]], [[a, 5], [d, 10]] => [[a, 5], [b, 2], [d, 10]] +def overwrite_params defaults, other + merged = [] + defaults.each do |k,v| + idx = other.index { |p| p[0] == k } + if idx == nil then + merged << [k, v] + else + merged << [k, other[idx][1]] + end + end + other.each do |k,v| + if merged.index { |pair| pair[0] == k} == nil then + merged << [k, v] + end + end + + return merged +end + +def request_route waypoints, bearings, user_params + raise "*** number of bearings does not equal the number of waypoints" unless bearings.size == 0 || bearings.size == waypoints.size + + defaults = [['output','json'], ['instructions',true], ['alt',false]] + params = overwrite_params defaults, user_params + encoded_waypoint = waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } + if bearings.size > 0 + encoded_bearings = bearings.map { |b| ["b", b.to_s]} + parasm = params.concat encoded_waypoint.zip(encoded_bearings).flatten! 1 + else + params = params.concat encoded_waypoint + end + + return request_path "viaroute", params +end + +def request_nearest node, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + params << ["loc", "#{node.lat},#{node.lon}"] + + return request_path "nearest", params + end + +def request_range node, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + params << ["loc", "#{node.lat},#{node.lon}"] + + return request_path "range", params +end + +def request_table waypoints, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + params = params.concat waypoints.map{ |w| [w[:type],"#{w[:coord].lat},#{w[:coord].lon}"] } + + return request_path "table", params +end + +def request_trip waypoints, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + params = params.concat waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } + + return request_path "trip", params +end + +def request_matching waypoints, timestamps, user_params + defaults = [['output', 'json']] + params = overwrite_params defaults, user_params + encoded_waypoint = waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } + if timestamps.size > 0 + encoded_timestamps = timestamps.map { |t| ["t", t.to_s]} + parasm = params.concat encoded_waypoint.zip(encoded_timestamps).flatten! 1 + else + params = params.concat encoded_waypoint + end + + return request_path "match", params +end + +def got_route? response + if response.code == "200" && !response.body.empty? + json = JSON.parse response.body + if json['status'] == 200 + return way_list( json['route_instructions']).empty? == false + end + end + return false +end + +def route_status response + if response.code == "200" && !response.body.empty? + json = JSON.parse response.body + return json['status'] + else + "HTTP #{response.code}" + end +end + +def extract_instruction_list instructions, index, postfix=nil + if instructions + instructions.reject { |r| r[0].to_s=="#{DESTINATION_REACHED}" }. + map { |r| r[index] }. + map { |r| (r=="" || r==nil) ? '""' : "#{r}#{postfix}" }. + join(',') + end +end + +def way_list instructions + extract_instruction_list instructions, 1 +end + +def compass_list instructions + extract_instruction_list instructions, 6 +end + +def bearing_list instructions + extract_instruction_list instructions, 7 +end + +def turn_list instructions + if instructions + types = { + 0 => :none, + 1 => :straight, + 2 => :slight_right, + 3 => :right, + 4 => :sharp_right, + 5 => :u_turn, + 6 => :sharp_left, + 7 => :left, + 8 => :slight_left, + 9 => :via, + 10 => :head, + 11 => :enter_roundabout, + 12 => :leave_roundabout, + 13 => :stay_roundabout, + 14 => :start_end_of_street, + 15 => :destination, + 16 => :name_changes, + 17 => :enter_contraflow, + 18 => :leave_contraflow + } + # replace instructions codes with strings + # "11-3" (enter roundabout and leave a 3rd exit) gets converted to "enter_roundabout-3" + instructions.map do |r| + r[0].to_s.gsub(/^\d*/) do |match| + types[match.to_i].to_s + end + end.join(',') + end +end + +def mode_list instructions + extract_instruction_list instructions, 8 +end + +def time_list instructions + extract_instruction_list instructions, 4, "s" +end + +def distance_list instructions + extract_instruction_list instructions, 2, "m" +end diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp index f0d9432ca90..664a1fbd46e 100644 --- a/include/engine/plugins/range_analysis.hpp +++ b/include/engine/plugins/range_analysis.hpp @@ -105,6 +105,7 @@ template class RangeAnalysis final : public BasePlugin json_result.values["title"] = "Range Analysis"; json_result.values["start"] = routeParameters.coordinates[0].lat; +<<<<<<< HEAD ======= BOOST_ASSERT(predecessorMap.size() == distanceMap.size()); @@ -112,6 +113,8 @@ template class RangeAnalysis final : public BasePlugin std::string temp_string; json_result.values["title"] = "Range Analysis"; >>>>>>> first implementation of routing algo with pretty json-print +======= +>>>>>>> added testing util::json::Array data; for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) @@ -122,21 +125,29 @@ template class RangeAnalysis final : public BasePlugin FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; +<<<<<<< HEAD <<<<<<< HEAD object.values["p1"] = std::move(source); ======= object.values["Source"] = std::move(source); >>>>>>> first implementation of routing algo with pretty json-print +======= + object.values["p1"] = std::move(source); +>>>>>>> added testing util::json::Object predecessor; FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; +<<<<<<< HEAD <<<<<<< HEAD object.values["p2"] = std::move(predecessor); ======= object.values["Predecessor"] = std::move(predecessor); >>>>>>> first implementation of routing algo with pretty json-print +======= + object.values["p2"] = std::move(predecessor); +>>>>>>> added testing util::json::Object distance; object.values["distance_from_start"] = distanceMap[it->first]; diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp index fd1988f34fc..9427d6799b3 100644 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -74,11 +74,16 @@ class OneToManyRouting final while (!query_heap.Empty()) { <<<<<<< HEAD +<<<<<<< HEAD // ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); ForwardRoutingStep(query_heap, backward_heap, ) ======= ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); >>>>>>> first implementation of routing algo with pretty json-print +======= +// ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); + ForwardRoutingStep(query_heap, backward_heap, ) +>>>>>>> added testing } } From 79999119f912d80aef84f9cd08f5f0661a4cd7b5 Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 13 Apr 2016 10:47:57 +0200 Subject: [PATCH 15/43] changes --- features/bicycle/range.feature | 13 +- features/step_definitions/range.rb | 26 +- features/support/route.rb | 2 +- include/engine/plugins/isochrone.hpp | 310 ++++++++++++++++++ include/engine/plugins/range_analysis.hpp | 167 ---------- .../engine/routing_algorithms/one_to_many.hpp | 141 -------- src/engine/engine.cpp | 2 + 7 files changed, 333 insertions(+), 328 deletions(-) create mode 100644 include/engine/plugins/isochrone.hpp delete mode 100644 include/engine/plugins/range_analysis.hpp delete mode 100644 include/engine/routing_algorithms/one_to_many.hpp diff --git a/features/bicycle/range.feature b/features/bicycle/range.feature index 8b0b958c341..10fc8603f00 100644 --- a/features/bicycle/range.feature +++ b/features/bicycle/range.feature @@ -1,21 +1,18 @@ -@range +@bycicle Feature: Getting the range limited by Distance Background: - Given the profile "bicycle" + Given the profile "car" Scenario: Range Given the node map - | a | | b | - | | | | - | | | c | + | a | b | c | And the ways | nodes | | ab | - | ac | + | bc | When I request range I should get |source | node | pred | - | a | b | a | - | a | c | a | + | a | b | c | \ No newline at end of file diff --git a/features/step_definitions/range.rb b/features/step_definitions/range.rb index 9537158ee38..175341d036e 100644 --- a/features/step_definitions/range.rb +++ b/features/step_definitions/range.rb @@ -19,20 +19,21 @@ response = request_range source, @query_params if response.code == "200" && response.body.empty? == false json = JSON.parse response.body - # print(json) + print(json) + print("\n") if json['status'] == 200 - size = json['Nodes found'] + size = json['Nodes Found'] range = json['Range-Analysis'] end end - #print(size) + # print(size + "\n") # got = {'source' => row['source'], # 'node' => p1, # 'pred' => p2} # 'distance' => retDis} - # print(range) + print(range) ok = false for coordinate in range; p1 = Array.new @@ -48,22 +49,25 @@ 'node' => p1, 'pred' => p2} - print(p1) - print(p2) - print("fuu"); + # print("test\n") + # print(p1) + # print("\n"); + # print(node) + # print("\n"); + # print("\n"); - if FuzzyMatch.match_location(p1, node) && FuzzyMatch.match_location(p2, pred) || ok - print("asdasdasd") + + if FuzzyMatch.match_location(p1, node) && FuzzyMatch.match_location(p2, pred) key = 'node' got[key] = row[key] key = 'pred' got[key] = row[key] ok = true - else + elsif !FuzzyMatch.match_location(p1, node) && !FuzzyMatch.match_location(p2, pred) key = 'node' row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" key = 'pred' - row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" + row[key] = "#{row[key]} [#{pred.lat},#{pred.lon}]" end end diff --git a/features/support/route.rb b/features/support/route.rb index 83e549b219c..6e3d1b17ae4 100644 --- a/features/support/route.rb +++ b/features/support/route.rb @@ -71,7 +71,7 @@ def request_range node, user_params params = overwrite_params defaults, user_params params << ["loc", "#{node.lat},#{node.lon}"] - return request_path "range", params + return request_path "isochrone", params end def request_table waypoints, user_params diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp new file mode 100644 index 00000000000..63bb2e9562b --- /dev/null +++ b/include/engine/plugins/isochrone.hpp @@ -0,0 +1,310 @@ +// +// Created by robin on 4/6/16. +// + +#ifndef ISOCHRONE_HPP +#define ISOCHRONE_HPP + +#include "engine/plugins/plugin_base.hpp" +#include "engine/search_engine.hpp" +#include "engine/datafacade/internal_datafacade.hpp" + +#include "util/binary_heap.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/dynamic_graph.hpp" +#include "util/fingerprint.hpp" +#include "util/graph_loader.hpp" +#include "util/make_unique.hpp" +#include "util/routed_options.hpp" +#include "util/static_graph.hpp" +#include "util/string_util.hpp" +#include "util/simple_logger.hpp" +#include "util/typedefs.hpp" + +#include "osrm/json_container.hpp" +#include "osrm/coordinate.hpp" + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ +struct SimpleEdgeData +{ + SimpleEdgeData() : weight(INVALID_EDGE_WEIGHT), real(false) {} + SimpleEdgeData(unsigned weight_, bool real_) : weight(weight_), real(real_) {} + unsigned weight; + bool real; +}; + +using SimpleGraph = util::StaticGraph; +using SimpleEdge = SimpleGraph::InputEdge; +using Datafacade = + osrm::engine::datafacade::InternalDataFacade; + +template class Isochrone final : public BasePlugin +{ + + private: + std::string temp_string; + std::string descriptor_string; + std::unique_ptr> search_engine_ptr; + std::shared_ptr graph; +// std::unique_ptr datafacade; + DataFacadeT *facade; + std::string path; + std::vector coordinate_list; + std::map distance_map; + std::map predecessor_map; + std::vector graph_edge_list; + std::size_t number_of_nodes; + + + void deleteFileIfExists(const std::string &file_name) + { + if (boost::filesystem::exists(file_name)) + { + boost::filesystem::remove(file_name); + } + } + + std::size_t loadGraph(const std::string &path, + std::vector &coordinate_list, + std::vector &graph_edge_list) + { + + std::ifstream input_stream(path, std::ifstream::in | std::ifstream::binary); + if (!input_stream.is_open()) + { + throw util::exception("Cannot open osrm file"); + } + + // load graph data + std::vector edge_list; + std::vector traffic_light_node_list; + std::vector barrier_node_list; + + auto number_of_nodes = util::loadNodesFromFile(input_stream, barrier_node_list, + traffic_light_node_list, coordinate_list); + + util::loadEdgesFromFile(input_stream, edge_list); + + traffic_light_node_list.clear(); + traffic_light_node_list.shrink_to_fit(); + + // Building an node-based graph + for (const auto &input_edge : edge_list) + { + if (input_edge.source == input_edge.target) + { + continue; + } + + // forward edge + graph_edge_list.emplace_back(input_edge.source, input_edge.target, input_edge.weight, + input_edge.forward); + // backward edge + graph_edge_list.emplace_back(input_edge.target, input_edge.source, input_edge.weight, + input_edge.backward); + } + + return number_of_nodes; + } + + public: + explicit Isochrone(DataFacadeT *facade, const std::string path) : descriptor_string("isochrone"), facade(facade), path(path) + { + number_of_nodes = loadGraph(path, coordinate_list, graph_edge_list); + + tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); + graph = std::make_shared(number_of_nodes, graph_edge_list); + graph_edge_list.clear(); + graph_edge_list.shrink_to_fit(); +// { +// std::unordered_map server_paths; +// server_paths["base"] = base; +// osrm::util::populate_base_path(server_paths); +// datafacade = std::unique_ptr(new Datafacade(server_paths)); +// } + } + + virtual ~Isochrone() {} + + const std::string GetDescriptor() const override final { return descriptor_string; } + + Status HandleRequest(const RouteParameters &routeParameters, + util::json::Object &json_result) override final + { + RouteParameters a = routeParameters; + util::json::Object o = json_result; + + auto phantomnodes = facade->NearestPhantomNodes( + {static_cast(routeParameters.coordinates.front().lat), + static_cast(routeParameters.coordinates.front().lon)}, + 1); + + std::sort(phantomnodes.begin(), phantomnodes.end(), + [&](const osrm::engine::PhantomNodeWithDistance &a, + const osrm::engine::PhantomNodeWithDistance &b) + { + return a.distance > b.distance; + }); + auto phantom = phantomnodes[0]; + NodeID source = 0; + + double sourceLat = phantom.phantom_node.location.lat / osrm::COORDINATE_PRECISION; + double sourceLon = phantom.phantom_node.location.lon / osrm::COORDINATE_PRECISION; + + if (facade->EdgeIsCompressed(phantom.phantom_node.forward_node_id)) + { + std::vector forward_id_vector; + facade->GetUncompressedGeometry(phantom.phantom_node.forward_packed_geometry_id, + forward_id_vector); + source = forward_id_vector[phantom.phantom_node.fwd_segment_position]; + } + else + { + source = phantom.phantom_node.forward_packed_geometry_id; + } + + util::SimpleLogger().Write() << "Node-Coord" << sourceLat << " " << sourceLon; + + // Init complete + + struct HeapData + { + NodeID parent; + /* explicit */ HeapData(NodeID p) : parent(p) {} + }; + + using QueryHeap = osrm::util::BinaryHeap>; + + QueryHeap heap(number_of_nodes); + + util::SimpleLogger().Write() << "asdasdasd"; + heap.Insert(source, -phantom.phantom_node.GetForwardWeightPlusOffset(), source); + + // value is in metres + const int MAX = 1000; + + std::unordered_set edgepoints; + std::unordered_set insidepoints; + std::vector border; + + { + // Standard Dijkstra search, terminating when path length > MAX + while (!heap.Empty()) + { + const NodeID source = heap.DeleteMin(); + const std::int32_t distance = heap.GetKey(source); + + for (const auto current_edge : graph->GetAdjacentEdgeRange(source)) + { + const auto target = graph->GetTarget(current_edge); + if (target != SPECIAL_NODEID) + { + const auto data = graph->GetEdgeData(current_edge); + if (data.real) + { + int to_distance = distance + data.weight; + util::SimpleLogger().Write() << "I am Node " << target; + util::SimpleLogger().Write() << "Distance to Source: " << to_distance; + if (to_distance > MAX) + { + edgepoints.insert(target); + // distance_map[target] = + // to_distance; + // predecessor_map[target] = source; + } + else if (!heap.WasInserted(target)) + { + heap.Insert(target, to_distance, source); + insidepoints.insert(source); + distance_map[target] = to_distance; + predecessor_map[target] = source; + } + else if (to_distance < heap.GetKey(target)) + { + heap.GetData(target).parent = source; + heap.DecreaseKey(target, to_distance); + distance_map[target] = to_distance; + predecessor_map[target] = source; + } + } + } + } + } + } + util::SimpleLogger().Write(); + util::SimpleLogger().Write() << "Inside-Points"; + util::SimpleLogger().Write() << insidepoints.size(); + for (auto p : insidepoints) + { + double lon = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; + double lat = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; + auto pre = predecessor_map[p]; + auto d = distance_map[p]; + util::SimpleLogger().Write() << "Lat: " << lat; + util::SimpleLogger().Write() << "Lon: " << lon; + util::SimpleLogger().Write() << "Source: " << p << " Predecessor: " << pre + << " Distance " << d; + // util::SimpleLogger().Write(); + } + + util::SimpleLogger().Write() << "Edgepoints"; + util::SimpleLogger().Write() << edgepoints.size(); + // for (auto p : edgepoints) + // { + // double lon = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; + // double lat = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; + // auto pre = predecessor_map[p]; + // auto d = distance_map[p]; + //// util::SimpleLogger().Write() << "Lat: " << lat; + //// util::SimpleLogger().Write() << "Lon: " << lon; + // util::SimpleLogger().Write() << "Predecessor: " << pre << "Distance " << d; + // util::SimpleLogger().Write(); + // } + + util::json::Array data; + for (auto p : insidepoints) + { + util::json::Object object; + + util::json::Object source; + source.values["lat"] = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; + source.values["lon"] = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; + object.values["p1"] = std::move(source); + + util::json::Object predecessor; + auto pre = predecessor_map[p]; + predecessor.values["lat"] = coordinate_list[pre].lat / osrm::COORDINATE_PRECISION; + predecessor.values["lon"] = coordinate_list[pre].lon / osrm::COORDINATE_PRECISION; + object.values["p2"] = std::move(predecessor); + + util::json::Object distance; + object.values["distance_from_start"] = distance_map[p]; + + data.values.push_back(object); + } + + json_result.values["Range-Analysis"] = std::move(data); + return Status::Ok; + } +}; +} +} +} + +#endif // ISOCHRONE_HPP diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp deleted file mode 100644 index 664a1fbd46e..00000000000 --- a/include/engine/plugins/range_analysis.hpp +++ /dev/null @@ -1,167 +0,0 @@ -// -// Created by robin on 3/10/16. -// - -#ifndef OSRM_TEST_PLUGIN_HPP -#define OSRM_TEST_PLUGIN_HPP - -#include "engine/plugins/plugin_base.hpp" -#include "engine/search_engine.hpp" -#include "engine/object_encoder.hpp" - -#include "util/make_unique.hpp" -#include "util/string_util.hpp" -#include "util/simple_logger.hpp" -#include "osrm/json_container.hpp" - -#include - -#include -#include -#include -#include - -namespace osrm -{ -namespace engine -{ -namespace plugins -{ - -template class RangeAnalysis final : public BasePlugin -{ - - using PredecessorMap = std::unordered_map; - using DistanceMap = std::unordered_map; - - private: - std::string temp_string; - std::string descriptor_string; - std::unique_ptr> search_engine_ptr; - DataFacadeT *facade; - PredecessorMap predecessorMap; - DistanceMap distanceMap; - - public: - explicit RangeAnalysis(DataFacadeT *facade) : descriptor_string("range"), facade(facade) - { - search_engine_ptr = util::make_unique>(facade); - } - - virtual ~RangeAnalysis() {} - - const std::string GetDescriptor() const override final { return descriptor_string; } - - Status HandleRequest(const RouteParameters &routeParameters, - util::json::Object &json_result) override final - { - /* Check if valid - * - */ - if (routeParameters.coordinates.size() != 1) - { - json_result.values["status_message"] = "Number of Coordinates should be 1"; - return Status::Error; - } - if (!routeParameters.coordinates.front().IsValid()) - { - json_result.values["status_message"] = "Coordinate is invalid"; - return Status::Error; - } - - const auto &input_bearings = routeParameters.bearings; - if (input_bearings.size() > 0 && - routeParameters.coordinates.size() != input_bearings.size()) - { - json_result.values["status_message"] = - "Number of bearings does not match number of coordinate"; - return Status::Error; - } - - /** Interesting code starts here - * - */ - - // auto number_of_results = static_cast(routeParameters.num_results); - const int bearing = input_bearings.size() > 0 ? input_bearings.front().first : 0; - const int range = - input_bearings.size() > 0 - ? (input_bearings.front().second ? *input_bearings.front().second : 10) - : 180; - - std::vector phantomNodeVector; - auto phantomNodePair = facade->NearestPhantomNodeWithAlternativeFromBigComponent( - routeParameters.coordinates.front(), bearing, range); - - phantomNodeVector.push_back(phantomNodePair); - auto snapped_source_phantoms = snapPhantomNodes(phantomNodeVector); - search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000, predecessorMap, - distanceMap); -<<<<<<< HEAD - - BOOST_ASSERT(predecessorMap.size() == distanceMap.size()); - - std::string temp_string; - json_result.values["title"] = "Range Analysis"; - - json_result.values["start"] = routeParameters.coordinates[0].lat; -<<<<<<< HEAD -======= - - BOOST_ASSERT(predecessorMap.size() == distanceMap.size()); - - std::string temp_string; - json_result.values["title"] = "Range Analysis"; ->>>>>>> first implementation of routing algo with pretty json-print -======= ->>>>>>> added testing - - util::json::Array data; - for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) - { - util::json::Object object; - - util::json::Object source; - FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); - source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; - source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; -<<<<<<< HEAD -<<<<<<< HEAD - object.values["p1"] = std::move(source); -======= - object.values["Source"] = std::move(source); ->>>>>>> first implementation of routing algo with pretty json-print -======= - object.values["p1"] = std::move(source); ->>>>>>> added testing - - util::json::Object predecessor; - FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); - predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; - predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; -<<<<<<< HEAD -<<<<<<< HEAD - object.values["p2"] = std::move(predecessor); -======= - object.values["Predecessor"] = std::move(predecessor); ->>>>>>> first implementation of routing algo with pretty json-print -======= - object.values["p2"] = std::move(predecessor); ->>>>>>> added testing - - util::json::Object distance; - object.values["distance_from_start"] = distanceMap[it->first]; - - data.values.push_back(object); - } - temp_string = std::to_string(distanceMap.size()); - json_result.values["Nodes Found"] = temp_string;; - json_result.values["Range-Analysis"] = std::move(data); - - return Status::Ok; - } -}; -} -} -} -#endif // OSRM_TEST_PLUGIN_HPP diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp deleted file mode 100644 index 9427d6799b3..00000000000 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ /dev/null @@ -1,141 +0,0 @@ -// -// Created by robin on 3/16/16. -// - -#ifndef OSRM_ONE_TO_MANY_HPP -#define OSRM_ONE_TO_MANY_HPP - -#include "engine/routing_algorithms/routing_base.hpp" -#include "engine/search_engine_data.hpp" -#include "util/typedefs.hpp" -#include "util/simple_logger.hpp" - -#include - -#include -#include -#include -#include - -namespace osrm -{ -namespace engine -{ -namespace routing_algorithms -{ - -template -class OneToManyRouting final - : public BasicRoutingInterface> -{ - using super = BasicRoutingInterface>; - using QueryHeap = SearchEngineData::QueryHeap; - using PredecessorMap = std::unordered_map; - using DistanceMap = std::unordered_map; - SearchEngineData &engine_working_data; - - public: - OneToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) - : super(facade), engine_working_data(engine_working_data) - { - } - - ~OneToManyRouting() {} - - void operator()(PhantomNode &phantomSource, - const int distance, - PredecessorMap &predecessorMap, - DistanceMap &distanceMap) const - { - - engine_working_data.InitializeOrClearFirstThreadLocalStorage( - super::facade->GetNumberOfNodes()); - - QueryHeap &query_heap = *(engine_working_data.forward_heap_1); - QueryHeap &backward_heap = *(engine_working_data.reverse_heap_1); - - query_heap.Clear(); - // insert target(s) at distance 0 - if (SPECIAL_NODEID != phantomSource.forward_node_id) - { - query_heap.Insert(phantomSource.forward_node_id, - -phantomSource.GetForwardWeightPlusOffset(), - phantomSource.forward_node_id); - } - if (SPECIAL_NODEID != phantomSource.reverse_node_id) - { - query_heap.Insert(phantomSource.reverse_node_id, - -phantomSource.GetReverseWeightPlusOffset(), - phantomSource.reverse_node_id); - } - - util::SimpleLogger().Write() << phantomSource; - // explore search space - while (!query_heap.Empty()) - { -<<<<<<< HEAD -<<<<<<< HEAD -// ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); - ForwardRoutingStep(query_heap, backward_heap, ) -======= - ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); ->>>>>>> first implementation of routing algo with pretty json-print -======= -// ForwardRoutingStep(query_heap, distance, predecessorMap, distanceMap); - ForwardRoutingStep(query_heap, backward_heap, ) ->>>>>>> added testing - } - } - - void ForwardRoutingStep(QueryHeap &query_heap, - const int distance, - PredecessorMap &predecessorMap, - DistanceMap &distanceMap) const - { - const NodeID node = query_heap.DeleteMin(); - const int source_distance = query_heap.GetKey(node); - for (auto edge : super::facade->GetAdjacentEdgeRange(node)) - { - const auto &data = super::facade->GetEdgeData(edge); - const NodeID to = super::facade->GetTarget(edge); - const int edge_weight = data.distance; - BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); - - if (query_heap.WasInserted(to)) - { - if (query_heap.GetKey(to) + edge_weight < source_distance) - { - continue; - } - } - const int to_distance = source_distance + edge_weight; - - if (to_distance > distance) - { - continue; - } - - // New Node discovered -> Add to Heap + Node Info Storage - if (!query_heap.WasInserted(to)) - { - query_heap.Insert(to, to_distance, node); - predecessorMap[to] = node; - distanceMap[to] = to_distance; - } - // Found a shorter Path -> Update distance - else if (to_distance < query_heap.GetKey(to)) - { - // new parent - query_heap.GetData(to).parent = node; - query_heap.DecreaseKey(to, to_distance); - - predecessorMap[to] = node; - distanceMap[to] = to_distance; - } - } - } -}; -} -} -} -#endif // OSRM_ONE_TO_MANY_HPP diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 00d56ab17d1..169af892588 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -5,6 +5,7 @@ #include "engine/plugins/table.hpp" #include "engine/plugins/nearest.hpp" +#include "engine/plugins/timestamp.hpp" #include "engine/plugins/trip.hpp" #include "engine/plugins/viaroute.hpp" #include "engine/plugins/match.hpp" @@ -16,6 +17,7 @@ #include "storage/shared_barriers.hpp" #include "util/make_unique.hpp" +#include "util/routed_options.hpp" #include "util/simple_logger.hpp" #include From 25164a83d205bad3fd9dd65566a67fc0b9541406 Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 14 Apr 2016 11:22:12 +0200 Subject: [PATCH 16/43] helloworld-isochrone with new API --- include/engine/api/isochrone_api.hpp | 31 ++ include/engine/api/isochrone_parameters.hpp | 28 ++ include/engine/engine.hpp | 4 + include/engine/plugins/isochrone.hpp | 289 +----------------- include/osrm/isochrone_parameters.hpp | 15 + include/osrm/osrm.hpp | 39 ++- include/osrm/osrm_fwd.hpp | 1 + .../api/isochrone_parameter_grammar.hpp | 47 +++ include/server/service/isochrone_service.hpp | 36 +++ src/engine/engine.cpp | 15 +- src/engine/plugins/isochrone.cpp | 25 ++ src/osrm/osrm.cpp | 5 + src/server/api/parameters_parser.cpp | 8 + src/server/service/isochrone_service.cpp | 76 +++++ src/server/service_handler.cpp | 2 + 15 files changed, 319 insertions(+), 302 deletions(-) create mode 100644 include/engine/api/isochrone_api.hpp create mode 100644 include/engine/api/isochrone_parameters.hpp create mode 100644 include/osrm/isochrone_parameters.hpp create mode 100644 include/server/api/isochrone_parameter_grammar.hpp create mode 100644 include/server/service/isochrone_service.hpp create mode 100644 src/engine/plugins/isochrone.cpp create mode 100644 src/server/service/isochrone_service.cpp diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp new file mode 100644 index 00000000000..ae5f7f5722f --- /dev/null +++ b/include/engine/api/isochrone_api.hpp @@ -0,0 +1,31 @@ +// +// Created by robin on 4/13/16. +// + +#ifndef ENGINE_API_ISOCHRONE_HPP +#define ENGINE_API_ISOCHRONE_HPP + +#include "engine/api/base_api.hpp" +#include "engine/api/isochrone_parameters.hpp" + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +class IsochroneAPI final : public BaseAPI +{ + public: + const IsochroneParameters ¶meters; + + IsochroneAPI(const datafacade::BaseDataFacade &facade_, const IsochroneParameters ¶meters_) + : BaseAPI(facade_, parameters_), parameters(parameters_) + { + } +}; +} +} +} +#endif // ENGINE_API_ISOCHRONE_HPP diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp new file mode 100644 index 00000000000..d05361b024c --- /dev/null +++ b/include/engine/api/isochrone_parameters.hpp @@ -0,0 +1,28 @@ +// +// Created by robin on 4/13/16. +// + +#ifndef ENGINE_API_ISOCHRONE_PARAMETERS_HPP +#define ENGINE_API_ISOCHRONE_PARAMETERS_HPP + +#include "engine/api/base_parameters.hpp" + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +struct IsochroneParameters : public BaseParameters +{ + unsigned number_of_results = 1; + + bool IsValid() const { + return BaseParameters::IsValid() && number_of_results >= 1; + } +}; +} +} +} +#endif // ENGINE_API_ISOCHRONE_PARAMETERS_HPP diff --git a/include/engine/engine.hpp b/include/engine/engine.hpp index 027320ba339..447b4f4adf7 100644 --- a/include/engine/engine.hpp +++ b/include/engine/engine.hpp @@ -32,6 +32,7 @@ struct NearestParameters; struct TripParameters; struct MatchParameters; struct TileParameters; +struct IsochroneParameters; } namespace plugins { @@ -41,6 +42,7 @@ class NearestPlugin; class TripPlugin; class MatchPlugin; class TilePlugin; +class IsochronePlugin; } // End fwd decls @@ -69,6 +71,7 @@ class Engine final Status Trip(const api::TripParameters ¶meters, util::json::Object &result); Status Match(const api::MatchParameters ¶meters, util::json::Object &result); Status Tile(const api::TileParameters ¶meters, std::string &result); + Status Isochrone(const api::IsochroneParameters ¶meters, util::json::Object &result); private: std::unique_ptr lock; @@ -79,6 +82,7 @@ class Engine final std::unique_ptr trip_plugin; std::unique_ptr match_plugin; std::unique_ptr tile_plugin; + std::unique_ptr isochrone_plugin; std::unique_ptr query_data_facade; }; diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index 63bb2e9562b..25bbc9580b4 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -6,33 +6,8 @@ #define ISOCHRONE_HPP #include "engine/plugins/plugin_base.hpp" -#include "engine/search_engine.hpp" -#include "engine/datafacade/internal_datafacade.hpp" - -#include "util/binary_heap.hpp" -#include "util/coordinate_calculation.hpp" -#include "util/dynamic_graph.hpp" -#include "util/fingerprint.hpp" -#include "util/graph_loader.hpp" -#include "util/make_unique.hpp" -#include "util/routed_options.hpp" -#include "util/static_graph.hpp" -#include "util/string_util.hpp" -#include "util/simple_logger.hpp" -#include "util/typedefs.hpp" - +#include "engine/api/isochrone_parameters.hpp" #include "osrm/json_container.hpp" -#include "osrm/coordinate.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include namespace osrm { @@ -40,268 +15,14 @@ namespace engine { namespace plugins { -struct SimpleEdgeData -{ - SimpleEdgeData() : weight(INVALID_EDGE_WEIGHT), real(false) {} - SimpleEdgeData(unsigned weight_, bool real_) : weight(weight_), real(real_) {} - unsigned weight; - bool real; -}; -using SimpleGraph = util::StaticGraph; -using SimpleEdge = SimpleGraph::InputEdge; -using Datafacade = - osrm::engine::datafacade::InternalDataFacade; - -template class Isochrone final : public BasePlugin +class IsochronePlugin final : public BasePlugin { - - private: - std::string temp_string; - std::string descriptor_string; - std::unique_ptr> search_engine_ptr; - std::shared_ptr graph; -// std::unique_ptr datafacade; - DataFacadeT *facade; - std::string path; - std::vector coordinate_list; - std::map distance_map; - std::map predecessor_map; - std::vector graph_edge_list; - std::size_t number_of_nodes; - - - void deleteFileIfExists(const std::string &file_name) - { - if (boost::filesystem::exists(file_name)) - { - boost::filesystem::remove(file_name); - } - } - - std::size_t loadGraph(const std::string &path, - std::vector &coordinate_list, - std::vector &graph_edge_list) - { - - std::ifstream input_stream(path, std::ifstream::in | std::ifstream::binary); - if (!input_stream.is_open()) - { - throw util::exception("Cannot open osrm file"); - } - - // load graph data - std::vector edge_list; - std::vector traffic_light_node_list; - std::vector barrier_node_list; - - auto number_of_nodes = util::loadNodesFromFile(input_stream, barrier_node_list, - traffic_light_node_list, coordinate_list); - - util::loadEdgesFromFile(input_stream, edge_list); - - traffic_light_node_list.clear(); - traffic_light_node_list.shrink_to_fit(); - - // Building an node-based graph - for (const auto &input_edge : edge_list) - { - if (input_edge.source == input_edge.target) - { - continue; - } - - // forward edge - graph_edge_list.emplace_back(input_edge.source, input_edge.target, input_edge.weight, - input_edge.forward); - // backward edge - graph_edge_list.emplace_back(input_edge.target, input_edge.source, input_edge.weight, - input_edge.backward); - } - - return number_of_nodes; - } - public: - explicit Isochrone(DataFacadeT *facade, const std::string path) : descriptor_string("isochrone"), facade(facade), path(path) - { - number_of_nodes = loadGraph(path, coordinate_list, graph_edge_list); - - tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); - graph = std::make_shared(number_of_nodes, graph_edge_list); - graph_edge_list.clear(); - graph_edge_list.shrink_to_fit(); -// { -// std::unordered_map server_paths; -// server_paths["base"] = base; -// osrm::util::populate_base_path(server_paths); -// datafacade = std::unique_ptr(new Datafacade(server_paths)); -// } - } - - virtual ~Isochrone() {} - - const std::string GetDescriptor() const override final { return descriptor_string; } - - Status HandleRequest(const RouteParameters &routeParameters, - util::json::Object &json_result) override final - { - RouteParameters a = routeParameters; - util::json::Object o = json_result; - - auto phantomnodes = facade->NearestPhantomNodes( - {static_cast(routeParameters.coordinates.front().lat), - static_cast(routeParameters.coordinates.front().lon)}, - 1); - - std::sort(phantomnodes.begin(), phantomnodes.end(), - [&](const osrm::engine::PhantomNodeWithDistance &a, - const osrm::engine::PhantomNodeWithDistance &b) - { - return a.distance > b.distance; - }); - auto phantom = phantomnodes[0]; - NodeID source = 0; - - double sourceLat = phantom.phantom_node.location.lat / osrm::COORDINATE_PRECISION; - double sourceLon = phantom.phantom_node.location.lon / osrm::COORDINATE_PRECISION; - - if (facade->EdgeIsCompressed(phantom.phantom_node.forward_node_id)) - { - std::vector forward_id_vector; - facade->GetUncompressedGeometry(phantom.phantom_node.forward_packed_geometry_id, - forward_id_vector); - source = forward_id_vector[phantom.phantom_node.fwd_segment_position]; - } - else - { - source = phantom.phantom_node.forward_packed_geometry_id; - } - - util::SimpleLogger().Write() << "Node-Coord" << sourceLat << " " << sourceLon; - - // Init complete - - struct HeapData - { - NodeID parent; - /* explicit */ HeapData(NodeID p) : parent(p) {} - }; - - using QueryHeap = osrm::util::BinaryHeap>; - - QueryHeap heap(number_of_nodes); - - util::SimpleLogger().Write() << "asdasdasd"; - heap.Insert(source, -phantom.phantom_node.GetForwardWeightPlusOffset(), source); - - // value is in metres - const int MAX = 1000; - - std::unordered_set edgepoints; - std::unordered_set insidepoints; - std::vector border; - - { - // Standard Dijkstra search, terminating when path length > MAX - while (!heap.Empty()) - { - const NodeID source = heap.DeleteMin(); - const std::int32_t distance = heap.GetKey(source); - - for (const auto current_edge : graph->GetAdjacentEdgeRange(source)) - { - const auto target = graph->GetTarget(current_edge); - if (target != SPECIAL_NODEID) - { - const auto data = graph->GetEdgeData(current_edge); - if (data.real) - { - int to_distance = distance + data.weight; - util::SimpleLogger().Write() << "I am Node " << target; - util::SimpleLogger().Write() << "Distance to Source: " << to_distance; - if (to_distance > MAX) - { - edgepoints.insert(target); - // distance_map[target] = - // to_distance; - // predecessor_map[target] = source; - } - else if (!heap.WasInserted(target)) - { - heap.Insert(target, to_distance, source); - insidepoints.insert(source); - distance_map[target] = to_distance; - predecessor_map[target] = source; - } - else if (to_distance < heap.GetKey(target)) - { - heap.GetData(target).parent = source; - heap.DecreaseKey(target, to_distance); - distance_map[target] = to_distance; - predecessor_map[target] = source; - } - } - } - } - } - } - util::SimpleLogger().Write(); - util::SimpleLogger().Write() << "Inside-Points"; - util::SimpleLogger().Write() << insidepoints.size(); - for (auto p : insidepoints) - { - double lon = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; - double lat = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; - auto pre = predecessor_map[p]; - auto d = distance_map[p]; - util::SimpleLogger().Write() << "Lat: " << lat; - util::SimpleLogger().Write() << "Lon: " << lon; - util::SimpleLogger().Write() << "Source: " << p << " Predecessor: " << pre - << " Distance " << d; - // util::SimpleLogger().Write(); - } - - util::SimpleLogger().Write() << "Edgepoints"; - util::SimpleLogger().Write() << edgepoints.size(); - // for (auto p : edgepoints) - // { - // double lon = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; - // double lat = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; - // auto pre = predecessor_map[p]; - // auto d = distance_map[p]; - //// util::SimpleLogger().Write() << "Lat: " << lat; - //// util::SimpleLogger().Write() << "Lon: " << lon; - // util::SimpleLogger().Write() << "Predecessor: " << pre << "Distance " << d; - // util::SimpleLogger().Write(); - // } - - util::json::Array data; - for (auto p : insidepoints) - { - util::json::Object object; - - util::json::Object source; - source.values["lat"] = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; - source.values["lon"] = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; - object.values["p1"] = std::move(source); - - util::json::Object predecessor; - auto pre = predecessor_map[p]; - predecessor.values["lat"] = coordinate_list[pre].lat / osrm::COORDINATE_PRECISION; - predecessor.values["lon"] = coordinate_list[pre].lon / osrm::COORDINATE_PRECISION; - object.values["p2"] = std::move(predecessor); - - util::json::Object distance; - object.values["distance_from_start"] = distance_map[p]; - - data.values.push_back(object); - } + explicit IsochronePlugin(datafacade::BaseDataFacade &facade); - json_result.values["Range-Analysis"] = std::move(data); - return Status::Ok; - } + Status HandleRequest(const api::IsochroneParameters ¶ms, + util::json::Object &json_result); }; } } diff --git a/include/osrm/isochrone_parameters.hpp b/include/osrm/isochrone_parameters.hpp new file mode 100644 index 00000000000..03e141210af --- /dev/null +++ b/include/osrm/isochrone_parameters.hpp @@ -0,0 +1,15 @@ +// +// Created by robin on 4/14/16. +// + +#ifndef GLOBAL_ISOCHRONE_PARAMETERS_HPP +#define GLOBAL_ISOCHRONE_PARAMETERS_HPP + +#include "engine/api/isochrone_parameters.hpp" + +namespace osrm +{ +using engine::api::IsochroneParameters; +} + +#endif // GLOBAL_ISOCHRONE_PARAMETERS_HPP diff --git a/include/osrm/osrm.hpp b/include/osrm/osrm.hpp index 1de8610a34f..20606765069 100644 --- a/include/osrm/osrm.hpp +++ b/include/osrm/osrm.hpp @@ -44,22 +44,23 @@ using engine::api::NearestParameters; using engine::api::TripParameters; using engine::api::MatchParameters; using engine::api::TileParameters; +using engine::api::IsochroneParameters; -/** - * Represents a Open Source Routing Machine with access to its services. - * - * This represents an Open Source Routing Machine (OSRM) instance, with the services: - * - * - Route: shortest path queries for coordinates - * - Table: distance tables for coordinates - * - Nearest: nearest street segment for coordinate - * - Trip: shortest round trip between coordinates - * - Match: snaps noisy coordinate traces to the road network - * - Tile: vector tiles with internal graph representation - * - * All services take service-specific parameters, fill a JSON object, and return a status code. - */ -class OSRM final + /** + * Represents a Open Source Routing Machine with access to its services. + * + * This represents an Open Source Routing Machine (OSRM) instance, with the services: + * + * - Route: shortest path queries for coordinates + * - Table: distance tables for coordinates + * - Nearest: nearest street segment for coordinate + * - Trip: shortest round trip between coordinates + * - Match: snaps noisy coordinate traces to the road network + * - Tile: vector tiles with internal graph representation + * + * All services take service-specific parameters, fill a JSON object, and return a status code. + */ + class OSRM final { public: /** @@ -130,6 +131,14 @@ class OSRM final */ Status Tile(const TileParameters ¶meters, std::string &result); + /** + * Isocrhone for given distance + * \param parameters nearest query specific parameters + * \return Status indicating success for the query or failure + * \see Status, NearestParameters and json::Object + */ + Status Isochrone(const IsochroneParameters ¶meters, json::Object &result); + private: std::unique_ptr engine_; }; diff --git a/include/osrm/osrm_fwd.hpp b/include/osrm/osrm_fwd.hpp index f0f1f17af62..9f436b0b755 100644 --- a/include/osrm/osrm_fwd.hpp +++ b/include/osrm/osrm_fwd.hpp @@ -52,6 +52,7 @@ struct NearestParameters; struct TripParameters; struct MatchParameters; struct TileParameters; +struct IsochroneParameters; } // ns api class Engine; diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp new file mode 100644 index 00000000000..d8c0693de2b --- /dev/null +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -0,0 +1,47 @@ +// +// Created by robin on 4/13/16. +// + +#ifndef OSRM_ISOCHRONE_PARAMETER_GRAMMAR_HPP +#define OSRM_ISOCHRONE_PARAMETER_GRAMMAR_HPP + +#include "engine/api/isochrone_parameters.hpp" +#include "server/api/base_parameters_grammar.hpp" + +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; + +struct IsochroneParametersGrammar final : public BaseParametersGrammar +{ + using Iterator = std::string::iterator; + + IsochroneParametersGrammar() : BaseParametersGrammar(root_rule, parameters) + { + const auto set_number = [this](const unsigned number) + { + parameters.number_of_results = number; + }; + isochrone_rule = (qi::lit("number=") > qi::uint_)[set_number]; + root_rule = + query_rule > -qi::lit(".json") > -(qi::lit("?") > (isochrone_rule | base_rule) % '&'); + } + + engine::api::IsochroneParameters parameters; + + private: + qi::rule root_rule; + qi::rule isochrone_rule; +}; +} +} +} + +#endif // OSRM_ISOCHRONE_PARAMETER_GRAMMAR_HPP diff --git a/include/server/service/isochrone_service.hpp b/include/server/service/isochrone_service.hpp new file mode 100644 index 00000000000..77de052943f --- /dev/null +++ b/include/server/service/isochrone_service.hpp @@ -0,0 +1,36 @@ +// +// Created by robin on 4/14/16. +// + +#ifndef SERVER_SERVICE_ISOCHRONE_SERVICE_HPP +#define SERVER_SERVICE_ISOCHRONE_SERVICE_HPP + +#include "server/service/base_service.hpp" + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class IsochroneService final : public BaseService +{ + public: + IsochroneService(OSRM &routing_machine) : BaseService(routing_machine) {} + + engine::Status RunQuery(std::string &query, ResultT &result) final override; + + unsigned GetVersion() final override { return 1; } +}; +} +} +} +#endif // SERVER_SERVICE_ISOCHRONE_SERVICE_HPP diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 169af892588..984d45504e1 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -5,11 +5,11 @@ #include "engine/plugins/table.hpp" #include "engine/plugins/nearest.hpp" -#include "engine/plugins/timestamp.hpp" #include "engine/plugins/trip.hpp" #include "engine/plugins/viaroute.hpp" #include "engine/plugins/match.hpp" #include "engine/plugins/tile.hpp" +#include "engine/plugins/isochrone.hpp" #include "engine/datafacade/datafacade_base.hpp" #include "engine/datafacade/internal_datafacade.hpp" @@ -17,7 +17,6 @@ #include "storage/shared_barriers.hpp" #include "util/make_unique.hpp" -#include "util/routed_options.hpp" #include "util/simple_logger.hpp" #include @@ -135,7 +134,12 @@ Engine::Engine(EngineConfig &config) } else { - return_code = plugin_iterator->second->HandleRequest(route_parameters, json_result); + if (!config.storage_config.IsValid()) + { + throw util::exception("Invalid file paths given!"); + } + query_data_facade = + util::make_unique(config.storage_config); } // Register plugins @@ -147,6 +151,7 @@ Engine::Engine(EngineConfig &config) trip_plugin = create(*query_data_facade, config.max_locations_trip); match_plugin = create(*query_data_facade, config.max_locations_map_matching); tile_plugin = create(*query_data_facade); + isochrone_plugin = create(*query_data_facade); } // make sure we deallocate the unique ptr at a position where we know the size of the plugins @@ -183,6 +188,10 @@ Status Engine::Tile(const api::TileParameters ¶ms, std::string &result) { return RunQuery(lock, *query_data_facade, params, *tile_plugin, result); } +Status Engine::Isochrone(const api::IsochroneParameters ¶ms, util::json::Object &result) { + return RunQuery(lock, *query_data_facade, params, *isochrone_plugin, result); +} + } // engine ns } // osrm ns diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp new file mode 100644 index 00000000000..22b57594d6d --- /dev/null +++ b/src/engine/plugins/isochrone.cpp @@ -0,0 +1,25 @@ +// +// Created by robin on 4/13/16. +// + +#include "engine/plugins/isochrone.hpp" +#include "engine/api/isochrone_parameters.hpp" +#include "engine/api/isochrone_api.hpp" + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ +IsochronePlugin::IsochronePlugin(datafacade::BaseDataFacade &facade) : BasePlugin{facade} {} + +Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, + util::json::Object &json_result) +{ + + return Status::Ok; +} +} +} +} diff --git a/src/osrm/osrm.cpp b/src/osrm/osrm.cpp index 9a69e2e1451..9a4b2575c1a 100644 --- a/src/osrm/osrm.cpp +++ b/src/osrm/osrm.cpp @@ -4,6 +4,7 @@ #include "engine/api/nearest_parameters.hpp" #include "engine/api/trip_parameters.hpp" #include "engine/api/match_parameters.hpp" +#include "engine/api/isochrone_parameters.hpp" #include "engine/engine.hpp" #include "engine/status.hpp" #include "engine/engine_config.hpp" @@ -50,5 +51,9 @@ engine::Status OSRM::Tile(const engine::api::TileParameters ¶ms, std::string { return engine_->Tile(params, result); } +engine::Status OSRM::Isochrone(const engine::api::IsochroneParameters ¶ms, json::Object &result) +{ + return engine_->Isochrone(params, result); +} } // ns osrm diff --git a/src/server/api/parameters_parser.cpp b/src/server/api/parameters_parser.cpp index 1d4485a0379..faf984b836a 100644 --- a/src/server/api/parameters_parser.cpp +++ b/src/server/api/parameters_parser.cpp @@ -6,6 +6,7 @@ #include "server/api/table_parameter_grammar.hpp" #include "server/api/tile_parameter_grammar.hpp" #include "server/api/trip_parameter_grammar.hpp" +#include "server/api/isochrone_parameter_grammar.hpp" #include @@ -95,6 +96,13 @@ boost::optional parseParameters(std::string::iterat { return detail::parseParameters(iter, end); } +template <> +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) +{ + return detail::parseParameters( + iter, end); +} } // ns api } // ns server diff --git a/src/server/service/isochrone_service.cpp b/src/server/service/isochrone_service.cpp new file mode 100644 index 00000000000..7be0a6cdf2a --- /dev/null +++ b/src/server/service/isochrone_service.cpp @@ -0,0 +1,76 @@ +// +// Created by robin on 4/14/16. +// + +#include "server/service/isochrone_service.hpp" +#include "server/service/utils.hpp" + +#include "engine/api/isochrone_parameters.hpp" +#include "server/api/parameters_parser.hpp" + +#include "util/json_container.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +namespace +{ +std::string getWrongOptionHelp(const engine::api::IsochroneParameters ¶meters) +{ + std::string help; + + const auto coord_size = parameters.coordinates.size(); + + const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints", + parameters.hints, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings", + parameters.bearings, coord_size, help) || + constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses", + parameters.radiuses, coord_size, help); + + if (!param_size_mismatch && parameters.coordinates.size() < 2) + { + help = "Number of coordinates needs to be at least two."; + } + + return help; +} +} // anon. ns + +engine::Status IsochroneService::RunQuery(std::string &query, ResultT &result) +{ + result = util::json::Object(); + auto &json_result = result.get(); + + auto query_iterator = query.begin(); + auto parameters = api::parseParameters(query_iterator, query.end()); + if (!parameters || query_iterator != query.end()) + { + const auto position = std::distance(query.begin(), query_iterator); + json_result.values["code"] = "InvalidQuery"; + json_result.values["message"] = + "Query string malformed close to position " + std::to_string(position); + return engine::Status::Error; + } + BOOST_ASSERT(parameters); + + if (!parameters->IsValid()) + { + json_result.values["code"] = "InvalidOptions"; + json_result.values["message"] = getWrongOptionHelp(*parameters); + return engine::Status::Error; + } + BOOST_ASSERT(parameters->IsValid()); + + return BaseService::routing_machine.Isochrone(*parameters, json_result); +} +} +} +} diff --git a/src/server/service_handler.cpp b/src/server/service_handler.cpp index 98ba6a61703..58740d22922 100644 --- a/src/server/service_handler.cpp +++ b/src/server/service_handler.cpp @@ -6,6 +6,7 @@ #include "server/service/trip_service.hpp" #include "server/service/match_service.hpp" #include "server/service/tile_service.hpp" +#include "server/service/isochrone_service.hpp" #include "server/api/parsed_url.hpp" #include "util/json_util.hpp" @@ -23,6 +24,7 @@ ServiceHandler::ServiceHandler(osrm::EngineConfig &config) : routing_machine(con service_map["trip"] = util::make_unique(routing_machine); service_map["match"] = util::make_unique(routing_machine); service_map["tile"] = util::make_unique(routing_machine); + service_map["isochrone"] = util::make_unique(routing_machine); } engine::Status ServiceHandler::RunQuery(api::ParsedURL parsed_url, From 9a7d0983b50b4c4a9e10312f36ae133b563438bf Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 20 Apr 2016 09:22:56 +0200 Subject: [PATCH 17/43] isochrone impl --- include/engine/api/isochrone_api.hpp | 25 ++ include/engine/api/isochrone_parameters.hpp | 1 + include/engine/plugins/isochrone.hpp | 36 ++- include/storage/storage_config.hpp | 1 + src/engine/engine.cpp | 2 +- src/engine/plugins/isochrone.cpp | 249 +++++++++++++++++++- src/storage/storage_config.cpp | 15 +- 7 files changed, 316 insertions(+), 13 deletions(-) diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp index ae5f7f5722f..7e171fb6712 100644 --- a/include/engine/api/isochrone_api.hpp +++ b/include/engine/api/isochrone_api.hpp @@ -7,6 +7,7 @@ #include "engine/api/base_api.hpp" #include "engine/api/isochrone_parameters.hpp" +#include "engine/plugins/plugin_base.hpp" namespace osrm { @@ -17,6 +18,7 @@ namespace api class IsochroneAPI final : public BaseAPI { + public: const IsochroneParameters ¶meters; @@ -24,6 +26,29 @@ class IsochroneAPI final : public BaseAPI : BaseAPI(facade_, parameters_), parameters(parameters_) { } + + void MakeResponse(const std::vector> &phantom_nodes, + util::json::Object &response) const + { + BOOST_ASSERT(phantom_nodes.size() == 1); + BOOST_ASSERT(parameters.coordinates.size() == 1); + + util::json::Array waypoints; + waypoints.values.resize(phantom_nodes.front().size()); + std::transform(phantom_nodes.front().begin(), phantom_nodes.front().end(), + waypoints.values.begin(), + [this](const PhantomNodeWithDistance &phantom_with_distance) + { + auto waypoint = MakeWaypoint(phantom_with_distance.phantom_node); + waypoint.values["distance"] = phantom_with_distance.distance; + return waypoint; + }); + + response.values["code"] = "Ok"; + response.values["waypoints"] = std::move(waypoints); + } + + }; } } diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp index d05361b024c..46e0b546446 100644 --- a/include/engine/api/isochrone_parameters.hpp +++ b/include/engine/api/isochrone_parameters.hpp @@ -17,6 +17,7 @@ namespace api struct IsochroneParameters : public BaseParameters { unsigned number_of_results = 1; + unsigned distance = 1000; bool IsValid() const { return BaseParameters::IsValid() && number_of_results >= 1; diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index 25bbc9580b4..cae057b952e 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -7,22 +7,52 @@ #include "engine/plugins/plugin_base.hpp" #include "engine/api/isochrone_parameters.hpp" +#include "util/static_graph.hpp" #include "osrm/json_container.hpp" +#include +#include +#include + +#include +#include + namespace osrm { namespace engine { namespace plugins { +struct SimpleEdgeData +{ + SimpleEdgeData() : weight(INVALID_EDGE_WEIGHT), real(false) {} + SimpleEdgeData(unsigned weight_, bool real_) : weight(weight_), real(real_) {} + unsigned weight; + bool real; +}; + +using SimpleGraph = util::StaticGraph; +using SimpleEdge = SimpleGraph::InputEdge; class IsochronePlugin final : public BasePlugin { + private: + boost::filesystem::path base; + std::shared_ptr graph; + std::vector coordinate_list; + std::map distance_map; + std::map predecessor_map; + std::vector graph_edge_list; + std::size_t number_of_nodes; + + std::size_t loadGraph(const std::string &path, + std::vector &coordinate_list, + std::vector &graph_edge_list); + public: - explicit IsochronePlugin(datafacade::BaseDataFacade &facade); + explicit IsochronePlugin(datafacade::BaseDataFacade &facade, const std::string base); - Status HandleRequest(const api::IsochroneParameters ¶ms, - util::json::Object &json_result); + Status HandleRequest(const api::IsochroneParameters ¶ms, util::json::Object &json_result); }; } } diff --git a/include/storage/storage_config.hpp b/include/storage/storage_config.hpp index 62f2fddd5d9..7afa377d1aa 100644 --- a/include/storage/storage_config.hpp +++ b/include/storage/storage_config.hpp @@ -52,6 +52,7 @@ struct StorageConfig final StorageConfig(const boost::filesystem::path &base); bool IsValid() const; + boost::filesystem::path base; boost::filesystem::path ram_index_path; boost::filesystem::path file_index_path; boost::filesystem::path hsgr_data_path; diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 984d45504e1..95a60ada631 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -151,7 +151,7 @@ Engine::Engine(EngineConfig &config) trip_plugin = create(*query_data_facade, config.max_locations_trip); match_plugin = create(*query_data_facade, config.max_locations_map_matching); tile_plugin = create(*query_data_facade); - isochrone_plugin = create(*query_data_facade); + isochrone_plugin = create(*query_data_facade, config.storage_config.base.string()); } // make sure we deallocate the unique ptr at a position where we know the size of the plugins diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index 22b57594d6d..0a6b5fb7a30 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -2,9 +2,17 @@ // Created by robin on 4/13/16. // + #include "engine/plugins/isochrone.hpp" -#include "engine/api/isochrone_parameters.hpp" #include "engine/api/isochrone_api.hpp" +#include "util/graph_loader.hpp" +#include "engine/phantom_node.hpp" +#include "util/coordinate.hpp" +#include "util/simple_logger.hpp" + +#include +#include +#include namespace osrm { @@ -12,14 +20,251 @@ namespace engine { namespace plugins { -IsochronePlugin::IsochronePlugin(datafacade::BaseDataFacade &facade) : BasePlugin{facade} {} + +IsochronePlugin::IsochronePlugin(datafacade::BaseDataFacade &facade, const std::string base) + : BasePlugin{facade}, base{base} +{ + number_of_nodes = loadGraph(base, coordinate_list, graph_edge_list); + + tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); + graph = std::make_shared(number_of_nodes, graph_edge_list); + graph_edge_list.clear(); + graph_edge_list.shrink_to_fit(); +} Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, util::json::Object &json_result) { + BOOST_ASSERT(params.IsValid()); + + if (!CheckAllCoordinates(params.coordinates)) + return Error("InvalidOptions", "Coordinates are invalid", json_result); + + if (params.coordinates.size() != 1) + { + return Error("InvalidOptions", "Only one input coordinate is supported", json_result); + } + + auto phantomnodes = GetPhantomNodes(params, params.number_of_results); + auto phantomnodes2 = GetPhantomNodes(params); + +// if (phantomnodes.front().) +// { +// return Error("NoSegment", "Could not find a matching segments for coordinate", json_result); +// } +// BOOST_ASSERT(phantomnodes[0] > 0); + + + + util::SimpleLogger().Write() << util::toFloating(params.coordinates.front().lat); + util::SimpleLogger().Write() << util::toFloating(params.coordinates.front().lon); + + + util::SimpleLogger().Write() << "Phantom-Node-Size: " << phantomnodes.size(); + util::SimpleLogger().Write() << "Phantom-Node-Size2: " << phantomnodes.front().size(); + +// api::IsochroneAPI isochroneAPI(facade, params); +// isochroneAPI.MakeResponse(phantom_nodes, json_result); + +// std::sort(phantomnodes.front().begin(), phantomnodes.front().end(), +// [&](const osrm::engine::PhantomNodeWithDistance &a, +// const osrm::engine::PhantomNodeWithDistance &b) +// { +// return a.distance > b.distance; +// }); + auto phantom = phantomnodes.front(); + + util::SimpleLogger().Write() << util::toFloating(phantom.front().phantom_node.location.lat); + util::SimpleLogger().Write() << util::toFloating(phantom.front().phantom_node.location.lon); + + util::SimpleLogger().Write() << util::toFloating(phantomnodes2.front().first.location.lat); + util::SimpleLogger().Write() << util::toFloating(phantomnodes2.front().first.location.lon); + NodeID source = 0; + +// if (facade.Edgeie(phantom.phantom_node.forward_segment_id.id)) +// { +// std::vector forward_id_vector; +// facade.GetUncompressedGeometry(phantom.phantom_node.forward_packed_geometry_id, +// forward_id_vector); +// source = forward_id_vector[phantom.phantom_node.fwd_segment_position]; +// } +// else +// + + source = phantom.front().phantom_node.forward_packed_geometry_id; +// } + +// util::SimpleLogger().Write() << "Node-Coord" << sourceLat << " " << sourceLon; + + // Init complete + + struct HeapData + { + NodeID parent; + /* explicit */ HeapData(NodeID p) : parent(p) {} + }; + + using QueryHeap = osrm::util::BinaryHeap>; + + QueryHeap heap(number_of_nodes); + + util::SimpleLogger().Write() << "asdasdasd"; + heap.Insert(source, -phantom.front().phantom_node.GetForwardWeightPlusOffset(), source); + + // value is in metres + const int MAX = 1000; + + std::unordered_set edgepoints; + std::unordered_set insidepoints; + std::vector border; + + { + // Standard Dijkstra search, terminating when path length > MAX + while (!heap.Empty()) + { + const NodeID source = heap.DeleteMin(); + const std::int32_t distance = heap.GetKey(source); + + for (const auto current_edge : graph->GetAdjacentEdgeRange(source)) + { + const auto target = graph->GetTarget(current_edge); + if (target != SPECIAL_NODEID) + { + const auto data = graph->GetEdgeData(current_edge); + if (data.real) + { + int to_distance = distance + data.weight; +// util::SimpleLogger().Write() << "I am Node " << target; +// util::SimpleLogger().Write() << "Distance to Source: " << to_distance; + if (to_distance > MAX) + { + edgepoints.insert(target); + // distance_map[target] = + // to_distance; + // predecessor_map[target] = source; + } + else if (!heap.WasInserted(target)) + { + heap.Insert(target, to_distance, source); + insidepoints.insert(source); + distance_map[target] = to_distance; + predecessor_map[target] = source; + } + else if (to_distance < heap.GetKey(target)) + { + heap.GetData(target).parent = source; + heap.DecreaseKey(target, to_distance); + distance_map[target] = to_distance; + predecessor_map[target] = source; + } + } + } + } + } + } + util::SimpleLogger().Write(); + util::SimpleLogger().Write() << "Inside-Points"; + util::SimpleLogger().Write() << insidepoints.size(); +// for (auto p : insidepoints) +// { +// auto lon = coordinate_list[p].lon; +// auto lat = coordinate_list[p].lat; +// auto pre = predecessor_map[p]; +// auto d = distance_map[p]; +// util::SimpleLogger().Write() << "Lat: " << lat; +// util::SimpleLogger().Write() << "Lon: " << lon; +// util::SimpleLogger().Write() << "Source: " << p << " Predecessor: " << pre +// << " Distance " << d; +// // util::SimpleLogger().Write(); +// } + + util::SimpleLogger().Write() << "Edgepoints"; + util::SimpleLogger().Write() << edgepoints.size(); + // for (auto p : edgepoints) + // { + // double lon = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; + // double lat = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; + // auto pre = predecessor_map[p]; + // auto d = distance_map[p]; + //// util::SimpleLogger().Write() << "Lat: " << lat; + //// util::SimpleLogger().Write() << "Lon: " << lon; + // util::SimpleLogger().Write() << "Predecessor: " << pre << "Distance " << d; + // util::SimpleLogger().Write(); + // } + + util::json::Array data; + for (auto p : insidepoints) + { + util::json::Object object; + + util::json::Object source; + source.values["lat"] = static_cast(util::toFloating(coordinate_list[p].lat)); + source.values["lon"] = static_cast(util::toFloating(coordinate_list[p].lon)); + object.values["p1"] = std::move(source); + + util::json::Object predecessor; + auto pre = predecessor_map[p]; + predecessor.values["lat"] = static_cast(util::toFloating(coordinate_list[pre].lat)); + predecessor.values["lon"] = static_cast(util::toFloating(coordinate_list[pre].lon)); + object.values["p2"] = std::move(predecessor); + + util::json::Object distance; + object.values["distance_from_start"] = distance_map[p]; + + data.values.push_back(object); + } + + json_result.values["Range-Analysis"] = std::move(data); return Status::Ok; } + + + + +std::size_t IsochronePlugin::loadGraph(const std::string &path, + std::vector &coordinate_list, + std::vector &graph_edge_list) +{ + + std::ifstream input_stream(path, std::ifstream::in | std::ifstream::binary); + if (!input_stream.is_open()) + { + throw util::exception("Cannot open osrm file"); + } + + // load graph data + std::vector edge_list; + std::vector traffic_light_node_list; + std::vector barrier_node_list; + + auto number_of_nodes = util::loadNodesFromFile(input_stream, barrier_node_list, + traffic_light_node_list, coordinate_list); + + util::loadEdgesFromFile(input_stream, edge_list); + + traffic_light_node_list.clear(); + traffic_light_node_list.shrink_to_fit(); + + // Building an node-based graph + for (const auto &input_edge : edge_list) + { + if (input_edge.source == input_edge.target) + { + continue; + } + + // forward edge + graph_edge_list.emplace_back(input_edge.source, input_edge.target, input_edge.weight, + input_edge.forward); + // backward edge + graph_edge_list.emplace_back(input_edge.target, input_edge.source, input_edge.weight, + input_edge.backward); + } + + return number_of_nodes; +} } } } diff --git a/src/storage/storage_config.cpp b/src/storage/storage_config.cpp index 6ade34a0c7f..3f571ebd0c9 100644 --- a/src/storage/storage_config.cpp +++ b/src/storage/storage_config.cpp @@ -8,20 +8,21 @@ namespace storage { StorageConfig::StorageConfig(const boost::filesystem::path &base) - : ram_index_path{base.string() + ".ramIndex"}, file_index_path{base.string() + ".fileIndex"}, - hsgr_data_path{base.string() + ".hsgr"}, nodes_data_path{base.string() + ".nodes"}, - edges_data_path{base.string() + ".edges"}, core_data_path{base.string() + ".core"}, - geometries_path{base.string() + ".geometry"}, timestamp_path{base.string() + ".timestamp"}, + : base{base.string()}, ram_index_path{base.string() + ".ramIndex"}, + file_index_path{base.string() + ".fileIndex"}, hsgr_data_path{base.string() + ".hsgr"}, + nodes_data_path{base.string() + ".nodes"}, edges_data_path{base.string() + ".edges"}, + core_data_path{base.string() + ".core"}, geometries_path{base.string() + ".geometry"}, + timestamp_path{base.string() + ".timestamp"}, datasource_names_path{base.string() + ".datasource_names"}, datasource_indexes_path{base.string() + ".datasource_indexes"}, - names_data_path{base.string() + ".names"}, - properties_path{base.string() + ".properties"} + names_data_path{base.string() + ".names"}, properties_path{base.string() + ".properties"} { } bool StorageConfig::IsValid() const { - return boost::filesystem::is_regular_file(ram_index_path) && + return boost::filesystem::is_regular_file(base) && + boost::filesystem::is_regular_file(ram_index_path) && boost::filesystem::is_regular_file(file_index_path) && boost::filesystem::is_regular_file(hsgr_data_path) && boost::filesystem::is_regular_file(nodes_data_path) && From 0cf0608fdeb9d47efcf87acbe837d8c72fadca3c Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 20 Apr 2016 10:07:47 +0200 Subject: [PATCH 18/43] added inline to functions to be able to use them for isochrones/avoids multiple definition error --- include/util/graph_loader.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/util/graph_loader.hpp b/include/util/graph_loader.hpp index b611e04bb6d..69c42168954 100644 --- a/include/util/graph_loader.hpp +++ b/include/util/graph_loader.hpp @@ -32,7 +32,7 @@ namespace util * The since the restrictions reference nodes using their external node id, * we need to renumber it to the new internal id. */ -unsigned loadRestrictionsFromFile(std::istream &input_stream, +inline unsigned loadRestrictionsFromFile(std::istream &input_stream, std::vector &restriction_list) { const FingerPrint fingerprint_valid = FingerPrint::GetValid(); @@ -62,7 +62,7 @@ unsigned loadRestrictionsFromFile(std::istream &input_stream, * - list of traffic lights * - nodes indexed by their internal (non-osm) id */ -NodeID loadNodesFromFile(std::istream &input_stream, +inline NodeID loadNodesFromFile(std::istream &input_stream, std::vector &barrier_node_list, std::vector &traffic_light_node_list, std::vector &node_array) @@ -107,7 +107,7 @@ NodeID loadNodesFromFile(std::istream &input_stream, /** * Reads a .osrm file and produces the edges. */ -NodeID loadEdgesFromFile(std::istream &input_stream, +inline NodeID loadEdgesFromFile(std::istream &input_stream, std::vector &edge_list) { EdgeID m; From 1440e54b49b48101a60dd953aea140674b7c9483 Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 27 Apr 2016 08:57:55 +0200 Subject: [PATCH 19/43] working isochrone calc, TODO: find out why distances are further than MAX_RANGE --- include/engine/api/isochrone_api.hpp | 17 +- include/engine/api/isochrone_parameters.hpp | 1 + include/engine/plugins/isochrone.hpp | 42 ++- src/engine/plugins/isochrone.cpp | 279 +++++++++++++------- 4 files changed, 229 insertions(+), 110 deletions(-) diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp index 7e171fb6712..6a65a861d44 100644 --- a/include/engine/api/isochrone_api.hpp +++ b/include/engine/api/isochrone_api.hpp @@ -30,22 +30,7 @@ class IsochroneAPI final : public BaseAPI void MakeResponse(const std::vector> &phantom_nodes, util::json::Object &response) const { - BOOST_ASSERT(phantom_nodes.size() == 1); - BOOST_ASSERT(parameters.coordinates.size() == 1); - - util::json::Array waypoints; - waypoints.values.resize(phantom_nodes.front().size()); - std::transform(phantom_nodes.front().begin(), phantom_nodes.front().end(), - waypoints.values.begin(), - [this](const PhantomNodeWithDistance &phantom_with_distance) - { - auto waypoint = MakeWaypoint(phantom_with_distance.phantom_node); - waypoint.values["distance"] = phantom_with_distance.distance; - return waypoint; - }); - - response.values["code"] = "Ok"; - response.values["waypoints"] = std::move(waypoints); + } diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp index 46e0b546446..9ae5e9778b2 100644 --- a/include/engine/api/isochrone_parameters.hpp +++ b/include/engine/api/isochrone_parameters.hpp @@ -19,6 +19,7 @@ struct IsochroneParameters : public BaseParameters unsigned number_of_results = 1; unsigned distance = 1000; + bool IsValid() const { return BaseParameters::IsValid() && number_of_results >= 1; } diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index cae057b952e..2aa0af5654b 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -10,12 +10,18 @@ #include "util/static_graph.hpp" #include "osrm/json_container.hpp" +#include +#include #include #include #include #include +#include #include +#include +#include +#include namespace osrm { @@ -23,6 +29,7 @@ namespace engine { namespace plugins { + struct SimpleEdgeData { SimpleEdgeData() : weight(INVALID_EDGE_WEIGHT), real(false) {} @@ -31,6 +38,33 @@ struct SimpleEdgeData bool real; }; +struct IsochroneNode +{ + IsochroneNode(NodeID node) : node(node) {} + IsochroneNode(NodeID node, NodeID predecessor, int distance) + : node(node), predecessor(predecessor), distance(distance) + { + } + + NodeID node; + NodeID predecessor; + int distance; +}; +struct IsochroneNodeIdCompare +{ + bool operator()(const IsochroneNode &a, const IsochroneNode &b) const + { + return a.node < b.node; + } +}; +struct IsochroneNodeDistanceCompare +{ + bool operator()(const IsochroneNode &a, const IsochroneNode &b) const + { + return a.distance < b.distance; + } +}; + using SimpleGraph = util::StaticGraph; using SimpleEdge = SimpleGraph::InputEdge; @@ -40,8 +74,9 @@ class IsochronePlugin final : public BasePlugin boost::filesystem::path base; std::shared_ptr graph; std::vector coordinate_list; - std::map distance_map; - std::map predecessor_map; + typedef std::set IsochroneSet; + typedef std::set IsochroneDistanceSet; + IsochroneSet isochroneSet; std::vector graph_edge_list; std::size_t number_of_nodes; @@ -49,6 +84,9 @@ class IsochronePlugin final : public BasePlugin std::vector &coordinate_list, std::vector &graph_edge_list); + void update(IsochroneSet &s, IsochroneNode node); + void findBorder(std::unordered_set &insidepoints, util::json::Object &response); + public: explicit IsochronePlugin(datafacade::BaseDataFacade &facade, const std::string base); diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index 0a6b5fb7a30..24c2ca79894 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -2,7 +2,6 @@ // Created by robin on 4/13/16. // - #include "engine/plugins/isochrone.hpp" #include "engine/api/isochrone_api.hpp" #include "util/graph_loader.hpp" @@ -12,6 +11,7 @@ #include #include +#include #include namespace osrm @@ -48,30 +48,28 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, auto phantomnodes = GetPhantomNodes(params, params.number_of_results); auto phantomnodes2 = GetPhantomNodes(params); -// if (phantomnodes.front().) -// { -// return Error("NoSegment", "Could not find a matching segments for coordinate", json_result); -// } -// BOOST_ASSERT(phantomnodes[0] > 0); - - + // if (phantomnodes.front().) + // { + // return Error("NoSegment", "Could not find a matching segments for coordinate", + // json_result); + // } + // BOOST_ASSERT(phantomnodes[0] > 0); util::SimpleLogger().Write() << util::toFloating(params.coordinates.front().lat); util::SimpleLogger().Write() << util::toFloating(params.coordinates.front().lon); + util::SimpleLogger().Write() << "Phantom-Node-Size: " << phantomnodes.size(); + util::SimpleLogger().Write() << "Phantom-Node-Size2: " << phantomnodes.front().size(); - util::SimpleLogger().Write() << "Phantom-Node-Size: " << phantomnodes.size(); - util::SimpleLogger().Write() << "Phantom-Node-Size2: " << phantomnodes.front().size(); - -// api::IsochroneAPI isochroneAPI(facade, params); -// isochroneAPI.MakeResponse(phantom_nodes, json_result); + // api::IsochroneAPI isochroneAPI(facade, params); + // isochroneAPI.MakeResponse(phantom_nodes, json_result); -// std::sort(phantomnodes.front().begin(), phantomnodes.front().end(), -// [&](const osrm::engine::PhantomNodeWithDistance &a, -// const osrm::engine::PhantomNodeWithDistance &b) -// { -// return a.distance > b.distance; -// }); + std::sort(phantomnodes.front().begin(), phantomnodes.front().end(), + [&](const osrm::engine::PhantomNodeWithDistance &a, + const osrm::engine::PhantomNodeWithDistance &b) + { + return a.distance > b.distance; + }); auto phantom = phantomnodes.front(); util::SimpleLogger().Write() << util::toFloating(phantom.front().phantom_node.location.lat); @@ -79,23 +77,27 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, util::SimpleLogger().Write() << util::toFloating(phantomnodes2.front().first.location.lat); util::SimpleLogger().Write() << util::toFloating(phantomnodes2.front().first.location.lon); - NodeID source = 0; - -// if (facade.Edgeie(phantom.phantom_node.forward_segment_id.id)) -// { -// std::vector forward_id_vector; -// facade.GetUncompressedGeometry(phantom.phantom_node.forward_packed_geometry_id, -// forward_id_vector); -// source = forward_id_vector[phantom.phantom_node.fwd_segment_position]; -// } -// else -// - - source = phantom.front().phantom_node.forward_packed_geometry_id; -// } - -// util::SimpleLogger().Write() << "Node-Coord" << sourceLat << " " << sourceLon; - + auto source = 0; + + // if (facade.Edgeie(phantom.phantom_node.forward_segment_id.id)) + // { + // std::vector forward_id_vector; + // facade.GetUncompressedGeometry(phantom.phantom_node.forward_packed_geometry_id, + // forward_id_vector); + // source = forward_id_vector[phantom.phantom_node.fwd_segment_position]; + // } + // else + // + + std::vector forward_id_vector; + facade.GetUncompressedGeometry(phantom.front().phantom_node.forward_packed_geometry_id, + forward_id_vector); + source = forward_id_vector[phantom.front().phantom_node.fwd_segment_position]; + util::SimpleLogger().Write() << coordinate_list[source].lat << " " + << coordinate_list[source].lon; + + util::SimpleLogger().Write() << "segment id " << phantom.front().phantom_node.forward_segment_id; + util::SimpleLogger().Write() << "packed id " << source; // Init complete struct HeapData @@ -105,15 +107,12 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, }; using QueryHeap = osrm::util::BinaryHeap>; + osrm::util::UnorderedMapStorage>; QueryHeap heap(number_of_nodes); - - util::SimpleLogger().Write() << "asdasdasd"; - heap.Insert(source, -phantom.front().phantom_node.GetForwardWeightPlusOffset(), source); - + heap.Insert(source, 0, source); // value is in metres - const int MAX = 1000; + const int MAX = 2000; std::unordered_set edgepoints; std::unordered_set insidepoints; @@ -135,97 +134,180 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, if (data.real) { int to_distance = distance + data.weight; -// util::SimpleLogger().Write() << "I am Node " << target; -// util::SimpleLogger().Write() << "Distance to Source: " << to_distance; if (to_distance > MAX) { edgepoints.insert(target); - // distance_map[target] = - // to_distance; - // predecessor_map[target] = source; } else if (!heap.WasInserted(target)) { heap.Insert(target, to_distance, source); insidepoints.insert(source); - distance_map[target] = to_distance; - predecessor_map[target] = source; + isochroneSet.insert(IsochroneNode(target, source, to_distance)); } else if (to_distance < heap.GetKey(target)) { heap.GetData(target).parent = source; heap.DecreaseKey(target, to_distance); - distance_map[target] = to_distance; - predecessor_map[target] = source; + update(isochroneSet, IsochroneNode(target, source, to_distance)); } } } } } } - util::SimpleLogger().Write(); - util::SimpleLogger().Write() << "Inside-Points"; - util::SimpleLogger().Write() << insidepoints.size(); -// for (auto p : insidepoints) -// { -// auto lon = coordinate_list[p].lon; -// auto lat = coordinate_list[p].lat; -// auto pre = predecessor_map[p]; -// auto d = distance_map[p]; -// util::SimpleLogger().Write() << "Lat: " << lat; -// util::SimpleLogger().Write() << "Lon: " << lon; -// util::SimpleLogger().Write() << "Source: " << p << " Predecessor: " << pre -// << " Distance " << d; -// // util::SimpleLogger().Write(); -// } - - util::SimpleLogger().Write() << "Edgepoints"; - util::SimpleLogger().Write() << edgepoints.size(); - // for (auto p : edgepoints) - // { - // double lon = coordinate_list[p].lon / osrm::COORDINATE_PRECISION; - // double lat = coordinate_list[p].lat / osrm::COORDINATE_PRECISION; - // auto pre = predecessor_map[p]; - // auto d = distance_map[p]; - //// util::SimpleLogger().Write() << "Lat: " << lat; - //// util::SimpleLogger().Write() << "Lon: " << lon; - // util::SimpleLogger().Write() << "Predecessor: " << pre << "Distance " << d; - // util::SimpleLogger().Write(); - // } + + util::SimpleLogger().Write() << isochroneSet.size(); util::json::Array data; - for (auto p : insidepoints) + for (auto node : isochroneSet) { util::json::Object object; util::json::Object source; - source.values["lat"] = static_cast(util::toFloating(coordinate_list[p].lat)); - source.values["lon"] = static_cast(util::toFloating(coordinate_list[p].lon)); + source.values["lat"] = + static_cast(util::toFloating(coordinate_list[node.node].lat)); + source.values["lon"] = + static_cast(util::toFloating(coordinate_list[node.node].lon)); object.values["p1"] = std::move(source); util::json::Object predecessor; - auto pre = predecessor_map[p]; - predecessor.values["lat"] = static_cast(util::toFloating(coordinate_list[pre].lat)); - predecessor.values["lon"] = static_cast(util::toFloating(coordinate_list[pre].lon)); + predecessor.values["lat"] = + static_cast(util::toFloating(coordinate_list[node.predecessor].lat)); + predecessor.values["lon"] = + static_cast(util::toFloating(coordinate_list[node.predecessor].lon)); object.values["p2"] = std::move(predecessor); util::json::Object distance; - object.values["distance_from_start"] = distance_map[p]; + object.values["distance_from_start"] = node.distance; + // util::SimpleLogger().Write() << "Node: " << node.node + // << " Predecessor: " << node.predecessor + // << " Distance: " << node.distance; data.values.push_back(object); } - - json_result.values["Range-Analysis"] = std::move(data); - + json_result.values["isochrone"] = std::move(data); + + // util::json::Array data2; + // for (auto b : edgepoints) + // { + // util::json::Object point; + // point.values["lat"] = static_cast(util::toFloating(coordinate_list[b].lat)); + // point.values["lon"] = static_cast(util::toFloating(coordinate_list[b].lon)); + // data2.values.push_back(point); + // } + + // json_result.values["border"] = std::move(data2); + // findBorder(insidepoints, json_result); return Status::Ok; } +void IsochronePlugin::findBorder(std::unordered_set &insidepoints, + util::json::Object &response) +{ + NodeID startnode = SPECIAL_NODEID; + std::vector border; + // Find the north-west most edge node + for (const auto node_id : insidepoints) + { + if (startnode == SPECIAL_NODEID) + { + startnode = node_id; + } + else + { + if (coordinate_list[node_id].lon < coordinate_list[startnode].lon) + { + startnode = node_id; + } + else if (coordinate_list[node_id].lon == coordinate_list[startnode].lon && + coordinate_list[node_id].lat > coordinate_list[startnode].lat) + { + startnode = node_id; + } + } + } + NodeID node_u = startnode; + border.push_back(node_u); + // Find the outgoing edge with the angle closest to 180 (because we're at the west-most node, + // there should be no edges with angles < 0 or > 180) + NodeID node_v = SPECIAL_NODEID; + for (const auto current_edge : graph->GetAdjacentEdgeRange(node_u)) + { + const auto target = graph->GetTarget(current_edge); + if (target != SPECIAL_NODEID && insidepoints.find(target) != insidepoints.end()) + { + if (node_v == SPECIAL_NODEID) + { + node_v = target; + } + else + { + if (osrm::util::coordinate_calculation::bearing(coordinate_list[node_u], + coordinate_list[target]) > + osrm::util::coordinate_calculation::bearing(coordinate_list[node_u], + coordinate_list[node_v])) + { + node_v = target; + } + } + BOOST_ASSERT(0 <= osrm::util::coordinate_calculation::bearing(coordinate_list[node_u], + coordinate_list[node_v])); + BOOST_ASSERT(180 >= osrm::util::coordinate_calculation::bearing( + coordinate_list[node_u], coordinate_list[node_v])); + } + } + + border.push_back(node_v); + + // Now, we're going to always turn right (relative to the last edge) + // only onto nodes that are onthe inside point set + NodeID firsttarget = node_v; + while (true) + { + NodeID node_w = SPECIAL_NODEID; + double best_angle = 361.0; + for (const auto current_edge : graph->GetAdjacentEdgeRange(node_v)) + { + const auto target = graph->GetTarget(current_edge); + if (target == SPECIAL_NODEID) + continue; + if (insidepoints.find(target) == insidepoints.end()) + continue; + + auto angle = osrm::util::coordinate_calculation::computeAngle( + coordinate_list[node_u], coordinate_list[node_v], coordinate_list[target]); + if (node_w == SPECIAL_NODEID || angle > best_angle) + { + node_w = target; + best_angle = angle; + } + } + if (firsttarget == node_w && startnode == node_v) + { + // Here, we've looped all the way around the outside and we've traversed + // the first segment again. Break! + break; + } + border.push_back(node_w); + node_u = node_v; + node_v = node_w; + util::json::Array borderjson; + for (auto b : border) + { + util::json::Object point; + point.values["lat"] = static_cast(util::toFloating(coordinate_list[b].lat)); + point.values["lon"] = static_cast(util::toFloating(coordinate_list[b].lon)); + borderjson.values.push_back(point); + } + response.values["border"] = std::move(borderjson); + } +} std::size_t IsochronePlugin::loadGraph(const std::string &path, - std::vector &coordinate_list, - std::vector &graph_edge_list) + std::vector &coordinate_list, + std::vector &graph_edge_list) { std::ifstream input_stream(path, std::ifstream::in | std::ifstream::binary); @@ -265,6 +347,19 @@ std::size_t IsochronePlugin::loadGraph(const std::string &path, return number_of_nodes; } + +void IsochronePlugin::update(IsochroneSet &s, IsochroneNode node) +{ + std::pair p = s.insert(node); + bool alreadyThere = !p.second; + if (alreadyThere) + { + IsochroneSet::iterator hint = p.first; + hint++; + s.erase(p.first); + s.insert(hint, node); + } +} } } } From 383d1abcf2737883bd20c6a535d7a38446e28dc9 Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 27 Apr 2016 09:40:18 +0200 Subject: [PATCH 20/43] merge with fork --- include/storage/shared_memory.hpp | 4 ++-- src/engine/plugins/isochrone.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/storage/shared_memory.hpp b/include/storage/shared_memory.hpp index 0aa6c3807bb..362422676c5 100644 --- a/include/storage/shared_memory.hpp +++ b/include/storage/shared_memory.hpp @@ -1,5 +1,5 @@ -/* - +#ifndef SHARED_MEMORY_HPP +#define SHARED_MEMORY_HPP #include "util/exception.hpp" #include "util/simple_logger.hpp" diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index 24c2ca79894..7768c7e7750 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -96,7 +96,7 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, util::SimpleLogger().Write() << coordinate_list[source].lat << " " << coordinate_list[source].lon; - util::SimpleLogger().Write() << "segment id " << phantom.front().phantom_node.forward_segment_id; + util::SimpleLogger().Write() << "segment id " << phantom.front().phantom_node.forward_segment_id.id; util::SimpleLogger().Write() << "packed id " << source; // Init complete From 6bb0a16839590b314b0b6f6a12f68d6bee3fd5ac Mon Sep 17 00:00:00 2001 From: roroettg Date: Tue, 10 May 2016 14:17:23 +0200 Subject: [PATCH 21/43] added graham scan --- include/engine/api/isochrone_api.hpp | 10 +- include/engine/plugins/isochrone.hpp | 27 ++-- include/util/graham_scan.hpp | 161 +++++++++++++++++++++ src/engine/plugins/isochrone.cpp | 206 ++++----------------------- 4 files changed, 210 insertions(+), 194 deletions(-) create mode 100644 include/util/graham_scan.hpp diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp index 6a65a861d44..17162982271 100644 --- a/include/engine/api/isochrone_api.hpp +++ b/include/engine/api/isochrone_api.hpp @@ -27,11 +27,11 @@ class IsochroneAPI final : public BaseAPI { } - void MakeResponse(const std::vector> &phantom_nodes, - util::json::Object &response) const - { - - } +// void MakeResponse(const std::vector> &phantom_nodes, +// util::json::Object &response) const +// { +// +// } }; diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index 2aa0af5654b..294f30d5092 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -8,7 +8,9 @@ #include "engine/plugins/plugin_base.hpp" #include "engine/api/isochrone_parameters.hpp" #include "util/static_graph.hpp" +#include "util/coordinate.hpp" #include "osrm/json_container.hpp" +#include #include #include @@ -22,6 +24,7 @@ #include #include #include +#include namespace osrm { @@ -29,7 +32,11 @@ namespace engine { namespace plugins { - +struct HeapData +{ + NodeID parent; + HeapData(NodeID p) : parent(p) {} +}; struct SimpleEdgeData { SimpleEdgeData() : weight(INVALID_EDGE_WEIGHT), real(false) {} @@ -40,21 +47,21 @@ struct SimpleEdgeData struct IsochroneNode { - IsochroneNode(NodeID node) : node(node) {} - IsochroneNode(NodeID node, NodeID predecessor, int distance) + IsochroneNode(){}; + IsochroneNode(osrm::extractor::QueryNode node, osrm::extractor::QueryNode predecessor, int distance) : node(node), predecessor(predecessor), distance(distance) { } - NodeID node; - NodeID predecessor; + osrm::extractor::QueryNode node; + osrm::extractor::QueryNode predecessor; int distance; }; struct IsochroneNodeIdCompare { bool operator()(const IsochroneNode &a, const IsochroneNode &b) const { - return a.node < b.node; + return a.node.node_id < b.node.node_id; } }; struct IsochroneNodeDistanceCompare @@ -67,6 +74,10 @@ struct IsochroneNodeDistanceCompare using SimpleGraph = util::StaticGraph; using SimpleEdge = SimpleGraph::InputEdge; +using QueryHeap = osrm::util:: + BinaryHeap>; +typedef std::set IsochroneSet; +typedef std::set IsochroneDistanceSet; class IsochronePlugin final : public BasePlugin { @@ -74,8 +85,6 @@ class IsochronePlugin final : public BasePlugin boost::filesystem::path base; std::shared_ptr graph; std::vector coordinate_list; - typedef std::set IsochroneSet; - typedef std::set IsochroneDistanceSet; IsochroneSet isochroneSet; std::vector graph_edge_list; std::size_t number_of_nodes; @@ -85,7 +94,7 @@ class IsochronePlugin final : public BasePlugin std::vector &graph_edge_list); void update(IsochroneSet &s, IsochroneNode node); - void findBorder(std::unordered_set &insidepoints, util::json::Object &response); + void findBorder(IsochroneSet &isochroneSet, util::json::Object &response); public: explicit IsochronePlugin(datafacade::BaseDataFacade &facade, const std::string base); diff --git a/include/util/graham_scan.hpp b/include/util/graham_scan.hpp new file mode 100644 index 00000000000..c395bdfb2ac --- /dev/null +++ b/include/util/graham_scan.hpp @@ -0,0 +1,161 @@ +// +// Created by robin on 4/27/16. +// + +#ifndef OSRM_GRAHAM_SCAN_HPP +#define OSRM_GRAHAM_SCAN_HPP + +#include +#include +#include +#include + +#include +#include +#include + +namespace osrm +{ +namespace util +{ +using Node = engine::plugins::IsochroneNode; + +bool lon_compare(engine::plugins::IsochroneNode &a, engine::plugins::IsochroneNode &b) +{ + return b.node.lon == a.node.lon ? b.node.lat < a.node.lat : b.node.lon < a.node.lon; +} +std::uint64_t squaredEuclideanDistance(Node n1, Node n2) +{ + return util::coordinate_calculation::squaredEuclideanDistance( + Coordinate(n1.node.lon, n1.node.lat), Coordinate(n2.node.lon, n2.node.lat)); +} + +int orientation(Node p, Node q, Node r) +{ + int Q_lat = static_cast(q.node.lat); // b_x -a_x + int Q_lon= static_cast(q.node.lon); // b_x -a_x + int R_lat = static_cast(r.node.lat); // c_x - c_a + int R_lon = static_cast(r.node.lon); // c_x - c_a + int P_lat = static_cast(p.node.lat); // b_y - a_y + int P_lon = static_cast(p.node.lon); // c_y - a_y + + int val = (Q_lat - P_lat) * (R_lon - P_lon) - (Q_lon - P_lon) * (R_lat - P_lat); + + if (val == 0) + { + return 0; // colinear + } + return (val < 0) ? -1 : 1; // clock or counterclock wise +} +struct NodeComparer +{ + const Node p0; // lowest Node + + explicit NodeComparer(Node &p0) : p0(p0){}; + + bool operator()(const Node &a, const Node &b) + { + int o = orientation(p0, a, b); + if (o == 0) + { + return (squaredEuclideanDistance(p0, a) < squaredEuclideanDistance(p0, b)); + } + return o == 1; + } +}; + +Node nextToTop(std::stack &S) +{ + Node p = S.top(); + S.pop(); + Node res = S.top(); + S.push(p); + return res; +} +void swap(Node &p1, Node &p2) +{ + Node temp = p1; + p1 = p2; + p2 = temp; +} +void convexHull(engine::plugins::IsochroneSet &set, util::json::Object &response) +{ + std::vector points(set.begin(), set.end()); + + auto result = std::min_element(points.begin(), points.end(), lon_compare); + int n = points.size(); + + swap(points[0], *result); + + + std::sort(std::next(points.begin()), points.end(), NodeComparer(points[0])); + +// points.push_back(*result); +// int m = 1; // Initialize size of modified array +// for (int i = 1; i < n; i++) +// { +// // Keep removing i while angle of i and i+1 is same +// // with respect to p0 +// while ((i < n - 1) && (orientation(points[0], points[i], points[i + 1]) == 0)) +// { +// i++; +// } +// +// points[m] = points[i]; +// m++; // Update size of modified array +// } + // If modified array of points has less than 3 points, + // convex hull is not possible +// if (m < 3) +// return; + + // Create an empty stack and push first three points + // to it. + std::stack S; + S.push(points[0]); + S.push(points[1]); + S.push(points[2]); + + // Process remaining n-3 points + for (int i = 3; i < n; i++) + { + // Keep removing top while the angle formed by + // points next-to-top, top, and points[i] makes + // a non-left turn + Node top = S.top(); + S.pop(); + while (orientation(S.top(), top, points[i]) != 1 ) + { + top = S.top(); + S.pop(); + } + S.push(top); + S.push(points[i]); + } + + // Now stack has the output points, print contents of stack + + std::vector vec; + while (!S.empty()) + { + Node p = S.top(); + vec.emplace_back(p); + S.pop(); + } + +// std::sort(vec.begin(), vec.end(), [](Node a, Node b) { +// return b.node.lon + b.node.lat < a.node.lon + a.node.lat; +// }); + util::json::Array borderjson; + for(auto p : vec) { + util::json::Object point; + point.values["lat"] = static_cast(util::toFloating(p.node.lat)); + point.values["lon"] = static_cast(util::toFloating(p.node.lon)); + borderjson.values.push_back(point); + } + response.values["border"] = std::move(borderjson); +} +} +} + +#endif // OSRM_GRAHAM_SCAN_HPP diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index 7768c7e7750..fda8e7a6e10 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -6,13 +6,13 @@ #include "engine/api/isochrone_api.hpp" #include "util/graph_loader.hpp" #include "engine/phantom_node.hpp" -#include "util/coordinate.hpp" #include "util/simple_logger.hpp" +#include #include #include #include -#include +#include namespace osrm { @@ -24,6 +24,7 @@ namespace plugins IsochronePlugin::IsochronePlugin(datafacade::BaseDataFacade &facade, const std::string base) : BasePlugin{facade}, base{base} { + // Prepares uncontracted Graph number_of_nodes = loadGraph(base, coordinate_list, graph_edge_list); tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); @@ -46,24 +47,13 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, } auto phantomnodes = GetPhantomNodes(params, params.number_of_results); - auto phantomnodes2 = GetPhantomNodes(params); - // if (phantomnodes.front().) - // { - // return Error("NoSegment", "Could not find a matching segments for coordinate", - // json_result); - // } - // BOOST_ASSERT(phantomnodes[0] > 0); - - util::SimpleLogger().Write() << util::toFloating(params.coordinates.front().lat); - util::SimpleLogger().Write() << util::toFloating(params.coordinates.front().lon); - - util::SimpleLogger().Write() << "Phantom-Node-Size: " << phantomnodes.size(); - util::SimpleLogger().Write() << "Phantom-Node-Size2: " << phantomnodes.front().size(); - - // api::IsochroneAPI isochroneAPI(facade, params); - // isochroneAPI.MakeResponse(phantom_nodes, json_result); + if (phantomnodes.front().size() <= 0) + { + return Error("PhantomNode", "PhantomNode couldnt be found for coordinate", json_result); + } + // Find closest phantomnode std::sort(phantomnodes.front().begin(), phantomnodes.front().end(), [&](const osrm::engine::PhantomNodeWithDistance &a, const osrm::engine::PhantomNodeWithDistance &b) @@ -75,47 +65,18 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, util::SimpleLogger().Write() << util::toFloating(phantom.front().phantom_node.location.lat); util::SimpleLogger().Write() << util::toFloating(phantom.front().phantom_node.location.lon); - util::SimpleLogger().Write() << util::toFloating(phantomnodes2.front().first.location.lat); - util::SimpleLogger().Write() << util::toFloating(phantomnodes2.front().first.location.lon); - auto source = 0; - - // if (facade.Edgeie(phantom.phantom_node.forward_segment_id.id)) - // { - // std::vector forward_id_vector; - // facade.GetUncompressedGeometry(phantom.phantom_node.forward_packed_geometry_id, - // forward_id_vector); - // source = forward_id_vector[phantom.phantom_node.fwd_segment_position]; - // } - // else - // - std::vector forward_id_vector; facade.GetUncompressedGeometry(phantom.front().phantom_node.forward_packed_geometry_id, forward_id_vector); - source = forward_id_vector[phantom.front().phantom_node.fwd_segment_position]; - util::SimpleLogger().Write() << coordinate_list[source].lat << " " - << coordinate_list[source].lon; - - util::SimpleLogger().Write() << "segment id " << phantom.front().phantom_node.forward_segment_id.id; - util::SimpleLogger().Write() << "packed id " << source; - // Init complete - - struct HeapData - { - NodeID parent; - /* explicit */ HeapData(NodeID p) : parent(p) {} - }; - - using QueryHeap = osrm::util::BinaryHeap>; + auto source = forward_id_vector[phantom.front().phantom_node.fwd_segment_position]; QueryHeap heap(number_of_nodes); heap.Insert(source, 0, source); + // value is in metres - const int MAX = 2000; +// const int MAX = 2000; + const int MAX = 7500; - std::unordered_set edgepoints; - std::unordered_set insidepoints; std::vector border; { @@ -136,19 +97,18 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, int to_distance = distance + data.weight; if (to_distance > MAX) { - edgepoints.insert(target); + continue; } else if (!heap.WasInserted(target)) { heap.Insert(target, to_distance, source); - insidepoints.insert(source); - isochroneSet.insert(IsochroneNode(target, source, to_distance)); + isochroneSet.insert(IsochroneNode(coordinate_list[target], coordinate_list[source], to_distance)); } else if (to_distance < heap.GetKey(target)) { heap.GetData(target).parent = source; heap.DecreaseKey(target, to_distance); - update(isochroneSet, IsochroneNode(target, source, to_distance)); + update(isochroneSet, IsochroneNode(coordinate_list[target], coordinate_list[source], to_distance)); } } } @@ -157,154 +117,40 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, } util::SimpleLogger().Write() << isochroneSet.size(); - + std::vector isoByDistance(isochroneSet.begin(), isochroneSet.end()); + std::sort(isoByDistance.begin(), isoByDistance.end(),[] (IsochroneNode n1, IsochroneNode n2){ + return n1.distance < n2.distance; + }); util::json::Array data; - for (auto node : isochroneSet) + for (auto isochrone : isoByDistance) { util::json::Object object; util::json::Object source; source.values["lat"] = - static_cast(util::toFloating(coordinate_list[node.node].lat)); + static_cast(util::toFloating(isochrone.node.lat)); source.values["lon"] = - static_cast(util::toFloating(coordinate_list[node.node].lon)); + static_cast(util::toFloating(isochrone.node.lon)); object.values["p1"] = std::move(source); util::json::Object predecessor; predecessor.values["lat"] = - static_cast(util::toFloating(coordinate_list[node.predecessor].lat)); + static_cast(util::toFloating(isochrone.predecessor.lat)); predecessor.values["lon"] = - static_cast(util::toFloating(coordinate_list[node.predecessor].lon)); + static_cast(util::toFloating(isochrone.predecessor.lon)); object.values["p2"] = std::move(predecessor); util::json::Object distance; - object.values["distance_from_start"] = node.distance; + object.values["distance_from_start"] = isochrone.distance; - // util::SimpleLogger().Write() << "Node: " << node.node - // << " Predecessor: " << node.predecessor - // << " Distance: " << node.distance; data.values.push_back(object); } json_result.values["isochrone"] = std::move(data); - // util::json::Array data2; - // for (auto b : edgepoints) - // { - // util::json::Object point; - // point.values["lat"] = static_cast(util::toFloating(coordinate_list[b].lat)); - // point.values["lon"] = static_cast(util::toFloating(coordinate_list[b].lon)); - // data2.values.push_back(point); - // } - - // json_result.values["border"] = std::move(data2); - // findBorder(insidepoints, json_result); + util::convexHull(isochroneSet, json_result); return Status::Ok; } -void IsochronePlugin::findBorder(std::unordered_set &insidepoints, - util::json::Object &response) -{ - NodeID startnode = SPECIAL_NODEID; - std::vector border; - // Find the north-west most edge node - for (const auto node_id : insidepoints) - { - if (startnode == SPECIAL_NODEID) - { - startnode = node_id; - } - else - { - if (coordinate_list[node_id].lon < coordinate_list[startnode].lon) - { - startnode = node_id; - } - else if (coordinate_list[node_id].lon == coordinate_list[startnode].lon && - coordinate_list[node_id].lat > coordinate_list[startnode].lat) - { - startnode = node_id; - } - } - } - NodeID node_u = startnode; - border.push_back(node_u); - - // Find the outgoing edge with the angle closest to 180 (because we're at the west-most node, - // there should be no edges with angles < 0 or > 180) - NodeID node_v = SPECIAL_NODEID; - for (const auto current_edge : graph->GetAdjacentEdgeRange(node_u)) - { - const auto target = graph->GetTarget(current_edge); - if (target != SPECIAL_NODEID && insidepoints.find(target) != insidepoints.end()) - { - if (node_v == SPECIAL_NODEID) - { - node_v = target; - } - else - { - if (osrm::util::coordinate_calculation::bearing(coordinate_list[node_u], - coordinate_list[target]) > - osrm::util::coordinate_calculation::bearing(coordinate_list[node_u], - coordinate_list[node_v])) - { - node_v = target; - } - } - BOOST_ASSERT(0 <= osrm::util::coordinate_calculation::bearing(coordinate_list[node_u], - coordinate_list[node_v])); - BOOST_ASSERT(180 >= osrm::util::coordinate_calculation::bearing( - coordinate_list[node_u], coordinate_list[node_v])); - } - } - - border.push_back(node_v); - - // Now, we're going to always turn right (relative to the last edge) - // only onto nodes that are onthe inside point set - NodeID firsttarget = node_v; - while (true) - { - NodeID node_w = SPECIAL_NODEID; - double best_angle = 361.0; - for (const auto current_edge : graph->GetAdjacentEdgeRange(node_v)) - { - const auto target = graph->GetTarget(current_edge); - if (target == SPECIAL_NODEID) - continue; - if (insidepoints.find(target) == insidepoints.end()) - continue; - - auto angle = osrm::util::coordinate_calculation::computeAngle( - coordinate_list[node_u], coordinate_list[node_v], coordinate_list[target]); - if (node_w == SPECIAL_NODEID || angle > best_angle) - { - node_w = target; - best_angle = angle; - } - } - if (firsttarget == node_w && startnode == node_v) - { - // Here, we've looped all the way around the outside and we've traversed - // the first segment again. Break! - break; - } - border.push_back(node_w); - - node_u = node_v; - node_v = node_w; - - util::json::Array borderjson; - for (auto b : border) - { - util::json::Object point; - point.values["lat"] = static_cast(util::toFloating(coordinate_list[b].lat)); - point.values["lon"] = static_cast(util::toFloating(coordinate_list[b].lon)); - borderjson.values.push_back(point); - } - response.values["border"] = std::move(borderjson); - } -} std::size_t IsochronePlugin::loadGraph(const std::string &path, std::vector &coordinate_list, std::vector &graph_edge_list) From b168f01b2c389de970816cd887c85c9c718f8055 Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 11 May 2016 09:02:32 +0200 Subject: [PATCH 22/43] fixed isochrone for current version of master --- .../api/isochrone_parameter_grammar.hpp | 34 ++++++----- include/server/service/isochrone_service.hpp | 2 +- src/server/api/parameters_parser.cpp | 56 ++++++++++++------- src/server/service/isochrone_service.cpp | 2 +- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp index d8c0693de2b..09b6ffab766 100644 --- a/include/server/api/isochrone_parameter_grammar.hpp +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -17,28 +17,34 @@ namespace server namespace api { +namespace +{ +namespace ph = boost::phoenix; namespace qi = boost::spirit::qi; +} -struct IsochroneParametersGrammar final : public BaseParametersGrammar +template +struct IsochroneParametersGrammar final : public BaseParametersGrammar { - using Iterator = std::string::iterator; + using BaseGrammar = BaseParametersGrammar; - IsochroneParametersGrammar() : BaseParametersGrammar(root_rule, parameters) + IsochroneParametersGrammar() : BaseGrammar(root_rule) { - const auto set_number = [this](const unsigned number) - { - parameters.number_of_results = number; - }; - isochrone_rule = (qi::lit("number=") > qi::uint_)[set_number]; - root_rule = - query_rule > -qi::lit(".json") > -(qi::lit("?") > (isochrone_rule | base_rule) % '&'); - } - engine::api::IsochroneParameters parameters; + isochrone_rule = (qi::lit("number=") > + qi::uint_)[ph::bind(&engine::api::IsochroneParameters::number_of_results, + qi::_r1) = qi::_1]; + + root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") > + -('?' > (isochrone_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&'); + } + // + // engine::api::IsochroneParameters parameters; private: - qi::rule root_rule; - qi::rule isochrone_rule; + qi::rule root_rule; + qi::rule isochrone_rule; }; } } diff --git a/include/server/service/isochrone_service.hpp b/include/server/service/isochrone_service.hpp index 77de052943f..5801c0770fe 100644 --- a/include/server/service/isochrone_service.hpp +++ b/include/server/service/isochrone_service.hpp @@ -26,7 +26,7 @@ class IsochroneService final : public BaseService public: IsochroneService(OSRM &routing_machine) : BaseService(routing_machine) {} - engine::Status RunQuery(std::string &query, ResultT &result) final override; + engine::Status RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override; unsigned GetVersion() final override { return 1; } }; diff --git a/src/server/api/parameters_parser.cpp b/src/server/api/parameters_parser.cpp index 72a2d4b3f11..1e6957f0f85 100644 --- a/src/server/api/parameters_parser.cpp +++ b/src/server/api/parameters_parser.cpp @@ -20,15 +20,22 @@ namespace api namespace detail { template -using is_grammar_t = std::integral_constant, T>::value || - std::is_same, T>::value || std::is_same, T>::value || - std::is_same, T>::value || std::is_same, T>::value || - std::is_same, T>::value>; - -template , T>::value || + std::is_same, T>::value || + std::is_same, T>::value || + std::is_same, T>::value || + std::is_same, T>::value || + std::is_same, T>::value || + std::is_same, T>::value>; + +template ::value, int>::type = 0, typename std::enable_if::value, int>::type = 0> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { using It = std::decay::type; @@ -37,7 +44,8 @@ boost::optional parseParameters(std::string::iterator &iter, const s try { ParameterT parameters; - const auto ok = boost::spirit::qi::parse(iter, end, grammar(boost::phoenix::ref(parameters))); + const auto ok = + boost::spirit::qi::parse(iter, end, grammar(boost::phoenix::ref(parameters))); // return move(a.b) is needed to move b out of a and then return the rvalue by implicit move if (ok && iter == end) @@ -55,37 +63,47 @@ boost::optional parseParameters(std::string::iterator &iter, const s } // ns detail template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { - return detail::parseParameters>(iter, end); + return detail::parseParameters>(iter, + end); } template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { - return detail::parseParameters>(iter, end); + return detail::parseParameters>(iter, + end); } template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { - return detail::parseParameters>(iter, end); + return detail::parseParameters>(iter, + end); } template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { return detail::parseParameters>(iter, end); } template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { - return detail::parseParameters>(iter, end); + return detail::parseParameters>(iter, + end); } template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { return detail::parseParameters>(iter, end); } @@ -93,7 +111,7 @@ template <> boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) { - return detail::parseParameters( + return detail::parseParameters>( iter, end); } diff --git a/src/server/service/isochrone_service.cpp b/src/server/service/isochrone_service.cpp index 7be0a6cdf2a..bef7394abe2 100644 --- a/src/server/service/isochrone_service.cpp +++ b/src/server/service/isochrone_service.cpp @@ -44,7 +44,7 @@ std::string getWrongOptionHelp(const engine::api::IsochroneParameters ¶meter } } // anon. ns -engine::Status IsochroneService::RunQuery(std::string &query, ResultT &result) +engine::Status IsochroneService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) { result = util::json::Object(); auto &json_result = result.get(); From 36c76c877129d0c3723acb7b41fd16b4b3e74839 Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 11 May 2016 11:12:30 +0200 Subject: [PATCH 23/43] fixed integer overflow bug in graham scan --- include/util/graham_scan.hpp | 63 +++++++++++++++----------------- src/engine/plugins/isochrone.cpp | 7 ++-- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/include/util/graham_scan.hpp b/include/util/graham_scan.hpp index c395bdfb2ac..f2c258748e7 100644 --- a/include/util/graham_scan.hpp +++ b/include/util/graham_scan.hpp @@ -32,15 +32,14 @@ std::uint64_t squaredEuclideanDistance(Node n1, Node n2) int orientation(Node p, Node q, Node r) { - int Q_lat = static_cast(q.node.lat); // b_x -a_x - int Q_lon= static_cast(q.node.lon); // b_x -a_x - int R_lat = static_cast(r.node.lat); // c_x - c_a - int R_lon = static_cast(r.node.lon); // c_x - c_a - int P_lat = static_cast(p.node.lat); // b_y - a_y - int P_lon = static_cast(p.node.lon); // c_y - a_y - - int val = (Q_lat - P_lat) * (R_lon - P_lon) - (Q_lon - P_lon) * (R_lat - P_lat); - + double Q_lat = static_cast(util::toFloating(q.node.lat)); // b_x -a_x + double Q_lon = static_cast(util::toFloating(q.node.lon)); // b_x -a_x + double R_lat = static_cast(util::toFloating(r.node.lat)); // c_x - c_a + double R_lon = static_cast(util::toFloating(r.node.lon)); // c_x - c_a + double P_lat = static_cast(util::toFloating(p.node.lat)); // b_y - a_y + double P_lon = static_cast(util::toFloating(p.node.lon)); // c_y - a_y + + double val = (Q_lat - P_lat) * (R_lon - P_lon) - (Q_lon - P_lon) * (R_lat - P_lat); if (val == 0) { return 0; // colinear @@ -87,27 +86,26 @@ void convexHull(engine::plugins::IsochroneSet &set, util::json::Object &response swap(points[0], *result); - std::sort(std::next(points.begin()), points.end(), NodeComparer(points[0])); -// points.push_back(*result); -// int m = 1; // Initialize size of modified array -// for (int i = 1; i < n; i++) -// { -// // Keep removing i while angle of i and i+1 is same -// // with respect to p0 -// while ((i < n - 1) && (orientation(points[0], points[i], points[i + 1]) == 0)) -// { -// i++; -// } -// -// points[m] = points[i]; -// m++; // Update size of modified array -// } - // If modified array of points has less than 3 points, - // convex hull is not possible -// if (m < 3) -// return; + points.push_back(*result); + int m = 1; // Initialize size of modified array + for (int i = 1; i < n; i++) + { + // Keep removing i while angle of i and i+1 is same + // with respect to p0 + while ((i < n - 1) && (orientation(points[0], points[i], points[i + 1]) == 0)) + { + i++; + } + + points[m] = points[i]; + m++; // Update size of modified array + } + // If modified array of points has less than 3 points, + // convex hull is not possible + if (m < 3) + return; // Create an empty stack and push first three points // to it. @@ -124,7 +122,7 @@ void convexHull(engine::plugins::IsochroneSet &set, util::json::Object &response // a non-left turn Node top = S.top(); S.pop(); - while (orientation(S.top(), top, points[i]) != 1 ) + while (orientation(S.top(), top, points[i]) != 1) { top = S.top(); S.pop(); @@ -134,7 +132,6 @@ void convexHull(engine::plugins::IsochroneSet &set, util::json::Object &response } // Now stack has the output points, print contents of stack - std::vector vec; while (!S.empty()) { @@ -143,11 +140,9 @@ void convexHull(engine::plugins::IsochroneSet &set, util::json::Object &response S.pop(); } -// std::sort(vec.begin(), vec.end(), [](Node a, Node b) { -// return b.node.lon + b.node.lat < a.node.lon + a.node.lat; -// }); util::json::Array borderjson; - for(auto p : vec) { + for (auto p : vec) + { util::json::Object point; point.values["lat"] = static_cast(util::toFloating(p.node.lat)); point.values["lon"] = static_cast(util::toFloating(p.node.lon)); diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index fda8e7a6e10..e7a5a692a3a 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -8,6 +8,7 @@ #include "engine/phantom_node.hpp" #include "util/simple_logger.hpp" #include +#include #include #include @@ -62,8 +63,6 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, }); auto phantom = phantomnodes.front(); - util::SimpleLogger().Write() << util::toFloating(phantom.front().phantom_node.location.lat); - util::SimpleLogger().Write() << util::toFloating(phantom.front().phantom_node.location.lon); std::vector forward_id_vector; facade.GetUncompressedGeometry(phantom.front().phantom_node.forward_packed_geometry_id, @@ -75,7 +74,7 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, // value is in metres // const int MAX = 2000; - const int MAX = 7500; + const int MAX = 10000; std::vector border; @@ -116,7 +115,6 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, } } - util::SimpleLogger().Write() << isochroneSet.size(); std::vector isoByDistance(isochroneSet.begin(), isochroneSet.end()); std::sort(isoByDistance.begin(), isoByDistance.end(),[] (IsochroneNode n1, IsochroneNode n2){ return n1.distance < n2.distance; @@ -148,6 +146,7 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, json_result.values["isochrone"] = std::move(data); util::convexHull(isochroneSet, json_result); +// util::monotoneChain(isoByDistance, json_result); return Status::Ok; } From e1114241050842345171e0e9c4d4d37a179a316f Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 11 May 2016 11:13:32 +0200 Subject: [PATCH 24/43] added Andrew's monotone chain convex hull algorithm --- include/util/monotone_chain.hpp | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 include/util/monotone_chain.hpp diff --git a/include/util/monotone_chain.hpp b/include/util/monotone_chain.hpp new file mode 100644 index 00000000000..45bdcede941 --- /dev/null +++ b/include/util/monotone_chain.hpp @@ -0,0 +1,92 @@ +// +// Created by robin on 5/11/16. +// + +#ifndef OSRM_MONOTONE_CHAIN_HPP +#define OSRM_MONOTONE_CHAIN_HPP + +#include +#include +#include +#include + +#include +#include +#include + +namespace osrm +{ +namespace util +{ +using Node = engine::plugins::IsochroneNode; + +int ccw(Node &p, Node &q, Node &r) +{ + //Using double values to avoid integer overflows + double Q_lat = static_cast(util::toFloating(q.node.lat)); + double Q_lon = static_cast(util::toFloating(q.node.lon)); + double R_lat = static_cast(util::toFloating(r.node.lat)); + double R_lon = static_cast(util::toFloating(r.node.lon)); + double P_lat = static_cast(util::toFloating(p.node.lat)); + double P_lon = static_cast(util::toFloating(p.node.lon)); + + double val = (Q_lat - P_lat) * (R_lon - P_lon) - (Q_lon - P_lon) * (R_lat - P_lat); + if (val == 0) + { + return 0; // colinear + } + return (val < 0) ? -1 : 1; // clock or counterclock wise +} + +void monotoneChain(std::vector &P, util::json::Object &response) +{ + int n = P.size(), k = 0; + std::vector H(2 * n); + + // sort Points + std::sort(P.begin(), P.end(), [&](const Node &a, const Node &b) + { + return a.node.lat < b.node.lat || + (a.node.lat == b.node.lat && a.node.lon < b.node.lon); + }); + + for(int i = 1; i < n; i++) { + if(P[i-1].node.lat > P[i].node.lat) { + util::SimpleLogger().Write() << "FFFUUUU"; + } + if(P[i-1].node.lat == P[i].node.lat && P[i-1].node.lon > P[i].node.lon) { + util::SimpleLogger().Write() << "FFFUUUU2"; + } + } + + // Build lower hull + for (int i = 0; i < n; ++i) + { + while (k >= 2 && ccw(H[k - 2], H[k - 1], P[i]) <= 0) + k--; + H[k++] = P[i]; + } + + // Build upper hull + for (int i = n - 2, t = k + 1; i >= 0; i--) + { + while (k >= t && ccw(H[k - 2], H[k - 1], P[i]) <= 0) + k--; + H[k++] = P[i]; + } + + H.resize(k - 1); + + util::json::Array borderjson; + for (Node p : H) + { + util::json::Object point; + point.values["lat"] = static_cast(util::toFloating(p.node.lat)); + point.values["lon"] = static_cast(util::toFloating(p.node.lon)); + borderjson.values.push_back(point); + } + response.values["border"] = std::move(borderjson); +} +} +} +#endif // OSRM_MONOTONE_CHAIN_HPP From a22d1695d83ebeb13ca630457ac85c48aa0aedbb Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 12 May 2016 11:31:14 +0200 Subject: [PATCH 25/43] added distance param to grammar --- include/engine/api/isochrone_parameters.hpp | 5 +- include/engine/plugins/range_analysis.hpp | 140 ------------------ .../api/isochrone_parameter_grammar.hpp | 6 +- src/engine/plugins/isochrone.cpp | 12 +- src/server/service/isochrone_service.cpp | 2 +- 5 files changed, 13 insertions(+), 152 deletions(-) delete mode 100644 include/engine/plugins/range_analysis.hpp diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp index 9ae5e9778b2..31246f40442 100644 --- a/include/engine/api/isochrone_parameters.hpp +++ b/include/engine/api/isochrone_parameters.hpp @@ -16,12 +16,11 @@ namespace api struct IsochroneParameters : public BaseParameters { - unsigned number_of_results = 1; - unsigned distance = 1000; + unsigned int distance; bool IsValid() const { - return BaseParameters::IsValid() && number_of_results >= 1; + return BaseParameters::IsValid(); } }; } diff --git a/include/engine/plugins/range_analysis.hpp b/include/engine/plugins/range_analysis.hpp deleted file mode 100644 index 04533cbe318..00000000000 --- a/include/engine/plugins/range_analysis.hpp +++ /dev/null @@ -1,140 +0,0 @@ -// -// Created by robin on 3/10/16. -// - -#ifndef OSRM_TEST_PLUGIN_HPP -#define OSRM_TEST_PLUGIN_HPP - -#include "engine/plugins/plugin_base.hpp" -#include "engine/search_engine.hpp" -#include "engine/object_encoder.hpp" - -#include "util/make_unique.hpp" -#include "util/string_util.hpp" -#include "util/simple_logger.hpp" -#include "osrm/json_container.hpp" - -#include - -#include -#include -#include -#include - -namespace osrm -{ -namespace engine -{ -namespace plugins -{ - -template class RangeAnalysis final : public BasePlugin -{ - - using PredecessorMap = std::unordered_map; - using DistanceMap = std::unordered_map; - - private: - std::string temp_string; - std::string descriptor_string; - std::unique_ptr> search_engine_ptr; - DataFacadeT *facade; - PredecessorMap predecessorMap; - DistanceMap distanceMap; - - public: - explicit RangeAnalysis(DataFacadeT *facade) : descriptor_string("range"), facade(facade) - { - search_engine_ptr = util::make_unique>(facade); - } - - virtual ~RangeAnalysis() {} - - const std::string GetDescriptor() const override final { return descriptor_string; } - - Status HandleRequest(const RouteParameters &routeParameters, - util::json::Object &json_result) override final - { - /* Check if valid - * - */ - if (routeParameters.coordinates.size() != 1) - { - json_result.values["status_message"] = "Number of Coordinates should be 1"; - return Status::Error; - } - if (!routeParameters.coordinates.front().IsValid()) - { - json_result.values["status_message"] = "Coordinate is invalid"; - return Status::Error; - } - - const auto &input_bearings = routeParameters.bearings; - if (input_bearings.size() > 0 && - routeParameters.coordinates.size() != input_bearings.size()) - { - json_result.values["status_message"] = - "Number of bearings does not match number of coordinate"; - return Status::Error; - } - - /** Interesting code starts here - * - */ - - // auto number_of_results = static_cast(routeParameters.num_results); - const int bearing = input_bearings.size() > 0 ? input_bearings.front().first : 0; - const int range = - input_bearings.size() > 0 - ? (input_bearings.front().second ? *input_bearings.front().second : 10) - : 180; - - std::vector phantomNodeVector; - auto phantomNodePair = facade->NearestPhantomNodeWithAlternativeFromBigComponent( - routeParameters.coordinates.front(), bearing, range); - - phantomNodeVector.push_back(phantomNodePair); - auto snapped_source_phantoms = snapPhantomNodes(phantomNodeVector); - search_engine_ptr->oneToMany(snapped_source_phantoms.front(), 10000, predecessorMap, - distanceMap); - - BOOST_ASSERT(predecessorMap.size() == distanceMap.size()); - - std::string temp_string; - json_result.values["title"] = "Range Analysis"; - - json_result.values["start"] = routeParameters.coordinates[0].lat; - - util::json::Array data; - for (auto it = predecessorMap.begin(); it != predecessorMap.end(); ++it) - { - util::json::Object object; - - util::json::Object source; - FixedPointCoordinate sourceCoordinate = facade->GetCoordinateOfNode(it->first); - source.values["lat"] = sourceCoordinate.lat / COORDINATE_PRECISION; - source.values["lon"] = sourceCoordinate.lon / COORDINATE_PRECISION; - object.values["p1"] = std::move(source); - - util::json::Object predecessor; - FixedPointCoordinate destinationSource = facade->GetCoordinateOfNode(it->second); - predecessor.values["lat"] = destinationSource.lat / COORDINATE_PRECISION; - predecessor.values["lon"] = destinationSource.lon / COORDINATE_PRECISION; - object.values["p2"] = std::move(predecessor); - - util::json::Object distance; - object.values["distance_from_start"] = distanceMap[it->first]; - - data.values.push_back(object); - } - temp_string = std::to_string(distanceMap.size()); - json_result.values["Nodes Found"] = temp_string;; - json_result.values["Range-Analysis"] = std::move(data); - - return Status::Ok; - } -}; -} -} -} -#endif // OSRM_TEST_PLUGIN_HPP diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp index 09b6ffab766..8ce0d42ea29 100644 --- a/include/server/api/isochrone_parameter_grammar.hpp +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -32,15 +32,13 @@ struct IsochroneParametersGrammar final : public BaseParametersGrammar - qi::uint_)[ph::bind(&engine::api::IsochroneParameters::number_of_results, + isochrone_rule = (qi::lit("distance=") > + qi::uint_)[ph::bind(&engine::api::IsochroneParameters::distance, qi::_r1) = qi::_1]; root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") > -('?' > (isochrone_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&'); } - // - // engine::api::IsochroneParameters parameters; private: qi::rule root_rule; diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index e7a5a692a3a..c0c633c733d 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -47,7 +47,7 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, return Error("InvalidOptions", "Only one input coordinate is supported", json_result); } - auto phantomnodes = GetPhantomNodes(params, params.number_of_results); + auto phantomnodes = GetPhantomNodes(params, 1); if (phantomnodes.front().size() <= 0) { @@ -63,6 +63,7 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, }); auto phantom = phantomnodes.front(); + isochroneSet.clear(); std::vector forward_id_vector; facade.GetUncompressedGeometry(phantom.front().phantom_node.forward_packed_geometry_id, @@ -74,7 +75,9 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, // value is in metres // const int MAX = 2000; - const int MAX = 10000; + int MAX = params.distance; + util::SimpleLogger().Write() << MAX; + std::vector border; @@ -115,6 +118,7 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, } } + util::SimpleLogger().Write() << "Nodes Found: " << isochroneSet.size(); std::vector isoByDistance(isochroneSet.begin(), isochroneSet.end()); std::sort(isoByDistance.begin(), isoByDistance.end(),[] (IsochroneNode n1, IsochroneNode n2){ return n1.distance < n2.distance; @@ -145,8 +149,8 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, } json_result.values["isochrone"] = std::move(data); - util::convexHull(isochroneSet, json_result); -// util::monotoneChain(isoByDistance, json_result); +// util::convexHull(isochroneSet, json_result); + util::monotoneChain(isoByDistance, json_result); return Status::Ok; } diff --git a/src/server/service/isochrone_service.cpp b/src/server/service/isochrone_service.cpp index bef7394abe2..a0c6e034424 100644 --- a/src/server/service/isochrone_service.cpp +++ b/src/server/service/isochrone_service.cpp @@ -56,7 +56,7 @@ engine::Status IsochroneService::RunQuery(std::size_t prefix_length, std::string const auto position = std::distance(query.begin(), query_iterator); json_result.values["code"] = "InvalidQuery"; json_result.values["message"] = - "Query string malformed close to position " + std::to_string(position); + "Query string malformed close to position " + std::to_string(prefix_length + position); return engine::Status::Error; } BOOST_ASSERT(parameters); From 931281f62f43832762e50f1191b0abfaaf10f179 Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 19 May 2016 09:37:07 +0200 Subject: [PATCH 26/43] refactoring --- include/engine/api/isochrone_api.hpp | 42 +++++++++++-- include/engine/plugins/isochrone.hpp | 37 +++++------ include/util/monotone_chain.hpp | 25 ++------ src/engine/plugins/isochrone.cpp | 91 +++++++++++----------------- 4 files changed, 91 insertions(+), 104 deletions(-) diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp index 17162982271..afd136f64ff 100644 --- a/include/engine/api/isochrone_api.hpp +++ b/include/engine/api/isochrone_api.hpp @@ -7,6 +7,7 @@ #include "engine/api/base_api.hpp" #include "engine/api/isochrone_parameters.hpp" +#include "engine/plugins/isochrone.hpp" #include "engine/plugins/plugin_base.hpp" namespace osrm @@ -27,13 +28,44 @@ class IsochroneAPI final : public BaseAPI { } -// void MakeResponse(const std::vector> &phantom_nodes, -// util::json::Object &response) const -// { -// -// } + void MakeResponse(const std::vector isochroneNodes, + const std::vector convexhull, + util::json::Object &response) const + { + util::json::Array data; + for (auto isochrone : isochroneNodes) + { + util::json::Object object; + + util::json::Object source; + source.values["lat"] = static_cast(util::toFloating(isochrone.node.lat)); + source.values["lon"] = static_cast(util::toFloating(isochrone.node.lon)); + object.values["p1"] = std::move(source); + util::json::Object predecessor; + predecessor.values["lat"] = + static_cast(util::toFloating(isochrone.predecessor.lat)); + predecessor.values["lon"] = + static_cast(util::toFloating(isochrone.predecessor.lon)); + object.values["p2"] = std::move(predecessor); + util::json::Object distance; + object.values["distance_from_start"] = isochrone.distance; + + data.values.push_back(object); + } + response.values["isochrone"] = std::move(data); + + util::json::Array borderjson; + for (engine::plugins::IsochroneNode n : convexhull) + { + util::json::Object point; + point.values["lat"] = static_cast(util::toFloating(n.node.lat)); + point.values["lon"] = static_cast(util::toFloating(n.node.lon)); + borderjson.values.push_back(point); + } + response.values["border"] = std::move(borderjson); + } }; } } diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index 294f30d5092..f713d0d6814 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -7,24 +7,26 @@ #include "engine/plugins/plugin_base.hpp" #include "engine/api/isochrone_parameters.hpp" +#include "osrm/json_container.hpp" #include "util/static_graph.hpp" #include "util/coordinate.hpp" -#include "osrm/json_container.hpp" -#include +#include "util/binary_heap.hpp" -#include #include +#include #include #include #include +#include #include -#include #include +#include #include -#include #include -#include +#include + + namespace osrm { @@ -56,28 +58,20 @@ struct IsochroneNode osrm::extractor::QueryNode node; osrm::extractor::QueryNode predecessor; int distance; -}; -struct IsochroneNodeIdCompare -{ - bool operator()(const IsochroneNode &a, const IsochroneNode &b) const - { - return a.node.node_id < b.node.node_id; - } -}; -struct IsochroneNodeDistanceCompare -{ - bool operator()(const IsochroneNode &a, const IsochroneNode &b) const + + bool operator<(const IsochroneNode &n) const { - return a.distance < b.distance; + return node.node_id < n.node.node_id; } }; + + using SimpleGraph = util::StaticGraph; using SimpleEdge = SimpleGraph::InputEdge; using QueryHeap = osrm::util:: BinaryHeap>; -typedef std::set IsochroneSet; -typedef std::set IsochroneDistanceSet; +typedef std::set IsochroneSet; class IsochronePlugin final : public BasePlugin { @@ -85,7 +79,6 @@ class IsochronePlugin final : public BasePlugin boost::filesystem::path base; std::shared_ptr graph; std::vector coordinate_list; - IsochroneSet isochroneSet; std::vector graph_edge_list; std::size_t number_of_nodes; @@ -93,8 +86,8 @@ class IsochronePlugin final : public BasePlugin std::vector &coordinate_list, std::vector &graph_edge_list); + void dijkstra(IsochroneSet &set, NodeID &source, int distance); void update(IsochroneSet &s, IsochroneNode node); - void findBorder(IsochroneSet &isochroneSet, util::json::Object &response); public: explicit IsochronePlugin(datafacade::BaseDataFacade &facade, const std::string base); diff --git a/include/util/monotone_chain.hpp b/include/util/monotone_chain.hpp index 45bdcede941..ddb40419295 100644 --- a/include/util/monotone_chain.hpp +++ b/include/util/monotone_chain.hpp @@ -38,27 +38,20 @@ int ccw(Node &p, Node &q, Node &r) return (val < 0) ? -1 : 1; // clock or counterclock wise } -void monotoneChain(std::vector &P, util::json::Object &response) +std::vector monotoneChain(std::vector &nodes) { + std::vector P(nodes); int n = P.size(), k = 0; std::vector H(2 * n); - // sort Points + + // sort Points by lat and lon std::sort(P.begin(), P.end(), [&](const Node &a, const Node &b) { return a.node.lat < b.node.lat || (a.node.lat == b.node.lat && a.node.lon < b.node.lon); }); - for(int i = 1; i < n; i++) { - if(P[i-1].node.lat > P[i].node.lat) { - util::SimpleLogger().Write() << "FFFUUUU"; - } - if(P[i-1].node.lat == P[i].node.lat && P[i-1].node.lon > P[i].node.lon) { - util::SimpleLogger().Write() << "FFFUUUU2"; - } - } - // Build lower hull for (int i = 0; i < n; ++i) { @@ -77,15 +70,7 @@ void monotoneChain(std::vector &P, util::json::Object &response) H.resize(k - 1); - util::json::Array borderjson; - for (Node p : H) - { - util::json::Object point; - point.values["lat"] = static_cast(util::toFloating(p.node.lat)); - point.values["lon"] = static_cast(util::toFloating(p.node.lon)); - borderjson.values.push_back(point); - } - response.values["border"] = std::move(borderjson); + return H; } } } diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index c0c633c733d..ed07f07d934 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -2,17 +2,15 @@ // Created by robin on 4/13/16. // -#include "engine/plugins/isochrone.hpp" #include "engine/api/isochrone_api.hpp" -#include "util/graph_loader.hpp" #include "engine/phantom_node.hpp" +#include "engine/plugins/isochrone.hpp" +#include "util/graph_loader.hpp" #include "util/simple_logger.hpp" -#include -#include +#include "util/graham_scan.hpp" +#include "util/monotone_chain.hpp" -#include -#include -#include +//#include #include namespace osrm @@ -62,25 +60,38 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, return a.distance > b.distance; }); auto phantom = phantomnodes.front(); - - isochroneSet.clear(); - std::vector forward_id_vector; facade.GetUncompressedGeometry(phantom.front().phantom_node.forward_packed_geometry_id, forward_id_vector); auto source = forward_id_vector[phantom.front().phantom_node.fwd_segment_position]; - QueryHeap heap(number_of_nodes); - heap.Insert(source, 0, source); + IsochroneSet isochroneSet; + dijkstra(isochroneSet, source, params.distance); - // value is in metres -// const int MAX = 2000; - int MAX = params.distance; - util::SimpleLogger().Write() << MAX; + util::SimpleLogger().Write() << "Nodes Found: " << isochroneSet.size(); + std::vector isoByDistance(isochroneSet.begin(), isochroneSet.end()); + std::sort(isoByDistance.begin(), isoByDistance.end(), + [&](const IsochroneNode n1, const IsochroneNode n2) + { + return n1.distance < n2.distance; + }); + // util::convexHull(isochroneSet, json_result); + std::vector convexhull = util::monotoneChain(isoByDistance); - std::vector border; + api::IsochroneAPI isochroneAPI(facade, params); + isochroneAPI.MakeResponse(isoByDistance, convexhull, json_result); + + return Status::Ok; +} +void IsochronePlugin::dijkstra(IsochroneSet &isochroneSet, NodeID &source, int distance) +{ + + QueryHeap heap(number_of_nodes); + heap.Insert(source, 0, source); + + int MAX_DISTANCE = distance; { // Standard Dijkstra search, terminating when path length > MAX while (!heap.Empty()) @@ -97,63 +108,30 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, if (data.real) { int to_distance = distance + data.weight; - if (to_distance > MAX) + if (to_distance > MAX_DISTANCE) { continue; } else if (!heap.WasInserted(target)) { heap.Insert(target, to_distance, source); - isochroneSet.insert(IsochroneNode(coordinate_list[target], coordinate_list[source], to_distance)); + isochroneSet.insert(IsochroneNode( + coordinate_list[target], coordinate_list[source], to_distance)); } else if (to_distance < heap.GetKey(target)) { heap.GetData(target).parent = source; heap.DecreaseKey(target, to_distance); - update(isochroneSet, IsochroneNode(coordinate_list[target], coordinate_list[source], to_distance)); + update(isochroneSet, + IsochroneNode(coordinate_list[target], coordinate_list[source], + to_distance)); } } } } } } - - util::SimpleLogger().Write() << "Nodes Found: " << isochroneSet.size(); - std::vector isoByDistance(isochroneSet.begin(), isochroneSet.end()); - std::sort(isoByDistance.begin(), isoByDistance.end(),[] (IsochroneNode n1, IsochroneNode n2){ - return n1.distance < n2.distance; - }); - util::json::Array data; - for (auto isochrone : isoByDistance) - { - util::json::Object object; - - util::json::Object source; - source.values["lat"] = - static_cast(util::toFloating(isochrone.node.lat)); - source.values["lon"] = - static_cast(util::toFloating(isochrone.node.lon)); - object.values["p1"] = std::move(source); - - util::json::Object predecessor; - predecessor.values["lat"] = - static_cast(util::toFloating(isochrone.predecessor.lat)); - predecessor.values["lon"] = - static_cast(util::toFloating(isochrone.predecessor.lon)); - object.values["p2"] = std::move(predecessor); - - util::json::Object distance; - object.values["distance_from_start"] = isochrone.distance; - - data.values.push_back(object); - } - json_result.values["isochrone"] = std::move(data); - -// util::convexHull(isochroneSet, json_result); - util::monotoneChain(isoByDistance, json_result); - return Status::Ok; } - std::size_t IsochronePlugin::loadGraph(const std::string &path, std::vector &coordinate_list, std::vector &graph_edge_list) @@ -185,7 +163,6 @@ std::size_t IsochronePlugin::loadGraph(const std::string &path, { continue; } - // forward edge graph_edge_list.emplace_back(input_edge.source, input_edge.target, input_edge.weight, input_edge.forward); From 510585af70fb27857283a57868b369493c77fc06 Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 19 May 2016 14:25:56 +0200 Subject: [PATCH 27/43] added convexhull api param --- features/bicycle/range.feature | 18 -- include/engine/api/isochrone_api.hpp | 20 ++- include/engine/api/isochrone_parameters.hpp | 1 + .../api/isochrone_parameter_grammar.hpp | 10 +- include/util/graham_scan.hpp | 156 ------------------ src/engine/plugins/isochrone.cpp | 11 +- 6 files changed, 25 insertions(+), 191 deletions(-) delete mode 100644 features/bicycle/range.feature delete mode 100644 include/util/graham_scan.hpp diff --git a/features/bicycle/range.feature b/features/bicycle/range.feature deleted file mode 100644 index 87e4fd14355..00000000000 --- a/features/bicycle/range.feature +++ /dev/null @@ -1,18 +0,0 @@ -@range -Feature: Getting the range limited by Distance - - Background: - Given the profile "car" - - Scenario: Range - Given the node map - | a | b | c | - - And the ways - | nodes | - | ab | - | bc | - - When I request range I should get - |source | node | pred | - | a | b | c | \ No newline at end of file diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp index afd136f64ff..6f61313b5fc 100644 --- a/include/engine/api/isochrone_api.hpp +++ b/include/engine/api/isochrone_api.hpp @@ -55,16 +55,18 @@ class IsochroneAPI final : public BaseAPI data.values.push_back(object); } response.values["isochrone"] = std::move(data); - - util::json::Array borderjson; - for (engine::plugins::IsochroneNode n : convexhull) - { - util::json::Object point; - point.values["lat"] = static_cast(util::toFloating(n.node.lat)); - point.values["lon"] = static_cast(util::toFloating(n.node.lon)); - borderjson.values.push_back(point); + if(!convexhull.empty()) { + util::json::Array convexhullArray; + for (engine::plugins::IsochroneNode n : convexhull) + { + util::json::Object point; + point.values["lat"] = static_cast(util::toFloating(n.node.lat)); + point.values["lon"] = static_cast(util::toFloating(n.node.lon)); + convexhullArray.values.push_back(point); + } + response.values["convexhull"] = std::move(convexhullArray); } - response.values["border"] = std::move(borderjson); + } }; } diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp index 31246f40442..eb8099f748d 100644 --- a/include/engine/api/isochrone_parameters.hpp +++ b/include/engine/api/isochrone_parameters.hpp @@ -17,6 +17,7 @@ namespace api struct IsochroneParameters : public BaseParameters { unsigned int distance; + bool convexhull; bool IsValid() const { diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp index 8ce0d42ea29..7bed67799f2 100644 --- a/include/server/api/isochrone_parameter_grammar.hpp +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -32,17 +32,21 @@ struct IsochroneParametersGrammar final : public BaseParametersGrammar + distance_rule = (qi::lit("distance=") > qi::uint_)[ph::bind(&engine::api::IsochroneParameters::distance, qi::_r1) = qi::_1]; + convexhull_rule = (qi::lit("convexhull=") > + qi::bool_)[ph::bind(&engine::api::IsochroneParameters::convexhull, + qi::_r1) = qi::_1]; root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") > - -('?' > (isochrone_rule(qi::_r1) | BaseGrammar::base_rule(qi::_r1)) % '&'); + -('?' > (distance_rule(qi::_r1) | convexhull_rule(qi::_r1)) % '&'); } private: qi::rule root_rule; - qi::rule isochrone_rule; + qi::rule distance_rule; + qi::rule convexhull_rule; }; } } diff --git a/include/util/graham_scan.hpp b/include/util/graham_scan.hpp deleted file mode 100644 index f2c258748e7..00000000000 --- a/include/util/graham_scan.hpp +++ /dev/null @@ -1,156 +0,0 @@ -// -// Created by robin on 4/27/16. -// - -#ifndef OSRM_GRAHAM_SCAN_HPP -#define OSRM_GRAHAM_SCAN_HPP - -#include -#include -#include -#include - -#include -#include -#include - -namespace osrm -{ -namespace util -{ -using Node = engine::plugins::IsochroneNode; - -bool lon_compare(engine::plugins::IsochroneNode &a, engine::plugins::IsochroneNode &b) -{ - return b.node.lon == a.node.lon ? b.node.lat < a.node.lat : b.node.lon < a.node.lon; -} -std::uint64_t squaredEuclideanDistance(Node n1, Node n2) -{ - return util::coordinate_calculation::squaredEuclideanDistance( - Coordinate(n1.node.lon, n1.node.lat), Coordinate(n2.node.lon, n2.node.lat)); -} - -int orientation(Node p, Node q, Node r) -{ - double Q_lat = static_cast(util::toFloating(q.node.lat)); // b_x -a_x - double Q_lon = static_cast(util::toFloating(q.node.lon)); // b_x -a_x - double R_lat = static_cast(util::toFloating(r.node.lat)); // c_x - c_a - double R_lon = static_cast(util::toFloating(r.node.lon)); // c_x - c_a - double P_lat = static_cast(util::toFloating(p.node.lat)); // b_y - a_y - double P_lon = static_cast(util::toFloating(p.node.lon)); // c_y - a_y - - double val = (Q_lat - P_lat) * (R_lon - P_lon) - (Q_lon - P_lon) * (R_lat - P_lat); - if (val == 0) - { - return 0; // colinear - } - return (val < 0) ? -1 : 1; // clock or counterclock wise -} -struct NodeComparer -{ - const Node p0; // lowest Node - - explicit NodeComparer(Node &p0) : p0(p0){}; - - bool operator()(const Node &a, const Node &b) - { - int o = orientation(p0, a, b); - if (o == 0) - { - return (squaredEuclideanDistance(p0, a) < squaredEuclideanDistance(p0, b)); - } - return o == 1; - } -}; - -Node nextToTop(std::stack &S) -{ - Node p = S.top(); - S.pop(); - Node res = S.top(); - S.push(p); - return res; -} -void swap(Node &p1, Node &p2) -{ - Node temp = p1; - p1 = p2; - p2 = temp; -} -void convexHull(engine::plugins::IsochroneSet &set, util::json::Object &response) -{ - std::vector points(set.begin(), set.end()); - - auto result = std::min_element(points.begin(), points.end(), lon_compare); - int n = points.size(); - - swap(points[0], *result); - - std::sort(std::next(points.begin()), points.end(), NodeComparer(points[0])); - - points.push_back(*result); - int m = 1; // Initialize size of modified array - for (int i = 1; i < n; i++) - { - // Keep removing i while angle of i and i+1 is same - // with respect to p0 - while ((i < n - 1) && (orientation(points[0], points[i], points[i + 1]) == 0)) - { - i++; - } - - points[m] = points[i]; - m++; // Update size of modified array - } - // If modified array of points has less than 3 points, - // convex hull is not possible - if (m < 3) - return; - - // Create an empty stack and push first three points - // to it. - std::stack S; - S.push(points[0]); - S.push(points[1]); - S.push(points[2]); - - // Process remaining n-3 points - for (int i = 3; i < n; i++) - { - // Keep removing top while the angle formed by - // points next-to-top, top, and points[i] makes - // a non-left turn - Node top = S.top(); - S.pop(); - while (orientation(S.top(), top, points[i]) != 1) - { - top = S.top(); - S.pop(); - } - S.push(top); - S.push(points[i]); - } - - // Now stack has the output points, print contents of stack - std::vector vec; - while (!S.empty()) - { - Node p = S.top(); - vec.emplace_back(p); - S.pop(); - } - - util::json::Array borderjson; - for (auto p : vec) - { - util::json::Object point; - point.values["lat"] = static_cast(util::toFloating(p.node.lat)); - point.values["lon"] = static_cast(util::toFloating(p.node.lon)); - borderjson.values.push_back(point); - } - response.values["border"] = std::move(borderjson); -} -} -} - -#endif // OSRM_GRAHAM_SCAN_HPP diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index ed07f07d934..a6da2a8917c 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -6,11 +6,9 @@ #include "engine/phantom_node.hpp" #include "engine/plugins/isochrone.hpp" #include "util/graph_loader.hpp" -#include "util/simple_logger.hpp" -#include "util/graham_scan.hpp" #include "util/monotone_chain.hpp" +#include "util/simple_logger.hpp" -//#include #include namespace osrm @@ -75,9 +73,12 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, { return n1.distance < n2.distance; }); + std::vector convexhull; + + if(params.convexhull) { + convexhull = util::monotoneChain(isoByDistance); + } - // util::convexHull(isochroneSet, json_result); - std::vector convexhull = util::monotoneChain(isoByDistance); api::IsochroneAPI isochroneAPI(facade, params); isochroneAPI.MakeResponse(isoByDistance, convexhull, json_result); From 8ce8a3893efc1ac5f6d089d597a451c113355f1f Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 19 May 2016 14:29:12 +0200 Subject: [PATCH 28/43] code format --- include/engine/api/isochrone_api.hpp | 8 +- include/engine/api/isochrone_parameters.hpp | 5 +- .../server/api/base_parameters_grammar.hpp | 92 ++++++++++--------- .../api/isochrone_parameter_grammar.hpp | 12 +-- src/engine/plugins/isochrone.cpp | 4 +- 5 files changed, 60 insertions(+), 61 deletions(-) diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp index 6f61313b5fc..35a5c247fae 100644 --- a/include/engine/api/isochrone_api.hpp +++ b/include/engine/api/isochrone_api.hpp @@ -44,9 +44,9 @@ class IsochroneAPI final : public BaseAPI util::json::Object predecessor; predecessor.values["lat"] = - static_cast(util::toFloating(isochrone.predecessor.lat)); + static_cast(util::toFloating(isochrone.predecessor.lat)); predecessor.values["lon"] = - static_cast(util::toFloating(isochrone.predecessor.lon)); + static_cast(util::toFloating(isochrone.predecessor.lon)); object.values["p2"] = std::move(predecessor); util::json::Object distance; @@ -55,7 +55,8 @@ class IsochroneAPI final : public BaseAPI data.values.push_back(object); } response.values["isochrone"] = std::move(data); - if(!convexhull.empty()) { + if (!convexhull.empty()) + { util::json::Array convexhullArray; for (engine::plugins::IsochroneNode n : convexhull) { @@ -66,7 +67,6 @@ class IsochroneAPI final : public BaseAPI } response.values["convexhull"] = std::move(convexhullArray); } - } }; } diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp index eb8099f748d..a5d486e707c 100644 --- a/include/engine/api/isochrone_parameters.hpp +++ b/include/engine/api/isochrone_parameters.hpp @@ -19,10 +19,7 @@ struct IsochroneParameters : public BaseParameters unsigned int distance; bool convexhull; - - bool IsValid() const { - return BaseParameters::IsValid(); - } + bool IsValid() const { return BaseParameters::IsValid(); } }; } } diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp index cc10cf33412..48c6c30e406 100644 --- a/include/server/api/base_parameters_grammar.hpp +++ b/include/server/api/base_parameters_grammar.hpp @@ -75,7 +75,8 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar BaseParametersGrammar(qi::rule &root_rule) : BaseParametersGrammar::base_type(root_rule) { - const auto add_hint = [](engine::api::BaseParameters &base_parameters, const boost::optional &hint_string) + const auto add_hint = [](engine::api::BaseParameters &base_parameters, + const boost::optional &hint_string) { if (hint_string) { @@ -87,8 +88,9 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar } }; - const auto add_bearing = [](engine::api::BaseParameters &base_parameters, - boost::optional> bearing_range) + const auto add_bearing = + [](engine::api::BaseParameters &base_parameters, + boost::optional> bearing_range) { boost::optional bearing; if (bearing_range) @@ -103,48 +105,48 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar base64_char = qi::char_("a-zA-Z0-9--_="); unlimited_rule = qi::lit("unlimited")[qi::_val = std::numeric_limits::infinity()]; - bearing_rule - = (qi::short_ > ',' > qi::short_) - [qi::_val = ph::bind([](short bearing, short range) { - return osrm::engine::Bearing{bearing, range}; - }, qi::_1, qi::_2)] - ; - - location_rule - = (double_ > qi::lit(',') > double_) - [qi::_val = ph::bind([](double lon, double lat) { - return util::Coordinate(util::FixedLongitude(lon * COORDINATE_PRECISION), - util::FixedLatitude(lat * COORDINATE_PRECISION)); - }, qi::_1, qi::_2)] - ; - - polyline_rule - = qi::as_string[qi::lit("polyline(") > +polyline_chars > ')'] - [qi::_val = ph::bind([](const std::string &polyline) { - return engine::decodePolyline(polyline); - }, qi::_1)] - ; - - query_rule - = ((location_rule % ';') | polyline_rule) - [ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1] - ; - - radiuses_rule - = qi::lit("radiuses=") - > (-(qi::double_ | unlimited_rule) % ';') - [ph::bind(&engine::api::BaseParameters::radiuses, qi::_r1) = qi::_1] - ; - - hints_rule - = qi::lit("hints=") - > (-qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]])[ph::bind(add_hint, qi::_r1, qi::_1)] % ';' - ; - - bearings_rule - = qi::lit("bearings=") > - (-(qi::short_ > ',' > qi::short_))[ph::bind(add_bearing, qi::_r1, qi::_1)] % ';' - ; + bearing_rule = + (qi::short_ > ',' > qi::short_)[qi::_val = ph::bind( + [](short bearing, short range) + { + return osrm::engine::Bearing{bearing, range}; + }, + qi::_1, qi::_2)]; + + location_rule = (double_ > qi::lit(',') > + double_)[qi::_val = ph::bind( + [](double lon, double lat) + { + return util::Coordinate( + util::FixedLongitude(lon * COORDINATE_PRECISION), + util::FixedLatitude(lat * COORDINATE_PRECISION)); + }, + qi::_1, qi::_2)]; + + polyline_rule = qi::as_string[qi::lit("polyline(") > +polyline_chars > + ')'][qi::_val = ph::bind( + [](const std::string &polyline) + { + return engine::decodePolyline(polyline); + }, + qi::_1)]; + + query_rule = + ((location_rule % ';') | + polyline_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1]; + + radiuses_rule = qi::lit("radiuses=") > + (-(qi::double_ | unlimited_rule) % + ';')[ph::bind(&engine::api::BaseParameters::radiuses, qi::_r1) = qi::_1]; + + hints_rule = qi::lit("hints=") > + (-qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]])[ph::bind( + add_hint, qi::_r1, qi::_1)] % + ';'; + + bearings_rule = + qi::lit("bearings=") > + (-(qi::short_ > ',' > qi::short_))[ph::bind(add_bearing, qi::_r1, qi::_1)] % ';'; base_rule = radiuses_rule(qi::_r1) | hints_rule(qi::_r1) | bearings_rule(qi::_r1); } diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp index 7bed67799f2..d4011d41e17 100644 --- a/include/server/api/isochrone_parameter_grammar.hpp +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -32,12 +32,12 @@ struct IsochroneParametersGrammar final : public BaseParametersGrammar - qi::uint_)[ph::bind(&engine::api::IsochroneParameters::distance, - qi::_r1) = qi::_1]; - convexhull_rule = (qi::lit("convexhull=") > - qi::bool_)[ph::bind(&engine::api::IsochroneParameters::convexhull, - qi::_r1) = qi::_1]; + distance_rule = + (qi::lit("distance=") > + qi::uint_)[ph::bind(&engine::api::IsochroneParameters::distance, qi::_r1) = qi::_1]; + convexhull_rule = + (qi::lit("convexhull=") > + qi::bool_)[ph::bind(&engine::api::IsochroneParameters::convexhull, qi::_r1) = qi::_1]; root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") > -('?' > (distance_rule(qi::_r1) | convexhull_rule(qi::_r1)) % '&'); diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index a6da2a8917c..7421382e6d1 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -75,11 +75,11 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, }); std::vector convexhull; - if(params.convexhull) { + if (params.convexhull) + { convexhull = util::monotoneChain(isoByDistance); } - api::IsochroneAPI isochroneAPI(facade, params); isochroneAPI.MakeResponse(isoByDistance, convexhull, json_result); From 9f73b690a6b2d61501f89fd51215827aeee1cdab Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 25 May 2016 10:28:47 +0200 Subject: [PATCH 29/43] bug fixes and tests --- features/support/route.js | 14 ++++++++++- src/engine/plugins/isochrone.cpp | 43 ++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/features/support/route.js b/features/support/route.js index 74d27af682b..fa169d863b2 100644 --- a/features/support/route.js +++ b/features/support/route.js @@ -1,5 +1,6 @@ var Timeout = require('node-timeout'); var request = require('request'); +var util = require('util'); module.exports = function () { this.requestPath = (service, params, callback) => { @@ -77,7 +78,18 @@ module.exports = function () { return this.requestPath('nearest', params, callback); }; - this.requestTable = (waypoints, userParams, callback) => { + this.requestIsochrone = (node, userParams, callback) => { + var defaults = { + output: 'json' + }, + params = this.overwriteParams(defaults, userParams); + params.coordinates = [[node.lon, node.lat].join(',')]; + params.distance = 1000; + + return this.requestPath('isochrone', params, callback); + }; + + this.requestTable = (isochrone, userParams, callback) => { var defaults = { output: 'json' }, diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index 7421382e6d1..697ad1f6fa7 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -8,6 +8,7 @@ #include "util/graph_loader.hpp" #include "util/monotone_chain.hpp" #include "util/simple_logger.hpp" +#include "util/shmem_graph_loader.hpp" #include @@ -21,8 +22,17 @@ namespace plugins IsochronePlugin::IsochronePlugin(datafacade::BaseDataFacade &facade, const std::string base) : BasePlugin{facade}, base{base} { + // Prepares uncontracted Graph - number_of_nodes = loadGraph(base, coordinate_list, graph_edge_list); + if (!base.empty()) + { + number_of_nodes = loadGraph(base, coordinate_list, graph_edge_list); + } + else + { + std::string path = util::getFilePathFromShmem(); + number_of_nodes = loadGraph(path, coordinate_list, graph_edge_list); + } tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); graph = std::make_shared(number_of_nodes, graph_edge_list); @@ -50,19 +60,29 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, return Error("PhantomNode", "PhantomNode couldnt be found for coordinate", json_result); } + util::SimpleLogger().Write() << phantomnodes.front().front().phantom_node.location; // Find closest phantomnode - std::sort(phantomnodes.front().begin(), phantomnodes.front().end(), - [&](const osrm::engine::PhantomNodeWithDistance &a, - const osrm::engine::PhantomNodeWithDistance &b) - { - return a.distance > b.distance; - }); + // isnt closest node +// std::sort(phantomnodes.front().begin(), phantomnodes.front().end(), +// [&](const osrm::engine::PhantomNodeWithDistance &a, +// const osrm::engine::PhantomNodeWithDistance &b) +// { +// return a.distance < b.distance; +// }); + +// for(auto p : phantomnodes.front()) { +// util::SimpleLogger().Write() << p.phantom_node.location; +// } + auto phantom = phantomnodes.front(); + util::SimpleLogger().Write() << "ph" << phantom.size(); + std::vector forward_id_vector; - facade.GetUncompressedGeometry(phantom.front().phantom_node.forward_packed_geometry_id, + facade.GetUncompressedGeometry(phantom.front().phantom_node.reverse_packed_geometry_id, forward_id_vector); - auto source = forward_id_vector[phantom.front().phantom_node.fwd_segment_position]; - + auto source = forward_id_vector[0]; +// auto source = phantom.front().phantom_node.forward_segment_id; + util::SimpleLogger().Write() << "forvec" << forward_id_vector.size(); IsochroneSet isochroneSet; dijkstra(isochroneSet, source, params.distance); @@ -92,6 +112,9 @@ void IsochronePlugin::dijkstra(IsochroneSet &isochroneSet, NodeID &source, int d QueryHeap heap(number_of_nodes); heap.Insert(source, 0, source); + isochroneSet.insert(IsochroneNode(coordinate_list[source], coordinate_list[source], 0)); + util::SimpleLogger().Write() << coordinate_list[source].lon << " " + << coordinate_list[source].lat; int MAX_DISTANCE = distance; { // Standard Dijkstra search, terminating when path length > MAX From fad6726f6cd9baf27f235104d19ec51d7cf5e932 Mon Sep 17 00:00:00 2001 From: roroettg Date: Fri, 27 May 2016 14:38:53 +0200 Subject: [PATCH 30/43] testfixes --- features/step_definitions/range.rb | 118 ------------------ features/support/route.js | 2 +- features/support/route.rb | 190 ----------------------------- src/engine/plugins/isochrone.cpp | 7 +- 4 files changed, 2 insertions(+), 315 deletions(-) delete mode 100644 features/step_definitions/range.rb delete mode 100644 features/support/route.rb diff --git a/features/step_definitions/range.rb b/features/step_definitions/range.rb deleted file mode 100644 index 175341d036e..00000000000 --- a/features/step_definitions/range.rb +++ /dev/null @@ -1,118 +0,0 @@ -When /^I request range I should get$/ do |table| - reprocess - actual = [] - OSRMLoader.load(self,"#{contracted_file}.osrm") do - table.hashes.each_with_index do |row,ri| - source = find_node_by_name row['source'] #a - raise "*** unknown in-node '#{row['source']}" unless source - - node = find_node_by_name row['node'] #b - raise "*** unknown in-node '#{row['node']}" unless node - - pred = find_node_by_name row['pred'] #a - raise "*** unknown in-node '#{row['pred']}" unless pred - - # dist = row['distance'] - # raise "*** unknown in-node '#{row['distance']}" unless dist - - #print(source_node) - response = request_range source, @query_params - if response.code == "200" && response.body.empty? == false - json = JSON.parse response.body - print(json) - print("\n") - if json['status'] == 200 - size = json['Nodes Found'] - range = json['Range-Analysis'] - end - end - - # print(size + "\n") - # got = {'source' => row['source'], - # 'node' => p1, - # 'pred' => p2} - # 'distance' => retDis} - - print(range) - ok = false - for coordinate in range; - p1 = Array.new - p2 = Array.new - - - p1 << coordinate['p1']['lat'] - p1 << coordinate['p1']['lon'] - p2 << coordinate['p2']['lat'] - p2 << coordinate['p2']['lon'] - - got = {'source' => row['source'], - 'node' => p1, - 'pred' => p2} - - # print("test\n") - # print(p1) - # print("\n"); - # print(node) - # print("\n"); - # print("\n"); - - - if FuzzyMatch.match_location(p1, node) && FuzzyMatch.match_location(p2, pred) - key = 'node' - got[key] = row[key] - key = 'pred' - got[key] = row[key] - ok = true - elsif !FuzzyMatch.match_location(p1, node) && !FuzzyMatch.match_location(p2, pred) - key = 'node' - row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" - key = 'pred' - row[key] = "#{row[key]} [#{pred.lat},#{pred.lon}]" - end - end - - # row.keys.each do |key| - # if key=='node' - # if FuzzyMatch.match_location p1, node - # got[key] = row[key] - # ok = true - # else - # row[key] = "#{row[key]} [#{node.lat},#{node.lon}]" - # end - # end - # if key=='pred' - # if FuzzyMatch.match_location p2, pred - # got[key] = row[key] - # ok = true - # else - # row[key] = "#{row[key]} [#{pred.lat},#{pred.lon}]" - # end - # end - # if key=='distance' - # if retDis == dist - # got[key] = row[key] - # else - # row[key] = dist - # ok = false - # end - # end - # end - - # end - - - # print(p1) - # print(p2) - # print(node) - - - - unless ok - failed = { :attempt => 'range', :query => @query, :response => response } - log_fail row,got,[failed] - end - actual << got - end - end - table.diff! actual -end diff --git a/features/support/route.js b/features/support/route.js index ad54de253d8..9494140e813 100644 --- a/features/support/route.js +++ b/features/support/route.js @@ -84,7 +84,7 @@ module.exports = function () { }, params = this.overwriteParams(defaults, userParams); params.coordinates = [[node.lon, node.lat].join(',')]; - params.distance = 1000; + params.distance = 10000; return this.requestPath('isochrone', params, callback); }; diff --git a/features/support/route.rb b/features/support/route.rb deleted file mode 100644 index 6e3d1b17ae4..00000000000 --- a/features/support/route.rb +++ /dev/null @@ -1,190 +0,0 @@ -require 'net/http' - -HOST = "http://127.0.0.1:#{OSRM_PORT}" -DESTINATION_REACHED = 15 #OSRM instruction code - -def request_path service, params - uri = "#{HOST}/" + service - response = send_request uri, params - return response -end - -def request_url path - uri = URI.parse"#{HOST}/#{path}" - @query = uri.to_s - Timeout.timeout(OSRM_TIMEOUT) do - Net::HTTP.get_response uri - end -rescue Errno::ECONNREFUSED => e - raise "*** osrm-routed is not running." -rescue Timeout::Error - raise "*** osrm-routed did not respond." -end - -# Overwriters the default values in defaults. -# e.g. [[a, 1], [b, 2]], [[a, 5], [d, 10]] => [[a, 5], [b, 2], [d, 10]] -def overwrite_params defaults, other - merged = [] - defaults.each do |k,v| - idx = other.index { |p| p[0] == k } - if idx == nil then - merged << [k, v] - else - merged << [k, other[idx][1]] - end - end - other.each do |k,v| - if merged.index { |pair| pair[0] == k} == nil then - merged << [k, v] - end - end - - return merged -end - -def request_route waypoints, bearings, user_params - raise "*** number of bearings does not equal the number of waypoints" unless bearings.size == 0 || bearings.size == waypoints.size - - defaults = [['output','json'], ['instructions',true], ['alt',false]] - params = overwrite_params defaults, user_params - encoded_waypoint = waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } - if bearings.size > 0 - encoded_bearings = bearings.map { |b| ["b", b.to_s]} - parasm = params.concat encoded_waypoint.zip(encoded_bearings).flatten! 1 - else - params = params.concat encoded_waypoint - end - - return request_path "viaroute", params -end - -def request_nearest node, user_params - defaults = [['output', 'json']] - params = overwrite_params defaults, user_params - params << ["loc", "#{node.lat},#{node.lon}"] - - return request_path "nearest", params - end - -def request_range node, user_params - defaults = [['output', 'json']] - params = overwrite_params defaults, user_params - params << ["loc", "#{node.lat},#{node.lon}"] - - return request_path "isochrone", params -end - -def request_table waypoints, user_params - defaults = [['output', 'json']] - params = overwrite_params defaults, user_params - params = params.concat waypoints.map{ |w| [w[:type],"#{w[:coord].lat},#{w[:coord].lon}"] } - - return request_path "table", params -end - -def request_trip waypoints, user_params - defaults = [['output', 'json']] - params = overwrite_params defaults, user_params - params = params.concat waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } - - return request_path "trip", params -end - -def request_matching waypoints, timestamps, user_params - defaults = [['output', 'json']] - params = overwrite_params defaults, user_params - encoded_waypoint = waypoints.map{ |w| ["loc","#{w.lat},#{w.lon}"] } - if timestamps.size > 0 - encoded_timestamps = timestamps.map { |t| ["t", t.to_s]} - parasm = params.concat encoded_waypoint.zip(encoded_timestamps).flatten! 1 - else - params = params.concat encoded_waypoint - end - - return request_path "match", params -end - -def got_route? response - if response.code == "200" && !response.body.empty? - json = JSON.parse response.body - if json['status'] == 200 - return way_list( json['route_instructions']).empty? == false - end - end - return false -end - -def route_status response - if response.code == "200" && !response.body.empty? - json = JSON.parse response.body - return json['status'] - else - "HTTP #{response.code}" - end -end - -def extract_instruction_list instructions, index, postfix=nil - if instructions - instructions.reject { |r| r[0].to_s=="#{DESTINATION_REACHED}" }. - map { |r| r[index] }. - map { |r| (r=="" || r==nil) ? '""' : "#{r}#{postfix}" }. - join(',') - end -end - -def way_list instructions - extract_instruction_list instructions, 1 -end - -def compass_list instructions - extract_instruction_list instructions, 6 -end - -def bearing_list instructions - extract_instruction_list instructions, 7 -end - -def turn_list instructions - if instructions - types = { - 0 => :none, - 1 => :straight, - 2 => :slight_right, - 3 => :right, - 4 => :sharp_right, - 5 => :u_turn, - 6 => :sharp_left, - 7 => :left, - 8 => :slight_left, - 9 => :via, - 10 => :head, - 11 => :enter_roundabout, - 12 => :leave_roundabout, - 13 => :stay_roundabout, - 14 => :start_end_of_street, - 15 => :destination, - 16 => :name_changes, - 17 => :enter_contraflow, - 18 => :leave_contraflow - } - # replace instructions codes with strings - # "11-3" (enter roundabout and leave a 3rd exit) gets converted to "enter_roundabout-3" - instructions.map do |r| - r[0].to_s.gsub(/^\d*/) do |match| - types[match.to_i].to_s - end - end.join(',') - end -end - -def mode_list instructions - extract_instruction_list instructions, 8 -end - -def time_list instructions - extract_instruction_list instructions, 4, "s" -end - -def distance_list instructions - extract_instruction_list instructions, 2, "m" -end diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index 697ad1f6fa7..68c3b80ece1 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -8,7 +8,6 @@ #include "util/graph_loader.hpp" #include "util/monotone_chain.hpp" #include "util/simple_logger.hpp" -#include "util/shmem_graph_loader.hpp" #include @@ -28,11 +27,7 @@ IsochronePlugin::IsochronePlugin(datafacade::BaseDataFacade &facade, const std:: { number_of_nodes = loadGraph(base, coordinate_list, graph_edge_list); } - else - { - std::string path = util::getFilePathFromShmem(); - number_of_nodes = loadGraph(path, coordinate_list, graph_edge_list); - } + tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); graph = std::make_shared(number_of_nodes, graph_edge_list); From 17c06d2a36b394b0f7811a462f9ecaaa8451362b Mon Sep 17 00:00:00 2001 From: roroettg Date: Fri, 27 May 2016 15:44:12 +0200 Subject: [PATCH 31/43] implemented isochrone as plugin --- include/engine/engine_config.hpp | 1 + src/engine/engine.cpp | 5 +- src/server/service_handler.cpp | 6 +- src/tools/routed.cpp | 109 ++++++++++++++++++------------- 4 files changed, 73 insertions(+), 48 deletions(-) diff --git a/include/engine/engine_config.hpp b/include/engine/engine_config.hpp index eb627198c54..1c1c945cce4 100644 --- a/include/engine/engine_config.hpp +++ b/include/engine/engine_config.hpp @@ -66,6 +66,7 @@ struct EngineConfig final int max_locations_distance_table = -1; int max_locations_map_matching = -1; bool use_shared_memory = true; + bool use_isochrone = true; }; } } diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 95a60ada631..9bd7b9d9c7f 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -151,7 +151,10 @@ Engine::Engine(EngineConfig &config) trip_plugin = create(*query_data_facade, config.max_locations_trip); match_plugin = create(*query_data_facade, config.max_locations_map_matching); tile_plugin = create(*query_data_facade); - isochrone_plugin = create(*query_data_facade, config.storage_config.base.string()); + if(config.use_isochrone) { + isochrone_plugin = create(*query_data_facade, config.storage_config.base.string()); + } + } // make sure we deallocate the unique ptr at a position where we know the size of the plugins diff --git a/src/server/service_handler.cpp b/src/server/service_handler.cpp index a27ea37dce0..5c4fb6a6622 100644 --- a/src/server/service_handler.cpp +++ b/src/server/service_handler.cpp @@ -1,3 +1,4 @@ +#include #include "server/service_handler.hpp" #include "server/service/route_service.hpp" @@ -24,7 +25,10 @@ ServiceHandler::ServiceHandler(osrm::EngineConfig &config) : routing_machine(con service_map["trip"] = util::make_unique(routing_machine); service_map["match"] = util::make_unique(routing_machine); service_map["tile"] = util::make_unique(routing_machine); - service_map["isochrone"] = util::make_unique(routing_machine); + if(config.use_isochrone) { + service_map["isochrone"] = util::make_unique(routing_machine); + } + } engine::Status ServiceHandler::RunQuery(api::ParsedURL parsed_url, diff --git a/src/tools/routed.cpp b/src/tools/routed.cpp index d60868dd406..01aaca46c07 100644 --- a/src/tools/routed.cpp +++ b/src/tools/routed.cpp @@ -52,19 +52,19 @@ const static unsigned INIT_OK_DO_NOT_START_ENGINE = 1; const static unsigned INIT_FAILED = -1; // generate boost::program_options object for the routing part -inline unsigned -generateServerProgramOptions(const int argc, - const char *argv[], - boost::filesystem::path &base_path, - std::string &ip_address, - int &ip_port, - int &requested_num_threads, - bool &use_shared_memory, - bool &trial, - int &max_locations_trip, - int &max_locations_viaroute, - int &max_locations_distance_table, - int &max_locations_map_matching) +inline unsigned generateServerProgramOptions(const int argc, + const char *argv[], + boost::filesystem::path &base_path, + std::string &ip_address, + int &ip_port, + int &requested_num_threads, + bool &use_shared_memory, + bool &trial, + int &max_locations_trip, + int &max_locations_viaroute, + int &max_locations_distance_table, + int &max_locations_map_matching, + bool &use_isochrone) { using boost::program_options::value; using boost::filesystem::path; @@ -77,7 +77,7 @@ generateServerProgramOptions(const int argc, // declare a group of options that will be allowed on command line boost::program_options::options_description config_options("Configuration"); - config_options.add_options() // + config_options.add_options() // ("ip,i", value(&ip_address)->default_value("0.0.0.0"), "IP address") // ("port,p", value(&ip_port)->default_value(5000), @@ -87,6 +87,9 @@ generateServerProgramOptions(const int argc, ("shared-memory,s", value(&use_shared_memory)->implicit_value(true)->default_value(false), "Load data from shared memory") // + ("isochrone,I", + value(&use_isochrone)->implicit_value(true)->default_value(false), + "Load the isochrone plugin") // ("max-viaroute-size", value(&max_locations_viaroute)->default_value(500), "Max. locations supported in viaroute query") // ("max-trip-size", value(&max_locations_trip)->default_value(100), @@ -109,7 +112,7 @@ generateServerProgramOptions(const int argc, boost::program_options::options_description cmdline_options; cmdline_options.add(generic_options).add(config_options).add(hidden_options); - const auto* executable = argv[0]; + const auto *executable = argv[0]; boost::program_options::options_description visible_options( boost::filesystem::path(executable).filename().string() + " []"); visible_options.add(generic_options).add(config_options); @@ -146,7 +149,8 @@ generateServerProgramOptions(const int argc, } else if (use_shared_memory && option_variables.count("base")) { - util::SimpleLogger().Write(logWARNING) << "Shared memory settings conflict with path settings."; + util::SimpleLogger().Write(logWARNING) + << "Shared memory settings conflict with path settings."; } util::SimpleLogger().Write() << visible_options; @@ -164,10 +168,10 @@ int main(int argc, const char *argv[]) try EngineConfig config; boost::filesystem::path base_path; const unsigned init_result = generateServerProgramOptions( - argc, argv, base_path, ip_address, ip_port, requested_thread_num, - config.use_shared_memory, trial_run, config.max_locations_trip, - config.max_locations_viaroute, config.max_locations_distance_table, - config.max_locations_map_matching); + argc, argv, base_path, ip_address, ip_port, requested_thread_num, config.use_shared_memory, + trial_run, config.max_locations_trip, config.max_locations_viaroute, + config.max_locations_distance_table, config.max_locations_map_matching, + config.use_isochrone); if (init_result == INIT_OK_DO_NOT_START_ENGINE) { return EXIT_SUCCESS; @@ -180,7 +184,7 @@ int main(int argc, const char *argv[]) try { config.storage_config = storage::StorageConfig(base_path); } - if(!config.IsValid()) + if (!config.IsValid()) { if (base_path.empty() != config.use_shared_memory) { @@ -188,53 +192,65 @@ int main(int argc, const char *argv[]) try } else { - if(!boost::filesystem::is_regular_file(config.storage_config.ram_index_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.ram_index_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.ram_index_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.ram_index_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.file_index_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.file_index_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.file_index_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.file_index_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.hsgr_data_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.hsgr_data_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.hsgr_data_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.hsgr_data_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.nodes_data_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.nodes_data_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.nodes_data_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.nodes_data_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.edges_data_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.edges_data_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.edges_data_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.edges_data_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.core_data_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.core_data_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.core_data_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.core_data_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.geometries_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.geometries_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.geometries_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.geometries_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.timestamp_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.timestamp_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.timestamp_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.timestamp_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.datasource_names_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.datasource_names_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.datasource_names_path << " is not found"; + util::SimpleLogger().Write(logWARNING) + << config.storage_config.datasource_names_path << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.datasource_indexes_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.datasource_indexes_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.datasource_indexes_path << " is not found"; + util::SimpleLogger().Write(logWARNING) + << config.storage_config.datasource_indexes_path << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.names_data_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.names_data_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.names_data_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.names_data_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.properties_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.properties_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.properties_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.properties_path + << " is not found"; } } return EXIT_FAILURE; @@ -306,7 +322,8 @@ int main(int argc, const char *argv[]) try sigaddset(&wait_mask, SIGTERM); pthread_sigmask(SIG_BLOCK, &wait_mask, nullptr); util::SimpleLogger().Write() << "running and waiting for requests"; - if(std::getenv("SIGNAL_PARENT_WHEN_READY")) { + if (std::getenv("SIGNAL_PARENT_WHEN_READY")) + { kill(getppid(), SIGUSR1); } sigwait(&wait_mask, &sig); From e334402649244ca3533f03e544b01243131233f9 Mon Sep 17 00:00:00 2001 From: roroettg Date: Fri, 27 May 2016 16:54:43 +0200 Subject: [PATCH 32/43] refactoring --- include/engine/api/isochrone_api.hpp | 1 + include/engine/plugins/isochrone.hpp | 11 ++---- src/engine/plugins/isochrone.cpp | 58 +++++++++------------------- 3 files changed, 23 insertions(+), 47 deletions(-) diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp index 35a5c247fae..53e1a7f1474 100644 --- a/include/engine/api/isochrone_api.hpp +++ b/include/engine/api/isochrone_api.hpp @@ -55,6 +55,7 @@ class IsochroneAPI final : public BaseAPI data.values.push_back(object); } response.values["isochrone"] = std::move(data); + if (!convexhull.empty()) { util::json::Array convexhullArray; diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index f713d0d6814..2c54e649fa8 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -58,11 +58,6 @@ struct IsochroneNode osrm::extractor::QueryNode node; osrm::extractor::QueryNode predecessor; int distance; - - bool operator<(const IsochroneNode &n) const - { - return node.node_id < n.node.node_id; - } }; @@ -71,7 +66,7 @@ using SimpleGraph = util::StaticGraph; using SimpleEdge = SimpleGraph::InputEdge; using QueryHeap = osrm::util:: BinaryHeap>; -typedef std::set IsochroneSet; +typedef std::vector IsochroneVector; class IsochronePlugin final : public BasePlugin { @@ -86,8 +81,8 @@ class IsochronePlugin final : public BasePlugin std::vector &coordinate_list, std::vector &graph_edge_list); - void dijkstra(IsochroneSet &set, NodeID &source, int distance); - void update(IsochroneSet &s, IsochroneNode node); + void dijkstra(IsochroneVector &set, NodeID &source, int distance); + void update(IsochroneVector &s, IsochroneNode node); public: explicit IsochronePlugin(datafacade::BaseDataFacade &facade, const std::string base); diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index 68c3b80ece1..e81d73fde79 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -22,13 +22,12 @@ IsochronePlugin::IsochronePlugin(datafacade::BaseDataFacade &facade, const std:: : BasePlugin{facade}, base{base} { - // Prepares uncontracted Graph + // Loads Graph into memory if (!base.empty()) { number_of_nodes = loadGraph(base, coordinate_list, graph_edge_list); } - tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end()); graph = std::make_shared(number_of_nodes, graph_edge_list); graph_edge_list.clear(); @@ -55,59 +54,42 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, return Error("PhantomNode", "PhantomNode couldnt be found for coordinate", json_result); } - util::SimpleLogger().Write() << phantomnodes.front().front().phantom_node.location; - // Find closest phantomnode - // isnt closest node -// std::sort(phantomnodes.front().begin(), phantomnodes.front().end(), -// [&](const osrm::engine::PhantomNodeWithDistance &a, -// const osrm::engine::PhantomNodeWithDistance &b) -// { -// return a.distance < b.distance; -// }); - -// for(auto p : phantomnodes.front()) { -// util::SimpleLogger().Write() << p.phantom_node.location; -// } - auto phantom = phantomnodes.front(); - util::SimpleLogger().Write() << "ph" << phantom.size(); - std::vector forward_id_vector; facade.GetUncompressedGeometry(phantom.front().phantom_node.reverse_packed_geometry_id, forward_id_vector); auto source = forward_id_vector[0]; -// auto source = phantom.front().phantom_node.forward_segment_id; - util::SimpleLogger().Write() << "forvec" << forward_id_vector.size(); - IsochroneSet isochroneSet; - dijkstra(isochroneSet, source, params.distance); - - util::SimpleLogger().Write() << "Nodes Found: " << isochroneSet.size(); - std::vector isoByDistance(isochroneSet.begin(), isochroneSet.end()); - std::sort(isoByDistance.begin(), isoByDistance.end(), + + IsochroneVector isochroneVector; + dijkstra(isochroneVector, source, params.distance); + + util::SimpleLogger().Write() << "Nodes Found: " << isochroneVector.size(); + std::sort(isochroneVector.begin(), isochroneVector.end(), [&](const IsochroneNode n1, const IsochroneNode n2) { return n1.distance < n2.distance; }); std::vector convexhull; + // Optional param for calculating Convex Hull if (params.convexhull) { - convexhull = util::monotoneChain(isoByDistance); + convexhull = util::monotoneChain(isochroneVector); } api::IsochroneAPI isochroneAPI(facade, params); - isochroneAPI.MakeResponse(isoByDistance, convexhull, json_result); + isochroneAPI.MakeResponse(isochroneVector, convexhull, json_result); return Status::Ok; } -void IsochronePlugin::dijkstra(IsochroneSet &isochroneSet, NodeID &source, int distance) +void IsochronePlugin::dijkstra(IsochroneVector &isochroneSet, NodeID &source, int distance) { QueryHeap heap(number_of_nodes); heap.Insert(source, 0, source); - isochroneSet.insert(IsochroneNode(coordinate_list[source], coordinate_list[source], 0)); + isochroneSet.emplace_back(IsochroneNode(coordinate_list[source], coordinate_list[source], 0)); util::SimpleLogger().Write() << coordinate_list[source].lon << " " << coordinate_list[source].lat; int MAX_DISTANCE = distance; @@ -134,7 +116,7 @@ void IsochronePlugin::dijkstra(IsochroneSet &isochroneSet, NodeID &source, int d else if (!heap.WasInserted(target)) { heap.Insert(target, to_distance, source); - isochroneSet.insert(IsochroneNode( + isochroneSet.emplace_back(IsochroneNode( coordinate_list[target], coordinate_list[source], to_distance)); } else if (to_distance < heap.GetKey(target)) @@ -193,16 +175,14 @@ std::size_t IsochronePlugin::loadGraph(const std::string &path, return number_of_nodes; } -void IsochronePlugin::update(IsochroneSet &s, IsochroneNode node) +void IsochronePlugin::update(IsochroneVector &v, IsochroneNode n) { - std::pair p = s.insert(node); - bool alreadyThere = !p.second; - if (alreadyThere) + for (auto node : v) { - IsochroneSet::iterator hint = p.first; - hint++; - s.erase(p.first); - s.insert(hint, node); + if (node.node.node_id == n.node.node_id) + { + node = n; + } } } } From 0ea7cde746a1529be439c87c38933755c294f249 Mon Sep 17 00:00:00 2001 From: roroettg Date: Fri, 27 May 2016 17:01:50 +0200 Subject: [PATCH 33/43] refactoring #2 --- include/engine/api/isochrone_api.hpp | 6 +----- include/engine/api/isochrone_parameters.hpp | 4 ---- include/engine/plugins/isochrone.hpp | 4 ---- include/engine/routing_algorithms/one_to_many.hpp | 4 ---- include/osrm/isochrone_parameters.hpp | 4 ---- include/server/api/isochrone_parameter_grammar.hpp | 4 ---- include/server/service/isochrone_service.hpp | 4 ---- include/util/monotone_chain.hpp | 4 ---- src/engine/plugins/isochrone.cpp | 4 ---- src/server/service/isochrone_service.cpp | 4 ---- 10 files changed, 1 insertion(+), 41 deletions(-) diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp index 53e1a7f1474..d3e2f7bef5c 100644 --- a/include/engine/api/isochrone_api.hpp +++ b/include/engine/api/isochrone_api.hpp @@ -1,7 +1,3 @@ -// -// Created by robin on 4/13/16. -// - #ifndef ENGINE_API_ISOCHRONE_HPP #define ENGINE_API_ISOCHRONE_HPP @@ -55,7 +51,7 @@ class IsochroneAPI final : public BaseAPI data.values.push_back(object); } response.values["isochrone"] = std::move(data); - + if (!convexhull.empty()) { util::json::Array convexhullArray; diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp index a5d486e707c..ccac27b7eb6 100644 --- a/include/engine/api/isochrone_parameters.hpp +++ b/include/engine/api/isochrone_parameters.hpp @@ -1,7 +1,3 @@ -// -// Created by robin on 4/13/16. -// - #ifndef ENGINE_API_ISOCHRONE_PARAMETERS_HPP #define ENGINE_API_ISOCHRONE_PARAMETERS_HPP diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index 2c54e649fa8..73e67b103ed 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -1,7 +1,3 @@ -// -// Created by robin on 4/6/16. -// - #ifndef ISOCHRONE_HPP #define ISOCHRONE_HPP diff --git a/include/engine/routing_algorithms/one_to_many.hpp b/include/engine/routing_algorithms/one_to_many.hpp index 4ab26b9db9c..7c16b7536ce 100644 --- a/include/engine/routing_algorithms/one_to_many.hpp +++ b/include/engine/routing_algorithms/one_to_many.hpp @@ -1,7 +1,3 @@ -// -// Created by robin on 3/16/16. -// - #ifndef OSRM_ONE_TO_MANY_HPP #define OSRM_ONE_TO_MANY_HPP diff --git a/include/osrm/isochrone_parameters.hpp b/include/osrm/isochrone_parameters.hpp index 03e141210af..dd196549507 100644 --- a/include/osrm/isochrone_parameters.hpp +++ b/include/osrm/isochrone_parameters.hpp @@ -1,7 +1,3 @@ -// -// Created by robin on 4/14/16. -// - #ifndef GLOBAL_ISOCHRONE_PARAMETERS_HPP #define GLOBAL_ISOCHRONE_PARAMETERS_HPP diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp index d4011d41e17..2473539efab 100644 --- a/include/server/api/isochrone_parameter_grammar.hpp +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -1,7 +1,3 @@ -// -// Created by robin on 4/13/16. -// - #ifndef OSRM_ISOCHRONE_PARAMETER_GRAMMAR_HPP #define OSRM_ISOCHRONE_PARAMETER_GRAMMAR_HPP diff --git a/include/server/service/isochrone_service.hpp b/include/server/service/isochrone_service.hpp index 5801c0770fe..d3102ff9119 100644 --- a/include/server/service/isochrone_service.hpp +++ b/include/server/service/isochrone_service.hpp @@ -1,7 +1,3 @@ -// -// Created by robin on 4/14/16. -// - #ifndef SERVER_SERVICE_ISOCHRONE_SERVICE_HPP #define SERVER_SERVICE_ISOCHRONE_SERVICE_HPP diff --git a/include/util/monotone_chain.hpp b/include/util/monotone_chain.hpp index ddb40419295..77b56d86b88 100644 --- a/include/util/monotone_chain.hpp +++ b/include/util/monotone_chain.hpp @@ -1,7 +1,3 @@ -// -// Created by robin on 5/11/16. -// - #ifndef OSRM_MONOTONE_CHAIN_HPP #define OSRM_MONOTONE_CHAIN_HPP diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index e81d73fde79..39510949183 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -1,7 +1,3 @@ -// -// Created by robin on 4/13/16. -// - #include "engine/api/isochrone_api.hpp" #include "engine/phantom_node.hpp" #include "engine/plugins/isochrone.hpp" diff --git a/src/server/service/isochrone_service.cpp b/src/server/service/isochrone_service.cpp index a0c6e034424..4f407539a71 100644 --- a/src/server/service/isochrone_service.cpp +++ b/src/server/service/isochrone_service.cpp @@ -1,7 +1,3 @@ -// -// Created by robin on 4/14/16. -// - #include "server/service/isochrone_service.hpp" #include "server/service/utils.hpp" From 4f8daac27c53786c1333e5b50af6d7206173a8cc Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 9 Jun 2016 15:02:22 +0200 Subject: [PATCH 34/43] added isochrone by distance or duration functionality --- include/engine/api/isochrone_api.hpp | 9 +- include/engine/api/isochrone_parameters.hpp | 5 +- include/engine/plugins/isochrone.hpp | 12 +- .../api/isochrone_parameter_grammar.hpp | 10 +- src/engine/plugins/isochrone.cpp | 131 +++++++++++++++--- 5 files changed, 137 insertions(+), 30 deletions(-) diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp index d3e2f7bef5c..b7d1546cab4 100644 --- a/include/engine/api/isochrone_api.hpp +++ b/include/engine/api/isochrone_api.hpp @@ -28,7 +28,7 @@ class IsochroneAPI final : public BaseAPI const std::vector convexhull, util::json::Object &response) const { - util::json::Array data; + util::json::Array isochroneJson; for (auto isochrone : isochroneNodes) { util::json::Object object; @@ -48,9 +48,12 @@ class IsochroneAPI final : public BaseAPI util::json::Object distance; object.values["distance_from_start"] = isochrone.distance; - data.values.push_back(object); + util::json::Object duration; + object.values["duration_from_start"] = isochrone.duration; + + isochroneJson.values.push_back(object); } - response.values["isochrone"] = std::move(data); + response.values["isochrone"] = std::move(isochroneJson); if (!convexhull.empty()) { diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp index ccac27b7eb6..12639342588 100644 --- a/include/engine/api/isochrone_parameters.hpp +++ b/include/engine/api/isochrone_parameters.hpp @@ -12,8 +12,9 @@ namespace api struct IsochroneParameters : public BaseParameters { - unsigned int distance; - bool convexhull; + unsigned int duration = 0; + double distance = 0; + bool convexhull = false; bool IsValid() const { return BaseParameters::IsValid(); } }; diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index 73e67b103ed..5d6630e284d 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -46,14 +46,15 @@ struct SimpleEdgeData struct IsochroneNode { IsochroneNode(){}; - IsochroneNode(osrm::extractor::QueryNode node, osrm::extractor::QueryNode predecessor, int distance) - : node(node), predecessor(predecessor), distance(distance) + IsochroneNode(osrm::extractor::QueryNode node, osrm::extractor::QueryNode predecessor, double distance, int duration) + : node(node), predecessor(predecessor), distance(distance), duration(duration) { } osrm::extractor::QueryNode node; osrm::extractor::QueryNode predecessor; - int distance; + double distance; + int duration; }; @@ -72,12 +73,15 @@ class IsochronePlugin final : public BasePlugin std::vector coordinate_list; std::vector graph_edge_list; std::size_t number_of_nodes; + IsochroneVector isochroneVector; + IsochroneVector convexhull; std::size_t loadGraph(const std::string &path, std::vector &coordinate_list, std::vector &graph_edge_list); - void dijkstra(IsochroneVector &set, NodeID &source, int distance); + void dijkstraByDuration(IsochroneVector &set, NodeID &source, int duration); + void dijkstraByDistance(IsochroneVector &isochroneSet, NodeID &source, double distance); void update(IsochroneVector &s, IsochroneNode node); public: diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp index 2473539efab..b0b4d6a0c6a 100644 --- a/include/server/api/isochrone_parameter_grammar.hpp +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -28,19 +28,23 @@ struct IsochroneParametersGrammar final : public BaseParametersGrammar - -('?' > (distance_rule(qi::_r1) | convexhull_rule(qi::_r1)) % '&'); + -('?' > (distance_rule(qi::_r1) | duration_rule(qi::_r1) | convexhull_rule(qi::_r1)) % '&'); } private: qi::rule root_rule; + qi::rule duration_rule; qi::rule distance_rule; qi::rule convexhull_rule; }; diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index 39510949183..6278183cf9b 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -1,9 +1,11 @@ #include "engine/api/isochrone_api.hpp" #include "engine/phantom_node.hpp" #include "engine/plugins/isochrone.hpp" +#include "util/coordinate_calculation.hpp" #include "util/graph_loader.hpp" #include "util/monotone_chain.hpp" #include "util/simple_logger.hpp" +#include "util/timing_util.hpp" #include @@ -43,6 +45,11 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, return Error("InvalidOptions", "Only one input coordinate is supported", json_result); } + if (params.distance != 0 && params.duration != 0) + { + return Error("InvalidOptions", "Only distance or duration should be set", json_result); + } + auto phantomnodes = GetPhantomNodes(params, 1); if (phantomnodes.front().size() <= 0) @@ -56,16 +63,42 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, forward_id_vector); auto source = forward_id_vector[0]; - IsochroneVector isochroneVector; - dijkstra(isochroneVector, source, params.distance); + isochroneVector.clear(); + + if (params.duration != 0) + { + TIMER_START(DIJKSTRA); + dijkstraByDuration(isochroneVector, source, params.duration); + TIMER_STOP(DIJKSTRA); + util::SimpleLogger().Write() << "DijkstraByDuration took: " << TIMER_MSEC(DIJKSTRA) << "ms"; + TIMER_START(SORTING); + std::sort(isochroneVector.begin(), isochroneVector.end(), + [&](const IsochroneNode n1, const IsochroneNode n2) + { + return n1.duration < n2.duration; + }); + TIMER_STOP(SORTING); + util::SimpleLogger().Write() << "SORTING took: " << TIMER_MSEC(SORTING) << "ms"; + } + if (params.distance != 0) + { + TIMER_START(DIJKSTRA); + dijkstraByDistance(isochroneVector, source, params.distance); + TIMER_STOP(DIJKSTRA); + util::SimpleLogger().Write() << "DijkstraByDistance took: " << TIMER_MSEC(DIJKSTRA) << "ms"; + TIMER_START(SORTING); + std::sort(isochroneVector.begin(), isochroneVector.end(), + [&](const IsochroneNode n1, const IsochroneNode n2) + { + return n1.distance < n2.distance; + }); + TIMER_STOP(SORTING); + util::SimpleLogger().Write() << "SORTING took: " << TIMER_MSEC(SORTING) << "ms"; + } util::SimpleLogger().Write() << "Nodes Found: " << isochroneVector.size(); - std::sort(isochroneVector.begin(), isochroneVector.end(), - [&](const IsochroneNode n1, const IsochroneNode n2) - { - return n1.distance < n2.distance; - }); - std::vector convexhull; + + convexhull.clear(); // Optional param for calculating Convex Hull if (params.convexhull) @@ -73,28 +106,84 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, convexhull = util::monotoneChain(isochroneVector); } + TIMER_START(RESPONSE); api::IsochroneAPI isochroneAPI(facade, params); isochroneAPI.MakeResponse(isochroneVector, convexhull, json_result); + TIMER_STOP(RESPONSE); + util::SimpleLogger().Write() << "RESPONSE took: " << TIMER_MSEC(RESPONSE) << "ms"; return Status::Ok; } -void IsochronePlugin::dijkstra(IsochroneVector &isochroneSet, NodeID &source, int distance) +void IsochronePlugin::dijkstraByDuration(IsochroneVector &isochroneSet, + NodeID &source, + int duration) { QueryHeap heap(number_of_nodes); heap.Insert(source, 0, source); - isochroneSet.emplace_back(IsochroneNode(coordinate_list[source], coordinate_list[source], 0)); - util::SimpleLogger().Write() << coordinate_list[source].lon << " " - << coordinate_list[source].lat; - int MAX_DISTANCE = distance; + isochroneSet.emplace_back( + IsochroneNode(coordinate_list[source], coordinate_list[source], 0, 0)); + + int MAX_DURATION = duration * 60 *10; { // Standard Dijkstra search, terminating when path length > MAX while (!heap.Empty()) { const NodeID source = heap.DeleteMin(); - const std::int32_t distance = heap.GetKey(source); + const std::int32_t weight = heap.GetKey(source); + + for (const auto current_edge : graph->GetAdjacentEdgeRange(source)) + { + const auto target = graph->GetTarget(current_edge); + if (target != SPECIAL_NODEID) + { + const auto data = graph->GetEdgeData(current_edge); + if (data.real) + { + int to_duration = weight + data.weight; + if (to_duration > MAX_DURATION) + { + continue; + } + else if (!heap.WasInserted(target)) + { + heap.Insert(target, to_duration, source); + isochroneSet.emplace_back(IsochroneNode( + coordinate_list[target], coordinate_list[source], 0, to_duration)); + } + else if (to_duration < heap.GetKey(target)) + { + heap.GetData(target).parent = source; + heap.DecreaseKey(target, to_duration); + update(isochroneSet, + IsochroneNode(coordinate_list[target], coordinate_list[source], + 0, to_duration)); + } + } + } + } + } + } +} +void IsochronePlugin::dijkstraByDistance(IsochroneVector &isochroneSet, + NodeID &source, + double distance) +{ + QueryHeap heap(number_of_nodes); + heap.Insert(source, 0, source); + + isochroneSet.emplace_back( + IsochroneNode(coordinate_list[source], coordinate_list[source], 0, 0)); + + int MAX_DISTANCE = distance; + { + // Standard Dijkstra search, terminating when path length > MAX + while (!heap.Empty()) + { + NodeID source = heap.DeleteMin(); + std::int32_t weight = heap.GetKey(source); for (const auto current_edge : graph->GetAdjacentEdgeRange(source)) { @@ -104,7 +193,14 @@ void IsochronePlugin::dijkstra(IsochroneVector &isochroneSet, NodeID &source, in const auto data = graph->GetEdgeData(current_edge); if (data.real) { - int to_distance = distance + data.weight; + Coordinate s(coordinate_list[source].lon, coordinate_list[source].lat); + Coordinate t(coordinate_list[target].lon, coordinate_list[target].lat); + //FIXME this might not be accurate enough + int to_distance = + static_cast( + util::coordinate_calculation::haversineDistance(s, t)) + + weight; + if (to_distance > MAX_DISTANCE) { continue; @@ -113,7 +209,7 @@ void IsochronePlugin::dijkstra(IsochroneVector &isochroneSet, NodeID &source, in { heap.Insert(target, to_distance, source); isochroneSet.emplace_back(IsochroneNode( - coordinate_list[target], coordinate_list[source], to_distance)); + coordinate_list[target], coordinate_list[source], to_distance, 0)); } else if (to_distance < heap.GetKey(target)) { @@ -121,7 +217,7 @@ void IsochronePlugin::dijkstra(IsochroneVector &isochroneSet, NodeID &source, in heap.DecreaseKey(target, to_distance); update(isochroneSet, IsochroneNode(coordinate_list[target], coordinate_list[source], - to_distance)); + to_distance, 0)); } } } @@ -149,7 +245,6 @@ std::size_t IsochronePlugin::loadGraph(const std::string &path, traffic_light_node_list, coordinate_list); util::loadEdgesFromFile(input_stream, edge_list); - traffic_light_node_list.clear(); traffic_light_node_list.shrink_to_fit(); From 71ed41b0232a9de3de1ca2bf334aa2e77cf1642f Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 23 Jun 2016 11:53:42 +0200 Subject: [PATCH 35/43] concave isochrones added --- include/engine/api/isochrone_api.hpp | 14 + include/engine/api/isochrone_parameters.hpp | 2 + include/engine/plugins/isochrone.hpp | 12 +- .../api/isochrone_parameter_grammar.hpp | 21 +- include/util/concave_hull.hpp | 273 ++++++++++++++++++ src/engine/plugins/isochrone.cpp | 12 +- 6 files changed, 324 insertions(+), 10 deletions(-) create mode 100644 include/util/concave_hull.hpp diff --git a/include/engine/api/isochrone_api.hpp b/include/engine/api/isochrone_api.hpp index b7d1546cab4..f4373dd7f5e 100644 --- a/include/engine/api/isochrone_api.hpp +++ b/include/engine/api/isochrone_api.hpp @@ -26,6 +26,7 @@ class IsochroneAPI final : public BaseAPI void MakeResponse(const std::vector isochroneNodes, const std::vector convexhull, + const std::vector concavehull, util::json::Object &response) const { util::json::Array isochroneJson; @@ -67,6 +68,19 @@ class IsochroneAPI final : public BaseAPI } response.values["convexhull"] = std::move(convexhullArray); } + + if (!concavehull.empty()) + { + util::json::Array concavehullArray; + for (engine::plugins::IsochroneNode n : concavehull) + { + util::json::Object point; + point.values["lat"] = static_cast(util::toFloating(n.node.lat)); + point.values["lon"] = static_cast(util::toFloating(n.node.lon)); + concavehullArray.values.push_back(point); + } + response.values["concavehull"] = std::move(concavehullArray); + } } }; } diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp index 12639342588..408caa5421b 100644 --- a/include/engine/api/isochrone_parameters.hpp +++ b/include/engine/api/isochrone_parameters.hpp @@ -15,6 +15,8 @@ struct IsochroneParameters : public BaseParameters unsigned int duration = 0; double distance = 0; bool convexhull = false; + bool concavehull = false; + unsigned int threshold = 1; // max precision bool IsValid() const { return BaseParameters::IsValid(); } }; diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index 5d6630e284d..eb67b7ba4ca 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -5,7 +5,6 @@ #include "engine/api/isochrone_parameters.hpp" #include "osrm/json_container.hpp" #include "util/static_graph.hpp" -#include "util/coordinate.hpp" #include "util/binary_heap.hpp" #include @@ -16,10 +15,7 @@ #include #include -#include -#include #include -#include #include @@ -55,6 +51,13 @@ struct IsochroneNode osrm::extractor::QueryNode predecessor; double distance; int duration; + + bool operator==(const IsochroneNode &n) const { + if (n.node.node_id == node.node_id) + return true; + else + return false; + } }; @@ -75,6 +78,7 @@ class IsochronePlugin final : public BasePlugin std::size_t number_of_nodes; IsochroneVector isochroneVector; IsochroneVector convexhull; + IsochroneVector concavehull; std::size_t loadGraph(const std::string &path, std::vector &coordinate_list, diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp index b0b4d6a0c6a..9719754ca0e 100644 --- a/include/server/api/isochrone_parameter_grammar.hpp +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -1,8 +1,8 @@ #ifndef OSRM_ISOCHRONE_PARAMETER_GRAMMAR_HPP #define OSRM_ISOCHRONE_PARAMETER_GRAMMAR_HPP -#include "engine/api/isochrone_parameters.hpp" #include "server/api/base_parameters_grammar.hpp" +#include "engine/api/isochrone_parameters.hpp" #include @@ -32,14 +32,23 @@ struct IsochroneParametersGrammar final : public BaseParametersGrammar - -('?' > (distance_rule(qi::_r1) | duration_rule(qi::_r1) | convexhull_rule(qi::_r1)) % '&'); + root_rule = + BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") > + -('?' > (distance_rule(qi::_r1) | duration_rule(qi::_r1) | convexhull_rule(qi::_r1) | + concavehull_rule(qi::_r1) | threshold_rule(qi::_r1)) % + '&'); } private: @@ -47,6 +56,8 @@ struct IsochroneParametersGrammar final : public BaseParametersGrammar duration_rule; qi::rule distance_rule; qi::rule convexhull_rule; + qi::rule concavehull_rule; + qi::rule threshold_rule; }; } } diff --git a/include/util/concave_hull.hpp b/include/util/concave_hull.hpp new file mode 100644 index 00000000000..83881367e88 --- /dev/null +++ b/include/util/concave_hull.hpp @@ -0,0 +1,273 @@ +#ifndef OSRM_CONCAVE_HULL_HPP +#define OSRM_CONCAVE_HULL_HPP + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace util +{ + +// Compute the dot product AB ⋅ BC +double dot(engine::plugins::IsochroneNode &A, + engine::plugins::IsochroneNode &B, + engine::plugins::IsochroneNode &C) +{ + double Ax = static_cast(util::toFloating(A.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double Ay = static_cast(util::toFloating(A.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double Bx = static_cast(util::toFloating(B.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double By = static_cast(util::toFloating(B.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double Cx = static_cast(util::toFloating(C.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double Cy = static_cast(util::toFloating(C.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + + double Axx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::cos(Ay); + double Ayy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::sin(Ay); + double Bxx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::cos(By); + double Byy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::sin(By); + double Cxx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::cos(Cy); + double Cyy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::sin(Cy); + auto AB = new double[2]; + auto BC = new double[2]; + AB[0] = Bxx - Axx; + AB[1] = Byy - Ayy; + BC[0] = Cxx - Bxx; + BC[1] = Cyy - Byy; + double dot = AB[0] * BC[0] + AB[1] * BC[1]; + return dot; +} +// Compute the cross product AB x AC +double cross(engine::plugins::IsochroneNode &A, + engine::plugins::IsochroneNode &B, + engine::plugins::IsochroneNode &C) +{ + // double Ax = static_cast(util::toFloating(A.node.lon)); + // double Ay = static_cast(util::toFloating(A.node.lat)); + // double Bx = static_cast(util::toFloating(B.node.lon)); + // double By = static_cast(util::toFloating(B.node.lat)); + // double Cx = static_cast(util::toFloating(C.node.lon)); + // double Cy = static_cast(util::toFloating(C.node.lat)); + + double Ax = static_cast(util::toFloating(A.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double Ay = static_cast(util::toFloating(A.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double Bx = static_cast(util::toFloating(B.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double By = static_cast(util::toFloating(B.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double Cx = static_cast(util::toFloating(C.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double Cy = static_cast(util::toFloating(C.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + + double Axx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::cos(Ay); + double Ayy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::sin(Ay); + double Bxx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::cos(By); + double Byy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::sin(By); + double Cxx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::cos(Cy); + double Cyy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::sin(Cy); + auto AB = new double[2]; + auto AC = new double[2]; + AB[0] = Bxx - Axx; + AB[1] = Byy - Ayy; + AC[0] = Cxx - Axx; + AC[1] = Cyy - Ayy; + double cross = AB[0] * AC[1] - AB[1] * AC[0]; + return cross; +} +// Compute the distance from A to B +double distance(engine::plugins::IsochroneNode &A, engine::plugins::IsochroneNode &B) +{ + // double Ax = static_cast(util::toFloating(A.node.lon)); + // double Ay = static_cast(util::toFloating(A.node.lat)); + // double Bx = static_cast(util::toFloating(B.node.lon)); + // double By = static_cast(util::toFloating(B.node.lat)); + double Ax = static_cast(util::toFloating(A.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double Ay = static_cast(util::toFloating(A.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double Bx = static_cast(util::toFloating(B.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + double By = static_cast(util::toFloating(B.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + + double Axx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::cos(Ay); + double Ayy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::sin(Ay); + double Bxx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::cos(By); + double Byy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::sin(By); + double d1 = Axx - Bxx; + double d2 = Ayy - Byy; + return std::sqrt(d1 * d1 + d2 * d2); +} + +// Compute the distance from AB to C +// if isSegment is true, AB is a segment, not a line. +double linePointDist(engine::plugins::IsochroneNode &A, + engine::plugins::IsochroneNode &B, + engine::plugins::IsochroneNode &C, + bool isSegment) +{ + double dist = cross(A, B, C) / distance(A, B); + // util::SimpleLogger().Write() << "dist " << dist; + if (isSegment) + { + double dot1 = dot(A, B, C); + // util::SimpleLogger().Write() << "dot " << dot1; + if (dot1 > 0) + { + // util::SimpleLogger().Write() << "dist " << distance(B, C); + return distance(B, C); + } + double dot2 = dot(B, A, C); + // util::SimpleLogger().Write() << "dot2 " << dot2; + if (dot2 > 0) + { + // util::SimpleLogger().Write() << "dist " << distance(A, C); + return distance(A, C); + } + } + return std::abs(dist); +} +double distanceToEdge(engine::plugins::IsochroneNode &p, + engine::plugins::IsochroneNode &e1, + engine::plugins::IsochroneNode &e2) +{ + // double pX = static_cast(util::toFloating(p.node.lon)); + // double pY = static_cast(util::toFloating(p.node.lat)); + // double vX = static_cast(util::toFloating(e1.node.lon)); + // double vY = static_cast(util::toFloating(e1.node.lat)); + // double wX = static_cast(util::toFloating(e2.node.lon)); + // double wY = static_cast(util::toFloating(e2.node.lat)); + const std::uint64_t pX = static_cast(p.node.lon); + const std::uint64_t pY = static_cast(p.node.lat); + const std::uint64_t vX = static_cast(e1.node.lon); + const std::uint64_t vY = static_cast(e1.node.lat); + const std::uint64_t wX = static_cast(e2.node.lon); + const std::uint64_t wY = static_cast(e2.node.lat); + + // double squaredEdgeLength = util::coordinate_calculation::haversineDistance( + // Coordinate(e1.node.lon, e1.node.lat), Coordinate(e2.node.lon, e2.node.lat)); + // if (squaredEdgeLength == 0) + // { + // // v==w + // return util::coordinate_calculation::haversineDistance( + // Coordinate(p.node.lon, p.node.lat), Coordinate(e1.node.lon, e1.node.lat)); + // } + // var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2; + // double t = + // (static_cast((p.node.lon - e1.node.lon) * (e2.node.lon - e1.node.lon)) + + // static_cast((p.node.lat - e1.node.lat) * (e2.node.lat - e2.node.lat))) + // / + // squaredEdgeLength; + + // double distance = std::abs((wX-vX)*(vY-pY) - (vX-pX)*(wY-vY)) / std::sqrt(std::pow(wX - + // vX, 2) + std::pow(wY -vY,2)); + double distance = std::abs((wX - vX) * (vY - pY) - (vX - pX) * (wY - vY)) / + std::sqrt(std::pow(wX - vX, 2) + std::pow(wY - vY, 2)); + return distance; + // double t = ((pX - vX) * (wX - vX) + (pY - vY) * (wY - vY)) / squaredEdgeLength; + // t = std::max(0.0, std::min(1.0, t)); + + // Coordinate tCoord(FloatLongitude(vX + t * (wX - vX)), FloatLatitude(vY + t * (wY - vY))); + + // return util::coordinate_calculation::haversineDistance( + // Coordinate(p.node.lon, p.node.lat), tCoord); +} + +std::vector +concavehull(std::vector &convexhull, + unsigned int threshold, + std::vector &isochrone) +{ + + std::vector concavehull(convexhull); + concavehull.push_back(concavehull[0]); + + for (std::vector::iterator it = concavehull.begin(); + it != concavehull.end() - 1; + ++it) + { + // find the nearest inner point pk ∈ G from the edge (ci1, ci2); + // pk should not closer to neighbor edges of (ci1, ci2) than (ci1, ci2) + engine::plugins::IsochroneNode n1 = *it; + engine::plugins::IsochroneNode n2 = *std::next(it); + engine::plugins::IsochroneNode pk; + double d = std::numeric_limits::max(); + + for (auto iso : isochrone) + { + if (std::find(concavehull.begin(), concavehull.end(), iso) != concavehull.end()) + { + continue; + } + // auto t = distanceToEdge(iso, n1, n2); + auto t = linePointDist(n1, n2, iso, true); + engine::plugins::IsochroneNode n3; + + // Test if point is closer to next Edge -> n2,n3 + if (std::next(it) == concavehull.end()) + { + n3 = *std::next(concavehull.begin()); + } + else + { + n3 = *std::next(it, 2); + } + auto t_next = linePointDist(n2, n3, iso, true); + + engine::plugins::IsochroneNode n0; + if (it == concavehull.begin()) // Start and End Point are the same (Avoid distance) + { + n0 = *std::prev(concavehull.end()); + } + else + { + n0 = *std::prev(it); + } + auto t_prev = linePointDist(n0, n1, iso, true); + + if (t_prev < t || t_next < t) + { + continue; // Distance of the point is closer to neighbour edge + } + if (d > t) + { + d = t; + pk = iso; + } + } + // double edgelength = util::coordinate_calculation::haversineDistance( + // Coordinate(n1.node.lon, n1.node.lat), Coordinate(n2.node.lon, n2.node.lat)); + double edgelength = distance(n1, n2); + // auto edgelength = + // std::sqrt(util::coordinate_calculation::squaredEuclideanDistance( + // Coordinate(n1.node.lon, n1.node.lat), Coordinate(n2.node.lon, n2.node.lat))); + + util::SimpleLogger().Write() << "Kante " << edgelength; + // util::SimpleLogger().Write() << "Kante " << edgelength2; + util::SimpleLogger().Write() << "distanz " << d; + if ((edgelength / d) > threshold) + { + it = concavehull.insert(std::next(it), pk); + it = std::prev(it); + } + } + return concavehull; +} +} +} +#endif // OSRM_CONCAVE_HULL_HPP diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index 6278183cf9b..afa764cdbbf 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -3,6 +3,7 @@ #include "engine/plugins/isochrone.hpp" #include "util/coordinate_calculation.hpp" #include "util/graph_loader.hpp" +#include "util/concave_hull.hpp" #include "util/monotone_chain.hpp" #include "util/simple_logger.hpp" #include "util/timing_util.hpp" @@ -50,6 +51,10 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, return Error("InvalidOptions", "Only distance or duration should be set", json_result); } + if (params.concavehull == true && params.convexhull == false) { + return Error("InvalidOptions", "If concavehull is set, convexhull must be set too", json_result); + } + auto phantomnodes = GetPhantomNodes(params, 1); if (phantomnodes.front().size() <= 0) @@ -99,16 +104,21 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, util::SimpleLogger().Write() << "Nodes Found: " << isochroneVector.size(); convexhull.clear(); + concavehull.clear(); // Optional param for calculating Convex Hull if (params.convexhull) { convexhull = util::monotoneChain(isochroneVector); + + } + if(params.concavehull && params.convexhull) { + concavehull = util::concavehull(convexhull, params.threshold, isochroneVector); } TIMER_START(RESPONSE); api::IsochroneAPI isochroneAPI(facade, params); - isochroneAPI.MakeResponse(isochroneVector, convexhull, json_result); + isochroneAPI.MakeResponse(isochroneVector, convexhull, concavehull, json_result); TIMER_STOP(RESPONSE); util::SimpleLogger().Write() << "RESPONSE took: " << TIMER_MSEC(RESPONSE) << "ms"; From 1cf81e11100725a97dea97bbfb3de6fec368eca7 Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 14 Jul 2016 15:37:17 +0200 Subject: [PATCH 36/43] fixed mem leak --- include/engine/api/isochrone_parameters.hpp | 2 +- .../api/isochrone_parameter_grammar.hpp | 2 +- include/util/concave_hull.hpp | 268 +++++++----------- src/engine/plugins/isochrone.cpp | 15 +- 4 files changed, 120 insertions(+), 167 deletions(-) diff --git a/include/engine/api/isochrone_parameters.hpp b/include/engine/api/isochrone_parameters.hpp index 408caa5421b..ef2708f6c89 100644 --- a/include/engine/api/isochrone_parameters.hpp +++ b/include/engine/api/isochrone_parameters.hpp @@ -16,7 +16,7 @@ struct IsochroneParameters : public BaseParameters double distance = 0; bool convexhull = false; bool concavehull = false; - unsigned int threshold = 1; // max precision + double threshold = 1; // max precision bool IsValid() const { return BaseParameters::IsValid(); } }; diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp index 9719754ca0e..3a6860756d3 100644 --- a/include/server/api/isochrone_parameter_grammar.hpp +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -42,7 +42,7 @@ struct IsochroneParametersGrammar final : public BaseParametersGrammar - qi::uint_)[ph::bind(&engine::api::IsochroneParameters::threshold, qi::_r1) = qi::_1]; + qi::double_)[ph::bind(&engine::api::IsochroneParameters::threshold, qi::_r1) = qi::_1]; root_rule = BaseGrammar::query_rule(qi::_r1) > -qi::lit(".json") > diff --git a/include/util/concave_hull.hpp b/include/util/concave_hull.hpp index 83881367e88..3124f04f899 100644 --- a/include/util/concave_hull.hpp +++ b/include/util/concave_hull.hpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -17,141 +18,135 @@ namespace util { // Compute the dot product AB ⋅ BC -double dot(engine::plugins::IsochroneNode &A, - engine::plugins::IsochroneNode &B, - engine::plugins::IsochroneNode &C) +double dot(const engine::plugins::IsochroneNode &A, + const engine::plugins::IsochroneNode &B, + const engine::plugins::IsochroneNode &C) { - double Ax = static_cast(util::toFloating(A.node.lon)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double Ay = static_cast(util::toFloating(A.node.lat)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double Bx = static_cast(util::toFloating(B.node.lon)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double By = static_cast(util::toFloating(B.node.lat)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double Cx = static_cast(util::toFloating(C.node.lon)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double Cy = static_cast(util::toFloating(C.node.lat)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - - double Axx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::cos(Ay); - double Ayy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::sin(Ay); - double Bxx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::cos(By); - double Byy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::sin(By); - double Cxx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::cos(Cy); - double Cyy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::sin(Cy); - auto AB = new double[2]; - auto BC = new double[2]; + const double Ax = static_cast(util::toFloating(A.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double Ay = static_cast(util::toFloating(A.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double Bx = static_cast(util::toFloating(B.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double By = static_cast(util::toFloating(B.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double Cx = static_cast(util::toFloating(C.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double Cy = static_cast(util::toFloating(C.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + + const double Axx = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::cos(Ay); + const double Ayy = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::sin(Ay); + const double Bxx = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::cos(By); + const double Byy = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::sin(By); + const double Cxx = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::cos(Cy); + const double Cyy = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::sin(Cy); + double AB[2]; + double BC[2]; AB[0] = Bxx - Axx; AB[1] = Byy - Ayy; BC[0] = Cxx - Bxx; BC[1] = Cyy - Byy; - double dot = AB[0] * BC[0] + AB[1] * BC[1]; + const double dot = AB[0] * BC[0] + AB[1] * BC[1]; return dot; } // Compute the cross product AB x AC -double cross(engine::plugins::IsochroneNode &A, - engine::plugins::IsochroneNode &B, - engine::plugins::IsochroneNode &C) +double cross(const engine::plugins::IsochroneNode &A, + const engine::plugins::IsochroneNode &B, + const engine::plugins::IsochroneNode &C) { - // double Ax = static_cast(util::toFloating(A.node.lon)); - // double Ay = static_cast(util::toFloating(A.node.lat)); - // double Bx = static_cast(util::toFloating(B.node.lon)); - // double By = static_cast(util::toFloating(B.node.lat)); - // double Cx = static_cast(util::toFloating(C.node.lon)); - // double Cy = static_cast(util::toFloating(C.node.lat)); - - double Ax = static_cast(util::toFloating(A.node.lon)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double Ay = static_cast(util::toFloating(A.node.lat)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double Bx = static_cast(util::toFloating(B.node.lon)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double By = static_cast(util::toFloating(B.node.lat)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double Cx = static_cast(util::toFloating(C.node.lon)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double Cy = static_cast(util::toFloating(C.node.lat)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - - double Axx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::cos(Ay); - double Ayy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::sin(Ay); - double Bxx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::cos(By); - double Byy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::sin(By); - double Cxx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::cos(Cy); - double Cyy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::sin(Cy); - auto AB = new double[2]; - auto AC = new double[2]; + const double Ax = static_cast(util::toFloating(A.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double Ay = static_cast(util::toFloating(A.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double Bx = static_cast(util::toFloating(B.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double By = static_cast(util::toFloating(B.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double Cx = static_cast(util::toFloating(C.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double Cy = static_cast(util::toFloating(C.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + + const double Axx = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::cos(Ay); + const double Ayy = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::sin(Ay); + const double Bxx = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::cos(By); + const double Byy = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::sin(By); + const double Cxx = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::cos(Cy); + const double Cyy = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Cx) * std::sin(Cy); + double AB[2]; + double AC[2]; AB[0] = Bxx - Axx; AB[1] = Byy - Ayy; AC[0] = Cxx - Axx; AC[1] = Cyy - Ayy; - double cross = AB[0] * AC[1] - AB[1] * AC[0]; + const double cross = AB[0] * AC[1] - AB[1] * AC[0]; return cross; } // Compute the distance from A to B -double distance(engine::plugins::IsochroneNode &A, engine::plugins::IsochroneNode &B) +double distance(const engine::plugins::IsochroneNode &A, const engine::plugins::IsochroneNode &B) { - // double Ax = static_cast(util::toFloating(A.node.lon)); - // double Ay = static_cast(util::toFloating(A.node.lat)); - // double Bx = static_cast(util::toFloating(B.node.lon)); - // double By = static_cast(util::toFloating(B.node.lat)); - double Ax = static_cast(util::toFloating(A.node.lon)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double Ay = static_cast(util::toFloating(A.node.lat)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double Bx = static_cast(util::toFloating(B.node.lon)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - double By = static_cast(util::toFloating(B.node.lat)) * - util::coordinate_calculation::detail::DEGREE_TO_RAD; - - double Axx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::cos(Ay); - double Ayy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::sin(Ay); - double Bxx = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::cos(By); - double Byy = util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::sin(By); - double d1 = Axx - Bxx; - double d2 = Ayy - Byy; + const double Ax = static_cast(util::toFloating(A.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double Ay = static_cast(util::toFloating(A.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double Bx = static_cast(util::toFloating(B.node.lon)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + const double By = static_cast(util::toFloating(B.node.lat)) * + util::coordinate_calculation::detail::DEGREE_TO_RAD; + + const double Axx = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::cos(Ay); + const double Ayy = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Ax) * std::sin(Ay); + const double Bxx = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::cos(By); + const double Byy = + util::coordinate_calculation::detail::EARTH_RADIUS * std::cos(Bx) * std::sin(By); + const double d1 = Axx - Bxx; + const double d2 = Ayy - Byy; return std::sqrt(d1 * d1 + d2 * d2); } // Compute the distance from AB to C // if isSegment is true, AB is a segment, not a line. -double linePointDist(engine::plugins::IsochroneNode &A, - engine::plugins::IsochroneNode &B, - engine::plugins::IsochroneNode &C, +double linePointDist(const engine::plugins::IsochroneNode &A, + const engine::plugins::IsochroneNode &B, + const engine::plugins::IsochroneNode &C, bool isSegment) { - double dist = cross(A, B, C) / distance(A, B); - // util::SimpleLogger().Write() << "dist " << dist; + const double dist = cross(A, B, C) / distance(A, B); if (isSegment) { - double dot1 = dot(A, B, C); - // util::SimpleLogger().Write() << "dot " << dot1; + const double dot1 = dot(A, B, C); if (dot1 > 0) { - // util::SimpleLogger().Write() << "dist " << distance(B, C); return distance(B, C); } - double dot2 = dot(B, A, C); - // util::SimpleLogger().Write() << "dot2 " << dot2; + const double dot2 = dot(B, A, C); if (dot2 > 0) { - // util::SimpleLogger().Write() << "dist " << distance(A, C); return distance(A, C); } } return std::abs(dist); } -double distanceToEdge(engine::plugins::IsochroneNode &p, - engine::plugins::IsochroneNode &e1, - engine::plugins::IsochroneNode &e2) +double distanceToEdge(const engine::plugins::IsochroneNode &p, + const engine::plugins::IsochroneNode &e1, + const engine::plugins::IsochroneNode &e2) { - // double pX = static_cast(util::toFloating(p.node.lon)); - // double pY = static_cast(util::toFloating(p.node.lat)); - // double vX = static_cast(util::toFloating(e1.node.lon)); - // double vY = static_cast(util::toFloating(e1.node.lat)); - // double wX = static_cast(util::toFloating(e2.node.lon)); - // double wY = static_cast(util::toFloating(e2.node.lat)); const std::uint64_t pX = static_cast(p.node.lon); const std::uint64_t pY = static_cast(p.node.lat); const std::uint64_t vX = static_cast(e1.node.lon); @@ -159,42 +154,20 @@ double distanceToEdge(engine::plugins::IsochroneNode &p, const std::uint64_t wX = static_cast(e2.node.lon); const std::uint64_t wY = static_cast(e2.node.lat); - // double squaredEdgeLength = util::coordinate_calculation::haversineDistance( - // Coordinate(e1.node.lon, e1.node.lat), Coordinate(e2.node.lon, e2.node.lat)); - // if (squaredEdgeLength == 0) - // { - // // v==w - // return util::coordinate_calculation::haversineDistance( - // Coordinate(p.node.lon, p.node.lat), Coordinate(e1.node.lon, e1.node.lat)); - // } - // var t = ((p.x - v.x) * (w.x - v.x) + (p.y - v.y) * (w.y - v.y)) / l2; - // double t = - // (static_cast((p.node.lon - e1.node.lon) * (e2.node.lon - e1.node.lon)) + - // static_cast((p.node.lat - e1.node.lat) * (e2.node.lat - e2.node.lat))) - // / - // squaredEdgeLength; - - // double distance = std::abs((wX-vX)*(vY-pY) - (vX-pX)*(wY-vY)) / std::sqrt(std::pow(wX - - // vX, 2) + std::pow(wY -vY,2)); - double distance = std::abs((wX - vX) * (vY - pY) - (vX - pX) * (wY - vY)) / - std::sqrt(std::pow(wX - vX, 2) + std::pow(wY - vY, 2)); + const double distance = std::abs((wX - vX) * (vY - pY) - (vX - pX) * (wY - vY)) / + std::sqrt(std::pow(wX - vX, 2) + std::pow(wY - vY, 2)); return distance; - // double t = ((pX - vX) * (wX - vX) + (pY - vY) * (wY - vY)) / squaredEdgeLength; - // t = std::max(0.0, std::min(1.0, t)); - - // Coordinate tCoord(FloatLongitude(vX + t * (wX - vX)), FloatLatitude(vY + t * (wY - vY))); - - // return util::coordinate_calculation::haversineDistance( - // Coordinate(p.node.lon, p.node.lat), tCoord); } + std::vector concavehull(std::vector &convexhull, unsigned int threshold, std::vector &isochrone) { - std::vector concavehull(convexhull); + // std::vector concavehull(convexhull); + std::vector concavehull = convexhull; concavehull.push_back(concavehull[0]); for (std::vector::iterator it = concavehull.begin(); @@ -207,6 +180,7 @@ concavehull(std::vector &convexhull, engine::plugins::IsochroneNode n2 = *std::next(it); engine::plugins::IsochroneNode pk; double d = std::numeric_limits::max(); + double edgelength = distance(n1, n2); for (auto iso : isochrone) { @@ -214,58 +188,26 @@ concavehull(std::vector &convexhull, { continue; } - // auto t = distanceToEdge(iso, n1, n2); - auto t = linePointDist(n1, n2, iso, true); - engine::plugins::IsochroneNode n3; - - // Test if point is closer to next Edge -> n2,n3 - if (std::next(it) == concavehull.end()) - { - n3 = *std::next(concavehull.begin()); - } - else - { - n3 = *std::next(it, 2); - } - auto t_next = linePointDist(n2, n3, iso, true); + auto temp = linePointDist(n1, n2, iso, true); - engine::plugins::IsochroneNode n0; - if (it == concavehull.begin()) // Start and End Point are the same (Avoid distance) + if (temp > edgelength) { - n0 = *std::prev(concavehull.end()); - } - else - { - n0 = *std::prev(it); - } - auto t_prev = linePointDist(n0, n1, iso, true); - - if (t_prev < t || t_next < t) - { - continue; // Distance of the point is closer to neighbour edge + continue; } - if (d > t) + if (d > temp) { - d = t; + d = temp; pk = iso; } } - // double edgelength = util::coordinate_calculation::haversineDistance( - // Coordinate(n1.node.lon, n1.node.lat), Coordinate(n2.node.lon, n2.node.lat)); - double edgelength = distance(n1, n2); - // auto edgelength = - // std::sqrt(util::coordinate_calculation::squaredEuclideanDistance( - // Coordinate(n1.node.lon, n1.node.lat), Coordinate(n2.node.lon, n2.node.lat))); - - util::SimpleLogger().Write() << "Kante " << edgelength; - // util::SimpleLogger().Write() << "Kante " << edgelength2; - util::SimpleLogger().Write() << "distanz " << d; if ((edgelength / d) > threshold) { it = concavehull.insert(std::next(it), pk); it = std::prev(it); + // util::SimpleLogger().Write() << "List-Size: " << concavehull.size(); } } + util::SimpleLogger().Write() << "Convex-Size: " << convexhull.size(); return concavehull; } } diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index afa764cdbbf..d39dfca5b23 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -109,11 +109,17 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, // Optional param for calculating Convex Hull if (params.convexhull) { - convexhull = util::monotoneChain(isochroneVector); + TIMER_START(CONVEXHULL); + convexhull = util::monotoneChain(isochroneVector); + TIMER_STOP(CONVEXHULL); + util::SimpleLogger().Write() << "CONVEXHULL took: " << TIMER_MSEC(CONVEXHULL) << "ms"; } if(params.concavehull && params.convexhull) { + TIMER_START(CONCAVEHULL); concavehull = util::concavehull(convexhull, params.threshold, isochroneVector); + TIMER_STOP(CONCAVEHULL); + util::SimpleLogger().Write() << "CONCAVEHULL took: " << TIMER_MSEC(CONCAVEHULL) << "ms"; } TIMER_START(RESPONSE); @@ -121,7 +127,12 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, isochroneAPI.MakeResponse(isochroneVector, convexhull, concavehull, json_result); TIMER_STOP(RESPONSE); util::SimpleLogger().Write() << "RESPONSE took: " << TIMER_MSEC(RESPONSE) << "ms"; - + isochroneVector.clear(); + isochroneVector.shrink_to_fit(); + convexhull.clear(); + convexhull.shrink_to_fit(); + concavehull.clear(); + concavehull.shrink_to_fit(); return Status::Ok; } From baed9c78fc4f45b9219ebf9fd1a81f951433bb5a Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 20 Jul 2016 11:47:37 +0200 Subject: [PATCH 37/43] Fixed bug multiple Requests --- include/engine/plugins/isochrone.hpp | 3 --- src/engine/plugins/isochrone.cpp | 8 +++----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index eb67b7ba4ca..6b74cb24d25 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -76,9 +76,6 @@ class IsochronePlugin final : public BasePlugin std::vector coordinate_list; std::vector graph_edge_list; std::size_t number_of_nodes; - IsochroneVector isochroneVector; - IsochroneVector convexhull; - IsochroneVector concavehull; std::size_t loadGraph(const std::string &path, std::vector &coordinate_list, diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index d39dfca5b23..bfdaa659706 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -68,7 +68,9 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, forward_id_vector); auto source = forward_id_vector[0]; - isochroneVector.clear(); + IsochroneVector isochroneVector; + IsochroneVector convexhull; + IsochroneVector concavehull; if (params.duration != 0) { @@ -102,10 +104,6 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, } util::SimpleLogger().Write() << "Nodes Found: " << isochroneVector.size(); - - convexhull.clear(); - concavehull.clear(); - // Optional param for calculating Convex Hull if (params.convexhull) { From 814e4d8c0fcd88381feb4927c69f0e3fd2910d09 Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 28 Jul 2016 09:30:36 +0200 Subject: [PATCH 38/43] Style Format --- .../engine/datafacade/internal_datafacade.hpp | 2 +- .../engine/datafacade/shared_datafacade.hpp | 3 +- include/engine/guidance/assemble_steps.hpp | 2 +- include/engine/plugins/isochrone.hpp | 18 +-- include/engine/search_engine.hpp | 6 +- include/extractor/extractor_callbacks.hpp | 7 +- include/extractor/guidance/turn_lane_data.hpp | 2 +- .../extractor/guidance/turn_lane_types.hpp | 2 +- include/extractor/internal_extractor_edge.hpp | 1 - include/extractor/query_node.hpp | 6 +- include/osrm/osrm.hpp | 30 ++--- .../api/isochrone_parameter_grammar.hpp | 2 +- include/server/service/isochrone_service.hpp | 5 +- include/storage/shared_memory.hpp | 1 - include/util/concave_hull.hpp | 1 - include/util/graph_loader.hpp | 10 +- include/util/io.hpp | 2 +- include/util/monotone_chain.hpp | 13 +- include/util/static_rtree.hpp | 6 +- include/util/strong_typedef.hpp | 52 ++++---- include/util/typedefs.hpp | 3 +- src/contractor/contractor.cpp | 36 ++--- src/engine/engine.cpp | 24 ++-- src/engine/guidance/post_processing.cpp | 2 +- src/engine/plugins/isochrone.cpp | 53 ++++---- src/engine/polyline_compressor.cpp | 6 +- src/extractor/edge_based_graph_factory.cpp | 2 +- src/extractor/extraction_containers.cpp | 14 +- src/extractor/extractor_callbacks.cpp | 45 ++++--- src/extractor/graph_compressor.cpp | 13 +- src/extractor/guidance/turn_analysis.cpp | 10 +- src/extractor/guidance/turn_handler.cpp | 2 +- .../guidance/turn_lane_augmentation.cpp | 3 +- src/extractor/guidance/turn_lane_handler.cpp | 10 +- src/extractor/guidance/turn_lane_matcher.cpp | 2 +- src/extractor/raster_source.cpp | 5 +- src/osrm/osrm.cpp | 8 +- src/server/api/parameters_parser.cpp | 41 ++++-- src/server/service/isochrone_service.cpp | 21 +-- src/server/service_handler.cpp | 14 +- src/storage/storage.cpp | 4 +- src/tools/routed.cpp | 126 +++++++++++------- src/util/coordinate_calculation.cpp | 6 +- unit_tests/util/packed_vector.cpp | 2 +- 44 files changed, 349 insertions(+), 274 deletions(-) diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp index 92c2c7eb7a7..e7dc965d887 100644 --- a/include/engine/datafacade/internal_datafacade.hpp +++ b/include/engine/datafacade/internal_datafacade.hpp @@ -360,7 +360,7 @@ class InternalDataFacade final : public BaseDataFacade std::vector bearing_classes; // and the actual bearing values std::uint64_t num_bearings; - intersection_stream.read(reinterpret_cast(&num_bearings),sizeof(num_bearings)); + intersection_stream.read(reinterpret_cast(&num_bearings), sizeof(num_bearings)); m_bearing_values_table.resize(num_bearings); intersection_stream.read(reinterpret_cast(&m_bearing_values_table[0]), sizeof(m_bearing_values_table[0]) * num_bearings); diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp index 91e2a02862b..7872fae760c 100644 --- a/include/engine/datafacade/shared_datafacade.hpp +++ b/include/engine/datafacade/shared_datafacade.hpp @@ -840,7 +840,8 @@ class SharedDataFacade final : public BaseDataFacade else return extractor::guidance::TurnLaneDescription( m_lane_description_masks.begin() + m_lane_description_offsets[lane_description_id], - m_lane_description_masks.begin() + m_lane_description_offsets[lane_description_id + 1]); + m_lane_description_masks.begin() + + m_lane_description_offsets[lane_description_id + 1]); } }; } diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp index 943c1b9d858..9a89ff8b39a 100644 --- a/include/engine/guidance/assemble_steps.hpp +++ b/include/engine/guidance/assemble_steps.hpp @@ -2,8 +2,8 @@ #define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_ #include "extractor/guidance/turn_instruction.hpp" -#include "extractor/travel_mode.hpp" #include "extractor/guidance/turn_lane_types.hpp" +#include "extractor/travel_mode.hpp" #include "engine/datafacade/datafacade_base.hpp" #include "engine/guidance/leg_geometry.hpp" #include "engine/guidance/route_step.hpp" diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index 6b74cb24d25..f4d7ebbaf25 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -1,16 +1,16 @@ #ifndef ISOCHRONE_HPP #define ISOCHRONE_HPP -#include "engine/plugins/plugin_base.hpp" #include "engine/api/isochrone_parameters.hpp" +#include "engine/plugins/plugin_base.hpp" #include "osrm/json_container.hpp" -#include "util/static_graph.hpp" #include "util/binary_heap.hpp" +#include "util/static_graph.hpp" #include -#include #include #include +#include #include #include @@ -18,8 +18,6 @@ #include #include - - namespace osrm { namespace engine @@ -42,7 +40,10 @@ struct SimpleEdgeData struct IsochroneNode { IsochroneNode(){}; - IsochroneNode(osrm::extractor::QueryNode node, osrm::extractor::QueryNode predecessor, double distance, int duration) + IsochroneNode(osrm::extractor::QueryNode node, + osrm::extractor::QueryNode predecessor, + double distance, + int duration) : node(node), predecessor(predecessor), distance(distance), duration(duration) { } @@ -52,7 +53,8 @@ struct IsochroneNode double distance; int duration; - bool operator==(const IsochroneNode &n) const { + bool operator==(const IsochroneNode &n) const + { if (n.node.node_id == node.node_id) return true; else @@ -60,8 +62,6 @@ struct IsochroneNode } }; - - using SimpleGraph = util::StaticGraph; using SimpleEdge = SimpleGraph::InputEdge; using QueryHeap = osrm::util:: diff --git a/include/engine/search_engine.hpp b/include/engine/search_engine.hpp index afb78c293f2..cab476756cb 100644 --- a/include/engine/search_engine.hpp +++ b/include/engine/search_engine.hpp @@ -1,13 +1,13 @@ #ifndef SEARCH_ENGINE_HPP #define SEARCH_ENGINE_HPP -#include "engine/search_engine_data.hpp" #include "engine/routing_algorithms/alternative_path.hpp" +#include "engine/routing_algorithms/direct_shortest_path.hpp" #include "engine/routing_algorithms/many_to_many.hpp" #include "engine/routing_algorithms/map_matching.hpp" -#include "engine/routing_algorithms/shortest_path.hpp" -#include "engine/routing_algorithms/direct_shortest_path.hpp" #include "engine/routing_algorithms/one_to_many.hpp" +#include "engine/routing_algorithms/shortest_path.hpp" +#include "engine/search_engine_data.hpp" #include diff --git a/include/extractor/extractor_callbacks.hpp b/include/extractor/extractor_callbacks.hpp index 2adfb7dc5b1..8268766f0fd 100644 --- a/include/extractor/extractor_callbacks.hpp +++ b/include/extractor/extractor_callbacks.hpp @@ -1,8 +1,8 @@ #ifndef EXTRACTOR_CALLBACKS_HPP #define EXTRACTOR_CALLBACKS_HPP -#include "util/typedefs.hpp" #include "extractor/guidance/turn_lane_types.hpp" +#include "util/typedefs.hpp" #include #include @@ -40,7 +40,10 @@ class ExtractorCallbacks using MapKey = std::pair; using MapVal = unsigned; std::unordered_map> string_map; - std::unordered_map lane_description_map; + std::unordered_map + lane_description_map; ExtractionContainers &external_memory; public: diff --git a/include/extractor/guidance/turn_lane_data.hpp b/include/extractor/guidance/turn_lane_data.hpp index f1a7d7e12d7..66aaede6d86 100644 --- a/include/extractor/guidance/turn_lane_data.hpp +++ b/include/extractor/guidance/turn_lane_data.hpp @@ -1,8 +1,8 @@ #ifndef OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_ #define OSRM_EXTRACTOR_GUIDANCE_TURN_LANE_DATA_HPP_ -#include "util/typedefs.hpp" #include "extractor/guidance/turn_lane_types.hpp" +#include "util/typedefs.hpp" #include #include diff --git a/include/extractor/guidance/turn_lane_types.hpp b/include/extractor/guidance/turn_lane_types.hpp index 3a06a3b10f1..d32a8e5c0a7 100644 --- a/include/extractor/guidance/turn_lane_types.hpp +++ b/include/extractor/guidance/turn_lane_types.hpp @@ -10,9 +10,9 @@ #include #include +#include "util/json_container.hpp" #include "util/simple_logger.hpp" #include "util/typedefs.hpp" -#include "util/json_container.hpp" namespace osrm { diff --git a/include/extractor/internal_extractor_edge.hpp b/include/extractor/internal_extractor_edge.hpp index 376e587c18a..68e7bfb963c 100644 --- a/include/extractor/internal_extractor_edge.hpp +++ b/include/extractor/internal_extractor_edge.hpp @@ -141,7 +141,6 @@ struct InternalExtractorEdge return v; } }; - } } diff --git a/include/extractor/query_node.hpp b/include/extractor/query_node.hpp index f66d9c513ec..53c7adad735 100644 --- a/include/extractor/query_node.hpp +++ b/include/extractor/query_node.hpp @@ -5,8 +5,8 @@ #include "util/coordinate.hpp" -#include #include +#include namespace osrm { @@ -15,8 +15,8 @@ namespace extractor struct QueryNode { - using key_type = OSMNodeID; // type of NodeID - using value_type = std::int32_t; // type of lat,lons + using key_type = OSMNodeID; // type of NodeID + using value_type = std::int32_t; // type of lat,lons explicit QueryNode(const util::FixedLongitude lon_, const util::FixedLatitude lat_, diff --git a/include/osrm/osrm.hpp b/include/osrm/osrm.hpp index 20606765069..e4d6801e698 100644 --- a/include/osrm/osrm.hpp +++ b/include/osrm/osrm.hpp @@ -46,21 +46,21 @@ using engine::api::MatchParameters; using engine::api::TileParameters; using engine::api::IsochroneParameters; - /** - * Represents a Open Source Routing Machine with access to its services. - * - * This represents an Open Source Routing Machine (OSRM) instance, with the services: - * - * - Route: shortest path queries for coordinates - * - Table: distance tables for coordinates - * - Nearest: nearest street segment for coordinate - * - Trip: shortest round trip between coordinates - * - Match: snaps noisy coordinate traces to the road network - * - Tile: vector tiles with internal graph representation - * - * All services take service-specific parameters, fill a JSON object, and return a status code. - */ - class OSRM final +/** + * Represents a Open Source Routing Machine with access to its services. + * + * This represents an Open Source Routing Machine (OSRM) instance, with the services: + * + * - Route: shortest path queries for coordinates + * - Table: distance tables for coordinates + * - Nearest: nearest street segment for coordinate + * - Trip: shortest round trip between coordinates + * - Match: snaps noisy coordinate traces to the road network + * - Tile: vector tiles with internal graph representation + * + * All services take service-specific parameters, fill a JSON object, and return a status code. + */ +class OSRM final { public: /** diff --git a/include/server/api/isochrone_parameter_grammar.hpp b/include/server/api/isochrone_parameter_grammar.hpp index 3a6860756d3..7e67a576148 100644 --- a/include/server/api/isochrone_parameter_grammar.hpp +++ b/include/server/api/isochrone_parameter_grammar.hpp @@ -40,7 +40,7 @@ struct IsochroneParametersGrammar final : public BaseParametersGrammar qi::bool_)[ph::bind(&engine::api::IsochroneParameters::concavehull, qi::_r1) = qi::_1]; - threshold_rule= + threshold_rule = (qi::lit("threshold=") > qi::double_)[ph::bind(&engine::api::IsochroneParameters::threshold, qi::_r1) = qi::_1]; diff --git a/include/server/service/isochrone_service.hpp b/include/server/service/isochrone_service.hpp index d3102ff9119..4774a94224f 100644 --- a/include/server/service/isochrone_service.hpp +++ b/include/server/service/isochrone_service.hpp @@ -4,8 +4,8 @@ #include "server/service/base_service.hpp" #include "engine/status.hpp" -#include "util/coordinate.hpp" #include "osrm/osrm.hpp" +#include "util/coordinate.hpp" #include #include @@ -22,7 +22,8 @@ class IsochroneService final : public BaseService public: IsochroneService(OSRM &routing_machine) : BaseService(routing_machine) {} - engine::Status RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override; + engine::Status + RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) final override; unsigned GetVersion() final override { return 1; } }; diff --git a/include/storage/shared_memory.hpp b/include/storage/shared_memory.hpp index bf0c2396728..2d58be9ef12 100644 --- a/include/storage/shared_memory.hpp +++ b/include/storage/shared_memory.hpp @@ -77,7 +77,6 @@ class SharedMemory public: void *Ptr() const { return region.get_address(); } - SharedMemory(const SharedMemory &) = delete; SharedMemory &operator=(const SharedMemory &) = delete; diff --git a/include/util/concave_hull.hpp b/include/util/concave_hull.hpp index 3124f04f899..21da7c8ddaa 100644 --- a/include/util/concave_hull.hpp +++ b/include/util/concave_hull.hpp @@ -159,7 +159,6 @@ double distanceToEdge(const engine::plugins::IsochroneNode &p, return distance; } - std::vector concavehull(std::vector &convexhull, unsigned int threshold, diff --git a/include/util/graph_loader.hpp b/include/util/graph_loader.hpp index d1bcb72e2f1..6ecde286a12 100644 --- a/include/util/graph_loader.hpp +++ b/include/util/graph_loader.hpp @@ -33,7 +33,7 @@ namespace util * we need to renumber it to the new internal id. */ inline unsigned loadRestrictionsFromFile(std::istream &input_stream, - std::vector &restriction_list) + std::vector &restriction_list) { const FingerPrint fingerprint_valid = FingerPrint::GetValid(); FingerPrint fingerprint_loaded; @@ -63,9 +63,9 @@ inline unsigned loadRestrictionsFromFile(std::istream &input_stream, * - nodes indexed by their internal (non-osm) id */ inline NodeID loadNodesFromFile(std::istream &input_stream, - std::vector &barrier_node_list, - std::vector &traffic_light_node_list, - std::vector &node_array) + std::vector &barrier_node_list, + std::vector &traffic_light_node_list, + std::vector &node_array) { const FingerPrint fingerprint_valid = FingerPrint::GetValid(); FingerPrint fingerprint_loaded; @@ -108,7 +108,7 @@ inline NodeID loadNodesFromFile(std::istream &input_stream, * Reads a .osrm file and produces the edges. */ inline NodeID loadEdgesFromFile(std::istream &input_stream, - std::vector &edge_list) + std::vector &edge_list) { EdgeID m; input_stream.read(reinterpret_cast(&m), sizeof(unsigned)); diff --git a/include/util/io.hpp b/include/util/io.hpp index cb4473dd4b9..e208f7c25de 100644 --- a/include/util/io.hpp +++ b/include/util/io.hpp @@ -151,7 +151,7 @@ bool serializeVector(std::ofstream &out_stream, const stxxl::vector template bool deserializeAdjacencyArray(const std::string &filename, std::vector &offsets, - std::vector& data) + std::vector &data) { std::ifstream in_stream(filename, std::ios::binary); diff --git a/include/util/monotone_chain.hpp b/include/util/monotone_chain.hpp index 77b56d86b88..7efdd0e6ef1 100644 --- a/include/util/monotone_chain.hpp +++ b/include/util/monotone_chain.hpp @@ -6,9 +6,9 @@ #include #include -#include #include #include +#include namespace osrm { @@ -18,7 +18,7 @@ using Node = engine::plugins::IsochroneNode; int ccw(Node &p, Node &q, Node &r) { - //Using double values to avoid integer overflows + // Using double values to avoid integer overflows double Q_lat = static_cast(util::toFloating(q.node.lat)); double Q_lon = static_cast(util::toFloating(q.node.lon)); double R_lat = static_cast(util::toFloating(r.node.lat)); @@ -40,13 +40,10 @@ std::vector monotoneChain(std::vector &nodes) int n = P.size(), k = 0; std::vector H(2 * n); - // sort Points by lat and lon - std::sort(P.begin(), P.end(), [&](const Node &a, const Node &b) - { - return a.node.lat < b.node.lat || - (a.node.lat == b.node.lat && a.node.lon < b.node.lon); - }); + std::sort(P.begin(), P.end(), [&](const Node &a, const Node &b) { + return a.node.lat < b.node.lat || (a.node.lat == b.node.lat && a.node.lon < b.node.lon); + }); // Build lower hull for (int i = 0; i < n; ++i) diff --git a/include/util/static_rtree.hpp b/include/util/static_rtree.hpp index c955151294d..971b303fd74 100644 --- a/include/util/static_rtree.hpp +++ b/include/util/static_rtree.hpp @@ -192,9 +192,9 @@ class StaticRTree Coordinate current_centroid = coordinate_calculation::centroid( m_coordinate_list[current_element.u], m_coordinate_list[current_element.v]); - current_centroid.lat = - FixedLatitude{static_cast(COORDINATE_PRECISION * - web_mercator::latToY(toFloating(current_centroid.lat)))}; + current_centroid.lat = FixedLatitude{static_cast( + COORDINATE_PRECISION * + web_mercator::latToY(toFloating(current_centroid.lat)))}; current_wrapper.m_hilbert_value = hilbertCode(current_centroid); } diff --git a/include/util/strong_typedef.hpp b/include/util/strong_typedef.hpp index 0d45a2d41df..02b1d622b51 100644 --- a/include/util/strong_typedef.hpp +++ b/include/util/strong_typedef.hpp @@ -40,32 +40,32 @@ namespace osrm * etc. Also clarifies what this random "int" value is * being used for. */ -#define OSRM_STRONG_TYPEDEF(From, To) \ - struct To final \ - { \ - static_assert(std::is_arithmetic(), ""); \ - From __value; \ - friend std::ostream &operator<<(std::ostream &stream, const To &inst); \ - \ - explicit operator From &() { return __value; } \ - explicit operator From() const { return __value; } \ - To operator+(const To rhs_) const { return To{__value + static_cast(rhs_)}; } \ - To operator-(const To rhs_) const { return To{__value - static_cast(rhs_)}; } \ - To operator*(const To rhs_) const { return To{__value * static_cast(rhs_)}; } \ - To operator/(const To rhs_) const { return To{__value / static_cast(rhs_)}; } \ - bool operator<(const To z_) const { return __value < static_cast(z_); } \ - bool operator>(const To z_) const { return __value > static_cast(z_); } \ - bool operator<=(const To z_) const { return __value <= static_cast(z_); } \ - bool operator>=(const To z_) const { return __value >= static_cast(z_); } \ - bool operator==(const To z_) const { return __value == static_cast(z_); } \ - bool operator!=(const To z_) const { return __value != static_cast(z_); } \ - }; \ - static_assert(std::is_trivial(), #To " is not a trivial type"); \ - static_assert(std::is_standard_layout(), #To " is not a standart layout"); \ - static_assert(std::is_pod(), #To " is not a POD layout"); \ - inline std::ostream &operator<<(std::ostream &stream, const To &inst) \ - { \ - return stream << inst.__value; \ +#define OSRM_STRONG_TYPEDEF(From, To) \ + struct To final \ + { \ + static_assert(std::is_arithmetic(), ""); \ + From __value; \ + friend std::ostream &operator<<(std::ostream &stream, const To &inst); \ + \ + explicit operator From &() { return __value; } \ + explicit operator From() const { return __value; } \ + To operator+(const To rhs_) const { return To{__value + static_cast(rhs_)}; } \ + To operator-(const To rhs_) const { return To{__value - static_cast(rhs_)}; } \ + To operator*(const To rhs_) const { return To{__value * static_cast(rhs_)}; } \ + To operator/(const To rhs_) const { return To{__value / static_cast(rhs_)}; } \ + bool operator<(const To z_) const { return __value < static_cast(z_); } \ + bool operator>(const To z_) const { return __value > static_cast(z_); } \ + bool operator<=(const To z_) const { return __value <= static_cast(z_); } \ + bool operator>=(const To z_) const { return __value >= static_cast(z_); } \ + bool operator==(const To z_) const { return __value == static_cast(z_); } \ + bool operator!=(const To z_) const { return __value != static_cast(z_); } \ + }; \ + static_assert(std::is_trivial(), #To " is not a trivial type"); \ + static_assert(std::is_standard_layout(), #To " is not a standart layout"); \ + static_assert(std::is_pod(), #To " is not a POD layout"); \ + inline std::ostream &operator<<(std::ostream &stream, const To &inst) \ + { \ + return stream << inst.__value; \ } #define OSRM_STRONG_TYPEDEF_HASHABLE(From, To) \ diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp index 2ffb57771cb..f4440c42393 100644 --- a/include/util/typedefs.hpp +++ b/include/util/typedefs.hpp @@ -64,7 +64,8 @@ static const LaneID INVALID_LANEID = std::numeric_limits::max(); using LaneDataID = std::uint16_t; static const LaneDataID INVALID_LANE_DATAID = std::numeric_limits::max(); using LaneDescriptionID = std::uint16_t; -static const LaneDescriptionID INVALID_LANE_DESCRIPTIONID = std::numeric_limits::max(); +static const LaneDescriptionID INVALID_LANE_DESCRIPTIONID = + std::numeric_limits::max(); using BearingClassID = std::uint32_t; static const BearingClassID INVALID_BEARING_CLASSID = std::numeric_limits::max(); diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp index d87abc48ba8..b6ee126a550 100644 --- a/src/contractor/contractor.cpp +++ b/src/contractor/contractor.cpp @@ -252,9 +252,8 @@ parse_segment_lookup_from_csv_files(const std::vector &segment_spee if (!ok || it != last) throw util::exception{"Segment speed file " + filename + " malformed"}; - SegmentSpeedSource val{ - {OSMNodeID{from_node_id}, OSMNodeID{to_node_id}}, - {speed, static_cast(file_id)}}; + SegmentSpeedSource val{{OSMNodeID{from_node_id}, OSMNodeID{to_node_id}}, + {speed, static_cast(file_id)}}; local.push_back(std::move(val)); } @@ -373,7 +372,7 @@ EdgeID Contractor::LoadEdgeExpandedGraph( using boost::interprocess::mapped_region; using boost::interprocess::read_only; - const file_mapping mapping{ filename.c_str(), read_only }; + const file_mapping mapping{filename.c_str(), read_only}; mapped_region region{mapping, read_only}; region.advise(mapped_region::advice_sequential); return region; @@ -400,19 +399,21 @@ EdgeID Contractor::LoadEdgeExpandedGraph( return boost::interprocess::mapped_region(); }(); - // Set the struct packing to 1 byte word sizes. This prevents any padding. We only use - // this struct once, so any alignment penalty is trivial. If this is *not* done, then - // the struct will be padded out by an extra 4 bytes, and sizeof() will mean we read - // too much data from the original file. - #pragma pack(push, r1, 1) - struct EdgeBasedGraphHeader { +// Set the struct packing to 1 byte word sizes. This prevents any padding. We only use +// this struct once, so any alignment penalty is trivial. If this is *not* done, then +// the struct will be padded out by an extra 4 bytes, and sizeof() will mean we read +// too much data from the original file. +#pragma pack(push, r1, 1) + struct EdgeBasedGraphHeader + { util::FingerPrint fingerprint; std::uint64_t number_of_edges; EdgeID max_edge_id; }; - #pragma pack(pop, r1) +#pragma pack(pop, r1) - const EdgeBasedGraphHeader graph_header = *(reinterpret_cast(edge_based_graph_region.get_address())); + const EdgeBasedGraphHeader graph_header = + *(reinterpret_cast(edge_based_graph_region.get_address())); const util::FingerPrint fingerprint_valid = util::FingerPrint::GetValid(); graph_header.fingerprint.TestContractor(fingerprint_valid); @@ -730,14 +731,17 @@ EdgeID Contractor::LoadEdgeExpandedGraph( tbb::parallel_invoke(maybe_save_geometries, save_datasource_indexes, save_datastore_names); - auto penaltyblock = - reinterpret_cast(edge_penalty_region.get_address()); + auto penaltyblock = reinterpret_cast( + edge_penalty_region.get_address()); auto edge_segment_byte_ptr = reinterpret_cast(edge_segment_region.get_address()); auto edge_based_edge_ptr = reinterpret_cast( - reinterpret_cast(edge_based_graph_region.get_address()) + sizeof(EdgeBasedGraphHeader)); + reinterpret_cast(edge_based_graph_region.get_address()) + + sizeof(EdgeBasedGraphHeader)); const auto edge_based_edge_last = reinterpret_cast( - reinterpret_cast(edge_based_graph_region.get_address()) + sizeof(EdgeBasedGraphHeader) + sizeof(extractor::EdgeBasedEdge) * graph_header.number_of_edges); + reinterpret_cast(edge_based_graph_region.get_address()) + + sizeof(EdgeBasedGraphHeader) + + sizeof(extractor::EdgeBasedEdge) * graph_header.number_of_edges); while (edge_based_edge_ptr != edge_based_edge_last) { diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 9875168dc4f..257794b56ed 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1,15 +1,15 @@ #include "engine/engine.hpp" -#include "engine/engine_config.hpp" #include "engine/api/route_parameters.hpp" +#include "engine/engine_config.hpp" #include "engine/status.hpp" -#include "engine/plugins/table.hpp" -#include "engine/plugins/nearest.hpp" +#include "engine/plugins/isochrone.hpp" #include "engine/plugins/match.hpp" +#include "engine/plugins/nearest.hpp" +#include "engine/plugins/table.hpp" +#include "engine/plugins/tile.hpp" #include "engine/plugins/trip.hpp" #include "engine/plugins/viaroute.hpp" -#include "engine/plugins/tile.hpp" -#include "engine/plugins/isochrone.hpp" #include "engine/datafacade/datafacade_base.hpp" #include "engine/datafacade/internal_datafacade.hpp" @@ -138,7 +138,8 @@ Engine::Engine(EngineConfig &config) { throw util::exception("Invalid file paths given!"); } - query_data_facade = util::make_unique(config.storage_config); + query_data_facade = + util::make_unique(config.storage_config); } // Register plugins @@ -150,10 +151,11 @@ Engine::Engine(EngineConfig &config) trip_plugin = create(*query_data_facade, config.max_locations_trip); match_plugin = create(*query_data_facade, config.max_locations_map_matching); tile_plugin = create(*query_data_facade); - if(config.use_isochrone) { - isochrone_plugin = create(*query_data_facade, config.storage_config.base.string()); + if (config.use_isochrone) + { + isochrone_plugin = + create(*query_data_facade, config.storage_config.base.string()); } - } // make sure we deallocate the unique ptr at a position where we know the size of the plugins @@ -190,10 +192,10 @@ Status Engine::Tile(const api::TileParameters ¶ms, std::string &result) { return RunQuery(lock, *query_data_facade, params, *tile_plugin, result); } -Status Engine::Isochrone(const api::IsochroneParameters ¶ms, util::json::Object &result) { +Status Engine::Isochrone(const api::IsochroneParameters ¶ms, util::json::Object &result) +{ return RunQuery(lock, *query_data_facade, params, *isochrone_plugin, result); } - } // engine ns } // osrm ns diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp index 6f4b0cab596..593c1936d9a 100644 --- a/src/engine/guidance/post_processing.cpp +++ b/src/engine/guidance/post_processing.cpp @@ -1,5 +1,5 @@ -#include "extractor/guidance/turn_instruction.hpp" #include "engine/guidance/post_processing.hpp" +#include "extractor/guidance/turn_instruction.hpp" #include "engine/guidance/assemble_steps.hpp" #include "engine/guidance/lane_processing.hpp" diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index bfdaa659706..d59bb798cb9 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -1,9 +1,9 @@ +#include "engine/plugins/isochrone.hpp" #include "engine/api/isochrone_api.hpp" #include "engine/phantom_node.hpp" -#include "engine/plugins/isochrone.hpp" +#include "util/concave_hull.hpp" #include "util/coordinate_calculation.hpp" #include "util/graph_loader.hpp" -#include "util/concave_hull.hpp" #include "util/monotone_chain.hpp" #include "util/simple_logger.hpp" #include "util/timing_util.hpp" @@ -51,8 +51,10 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, return Error("InvalidOptions", "Only distance or duration should be set", json_result); } - if (params.concavehull == true && params.convexhull == false) { - return Error("InvalidOptions", "If concavehull is set, convexhull must be set too", json_result); + if (params.concavehull == true && params.convexhull == false) + { + return Error( + "InvalidOptions", "If concavehull is set, convexhull must be set too", json_result); } auto phantomnodes = GetPhantomNodes(params, 1); @@ -79,9 +81,9 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, TIMER_STOP(DIJKSTRA); util::SimpleLogger().Write() << "DijkstraByDuration took: " << TIMER_MSEC(DIJKSTRA) << "ms"; TIMER_START(SORTING); - std::sort(isochroneVector.begin(), isochroneVector.end(), - [&](const IsochroneNode n1, const IsochroneNode n2) - { + std::sort(isochroneVector.begin(), + isochroneVector.end(), + [&](const IsochroneNode n1, const IsochroneNode n2) { return n1.duration < n2.duration; }); TIMER_STOP(SORTING); @@ -94,9 +96,9 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, TIMER_STOP(DIJKSTRA); util::SimpleLogger().Write() << "DijkstraByDistance took: " << TIMER_MSEC(DIJKSTRA) << "ms"; TIMER_START(SORTING); - std::sort(isochroneVector.begin(), isochroneVector.end(), - [&](const IsochroneNode n1, const IsochroneNode n2) - { + std::sort(isochroneVector.begin(), + isochroneVector.end(), + [&](const IsochroneNode n1, const IsochroneNode n2) { return n1.distance < n2.distance; }); TIMER_STOP(SORTING); @@ -113,7 +115,8 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, TIMER_STOP(CONVEXHULL); util::SimpleLogger().Write() << "CONVEXHULL took: " << TIMER_MSEC(CONVEXHULL) << "ms"; } - if(params.concavehull && params.convexhull) { + if (params.concavehull && params.convexhull) + { TIMER_START(CONCAVEHULL); concavehull = util::concavehull(convexhull, params.threshold, isochroneVector); TIMER_STOP(CONCAVEHULL); @@ -145,7 +148,7 @@ void IsochronePlugin::dijkstraByDuration(IsochroneVector &isochroneSet, isochroneSet.emplace_back( IsochroneNode(coordinate_list[source], coordinate_list[source], 0, 0)); - int MAX_DURATION = duration * 60 *10; + int MAX_DURATION = duration * 60 * 10; { // Standard Dijkstra search, terminating when path length > MAX while (!heap.Empty()) @@ -177,8 +180,10 @@ void IsochronePlugin::dijkstraByDuration(IsochroneVector &isochroneSet, heap.GetData(target).parent = source; heap.DecreaseKey(target, to_duration); update(isochroneSet, - IsochroneNode(coordinate_list[target], coordinate_list[source], - 0, to_duration)); + IsochroneNode(coordinate_list[target], + coordinate_list[source], + 0, + to_duration)); } } } @@ -214,7 +219,7 @@ void IsochronePlugin::dijkstraByDistance(IsochroneVector &isochroneSet, { Coordinate s(coordinate_list[source].lon, coordinate_list[source].lat); Coordinate t(coordinate_list[target].lon, coordinate_list[target].lat); - //FIXME this might not be accurate enough + // FIXME this might not be accurate enough int to_distance = static_cast( util::coordinate_calculation::haversineDistance(s, t)) + @@ -235,8 +240,10 @@ void IsochronePlugin::dijkstraByDistance(IsochroneVector &isochroneSet, heap.GetData(target).parent = source; heap.DecreaseKey(target, to_distance); update(isochroneSet, - IsochroneNode(coordinate_list[target], coordinate_list[source], - to_distance, 0)); + IsochroneNode(coordinate_list[target], + coordinate_list[source], + to_distance, + 0)); } } } @@ -260,8 +267,8 @@ std::size_t IsochronePlugin::loadGraph(const std::string &path, std::vector traffic_light_node_list; std::vector barrier_node_list; - auto number_of_nodes = util::loadNodesFromFile(input_stream, barrier_node_list, - traffic_light_node_list, coordinate_list); + auto number_of_nodes = util::loadNodesFromFile( + input_stream, barrier_node_list, traffic_light_node_list, coordinate_list); util::loadEdgesFromFile(input_stream, edge_list); traffic_light_node_list.clear(); @@ -275,11 +282,11 @@ std::size_t IsochronePlugin::loadGraph(const std::string &path, continue; } // forward edge - graph_edge_list.emplace_back(input_edge.source, input_edge.target, input_edge.weight, - input_edge.forward); + graph_edge_list.emplace_back( + input_edge.source, input_edge.target, input_edge.weight, input_edge.forward); // backward edge - graph_edge_list.emplace_back(input_edge.target, input_edge.source, input_edge.weight, - input_edge.backward); + graph_edge_list.emplace_back( + input_edge.target, input_edge.source, input_edge.weight, input_edge.backward); } return number_of_nodes; diff --git a/src/engine/polyline_compressor.cpp b/src/engine/polyline_compressor.cpp index 701dac091b5..1dc9b70453f 100644 --- a/src/engine/polyline_compressor.cpp +++ b/src/engine/polyline_compressor.cpp @@ -116,8 +116,10 @@ std::vector decodePolyline(const std::string &geometry_string) lng += dlng; util::Coordinate p; - p.lat = util::FixedLatitude{static_cast(lat * detail::POLYLINE_TO_COORDINATE)}; - p.lon = util::FixedLongitude{static_cast(lng * detail::POLYLINE_TO_COORDINATE)}; + p.lat = + util::FixedLatitude{static_cast(lat * detail::POLYLINE_TO_COORDINATE)}; + p.lon = + util::FixedLongitude{static_cast(lng * detail::POLYLINE_TO_COORDINATE)}; new_coordinates.push_back(p); } diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index d7182f0e151..8077d3c59d4 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -1,5 +1,5 @@ -#include "extractor/edge_based_edge.hpp" #include "extractor/edge_based_graph_factory.hpp" +#include "extractor/edge_based_edge.hpp" #include "util/coordinate.hpp" #include "util/coordinate_calculation.hpp" #include "util/exception.hpp" diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp index 908083ff3aa..a61d672122a 100644 --- a/src/extractor/extraction_containers.cpp +++ b/src/extractor/extraction_containers.cpp @@ -99,7 +99,6 @@ struct CmpEdgeByInternalSourceTargetAndName const oe::ExtractionContainers::STXXLNameCharData &name_data; const oe::ExtractionContainers::STXXLNameOffsets &name_offsets; }; - } namespace osrm @@ -202,7 +201,8 @@ void ExtractionContainers::WriteCharData(const std::string &file_name) // transforms in-place name offsets to name lengths BOOST_ASSERT(!name_offsets.empty()); for (auto curr = name_offsets.begin(), next = name_offsets.begin() + 1; - next != name_offsets.end(); ++curr, ++next) + next != name_offsets.end(); + ++curr, ++next) { *curr = *next - *curr; } @@ -742,8 +742,9 @@ void ExtractionContainers::PrepareRestrictions() continue; } - BOOST_ASSERT(way_start_and_end_iterator->way_id == - OSMWayID{static_cast(restrictions_iterator->restriction.from.way)}); + BOOST_ASSERT( + way_start_and_end_iterator->way_id == + OSMWayID{static_cast(restrictions_iterator->restriction.from.way)}); // we do not remap the via id yet, since we will need it for the to node as well const OSMNodeID via_node_id = OSMNodeID{restrictions_iterator->restriction.via.node}; @@ -840,8 +841,9 @@ void ExtractionContainers::PrepareRestrictions() ++restrictions_iterator; continue; } - BOOST_ASSERT(way_start_and_end_iterator->way_id == - OSMWayID{static_cast(restrictions_iterator->restriction.to.way)}); + BOOST_ASSERT( + way_start_and_end_iterator->way_id == + OSMWayID{static_cast(restrictions_iterator->restriction.to.way)}); const OSMNodeID via_node_id = OSMNodeID{restrictions_iterator->restriction.via.node}; // assign new via node id diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp index 4287255be7e..14428c5bbb5 100644 --- a/src/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -193,7 +193,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti for (auto token_itr = inner_tokens.begin(); token_itr != inner_tokens.end(); ++token_itr) { - auto position = std::find(osm_lane_strings, osm_lane_strings + num_osm_tags, *token_itr); + auto position = + std::find(osm_lane_strings, osm_lane_strings + num_osm_tags, *token_itr); const auto translated_mask = masks_by_osm_string[std::distance(osm_lane_strings, position)]; if (translated_mask == TurnLaneType::empty) @@ -265,8 +266,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti // name_offsets already has an offset of a new name, take the offset index as the name id name_id = external_memory.name_offsets.size() - 1; - external_memory.name_char_data.reserve(external_memory.name_char_data.size() + name_length - + destinations_length + pronunciation_length); + external_memory.name_char_data.reserve(external_memory.name_char_data.size() + name_length + + destinations_length + pronunciation_length); std::copy(parsed_way.name.c_str(), parsed_way.name.c_str() + name_length, @@ -306,7 +307,9 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti std::transform(input_way.nodes().begin(), input_way.nodes().end(), std::back_inserter(external_memory.used_node_id_list), - [](const osmium::NodeRef &ref) { return OSMNodeID{static_cast(ref.ref())}; }); + [](const osmium::NodeRef &ref) { + return OSMNodeID{static_cast(ref.ref())}; + }); const bool is_opposite_way = TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode; @@ -338,7 +341,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti external_memory.way_start_end_id_list.push_back( {OSMWayID{static_cast(input_way.id())}, OSMNodeID{static_cast(input_way.nodes().back().ref())}, - OSMNodeID{static_cast(input_way.nodes()[input_way.nodes().size() - 2].ref())}, + OSMNodeID{ + static_cast(input_way.nodes()[input_way.nodes().size() - 2].ref())}, OSMNodeID{static_cast(input_way.nodes()[1].ref())}, OSMNodeID{static_cast(input_way.nodes()[0].ref())}}); } @@ -372,27 +376,28 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti input_way.nodes().cbegin(), input_way.nodes().cend(), [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) { - external_memory.all_edges_list.push_back( - InternalExtractorEdge(OSMNodeID{static_cast(first_node.ref())}, - OSMNodeID{static_cast(last_node.ref())}, - name_id, - backward_weight_data, - false, - true, - parsed_way.roundabout, - parsed_way.is_access_restricted, - parsed_way.is_startpoint, - parsed_way.backward_travel_mode, - true, - turn_lane_id_backward, - road_classification)); + external_memory.all_edges_list.push_back(InternalExtractorEdge( + OSMNodeID{static_cast(first_node.ref())}, + OSMNodeID{static_cast(last_node.ref())}, + name_id, + backward_weight_data, + false, + true, + parsed_way.roundabout, + parsed_way.is_access_restricted, + parsed_way.is_startpoint, + parsed_way.backward_travel_mode, + true, + turn_lane_id_backward, + road_classification)); }); } external_memory.way_start_end_id_list.push_back( {OSMWayID{static_cast(input_way.id())}, OSMNodeID{static_cast(input_way.nodes().back().ref())}, - OSMNodeID{static_cast(input_way.nodes()[input_way.nodes().size() - 2].ref())}, + OSMNodeID{ + static_cast(input_way.nodes()[input_way.nodes().size() - 2].ref())}, OSMNodeID{static_cast(input_way.nodes()[1].ref())}, OSMNodeID{static_cast(input_way.nodes()[0].ref())}}); } diff --git a/src/extractor/graph_compressor.cpp b/src/extractor/graph_compressor.cpp index 18af7fdb411..55554a2353f 100644 --- a/src/extractor/graph_compressor.cpp +++ b/src/extractor/graph_compressor.cpp @@ -160,7 +160,8 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, * turn-lanes. Without this,we would have to treat any turn-lane beginning/ending just * like a barrier. */ - const auto selectLaneID = [](const LaneDescriptionID front, const LaneDescriptionID back) { + const auto selectLaneID = [](const LaneDescriptionID front, + const LaneDescriptionID back) { // A lane has tags: u - (front) - v - (back) - w // During contraction, we keep only one of the tags. Usually the one closer to the // intersection is preferred. If its empty, however, we keep the non-empty one @@ -168,10 +169,12 @@ void GraphCompressor::Compress(const std::unordered_set &barrier_nodes, return front; return back; }; - graph.GetEdgeData(forward_e1).lane_description_id = selectLaneID( - graph.GetEdgeData(forward_e1).lane_description_id, fwd_edge_data2.lane_description_id); - graph.GetEdgeData(reverse_e1).lane_description_id = selectLaneID( - graph.GetEdgeData(reverse_e1).lane_description_id, rev_edge_data2.lane_description_id); + graph.GetEdgeData(forward_e1).lane_description_id = + selectLaneID(graph.GetEdgeData(forward_e1).lane_description_id, + fwd_edge_data2.lane_description_id); + graph.GetEdgeData(reverse_e1).lane_description_id = + selectLaneID(graph.GetEdgeData(reverse_e1).lane_description_id, + rev_edge_data2.lane_description_id); // remove e2's (if bidir, otherwise only one) graph.DeleteEdge(node_v, forward_e2); diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp index a95ff907ec5..d1934998f25 100644 --- a/src/extractor/guidance/turn_analysis.cpp +++ b/src/extractor/guidance/turn_analysis.cpp @@ -1,6 +1,6 @@ +#include "extractor/guidance/turn_analysis.hpp" #include "extractor/guidance/classification_data.hpp" #include "extractor/guidance/constants.hpp" -#include "extractor/guidance/turn_analysis.hpp" #include "util/coordinate.hpp" #include "util/coordinate_calculation.hpp" @@ -181,8 +181,8 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id, const auto next_road_next_intersection = [&]() { auto intersection = intersection_generator(intersection_node_id, next_road->turn.eid); auto in_edge = next_road->turn.eid; - //skip over traffic lights - if(intersection.size() == 2) + // skip over traffic lights + if (intersection.size() == 2) { const auto node = node_based_graph.GetTarget(in_edge); in_edge = intersection[1].turn.eid; @@ -206,8 +206,8 @@ Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id, { const auto target_intersection = [&](NodeID node, EdgeID eid) { auto intersection = intersection_generator(node, eid); - //skip over traffic lights - if(intersection.size() == 2) + // skip over traffic lights + if (intersection.size() == 2) { node = node_based_graph.GetTarget(eid); eid = intersection[1].turn.eid; diff --git a/src/extractor/guidance/turn_handler.cpp b/src/extractor/guidance/turn_handler.cpp index ebfa0448c65..4722b736eeb 100644 --- a/src/extractor/guidance/turn_handler.cpp +++ b/src/extractor/guidance/turn_handler.cpp @@ -1,7 +1,7 @@ +#include "extractor/guidance/turn_handler.hpp" #include "extractor/guidance/constants.hpp" #include "extractor/guidance/intersection_scenario_three_way.hpp" #include "extractor/guidance/toolkit.hpp" -#include "extractor/guidance/turn_handler.hpp" #include "util/guidance/toolkit.hpp" diff --git a/src/extractor/guidance/turn_lane_augmentation.cpp b/src/extractor/guidance/turn_lane_augmentation.cpp index eee972c9f32..e7fb7561ab8 100644 --- a/src/extractor/guidance/turn_lane_augmentation.cpp +++ b/src/extractor/guidance/turn_lane_augmentation.cpp @@ -268,7 +268,8 @@ LaneDataVector handleNoneValueAtSimpleTurn(LaneDataVector lane_data, ((intersection[0].entry_allowed && lane_data.back().tag != TurnLaneType::uturn) ? 1 : 0); // TODO check for impossible turns to see whether the turn lane is at the correct place - const std::size_t none_index = std::distance(lane_data.begin(), findTag(TurnLaneType::none, lane_data)); + const std::size_t none_index = + std::distance(lane_data.begin(), findTag(TurnLaneType::none, lane_data)); BOOST_ASSERT(none_index != lane_data.size()); // we have to create multiple turns if (connection_count > lane_data.size()) diff --git a/src/extractor/guidance/turn_lane_handler.cpp b/src/extractor/guidance/turn_lane_handler.cpp index e1d254474ab..852f9152150 100644 --- a/src/extractor/guidance/turn_lane_handler.cpp +++ b/src/extractor/guidance/turn_lane_handler.cpp @@ -1,7 +1,7 @@ +#include "extractor/guidance/turn_lane_handler.hpp" #include "extractor/guidance/constants.hpp" #include "extractor/guidance/turn_discovery.hpp" #include "extractor/guidance/turn_lane_augmentation.hpp" -#include "extractor/guidance/turn_lane_handler.hpp" #include "extractor/guidance/turn_lane_matcher.hpp" #include "util/simple_logger.hpp" #include "util/typedefs.hpp" @@ -63,8 +63,8 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at, Intersection intersection, LaneDataIdMap &id_map) const { - //if only a uturn exists, there is nothing we can do - if( intersection.size() == 1 ) + // if only a uturn exists, there is nothing we can do + if (intersection.size() == 1) return intersection; const auto &data = node_based_graph.GetEdgeData(via_edge); @@ -77,7 +77,9 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at, turn_lane_masks.begin() + turn_lane_offsets[data.lane_description_id + 1]) : TurnLaneDescription(); - BOOST_ASSERT( turn_lane_description.empty() || turn_lane_description.size() == (turn_lane_offsets[data.lane_description_id+1] - turn_lane_offsets[data.lane_description_id])); + BOOST_ASSERT(turn_lane_description.empty() || + turn_lane_description.size() == (turn_lane_offsets[data.lane_description_id + 1] - + turn_lane_offsets[data.lane_description_id])); // going straight, due to traffic signals, we can have uncompressed geometry if (intersection.size() == 2 && diff --git a/src/extractor/guidance/turn_lane_matcher.cpp b/src/extractor/guidance/turn_lane_matcher.cpp index 7b0d4135590..5dffafb4072 100644 --- a/src/extractor/guidance/turn_lane_matcher.cpp +++ b/src/extractor/guidance/turn_lane_matcher.cpp @@ -1,5 +1,5 @@ -#include "extractor/guidance/toolkit.hpp" #include "extractor/guidance/turn_lane_matcher.hpp" +#include "extractor/guidance/toolkit.hpp" #include "util/guidance/toolkit.hpp" #include diff --git a/src/extractor/raster_source.cpp b/src/extractor/raster_source.cpp index ee54b4adf5c..9570ca4fbf9 100644 --- a/src/extractor/raster_source.cpp +++ b/src/extractor/raster_source.cpp @@ -154,8 +154,9 @@ SourceContainer::GetRasterInterpolateFromSource(unsigned int source_id, double l BOOST_ASSERT(lon > -180); const auto &found = LoadedSources[source_id]; - return found.GetRasterInterpolate(static_cast(util::toFixed(util::FloatLongitude{lon})), - static_cast(util::toFixed(util::FloatLatitude{lat}))); + return found.GetRasterInterpolate( + static_cast(util::toFixed(util::FloatLongitude{lon})), + static_cast(util::toFixed(util::FloatLatitude{lat}))); } } } diff --git a/src/osrm/osrm.cpp b/src/osrm/osrm.cpp index 9a4b2575c1a..fa31cb3f445 100644 --- a/src/osrm/osrm.cpp +++ b/src/osrm/osrm.cpp @@ -1,13 +1,13 @@ #include "osrm/osrm.hpp" +#include "engine/api/isochrone_parameters.hpp" +#include "engine/api/match_parameters.hpp" +#include "engine/api/nearest_parameters.hpp" #include "engine/api/route_parameters.hpp" #include "engine/api/table_parameters.hpp" -#include "engine/api/nearest_parameters.hpp" #include "engine/api/trip_parameters.hpp" -#include "engine/api/match_parameters.hpp" -#include "engine/api/isochrone_parameters.hpp" #include "engine/engine.hpp" -#include "engine/status.hpp" #include "engine/engine_config.hpp" +#include "engine/status.hpp" #include "util/make_unique.hpp" namespace osrm diff --git a/src/server/api/parameters_parser.cpp b/src/server/api/parameters_parser.cpp index f186c4446f8..7ab495747dd 100644 --- a/src/server/api/parameters_parser.cpp +++ b/src/server/api/parameters_parser.cpp @@ -1,12 +1,12 @@ #include "server/api/parameters_parser.hpp" +#include "server/api/isochrone_parameter_grammar.hpp" #include "server/api/match_parameter_grammar.hpp" #include "server/api/nearest_parameter_grammar.hpp" #include "server/api/route_parameters_grammar.hpp" #include "server/api/table_parameter_grammar.hpp" #include "server/api/tile_parameter_grammar.hpp" #include "server/api/trip_parameter_grammar.hpp" -#include "server/api/isochrone_parameter_grammar.hpp" #include @@ -30,10 +30,12 @@ using is_grammar_t = std::is_same, T>::value || std::is_same, T>::value>; -template ::value, int>::type = 0, typename std::enable_if::value, int>::type = 0> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { using It = std::decay::type; @@ -42,7 +44,8 @@ boost::optional parseParameters(std::string::iterator &iter, const s try { ParameterT parameters; - const auto ok = boost::spirit::qi::parse(iter, end, grammar(boost::phoenix::ref(parameters))); + const auto ok = + boost::spirit::qi::parse(iter, end, grammar(boost::phoenix::ref(parameters))); // return move(a.b) is needed to move b out of a and then return the rvalue by implicit move if (ok && iter == end) @@ -60,37 +63,47 @@ boost::optional parseParameters(std::string::iterator &iter, const s } // ns detail template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { - return detail::parseParameters>(iter, end); + return detail::parseParameters>(iter, + end); } template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { - return detail::parseParameters>(iter, end); + return detail::parseParameters>(iter, + end); } template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { - return detail::parseParameters>(iter, end); + return detail::parseParameters>(iter, + end); } template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { return detail::parseParameters>(iter, end); } template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { - return detail::parseParameters>(iter, end); + return detail::parseParameters>(iter, + end); } template <> -boost::optional parseParameters(std::string::iterator &iter, const std::string::iterator end) +boost::optional parseParameters(std::string::iterator &iter, + const std::string::iterator end) { return detail::parseParameters>(iter, end); } diff --git a/src/server/service/isochrone_service.cpp b/src/server/service/isochrone_service.cpp index 4f407539a71..31d3e2ae461 100644 --- a/src/server/service/isochrone_service.cpp +++ b/src/server/service/isochrone_service.cpp @@ -1,8 +1,8 @@ #include "server/service/isochrone_service.hpp" #include "server/service/utils.hpp" -#include "engine/api/isochrone_parameters.hpp" #include "server/api/parameters_parser.hpp" +#include "engine/api/isochrone_parameters.hpp" #include "util/json_container.hpp" @@ -24,12 +24,13 @@ std::string getWrongOptionHelp(const engine::api::IsochroneParameters ¶meter const auto coord_size = parameters.coordinates.size(); - const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints", - parameters.hints, coord_size, help) || - constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings", - parameters.bearings, coord_size, help) || - constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses", - parameters.radiuses, coord_size, help); + const bool param_size_mismatch = + constrainParamSize( + PARAMETER_SIZE_MISMATCH_MSG, "hints", parameters.hints, coord_size, help) || + constrainParamSize( + PARAMETER_SIZE_MISMATCH_MSG, "bearings", parameters.bearings, coord_size, help) || + constrainParamSize( + PARAMETER_SIZE_MISMATCH_MSG, "radiuses", parameters.radiuses, coord_size, help); if (!param_size_mismatch && parameters.coordinates.size() < 2) { @@ -40,13 +41,15 @@ std::string getWrongOptionHelp(const engine::api::IsochroneParameters ¶meter } } // anon. ns -engine::Status IsochroneService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) +engine::Status +IsochroneService::RunQuery(std::size_t prefix_length, std::string &query, ResultT &result) { result = util::json::Object(); auto &json_result = result.get(); auto query_iterator = query.begin(); - auto parameters = api::parseParameters(query_iterator, query.end()); + auto parameters = + api::parseParameters(query_iterator, query.end()); if (!parameters || query_iterator != query.end()) { const auto position = std::distance(query.begin(), query_iterator); diff --git a/src/server/service_handler.cpp b/src/server/service_handler.cpp index 5c4fb6a6622..065d0cc7741 100644 --- a/src/server/service_handler.cpp +++ b/src/server/service_handler.cpp @@ -1,13 +1,13 @@ -#include #include "server/service_handler.hpp" +#include +#include "server/service/isochrone_service.hpp" +#include "server/service/match_service.hpp" +#include "server/service/nearest_service.hpp" #include "server/service/route_service.hpp" #include "server/service/table_service.hpp" -#include "server/service/nearest_service.hpp" -#include "server/service/trip_service.hpp" -#include "server/service/match_service.hpp" #include "server/service/tile_service.hpp" -#include "server/service/isochrone_service.hpp" +#include "server/service/trip_service.hpp" #include "server/api/parsed_url.hpp" #include "util/json_util.hpp" @@ -25,10 +25,10 @@ ServiceHandler::ServiceHandler(osrm::EngineConfig &config) : routing_machine(con service_map["trip"] = util::make_unique(routing_machine); service_map["match"] = util::make_unique(routing_machine); service_map["tile"] = util::make_unique(routing_machine); - if(config.use_isochrone) { + if (config.use_isochrone) + { service_map["isochrone"] = util::make_unique(routing_machine); } - } engine::Status ServiceHandler::RunQuery(api::ParsedURL parsed_url, diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index 119549db9be..3f27284aa77 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -1,3 +1,4 @@ +#include "storage/storage.hpp" #include "contractor/query_edge.hpp" #include "extractor/compressed_edge_container.hpp" #include "extractor/guidance/turn_instruction.hpp" @@ -8,7 +9,6 @@ #include "storage/shared_barriers.hpp" #include "storage/shared_datatype.hpp" #include "storage/shared_memory.hpp" -#include "storage/storage.hpp" #include "engine/datafacade/datafacade_base.hpp" #include "util/coordinate.hpp" #include "util/exception.hpp" @@ -375,7 +375,7 @@ int Storage::Run() } std::uint64_t num_bearings; - intersection_stream.read(reinterpret_cast(&num_bearings),sizeof(num_bearings)); + intersection_stream.read(reinterpret_cast(&num_bearings), sizeof(num_bearings)); std::vector bearing_class_table(num_bearings); intersection_stream.read(reinterpret_cast(&bearing_class_table[0]), diff --git a/src/tools/routed.cpp b/src/tools/routed.cpp index 7b9729f4a27..16c3f2b4d4d 100644 --- a/src/tools/routed.cpp +++ b/src/tools/routed.cpp @@ -3,8 +3,8 @@ #include "util/simple_logger.hpp" #include "util/version.hpp" -#include "osrm/osrm.hpp" #include "osrm/engine_config.hpp" +#include "osrm/osrm.hpp" #include "osrm/storage_config.hpp" #include @@ -23,8 +23,8 @@ #include #include #include -#include #include +#include #ifdef _WIN32 boost::function0 console_ctrl_function; @@ -78,11 +78,14 @@ inline unsigned generateServerProgramOptions(const int argc, // declare a group of options that will be allowed on command line boost::program_options::options_description config_options("Configuration"); config_options.add_options() // - ("ip,i", value(&ip_address)->default_value("0.0.0.0"), + ("ip,i", + value(&ip_address)->default_value("0.0.0.0"), "IP address") // - ("port,p", value(&ip_port)->default_value(5000), + ("port,p", + value(&ip_port)->default_value(5000), "TCP/IP port") // - ("threads,t", value(&requested_num_threads)->default_value(8), + ("threads,t", + value(&requested_num_threads)->default_value(8), "Number of threads to use") // ("shared-memory,s", value(&use_shared_memory)->implicit_value(true)->default_value(false), @@ -90,19 +93,23 @@ inline unsigned generateServerProgramOptions(const int argc, ("isochrone,I", value(&use_isochrone)->implicit_value(true)->default_value(false), "Load the isochrone plugin") // - ("max-viaroute-size", value(&max_locations_viaroute)->default_value(500), + ("max-viaroute-size", + value(&max_locations_viaroute)->default_value(500), "Max. locations supported in viaroute query") // - ("max-trip-size", value(&max_locations_trip)->default_value(100), + ("max-trip-size", + value(&max_locations_trip)->default_value(100), "Max. locations supported in trip query") // - ("max-table-size", value(&max_locations_distance_table)->default_value(100), + ("max-table-size", + value(&max_locations_distance_table)->default_value(100), "Max. locations supported in distance table query") // - ("max-matching-size", value(&max_locations_map_matching)->default_value(100), + ("max-matching-size", + value(&max_locations_map_matching)->default_value(100), "Max. locations supported in map matching query"); // hidden options, will be allowed on command line, but will not be shown to the user boost::program_options::options_description hidden_options("Hidden options"); - hidden_options.add_options()("base,b", value(&base_path), - "base path to .osrm file"); + hidden_options.add_options()( + "base,b", value(&base_path), "base path to .osrm file"); // positional option boost::program_options::positional_options_description positional_options; @@ -112,7 +119,7 @@ inline unsigned generateServerProgramOptions(const int argc, boost::program_options::options_description cmdline_options; cmdline_options.add(generic_options).add(config_options).add(hidden_options); - const auto* executable = argv[0]; + const auto *executable = argv[0]; boost::program_options::options_description visible_options( boost::filesystem::path(executable).filename().string() + " []"); visible_options.add(generic_options).add(config_options); @@ -149,7 +156,8 @@ inline unsigned generateServerProgramOptions(const int argc, } else if (use_shared_memory && option_variables.count("base")) { - util::SimpleLogger().Write(logWARNING) << "Shared memory settings conflict with path settings."; + util::SimpleLogger().Write(logWARNING) + << "Shared memory settings conflict with path settings."; } util::SimpleLogger().Write() << visible_options; @@ -166,11 +174,19 @@ int main(int argc, const char *argv[]) try EngineConfig config; boost::filesystem::path base_path; - const unsigned init_result = generateServerProgramOptions( - argc, argv, base_path, ip_address, ip_port, requested_thread_num, config.use_shared_memory, - trial_run, config.max_locations_trip, config.max_locations_viaroute, - config.max_locations_distance_table, config.max_locations_map_matching, - config.use_isochrone); + const unsigned init_result = generateServerProgramOptions(argc, + argv, + base_path, + ip_address, + ip_port, + requested_thread_num, + config.use_shared_memory, + trial_run, + config.max_locations_trip, + config.max_locations_viaroute, + config.max_locations_distance_table, + config.max_locations_map_matching, + config.use_isochrone); if (init_result == INIT_OK_DO_NOT_START_ENGINE) { return EXIT_SUCCESS; @@ -183,7 +199,7 @@ int main(int argc, const char *argv[]) try { config.storage_config = storage::StorageConfig(base_path); } - if(!config.IsValid()) + if (!config.IsValid()) { if (base_path.empty() != config.use_shared_memory) { @@ -191,53 +207,65 @@ int main(int argc, const char *argv[]) try } else { - if(!boost::filesystem::is_regular_file(config.storage_config.ram_index_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.ram_index_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.ram_index_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.ram_index_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.file_index_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.file_index_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.file_index_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.file_index_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.hsgr_data_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.hsgr_data_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.hsgr_data_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.hsgr_data_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.nodes_data_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.nodes_data_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.nodes_data_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.nodes_data_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.edges_data_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.edges_data_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.edges_data_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.edges_data_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.core_data_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.core_data_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.core_data_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.core_data_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.geometries_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.geometries_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.geometries_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.geometries_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.timestamp_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.timestamp_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.timestamp_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.timestamp_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.datasource_names_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.datasource_names_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.datasource_names_path << " is not found"; + util::SimpleLogger().Write(logWARNING) + << config.storage_config.datasource_names_path << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.datasource_indexes_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.datasource_indexes_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.datasource_indexes_path << " is not found"; + util::SimpleLogger().Write(logWARNING) + << config.storage_config.datasource_indexes_path << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.names_data_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.names_data_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.names_data_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.names_data_path + << " is not found"; } - if(!boost::filesystem::is_regular_file(config.storage_config.properties_path)) + if (!boost::filesystem::is_regular_file(config.storage_config.properties_path)) { - util::SimpleLogger().Write(logWARNING) << config.storage_config.properties_path << " is not found"; + util::SimpleLogger().Write(logWARNING) << config.storage_config.properties_path + << " is not found"; } } return EXIT_FAILURE; @@ -292,11 +320,10 @@ int main(int argc, const char *argv[]) try } else { - std::packaged_task server_task([&] - { - routing_server->Run(); - return 0; - }); + std::packaged_task server_task([&] { + routing_server->Run(); + return 0; + }); auto future = server_task.get_future(); std::thread server_thread(std::move(server_task)); @@ -309,7 +336,8 @@ int main(int argc, const char *argv[]) try sigaddset(&wait_mask, SIGTERM); pthread_sigmask(SIG_BLOCK, &wait_mask, nullptr); util::SimpleLogger().Write() << "running and waiting for requests"; - if(std::getenv("SIGNAL_PARENT_WHEN_READY")) { + if (std::getenv("SIGNAL_PARENT_WHEN_READY")) + { kill(getppid(), SIGUSR1); } sigwait(&wait_mask, &sig); diff --git a/src/util/coordinate_calculation.cpp b/src/util/coordinate_calculation.cpp index 029129bfba1..9dff34a1c73 100644 --- a/src/util/coordinate_calculation.cpp +++ b/src/util/coordinate_calculation.cpp @@ -284,8 +284,10 @@ Coordinate interpolateLinear(double factor, const Coordinate from, const Coordin const auto to_lon = static_cast(to.lon); const auto to_lat = static_cast(to.lat); - FixedLongitude interpolated_lon{static_cast(from_lon + factor * (to_lon - from_lon))}; - FixedLatitude interpolated_lat{static_cast(from_lat + factor * (to_lat - from_lat))}; + FixedLongitude interpolated_lon{ + static_cast(from_lon + factor * (to_lon - from_lon))}; + FixedLatitude interpolated_lat{ + static_cast(from_lat + factor * (to_lat - from_lat))}; return {std::move(interpolated_lon), std::move(interpolated_lat)}; } diff --git a/unit_tests/util/packed_vector.cpp b/unit_tests/util/packed_vector.cpp index e95bc03a78c..adb58d4f162 100644 --- a/unit_tests/util/packed_vector.cpp +++ b/unit_tests/util/packed_vector.cpp @@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(insert_and_retrieve_packed_test) for (std::size_t i = 0; i < num_test_cases; i++) { - OSMNodeID r {static_cast(rand() % 2147483647)}; // max 33-bit uint + OSMNodeID r{static_cast(rand() % 2147483647)}; // max 33-bit uint packed_ids.push_back(r); original_ids.push_back(r); From 554303d65abc0e159357f872d7c0527aa627916a Mon Sep 17 00:00:00 2001 From: roroettg Date: Tue, 9 Aug 2016 12:17:04 +0200 Subject: [PATCH 39/43] merge --- src/tools/components.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/components.cpp b/src/tools/components.cpp index 9ee2d61f1a0..9a674ac652c 100644 --- a/src/tools/components.cpp +++ b/src/tools/components.cpp @@ -227,4 +227,7 @@ int main(int argc, char *argv[]) try osrm::util::SimpleLogger().Write() << "finished component analysis"; return EXIT_SUCCESS; +} catch(...) { + } + From fbc52125b774fb626ef916e8e096f8eddc9f2d4f Mon Sep 17 00:00:00 2001 From: roroettg Date: Tue, 23 Aug 2016 11:05:23 +0200 Subject: [PATCH 40/43] timing --- src/engine/plugins/isochrone.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index d59bb798cb9..aaaf46cbc79 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -148,11 +148,13 @@ void IsochronePlugin::dijkstraByDuration(IsochroneVector &isochroneSet, isochroneSet.emplace_back( IsochroneNode(coordinate_list[source], coordinate_list[source], 0, 0)); + int steps = 0; int MAX_DURATION = duration * 60 * 10; { // Standard Dijkstra search, terminating when path length > MAX while (!heap.Empty()) { + steps++; const NodeID source = heap.DeleteMin(); const std::int32_t weight = heap.GetKey(source); @@ -190,6 +192,7 @@ void IsochronePlugin::dijkstraByDuration(IsochroneVector &isochroneSet, } } } + util::SimpleLogger().Write() << "Steps took: " << steps; } void IsochronePlugin::dijkstraByDistance(IsochroneVector &isochroneSet, NodeID &source, @@ -200,12 +203,13 @@ void IsochronePlugin::dijkstraByDistance(IsochroneVector &isochroneSet, isochroneSet.emplace_back( IsochroneNode(coordinate_list[source], coordinate_list[source], 0, 0)); - + int steps = 0; int MAX_DISTANCE = distance; { // Standard Dijkstra search, terminating when path length > MAX while (!heap.Empty()) { + steps++; NodeID source = heap.DeleteMin(); std::int32_t weight = heap.GetKey(source); @@ -250,6 +254,7 @@ void IsochronePlugin::dijkstraByDistance(IsochroneVector &isochroneSet, } } } + util::SimpleLogger().Write() << "Steps took: " << steps; } std::size_t IsochronePlugin::loadGraph(const std::string &path, std::vector &coordinate_list, From 667cc379ab52f4afc19c083473f8ba86af5c81de Mon Sep 17 00:00:00 2001 From: roroettg Date: Wed, 24 Aug 2016 11:54:27 +0200 Subject: [PATCH 41/43] first docker impl --- mydocker/Dockerfile | 44 +++++++++++++++++++++++++++++++++++ mydocker/build-image.sh | 8 +++++++ mydocker/docker-entrypoint.sh | 25 ++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 mydocker/Dockerfile create mode 100755 mydocker/build-image.sh create mode 100644 mydocker/docker-entrypoint.sh diff --git a/mydocker/Dockerfile b/mydocker/Dockerfile new file mode 100644 index 00000000000..0c3714e6936 --- /dev/null +++ b/mydocker/Dockerfile @@ -0,0 +1,44 @@ +FROM ubuntu + +MAINTAINER Robin Roettger + +RUN apt-get update -y && apt-get install -y software-properties-common +RUN add-apt-repository ppa:ubuntu-toolchain-r/test +RUN apt-get update -y && apt-get install -y g++-5 libbz2-dev libstxxl-dev libstxxl1v5 libxml2-dev libzip-dev lua5.1 liblua5.1-0-dev libtbb-dev libgdal-dev libluabind-dev libboost-all-dev ccache +RUN apt-get -y install curl cmake cmake-curses-gui git + + +RUN mkdir -p /osrm-src \ + && mkdir -p /osrm-build \ + && mkdir -p /osrm-data + +WORKDIR /osrm-build + +RUN git clone https://github.com/beemogmbh/osrm-backend.git /osrm-src \ + && cd /osrm-src \ + && git fetch \ + && git checkout master \ + && cd /osrm-build \ + && cmake /osrm-src -DCMAKE_BUILD_TYPE=Release\ + && cmake --build . \ + && mv /osrm-src/profiles/car.lua profile.lua \ + && mv /osrm-src/profiles/lib/ lib \ + && echo "disk=/tmp/stxxl,25000,syscall" > .stxxl \ + && rm -rf /osrm-src + + +# Cleanup -------------------------------- + +RUN apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Publish -------------------------------- + +COPY docker-entrypoint.sh / +RUN chmod +x /docker-entrypoint.sh +ENTRYPOINT ["/docker-entrypoint.sh"] + + +EXPOSE 5000 + + diff --git a/mydocker/build-image.sh b/mydocker/build-image.sh new file mode 100755 index 00000000000..83f24c896f7 --- /dev/null +++ b/mydocker/build-image.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +set -e +set -o pipefail + +docker build \ + -t beemo/osrm:linux \ + mydocker/ diff --git a/mydocker/docker-entrypoint.sh b/mydocker/docker-entrypoint.sh new file mode 100644 index 00000000000..b41fd36cf00 --- /dev/null +++ b/mydocker/docker-entrypoint.sh @@ -0,0 +1,25 @@ +#!/bin/bash +DATA_PATH=${DATA_PATH:="/osrm-data"} + +_sig() { + kill -TERM $child 2>/dev/null +} + +if [ "$1" = 'osrm' ]; then + trap _sig SIGKILL SIGTERM SIGHUP SIGINT EXIT + + if [ ! -f $DATA_PATH/$2.osrm ]; then + if [ ! -f $DATA_PATH/$2.osm.pbf ]; then + curl $3 > $DATA_PATH/$2.osm.pbf + fi + ./osrm-extract $DATA_PATH/$2.osm.pbf + ./osrm-contract $DATA_PATH/$2.osrm + rm $DATA_PATH/$2.osm.pbf + fi + + ./osrm-routed $DATA_PATH/$2.osrm -I 1& + child=$! + wait "$child" +else + exec "$@" +fi From b694ab639cdbe513a0383dc029cb9f0e7351b59f Mon Sep 17 00:00:00 2001 From: roroettg Date: Thu, 1 Sep 2016 14:27:27 +0200 Subject: [PATCH 42/43] removed docker --- mydocker/Dockerfile | 44 ----------------------------------- mydocker/build-image.sh | 8 ------- mydocker/docker-entrypoint.sh | 25 -------------------- 3 files changed, 77 deletions(-) delete mode 100644 mydocker/Dockerfile delete mode 100755 mydocker/build-image.sh delete mode 100644 mydocker/docker-entrypoint.sh diff --git a/mydocker/Dockerfile b/mydocker/Dockerfile deleted file mode 100644 index 0c3714e6936..00000000000 --- a/mydocker/Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -FROM ubuntu - -MAINTAINER Robin Roettger - -RUN apt-get update -y && apt-get install -y software-properties-common -RUN add-apt-repository ppa:ubuntu-toolchain-r/test -RUN apt-get update -y && apt-get install -y g++-5 libbz2-dev libstxxl-dev libstxxl1v5 libxml2-dev libzip-dev lua5.1 liblua5.1-0-dev libtbb-dev libgdal-dev libluabind-dev libboost-all-dev ccache -RUN apt-get -y install curl cmake cmake-curses-gui git - - -RUN mkdir -p /osrm-src \ - && mkdir -p /osrm-build \ - && mkdir -p /osrm-data - -WORKDIR /osrm-build - -RUN git clone https://github.com/beemogmbh/osrm-backend.git /osrm-src \ - && cd /osrm-src \ - && git fetch \ - && git checkout master \ - && cd /osrm-build \ - && cmake /osrm-src -DCMAKE_BUILD_TYPE=Release\ - && cmake --build . \ - && mv /osrm-src/profiles/car.lua profile.lua \ - && mv /osrm-src/profiles/lib/ lib \ - && echo "disk=/tmp/stxxl,25000,syscall" > .stxxl \ - && rm -rf /osrm-src - - -# Cleanup -------------------------------- - -RUN apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Publish -------------------------------- - -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh -ENTRYPOINT ["/docker-entrypoint.sh"] - - -EXPOSE 5000 - - diff --git a/mydocker/build-image.sh b/mydocker/build-image.sh deleted file mode 100755 index 83f24c896f7..00000000000 --- a/mydocker/build-image.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -o pipefail - -docker build \ - -t beemo/osrm:linux \ - mydocker/ diff --git a/mydocker/docker-entrypoint.sh b/mydocker/docker-entrypoint.sh deleted file mode 100644 index b41fd36cf00..00000000000 --- a/mydocker/docker-entrypoint.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -DATA_PATH=${DATA_PATH:="/osrm-data"} - -_sig() { - kill -TERM $child 2>/dev/null -} - -if [ "$1" = 'osrm' ]; then - trap _sig SIGKILL SIGTERM SIGHUP SIGINT EXIT - - if [ ! -f $DATA_PATH/$2.osrm ]; then - if [ ! -f $DATA_PATH/$2.osm.pbf ]; then - curl $3 > $DATA_PATH/$2.osm.pbf - fi - ./osrm-extract $DATA_PATH/$2.osm.pbf - ./osrm-contract $DATA_PATH/$2.osrm - rm $DATA_PATH/$2.osm.pbf - fi - - ./osrm-routed $DATA_PATH/$2.osrm -I 1& - child=$! - wait "$child" -else - exec "$@" -fi From 80062ae52039d202249e5bafc2491a6488dfeef6 Mon Sep 17 00:00:00 2001 From: roroettg Date: Mon, 10 Oct 2016 12:43:36 +0200 Subject: [PATCH 43/43] merge --- .travis.yml | 28 +- CHANGELOG.md | 36 +- CMakeLists.txt | 24 +- cmake/FindLua.cmake | 188 ++ cmake/FindLua52.cmake | 82 - cmake/FindLuaJIT.cmake | 93 - cmake/FindLuabind.cmake | 17 +- cmake/check_luabind.cmake | 40 - cucumber.js | 10 +- docs/http.md | 13 +- example/example.cpp | 2 +- features/bicycle/names.feature | 4 +- features/bicycle/ref.feature | 8 +- features/car/access.feature | 19 + features/car/names.feature | 29 +- features/car/summaries.feature | 94 + features/car/traffic_speeds.feature | 39 +- features/car/traffic_turn_penalties.feature | 4 +- features/foot/names.feature | 2 +- features/foot/ref.feature | 6 +- features/guidance/advanced-lanes.feature | 22 +- features/guidance/anticipate-lanes.feature | 9 +- features/guidance/collapse.feature | 46 + .../guidance/dedicated-turn-roads.feature | 89 +- features/guidance/destination-signs.feature | 20 +- features/guidance/fork.feature | 31 + features/guidance/new-name.feature | 169 ++ features/guidance/notification.feature | 57 + features/guidance/roundabout-turn.feature | 46 +- features/guidance/roundabout.feature | 62 +- features/guidance/suffix-changes.feature | 8 +- features/guidance/suppressed.feature | 4 +- features/guidance/turn-lanes.feature | 17 +- features/guidance/turn.feature | 86 + features/lib/hash.js | 31 + features/{support/build_osm.js => lib/osm.js} | 8 +- features/lib/osrm_loader.js | 169 ++ features/lib/table_diff.js | 54 + features/lib/try_connect.js | 13 + features/lib/utils.js | 17 + features/options/contract/datasources.feature | 10 +- .../edge-weight-updates-over-factor.feature | 30 + features/options/contract/files.feature | 13 +- features/options/contract/help.feature | 8 +- features/options/contract/invalid.feature | 4 +- features/options/contract/version.feature | 6 +- features/options/extract/files.feature | 14 +- features/options/extract/help.feature | 6 +- features/options/extract/invalid.feature | 4 +- features/options/extract/version.feature | 4 +- features/options/routed/files.feature | 2 +- features/options/routed/help.feature | 6 +- features/options/routed/invalid.feature | 8 +- features/options/routed/version.feature | 4 +- features/raster/extract.feature | 19 +- features/raster/weights.feature | 20 +- features/step_definitions/data.js | 144 +- features/step_definitions/distance_matrix.js | 8 - features/step_definitions/hooks.js | 18 - features/step_definitions/matching.js | 1 - features/step_definitions/nearest.js | 8 - features/step_definitions/options.js | 74 +- features/step_definitions/requests.js | 2 +- features/step_definitions/routability.js | 8 +- features/step_definitions/trip.js | 23 +- features/support/cache.js | 190 ++ features/support/config.js | 127 -- features/support/data.js | 259 +-- features/support/data_classes.js | 40 +- features/support/env.js | 130 +- features/support/exception_classes.js | 132 -- features/support/exceptions.js | 15 - features/support/hash.js | 43 - features/support/hooks.js | 73 +- features/support/http.js | 12 +- features/support/launch.js | 5 - features/support/launch_classes.js | 164 -- features/support/log.js | 90 - features/support/route.js | 19 +- features/support/run.js | 70 +- features/support/shared_steps.js | 26 +- features/support/table_patch.js | 11 - features/testbot/bad.feature | 4 +- features/testbot/bugs.feature | 5 - features/testbot/matching.feature | 2 +- features/testbot/side_bias.feature | 35 +- features/testbot/summary.feature | 6 +- .../testbot/traffic_turn_penalties.feature | 2 +- features/testbot/trip.feature | 16 +- features/testbot/via.feature | 2 +- include/contractor/contractor.hpp | 3 +- include/contractor/contractor_config.hpp | 1 + include/engine/api/route_parameters.hpp | 15 + include/engine/datafacade/datafacade_base.hpp | 23 +- .../engine/datafacade/internal_datafacade.hpp | 176 +- .../engine/datafacade/shared_datafacade.hpp | 187 +- include/engine/edge_unpacker.hpp | 107 ++ include/engine/engine.hpp | 18 +- include/engine/engine_config.hpp | 2 + include/engine/geospatial_query.hpp | 67 +- include/engine/guidance/assemble_geometry.hpp | 20 +- include/engine/guidance/assemble_leg.hpp | 38 +- include/engine/guidance/assemble_steps.hpp | 5 + include/engine/guidance/post_processing.hpp | 1 - include/engine/guidance/route_step.hpp | 6 +- include/engine/hint.hpp | 4 +- include/engine/phantom_node.hpp | 19 +- include/engine/plugins/isochrone.hpp | 6 +- include/engine/plugins/match.hpp | 10 +- include/engine/plugins/nearest.hpp | 9 +- include/engine/plugins/plugin_base.hpp | 15 +- include/engine/plugins/table.hpp | 7 +- include/engine/plugins/tile.hpp | 8 +- include/engine/plugins/trip.hpp | 12 +- include/engine/plugins/viaroute.hpp | 5 +- .../routing_algorithms/alternative_path.hpp | 148 +- .../direct_shortest_path.hpp | 23 +- .../routing_algorithms/many_to_many.hpp | 55 +- .../routing_algorithms/map_matching.hpp | 20 +- .../routing_algorithms/routing_base.hpp | 411 ++--- .../routing_algorithms/shortest_path.hpp | 63 +- include/engine/trip/trip_brute_force.hpp | 8 +- .../engine/trip/trip_farthest_insertion.hpp | 2 +- .../extractor/compressed_edge_container.hpp | 26 +- .../extractor/edge_based_graph_factory.hpp | 4 +- include/extractor/edge_based_node.hpp | 13 +- include/extractor/extraction_containers.hpp | 4 - include/extractor/extraction_way.hpp | 2 + include/extractor/extractor_callbacks.hpp | 27 +- .../extractor/guidance/roundabout_handler.hpp | 5 +- include/extractor/guidance/toolkit.hpp | 120 +- include/extractor/original_edge_data.hpp | 8 +- include/extractor/restriction_parser.hpp | 4 +- include/extractor/scripting_environment.hpp | 2 +- .../extractor/scripting_environment_lua.hpp | 2 +- include/extractor/suffix_table.hpp | 1 + include/osrm/osrm.hpp | 14 +- include/util/bearing.hpp | 2 +- include/util/guidance/toolkit.hpp | 129 +- include/util/json_deep_compare.hpp | 2 +- include/util/json_logger.hpp | 62 - include/util/name_table.hpp | 6 + include/util/shared_memory_vector_wrapper.hpp | 32 + include/util/static_graph.hpp | 23 +- include/util/static_rtree.hpp | 1 + include/util/typedefs.hpp | 17 + include/util/vector_tile.hpp | 23 +- package.json | 2 + profiles/bicycle.lua | 19 +- profiles/car.lua | 83 +- profiles/foot.lua | 15 +- profiles/lhs.lua | 20 - profiles/rasterbot.lua | 6 +- profiles/rasterbotinterp.lua | 6 +- profiles/rhs.lua | 20 - scripts/travis/before_install.i686.sh | 2 +- ...epdendencies.sh => update_dependencies.sh} | 7 +- src/contractor/contractor.cpp | 191 +- src/engine/api/json_factory.cpp | 2 + src/engine/engine.cpp | 76 +- src/engine/engine_config.cpp | 14 +- src/engine/guidance/post_processing.cpp | 112 +- src/engine/plugins/isochrone.cpp | 21 +- src/engine/plugins/match.cpp | 18 +- src/engine/plugins/nearest.cpp | 21 +- src/engine/plugins/table.cpp | 16 +- src/engine/plugins/tile.cpp | 996 ++++++++--- src/engine/plugins/trip.cpp | 16 +- src/engine/plugins/viaroute.cpp | 27 +- src/engine/polyline_compressor.cpp | 4 +- src/extractor/compressed_edge_container.cpp | 98 +- src/extractor/edge_based_graph_factory.cpp | 47 +- src/extractor/extraction_containers.cpp | 3 +- src/extractor/extractor.cpp | 3 +- src/extractor/extractor_callbacks.cpp | 32 +- .../guidance/intersection_generator.cpp | 16 +- .../guidance/intersection_handler.cpp | 102 +- src/extractor/guidance/roundabout_handler.cpp | 124 +- src/extractor/guidance/sliproad_handler.cpp | 51 +- src/extractor/guidance/turn_analysis.cpp | 3 +- src/extractor/guidance/turn_handler.cpp | 90 +- .../guidance/turn_lane_augmentation.cpp | 2 - src/extractor/guidance/turn_lane_matcher.cpp | 2 +- src/extractor/restriction_parser.cpp | 49 +- src/extractor/scripting_environment_lua.cpp | 12 +- src/osrm/osrm.cpp | 14 +- src/storage/storage.cpp | 4 +- src/tools/contract.cpp | 7 +- src/tools/routed.cpp | 9 +- src/util/name_table.cpp | 21 + taginfo.json | 18 + third_party/libosmium/.gitignore | 2 + third_party/libosmium/.travis.yml | 86 +- third_party/libosmium/.ycm_extra_conf.py | 5 + third_party/libosmium/CHANGELOG.md | 141 +- third_party/libosmium/CMakeLists.txt | 25 +- third_party/libosmium/CONTRIBUTING.md | 147 +- third_party/libosmium/NOTES_FOR_DEVELOPERS.md | 143 ++ third_party/libosmium/appveyor.yml | 85 +- .../benchmarks/osmium_benchmark_count.cpp | 8 +- .../benchmarks/osmium_benchmark_count_tag.cpp | 8 +- .../benchmarks/osmium_benchmark_index_map.cpp | 16 +- ...mium_benchmark_static_vs_dynamic_index.cpp | 58 +- .../benchmarks/osmium_benchmark_write_pbf.cpp | 17 +- third_party/libosmium/build-appveyor.bat | 123 ++ third_party/libosmium/build-local.bat | 43 + third_party/libosmium/cmake/iwyu.sh | 5 + third_party/libosmium/examples/CMakeLists.txt | 40 +- third_party/libosmium/examples/README.md | 32 + .../libosmium/examples/osmium_area_test.cpp | 131 +- .../libosmium/examples/osmium_convert.cpp | 92 +- .../libosmium/examples/osmium_count.cpp | 69 +- .../examples/osmium_create_node_cache.cpp | 55 - .../libosmium/examples/osmium_debug.cpp | 46 +- .../examples/osmium_filter_discussions.cpp | 48 +- .../libosmium/examples/osmium_index.cpp | 145 +- .../examples/osmium_location_cache_create.cpp | 87 + .../examples/osmium_location_cache_use.cpp | 101 ++ .../libosmium/examples/osmium_pub_names.cpp | 89 + .../libosmium/examples/osmium_read.cpp | 26 +- .../examples/osmium_read_with_progress.cpp | 56 + .../libosmium/examples/osmium_road_length.cpp | 92 + .../libosmium/examples/osmium_serdump.cpp | 24 +- .../libosmium/examples/osmium_tiles.cpp | 72 + .../examples/osmium_use_node_cache.cpp | 68 - third_party/libosmium/include/gdalcpp.hpp | 91 +- .../include/osmium/area/assembler.hpp | 1564 +++++++++++++---- .../osmium/area/detail/node_ref_segment.hpp | 310 +++- .../include/osmium/area/detail/proto_ring.hpp | 240 +-- .../osmium/area/detail/segment_list.hpp | 173 +- .../include/osmium/area/detail/vector.hpp | 121 ++ .../osmium/area/multipolygon_collector.hpp | 31 +- .../include/osmium/area/problem_reporter.hpp | 78 +- .../area/problem_reporter_exception.hpp | 31 +- .../osmium/area/problem_reporter_ogr.hpp | 143 +- .../osmium/area/problem_reporter_stream.hpp | 33 +- .../libosmium/include/osmium/area/stats.hpp | 128 ++ .../libosmium/include/osmium/builder/attr.hpp | 37 +- .../include/osmium/builder/builder.hpp | 3 +- .../include/osmium/builder/builder_helper.hpp | 8 +- .../osmium/builder/osm_object_builder.hpp | 22 +- .../include/osmium/diff_iterator.hpp | 10 +- .../include/osmium/dynamic_handler.hpp | 9 +- .../osmium/experimental/flex_reader.hpp | 3 +- .../libosmium/include/osmium/geom/factory.hpp | 34 +- .../libosmium/include/osmium/geom/geojson.hpp | 13 +- .../libosmium/include/osmium/geom/geos.hpp | 57 +- .../include/osmium/geom/haversine.hpp | 2 +- .../libosmium/include/osmium/geom/ogr.hpp | 38 +- .../include/osmium/geom/projection.hpp | 22 +- .../include/osmium/geom/rapid_geojson.hpp | 14 +- .../libosmium/include/osmium/geom/tile.hpp | 45 +- .../libosmium/include/osmium/geom/util.hpp | 4 +- .../libosmium/include/osmium/geom/wkb.hpp | 30 +- .../libosmium/include/osmium/geom/wkt.hpp | 39 +- .../libosmium/include/osmium/handler.hpp | 56 +- .../include/osmium/handler/chain.hpp | 2 +- .../include/osmium/handler/check_order.hpp | 4 +- .../include/osmium/handler/disk_store.hpp | 2 +- .../handler/node_locations_for_ways.hpp | 18 +- .../osmium/handler/object_relations.hpp | 2 +- .../index/detail/create_map_with_fd.hpp | 4 +- .../osmium/index/detail/mmap_vector_base.hpp | 38 +- .../osmium/index/detail/mmap_vector_file.hpp | 19 +- .../osmium/index/detail/vector_map.hpp | 18 +- .../osmium/index/detail/vector_multimap.hpp | 10 +- .../libosmium/include/osmium/index/index.hpp | 4 +- .../libosmium/include/osmium/index/map.hpp | 14 +- .../osmium/index/map/sparse_mem_map.hpp | 3 +- .../include/osmium/index/multimap.hpp | 8 +- .../include/osmium/index/multimap/hybrid.hpp | 14 +- .../index/multimap/sparse_mem_multimap.hpp | 11 +- .../libosmium/include/osmium/io/any_input.hpp | 3 +- .../include/osmium/io/bzip2_compression.hpp | 10 +- .../include/osmium/io/compression.hpp | 45 +- .../osmium/io/detail/debug_output_format.hpp | 217 ++- .../include/osmium/io/detail/input_format.hpp | 16 +- .../osmium/io/detail/o5m_input_format.hpp | 13 +- .../osmium/io/detail/opl_input_format.hpp | 156 ++ .../osmium/io/detail/opl_output_format.hpp | 188 +- .../osmium/io/detail/opl_parser_functions.hpp | 747 ++++++++ .../osmium/io/detail/output_format.hpp | 33 +- .../include/osmium/io/detail/pbf.hpp | 2 +- .../include/osmium/io/detail/pbf_decoder.hpp | 237 ++- .../osmium/io/detail/pbf_input_format.hpp | 21 +- .../osmium/io/detail/pbf_output_format.hpp | 68 +- .../osmium/io/detail/protobuf_tags.hpp | 4 +- .../include/osmium/io/detail/queue_util.hpp | 19 +- .../include/osmium/io/detail/read_write.hpp | 7 +- .../include/osmium/io/detail/string_table.hpp | 77 +- .../include/osmium/io/detail/string_util.hpp | 57 +- .../include/osmium/io/detail/write_thread.hpp | 1 + .../osmium/io/detail/xml_input_format.hpp | 157 +- .../osmium/io/detail/xml_output_format.hpp | 116 +- .../include/osmium/io/detail/zlib.hpp | 12 +- .../libosmium/include/osmium/io/file.hpp | 6 +- .../include/osmium/io/gzip_compression.hpp | 12 +- .../include/osmium/io/input_iterator.hpp | 21 +- .../libosmium/include/osmium/io/opl_input.hpp | 46 + .../include/osmium/io/output_iterator.hpp | 11 +- .../libosmium/include/osmium/io/overwrite.hpp | 2 +- .../libosmium/include/osmium/io/reader.hpp | 61 +- .../libosmium/include/osmium/io/writer.hpp | 24 +- .../include/osmium/memory/buffer.hpp | 10 + .../include/osmium/memory/collection.hpp | 16 +- .../libosmium/include/osmium/memory/item.hpp | 36 +- .../include/osmium/memory/item_iterator.hpp | 125 +- .../osmium/object_pointer_collection.hpp | 6 +- third_party/libosmium/include/osmium/opl.hpp | 67 + third_party/libosmium/include/osmium/osm.hpp | 15 +- .../libosmium/include/osmium/osm/area.hpp | 35 +- .../libosmium/include/osmium/osm/box.hpp | 1 - .../include/osmium/osm/changeset.hpp | 25 +- .../libosmium/include/osmium/osm/crc.hpp | 29 +- .../include/osmium/osm/diff_object.hpp | 5 +- .../include/osmium/osm/entity_bits.hpp | 18 +- .../libosmium/include/osmium/osm/location.hpp | 284 ++- .../include/osmium/osm/node_ref_list.hpp | 3 +- .../libosmium/include/osmium/osm/object.hpp | 91 +- .../include/osmium/osm/object_comparisons.hpp | 29 +- .../libosmium/include/osmium/osm/relation.hpp | 4 +- .../libosmium/include/osmium/osm/segment.hpp | 1 - .../libosmium/include/osmium/osm/tag.hpp | 60 +- .../include/osmium/osm/timestamp.hpp | 83 +- .../libosmium/include/osmium/osm/types.hpp | 20 +- .../include/osmium/osm/types_from_string.hpp | 21 +- .../libosmium/include/osmium/osm/way.hpp | 5 +- .../include/osmium/relations/collector.hpp | 53 +- .../osmium/relations/detail/member_meta.hpp | 4 +- .../osmium/relations/detail/relation_meta.hpp | 4 +- .../libosmium/include/osmium/tags/filter.hpp | 19 +- .../include/osmium/tags/regex_filter.hpp | 2 +- .../libosmium/include/osmium/tags/taglist.hpp | 2 +- .../libosmium/include/osmium/thread/pool.hpp | 27 +- .../libosmium/include/osmium/thread/queue.hpp | 64 +- .../include/osmium/thread/sorted_queue.hpp | 159 -- .../libosmium/include/osmium/thread/util.hpp | 2 + .../libosmium/include/osmium/util/config.hpp | 17 +- .../libosmium/include/osmium/util/delta.hpp | 51 +- .../libosmium/include/osmium/util/double.hpp | 3 +- .../libosmium/include/osmium/util/file.hpp | 82 +- .../include/osmium/util/iterator.hpp | 18 +- .../include/osmium/util/memory_mapping.hpp | 25 +- .../libosmium/include/osmium/util/misc.hpp | 52 + .../libosmium/include/osmium/util/options.hpp | 9 +- .../include/osmium/util/progress_bar.hpp | 179 ++ .../libosmium/include/osmium/util/string.hpp | 2 +- .../libosmium/include/osmium/util/timer.hpp | 98 ++ .../include/osmium/util/verbose_output.hpp | 8 +- .../libosmium/include/osmium/version.hpp | 42 + .../libosmium/include/protozero/byteswap.hpp | 6 +- .../libosmium/include/protozero/exception.hpp | 8 +- .../libosmium/include/protozero/iterators.hpp | 373 ++++ .../include/protozero/pbf_builder.hpp | 20 +- .../include/protozero/pbf_message.hpp | 10 +- .../include/protozero/pbf_reader.hpp | 763 +++----- .../include/protozero/pbf_writer.hpp | 244 ++- .../libosmium/include/protozero/types.hpp | 170 +- .../libosmium/include/protozero/varint.hpp | 125 +- .../libosmium/include/protozero/version.hpp | 19 +- third_party/libosmium/osmium.imp | 9 +- third_party/libosmium/test/CMakeLists.txt | 44 +- third_party/libosmium/test/README | 8 - .../test/data-tests/include/common.hpp | 6 +- .../test/data-tests/testdata-multipolygon.cpp | 60 +- .../test/data-tests/testdata-overview.cpp | 23 +- .../test/data-tests/testdata-testcases.cpp | 6 +- .../test/data-tests/testdata-xml.cpp | 118 +- third_party/libosmium/test/include/catch.hpp | 622 +++++-- .../test/t/area/test_node_ref_segment.cpp | 185 +- .../libosmium/test/t/basic/test_area.cpp | 78 + .../test/t/basic/test_entity_bits.cpp | 1 + .../libosmium/test/t/basic/test_location.cpp | 220 ++- .../libosmium/test/t/basic/test_node.cpp | 68 +- .../test/t/basic/test_object_comparisons.cpp | 132 +- .../libosmium/test/t/basic/test_timestamp.cpp | 44 + .../test/t/basic/test_types_from_string.cpp | 14 +- .../libosmium/test/t/basic/test_way.cpp | 2 +- .../libosmium/test/t/builder/test_attr.cpp | 4 +- .../libosmium/test/t/geom/test_geojson.cpp | 22 +- .../libosmium/test/t/geom/test_geos.cpp | 17 +- .../libosmium/test/t/geom/test_ogr.cpp | 4 + .../libosmium/test/t/geom/test_tile.cpp | 140 +- .../libosmium/test/t/geom/test_wkb.cpp | 160 +- .../libosmium/test/t/geom/test_wkt.cpp | 177 +- .../test/t/index/test_file_based_index.cpp | 155 ++ .../test/t/index/test_id_to_location.cpp | 36 +- .../libosmium/test/t/io/test_opl_parser.cpp | 1075 +++++++++++ .../test_reader_with_mock_decompression.cpp | 10 +- .../t/io/test_reader_with_mock_parser.cpp | 6 +- .../libosmium/test/t/io/test_string_table.cpp | 40 +- .../libosmium/test/t/io/test_writer.cpp | 9 +- .../io/test_writer_with_mock_compression.cpp | 5 +- .../t/io/test_writer_with_mock_encoder.cpp | 3 +- .../libosmium/test/t/tags/test_filter.cpp | 6 +- .../libosmium/test/t/tags/test_tag_list.cpp | 8 +- .../test/t/util/test_cast_with_assert.cpp | 2 +- .../libosmium/test/t/util/test_delta.cpp | 22 - .../test/t/util/test_memory_mapping.cpp | 13 +- unit_tests/library/limits.cpp | 30 + unit_tests/library/tile.cpp | 98 +- unit_tests/mocks/mock_datafacade.hpp | 41 +- unit_tests/server/parameters_parser.cpp | 44 +- unit_tests/util/bearing.cpp | 3 + unit_tests/util/static_rtree.cpp | 3 +- 405 files changed, 16470 insertions(+), 7128 deletions(-) create mode 100644 cmake/FindLua.cmake delete mode 100644 cmake/FindLua52.cmake delete mode 100644 cmake/FindLuaJIT.cmake delete mode 100644 cmake/check_luabind.cmake create mode 100644 features/car/summaries.feature create mode 100644 features/guidance/notification.feature create mode 100644 features/lib/hash.js rename features/{support/build_osm.js => lib/osm.js} (96%) create mode 100644 features/lib/osrm_loader.js create mode 100644 features/lib/table_diff.js create mode 100644 features/lib/try_connect.js create mode 100644 features/lib/utils.js create mode 100644 features/options/contract/edge-weight-updates-over-factor.feature delete mode 100644 features/step_definitions/hooks.js create mode 100644 features/support/cache.js delete mode 100644 features/support/config.js delete mode 100644 features/support/exception_classes.js delete mode 100644 features/support/exceptions.js delete mode 100644 features/support/hash.js delete mode 100644 features/support/launch.js delete mode 100644 features/support/launch_classes.js delete mode 100644 features/support/log.js delete mode 100644 features/support/table_patch.js delete mode 100644 features/testbot/bugs.feature create mode 100644 include/engine/edge_unpacker.hpp delete mode 100644 include/util/json_logger.hpp delete mode 100644 profiles/lhs.lua delete mode 100644 profiles/rhs.lua rename scripts/{update_depdendencies.sh => update_dependencies.sh} (70%) create mode 100644 third_party/libosmium/NOTES_FOR_DEVELOPERS.md create mode 100644 third_party/libosmium/build-appveyor.bat create mode 100644 third_party/libosmium/build-local.bat create mode 100644 third_party/libosmium/examples/README.md delete mode 100644 third_party/libosmium/examples/osmium_create_node_cache.cpp create mode 100644 third_party/libosmium/examples/osmium_location_cache_create.cpp create mode 100644 third_party/libosmium/examples/osmium_location_cache_use.cpp create mode 100755 third_party/libosmium/examples/osmium_pub_names.cpp create mode 100644 third_party/libosmium/examples/osmium_read_with_progress.cpp create mode 100755 third_party/libosmium/examples/osmium_road_length.cpp create mode 100644 third_party/libosmium/examples/osmium_tiles.cpp delete mode 100644 third_party/libosmium/examples/osmium_use_node_cache.cpp create mode 100644 third_party/libosmium/include/osmium/area/detail/vector.hpp create mode 100644 third_party/libosmium/include/osmium/area/stats.hpp create mode 100644 third_party/libosmium/include/osmium/io/detail/opl_input_format.hpp create mode 100644 third_party/libosmium/include/osmium/io/detail/opl_parser_functions.hpp create mode 100644 third_party/libosmium/include/osmium/io/opl_input.hpp create mode 100644 third_party/libosmium/include/osmium/opl.hpp delete mode 100644 third_party/libosmium/include/osmium/thread/sorted_queue.hpp create mode 100644 third_party/libosmium/include/osmium/util/misc.hpp create mode 100644 third_party/libosmium/include/osmium/util/progress_bar.hpp create mode 100644 third_party/libosmium/include/osmium/util/timer.hpp create mode 100644 third_party/libosmium/include/osmium/version.hpp create mode 100644 third_party/libosmium/include/protozero/iterators.hpp create mode 100644 third_party/libosmium/test/t/basic/test_area.cpp create mode 100644 third_party/libosmium/test/t/index/test_file_based_index.cpp create mode 100644 third_party/libosmium/test/t/io/test_opl_parser.cpp diff --git a/.travis.yml b/.travis.yml index ceffe18f6d7..8bcb98e8853 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,12 +35,12 @@ matrix: # Debug Builds - os: linux - compiler: "gcc-5-debug" - addons: &gcc5 + compiler: "gcc-6-debug" + addons: &gcc6 apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache'] - env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Debug' COVERAGE=ON + packages: ['g++-6', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache'] + env: CCOMPILER='gcc-6' CXXCOMPILER='g++-6' BUILD_TYPE='Debug' COVERAGE=ON - os: linux compiler: "clang-3.8-debug" @@ -57,16 +57,16 @@ matrix: # Release Builds - os: linux - compiler: "gcc-5-release" - addons: &gcc5 + compiler: "gcc-6-release" + addons: &gcc6 apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache'] - env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Release' + packages: ['g++-6', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache'] + env: CCOMPILER='gcc-6' CXXCOMPILER='g++-6' BUILD_TYPE='Release' - os: linux - compiler: "gcc-5-release-i686" - env: TARGET_ARCH='i686' CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Release' + compiler: "gcc-6-release-i686" + env: TARGET_ARCH='i686' CCOMPILER='gcc-6' CXXCOMPILER='g++-6' BUILD_TYPE='Release' # Disabled because of CI slowness #- os: linux @@ -85,12 +85,12 @@ matrix: # Shared Library - os: linux - compiler: "gcc-5-release-shared" - addons: &gcc5 + compiler: "gcc-6-release-shared" + addons: &gcc6 apt: sources: ['ubuntu-toolchain-r-test'] - packages: ['g++-5', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache'] - env: CCOMPILER='gcc-5' CXXCOMPILER='g++-5' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON + packages: ['g++-6', 'libbz2-dev', 'libstxxl-dev', 'libstxxl1', 'libxml2-dev', 'libzip-dev', 'lua5.1', 'liblua5.1-0-dev', 'libtbb-dev', 'libgdal-dev', 'libluabind-dev', 'libboost-all-dev', 'ccache'] + env: CCOMPILER='gcc-6' CXXCOMPILER='g++-6' BUILD_TYPE='Release' BUILD_SHARED_LIBS=ON # Disabled because CI slowness #- os: linux diff --git a/CHANGELOG.md b/CHANGELOG.md index 4feea86eac5..148001a6c55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,34 @@ +# 5.5.0 + - Changes from 5.4.0 + - Profiles + - `restrictions` is now used for namespaced restrictions and restriction exceptions (e.g. `restriction:motorcar=` as well as `except=motorcar`) + - replaced lhs/rhs profiles by using test defined profiles + - Guidance + - Notifications are now exposed more prominently, announcing turns onto a ferry/pushing your bike more prominently + - Trip Plugin + - changed internal behaviour to prefer the smallest lexicographic result over the largest one + - Bugfixes + - fixed a bug where polyline decoding on a defective polyline could end up in out-of-bound access on a vector + - fixed compile errors in tile unit-test framework + - Debug Tiles + - Added support for turn penalties + # 5.4.0 - Changes from 5.3.0 + - Changes from 5.3.0 - Profiles - includes library guidance.lua that offers preliminary configuration on guidance. - added left_hand_driving flag in global profile properties - modified turn penalty function for car profile - better fit to real data + - return `ref` and `name` as separate fields. Do no use ref or destination as fallback for name value + - the default profile for car now ignores HOV only roads - Guidance - Handle Access tags for lanes, only considering valid lanes in lane-guidance (think car | car | bike | car) + - Improved the detection of non-noticeable name-changes + - Summaries have been improved to consider references as well - API: - `annotations=true` now returns the data source id for each segment as `datasources` - Reduced semantic of merge to refer only to merges from a lane onto a motorway-like road + - new `ref` field in the `RouteStep` object. It contains the reference code or name of a way. Previously merged into the `name` property like `name (ref)` and are now separate fields. - Bugfixes - Fixed an issue that would result in segfaults for viaroutes with an invalid intermediate segment when u-turns were allowed at the via-location - Invalid only_* restrictions could result in loss of connectivity. As a fallback, we assume all turns allowed when the restriction is not valid @@ -18,9 +38,15 @@ - Fixed an issue that could emit `invalid` as instruction when ending on a sliproad after a traffic-light - Fixed an issue that would detect turning circles as sliproads - Fixed a bug where post-processing instructions (e.g. left + left -> uturn) could result in false pronunciations + - Fixes a bug where a bearing range of zero would cause exhaustive graph traversals + - Fixes a bug where certain looped geometries could cause an infinite loop during extraction + - Fixed a bug where some roads could be falsly identified as sliproads + - Fixed a bug where roundabout intersections could result in breaking assertions when immediately exited + - Infrastructure: + - Adds a feature to limit results in nearest service with a default of 100 in `osrm-routed` # 5.3.0 - Changes from 5.3.0-rc.3 + - Changes from 5.3.0-rc.3 - Guidance - Only announce `use lane` on required turns (not using all lanes to go straight) - Moved `lanes` to the intersection objects. This is BREAKING in relation to other Release Candidates but not with respect to other releases. @@ -28,7 +54,7 @@ - Fix BREAKING: bug that could result in failure to load 'osrm.icd' files. This breaks the dataformat - Fix: bug that results in segfaults when `use lane` instructions are suppressed - Changes form 5.2.7 + - Changes form 5.2.7 - API - Introduces new `TurnType` in the form of `use lane`. The type indicates that you have to stick to a lane without turning - Introduces `lanes` to the `Intersection` object. The lane data contains both the markings at the intersection and a flag indicating if they can be chosen for the next turn @@ -52,7 +78,7 @@ - Fix devide by zero on updating speed data using osrm-contract # 5.3.0 RC3 - Changes from 5.3.0-rc.2 + - Changes from 5.3.0-rc.2 - Guidance - Improved detection of obvious turns - Improved turn lane detection @@ -60,7 +86,7 @@ - Fix bug that didn't chose minimal weights on overlapping edges # 5.3.0 RC2 - Changes from 5.3.0-rc.1 + - Changes from 5.3.0-rc.1 - Bugfixes - Fixes invalid checks in the lane-extraction part of the car profile diff --git a/CMakeLists.txt b/CMakeLists.txt index 35b81fec6ec..b0ac5911783 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ endif() project(OSRM C CXX) set(OSRM_VERSION_MAJOR 5) -set(OSRM_VERSION_MINOR 3) +set(OSRM_VERSION_MINOR 4) set(OSRM_VERSION_PATCH 0) # these two functions build up custom variables: @@ -47,7 +47,6 @@ if(WIN32 AND MSVC_VERSION LESS 1900) endif() option(ENABLE_CCACHE "Speed up incremental rebuilds via ccache" ON) -option(ENABLE_JSON_LOGGING "Adds additional JSON debug logging to the response" OFF) option(BUILD_TOOLS "Build OSRM tools" OFF) option(BUILD_COMPONENTS "Build osrm-components" OFF) option(ENABLE_ASSERTIONS OFF) @@ -127,8 +126,8 @@ if(ENABLE_GOLD_LINKER) # Issue 2785: check gold binutils version and don't use gc-sections for versions prior 2.25 string(REGEX REPLACE ".*\\(GNU Binutils[^\\)0-9]+([0-9]+\\.[0-9]+)[^\\)]*\\).*" "\\1" GOLD_BINUTILS_VERSION "${LD_VERSION}") - if ("${GOLD_BINUTILS_VERSION}" VERSION_LESS "2.25") - message(STATUS "Disabling gc-sections on gold binutils < 2.25, see: https://sourceware.org/bugzilla/show_bug.cgi?id=17639") + if ("${GOLD_BINUTILS_VERSION}" VERSION_LESS "2.26") + message(STATUS "Disabling gc-sections on gold binutils < 2.26, see: https://sourceware.org/bugzilla/show_bug.cgi?id=17639") set(LD_AVOID_GC_SECTIONS TRUE) endif() else() @@ -193,7 +192,7 @@ endif() # Configuring compilers if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wuninitialized -Wunreachable-code -Wstrict-overflow=2 -D_FORTIFY_SOURCE=2 -fPIC -fcolor-diagnostics") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wuninitialized -Wunreachable-code -Wstrict-overflow=2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC -fcolor-diagnostics") elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") set(COLOR_FLAG "-fdiagnostics-color=auto") check_cxx_compiler_flag("-fdiagnostics-color=auto" HAS_COLOR_FLAG) @@ -201,7 +200,7 @@ elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") set(COLOR_FLAG "") endif() # using GCC - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wuninitialized -Wunreachable-code -Wstrict-overflow=1 -D_FORTIFY_SOURCE=2 ${COLOR_FLAG} -fPIC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wuninitialized -Wunreachable-code -Wstrict-overflow=1 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 ${COLOR_FLAG} -fPIC") if(WIN32) # using mingw add_dependency_defines(-DWIN32) set(OPTIONAL_SOCKET_LIBS ws2_32 wsock32) @@ -272,6 +271,9 @@ set(OSMIUM_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third_party/libosmium/includ find_package(Osmium REQUIRED COMPONENTS io) add_dependency_includes(${OSMIUM_INCLUDE_DIR}) +# Disallow deprecated protozero APIs +add_definitions(-DPROTOZERO_STRICT_API) + find_package(Boost 1.49.0 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) @@ -304,13 +306,8 @@ if(WIN32 AND CMAKE_BUILD_TYPE MATCHES Debug) endif() find_package(Luabind REQUIRED) -include(check_luabind) add_dependency_includes(${LUABIND_INCLUDE_DIR}) - set(USED_LUA_LIBRARIES ${LUA_LIBRARY}) -if(LUAJIT_FOUND) - set(USED_LUA_LIBRARIES, LUAJIT_LIBRARIES) -endif() add_dependency_includes(${LUA_INCLUDE_DIR}) find_package(EXPAT REQUIRED) @@ -332,11 +329,6 @@ add_dependency_includes(${BZIP2_INCLUDE_DIR}) find_package(ZLIB REQUIRED) add_dependency_includes(${ZLIB_INCLUDE_DIRS}) -if (ENABLE_JSON_LOGGING) - message(STATUS "Enabling json logging") - add_dependency_defines(-DENABLE_JSON_LOGGING) -endif() - add_definitions(${OSRM_DEFINES}) include_directories(SYSTEM ${OSRM_INCLUDE_PATHS}) diff --git a/cmake/FindLua.cmake b/cmake/FindLua.cmake new file mode 100644 index 00000000000..0973bd18759 --- /dev/null +++ b/cmake/FindLua.cmake @@ -0,0 +1,188 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#.rst: +# FindLua +# ------- +# +# +# +# Locate Lua library This module defines +# +# :: +# +# LUA_FOUND - if false, do not try to link to Lua +# LUA_LIBRARIES - both lua and lualib +# LUA_INCLUDE_DIR - where to find lua.h +# LUA_VERSION_STRING - the version of Lua found +# LUA_VERSION_MAJOR - the major version of Lua +# LUA_VERSION_MINOR - the minor version of Lua +# LUA_VERSION_PATCH - the patch version of Lua +# +# +# +# Note that the expected include convention is +# +# :: +# +# #include "lua.h" +# +# and not +# +# :: +# +# #include +# +# This is because, the lua location is not standardized and may exist in +# locations other than lua/ + +unset(_lua_include_subdirs) +unset(_lua_library_names) +unset(_lua_append_versions) + +# this is a function only to have all the variables inside go away automatically +function(_lua_set_version_vars) + set(LUA_VERSIONS5 5.3 5.2 5.1 5.0) + + if (Lua_FIND_VERSION_EXACT) + if (Lua_FIND_VERSION_COUNT GREATER 1) + set(_lua_append_versions ${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}) + endif () + elseif (Lua_FIND_VERSION) + # once there is a different major version supported this should become a loop + if (NOT Lua_FIND_VERSION_MAJOR GREATER 5) + if (Lua_FIND_VERSION_COUNT EQUAL 1) + set(_lua_append_versions ${LUA_VERSIONS5}) + else () + foreach (subver IN LISTS LUA_VERSIONS5) + if (NOT subver VERSION_LESS ${Lua_FIND_VERSION}) + list(APPEND _lua_append_versions ${subver}) + endif () + endforeach () + endif () + endif () + else () + # once there is a different major version supported this should become a loop + set(_lua_append_versions ${LUA_VERSIONS5}) + endif () + + list(APPEND _lua_include_subdirs "include/lua" "include") + + foreach (ver IN LISTS _lua_append_versions) + string(REGEX MATCH "^([0-9]+)\\.([0-9]+)$" _ver "${ver}") + list(APPEND _lua_include_subdirs + include/lua${CMAKE_MATCH_1}${CMAKE_MATCH_2} + include/lua${CMAKE_MATCH_1}.${CMAKE_MATCH_2} + include/lua-${CMAKE_MATCH_1}.${CMAKE_MATCH_2} + ) + list(APPEND _lua_library_names + lua${CMAKE_MATCH_1}${CMAKE_MATCH_2} + lua${CMAKE_MATCH_1}.${CMAKE_MATCH_2} + lua-${CMAKE_MATCH_1}.${CMAKE_MATCH_2} + lua.${CMAKE_MATCH_1}.${CMAKE_MATCH_2} + ) + endforeach () + + set(_lua_include_subdirs "${_lua_include_subdirs}" PARENT_SCOPE) + set(_lua_library_names "${_lua_library_names}" PARENT_SCOPE) + set(_lua_append_versions "${_lua_append_versions}" PARENT_SCOPE) +endfunction(_lua_set_version_vars) + +function(_lua_check_header_version _hdr_file) + # At least 5.[012] have different ways to express the version + # so all of them need to be tested. Lua 5.2 defines LUA_VERSION + # and LUA_RELEASE as joined by the C preprocessor, so avoid those. + file(STRINGS "${_hdr_file}" lua_version_strings + REGEX "^#define[ \t]+LUA_(RELEASE[ \t]+\"Lua [0-9]|VERSION([ \t]+\"Lua [0-9]|_[MR])).*") + + string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MAJOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MAJOR ";${lua_version_strings};") + if (LUA_VERSION_MAJOR MATCHES "^[0-9]+$") + string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MINOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MINOR ";${lua_version_strings};") + string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_RELEASE[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_PATCH ";${lua_version_strings};") + set(LUA_VERSION_STRING "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}") + else () + string(REGEX REPLACE ".*;#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};") + if (NOT LUA_VERSION_STRING MATCHES "^[0-9.]+$") + string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};") + endif () + string(REGEX REPLACE "^([0-9]+)\\.[0-9.]*$" "\\1" LUA_VERSION_MAJOR "${LUA_VERSION_STRING}") + string(REGEX REPLACE "^[0-9]+\\.([0-9]+)[0-9.]*$" "\\1" LUA_VERSION_MINOR "${LUA_VERSION_STRING}") + string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]).*" "\\1" LUA_VERSION_PATCH "${LUA_VERSION_STRING}") + endif () + foreach (ver IN LISTS _lua_append_versions) + if (ver STREQUAL "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}") + set(LUA_VERSION_MAJOR ${LUA_VERSION_MAJOR} PARENT_SCOPE) + set(LUA_VERSION_MINOR ${LUA_VERSION_MINOR} PARENT_SCOPE) + set(LUA_VERSION_PATCH ${LUA_VERSION_PATCH} PARENT_SCOPE) + set(LUA_VERSION_STRING ${LUA_VERSION_STRING} PARENT_SCOPE) + return() + endif () + endforeach () +endfunction(_lua_check_header_version) + +_lua_set_version_vars() + +if (LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") + _lua_check_header_version("${LUA_INCLUDE_DIR}/lua.h") +endif () + +if (NOT LUA_VERSION_STRING) + foreach (subdir IN LISTS _lua_include_subdirs) + unset(LUA_INCLUDE_PREFIX CACHE) + find_path(LUA_INCLUDE_PREFIX ${subdir}/lua.h + HINTS + ENV LUA_DIR + PATHS + ~/Library/Frameworks + /Library/Frameworks + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt + ) + if (LUA_INCLUDE_PREFIX) + _lua_check_header_version("${LUA_INCLUDE_PREFIX}/${subdir}/lua.h") + if (LUA_VERSION_STRING) + set(LUA_INCLUDE_DIR "${LUA_INCLUDE_PREFIX}/${subdir}") + break() + endif () + endif () + endforeach () +endif () +unset(_lua_include_subdirs) +unset(_lua_append_versions) + +find_library(LUA_LIBRARY + NAMES ${_lua_library_names} lua + HINTS + ENV LUA_DIR + PATH_SUFFIXES lib + PATHS + ~/Library/Frameworks + /Library/Frameworks + /sw + /opt/local + /opt/csw + /opt +) +unset(_lua_library_names) + +if (LUA_LIBRARY) + # include the math library for Unix + if (UNIX AND NOT APPLE AND NOT BEOS) + find_library(LUA_MATH_LIBRARY m) + set(LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}") + # For Windows and Mac, don't need to explicitly include the math library + else () + set(LUA_LIBRARIES "${LUA_LIBRARY}") + endif () +endif () + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if +# all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua + REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR + VERSION_VAR LUA_VERSION_STRING) + +mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARY LUA_MATH_LIBRARY) diff --git a/cmake/FindLua52.cmake b/cmake/FindLua52.cmake deleted file mode 100644 index 4aeaf94f9ef..00000000000 --- a/cmake/FindLua52.cmake +++ /dev/null @@ -1,82 +0,0 @@ -# Locate Lua library -# This module defines -# LUA52_FOUND, if false, do not try to link to Lua -# LUA_LIBRARIES -# LUA_INCLUDE_DIR, where to find lua.h -# LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) -# -# Note that the expected include convention is -# #include "lua.h" -# and not -# #include -# This is because, the lua location is not standardized and may exist -# in locations other than lua/ - -#============================================================================= -# Copyright 2007-2009 Kitware, Inc. -# Copyright 2016 for Project-OSRM, Lua5.1 => Lua5.2 -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distribute this file outside of CMake, substitute the full -# License text for the above reference.) - -find_path(LUA_INCLUDE_DIR lua.h - HINTS - ENV LUA_DIR - PATH_SUFFIXES include/lua52 include/lua5.2 include/lua-5.2 include/lua include - PATHS - ~/Library/Frameworks - /Library/Frameworks - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) - -find_library(LUA_LIBRARY - NAMES lua52 lua5.2 lua-5.2 lua - HINTS - ENV LUA_DIR - PATH_SUFFIXES lib - PATHS - ~/Library/Frameworks - /Library/Frameworks - /sw - /opt/local - /opt/csw - /opt -) - -if(LUA_LIBRARY) - # include the math library for Unix - if(UNIX AND NOT APPLE AND NOT BEOS) - find_library(LUA_MATH_LIBRARY m) - set( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") - # For Windows and Mac, don't need to explicitly include the math library - else() - set( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") - endif() -endif() - -if(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") - file(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") - - string(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") - unset(lua_version_str) -endif() - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if -# all listed variables are TRUE -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua52 - REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR - VERSION_VAR LUA_VERSION_STRING) - -mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY) - diff --git a/cmake/FindLuaJIT.cmake b/cmake/FindLuaJIT.cmake deleted file mode 100644 index 5d5e6aceb49..00000000000 --- a/cmake/FindLuaJIT.cmake +++ /dev/null @@ -1,93 +0,0 @@ -# Locate Lua library -# This module defines -# LUAJIT_FOUND, if false, do not try to link to Lua -# LUAJIT_LIBRARIES -# LUAJIT_INCLUDE_DIR, where to find lua.h -# -# Note that the expected include convention is -# #include "lua.h" -# and not -# #include -# This is because, the lua location is not standardized and may exist -# in locations other than lua/ - -#============================================================================= -# Copyright 2007-2009 Kitware, Inc. -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distributed this file outside of CMake, substitute the full -# License text for the above reference.) -# -# ################ -# 2010 - modified for cronkite to find luajit instead of lua, as it was before. -# - -if ( NOT LuaJIT_FIND_VERSION ) - MESSAGE(FATAL_ERROR "You need to specify a version of libluajit to use") -ENDIF() - -IF( NOT LUAJIT_FIND_QUIETLY ) - MESSAGE(STATUS "Looking for LuaJIT ${LuaJIT_FIND_VERSION}") -ENDIF() - -FIND_PATH(LUAJIT_INCLUDE_DIR lua.h - HINTS - $ENV{LUAJIT_DIR} - PATH_SUFFIXES include/luajit-2.0 include/luajit2.0 include/luajit include - PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local - /usr - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) - -FIND_LIBRARY(LUAJIT_LIBRARY - NAMES luajit-${LuaJIT_FIND_VERSION_MAJOR}${LuaJIT_FIND_VERSION_MINOR} luajit-${LuaJIT_FIND_VERSION} - HINTS - $ENV{LUAJIT_DIR} - PATH_SUFFIXES lib64 lib - PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local - /usr - /sw - /opt/local - /opt/csw - /opt -) - -IF(LUAJIT_LIBRARY) - # include the math library for Unix - IF(UNIX AND NOT APPLE) - FIND_LIBRARY(LUAJIT_MATH_LIBRARY m) - SET( LUAJIT_LIBRARIES "${LUAJIT_LIBRARY};${LUAJIT_MATH_LIBRARY}" CACHE STRING "Lua Libraries") - # For Windows and Mac, don't need to explicitly include the math library - ELSE(UNIX AND NOT APPLE) - SET( LUAJIT_LIBRARIES "${LUAJIT_LIBRARY}" CACHE STRING "Lua Libraries") - ENDIF(UNIX AND NOT APPLE) -ENDIF(LUAJIT_LIBRARY) - -INCLUDE(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set LUAJIT_FOUND to TRUE if -# all listed variables are TRUE -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LUAJIT DEFAULT_MSG LUAJIT_LIBRARIES LUAJIT_INCLUDE_DIR) - -IF( NOT LUAJIT_FIND_QUIETLY ) - IF( LUAJIT_FOUND AND LUAJIT_LIBRARIES) - MESSAGE(STATUS "Found LuaJIT: ${LUAJIT_LIBRARY}" ) - MARK_AS_ADVANCED(LUAJIT_INCLUDE_DIR LUAJIT_LIBRARIES LUAJIT_LIBRARY LUAJIT_MATH_LIBRARY) - ELSE() - SET ( LUAJIT_FOUND FALSE ) - ENDIF() -ENDIF() diff --git a/cmake/FindLuabind.cmake b/cmake/FindLuabind.cmake index 4b99b3c0e68..368e9c02f0b 100644 --- a/cmake/FindLuabind.cmake +++ b/cmake/FindLuabind.cmake @@ -3,14 +3,17 @@ # LUABIND_FOUND, if false, do not try to link to Luabind # LUABIND_LIBRARIES # LUABIND_INCLUDE_DIR, where to find luabind.hpp -# -# Note that the expected include convention is -# #include -# and not -# #include -IF( NOT LUABIND_FIND_QUIETLY ) - MESSAGE(STATUS "Looking for Luabind...") +FIND_PACKAGE(Lua 5.2 EXACT) +IF (LUA_FOUND) + MESSAGE(STATUS "Using Lua ${LUA_VERSION_STRING}") +ELSE() + FIND_PACKAGE(Lua 5.1 EXACT) + IF (LUA_FOUND) + MESSAGE(STATUS "Using Lua ${LUA_VERSION_STRING}") + ELSE() + MESSAGE(FATAL_ERROR "Lua 5.1 or 5.2 was not found.") + ENDIF() ENDIF() FIND_PATH(LUABIND_INCLUDE_DIR luabind.hpp diff --git a/cmake/check_luabind.cmake b/cmake/check_luabind.cmake deleted file mode 100644 index 79253af2bd2..00000000000 --- a/cmake/check_luabind.cmake +++ /dev/null @@ -1,40 +0,0 @@ -INCLUDE (CheckCXXSourceCompiles) -unset(LUABIND_WORKS CACHE) -unset(LUABIND51_WORKS CACHE) -set (LUABIND_CHECK_SRC "extern \"C\" {\n#include \"lua.h\"\n#include \"lauxlib.h\"\n}\n#include \nint main() { lua_State *x = luaL_newstate(); luabind::open(x); }") -set (CMAKE_TRY_COMPILE_CONFIGURATION ${CMAKE_BUILD_TYPE}) -set (CMAKE_REQUIRED_INCLUDES "${Boost_INCLUDE_DIR};${LUABIND_INCLUDE_DIR};${LUA_INCLUDE_DIR}") -set (CMAKE_REQUIRED_LIBRARIES "${LUABIND_LIBRARY};${LUA_LIBRARY}") - -find_package(Lua52) -if(NOT APPLE) - find_package(LuaJIT 5.2) -endif() -if(LUA52_FOUND) - set (CMAKE_REQUIRED_INCLUDES "${Boost_INCLUDE_DIR};${LUABIND_INCLUDE_DIR};${LUA_INCLUDE_DIR}") - set (CMAKE_REQUIRED_LIBRARIES "${LUABIND_LIBRARY};${LUA_LIBRARY}") - CHECK_CXX_SOURCE_COMPILES("${LUABIND_CHECK_SRC}" LUABIND_WORKS) -endif() - -if(LUABIND_WORKS) - message(STATUS "Luabind/Lua5.2 combination working with ${LUA_LIBRARY}") -else() - message(STATUS "Luabind/Lua5.2 not feasible, falling back to Lua 5.1.") - unset(LUA_FOUND CACHE) - unset(LUA_INCLUDE_DIR CACHE) - unset(LUA_LIBRARY CACHE) - find_package(Lua51 REQUIRED) - if(NOT APPLE) - find_package(LuaJIT 5.1) - endif() - set (CMAKE_REQUIRED_INCLUDES "${Boost_INCLUDE_DIR};${LUABIND_INCLUDE_DIR};${LUA_INCLUDE_DIR}") - set (CMAKE_REQUIRED_LIBRARIES "${LUABIND_LIBRARY};${LUA_LIBRARY}") - - CHECK_CXX_SOURCE_COMPILES("${LUABIND_CHECK_SRC}" LUABIND51_WORKS) - - if(LUABIND51_WORKS) - message(STATUS "Luabind works with Lua 5.1 at ${LUA_LIBRARY}") - else() - message(FATAL_ERROR "Luabind does not work with Lua 5.1 at ${LUA_LIBRARY}, no working Luabind found") - endif() -endif() diff --git a/cucumber.js b/cucumber.js index 5a87dd7f1c2..0bf6f570f95 100644 --- a/cucumber.js +++ b/cucumber.js @@ -1,10 +1,8 @@ module.exports = { - default: '--require features --tags ~@stress --tags ~@todo', - verify: '--require features --tags ~@todo --tags ~@bug --tags ~@stress -f progress', - jenkins: '--require features --tags ~@todo --tags ~@bug --tags ~@stress --tags ~@options -f progress', - bugs: '--require features --tags @bug', - todo: '--require features --tags @todo', - all: '--require features' + default: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions', + verify: '--strict --tags ~@stress --tags ~@todo -f progress --require features/support --require features/step_definitions', + todo: '--strict --tags @todo --require features/support --require features/step_definitions', + all: '--strict --require features/support --require features/step_definitions' } diff --git a/docs/http.md b/docs/http.md index 04b01b65395..24a4900ba11 100644 --- a/docs/http.md +++ b/docs/http.md @@ -30,17 +30,17 @@ http://{server}/{service}/{version}/{profile}/{coordinates}[.{format}]?option=va | Service | Description | |-------------|-----------------------------------------------------------| - | [`route`](#service-route) | shortest path between given coordinates | + | [`route`](#service-route) | fastest path between given coordinates | | [`nearest`](#service-nearest) | returns the nearest street segment for a given coordinate | | [`table`](#service-table) | computes distance tables for given coordinates | | [`match`](#service-match) | matches given coordinates to the road network | - | [`trip`](#service-trip) | Compute the shortest round trip between given coordinates | + | [`trip`](#service-trip) | Compute the fastest round trip between given coordinates | | [`tile`](#service-tile) | Return vector tiles containing debugging info | - `version`: Version of the protocol implemented by the service. -- `profile`: Mode of transportation, is determined by the profile that is used to prepare the data +- `profile`: Mode of transportation, is determined statically by the Lua profile that is used to prepare the data using `osrm-extract`. - `coordinates`: String of format `{longitude},{latitude};{longitude},{latitude}[;{longitude},{latitude} ...]` or `polyline({polyline})`. -- `format`: Only `json` is supportest at the moment. This parameter is optional and defaults to `json`. +- `format`: Only `json` is supported at the moment. This parameter is optional and defaults to `json`. Passing any `option=value` is optional. `polyline` follows Google's polyline format with precision 5 and can be generated using [this package](https://www.npmjs.com/package/polyline). To pass parameters to each location some options support an array like encoding: @@ -249,7 +249,7 @@ http://router.project-osrm.org/table/v1/driving/13.388860,52.517037;13.397634,52 Returns a asymmetric 3x2 matrix with from the polyline encoded locations `qikdcB}~dpXkkHz`: ``` -http://router.project-osrm.org/table/v1/driving/qikdcB}~dpXkkHz?sources=0;1;3&destinations=2;4 +http://router.project-osrm.org/table/v1/driving/polyline(egs_Iq_aqAppHzbHulFzeMe`EuvKpnCglA)?sources=0;1;3&destinations=2;4 ``` ## Service `match` @@ -302,7 +302,7 @@ All other fields might be undefined. ## Service `trip` The trip plugin solves the Traveling Salesman Problem using a greedy heuristic (farthest-insertion algorithm). -The returned path does not have to be the shortest path, as TSP is NP-hard it is only an approximation. +The returned path does not have to be the fastest path, as TSP is NP-hard it is only an approximation. Note that if the input coordinates can not be joined by a single trip (e.g. the coordinates are on several disconnected islands) multiple trips for each connected component are returned. @@ -469,6 +469,7 @@ step. | geojson | [GeoJSON `LineString`](http://geojson.org/geojson-spec.html#linestring) or [GeoJSON `Point`](http://geojson.org/geojson-spec.html#point) if it is only one coordinate (not wrapped by a GeoJSON feature)| - `name`: The name of the way along which travel proceeds. +- `ref`: A reference number or code for the way. Optionally included, if ref data is available for the given way. - `pronunciation`: The pronunciation hint of the way name. Will be `undefined` if there is no pronunciation hit. - `destinations`: The destinations of the way. Will be `undefined` if there are no destinations. - `mode`: A string signifying the mode of transportation. diff --git a/example/example.cpp b/example/example.cpp index a6bb6af0464..3ad13e6b2bb 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -34,7 +34,7 @@ int main(int argc, const char *argv[]) config.use_shared_memory = false; // Routing machine with several services (such as Route, Table, Nearest, Trip, Match) - OSRM osrm{config}; + const OSRM osrm{config}; // The following shows how to use the Route service; configure this service RouteParameters params; diff --git a/features/bicycle/names.feature b/features/bicycle/names.feature index b19685704fb..38b1d2ea794 100644 --- a/features/bicycle/names.feature +++ b/features/bicycle/names.feature @@ -15,8 +15,8 @@ Feature: Bike - Street names in instructions | bc | Your Way | A7 | When I route I should get - | from | to | route | - | a | c | My Way (A6),Your Way (A7),Your Way (A7) | + | from | to | route | ref | + | a | c | My Way,Your Way,Your Way | A6,A7,A7 | @unnamed Scenario: Bike - Use way type to describe unnamed ways diff --git a/features/bicycle/ref.feature b/features/bicycle/ref.feature index a9c90844273..8c64232fac2 100644 --- a/features/bicycle/ref.feature +++ b/features/bicycle/ref.feature @@ -13,8 +13,8 @@ Feature: Bike - Way ref | ab | Utopia Drive | E7 | When I route I should get - | from | to | route | - | a | b | Utopia Drive (E7),Utopia Drive (E7) | + | from | to | route | ref | + | a | b | Utopia Drive,Utopia Drive | E7,E7 | Scenario: Bike - Way with only ref Given the node map @@ -25,8 +25,8 @@ Feature: Bike - Way ref | ab | | E7 | When I route I should get - | from | to | route | - | a | b | E7,E7 | + | from | to | route | ref | + | a | b | {highway:primary},{highway:primary} | E7,E7 | Scenario: Bike - Way with only name Given the node map diff --git a/features/car/access.feature b/features/car/access.feature index 7a650b6b20d..bcbdae874b3 100644 --- a/features/car/access.feature +++ b/features/car/access.feature @@ -156,6 +156,25 @@ Feature: Car - Restricted access | primary | yes | x | | primary | no | x | + Scenario: Car - a way with all lanes HOV-designated is inaccessible by default (similar to hov=designated) + Then routability should be + | highway | hov:lanes:forward | hov:lanes:backward | hov:lanes | oneway | forw | backw | + | primary | designated | designated | | | | | + | primary | | designated | | | x | | + | primary | designated | | | | | x | + | primary | designated\|designated | designated\|designated | | | | | + | primary | designated\|no | designated\|no | | | x | x | + | primary | yes\|no | yes\|no | | | x | x | + | primary | | | | | x | x | + | primary | designated | | | -1 | | | + | primary | | designated | | -1 | | x | + | primary | | | designated | yes | | | + | primary | | | designated | -1 | | | + | primary | | | designated\|designated | yes | | | + | primary | | | designated\|designated | -1 | | | + | primary | | | designated\|yes | yes | x | | + | primary | | | designated\|no | -1 | | x | + Scenario: Car - these toll roads always work Then routability should be | highway | toll | bothw | diff --git a/features/car/names.feature b/features/car/names.feature index e03a0308a69..de76b252e68 100644 --- a/features/car/names.feature +++ b/features/car/names.feature @@ -15,8 +15,8 @@ Feature: Car - Street names in instructions | bc | Your Way | A1 | When I route I should get - | from | to | route | - | a | c | My Way,Your Way (A1),Your Way (A1) | + | from | to | route | ref | + | a | c | My Way,Your Way,Your Way | ,A1,A1| Scenario: Car - A named street with pronunciation Given the node map @@ -25,15 +25,32 @@ Feature: Car - Street names in instructions | | c | | And the ways - | nodes | name |name:pronunciation | ref | + | nodes | name |name:pronunciation | ref | | ab | My Way | | | | bd | My Way | meyeway | A1 | | cd | Your Way | yourewaye | | When I route I should get - | from | to | route | pronunciations | - | a | d | My Way,My Way (A1) | ,meyeway | - | 1 | c | Your Way,Your Way | yourewaye,yourewaye | + | from | to | route | pronunciations | ref | + | a | d | My Way,My Way | ,meyeway | ,A1 | + | 1 | c | Your Way,Your Way | yourewaye,yourewaye | , | + + # See #2860 + Scenario: Car - same street name but different pronunciation + Given the node map + | a | b | c | + | | d | | + | | e | | + + And the ways + | nodes | name | name:pronunciation | + | abc | Houston St | hew-stun | + | bde | Houston St | how-stun | + + When I route I should get + | from | to | route | pronunciations | + | a | c | Houston St,Houston St | hew-stun,hew-stun | + | a | e | Houston St,Houston St,Houston St | hew-stun,how-stun,how-stun | @todo Scenario: Car - Use way type to describe unnamed ways diff --git a/features/car/summaries.feature b/features/car/summaries.feature new file mode 100644 index 00000000000..38670ea5460 --- /dev/null +++ b/features/car/summaries.feature @@ -0,0 +1,94 @@ +@routing @basic @car +Feature: Basic Routing + + Background: + Given the profile "car" + Given a grid size of 500 meters + + @smallest + Scenario: Summaries when routing on a simple network + Given the node map + | b | | | f | + | | | | | + | c | d | | g | + | | | | | + | a | | e | | + + And the ways + | nodes | name | + | acb | road | + | de | 1 st | + | cd | | + | dg | blvd | + | df | street | + + When I route I should get + | waypoints | route | summary | + | a,e | road,,1 st,1 st | road, 1 st | + | a,d,f | road,,,street,street | road;street | + | a,e,f | road,,1 st,1 st,1 st,street,street | road, 1 st;1 st, street | + + Scenario: Name Empty + Given the node map + | a | | b | | | c | + + And the ways + | nodes | name | + | ab | road | + | bc | | + + When I route I should get + | waypoints | route | summary | + | a,c | road, | road | + + Scenario: Name Empty But Ref + Given the node map + | a | | b | | | c | + + And the ways + | nodes | name | ref | + | ab | road | | + | bc | | 101 | + + When I route I should get + | waypoints | route | summary | + | a,c | road, | road, 101 | + + Scenario: Only Refs + Given the node map + | a | | b | | | c | + + And the ways + | nodes | name | ref | + | ab | | 100 | + | bc | | 101 | + + When I route I should get + | waypoints | route | summary | + | a,c | , | 100, 101 | + + Scenario: Single Ref + Given the node map + | a | | b | | | c | + + And the ways + | nodes | name | ref | + | ab | | | + | bc | | 101 | + + When I route I should get + | waypoints | route | summary | + | a,c | ,, | 101 | + + Scenario: Nothing + Given the node map + | a | | b | | | c | + + And the ways + | nodes | name | + | ab | | + | bc | | + + When I route I should get + | waypoints | route | summary | + | a,c | , | | diff --git a/features/car/traffic_speeds.feature b/features/car/traffic_speeds.feature index c2ad0928806..24e2e7f59b4 100644 --- a/features/car/traffic_speeds.feature +++ b/features/car/traffic_speeds.feature @@ -7,12 +7,12 @@ Feature: Traffic - speeds Given the node locations | node | lat | lon | | a | 0.1 | 0.1 | - | b | .05 | 0.1 | + | b | 0.05 | 0.1 | | c | 0.0 | 0.1 | - | d | .05 | .03 | - | e | .05 | .066 | - | f | .075 | .066 | - | g | .075 | 0.1 | + | d | 0.05 | 0.03 | + | e | 0.05 | 0.066 | + | f | 0.075 | 0.066 | + | g | 0.075 | 0.1 | And the ways | nodes | highway | | ab | primary | @@ -25,7 +25,7 @@ Feature: Traffic - speeds | fb | primary | Given the profile "testbot" Given the extract extra arguments "--generate-edge-lookup" - Given the contract extra arguments "--segment-speed-file speeds.csv" + Given the contract extra arguments "--segment-speed-file {speeds_file}" Given the speed file """ 1,2,0 @@ -50,12 +50,12 @@ Feature: Traffic - speeds Given the node locations | node | lat | lon | | a | 0.1 | 0.1 | - | b | .05 | 0.1 | + | b | 0.05 | 0.1 | | c | 0.0 | 0.1 | - | d | .05 | .03 | - | e | .05 | .066 | - | f | .075 | .066 | - | g | .075 | 0.1 | + | d | 0.05 | 0.03 | + | e | 0.05 | 0.066 | + | f | 0.075 | 0.066 | + | g | 0.075 | 0.1 | | h | 2.075 | 19.1 | And the ways | nodes | highway | @@ -69,7 +69,7 @@ Feature: Traffic - speeds | fb | primary | Given the profile "testbot" Given the extract extra arguments "--generate-edge-lookup" - Given the contract extra arguments "--segment-speed-file speeds.csv" + Given the contract extra arguments "--segment-speed-file {speeds_file}" Given the speed file """ 1,2,0 @@ -93,12 +93,12 @@ Feature: Traffic - speeds Given the node locations | node | lat | lon | | a | 0.1 | 0.1 | - | b | .05 | 0.1 | + | b | 0.05 | 0.1 | | c | 0.0 | 0.1 | - | d | .05 | .03 | - | e | .05 | .066 | - | f | .075 | .066 | - | g | .075 | 0.1 | + | d | 0.05 | 0.03 | + | e | 0.05 | 0.066 | + | f | 0.075 | 0.066 | + | g | 0.075 | 0.1 | | h | 1.075 | 10.1 | And the ways | nodes | highway | @@ -112,7 +112,6 @@ Feature: Traffic - speeds | fb | primary | Given the profile "testbot" Given the extract extra arguments "--generate-edge-lookup" - Given the contract extra arguments "--segment-speed-file speeds.csv" Given the speed file """ 1,2,-10 @@ -123,6 +122,6 @@ Feature: Traffic - speeds 4,1,-5 """ And the data has been extracted - When I run "osrm-contract --segment-speed-file speeds.csv {extracted_base}.osrm" + When I try to run "osrm-contract --segment-speed-file {speeds_file} {processed_file}" And stderr should contain "malformed" - And it should exit with code not 0 + And it should exit with an error diff --git a/features/car/traffic_turn_penalties.feature b/features/car/traffic_turn_penalties.feature index 09f5323b1d8..cb3907a5e50 100644 --- a/features/car/traffic_turn_penalties.feature +++ b/features/car/traffic_turn_penalties.feature @@ -58,7 +58,7 @@ Feature: Traffic - turn penalties 8,11,12,23 1,4,5,-0.2 """ - And the contract extra arguments "--turn-penalty-file penalties.csv" + And the contract extra arguments "--turn-penalty-file {penalties_file}" When I route I should get | from | to | route | speed | time | | a | h | ad,dhk,dhk | 63 km/h | 11.5s +-1 | @@ -81,7 +81,7 @@ Feature: Traffic - turn penalties # double left - hdc penalty ever so slightly higher than imn; forces all the way around Scenario: Too-negative penalty clamps, but does not fail - Given the contract extra arguments "--turn-penalty-file penalties.csv" + Given the contract extra arguments "--turn-penalty-file {penalties_file}" And the profile "testbot" And the turn penalty file """ diff --git a/features/foot/names.feature b/features/foot/names.feature index 6a28aaf311a..2c3d76bf631 100644 --- a/features/foot/names.feature +++ b/features/foot/names.feature @@ -16,7 +16,7 @@ Feature: Foot - Street names in instructions When I route I should get | from | to | route | - | a | c | My Way (A6),Your Way (B7),Your Way (B7) | + | a | c | My Way,Your Way,Your Way | @unnamed Scenario: Foot - Use way type to describe unnamed ways diff --git a/features/foot/ref.feature b/features/foot/ref.feature index 39bc3e8dcc7..a99c24bb307 100644 --- a/features/foot/ref.feature +++ b/features/foot/ref.feature @@ -14,7 +14,7 @@ Feature: Foot - Way ref When I route I should get | from | to | route | - | a | b | Utopia Drive (E7),Utopia Drive (E7) | + | a | b | Utopia Drive,Utopia Drive | Scenario: Foot - Way with only ref Given the node map @@ -25,8 +25,8 @@ Feature: Foot - Way ref | ab | | E7 | When I route I should get - | from | to | route | - | a | b | E7,E7 | + | from | to | route | + | a | b | {highway:primary},{highway:primary} | Scenario: Foot - Way with only name Given the node map diff --git a/features/guidance/advanced-lanes.feature b/features/guidance/advanced-lanes.feature index dc6e413fbf2..17bc97fe2c3 100644 --- a/features/guidance/advanced-lanes.feature +++ b/features/guidance/advanced-lanes.feature @@ -103,7 +103,7 @@ Feature: Turn Lane Guidance | a,j | in,turn,other,other | depart,turn right,turn left,arrive | ,,left:true right:false, | | a,i | in,turn,other,other | depart,turn right,turn right,arrive | ,,left:false right:true, | - @todo @bug @2654 @none + @todo @2654 @none #https://github.com/Project-OSRM/osrm-backend/issues/2645 #http://www.openstreetmap.org/export#map=19/52.56054/13.32152 Scenario: Kurt-Schuhmacher-Damm @@ -131,7 +131,7 @@ Feature: Turn Lane Guidance | a,f | ,ksd,ksd | depart,turn left,arrive | ,left:true none:true right:false, | | a,i | ,ksd,ksd | depart,turn right,arrive | ,left:false none:true right:true, | - @todo @bug @2650 @sliproads + @todo @2650 @sliproads #market and haight in SF, restricted turn #http://www.openstreetmap.org/#map=19/37.77308/-122.42238 Scenario: Market/Haight without Through Street @@ -177,7 +177,7 @@ Feature: Turn Lane Guidance | a,j | ghough,market,market | depart,turn left,arrive | ,none:true straight:false straight:false straight:false, | | a,f | ghough,ghough,ghough | depart,continue slight left,arrive | ,none:true straight:true straight:false straight:false, | - @todo @2650 @bug @sliproads + @todo @2650 @sliproads #market and haight in SF, unrestricted #http://www.openstreetmap.org/#map=19/37.77308/-122.42238 Scenario: Market/Haight without Through Street @@ -218,3 +218,19 @@ Feature: Turn Lane Guidance | a,h | ghough,market,market | depart,turn slight right,arrive | ,none:false straight:false straight:true straight:true, | | a,j | ghough,market,market | depart,turn left,arrive | ,none:true straight:false straight:false straight:false, | | a,f | ghough,ghough,ghough | depart,continue slight left,arrive | ,none:true straight:true straight:false straight:false, | + + Scenario: Check sliproad handler loop's exit condition, Issue #2896 + # http://www.openstreetmap.org/way/198481519 + Given the node locations + | node | lat | lon | + | a | 7.6125350 | 126.5708309 | + | b | 7.6125156 | 126.5707219 | + | c | 7.6125363 | 126.5708337 | + + And the ways + | nodes | name | + | cbac | | + + When I route I should get + | from | to | route | turns | + | a | c | , | depart,arrive | diff --git a/features/guidance/anticipate-lanes.feature b/features/guidance/anticipate-lanes.feature index f180b7a3343..049faf97c13 100644 --- a/features/guidance/anticipate-lanes.feature +++ b/features/guidance/anticipate-lanes.feature @@ -422,7 +422,7 @@ Feature: Turn Lane Guidance | waypoints | route | turns | lanes | | a,e | main,main,main,main | depart,use lane straight,continue right,arrive | ,left:false straight:false straight:false straight:false straight:true straight:true right:false,straight:false straight:false right:false right:true right:true, | - @anticipate @todo @bug @2661 + @anticipate @todo @2661 Scenario: Anticipate with lanes in roundabout: roundabouts as the unit of anticipation Given the node map | | | e | | | @@ -530,7 +530,10 @@ Feature: Turn Lane Guidance @anticipate Scenario: No Lanes for Roundabouts, see #2626 - Given the profile "lhs" + Given the profile file "car" extended with + """ + properties.left_hand_driving = true + """ And the node map | | | a | | | | | | b | | | @@ -667,7 +670,7 @@ Feature: Turn Lane Guidance | a,f | abc,bdeh,feg,feg | depart,turn right,turn right,arrive | ,none:false none:false right:false right:true,left:false none:false none:false right:true, | @anticipate - Scenario: Tripple Right keeping Left + Scenario: Triple Right keeping Left Given the node map | a | | | | b | | i | | | | | | | | | diff --git a/features/guidance/collapse.feature b/features/guidance/collapse.feature index c66ee28134d..ea318cf3931 100644 --- a/features/guidance/collapse.feature +++ b/features/guidance/collapse.feature @@ -792,3 +792,49 @@ Feature: Collapse When I route I should get | waypoints | route | turns | | a,e | main,main | depart,arrive | + + Scenario: Don't collapse different travel modes + Given the node map + | g | | | | | | | h | | + | a | b | | c | | | | e | f | + | | | | | | d | | | | + | | | | i | j | | | | | + + And the ways + | nodes | highway | route | name | + | ab | primary | | road | + | bc | primary | ferry | | + | cd | primary | | road | + | de | | ferry | | + | ef | primary | | road | + | bg | service | | turn | + | ci | service | | turn | + | dj | service | | turn | + | eh | service | | turn | + + When I route I should get + | waypoints | route | + | a,f | road,,road,,road,road | + + Scenario: U-Turn onto a Ferry + Given the node map + | | | | | | | i | | | + | j | e | | | | | d | c | h | + | | | | | | | | | | + | | | | | | | | | | + | k | g | | | | | a | b | f | + + And the ways + | nodes | highway | route | name | oneway | + | bf | primary | | road | yes | + | hcd | primary | | road | yes | + | bc | primary | | | yes | + | di | service | | serv | yes | + | ed | | ferry | ferry | | + | gab | | ferry | ferry | | + | kg | primary | | on | yes | + | ej | primary | | off | yes | + + When I route I should get + | waypoints | route | turns | + | k,j | on,ferry,,ferry,off,off | depart,notification straight,continue uturn,turn straight,notification straight,arrive | diff --git a/features/guidance/dedicated-turn-roads.feature b/features/guidance/dedicated-turn-roads.feature index ad0e2d68bf1..d6fe959cac2 100644 --- a/features/guidance/dedicated-turn-roads.feature +++ b/features/guidance/dedicated-turn-roads.feature @@ -165,8 +165,8 @@ Feature: Slipways and Dedicated Turn Lanes | qe | secondary_link | Ettlinger Allee | | yes | When I route I should get - | waypoints | route | turns | - | a,o | Schwarzwaldstrasse (L561),Ettlinger Allee,Ettlinger Allee | depart,turn right,arrive | + | waypoints | route | turns | ref | + | a,o | Schwarzwaldstrasse,Ettlinger Allee,Ettlinger Allee | depart,turn right,arrive | L561,, | Scenario: Traffic Lights everywhere #http://map.project-osrm.org/?z=18¢er=48.995336%2C8.383813&loc=48.995467%2C8.384548&loc=48.995115%2C8.382761&hl=en&alt=0 @@ -264,3 +264,88 @@ Feature: Slipways and Dedicated Turn Lanes When I route I should get | waypoints | bearings | route | turns | | b,a | 90,10 270,10 | circled,circled | depart,arrive | + + #http://www.openstreetmap.org/#map=19/38.90597/-77.01276 + Scenario: Don't falsly classify as sliproads + Given the node map + # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 + | | | | | | | | | | | | | | | | | | | | | | | | j | | | | | | | | + | a | b | | | | | | | | | | | | | | | | | | | | | | c | | | | | | | d | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | e | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | 1 | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | f | | | | | g | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | | | | | | | | i | | | | | | | h | + + And the ways + | nodes | name | highway | oneway | maxspeed | + | abcd | new york | primary | yes | 35 | + | befgh | m street | secondary | yes | 35 | + | igcj | 1st street | tertiary | no | 20 | + + And the nodes + | node | highway | + | c | traffic_signals | + | g | traffic_signals | + + When I route I should get + | waypoints | route | turns | # | + | a,d | new york,new york | depart,arrive | this is the sinatra route | + | a,j | new york,1st street,1st street | depart,turn left,arrive | | + | a,1 | new york,m street,1st street,1st street | depart,turn right,turn left,arrive | this can false be seen as a sliproad | + + # Merging into degree two loop on dedicated turn detection / 2927 + Scenario: Turn Instead of Ramp + Given the node map + | | | | | | | | | | | | | | | | f | + | | | | | g | | | | | | h | | | | | | + | | | | | | | | | | | | | d | | | e | + | i | | | | c | | | | | | j | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | b | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | a | | | | | | | | | | | | | | + + And the ways + | nodes | highway | name | oneway | + | abi | primary | road | yes | + | bcjd | primary | loop | yes | + | dhgf | primary | loop | yes | + | fed | primary | loop | yes | + + And the nodes + | node | highway | + | g | traffic_signals | + | c | traffic_signals | + + # We don't actually care about routes here, this is all about endless loops in turn discovery + When I route I should get + | waypoints | route | turns | + | a,i | road,road,road | depart,fork slight left,arrive | diff --git a/features/guidance/destination-signs.feature b/features/guidance/destination-signs.feature index 3c5aa4e6903..73f90d9b067 100644 --- a/features/guidance/destination-signs.feature +++ b/features/guidance/destination-signs.feature @@ -29,13 +29,13 @@ Feature: Destination Signs | qr | QR | | | A1;A2 | yes | | When I route I should get - | from | to | route | destinations | # | - | a | b | AB (E1),AB (E1) | , | | - | c | d | CD (Berlin),CD (Berlin) | Berlin,Berlin | | - | e | f | EF (A1: Berlin),EF (A1: Berlin) | A1: Berlin,A1: Berlin | | - | g | h | , | A1: Berlin,A1: Berlin | | - | i | j | , | Berlin,Berlin | | - | k | l | KL (E1),KL (E1) | A1: Berlin,A1: Berlin | | - | m | n | MN (A1, A2: Berlin, Hamburg),MN (A1, A2: Berlin, Hamburg) | A1, A2: Berlin, Hamburg,A1, A2: Berlin, Hamburg | | - | o | p | OP,OP | , | guard against mis-tagging | - | q | r | QR (A1, A2),QR (A1, A2) | A1, A2,A1, A2 | | + | from | to | route | destinations | ref | # | + | a | b | AB,AB | , | E1,E1 | | + | c | d | CD,CD | Berlin,Berlin | , | | + | e | f | EF,EF | A1: Berlin,A1: Berlin | , | | + | g | h | , | A1: Berlin,A1: Berlin | , | | + | i | j | , | Berlin,Berlin | , | | + | k | l | KL,KL | A1: Berlin,A1: Berlin | E1,E1 | | + | m | n | MN,MN | A1, A2: Berlin, Hamburg,A1, A2: Berlin, Hamburg | , | | + | o | p | OP,OP | , | , | guard against mis-tagging | + | q | r | QR,QR | A1, A2,A1, A2 | , | | diff --git a/features/guidance/fork.feature b/features/guidance/fork.feature index 047d897bd89..879584aaef3 100644 --- a/features/guidance/fork.feature +++ b/features/guidance/fork.feature @@ -22,6 +22,37 @@ Feature: Fork Instructions | a,c | ab,bc,bc | depart,fork slight left,arrive | | a,d | ab,bd,bd | depart,fork slight right,arrive | + Scenario: Don't Fork On Single Road + Given the node map + | | | | | c | + | a | | b | | | + | | | | | d | + + And the ways + | nodes | highway | oneway | + | ab | primary | no | + | cb | primary | yes | + | bd | primary | yes | + + When I route I should get + | waypoints | route | turns | + | a,d | ab,bd,bd | depart,new name slight right,arrive | + + Scenario: Don't Fork On Single Road + Given the node map + | | | | | | | c | + | a | | b | | d | | | + + And the ways + | nodes | highway | oneway | name | + | ab | primary | no | road | + | cb | primary | yes | road | + | bd | primary | yes | turn | + + When I route I should get + | waypoints | route | turns | + | a,d | road,turn,turn | depart,new name straight,arrive | + Scenario: Do not fork on link type Given the node map | | | | | c | diff --git a/features/guidance/new-name.feature b/features/guidance/new-name.feature index 748380d126c..9ff97561bf6 100644 --- a/features/guidance/new-name.feature +++ b/features/guidance/new-name.feature @@ -164,3 +164,172 @@ Feature: New-Name Instructions | waypoints | route | turns | | a,e | name,with-name,with-name | depart,new name straight,arrive | | b,e | with-name,with-name | depart,arrive | + + Scenario: Both Name and Ref Empty + Given the node map + | a | | b | | c | + + And the ways + | nodes | name | ref | + | ab | | | + | bc | | | + + When I route I should get + | waypoints | route | turns | + | a,c | , | depart,arrive | + + Scenario: Same Name, Ref Extended + Given the node map + | a | | b | | c | + + And the ways + | nodes | name | ref | + | ab | A | B1 | + | bc | C | B1;B2 | + + When I route I should get + | waypoints | route | turns | + | a,c | A,C,C | depart,new name straight,arrive | + + Scenario: Same Name, Ref Removed + Given the node map + | a | | b | | c | + + And the ways + | nodes | name | ref | + | ab | A | B1;B2 | + | bc | C | B1 | + + When I route I should get + | waypoints | route | turns | + | a,c | A,C,C | depart,new name straight,arrive | + + Scenario: Name Removed, Ref Extended + Given the node map + | a | | b | | c | + + And the ways + | nodes | name | ref | + | ab | A | B1 | + | bc | | B1;B2 | + + When I route I should get + | waypoints | route | turns | + | a,c | A, | depart,arrive | + + Scenario: Name Added, Ref Removed + Given the node map + | a | | b | | c | + + And the ways + | nodes | name | ref | + | ab | | B1 | + | bc | A | | + + When I route I should get + | waypoints | route | turns | + | a,c | ,A,A | depart,new name straight,arrive | + + Scenario: Prefix Change + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | North Central Expressway | US 75 | motorway | + | bc | Central Expressway | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | North Central Expressway,Central Expressway,Central Expressway | depart,new name straight,arrive | + + Scenario: Prefix Change + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ba | North Central Expressway | US 75 | motorway | + | cb | Central Expressway | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | c,a | Central Expressway,North Central Expressway,North Central Expressway | depart,new name straight,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | Central Expressway | US 75 | motorway | + | bc | | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | Central Expressway, | depart,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | | US 75 | motorway | + | bc | Central Expressway | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | ,Central Expressway | depart,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | | US 75;US 69 | motorway | + | bc | | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | , | depart,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | | US 69;US 75 | motorway | + | bc | | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | , | depart,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | | US 75 | motorway | + | bc | | US 75;US 69 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | , | depart,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | | US 75 | motorway | + | bc | | US 69;US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | , | depart,arrive | diff --git a/features/guidance/notification.feature b/features/guidance/notification.feature new file mode 100644 index 00000000000..f6f17ae2baf --- /dev/null +++ b/features/guidance/notification.feature @@ -0,0 +1,57 @@ +@routing @guidance @mode-change +Feature: Notification on turn onto mode change + + Background: + Given the profile "car" + Given a grid size of 400 meters + + Scenario: Turn onto a Ferry + Given the node map + | f | | | | | + | b | | | d | | + | a | | | | e | + + And the ways + | nodes | highway | route | name | + | abf | primary | | | + | bd | | ferry | ferry | + | de | primary | | | + + When I route I should get + | waypoints | route | turns | modes | + | a,e | ,ferry,, | depart,turn right,notification right,arrive | driving,ferry,driving,driving | + + Scenario: Turn onto a Ferry + Given the node map + | h | | | g | + | a | c | | e | + | b | | | f | + + And the ways + | nodes | highway | route | name | + | ac | primary | | | + | bah | primary | | | + | ec | | ferry | ferry | + | gef | primary | | | + + When I route I should get + | waypoints | route | turns | modes | + | g,h | ,ferry,,, | depart,turn right,notification straight,turn right,arrive | driving,ferry,driving,driving,driving | + | b,g | ,,ferry,, | depart,turn right,notification straight,turn left,arrive | driving,driving,ferry,driving,driving | + + Scenario: Straight onto a Ferry + Given the node map + | | | | | | + | | c | d | | i | + | a | | | | | + | | | | f | | + + And the ways + | nodes | highway | route | name | + | ac | primary | | | + | dc | | ferry | ferry | + | df | primary | | | + + When I route I should get + | waypoints | route | turns | modes | + | a,f | ,ferry,, | depart,notification right,notification right,arrive | driving,ferry,driving,driving | diff --git a/features/guidance/roundabout-turn.feature b/features/guidance/roundabout-turn.feature index 743f2fabbf3..00b7644a0a2 100644 --- a/features/guidance/roundabout-turn.feature +++ b/features/guidance/roundabout-turn.feature @@ -243,9 +243,29 @@ Feature: Basic Roundabout | df | | When I route I should get - | waypoints | route | turns | - | a,e | ab,ce,ce | depart,roundabout-exit-1,arrive | - | a,f | ab,df,df | depart,roundabout-exit-2,arrive | + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,roundabout turn right exit-1,arrive | + | a,f | ab,df,df | depart,roundabout turn straight exit-2,arrive | + + Scenario: Collinear in Y + Given the node map + | | a | + | | b | + | e | c | + | | d | + | | f | + + And the ways + | nodes | junction | + | ab | | + | bcdb | roundabout | + | ce | | + | df | | + + When I route I should get + | waypoints | route | turns | + | a,e | ab,ce,ce | depart,roundabout turn right exit-1,arrive | + | a,f | ab,df,df | depart,roundabout turn straight exit-2,arrive | Scenario: Collinear in X,Y Given the node map @@ -491,3 +511,23 @@ Feature: Basic Roundabout | h,a | gh,ab,ab | depart,roundabout turn left exit-3,arrive | | h,d | gh,cd,cd | depart,roundabout turn straight exit-2,arrive | | h,f | gh,ef,ef | depart,roundabout turn right exit-1,arrive | + + #http://www.openstreetmap.org/#map=19/41.03275/-2.18990 + #at some point we probably want to recognise these situations and don't mention the roundabout at all here + Scenario: Enter And Exit Throughabout + Given the node map + | | | | | | h | | | | | + | | | | | | | | | | | + | c | b | | d | | | | e | | f | + | | | | | | | | | | | + | | a | | | | g | | | | | + + And the ways + | nodes | highway | name | junction | oneway | + | dghd | tertiary_link | | roundabout | | + | cbdef | trunk | through | | no | + | ab | residential | in | | | + + When I route I should get + | waypoints | turns | route | + | a,f | depart,turn right,roundabout turn straight exit-1,arrive | in,through,through,through | diff --git a/features/guidance/roundabout.feature b/features/guidance/roundabout.feature index 9700cae89df..56af30c48a7 100644 --- a/features/guidance/roundabout.feature +++ b/features/guidance/roundabout.feature @@ -36,6 +36,25 @@ Feature: Basic Roundabout | h,c | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | | h,e | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + #2927 + Scenario: Only Roundabout + Given the node map + | | | a | | | + | | | | | | + | | | | | | + | b | | | | d | + | | | | | | + | | | | | | + | | | c | | | + + And the ways + | nodes | junction | + | abcda | roundabout | + + When I route I should get + | waypoints | route | turns | + | a,c | abcda,abcda | depart,arrive | + Scenario: Only Exit Given the node map | | | a | | | @@ -212,43 +231,6 @@ Feature: Basic Roundabout | j,f | jk,ef,ef | depart,roundabout-exit-2,arrive | | j,c | jk,bc,bc | depart,roundabout-exit-3,arrive | - Scenario: Collinear in X - Given the node map - | a | b | c | d | f | - | | | e | | | - - And the ways - | nodes | junction | - | ab | | - | bcdb | roundabout | - | ce | | - | df | | - - When I route I should get - | waypoints | route | turns | - | a,e | ab,ce,ce | depart,roundabout-exit-1,arrive | - | a,f | ab,df,df | depart,roundabout-exit-2,arrive | - - Scenario: Collinear in Y - Given the node map - | | a | - | | b | - | e | c | - | | d | - | | f | - - And the ways - | nodes | junction | - | ab | | - | bcdb | roundabout | - | ce | | - | df | | - - When I route I should get - | waypoints | route | turns | - | a,e | ab,ce,ce | depart,roundabout-exit-1,arrive | - | a,f | ab,df,df | depart,roundabout-exit-2,arrive | - Scenario: Motorway Roundabout #See 39.933742 -75.082345 Given the node map @@ -283,9 +265,9 @@ Feature: Basic Roundabout | dmg | roundabout | | trunk_link | yes | | When I route I should get - | waypoints | route | turns | - | a,e | crescent (US 130),crescent (US 130),crescent (US 130) | depart,roundabout-exit-3,arrive | - | j,l | NJ 38,NJ 38,NJ 38 | depart,roundabout-exit-2,arrive | + | waypoints | route | turns | ref | + | a,e | crescent,crescent,crescent | depart,roundabout-exit-3,arrive | US 130,US 130,US 130 | + | j,l | ,, | depart,roundabout-exit-2,arrive | NJ 38,NJ 38,NJ 38 | Scenario: Double Roundabout with through-lane #http://map.project-osrm.org/?z=18¢er=38.911752%2C-77.048667&loc=38.912003%2C-77.050831&loc=38.909277%2C-77.042516&hl=en&alt=0 diff --git a/features/guidance/suffix-changes.feature b/features/guidance/suffix-changes.feature index 06a5d2189c4..ae7a79a4578 100644 --- a/features/guidance/suffix-changes.feature +++ b/features/guidance/suffix-changes.feature @@ -28,8 +28,8 @@ Feature: Suppress New Names on dedicated Suffices | bc | 42 S | 101 | When I route I should get - | waypoints | route | turns | - | a,c | 42 N,42 S (101) | depart,arrive | + | waypoints | route | turns | ref | + | a,c | 42 N,42 S | depart,arrive | ,101 | Scenario: Prefix Change Given the node map @@ -67,8 +67,8 @@ Feature: Suppress New Names on dedicated Suffices | bc | East 42 | | When I route I should get - | waypoints | route | turns | - | a,c | West 42 (101),East 42 | depart,arrive | + | waypoints | route | turns | ref | + | a,c | West 42,East 42 | depart,arrive | 101, | Scenario: Suffix To Suffix - Turn Given the node map diff --git a/features/guidance/suppressed.feature b/features/guidance/suppressed.feature index 89b58655cdf..02555add5cd 100644 --- a/features/guidance/suppressed.feature +++ b/features/guidance/suppressed.feature @@ -32,8 +32,8 @@ Feature: Suppressed Turns | ef | motorway | highway | A1 | When I route I should get - | waypoints | route | turns | - | a,f | highway (A1),highway (A1) | depart,arrive | + | waypoints | route | turns | ref | + | a,f | highway,highway | depart,arrive | A1,A1 | Scenario: Don't Announce Turn on following major road class -- service diff --git a/features/guidance/turn-lanes.feature b/features/guidance/turn-lanes.feature index e1d493b49c8..81c094b8dd7 100644 --- a/features/guidance/turn-lanes.feature +++ b/features/guidance/turn-lanes.feature @@ -145,7 +145,6 @@ Feature: Turn Lane Guidance | a,c | road,road | depart,arrive | , | # This tests whether empty/invalid PSV tags cause osrm-extract to crash - @bug Scenario: Turn with Bus-Lane Given the node map | a | | b | | c | @@ -181,7 +180,7 @@ Feature: Turn Lane Guidance | a,c | road,road | depart,arrive | #turn lanes are often drawn at the incoming road, even though the actual turn requires crossing the intersection first - @todo @bug @collapse @partition-lanes + @todo @collapse @partition-lanes Scenario: Turn Lanes at Segregated Road Given the node map | | | i | l | | | @@ -383,7 +382,7 @@ Feature: Turn Lane Guidance | a,f | road,turn,turn | depart,turn left,arrive | ,left:true right:false, | | a,g | road,right,right | depart,turn right,arrive | ,left:false right:true, | - @todo @bug @partition-lanes @previous-lanes + @todo @partition-lanes @previous-lanes Scenario: Narrowing Turn Lanes Given the node map | | | | | g | | @@ -428,7 +427,7 @@ Feature: Turn Lane Guidance | a,d | road,road | depart,arrive | , | | a,e | road,turn,turn | depart,turn right,arrive | ,straight:false right:true, | - @bug @todo @roundabout + @todo @roundabout Scenario: Theodor Heuss Platz Given the node map | | | | i | o | | | l | | @@ -555,7 +554,7 @@ Feature: Turn Lane Guidance | a,e | road,road,road | depart,turn uturn,arrive | ,left:true left:false left:false straight:false straight:false, | | a,g | road,straight,straight | depart,new name straight,arrive | ,left:false left:false left:false straight:true straight:true, | - @bug @todo @roundabout + @todo @roundabout Scenario: Passing Through a Roundabout Given the node map | | | h | | g | | | @@ -620,7 +619,7 @@ Feature: Turn Lane Guidance | a,d | hwy,hwy | depart,arrive | , | | a,e | hwy,ramp,ramp | depart,off ramp slight right,arrive | ,straight:false straight:false straight;slight right:true slight right:true, | - @bug @todo + @todo Scenario: Turning Off Ramp Given the node map | | a | | @@ -864,7 +863,7 @@ Feature: Turn Lane Guidance | a,c | ab,bc,bc | depart,turn left,arrive | ,left:true right:false, | # http://www.openstreetmap.org/#map=19/47.97685/7.82933&layers=D - @bug @todo + @todo Scenario: Lane Parsing Issue #2706: None Assignments I Given the node map | | f | | | j | | @@ -904,7 +903,7 @@ Feature: Turn Lane Guidance # Note: at the moment we don't care about routes, we care about the extract process triggering assertions # https://www.openstreetmap.org/#map=19/47.99257/7.83276&layers=D - @bug @todo + @todo Scenario: Lane Parsing Issue #2706: None Assignments II Given the node map | | k | l | | @@ -939,7 +938,7 @@ Feature: Turn Lane Guidance | i,e |||| # Note: at the moment we don't care about routes, we care about the extract process triggering assertions - @bug @todo + @todo Scenario: Lane Parsing Issue #2706: None Assignments III - Minimal reproduction recipe Given the node map | | | l | | diff --git a/features/guidance/turn.feature b/features/guidance/turn.feature index 6c3184b5169..73b23ae99b3 100644 --- a/features/guidance/turn.feature +++ b/features/guidance/turn.feature @@ -133,6 +133,22 @@ Feature: Simple Turns | d,c | db,cb,cb | depart,turn right,arrive | | d,a | db,ab,ab | depart,new name straight,arrive | + Scenario: Three Way Intersection - Meeting Oneways + Given the node map + | | c | | + | a | b | d | + + And the ways + | nodes | highway | oneway | + | ab | primary | yes | + | bc | primary | yes | + | db | primary | yes | + + When I route I should get + | waypoints | route | turns | + | a,c | ab,bc,bc | depart,turn left,arrive | + | d,c | db,bc,bc | depart,turn right,arrive | + Scenario: Three Way Intersection on Through Street Given the node map | | d | | @@ -1064,3 +1080,73 @@ Feature: Simple Turns | f,a | depart,arrive | Hermannstr,Hermannstr | | y,f | depart,arrive | Hermannstr,Hermannstr | | f,y | depart,arrive | Hermannstr,Hermannstr | + + # http://www.openstreetmap.org/#map=18/39.28158/-76.62291 + @3002 + Scenario: Obvious Index wigh very narrow turn to the right + Given the node map + | a | | b | | | | | | c | + | | | | | | | | | d | + + And the ways + | nodes | highway | name | + | abc | primary | road | + | bd | primary_link | | + + When I route I should get + | waypoints | turns | route | + | a,c | depart,arrive | road,road | + | a,d | depart,turn slight right,arrive | road,, | + + # http://www.openstreetmap.org/#map=18/39.28158/-76.62291 + @3002 + Scenario: Obvious Index wigh very narrow turn to the right + Given the node map + | a | | b | | | | | | c | | + | | | | | e | | | | d | f | + + And the ways + | nodes | highway | name | + | abc | primary | road | + | bd | primary_link | | + | edf | primary_link | | + + When I route I should get + | waypoints | turns | route | + | a,c | depart,arrive | road,road | + | a,f | depart,turn slight right,arrive | road,, | + + # http://www.openstreetmap.org/#map=18/39.28158/-76.62291 + @3002 + Scenario: Obvious Index wigh very narrow turn to the left + Given the node map + | | | | | | | | | d | + | a | | b | | | | | | c | + + And the ways + | nodes | highway | name | + | abc | primary | road | + | bd | primary_link | | + + When I route I should get + | waypoints | turns | route | + | a,c | depart,arrive | road,road | + | a,d | depart,turn slight left,arrive | road,, | + + # http://www.openstreetmap.org/#map=18/39.28158/-76.62291 + @3002 + Scenario: Obvious Index wigh very narrow turn to the left + Given the node map + | | | | | e | | | | d | f | + | a | | b | | | | | | c | | + + And the ways + | nodes | highway | name | + | abc | primary | road | + | bd | primary_link | | + | edf | primary_link | | + + When I route I should get + | waypoints | turns | route | + | a,f | depart,turn slight left,arrive | road,, | + | a,c | depart,arrive | road,road | diff --git a/features/lib/hash.js b/features/lib/hash.js new file mode 100644 index 00000000000..57563e3da84 --- /dev/null +++ b/features/lib/hash.js @@ -0,0 +1,31 @@ +'use strict'; + +const fs = require('fs'); +const crypto = require('crypto'); +const d3 = require('d3-queue'); + +module.exports = { + hashOfFiles: (paths, cb) => { + let queue = d3.queue(); + for (let i = 0; i < paths.length; ++i) { + queue.defer(fs.readFile, paths[i]); + } + queue.awaitAll((err, results) => { + if (err) return cb(err); + let checksum = crypto.createHash('md5'); + for (let i = 0; i < results.length; ++i) { + checksum.update(results[i]); + } + cb(null, checksum.digest('hex')); + }); + }, + + hashOfFile: (path, cb) => { + fs.readFile(path, (err, result) => { + if (err) return cb(err); + let checksum = crypto.createHash('md5'); + checksum.update(result); + cb(null, checksum.digest('hex')); + }); + } +}; diff --git a/features/support/build_osm.js b/features/lib/osm.js similarity index 96% rename from features/support/build_osm.js rename to features/lib/osm.js index 7fe6874aeea..5387b7f0110 100644 --- a/features/support/build_osm.js +++ b/features/lib/osm.js @@ -1,11 +1,7 @@ 'use strict'; -var builder = require('xmlbuilder'); - -var ensureDecimal = (i) => { - if (parseInt(i) === i) return i.toFixed(1); - else return i; -}; +const builder = require('xmlbuilder'); +const ensureDecimal = require('./utils').ensureDecimal; class DB { constructor () { diff --git a/features/lib/osrm_loader.js b/features/lib/osrm_loader.js new file mode 100644 index 00000000000..28ec7ed2b42 --- /dev/null +++ b/features/lib/osrm_loader.js @@ -0,0 +1,169 @@ +'use strict'; + +const fs = require('fs'); +const util = require('util'); +const Timeout = require('node-timeout'); +const tryConnect = require('../lib/try_connect'); +const errorReason = require('./utils').errorReason; + +class OSRMBaseLoader{ + constructor (scope) { + this.scope = scope; + this.child = null; + } + + launch (callback) { + var limit = Timeout(this.scope.TIMEOUT, { err: new Error('*** Launching osrm-routed timed out.') }); + + var runLaunch = (cb) => { + this.osrmUp(() => { this.waitForConnection(cb); }); + }; + + runLaunch(limit((e) => { if (e) callback(e); else callback(); })); + } + + shutdown (callback) { + if (!this.osrmIsRunning()) return callback(); + + var limit = Timeout(this.scope.TIMEOUT, { err: new Error('*** Shutting down osrm-routed timed out.')}); + + this.osrmDown(limit(callback)); + } + + osrmIsRunning () { + return this.child && !this.child.killed; + } + + osrmDown (callback) { + if (this.osrmIsRunning()) { + this.child.on('exit', (code, signal) => {callback();}); + this.child.kill(); + } else callback(); + } + + waitForConnection (callback) { + var retryCount = 0; + let retry = (err) => { + if (err) { + if (retryCount < 10) { + retryCount++; + setTimeout(() => { tryConnect(this.scope.OSRM_PORT, retry); }, 10); + } else { + callback(new Error("Could not connect to osrm-routed after ten retries.")); + } + } + else + { + callback(); + } + }; + + tryConnect(this.scope.OSRM_PORT, retry); + } +}; + +class OSRMDirectLoader extends OSRMBaseLoader { + constructor (scope) { + super(scope); + } + + load (inputFile, callback) { + this.inputFile = inputFile; + this.shutdown(() => { + this.launch(callback); + }); + } + + osrmUp (callback) { + if (this.osrmIsRunning()) return callback(new Error("osrm-routed already running!")); + + this.child = this.scope.runBin('osrm-routed', util.format("%s -p %d", this.inputFile, this.scope.OSRM_PORT), this.scope.environment, (err) => { + if (err) { + throw new Error(util.format('osrm-routed %s: %s', errorReason(err), err.cmd)); + } + }); + callback(); + } +}; + +class OSRMDatastoreLoader extends OSRMBaseLoader { + constructor (scope) { + super(scope); + } + + load (inputFile, callback) { + this.inputFile = inputFile; + + this.loadData((err) => { + if (err) return callback(err); + if (!this.osrmIsRunning()) this.launch(callback); + else { + this.scope.setupOutputLog(this.child, fs.createWriteStream(this.scope.scenarioLogFile, {'flags': 'a'})); + callback(); + } + }); + } + + loadData (callback) { + this.scope.runBin('osrm-datastore', this.inputFile, this.scope.environment, (err) => { + if (err) return callback(new Error('*** osrm-datastore exited with ' + err.code + ': ' + err)); + callback(); + }); + } + + osrmUp (callback) { + if (this.osrmIsRunning()) return callback(); + + this.child = this.scope.runBin('osrm-routed', util.format('--shared-memory=1 -p %d', this.scope.OSRM_PORT), this.scope.environment, (err) => { + if (err) { + throw new Error(util.format('osrm-routed %s: %s', errorReason(err), err.cmd)); + } + }); + + // we call the callback here, becuase we don't want to wait for the child process to finish + callback(); + } +}; + +class OSRMLoader { + constructor (scope) { + this.scope = scope; + this.sharedLoader = new OSRMDatastoreLoader(this.scope); + this.directLoader = new OSRMDirectLoader(this.scope); + this.method = scope.DEFAULT_LOAD_METHOD; + } + + load (inputFile, callback) { + if (this.method === 'datastore') { + this.directLoader.shutdown((err) => { + if (err) return callback(err); + this.loader = this.sharedLoader; + this.sharedLoader.load(inputFile, callback); + }); + } else if (this.method === 'directly') { + this.sharedLoader.shutdown((err) => { + if (err) return callback(err); + this.loader = this.directLoader; + this.directLoader.load(inputFile, callback); + }); + } else { + callback(new Error('*** Unknown load method ' + method)); + } + } + + setLoadMethod (method) { + this.method = method; + } + + shutdown (callback) { + if (!this.loader) return callback(); + + this.loader.shutdown(callback); + } + + up () { + return this.loader ? this.loader.osrmIsRunning() : false; + } +}; + +module.exports = OSRMLoader; diff --git a/features/lib/table_diff.js b/features/lib/table_diff.js new file mode 100644 index 00000000000..4acbd23c139 --- /dev/null +++ b/features/lib/table_diff.js @@ -0,0 +1,54 @@ +'use strict'; + +var util = require('util'); +var path = require('path'); +var fs = require('fs'); +var chalk = require('chalk'); + +var unescapeStr = (str) => str.replace(/\\\|/g, '\|').replace(/\\\\/g, '\\'); + +module.exports = function (expected, actual) { + let headers = expected.raw()[0]; + let expected_keys = expected.hashes(); + let diff = []; + let hasErrors = false; + + var good = 0, bad = 0; + + expected_keys.forEach((row, i) => { + var rowError = false; + + for (var j in row) { + if (unescapeStr(row[j]) != actual[i][j]) { + rowError = true; + hasErrors = true; + break; + } + } + + if (rowError) { + bad++; + diff.push(Object.assign({}, row, {c_status: 'undefined'})); + diff.push(Object.assign({}, actual[i], {c_status: 'comment'})); + } else { + good++; + diff.push(row); + } + }); + + if (!hasErrors) return null; + + var s = ['Tables were not identical:']; + s.push(headers.map(key => ' ' + key).join(' | ')); + diff.forEach((row) => { + var rowString = '| '; + headers.forEach((header) => { + if (!row.c_status) rowString += chalk.green(' ' + row[header] + ' | '); + else if (row.c_status === 'undefined') rowString += chalk.yellow('(-) ' + row[header] + ' | '); + else rowString += chalk.red('(+) ' + row[header] + ' | '); + }); + s.push(rowString); + }); + + return s.join('\n') + '\nTODO this is a temp workaround waiting for https://github.com/cucumber/cucumber-js/issues/534'; +}; diff --git a/features/lib/try_connect.js b/features/lib/try_connect.js new file mode 100644 index 00000000000..0461dddb8dd --- /dev/null +++ b/features/lib/try_connect.js @@ -0,0 +1,13 @@ +'use strict'; + +const net = require('net'); +const Timeout = require('node-timeout'); + +module.exports = function tryConnect(port, callback) { + net.connect({ port: port, host: '127.0.0.1' }) + .on('connect', () => { callback(); }) + .on('error', () => { + callback(new Error('Could not connect.')); + }); +} + diff --git a/features/lib/utils.js b/features/lib/utils.js new file mode 100644 index 00000000000..27b63af6cd1 --- /dev/null +++ b/features/lib/utils.js @@ -0,0 +1,17 @@ +'use strict'; + +const util = require('util'); + +module.exports = { + + ensureDecimal: (i) => { + if (parseInt(i) === i) return i.toFixed(1); + else return i; + }, + + errorReason: (err) => { + return err.signal ? + util.format('killed by signal %s', err.signal) : + util.format('exited with code %d', err.code); + } +}; diff --git a/features/options/contract/datasources.feature b/features/options/contract/datasources.feature index bf8eed5d572..19913885f88 100644 --- a/features/options/contract/datasources.feature +++ b/features/options/contract/datasources.feature @@ -1,8 +1,7 @@ @prepare @options @files Feature: osrm-contract command line options: datasources # expansions: -# {extracted_base} => path to current extracted input file -# {profile} => path to current profile script +# {processed_file} => path to .osrm file Background: Given the profile "testbot" @@ -24,7 +23,6 @@ Feature: osrm-contract command line options: datasources And the data has been extracted Scenario: osrm-contract - Passing base file - When I run "osrm-contract --segment-speed-file speeds.csv {extracted_base}.osrm" - Then stderr should be empty - And datasource names should contain "lua profile,speeds" - And it should exit with code 0 + When I run "osrm-contract --segment-speed-file {speeds_file} {processed_file}" + Then datasource names should contain "lua profile,25_osrmcontract_passing_base_file_speeds" + And it should exit successfully diff --git a/features/options/contract/edge-weight-updates-over-factor.feature b/features/options/contract/edge-weight-updates-over-factor.feature new file mode 100644 index 00000000000..85e5fa8fecb --- /dev/null +++ b/features/options/contract/edge-weight-updates-over-factor.feature @@ -0,0 +1,30 @@ +@contract @options @edge-weight-updates-over-factor +Feature: osrm-contract command line option: edge-weight-updates-over-factor + + Background: Log edge weight updates over given factor + Given the node locations + | node | lat | lon | + | a | 0.1 | 0.1 | + | b | 0.05 | 0.1 | + | c | 0.3 | 0.1 | + And the ways + | nodes | highway | + | ab | residential | + | ac | primary | + Given the profile "testbot" + Given the speed file + """ + 1,2,100 + 1,3,100 + """ + And the data has been saved to disk + + Scenario: Logging weight with updates over factor of 2, long segment + When I run "osrm-extract --profile {profile_file} {osm_file} --generate-edge-lookup" + When I run "osrm-contract --edge-weight-updates-over-factor 2 --segment-speed-file {speeds_file} {processed_file}" + And stderr should contain "weight updates" + And stderr should contain "New speed: 100 kph" + And I route I should get + | from | to | route | speed | + | a | b | ab,ab | 100 km/h | + | a | c | ac,ac | 100 km/h | diff --git a/features/options/contract/files.feature b/features/options/contract/files.feature index 5e50e14f285..46cf4fba01b 100644 --- a/features/options/contract/files.feature +++ b/features/options/contract/files.feature @@ -1,9 +1,5 @@ @prepare @options @files Feature: osrm-contract command line options: files -# expansions: -# {extracted_base} => path to current extracted input file -# {profile} => path to current profile script - Background: Given the profile "testbot" And the node map @@ -14,12 +10,11 @@ Feature: osrm-contract command line options: files And the data has been extracted Scenario: osrm-contract - Passing base file - When I run "osrm-contract {extracted_base}.osrm" - Then stderr should be empty - And it should exit with code 0 + When I run "osrm-contract {processed_file}" + Then it should exit successfully Scenario: osrm-contract - Missing input file - When I run "osrm-contract over-the-rainbow.osrm" + When I try to run "osrm-contract over-the-rainbow.osrm" And stderr should contain "over-the-rainbow.osrm" And stderr should contain "not found" - And it should exit with code 1 + And it should exit with an error diff --git a/features/options/contract/help.feature b/features/options/contract/help.feature index 411bc55da6b..b4d81c557f3 100644 --- a/features/options/contract/help.feature +++ b/features/options/contract/help.feature @@ -2,7 +2,7 @@ Feature: osrm-contract command line options: help Scenario: osrm-contract - Help should be shown when no options are passed - When I run "osrm-contract" + When I try to run "osrm-contract" Then stderr should be empty And stdout should contain "osrm-contract [options]:" And stdout should contain "Options:" @@ -13,7 +13,7 @@ Feature: osrm-contract command line options: help And stdout should contain "--core" And stdout should contain "--level-cache" And stdout should contain "--segment-speed-file" - And it should exit with code 1 + And it should exit with an error Scenario: osrm-contract - Help, short When I run "osrm-contract -h" @@ -27,7 +27,7 @@ Feature: osrm-contract command line options: help And stdout should contain "--core" And stdout should contain "--level-cache" And stdout should contain "--segment-speed-file" - And it should exit with code 0 + And it should exit successfully Scenario: osrm-contract - Help, long When I run "osrm-contract --help" @@ -41,4 +41,4 @@ Feature: osrm-contract command line options: help And stdout should contain "--core" And stdout should contain "--level-cache" And stdout should contain "--segment-speed-file" - And it should exit with code 0 + And it should exit successfully diff --git a/features/options/contract/invalid.feature b/features/options/contract/invalid.feature index 38ee3ace9ac..127761ee3be 100644 --- a/features/options/contract/invalid.feature +++ b/features/options/contract/invalid.feature @@ -5,8 +5,8 @@ Feature: osrm-contract command line options: invalid options Given the profile "testbot" Scenario: osrm-contract - Non-existing option - When I run "osrm-contract --fly-me-to-the-moon" + When I try to run "osrm-contract --fly-me-to-the-moon" Then stdout should be empty And stderr should contain "option" And stderr should contain "fly-me-to-the-moon" - And it should exit with code 1 + And it should exit with an error diff --git a/features/options/contract/version.feature b/features/options/contract/version.feature index be99bbed150..88ec8b734e4 100644 --- a/features/options/contract/version.feature +++ b/features/options/contract/version.feature @@ -6,17 +6,17 @@ Feature: osrm-contract command line options: version Background: Given the profile "testbot" - + Scenario: osrm-contract - Version, short When I run "osrm-contract --v" Then stderr should be empty And stdout should contain 1 line And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ - And it should exit with code 0 + And it should exit successfully Scenario: osrm-contract - Version, long When I run "osrm-contract --version" Then stderr should be empty And stdout should contain 1 line And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ - And it should exit with code 0 + And it should exit successfully diff --git a/features/options/extract/files.feature b/features/options/extract/files.feature index aceab19f74a..c4e14a278f3 100644 --- a/features/options/extract/files.feature +++ b/features/options/extract/files.feature @@ -14,17 +14,15 @@ Feature: osrm-extract command line options: files And the data has been saved to disk Scenario: osrm-extract - Passing base file - When I run "osrm-extract {osm_base}.osm --profile {profile}" - Then stderr should be empty - And it should exit with code 0 + When I run "osrm-extract {osm_file} --profile {profile_file}" + Then it should exit successfully Scenario: osrm-extract - Order of options should not matter - When I run "osrm-extract --profile {profile} {osm_base}.osm" - Then stderr should be empty - And it should exit with code 0 + When I run "osrm-extract --profile {profile_file} {osm_file}" + Then it should exit successfully Scenario: osrm-extract - Missing input file - When I run "osrm-extract over-the-rainbow.osrm --profile {profile}" + When I try to run "osrm-extract over-the-rainbow.osrm --profile {profile_file}" And stderr should contain "over-the-rainbow.osrm" And stderr should contain "not found" - And it should exit with code 1 + And it should exit with an error diff --git a/features/options/extract/help.feature b/features/options/extract/help.feature index cdf1eb9a36a..0d400edbafe 100644 --- a/features/options/extract/help.feature +++ b/features/options/extract/help.feature @@ -16,7 +16,7 @@ Feature: osrm-extract command line options: help And stdout should contain "--threads" And stdout should contain "--generate-edge-lookup" And stdout should contain "--small-component-size" - And it should exit with code 0 + And it should exit successfully Scenario: osrm-extract - Help, short When I run "osrm-extract -h" @@ -30,7 +30,7 @@ Feature: osrm-extract command line options: help And stdout should contain "--threads" And stdout should contain "--generate-edge-lookup" And stdout should contain "--small-component-size" - And it should exit with code 0 + And it should exit successfully Scenario: osrm-extract - Help, long When I run "osrm-extract --help" @@ -44,4 +44,4 @@ Feature: osrm-extract command line options: help And stdout should contain "--threads" And stdout should contain "--generate-edge-lookup" And stdout should contain "--small-component-size" - And it should exit with code 0 + And it should exit successfully diff --git a/features/options/extract/invalid.feature b/features/options/extract/invalid.feature index 169e53caa9c..936f456fb43 100644 --- a/features/options/extract/invalid.feature +++ b/features/options/extract/invalid.feature @@ -5,8 +5,8 @@ Feature: osrm-extract command line options: invalid options Given the profile "testbot" Scenario: osrm-extract - Non-existing option - When I run "osrm-extract --fly-me-to-the-moon" + When I try to run "osrm-extract --fly-me-to-the-moon" Then stdout should be empty And stderr should contain "option" And stderr should contain "fly-me-to-the-moon" - And it should exit with code 1 + And it should exit with an error diff --git a/features/options/extract/version.feature b/features/options/extract/version.feature index 0dd5f65886b..77ee46cd87c 100644 --- a/features/options/extract/version.feature +++ b/features/options/extract/version.feature @@ -12,11 +12,11 @@ Feature: osrm-extract command line options: version Then stderr should be empty And stdout should contain 1 line And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ - And it should exit with code 0 + And it should exit successfully Scenario: osrm-extract - Version, long When I run "osrm-extract --version" Then stderr should be empty And stdout should contain 1 line And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ - And it should exit with code 0 + And it should exit successfully diff --git a/features/options/routed/files.feature b/features/options/routed/files.feature index 59ce7c21336..b28c8b11a27 100644 --- a/features/options/routed/files.feature +++ b/features/options/routed/files.feature @@ -29,4 +29,4 @@ Feature: osrm-routed command line options: files And stdout should contain /^\[info\] loaded plugin: viaroute/ And stdout should contain /^\[info\] trial run/ And stdout should contain /^\[info\] shutdown completed/ - And it should exit with code 0 + And it should exit successfully diff --git a/features/options/routed/help.feature b/features/options/routed/help.feature index 8f64bd96753..e8c6430f6dd 100644 --- a/features/options/routed/help.feature +++ b/features/options/routed/help.feature @@ -21,7 +21,7 @@ Feature: osrm-routed command line options: help And stdout should contain "--max-trip-size" And stdout should contain "--max-table-size" And stdout should contain "--max-matching-size" - And it should exit with code 0 + And it should exit successfully Scenario: osrm-routed - Help, short When I run "osrm-routed -h" @@ -40,7 +40,7 @@ Feature: osrm-routed command line options: help And stdout should contain "--max-trip-size" And stdout should contain "--max-table-size" And stdout should contain "--max-matching-size" - And it should exit with code 0 + And it should exit successfully Scenario: osrm-routed - Help, long When I run "osrm-routed --help" @@ -59,4 +59,4 @@ Feature: osrm-routed command line options: help And stdout should contain "--max-table-size" And stdout should contain "--max-table-size" And stdout should contain "--max-matching-size" - And it should exit with code 0 + And it should exit successfully diff --git a/features/options/routed/invalid.feature b/features/options/routed/invalid.feature index 9c84357840c..78d28a06411 100644 --- a/features/options/routed/invalid.feature +++ b/features/options/routed/invalid.feature @@ -5,14 +5,14 @@ Feature: osrm-routed command line options: invalid options Given the profile "testbot" Scenario: osrm-routed - Non-existing option - When I run "osrm-routed --fly-me-to-the-moon" + When I try to run "osrm-routed --fly-me-to-the-moon" Then stdout should be empty And stderr should contain "unrecognised" And stderr should contain "fly-me-to-the-moon" - And it should exit with code 1 + And it should exit with an error Scenario: osrm-routed - Missing file - When I run "osrm-routed over-the-rainbow.osrm" + When I try to run "osrm-routed over-the-rainbow.osrm" Then stderr should contain "over-the-rainbow.osrm" And stderr should contain "not found" - And it should exit with code 1 + And it should exit with an error diff --git a/features/options/routed/version.feature b/features/options/routed/version.feature index b544e36e6e5..0a3cad55e0a 100644 --- a/features/options/routed/version.feature +++ b/features/options/routed/version.feature @@ -12,11 +12,11 @@ Feature: osrm-routed command line options: version Then stderr should be empty And stdout should contain 1 line And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ - And it should exit with code 0 + And it should exit successfully Scenario: osrm-routed - Version, long When I run "osrm-routed --version" Then stderr should be empty And stdout should contain 1 line And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ - And it should exit with code 0 + And it should exit successfully diff --git a/features/raster/extract.feature b/features/raster/extract.feature index 9ca0635d87e..86a7537163f 100644 --- a/features/raster/extract.feature +++ b/features/raster/extract.feature @@ -1,9 +1,5 @@ @raster @extract Feature: osrm-extract with a profile containing raster source -# expansions: -# {osm_base} => path to current input file -# {profile} => path to current profile script - Scenario: osrm-extract on a valid profile Given the profile "rasterbot" And the node map @@ -11,8 +7,15 @@ Feature: osrm-extract with a profile containing raster source And the ways | nodes | | ab | + And the raster source + """ + 0 0 0 0 + 0 0 0 250 + 0 0 250 500 + 0 0 0 250 + 0 0 0 0 + """ And the data has been saved to disk - When I run "osrm-extract {osm_base}.osm -p {profile}" - Then stderr should be empty - And stdout should contain "source loader" - And it should exit with code 0 + When I run "osrm-extract {osm_file} -p {profile_file}" + Then stdout should contain "source loader" + And it should exit successfully diff --git a/features/raster/weights.feature b/features/raster/weights.feature index ae782a72281..de4ce436665 100644 --- a/features/raster/weights.feature +++ b/features/raster/weights.feature @@ -5,11 +5,11 @@ Feature: Raster - weights Given the node locations | node | lat | lon | | a | 0.1 | 0.1 | - | b | .05 | 0.1 | + | b | 0.05 | 0.1 | | c | 0.0 | 0.1 | - | d | .05 | .03 | - | e | .05 | .066 | - | f | .075 | .066 | + | d | 0.05 | 0.03 | + | e | 0.05 | 0.066 | + | f | 0.075 | 0.066 | And the ways | nodes | highway | | ab | primary | @@ -32,8 +32,8 @@ Feature: Raster - weights Scenario: Weighting not based on raster sources Given the profile "testbot" - When I run "osrm-extract {osm_base}.osm -p {profile}" - And I run "osrm-contract {osm_base}.osm" + When I run "osrm-extract {osm_file} -p {profile_file}" + And I run "osrm-contract {processed_file}" And I route I should get | from | to | route | speed | | a | b | ab,ab | 36 km/h | @@ -44,9 +44,9 @@ Feature: Raster - weights Scenario: Weighting based on raster sources Given the profile "rasterbot" - When I run "osrm-extract {osm_base}.osm -p {profile}" + When I run "osrm-extract {osm_file} -p {profile_file}" Then stdout should contain "evaluating segment" - And I run "osrm-contract {osm_base}.osm" + And I run "osrm-contract {processed_file}" And I route I should get | from | to | route | speed | | a | b | ab,ab | 8 km/h | @@ -62,9 +62,9 @@ Feature: Raster - weights Scenario: Weighting based on raster sources Given the profile "rasterbotinterp" - When I run "osrm-extract {osm_base}.osm -p {profile}" + When I run "osrm-extract {osm_file} -p {profile_file}" Then stdout should contain "evaluating segment" - And I run "osrm-contract {osm_base}.osm" + And I run "osrm-contract {processed_file}" And I route I should get | from | to | route | speed | | a | b | ab,ab | 8 km/h | diff --git a/features/step_definitions/data.js b/features/step_definitions/data.js index 58a3fc5ff1f..acd320a72a3 100644 --- a/features/step_definitions/data.js +++ b/features/step_definitions/data.js @@ -1,20 +1,26 @@ +'use strict'; + var util = require('util'); var path = require('path'); var fs = require('fs'); var d3 = require('d3-queue'); -var OSM = require('../support/build_osm'); +var OSM = require('../lib/osm'); module.exports = function () { this.Given(/^the profile "([^"]*)"$/, (profile, callback) => { - this.setProfile(profile, callback); + this.profile = profile; + this.profileFile = path.join(this.PROFILES_PATH, this.profile + '.lua'); + callback(); }); this.Given(/^the extract extra arguments "(.*?)"$/, (args, callback) => { - this.setExtractArgs(args, callback); + this.extractArgs = this.expandOptions(args); + callback(); }); this.Given(/^the contract extra arguments "(.*?)"$/, (args, callback) => { - this.setContractArgs(args, callback); + this.contractArgs = this.expandOptions(args); + callback(); }); this.Given(/^a grid size of ([0-9.]+) meters$/, (meters, callback) => { @@ -28,9 +34,9 @@ module.exports = function () { }); this.Given(/^the shortcuts$/, (table, callback) => { - var q = d3.queue(); + let q = d3.queue(); - var addShortcut = (row, cb) => { + let addShortcut = (row, cb) => { this.shortcutsHash[row.key] = row.value; cb(); }; @@ -43,29 +49,28 @@ module.exports = function () { }); this.Given(/^the node map$/, (table, callback) => { - var q = d3.queue(); + let q = d3.queue(); - var addNode = (name, ri, ci, cb) => { + let addNode = (name, ri, ci, cb) => { if (name) { - var nodeWithID = name.match(/([a-z])\:([0-9]*)/); + let nodeWithID = name.match(/([a-z])\:([0-9]*)/); if (nodeWithID) { - var nodeName = nodeWithID[1], + let nodeName = nodeWithID[1], nodeID = nodeWithID[2]; if (this.nameNodeHash[nodeName]) throw new Error(util.format('*** duplicate node %s', name)); - lonLat = this.tableCoordToLonLat(ci, ri); + let lonLat = this.tableCoordToLonLat(ci, ri); this.addOSMNode(nodeName, lonLat[0], lonLat[1], nodeID); } else { if (name.length !== 1) throw new Error(util.format('*** node invalid name %s, must be single characters', name)); if (!name.match(/[a-z0-9]/)) throw new Error(util.format('*** invalid node name %s, must me alphanumeric', name)); - var lonLat; if (name.match(/[a-z]/)) { if (this.nameNodeHash[name]) throw new Error(util.format('*** duplicate node %s', name)); - lonLat = this.tableCoordToLonLat(ci, ri); + let lonLat = this.tableCoordToLonLat(ci, ri); this.addOSMNode(name, lonLat[0], lonLat[1], null); } else { if (this.locationHash[name]) throw new Error(util.format('*** duplicate node %s'), name); - lonLat = this.tableCoordToLonLat(ci, ri); + let lonLat = this.tableCoordToLonLat(ci, ri); this.addLocation(name, lonLat[0], lonLat[1], null); } } @@ -85,14 +90,14 @@ module.exports = function () { }); this.Given(/^the node locations$/, (table, callback) => { - var q = d3.queue(); + let q = d3.queue(); - var addNodeLocations = (row, cb) => { - var name = row.node; + let addNodeLocations = (row, cb) => { + let name = row.node; if (this.findNodeByName(name)) throw new Error(util.format('*** duplicate node %s'), name); if (name.match(/[a-z]/)) { - var id = row.id && parseInt(row.id); + let id = row.id && parseInt(row.id); this.addOSMNode(name, row.lon, row.lat, id); } else { this.addLocation(name, row.lon, row.lat); @@ -107,14 +112,14 @@ module.exports = function () { }); this.Given(/^the nodes$/, (table, callback) => { - var q = d3.queue(); + let q = d3.queue(); - var addNode = (row, cb) => { - var name = row.node, + let addNode = (row, cb) => { + let name = row.node, node = this.findNodeByName(name); delete row.node; if (!node) throw new Error(util.format('*** unknown node %s'), name); - for (var key in row) { + for (let key in row) { node.addTag(key, row[key]); } cb(); @@ -128,27 +133,27 @@ module.exports = function () { this.Given(/^the ways$/, (table, callback) => { if (this.osm_str) throw new Error('*** Map data already defined - did you pass an input file in this scenario?'); - var q = d3.queue(); + let q = d3.queue(); - var addWay = (row, cb) => { - var way = new OSM.Way(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID); + let addWay = (row, cb) => { + let way = new OSM.Way(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID); - var nodes = row.nodes; + let nodes = row.nodes; if (this.nameWayHash.nodes) throw new Error(util.format('*** duplicate way %s', nodes)); - for (var i=0; i { if (this.osm_str) throw new Error('*** Map data already defined - did you pass an input file in this scenario?'); - var q = d3.queue(); + let q = d3.queue(); - var addRelation = (row, cb) => { - var relation = new OSM.Relation(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID); + let addRelation = (row, cb) => { + let relation = new OSM.Relation(this.makeOSMId(), this.OSM_USER, this.OSM_TIMESTAMP, this.OSM_UID); - for (var key in row) { - var isNode = key.match(/^node:(.*)/), + for (let key in row) { + let isNode = key.match(/^node:(.*)/), isWay = key.match(/^way:(.*)/), isColonSeparated = key.match(/^(.*):(.*)/); if (isNode) { row[key].split(',').map(function(v) { return v.trim(); }).forEach((nodeName) => { if (nodeName.length !== 1) throw new Error(util.format('*** invalid relation node member "%s"'), nodeName); - var node = this.findNodeByName(nodeName); + let node = this.findNodeByName(nodeName); if (!node) throw new Error(util.format('*** unknown relation node member "%s"'), nodeName); relation.addMember('node', node.id, isNode[1]); }); } else if (isWay) { row[key].split(',').map(function(v) { return v.trim(); }).forEach((wayName) => { - var way = this.findWayByName(wayName); + let way = this.findWayByName(wayName); if (!way) throw new Error(util.format('*** unknown relation way member "%s"'), wayName); relation.addMember('way', way.id, isWay[1]); }); @@ -228,58 +233,61 @@ module.exports = function () { }); this.Given(/^the raster source$/, (data, callback) => { - this.updateFingerprintExtract(data); - fs.writeFile(path.resolve(this.TEST_FOLDER, 'rastersource.asc'), data, callback); + // TODO: Don't overwrite if it exists + fs.writeFile(this.rasterCacheFile, data, callback); + // we need this to pass it to the profiles + this.environment = Object.assign({OSRM_RASTER_SOURCE: this.rasterCacheFile}, this.environment); }); this.Given(/^the speed file$/, (data, callback) => { - this.updateFingerprintContract(data); - fs.writeFile(path.resolve(this.TEST_FOLDER, 'speeds.csv'), data, callback); + // TODO: Don't overwrite if it exists + fs.writeFile(this.speedsCacheFile, data, callback); }); this.Given(/^the turn penalty file$/, (data, callback) => { - this.updateFingerprintContract(data); - fs.writeFile(path.resolve(this.TEST_FOLDER, 'penalties.csv'), data, callback); + // TODO: Don't overwrite if it exists + fs.writeFile(this.penaltiesCacheFile, data, callback); }); - this.Given(/^the data has been saved to disk$/, (callback) => { - try { - this.reprocess(callback); - } catch(e) { - this.processError = e; - callback(e); + this.Given(/^the profile file(?: "([^"]*)" extended with)?$/, (profile, data, callback) => { + let text = 'package.path = "' + this.PROFILES_PATH + '/?.lua;" .. package.path\n'; + if (profile == null) { + text += data + '\n'; + } else { + text += 'local f = assert(io.open("' + this.PROFILES_PATH + '/' + profile + '.lua", "r"))\n'; + text += 'local m = assert(loadstring(f:read("*all") .. [[\n' + data + '\n]]))\n'; + text += 'f:close()\n'; + text += 'm()\n'; } + this.profileFile = this.profileCacheFile; + // TODO: Don't overwrite if it exists + fs.writeFile(this.profileCacheFile, text, callback); + }); + + this.Given(/^the data has been saved to disk$/, (callback) => { + this.writeAndLinkOSM(callback); }); this.Given(/^the data has been extracted$/, (callback) => { - this.osmData.populate(() => { - this.writeAndExtract((err) => { - if (err) this.processError = err; - callback(); - }); - }); + this.reprocess(callback); }); this.Given(/^the data has been contracted$/, (callback) => { - this.reprocess((err) => { - if (err) this.processError = err; - callback(); - }); + this.reprocess(callback); }); this.Given(/^osrm\-routed is stopped$/, (callback) => { - this.OSRMLoader.shutdown((err) => { - if (err) this.processError = err; - callback(); - }); + this.OSRMLoader.shutdown(callback); }); - this.Given(/^data is loaded directly/, () => { - this.loadMethod = 'directly'; + this.Given(/^data is loaded directly/, (callback) => { + this.osrmLoader.setLoadMethod('directly'); + callback(); }); - this.Given(/^data is loaded with datastore$/, () => { - this.loadMethod = 'datastore'; + this.Given(/^data is loaded with datastore$/, (callback) => { + this.osrmLoader.setLoadMethod('datastore'); + callback(); }); this.Given(/^the HTTP method "([^"]*)"$/, (method, callback) => { diff --git a/features/step_definitions/distance_matrix.js b/features/step_definitions/distance_matrix.js index f032f08e92e..9c2bc0adda0 100644 --- a/features/step_definitions/distance_matrix.js +++ b/features/step_definitions/distance_matrix.js @@ -53,8 +53,6 @@ module.exports = function () { }); var testRow = (row, ri, cb) => { - var ok = true; - for (var k in result[ri]) { if (this.FuzzyMatch.match(result[ri][k], row[k])) { result[ri][k] = row[k]; @@ -62,15 +60,9 @@ module.exports = function () { result[ri][k] = ''; } else { result[ri][k] = result[ri][k].toString(); - ok = false; } } - if (!ok) { - var failed = { attempt: 'distance_matrix', query: this.query, response: response }; - this.logFail(row, result[ri], [failed]); - } - result[ri][''] = row['']; cb(null, result[ri]); }; diff --git a/features/step_definitions/hooks.js b/features/step_definitions/hooks.js deleted file mode 100644 index d6ed251b46e..00000000000 --- a/features/step_definitions/hooks.js +++ /dev/null @@ -1,18 +0,0 @@ -var util = require('util'); - -module.exports = function () { - this.Before((scenario, callback) => { - this.scenarioTitle = scenario.getName(); - - this.loadMethod = this.DEFAULT_LOAD_METHOD; - this.queryParams = {}; - var d = new Date(); - this.scenarioTime = util.format('%d-%d-%dT%s:%s:%sZ', d.getFullYear(), d.getMonth()+1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()); - this.resetData(); - this.hasLoggedPreprocessInfo = false; - this.hasLoggedScenarioInfo = false; - this.setGridSize(this.DEFAULT_GRID_SIZE); - this.setOrigin(this.DEFAULT_ORIGIN); - callback(); - }); -}; diff --git a/features/step_definitions/matching.js b/features/step_definitions/matching.js index e3dc4e6e3a1..41cd0ea37ab 100644 --- a/features/step_definitions/matching.js +++ b/features/step_definitions/matching.js @@ -157,7 +157,6 @@ module.exports = function () { } else { got.matchings = encodedResult; row.matchings = extendedTarget; - this.logFail(row, got, { matching: { query: this.query, response: res } }); } cb(null, got); diff --git a/features/step_definitions/nearest.js b/features/step_definitions/nearest.js index 919cb857dc1..450dce20a7b 100644 --- a/features/step_definitions/nearest.js +++ b/features/step_definitions/nearest.js @@ -22,24 +22,16 @@ module.exports = function () { var got = { in: row.in, out: row.out }; - var ok = true; - Object.keys(row).forEach((key) => { if (key === 'out') { if (this.FuzzyMatch.matchLocation(coord, outNode)) { got[key] = row[key]; } else { row[key] = util.format('%s [%d,%d]', row[key], outNode.lat, outNode.lon); - ok = false; } } }); - if (!ok) { - var failed = { attempt: 'nearest', query: this.query, response: response }; - this.logFail(row, got, [failed]); - } - cb(null, got); } else { diff --git a/features/step_definitions/options.js b/features/step_definitions/options.js index 5dc6929925e..a6942e9fbd5 100644 --- a/features/step_definitions/options.js +++ b/features/step_definitions/options.js @@ -1,37 +1,69 @@ -var assert = require('assert'); -var fs = require('fs'); +'use strict'; + +const assert = require('assert'); +const fs = require('fs'); module.exports = function () { - this.When(/^I run "osrm\-routed\s?(.*?)"$/, { timeout: this.TIMEOUT }, (options, callback) => { - this.runBin('osrm-routed', options, () => { - callback(); + this.resetOptionsOutput = () => { + this.stdout = null; + this.stderr = null; + this.exitCode = null; + this.termSignal = null; + }; + + this.runAndSafeOutput = (binary, options, callback) => { + this.runBin(binary, this.expandOptions(options), this.environment, (err, stdout, stderr) => { + this.stdout = stdout; + this.stderr = stderr; + this.exitCode = err && err.code || 0; + this.termSignal = err && err.signal || ''; + callback(err); }); + }; + + this.When(/^I run "osrm\-routed\s?(.*?)"$/, { timeout: this.TIMEOUT }, (options, callback) => { + this.runAndSafeOutput('osrm-routed', options, callback); }); this.When(/^I run "osrm\-extract\s?(.*?)"$/, (options, callback) => { - this.runBin('osrm-extract', options, () => { - callback(); + const stamp = this.processedCacheFile + '.extract'; + this.runAndSafeOutput('osrm-extract', options, (err) => { + if (err) return callback(err); + fs.writeFile(stamp, 'ok', callback); }); }); this.When(/^I run "osrm\-contract\s?(.*?)"$/, (options, callback) => { - this.runBin('osrm-contract', options, () => { - callback(); + const stamp = this.processedCacheFile + '.contract'; + this.runAndSafeOutput('osrm-contract', options, (err) => { + if (err) return callback(err); + fs.writeFile(stamp, 'ok', callback); }); }); + this.When(/^I try to run "osrm\-routed\s?(.*?)"$/, (options, callback) => { + this.runAndSafeOutput('osrm-routed', options, () => { callback(); }); + }); + + this.When(/^I try to run "osrm\-extract\s?(.*?)"$/, (options, callback) => { + this.runAndSafeOutput('osrm-extract', options, () => { callback(); }); + }); + + this.When(/^I try to run "osrm\-contract\s?(.*?)"$/, (options, callback) => { + this.runAndSafeOutput('osrm-contract', options, () => { callback(); }); + }); + this.When(/^I run "osrm\-datastore\s?(.*?)"$/, (options, callback) => { - this.runBin('osrm-datastore', options, () => { - callback(); - }); + this.runAndSafeOutput('osrm-datastore', options, callback); }); - this.Then(/^it should exit with code (\d+)$/, (code) => { - assert.equal(this.exitCode, parseInt(code)); + this.Then(/^it should exit successfully$/, () => { + assert.equal(this.exitCode, 0); + assert.equal(this.termSignal, ''); }); - this.Then(/^it should exit with code not (\d+)$/, (code) => { - assert.notEqual(this.exitCode, parseInt(code)); + this.Then(/^it should exit with an error$/, () => { + assert.ok(this.exitCode !== 0 || this.termSignal); }); this.Then(/^stdout should contain "(.*?)"$/, (str) => { @@ -43,12 +75,12 @@ module.exports = function () { }); this.Then(/^stdout should contain \/(.*)\/$/, (regexStr) => { - var re = new RegExp(regexStr); + const re = new RegExp(regexStr); assert.ok(this.stdout.match(re)); }); this.Then(/^stderr should contain \/(.*)\/$/, (regexStr) => { - var re = new RegExp(regexStr); + const re = new RegExp(regexStr); assert.ok(this.stdout.match(re)); }); @@ -64,8 +96,12 @@ module.exports = function () { assert.equal(this.stdout.split('\n').length - 1, parseInt(lines)); }); + this.Then(/^stderr should contain (\d+) lines?$/, (lines) => { + assert.equal(this.stderr.split('\n').length - 1, parseInt(lines)); + }); + this.Then(/^datasource names should contain "(.+)"$/, (expectedData) => { - var actualData = fs.readFileSync(this.osmData.extractedFile + '.osrm.datasource_names', {encoding:'UTF-8'}).trim().split('\n').join(','); + const actualData = fs.readFileSync(this.processedCacheFile + '.datasource_names', {encoding:'UTF-8'}).trim().split('\n').join(','); assert.equal(actualData, expectedData); }); diff --git a/features/step_definitions/requests.js b/features/step_definitions/requests.js index cb118845744..36eef830bc9 100644 --- a/features/step_definitions/requests.js +++ b/features/step_definitions/requests.js @@ -51,7 +51,7 @@ module.exports = function () { }); this.Then(/^"([^"]*)" should return code (\d+)$/, (binary, code) => { - assert.ok(this.processError instanceof this.OSRMError); + assert.ok(this.processError instanceof Error); assert.equal(this.processError.process, binary); assert.equal(parseInt(this.processError.code), parseInt(code)); }); diff --git a/features/step_definitions/routability.js b/features/step_definitions/routability.js index c4190c547c5..ea2a45e656b 100644 --- a/features/step_definitions/routability.js +++ b/features/step_definitions/routability.js @@ -13,7 +13,7 @@ module.exports = function () { } this.reprocessAndLoadData((e) => { - if (e) callback(e); + if (e) return callback(e); var testRow = (row, i, cb) => { var outputRow = row; @@ -41,10 +41,6 @@ module.exports = function () { } }); - if (outputRow != row) { - this.logFail(row, outputRow, result); - } - cb(null, outputRow); }); }; @@ -116,7 +112,7 @@ module.exports = function () { sq.defer(parseRes, key); }); - sq.awaitAll(() => { cb(null, result); }); + sq.awaitAll((err) => { cb(err, result); }); }); }; }; diff --git a/features/step_definitions/trip.js b/features/step_definitions/trip.js index fe4ff892bb2..8de1fe7ebb2 100644 --- a/features/step_definitions/trip.js +++ b/features/step_definitions/trip.js @@ -1,6 +1,10 @@ var util = require('util'); module.exports = function () { + function add(a, b) { + return a + b; + } + this.When(/^I plan a trip I should get$/, (table, callback) => { var got; @@ -43,6 +47,7 @@ module.exports = function () { } var subTrips; + var trip_durations; if (res.statusCode === 200) { if (headers.has('trips')) { subTrips = json.trips.filter(t => !!t).map(t => t.legs).map(tl => Array.prototype.concat.apply([], tl.map((sl, i) => { @@ -52,6 +57,12 @@ module.exports = function () { return toAdd; }))); } + if(headers.has('durations')) { + var all_durations = json.trips.filter(t => !!t).map(t => t.legs).map(tl => Array.prototype.concat.apply([], tl.map(sl => { + return sl.duration; + }))); + trip_durations = all_durations.map( a => a.reduce(add, 0)); + } } var ok = true, @@ -62,7 +73,6 @@ module.exports = function () { if (si >= subTrips.length) { ok = false; } else { - ok = false; // TODO: Check all rotations of the round trip for (var ni=0; ni { + this.getOSRMHash((err, osrmHash) => { + if (err) return callback(err); + this.osrmHash = osrmHash; + callback(); + }); + }; + + // computes all paths for every feature + this.setupFeatures = (features, callback) => { + this.featureIDs = {}; + this.featureCacheDirectories = {}; + this.featureProcessedCacheDirectories = {}; + let queue = d3.queue(); + + function initializeFeature(feature, callback) { + let uri = feature.getUri(); + + // setup cache for feature data + hash.hashOfFile(uri, (err, hash) => { + if (err) return callback(err); + + // shorten uri to be realtive to 'features/' + let featurePath = path.relative(path.resolve('./features'), uri); + // bicycle/bollards/{HASH}/ + let featureID = path.join(featurePath, hash); + let featureCacheDirectory = this.getFeatureCacheDirectory(featureID); + let featureProcessedCacheDirectory = this.getFeatureProcessedCacheDirectory(featureCacheDirectory, this.osrmHash); + this.featureIDs[uri] = featureID; + this.featureCacheDirectories[uri] = featureCacheDirectory; + this.featureProcessedCacheDirectories[uri] = featureProcessedCacheDirectory; + + d3.queue(1) + .defer(mkdirp, featureProcessedCacheDirectory) + .defer(this.cleanupFeatureCache.bind(this), featureCacheDirectory, hash) + .defer(this.cleanupProcessedFeatureCache.bind(this), featureProcessedCacheDirectory, this.osrmHash) + .awaitAll(callback); + }); + } + + for (let i = 0; i < features.length; ++i) { + queue.defer(initializeFeature.bind(this), features[i]); + } + queue.awaitAll(callback); + }; + + this.cleanupProcessedFeatureCache = (directory, osrmHash, callback) => { + let parentPath = path.resolve(path.join(directory, '..')); + fs.readdir(parentPath, (err, files) => { + let q = d3.queue(); + function runStats(path, callback) { + fs.stat(path, (err, stat) => { + if (err) return callback(err); + callback(null, {file: path, stat: stat}); + }); + } + files.map(f => { q.defer(runStats, path.join(parentPath, f)); }); + q.awaitAll((err, results) => { + if (err) return callback(err); + let q = d3.queue(); + results.forEach(r => { + if (r.stat.isDirectory() && r.file.search(osrmHash) < 0) { + q.defer(rimraf, r.file); + } + }); + q.awaitAll(callback); + }); + }); + }; + + this.cleanupFeatureCache = (directory, featureHash, callback) => { + let parentPath = path.resolve(path.join(directory, '..')); + fs.readdir(parentPath, (err, files) => { + let q = d3.queue(); + files.filter(name => { return name !== featureHash;}) + .map((f) => { q.defer(rimraf, path.join(parentPath, f)); }); + q.awaitAll(callback); + }); + }; + + this.setupFeatureCache = (feature) => { + let uri = feature.getUri(); + this.featureID = this.featureIDs[uri]; + this.featureCacheDirectory = this.featureCacheDirectories[uri]; + this.featureProcessedCacheDirectory = this.featureProcessedCacheDirectories[uri]; + }; + + this.setupScenarioCache = (scenarioID) => { + this.scenarioCacheFile = this.getScenarioCacheFile(this.featureCacheDirectory, scenarioID); + this.processedCacheFile = this.getProcessedCacheFile(this.featureProcessedCacheDirectory, scenarioID); + this.inputCacheFile = this.getInputCacheFile(this.featureProcessedCacheDirectory, scenarioID); + this.rasterCacheFile = this.getRasterCacheFile(this.featureProcessedCacheDirectory, scenarioID); + this.speedsCacheFile = this.getSpeedsCacheFile(this.featureProcessedCacheDirectory, scenarioID); + this.penaltiesCacheFile = this.getPenaltiesCacheFile(this.featureProcessedCacheDirectory, scenarioID); + this.profileCacheFile = this.getProfileCacheFile(this.featureProcessedCacheDirectory, scenarioID); + }; + + // returns a hash of all OSRM code side dependencies + this.getOSRMHash = (callback) => { + let dependencies = [ + this.OSRM_EXTRACT_PATH, + this.OSRM_CONTRACT_PATH, + this.LIB_OSRM_EXTRACT_PATH, + this.LIB_OSRM_CONTRACT_PATH + ]; + + var addLuaFiles = (directory, callback) => { + fs.readdir(path.normalize(directory), (err, files) => { + if (err) return callback(err); + + var luaFiles = files.filter(f => !!f.match(/\.lua$/)).map(f => path.normalize(directory + '/' + f)); + Array.prototype.push.apply(dependencies, luaFiles); + + callback(); + }); + }; + + // Note: we need a serialized queue here to ensure that the order of the files + // passed is stable. Otherwise the hash will not be stable + d3.queue(1) + .defer(addLuaFiles, this.PROFILES_PATH) + .defer(addLuaFiles, this.PROFILES_PATH + '/lib') + .awaitAll(hash.hashOfFiles.bind(hash, dependencies, callback)); + }; + + // test/cache/bicycle/bollards/{HASH}/ + this.getFeatureCacheDirectory = (featureID) => { + return path.join(this.CACHE_PATH, featureID); + }; + + // converts the scenario titles in file prefixes + this.getScenarioID = (scenario) => { + let name = scenario.getName().toLowerCase().replace(/[\/\-'=,\(\)]/g, '').replace(/\s/g, '_').replace(/__/g, '_').replace(/\.\./g, '.'); + return util.format('%d_%s', scenario.getLine(), name); + }; + + // test/cache/{feature_path}/{feature_hash}/{scenario}_raster.asc + this.getRasterCacheFile = (featureCacheDirectory, scenarioID) => { + return path.join(featureCacheDirectory, scenarioID) + '_raster.asc'; + }; + + // test/cache/{feature_path}/{feature_hash}/{scenario}_speeds.csv + this.getSpeedsCacheFile = (featureCacheDirectory, scenarioID) => { + return path.join(featureCacheDirectory, scenarioID) + '_speeds.csv'; + }; + + // test/cache/{feature_path}/{feature_hash}/{scenario}_penalties.csv + this.getPenaltiesCacheFile = (featureCacheDirectory, scenarioID) => { + return path.join(featureCacheDirectory, scenarioID) + '_penalties.csv'; + }; + + // test/cache/{feature_path}/{feature_hash}/{scenario}_profile.lua + this.getProfileCacheFile = (featureCacheDirectory, scenarioID) => { + return path.join(featureCacheDirectory, scenarioID) + '_profile.lua'; + }; + + // test/cache/{feature_path}/{feature_hash}/{scenario}.osm + this.getScenarioCacheFile = (featureCacheDirectory, scenarioID) => { + return path.join(featureCacheDirectory, scenarioID) + '.osm'; + }; + + // test/cache/{feature_path}/{feature_hash}/{osrm_hash}/ + this.getFeatureProcessedCacheDirectory = (featureCacheDirectory, osrmHash) => { + return path.join(featureCacheDirectory, osrmHash); + }; + + // test/cache/{feature_path}/{feature_hash}/{osrm_hash}/{scenario}.osrm + this.getProcessedCacheFile = (featureProcessedCacheDirectory, scenarioID) => { + return path.join(featureProcessedCacheDirectory, scenarioID) + '.osrm'; + }; + + // test/cache/{feature_path}/{feature_hash}/{osrm_hash}/{scenario}.osm + this.getInputCacheFile = (featureProcessedCacheDirectory, scenarioID) => { + return path.join(featureProcessedCacheDirectory, scenarioID) + '.osm'; + }; + + + return this; +}; diff --git a/features/support/config.js b/features/support/config.js deleted file mode 100644 index 769755f4f3b..00000000000 --- a/features/support/config.js +++ /dev/null @@ -1,127 +0,0 @@ -var fs = require('fs'); -var path = require('path'); -var util = require('util'); -var d3 = require('d3-queue'); -var OSM = require('./build_osm'); -var classes = require('./data_classes'); - -module.exports = function () { - this.initializeOptions = (callback) => { - this.profile = this.profile || this.DEFAULT_SPEEDPROFILE; - - this.OSMDB = this.OSMDB || new OSM.DB(); - - this.nameNodeHash = this.nameNodeHash || {}; - - this.locationHash = this.locationHash || {}; - - this.nameWayHash = this.nameWayHash || {}; - - this.osmData = new classes.osmData(this); - - this.OSRMLoader = this._OSRMLoader(); - - this.PREPROCESS_LOG_FILE = path.resolve(this.TEST_FOLDER, 'preprocessing.log'); - - this.LOG_FILE = path.resolve(this.TEST_FOLDER, 'fail.log'); - - this.HOST = 'http://127.0.0.1:' + this.OSRM_PORT; - - this.DESTINATION_REACHED = 15; // OSRM instruction code - - this.shortcutsHash = this.shortcutsHash || {}; - - var hashLuaLib = (cb) => { - fs.readdir(path.normalize(this.PROFILES_PATH + '/lib/'), (err, files) => { - if (err) cb(err); - var luaFiles = files.filter(f => !!f.match(/\.lua$/)).map(f => path.normalize(this.PROFILES_PATH + '/lib/' + f)); - this.hashOfFiles(luaFiles, hash => { - this.luaLibHash = hash; - cb(); - }); - }); - }; - - var hashProfile = (cb) => { - this.hashProfile((hash) => { - this.profileHash = hash; - cb(); - }); - }; - - var hashExtract = (cb) => { - var files = [ util.format('%s/osrm-extract%s', this.BIN_PATH, this.EXE), - util.format('%s/libosrm_extract%s', this.BIN_PATH, this.LIB) ]; - this.hashOfFiles(files, (hash) => { - this.binExtractHash = hash; - cb(); - }); - }; - - var hashContract = (cb) => { - var files = [ util.format('%s/osrm-contract%s', this.BIN_PATH, this.EXE), - util.format('%s/libosrm_contract%s', this.BIN_PATH, this.LIB) ]; - this.hashOfFiles(files, (hash) => { - this.binContractHash = hash; - cb(); - }); - }; - - var hashRouted = (cb) => { - var files = [ util.format('%s/osrm-routed%s', this.BIN_PATH, this.EXE), - util.format('%s/libosrm%s', this.BIN_PATH, this.LIB) ]; - this.hashOfFiles(files, (hash) => { - this.binRoutedHash = hash; - this.fingerprintRoute = this.hashString(this.binRoutedHash); - cb(); - }); - }; - - d3.queue() - .defer(hashLuaLib) - .defer(hashProfile) - .defer(hashExtract) - .defer(hashContract) - .defer(hashRouted) - .awaitAll(() => { - this.AfterConfiguration(() => { - callback(); - }); - }); - }; - - this.updateFingerprintExtract = (str) => { - this.fingerprintExtract = this.hashString([this.fingerprintExtract, str].join('-')); - }; - - this.updateFingerprintContract = (str) => { - this.fingerprintContract = this.hashString([this.fingerprintContract, str].join('-')); - }; - - this.setProfile = (profile, cb) => { - var lastProfile = this.profile; - if (profile !== lastProfile) { - this.profile = profile; - this.hashProfile((hash) => { - this.profileHash = hash; - this.updateFingerprintExtract(this.profileHash); - cb(); - }); - } else { - this.updateFingerprintExtract(this.profileHash); - cb(); - } - }; - - this.setExtractArgs = (args, callback) => { - this.extractArgs = args; - this.updateFingerprintExtract(args); - callback(); - }; - - this.setContractArgs = (args, callback) => { - this.contractArgs = args; - this.updateFingerprintContract(args); - callback(); - }; -}; diff --git a/features/support/data.js b/features/support/data.js index 48e5d612c1b..53dc779322b 100644 --- a/features/support/data.js +++ b/features/support/data.js @@ -1,11 +1,14 @@ -var fs = require('fs'); -var path = require('path'); -var util = require('util'); -var exec = require('child_process').exec; -var d3 = require('d3-queue'); +'use strict'; -var OSM = require('./build_osm'); -var classes = require('./data_classes'); +const fs = require('fs'); +const util = require('util'); +const d3 = require('d3-queue'); + +const OSM = require('../lib/osm'); +const classes = require('./data_classes'); +const tableDiff = require('../lib/table_diff'); +const ensureDecimal = require('../lib/utils').ensureDecimal; +const errorReason = require('../lib/utils').errorReason; module.exports = function () { this.setGridSize = (meters) => { @@ -94,13 +97,8 @@ module.exports = function () { q.awaitAll(callback); }; - this.ensureDecimal = (i) => { - if (parseInt(i) === i) return i.toFixed(1); - else return i; - }; - this.tableCoordToLonLat = (ci, ri) => { - return [this.origin[0] + ci * this.zoom, this.origin[1] - ri * this.zoom].map(this.ensureDecimal); + return [this.origin[0] + ci * this.zoom, this.origin[1] - ri * this.zoom].map(ensureDecimal); }; this.addOSMNode = (name, lon, lat, id) => { @@ -132,10 +130,6 @@ module.exports = function () { return this.nameWayHash[s.toString()] || this.nameWayHash[s.toString().split('').reverse().join('')]; }; - this.resetData = () => { - this.resetOSM(); - }; - this.makeOSMId = () => { this.osmID = this.osmID + 1; return this.osmID; @@ -143,206 +137,93 @@ module.exports = function () { this.resetOSM = () => { this.OSMDB.clear(); - this.osmData.reset(); this.nameNodeHash = {}; this.locationHash = {}; + this.shortcutsHash = {}; this.nameWayHash = {}; this.osmID = 0; }; this.writeOSM = (callback) => { - fs.exists(this.DATA_FOLDER, (exists) => { - var mkDirFn = exists ? (cb) => { cb(); } : fs.mkdir.bind(fs.mkdir, this.DATA_FOLDER); - mkDirFn((err) => { - if (err) return callback(err); - var osmPath = path.resolve(this.DATA_FOLDER, util.format('%s.osm', this.osmData.osmFile)); - fs.exists(osmPath, (exists) => { - if (!exists) fs.writeFile(osmPath, this.osmData.str, callback); - else callback(); + fs.exists(this.scenarioCacheFile, (exists) => { + if (exists) callback(); + else { + this.OSMDB.toXML((xml) => { + fs.writeFile(this.scenarioCacheFile, xml, callback); }); - }); - }); - }; - - this.isExtracted = (callback) => { - fs.exists(util.format('%s.osrm', this.osmData.extractedFile), (core) => { - if (!core) return callback(false); - fs.exists(util.format('%s.osrm.names', this.osmData.extractedFile), (names) => { - if (!names) return callback(false); - fs.exists(util.format('%s.osrm.restrictions', this.osmData.extractedFile), (restrictions) => { - return callback(restrictions); - }); - }); + } }); }; - this.isContracted = (callback) => { - fs.exists(util.format('%s.osrm.hsgr', this.osmData.contractedFile), callback); - }; - - this.writeTimestamp = (callback) => { - fs.writeFile(util.format('%s.osrm.timestamp', this.osmData.contractedFile), this.OSM_TIMESTAMP, callback); - }; - - this.writeInputData = (callback) => { - this.writeOSM((err) => { - if (err) return callback(err); - this.writeTimestamp(callback); + this.linkOSM = (callback) => { + fs.exists(this.inputCacheFile, (exists) => { + if (exists) callback(); + else { + fs.link(this.scenarioCacheFile, this.inputCacheFile, callback); + } }); }; - this.extractData = (callback) => { - this.logPreprocessInfo(); - this.log(util.format('== Extracting %s.osm...', this.osmData.osmFile), 'preprocess'); - var cmd = util.format('%s/osrm-extract %s.osm %s --profile %s/%s.lua >>%s 2>&1', - this.BIN_PATH, this.osmData.osmFile, this.extractArgs || '', this.PROFILES_PATH, this.profile, this.PREPROCESS_LOG_FILE); - this.log(cmd); - process.chdir(this.TEST_FOLDER); - exec(cmd, (err) => { - if (err) { - this.log(util.format('*** Exited with code %d', err.code), 'preprocess'); - process.chdir('../'); - return callback(this.ExtractError(err.code, util.format('osrm-extract exited with code %d', err.code))); - } - - var q = d3.queue(); - - var rename = (file, cb) => { - this.log(util.format('Renaming %s.%s to %s.%s', this.osmData.osmFile, file, this.osmData.extractedFile, file), 'preprocess'); - fs.rename([this.osmData.osmFile, file].join('.'), [this.osmData.extractedFile, file].join('.'), (err) => { - if (err) return cb(this.FileError(null, 'failed to rename data file after extracting')); - cb(); - }); - }; - - var renameIfExists = (file, cb) => { - fs.stat([this.osmData.osmFile, file].join('.'), (doesNotExistErr, exists) => { - if (exists) rename(file, cb); - else cb(); - }); - }; - - ['osrm', 'osrm.ebg', 'osrm.edges', 'osrm.enw', 'osrm.fileIndex', 'osrm.geometry', 'osrm.icd', - 'osrm.names', 'osrm.nodes', 'osrm.properties', 'osrm.ramIndex', 'osrm.restrictions', 'osrm.tld', 'osrm.tls'].forEach(file => { - q.defer(rename, file); - }); - - ['osrm.edge_penalties', 'osrm.edge_segment_lookup'].forEach(file => { - q.defer(renameIfExists, file); - }); + this.extractData = (p, callback) => { + let stamp = p.processedCacheFile + '.extract'; + fs.exists(stamp, (exists) => { + if (exists) return callback(); - q.awaitAll((err) => { - this.log('Finished extracting ' + this.osmData.extractedFile, 'preprocess'); - process.chdir('../'); - callback(err); + this.runBin('osrm-extract', util.format('%s --profile %s %s', p.extractArgs, p.profileFile, p.inputCacheFile), p.environment, (err) => { + if (err) { + return callback(new Error(util.format('osrm-extract %s: %s', errorReason(err), err.cmd))); + } + fs.writeFile(stamp, 'ok', callback); }); }); }; - this.contractData = (callback) => { - this.logPreprocessInfo(); - this.log(util.format('== Contracting %s.osm...', this.osmData.extractedFile), 'preprocess'); - var cmd = util.format('%s/osrm-contract %s %s.osrm >>%s 2>&1', - this.BIN_PATH, this.contractArgs || '', this.osmData.extractedFile, this.PREPROCESS_LOG_FILE); - this.log(cmd); - process.chdir(this.TEST_FOLDER); - exec(cmd, (err) => { - if (err) { - this.log(util.format('*** Exited with code %d', err.code), 'preprocess'); - process.chdir('../'); - return callback(this.ContractError(err.code, util.format('osrm-contract exited with code %d', err.code))); - } - - var rename = (file, cb) => { - this.log(util.format('Renaming %s.%s to %s.%s', this.osmData.extractedFile, file, this.osmData.contractedFile, file), 'preprocess'); - fs.rename([this.osmData.extractedFile, file].join('.'), [this.osmData.contractedFile, file].join('.'), (err) => { - if (err) return cb(this.FileError(null, 'failed to rename data file after contracting.')); - cb(); - }); - }; - - var renameIfExists = (file, cb) => { - fs.stat([this.osmData.extractedFile, file].join('.'), (doesNotExistErr, exists) => { - if (exists) rename(file, cb); - else cb(); - }); - }; - - var copy = (file, cb) => { - this.log(util.format('Copying %s.%s to %s.%s', this.osmData.extractedFile, file, this.osmData.contractedFile, file), 'preprocess'); - fs.createReadStream([this.osmData.extractedFile, file].join('.')) - .pipe(fs.createWriteStream([this.osmData.contractedFile, file].join('.')) - .on('finish', cb) - ) - .on('error', () => { - return cb(this.FileError(null, 'failed to copy data after contracting.')); - }); - }; - - var q = d3.queue(); - - ['osrm', 'osrm.core', 'osrm.datasource_indexes', 'osrm.datasource_names', 'osrm.ebg','osrm.edges', - 'osrm.enw', 'osrm.fileIndex', 'osrm.geometry', 'osrm.hsgr', 'osrm.icd','osrm.level', 'osrm.names', - 'osrm.nodes', 'osrm.properties', 'osrm.ramIndex', 'osrm.restrictions', 'osrm.tld', 'osrm.tls'].forEach((file) => { - q.defer(rename, file); - }); - - ['osrm.edge_penalties', 'osrm.edge_segment_lookup'].forEach(file => { - q.defer(renameIfExists, file); - }); - - [].forEach((file) => { - q.defer(copy, file); - }); + this.contractData = (p, callback) => { + let stamp = p.processedCacheFile + '.contract'; + fs.exists(stamp, (exists) => { + if (exists) return callback(); - q.awaitAll((err) => { - this.log('Finished contracting ' + this.osmData.contractedFile, 'preprocess'); - process.chdir('../'); - callback(err); + this.runBin('osrm-contract', util.format('%s %s', p.contractArgs, p.processedCacheFile), p.environment, (err) => { + if (err) { + return callback(new Error(util.format('osrm-contract %s: %s', errorReason(err), err))); + } + fs.writeFile(stamp, 'ok', callback); }); }); }; - var noop = (cb) => cb(); + this.extractAndContract = (callback) => { + // a shallow copy of scenario parameters to avoid data inconsistency + // if a cucumber timeout occurs during deferred jobs + let p = {extractArgs: this.extractArgs, contractArgs: this.contractArgs, + profileFile: this.profileFile, inputCacheFile: this.inputCacheFile, + processedCacheFile: this.processedCacheFile, environment: this.environment}; + let queue = d3.queue(1); + queue.defer(this.extractData.bind(this), p); + queue.defer(this.contractData.bind(this), p); + queue.awaitAll(callback); + }; - this.reprocess = (callback) => { - this.osmData.populate(() => { - this.isContracted((isContracted) => { - if (!isContracted) { - this.writeAndExtract((e) => { - if (e) return callback(e); - this.contractData((e) => { - if (e) return callback(e); - this.logPreprocessDone(); - callback(); - }); - }); - } else { - this.log('Already contracted ' + this.osmData.contractedFile, 'preprocess'); - callback(); - } - }); - }); + this.writeAndLinkOSM = (callback) => { + let queue = d3.queue(1); + queue.defer(this.writeOSM.bind(this)); + queue.defer(this.linkOSM.bind(this)); + queue.awaitAll(callback); }; - this.writeAndExtract = (callback) => { - this.writeInputData((e) => { - if (e) return callback(e); - this.isExtracted((isExtracted) => { - var extractFn = isExtracted ? noop : this.extractData; - if (isExtracted) this.log('Already extracted ' + this.osmData.extractedFile, 'preprocess'); - extractFn((e) => { - callback(e); - }); - }); - }); + this.reprocess = (callback) => { + let queue = d3.queue(1); + queue.defer(this.writeAndLinkOSM.bind(this)); + queue.defer(this.extractAndContract.bind(this)); + queue.awaitAll(callback); }; this.reprocessAndLoadData = (callback) => { - this.reprocess((e) => { - if (e) return callback(e); - this.OSRMLoader.load(util.format('%s.osrm', this.osmData.contractedFile), callback); - }); + let queue = d3.queue(1); + queue.defer(this.writeAndLinkOSM.bind(this)); + queue.defer(this.extractAndContract.bind(this)); + queue.defer(this.osrmLoader.load.bind(this.osrmLoader), this.processedCacheFile); + queue.awaitAll(callback); }; this.processRowsAndDiff = (table, fn, callback) => { @@ -352,7 +233,9 @@ module.exports = function () { q.awaitAll((err, actual) => { if (err) return callback(err); - this.diffTables(table, actual, {}, callback); + let diff = tableDiff(table, actual); + if (diff) callback(new Error(diff)); + else callback(); }); }; }; diff --git a/features/support/data_classes.js b/features/support/data_classes.js index 391bb1c6de3..a17141e5e7d 100644 --- a/features/support/data_classes.js +++ b/features/support/data_classes.js @@ -1,7 +1,6 @@ 'use strict'; -var util = require('util'); -var path = require('path'); +const util = require('util'); module.exports = { Location: class { @@ -11,43 +10,6 @@ module.exports = { } }, - osmData: class { - constructor (scope) { - this.scope = scope; - this.str = null; - this.hash = null; - this.fingerprintOSM = null; - this.osmFile = null; - this.extractedFile = null; - this.contractedFile = null; - } - - populate (callback) { - this.scope.OSMDB.toXML((str) => { - this.str = str; - - this.hash = this.scope.hashString(str); - this.fingerprintOSM = this.scope.hashString(this.hash); - - this.osmFile = path.resolve(this.scope.DATA_FOLDER, this.fingerprintOSM); - - this.extractedFile = path.resolve([this.osmFile, this.scope.fingerprintExtract].join('_')); - this.contractedFile = path.resolve([this.osmFile, this.scope.fingerprintExtract, this.scope.fingerprintContract].join('_')); - - callback(); - }); - } - - reset () { - this.str = null; - this.hash = null; - this.fingerprintOSM = null; - this.osmFile = null; - this.extractedFile = null; - this.contractedFile = null; - } - }, - FuzzyMatch: class { match (got, want) { var matchPercent = want.match(/(.*)\s+~(.+)%$/), diff --git a/features/support/env.js b/features/support/env.js index c99d5440312..5df8c664481 100644 --- a/features/support/env.js +++ b/features/support/env.js @@ -1,32 +1,46 @@ -var path = require('path'); -var util = require('util'); -var fs = require('fs'); -var exec = require('child_process').exec; -var d3 = require('d3-queue'); +'use strict'; +const path = require('path'); +const util = require('util'); +const fs = require('fs'); +const d3 = require('d3-queue'); +const child_process = require('child_process'); +const tryConnect = require('../lib/try_connect'); + +// Sets up all constants that are valid for all features module.exports = function () { this.initializeEnv = (callback) => { - this.OSRM_PORT = process.env.OSRM_PORT && parseInt(process.env.OSRM_PORT) || 5000; this.TIMEOUT = process.env.CUCUMBER_TIMEOUT && parseInt(process.env.CUCUMBER_TIMEOUT) || 5000; + // set cucumber default timeout this.setDefaultTimeout(this.TIMEOUT); - this.ROOT_FOLDER = process.cwd(); + this.ROOT_PATH = process.cwd(); + + this.TEST_PATH = path.resolve(this.ROOT_PATH, 'test'); + this.CACHE_PATH = path.resolve(this.TEST_PATH, 'cache'); + this.LOGS_PATH = path.resolve(this.TEST_PATH, 'logs'); + + this.PROFILES_PATH = path.resolve(this.ROOT_PATH, 'profiles'); + this.FIXTURES_PATH = path.resolve(this.ROOT_PATH, 'unit_tests/fixtures'); + this.BIN_PATH = process.env.OSRM_BUILD_DIR && process.env.OSRM_BUILD_DIR || path.resolve(this.ROOT_PATH, 'build'); + var stxxl_config = path.resolve(this.ROOT_PATH, 'test/.stxxl'); + if (!fs.existsSync(stxxl_config)) { + return callback(new Error('*** '+stxxl_config+ 'does not exist')); + } + + this.DEFAULT_ENVIRONMENT = Object.assign({STXXLCFG: stxxl_config}, process.env); + this.DEFAULT_PROFILE = 'bicycle'; + this.DEFAULT_INPUT_FORMAT = 'osm'; + this.DEFAULT_LOAD_METHOD = 'datastore'; + this.DEFAULT_ORIGIN = [1,1]; this.OSM_USER = 'osrm'; this.OSM_GENERATOR = 'osrm-test'; this.OSM_UID = 1; - this.TEST_FOLDER = path.resolve(this.ROOT_FOLDER, 'test'); - this.DATA_FOLDER = path.resolve(this.TEST_FOLDER, 'cache'); this.OSM_TIMESTAMP = '2000-01-01T00:00:00Z'; - this.DEFAULT_SPEEDPROFILE = 'bicycle'; this.WAY_SPACING = 100; - this.DEFAULT_GRID_SIZE = 100; // meters - this.PROFILES_PATH = path.resolve(this.ROOT_FOLDER, 'profiles'); - this.FIXTURES_PATH = path.resolve(this.ROOT_FOLDER, 'unit_tests/fixtures'); - this.BIN_PATH = process.env.OSRM_BUILD_DIR && process.env.OSRM_BUILD_DIR || path.resolve(this.ROOT_FOLDER, 'build'); - this.DEFAULT_INPUT_FORMAT = 'osm'; - this.DEFAULT_ORIGIN = [1,1]; - this.DEFAULT_LOAD_METHOD = 'datastore'; - this.OSRM_ROUTED_LOG_FILE = path.resolve(this.TEST_FOLDER, 'osrm-routed.log'); - this.ERROR_LOG_FILE = path.resolve(this.TEST_FOLDER, 'error.log'); + this.DEFAULT_GRID_SIZE = 100; // meters + + this.OSRM_PORT = process.env.OSRM_PORT && parseInt(process.env.OSRM_PORT) || 5000; + this.HOST = 'http://127.0.0.1:' + this.OSRM_PORT; // TODO make sure this works on win if (process.platform.match(/indows.*/)) { @@ -37,36 +51,67 @@ module.exports = function () { } else { this.TERMSIGNAL = 'SIGTERM'; this.EXE = ''; - this.LIB = '.so'; + + // heuristically detect .so/.a suffix + this.LIB = null; + + try { + const dot_a = util.format('%s/libosrm%s', this.BIN_PATH, '.a'); + fs.accessSync(dot_a, fs.F_OK); + this.LIB = '.a'; + } catch(e) { /*nop*/ } + + try { + const dot_so = util.format('%s/libosrm%s', this.BIN_PATH, '.so'); + fs.accessSync(dot_so, fs.F_OK); + this.LIB = '.so'; + } catch(e) { /*nop*/ } + + if (!this.LIB) { + throw new Error('*** Unable to detect dynamic or static libosrm libraries'); + } + this.QQ = ''; } + this.OSRM_EXTRACT_PATH = path.resolve(util.format('%s/%s%s', this.BIN_PATH, 'osrm-extract', this.EXE)); + this.OSRM_CONTRACT_PATH = path.resolve(util.format('%s/%s%s', this.BIN_PATH, 'osrm-contract', this.EXE)); + this.OSRM_ROUTED_PATH = path.resolve(util.format('%s/%s%s', this.BIN_PATH, 'osrm-routed', this.EXE)); + this.LIB_OSRM_EXTRACT_PATH = util.format('%s/libosrm_extract%s', this.BIN_PATH, this.LIB), + this.LIB_OSRM_CONTRACT_PATH = util.format('%s/libosrm_contract%s', this.BIN_PATH, this.LIB), + this.LIB_OSRM_PATH = util.format('%s/libosrm%s', this.BIN_PATH, this.LIB); + // eslint-disable-next-line no-console console.info(util.format('Node Version', process.version)); - if (parseInt(process.version.match(/v(\d)/)[1]) < 4) throw new Error('*** PLease upgrade to Node 4.+ to run OSRM cucumber tests'); + if (parseInt(process.version.match(/v(\d)/)[1]) < 4) throw new Error('*** Please upgrade to Node 4.+ to run OSRM cucumber tests'); - fs.exists(this.TEST_FOLDER, (exists) => { - if (!exists) throw new Error(util.format('*** Test folder %s doesn\'t exist.', this.TEST_FOLDER)); - callback(); + fs.exists(this.TEST_PATH, (exists) => { + if (exists) + return callback(); + else + return callback(new Error('*** Test folder doesn\'t exist.')); }); }; - this.verifyOSRMIsNotRunning = () => { - if (this.OSRMLoader.up()) { - throw new Error('*** osrm-routed is already running.'); - } + this.getProfilePath = (profile) => { + return path.resolve(this.PROFILES_PATH, profile + '.lua'); + }; + + this.verifyOSRMIsNotRunning = (callback) => { + tryConnect(this.OSRM_PORT, (err) => { + if (!err) return callback(new Error('*** osrm-routed is already running.')); + else callback(); + }); }; this.verifyExistenceOfBinaries = (callback) => { - var verify = (bin, cb) => { - var binPath = path.resolve(util.format('%s/%s%s', this.BIN_PATH, bin, this.EXE)); + var verify = (binPath, cb) => { fs.exists(binPath, (exists) => { - if (!exists) throw new Error(util.format('%s is missing. Build failed?', binPath)); + if (!exists) return cb(new Error(util.format('%s is missing. Build failed?', binPath))); var helpPath = util.format('%s --help > /dev/null 2>&1', binPath); - exec(helpPath, (err) => { + child_process.exec(helpPath, (err) => { if (err) { - this.log(util.format('*** Exited with code %d', err.code), 'preprocess'); - throw new Error(util.format('*** %s exited with code %d', helpPath, err.code)); + return cb(new Error(util.format('*** %s exited with code %d', helpPath, err.code))); } cb(); }); @@ -74,23 +119,12 @@ module.exports = function () { }; var q = d3.queue(); - ['osrm-extract', 'osrm-contract', 'osrm-routed'].forEach(bin => { q.defer(verify, bin); }); - q.awaitAll(() => { - callback(); - }); - }; - - this.AfterConfiguration = (callback) => { - this.clearLogFiles(() => { - this.verifyOSRMIsNotRunning(); - this.verifyExistenceOfBinaries(() => { - callback(); - }); - }); + [this.OSRM_EXTRACT_PATH, this.OSRM_CONTRACT_PATH, this.OSRM_ROUTED_PATH].forEach(bin => { q.defer(verify, bin); }); + q.awaitAll(callback); }; process.on('exit', () => { - if (this.OSRMLoader.loader) this.OSRMLoader.shutdown(() => {}); + this.osrmLoader.shutdown(() => {}); }); process.on('SIGINT', () => { diff --git a/features/support/exception_classes.js b/features/support/exception_classes.js deleted file mode 100644 index 36bdffe8b53..00000000000 --- a/features/support/exception_classes.js +++ /dev/null @@ -1,132 +0,0 @@ -'use strict'; - -var util = require('util'); -var path = require('path'); -var fs = require('fs'); -var chalk = require('chalk'); - -var OSRMError = class extends Error { - constructor (process, code, msg, log, lines) { - super(msg); - this.process = process; - this.code = code; - this.msg = msg; - this.lines = lines; - this.log = log; - } - - extract (callback) { - this.logTail(this.log, this.lines, callback); - } - - // toString (callback) { - // this.extract((tail) => { - // callback(util.format('*** %s\nLast %s from %s:\n%s\n', this.msg, this.lines, this.log, tail)); - // }); - // } - - logTail (logPath, n, callback) { - var expanded = path.resolve(this.TEST_FOLDER, logPath); - fs.exists(expanded, (exists) => { - if (exists) { - fs.readFile(expanded, (err, data) => { - var lines = data.toString().trim().split('\n'); - callback(lines - .slice(lines.length - n) - .map(line => util.format(' %s', line)) - .join('\n')); - }); - } else { - callback(util.format('File %s does not exist!', expanded)); - } - }); - } -}; - -var unescapeStr = (str) => str.replace(/\\\|/g, '\|').replace(/\\\\/g, '\\'); - -module.exports = { - OSRMError: OSRMError, - - FileError: class extends OSRMError { - constructor (logFile, code, msg) { - super ('fileutil', code, msg, logFile, 5); - } - }, - - LaunchError: class extends OSRMError { - constructor (logFile, launchProcess, code, msg) { - super (launchProcess, code, msg, logFile, 5); - } - }, - - ExtractError: class extends OSRMError { - constructor (logFile, code, msg) { - super('osrm-extract', code, msg, logFile, 3); - } - }, - - ContractError: class extends OSRMError { - constructor (logFile, code, msg) { - super('osrm-contract', code, msg, logFile, 3); - } - }, - - RoutedError: class extends OSRMError { - constructor (logFile, msg) { - super('osrm-routed', null, msg, logFile, 3); - } - }, - - TableDiffError: class extends Error { - constructor (expected, actual) { - super(); - this.headers = expected.raw()[0]; - this.expected = expected.hashes(); - this.actual = actual; - this.diff = []; - this.hasErrors = false; - - var good = 0, bad = 0; - - this.expected.forEach((row, i) => { - var rowError = false; - - for (var j in row) { - if (unescapeStr(row[j]) != actual[i][j]) { - rowError = true; - this.hasErrors = true; - break; - } - } - - if (rowError) { - bad++; - this.diff.push(Object.assign({}, row, {c_status: 'undefined'})); - this.diff.push(Object.assign({}, actual[i], {c_status: 'comment'})); - } else { - good++; - this.diff.push(row); - } - }); - } - - get string () { - if (!this.hasErrors) return null; - - var s = ['Tables were not identical:']; - s.push(this.headers.map(key => ' ' + key).join(' | ')); - this.diff.forEach((row) => { - var rowString = '| '; - this.headers.forEach((header) => { - if (!row.c_status) rowString += chalk.green(' ' + row[header] + ' | '); - else if (row.c_status === 'undefined') rowString += chalk.yellow('(-) ' + row[header] + ' | '); - else rowString += chalk.red('(+) ' + row[header] + ' | '); - }); - s.push(rowString); - }); - - return s.join('\n') + '\nTODO this is a temp workaround waiting for https://github.com/cucumber/cucumber-js/issues/534'; - } - } -}; diff --git a/features/support/exceptions.js b/features/support/exceptions.js deleted file mode 100644 index 6af1a93b0ad..00000000000 --- a/features/support/exceptions.js +++ /dev/null @@ -1,15 +0,0 @@ -var exceptions = require('./exception_classes'); - -module.exports = function () { - this.OSRMError = exceptions.OSRMError, - - this.FileError = (code, msg) => new (exceptions.FileError.bind(exceptions.FileError, this.PREPROCESS_LOG_FILE))(code, msg); - - this.LaunchError = (code, launchProcess, msg) => new (exceptions.LaunchError.bind(exceptions.LaunchError, this.ERROR_LOG_FILE))(code, launchProcess, msg); - - this.ExtractError = (code, msg) => new (exceptions.ExtractError.bind(exceptions.ExtractError, this.PREPROCESS_LOG_FILE))(code, msg); - - this.ContractError = (code, msg) => new (exceptions.ContractError.bind(exceptions.ContractError, this.PREPROCESS_LOG_FILE))(code, msg); - - this.RoutedError = (msg) => new (exceptions.RoutedError.bind(exceptions.RoutedError, this.OSRM_ROUTED_LOG_FILE))(msg); -}; diff --git a/features/support/hash.js b/features/support/hash.js deleted file mode 100644 index 399dd51cac5..00000000000 --- a/features/support/hash.js +++ /dev/null @@ -1,43 +0,0 @@ -var fs = require('fs'); -var path = require('path'); -var crypto = require('crypto'); -var d3 = require('d3-queue'); - -module.exports = function () { - this.hashOfFiles = (paths, cb) => { - paths = Array.isArray(paths) ? paths : [paths]; - var shasum = crypto.createHash('sha1'), hashedFiles = false; - - var q = d3.queue(1); - - var addFile = (path, cb) => { - fs.readFile(path, (err, data) => { - if (err && err.code === 'ENOENT') cb(); // ignore non-existing files - else if (err) cb(err); - else { - shasum.update(data); - hashedFiles = true; - cb(); - } - }); - }; - - paths.forEach(path => { q.defer(addFile, path); }); - - q.awaitAll(err => { - if (err) throw new Error('*** Error reading files:', err); - if (!hashedFiles) throw new Error('*** No files found: [' + paths.join(', ') + ']'); - cb(shasum.digest('hex')); - }); - }; - - this.hashProfile = (cb) => { - this.hashOfFiles(path.resolve(this.PROFILES_PATH, this.profile + '.lua'), cb); - }; - - this.hashString = (str) => { - return crypto.createHash('sha1').update(str).digest('hex'); - }; - - return this; -}; diff --git a/features/support/hooks.js b/features/support/hooks.js index 1e265ea14ec..b0f6b0b77f8 100644 --- a/features/support/hooks.js +++ b/features/support/hooks.js @@ -1,36 +1,61 @@ -var util = require('util'); +'use strict'; + +var d3 = require('d3-queue'); +var path = require('path'); +var mkdirp = require('mkdirp'); +var rimraf = require('rimraf'); +var OSM = require('../lib/osm'); +var OSRMLoader = require('../lib/osrm_loader'); module.exports = function () { - this.BeforeFeatures((features, callback) => { - this.pid = null; - this.initializeEnv(() => { - this.initializeOptions(callback); - }); + this.registerHandler('BeforeFeatures', {timeout: 30000}, (features, callback) => { + this.osrmLoader = new OSRMLoader(this); + this.OSMDB = new OSM.DB(); + + let queue = d3.queue(1); + queue.defer(this.initializeEnv.bind(this)); + queue.defer(this.verifyOSRMIsNotRunning.bind(this)); + queue.defer(this.verifyExistenceOfBinaries.bind(this)); + queue.defer(this.initializeCache.bind(this)); + queue.defer(this.setupFeatures.bind(this, features)); + queue.awaitAll(callback); }); - this.Before((scenario, callback) => { - this.scenarioTitle = scenario.getName(); + this.BeforeFeature((feature, callback) => { + this.profile = this.DEFAULT_PROFILE; + this.profileFile = path.join(this.PROFILES_PATH, this.profile + '.lua'); + this.setupFeatureCache(feature); + callback(); + }); - this.loadMethod = this.DEFAULT_LOAD_METHOD; - this.queryParams = {}; - var d = new Date(); - this.scenarioTime = util.format('%d-%d-%dT%s:%s:%sZ', d.getFullYear(), d.getMonth()+1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()); - this.resetData(); - this.hasLoggedPreprocessInfo = false; - this.hasLoggedScenarioInfo = false; + this.Before((scenario, callback) => { + this.osrmLoader.setLoadMethod(this.DEFAULT_LOAD_METHOD); this.setGridSize(this.DEFAULT_GRID_SIZE); this.setOrigin(this.DEFAULT_ORIGIN); - this.fingerprintExtract = this.hashString([this.luaLibHash, this.binExtractHash].join('-')); - this.fingerprintContract = this.hashString(this.binContractHash); - callback(); + this.queryParams = {}; + this.extractArgs = ''; + this.contractArgs = ''; + this.environment = Object.assign(this.DEFAULT_ENVIRONMENT); + this.resetOSM(); + + this.scenarioID = this.getScenarioID(scenario); + this.setupScenarioCache(this.scenarioID); + + // setup output logging + let logDir = path.join(this.LOGS_PATH, this.featureID); + this.scenarioLogFile = path.join(logDir, this.scenarioID) + '.log'; + d3.queue(1) + .defer(mkdirp, logDir) + .defer(rimraf, this.scenarioLogFile) + .awaitAll(callback); }); this.After((scenario, callback) => { - this.setExtractArgs('', () => { - this.setContractArgs('', () => { - if (this.loadMethod === 'directly' && !!this.OSRMLoader.loader) this.OSRMLoader.shutdown(callback); - else callback(); - }); - }); + this.resetOptionsOutput(); + callback(); + }); + + this.AfterFeatures((features, callback) => { + callback(); }); }; diff --git a/features/support/http.js b/features/support/http.js index 3ae2edc9301..71f61761a45 100644 --- a/features/support/http.js +++ b/features/support/http.js @@ -19,6 +19,9 @@ module.exports = function () { return paramString; }; + // FIXME this needs to be simplified! + // - remove usage of node-timeout + // - replace with node's native timout mechanism this.sendRequest = (baseUri, parameters, callback) => { var limit = Timeout(this.TIMEOUT, { err: { statusCode: 408 } }); @@ -28,9 +31,9 @@ module.exports = function () { request(this.query, (err, res, body) => { if (err && err.code === 'ECONNREFUSED') { - throw new Error('*** osrm-routed is not running.'); + return cb(new Error('*** osrm-routed is not running.')); } else if (err && err.statusCode === 408) { - throw new Error(); + return cb(new Error()); } return cb(err, res, body); @@ -40,11 +43,10 @@ module.exports = function () { runRequest(limit((err, res, body) => { if (err) { if (err.statusCode === 408) - return callback(this.RoutedError('*** osrm-routed did not respond')); + return callback(new Error('*** osrm-routed did not respond')); else if (err.code === 'ECONNREFUSED') - return callback(this.RoutedError('*** osrm-routed is not running')); + return callback(new Error('*** osrm-routed is not running')); } - //console.log(body+"\n"); return callback(err, res, body); })); }; diff --git a/features/support/launch.js b/features/support/launch.js deleted file mode 100644 index ee335e301f5..00000000000 --- a/features/support/launch.js +++ /dev/null @@ -1,5 +0,0 @@ -var launchClasses = require('./launch_classes'); - -module.exports = function () { - this._OSRMLoader = () => new (launchClasses._OSRMLoader.bind(launchClasses._OSRMLoader, this))(); -}; diff --git a/features/support/launch_classes.js b/features/support/launch_classes.js deleted file mode 100644 index 2aace884613..00000000000 --- a/features/support/launch_classes.js +++ /dev/null @@ -1,164 +0,0 @@ -'use strict'; - -var fs = require('fs'); -var spawn = require('child_process').spawn; -var util = require('util'); -var net = require('net'); -var Timeout = require('node-timeout'); - -var OSRMBaseLoader = class { - constructor (scope) { - this.scope = scope; - } - - launch (callback) { - var limit = Timeout(this.scope.TIMEOUT, { err: this.scope.RoutedError('Launching osrm-routed timed out.') }); - - var runLaunch = (cb) => { - this.osrmUp(() => { this.waitForConnection(cb); }); - }; - - runLaunch(limit((e) => { if (e) callback(e); else callback(); })); - } - - shutdown (callback) { - var limit = Timeout(this.scope.TIMEOUT, { err: this.scope.RoutedError('Shutting down osrm-routed timed out.')}); - - var runShutdown = (cb) => { - this.osrmDown(cb); - }; - - runShutdown(limit((e) => { if (e) callback(e); else callback(); })); - } - - osrmIsRunning () { - return !!this.scope.pid && this.child && !this.child.killed; - } - - osrmDown (callback) { - if (this.scope.pid) { - process.kill(this.scope.pid, this.scope.TERMSIGNAL); - this.waitForShutdown(callback); - this.scope.pid = null; - } else callback(true); - } - - waitForConnection (callback) { - var retryCount = 0; - var connectWithRetry = () => { - net.connect({ port: this.scope.OSRM_PORT, host: '127.0.0.1' }) - .on('connect', () => { callback(); }) - .on('error', () => { - if (retryCount < 2) { - retryCount++; - setTimeout(connectWithRetry, 100); - } else { - callback(new Error('Could not connect to osrm-routed after three retires')); - } - }); - }; - - connectWithRetry(); - } - - waitForShutdown (callback) { - var check = () => { - if (!this.osrmIsRunning()) return callback(); - }; - setTimeout(check, 100); - } -}; - -var OSRMDirectLoader = class extends OSRMBaseLoader { - constructor (scope) { - super(scope); - } - - load (inputFile, callback) { - this.inputFile = inputFile; - this.shutdown(() => { - this.launch(callback); - }); - } - - osrmUp (callback) { - if (this.scope.pid) return callback(); - var writeToLog = (data) => { - fs.appendFile(this.scope.OSRM_ROUTED_LOG_FILE, data, (err) => { if (err) throw err; }); - }; - - var child = spawn(util.format('%s/osrm-routed', this.scope.BIN_PATH), [this.inputFile, util.format('-p%d', this.scope.OSRM_PORT)]); - this.scope.pid = child.pid; - child.stdout.on('data', writeToLog); - child.stderr.on('data', writeToLog); - - callback(); - } -}; - -var OSRMDatastoreLoader = class extends OSRMBaseLoader { - constructor (scope) { - super(scope); - } - - load (inputFile, callback) { - this.inputFile = inputFile; - this.loadData((err) => { - if (err) return callback(err); - if (!this.scope.pid) return this.launch(callback); - else callback(); - }); - } - - loadData (callback) { - this.scope.runBin('osrm-datastore', this.inputFile, (err) => { - if (err) return callback(this.scope.LaunchError(this.exitCode, 'datastore', err)); - callback(); - }); - } - - osrmUp (callback) { - if (this.scope.pid) return callback(); - var writeToLog = (data) => { - fs.appendFile(this.scope.OSRM_ROUTED_LOG_FILE, data, (err) => { if (err) throw err; }); - }; - - var child = spawn(util.format('%s/osrm-routed', this.scope.BIN_PATH), ['--shared-memory=1', util.format('-p%d', this.scope.OSRM_PORT)]); - this.child = child; - this.scope.pid = child.pid; - child.stdout.on('data', writeToLog); - child.stderr.on('data', writeToLog); - - callback(); - } -}; - -module.exports = { - _OSRMLoader: class { - constructor (scope) { - this.scope = scope; - this.loader = null; - } - - load (inputFile, callback) { - var method = this.scope.loadMethod; - if (method === 'datastore') { - this.loader = new OSRMDatastoreLoader(this.scope); - this.loader.load(inputFile, callback); - } else if (method === 'directly') { - this.loader = new OSRMDirectLoader(this.scope); - this.loader.load(inputFile, callback); - } else { - callback(new Error('*** Unknown load method ' + method)); - } - } - - shutdown (callback) { - this.loader.shutdown(callback); - } - - up () { - return this.loader ? this.loader.osrmIsRunning() : false; - } - } -}; diff --git a/features/support/log.js b/features/support/log.js deleted file mode 100644 index c428cb9d985..00000000000 --- a/features/support/log.js +++ /dev/null @@ -1,90 +0,0 @@ -var fs = require('fs'); - -module.exports = function () { - this.clearLogFiles = (callback) => { - // emptying existing files, rather than deleting and writing new ones makes it - // easier to use tail -f from the command line - fs.writeFile(this.OSRM_ROUTED_LOG_FILE, '', err => { - if (err) throw err; - fs.writeFile(this.PREPROCESS_LOG_FILE, '', err => { - if (err) throw err; - fs.writeFile(this.LOG_FILE, '', err => { - if (err) throw err; - callback(); - }); - }); - }); - }; - - var log = this.log = (s, type) => { - s = s || ''; - type = type || null; - var file = type === 'preprocess' ? this.PREPROCESS_LOG_FILE : this.LOG_FILE; - fs.appendFile(file, s + '\n', err => { - if (err) throw err; - }); - }; - - this.logScenarioFailInfo = () => { - if (this.hasLoggedScenarioInfo) return; - - log('========================================='); - log('Failed scenario: ' + this.scenarioTitle); - log('Time: ' + this.scenarioTime); - log('Fingerprint osm stage: ' + this.osmData.fingerprintOSM); - log('Fingerprint extract stage: ' + this.fingerprintExtract); - log('Fingerprint contract stage: ' + this.fingerprintContract); - log('Fingerprint route stage: ' + this.fingerprintRoute); - log('Profile: ' + this.profile); - log(); - log('```xml'); // so output can be posted directly to github comment fields - log(this.osmData.str.trim()); - log('```'); - log(); - log(); - - this.hasLoggedScenarioInfo = true; - }; - - this.logFail = (expected, got, attempts) => { - this.logScenarioFailInfo(); - log('== '); - log('Expected: ' + JSON.stringify(expected)); - log('Got: ' + JSON.stringify(got)); - log(); - ['route','forw','backw'].forEach((direction) => { - if (attempts[direction]) { - log('Direction: ' + direction); - log('Query: ' + attempts[direction].query); - log('Response: ' + attempts[direction].response.body); - log(); - } - }); - }; - - this.logPreprocessInfo = () => { - if (this.hasLoggedPreprocessInfo) return; - log('=========================================', 'preprocess'); - log('Preprocessing data for scenario: ' + this.scenarioTitle, 'preprocess'); - log('Time: ' + this.scenarioTime, 'preprocess'); - log('', 'preprocess'); - log('== OSM data:', 'preprocess'); - log('```xml', 'preprocess'); // so output can be posted directly to github comment fields - log(this.osmData.str, 'preprocess'); - log('```', 'preprocess'); - log('', 'preprocess'); - log('== Profile:', 'preprocess'); - log(this.profile, 'preprocess'); - log('', 'preprocess'); - this.hasLoggedPreprocessInfo = true; - }; - - this.logPreprocess = (str) => { - this.logPreprocessInfo(); - log(str, 'preprocess'); - }; - - this.logPreprocessDone = () => { - log('Done with preprocessing at ' + new Date(), 'preprocess'); - }; -}; diff --git a/features/support/route.js b/features/support/route.js index 746f6bfd8b1..aad92f8157e 100644 --- a/features/support/route.js +++ b/features/support/route.js @@ -1,8 +1,8 @@ 'use strict'; -var Timeout = require('node-timeout'); -var request = require('request'); -var util = require('util'); +const Timeout = require('node-timeout'); +const request = require('request'); +const ensureDecimal = require('../lib/utils').ensureDecimal; module.exports = function () { this.requestPath = (service, params, callback) => { @@ -80,18 +80,7 @@ module.exports = function () { return this.requestPath('nearest', params, callback); }; - this.requestIsochrone = (node, userParams, callback) => { - var defaults = { - output: 'json' - }, - params = this.overwriteParams(defaults, userParams); - params.coordinates = [[node.lon, node.lat].join(',')]; - params.distance = 10000; - - return this.requestPath('isochrone', params, callback); - }; - - this.requestTable = (isochrone, userParams, callback) => { + this.requestTable = (waypoints, userParams, callback) => { var defaults = { output: 'json' }, diff --git a/features/support/run.js b/features/support/run.js index 35561d80301..f6be19744f5 100644 --- a/features/support/run.js +++ b/features/support/run.js @@ -1,40 +1,52 @@ -var fs = require('fs'); -var util = require('util'); -var exec = require('child_process').exec; +'use strict'; + +const fs = require('fs'); +const util = require('util'); +const child_process = require('child_process'); module.exports = function () { - this.runBin = (bin, options, callback) => { - var opts = options.slice(); + // replaces placeholders for in user supplied commands + this.expandOptions = (options) => { + let opts = options.slice(); + let table = { + '{osm_file}': this.inputCacheFile, + '{processed_file}': this.processedCacheFile, + '{profile_file}': this.profileFile, + '{rastersource_file}': this.rasterCacheFile, + '{speeds_file}': this.speedsCacheFile, + '{penalties_file}': this.penaltiesCacheFile + }; - if (opts.match('{osm_base}')) { - if (!this.osmData.osmFile) throw new Error('*** {osm_base} is missing'); - opts = opts.replace('{osm_base}', this.osmData.osmFile); + for (let k in table) { + opts = opts.replace(k, table[k]); } - if (opts.match('{extracted_base}')) { - if (!this.osmData.extractedFile) throw new Error('*** {extracted_base} is missing'); - opts = opts.replace('{extracted_base}', this.osmData.extractedFile); - } + return opts; + }; - if (opts.match('{contracted_base}')) { - if (!this.osmData.contractedFile) throw new Error('*** {contracted_base} is missing'); - opts = opts.replace('{contracted_base}', this.osmData.contractedFile); + this.setupOutputLog = (process, log) => { + if (process.logFunc) { + process.stdout.removeListener('data', process.logFunc); + process.stderr.removeListener('data', process.logFunc); } - if (opts.match('{profile}')) { - opts = opts.replace('{profile}', [this.PROFILES_PATH, this.profile + '.lua'].join('/')); - } + process.logFunc = (message) => { log.write(message); }; + process.stdout.on('data', process.logFunc); + process.stderr.on('data', process.logFunc); + }; - var cmd = util.format('%s%s/%s%s%s %s 2>%s', this.QQ, this.BIN_PATH, bin, this.EXE, this.QQ, opts, this.ERROR_LOG_FILE); - process.chdir(this.TEST_FOLDER); - exec(cmd, (err, stdout, stderr) => { - this.stdout = stdout.toString(); - fs.readFile(this.ERROR_LOG_FILE, (e, data) => { - this.stderr = data ? data.toString() : ''; - this.exitCode = err && err.code || 0; - process.chdir('../'); - callback(err, stdout, stderr); - }); - }); + this.runBin = (bin, options, env, callback) => { + let cmd = util.format('%s%s/%s%s%s', this.QQ, this.BIN_PATH, bin, this.EXE, this.QQ); + let opts = options.split(' ').filter((x) => { return x && x.length > 0; }); + let log = fs.createWriteStream(this.scenarioLogFile, {'flags': 'a'}); + log.write(util.format('*** running %s %s\n', cmd, options)); + // we need to set a large maxbuffer here because we have long running processes like osrm-routed + // with lots of log output + let child = child_process.execFile(cmd, opts, {maxBuffer: 1024 * 1024 * 1000, env: env}, callback); + child.on('exit', function(code) { + log.write(util.format('*** %s exited with code %d\n', bin, code)); + }.bind(this)); + this.setupOutputLog(child, log); + return child; }; }; diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js index bd6850ab304..0252cd47b71 100644 --- a/features/support/shared_steps.js +++ b/features/support/shared_steps.js @@ -34,7 +34,7 @@ module.exports = function () { var afterRequest = (err, res, body) => { if (err) return cb(err); if (body && body.length) { - let destinations, pronunciations, instructions, bearings, turns, modes, times, + let destinations, pronunciations, instructions, refs, bearings, turns, modes, times, distances, summary, intersections, lanes; let json = JSON.parse(body); @@ -44,6 +44,7 @@ module.exports = function () { if (hasRoute) { instructions = this.wayList(json.routes[0]); pronunciations = this.pronunciationList(json.routes[0]); + refs = this.refList(json.routes[0]); destinations = this.destinationsList(json.routes[0]); bearings = this.bearingList(json.routes[0]); turns = this.turnList(json.routes[0]); @@ -92,7 +93,7 @@ module.exports = function () { if (headers.has('distance')) { if (row.distance.length) { if (!row.distance.match(/\d+m/)) - throw new Error('*** Distance must be specified in meters. (ex: 250m)'); + return cb(new Error('*** Distance must be specified in meters. (ex: 250m)')); got.distance = instructions ? util.format('%dm', distance) : ''; } else { got.distance = ''; @@ -101,7 +102,7 @@ module.exports = function () { if (headers.has('time')) { if (!row.time.match(/\d+s/)) - throw new Error('*** Time must be specied in seconds. (ex: 60s)'); + return cb(new Error('*** Time must be specied in seconds. (ex: 60s)')); got.time = instructions ? util.format('%ds', time) : ''; } @@ -112,7 +113,7 @@ module.exports = function () { if (headers.has('speed')) { if (row.speed !== '' && instructions) { if (!row.speed.match(/\d+ km\/h/)) - throw new Error('*** Speed must be specied in km/h. (ex: 50 km/h)'); + cb(new Error('*** Speed must be specied in km/h. (ex: 50 km/h)')); var speed = time > 0 ? Math.round(3.6*distance/time) : null; got.speed = util.format('%d km/h', speed); } else { @@ -128,6 +129,7 @@ module.exports = function () { if (headers.has(key)) got[key] = instructions ? value : ''; }; + putValue('ref', refs); putValue('bearing', bearings); putValue('turns', turns); putValue('modes', modes); @@ -137,20 +139,12 @@ module.exports = function () { putValue('destinations', destinations); } - var ok = true; - for (var key in row) { if (this.FuzzyMatch.match(got[key], row[key])) { got[key] = row[key]; - } else { - ok = false; } } - if (!ok) { - this.logFail(row, got, { route: { query: this.query, response: res }}); - } - cb(null, got); } else { cb(new Error('request failed to return valid body')); @@ -187,11 +181,11 @@ module.exports = function () { if (row.from && row.to) { var fromNode = this.findNodeByName(row.from); - if (!fromNode) throw new Error(util.format('*** unknown from-node "%s"'), row.from); + if (!fromNode) return cb(new Error(util.format('*** unknown from-node "%s"'), row.from)); waypoints.push(fromNode); var toNode = this.findNodeByName(row.to); - if (!toNode) throw new Error(util.format('*** unknown to-node "%s"'), row.to); + if (!toNode) return cb(new Error(util.format('*** unknown to-node "%s"'), row.to)); waypoints.push(toNode); got.from = row.from; @@ -200,13 +194,13 @@ module.exports = function () { } else if (row.waypoints) { row.waypoints.split(',').forEach((n) => { var node = this.findNodeByName(n.trim()); - if (!node) throw new Error('*** unknown waypoint node "%s"', n.trim()); + if (!node) return cb(new Error('*** unknown waypoint node "%s"', n.trim())); waypoints.push(node); }); got.waypoints = row.waypoints; this.requestRoute(waypoints, bearings, params, afterRequest); } else { - throw new Error('*** no waypoints'); + return cb(new Error('*** no waypoints')); } } }; diff --git a/features/support/table_patch.js b/features/support/table_patch.js deleted file mode 100644 index 16ffebb8c07..00000000000 --- a/features/support/table_patch.js +++ /dev/null @@ -1,11 +0,0 @@ -var DifferentError = require('./exception_classes').TableDiffError; - -module.exports = function () { - this.diffTables = (expected, actual, options, callback) => { - // this is a temp workaround while waiting for https://github.com/cucumber/cucumber-js/issues/534 - - var error = new DifferentError(expected, actual); - - return callback(error.string); - }; -}; diff --git a/features/testbot/bad.feature b/features/testbot/bad.feature index 18ee5f862df..aeddc95d19e 100644 --- a/features/testbot/bad.feature +++ b/features/testbot/bad.feature @@ -11,8 +11,8 @@ Feature: Handle bad data in a graceful manner Given the ways | nodes | - When the data has been contracted - Then "osrm-extract" should return code 1 + When I try to run "osrm-extract {osm_file} --profile {profile_file}" + Then it should exit with an error Scenario: Only dead-end oneways Given the node map diff --git a/features/testbot/bugs.feature b/features/testbot/bugs.feature deleted file mode 100644 index 26be28aa6ff..00000000000 --- a/features/testbot/bugs.feature +++ /dev/null @@ -1,5 +0,0 @@ -@routing @testbot @bug -Feature: Known bugs - - Background: - Given the profile "testbot" diff --git a/features/testbot/matching.feature b/features/testbot/matching.feature index 9c7faa01f40..ead4b6a7461 100644 --- a/features/testbot/matching.feature +++ b/features/testbot/matching.feature @@ -124,7 +124,7 @@ Feature: Basic Map Matching 1,2,36 """ - And the contract extra arguments "--segment-speed-file speeds.csv" + And the contract extra arguments "--segment-speed-file {speeds_file}" When I match I should get | trace | matchings | annotation | diff --git a/features/testbot/side_bias.feature b/features/testbot/side_bias.feature index fe24117c513..1d6d629c7b2 100644 --- a/features/testbot/side_bias.feature +++ b/features/testbot/side_bias.feature @@ -1,9 +1,23 @@ @routing @testbot @sidebias Feature: Testbot - side bias + Background: + Given the profile file + """ + require 'testbot' + properties.left_hand_driving = true + """ + Scenario: Left hand bias - Given the profile "lhs" - And the node map + Given the profile file "testbot" extended with + """ + properties.left_hand_driving = true + function turn_function (angle) + local k = 10 * angle * angle * 50 / (90.0 * 90.0) + return (angle >= 0) and k * 1.2 or k / 1.2 + end + """ + Given the node map | a | | b | | c | | | | | | | | | | d | | | @@ -19,7 +33,14 @@ Feature: Testbot - side bias | d | c | bd,bc,bc | 100s +-1 | Scenario: Right hand bias - Given the profile "rhs" + Given the profile file "testbot" extended with + """ + properties.left_hand_driving = false + function turn_function (angle) + local k = 10 * angle * angle * 50 / (90.0 * 90.0) + return (angle >= 0) and k / 1.2 or k * 1.2 + end + """ And the node map | a | | b | | c | | | | | | | @@ -36,7 +57,6 @@ Feature: Testbot - side bias | d | c | bd,bc,bc | 82s +-1 | Scenario: Roundabout exit counting for left sided driving - Given the profile "lhs" And a grid size of 10 meters And the node map | | | a | | | @@ -44,7 +64,7 @@ Feature: Testbot - side bias | h | g | | c | d | | | | e | | | | | | f | | | - And the ways + And the ways | nodes | junction | | ab | | | cd | | @@ -52,14 +72,13 @@ Feature: Testbot - side bias | gh | | | bcegb | roundabout | - When I route I should get + When I route I should get | waypoints | route | turns | | a,d | ab,cd,cd | depart,roundabout turn left exit-1,arrive | | a,f | ab,ef,ef | depart,roundabout turn straight exit-2,arrive | | a,h | ab,gh,gh | depart,roundabout turn right exit-3,arrive | - Scenario: Mixed Entry and Exit - Given the profile "lhs" + Scenario: Mixed Entry and Exit And a grid size of 10 meters And the node map | | c | | a | | diff --git a/features/testbot/summary.feature b/features/testbot/summary.feature index 563230a0341..83c05684f7e 100644 --- a/features/testbot/summary.feature +++ b/features/testbot/summary.feature @@ -53,9 +53,9 @@ Feature: Basic Routing | ab | When I route I should get - | from | to | route | summary | - | a | b | ab,ab | ab | - | b | a | ab,ab | ab | + | from | to | route | summary | + | a | b | ab,ab | ab | + | b | a | ab,ab | ab | @repeated Scenario: Check handling empty values diff --git a/features/testbot/traffic_turn_penalties.feature b/features/testbot/traffic_turn_penalties.feature index a8f82e0dc63..7ee9424ab54 100644 --- a/features/testbot/traffic_turn_penalties.feature +++ b/features/testbot/traffic_turn_penalties.feature @@ -26,7 +26,7 @@ Feature: Traffic - turn penalties applied to turn onto which a phantom node snap 1,2,5,0,comment 3,4,7,-20 """ - And the contract extra arguments "--turn-penalty-file penalties.csv" + And the contract extra arguments "--turn-penalty-file {penalties_file}" When I route I should get | from | to | route | speed | time | | a | e | ab,be,be | 36 km/h | 40s +-1 | diff --git a/features/testbot/trip.feature b/features/testbot/trip.feature index 5f3376369e2..33a1b4956da 100644 --- a/features/testbot/trip.feature +++ b/features/testbot/trip.feature @@ -8,7 +8,7 @@ Feature: Basic trip planning Scenario: Testbot - Trip planning with less than 10 nodes Given the node map | a | b | - | d | c | + | c | d | And the ways | nodes | @@ -18,8 +18,9 @@ Feature: Basic trip planning | da | When I plan a trip I should get - | waypoints | trips | - | a,b,c,d | dcba | + | waypoints | trips | durations | + | a,b,c,d | abcd | 7.6 | + | d,b,c,a | dbca | 7.6 | Scenario: Testbot - Trip planning with more than 10 nodes Given the node map @@ -55,8 +56,8 @@ Feature: Basic trip planning | k | | | f | | j | i | h | g | | | | | | - | m | n | | | - | p | o | | | + | q | m | n | | + | | p | o | | And the ways | nodes | @@ -75,12 +76,13 @@ Feature: Basic trip planning | mn | | no | | op | - | pm | + | pq | + | qm | When I plan a trip I should get | waypoints | trips | - | a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p | cbalkjihgfedc,ponm | + | a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p | cbalkjihgfedc,mnop | # Test single node in each component #1850 Scenario: Testbot - Trip planning with less than 10 nodes diff --git a/features/testbot/via.feature b/features/testbot/via.feature index fcd9669025f..62699b8f07b 100644 --- a/features/testbot/via.feature +++ b/features/testbot/via.feature @@ -173,7 +173,7 @@ Feature: Via points | c,d,a | abc,bd,bd,bd,abc,abc | # See issue #2349 - @bug @todo + @todo Scenario: Via point at a dead end with oneway Given the node map | a | b | c | diff --git a/include/contractor/contractor.hpp b/include/contractor/contractor.hpp index 3c9dff9c0b7..7cc70cc484d 100644 --- a/include/contractor/contractor.hpp +++ b/include/contractor/contractor.hpp @@ -89,7 +89,8 @@ class Contractor const std::string &geometry_filename, const std::string &datasource_names_filename, const std::string &datasource_indexes_filename, - const std::string &rtree_leaf_filename); + const std::string &rtree_leaf_filename, + const double log_edge_updates_factor); }; } } diff --git a/include/contractor/contractor_config.hpp b/include/contractor/contractor_config.hpp index e820b3f091a..cdb1b080dd7 100644 --- a/include/contractor/contractor_config.hpp +++ b/include/contractor/contractor_config.hpp @@ -73,6 +73,7 @@ struct ContractorConfig bool use_cached_priority; unsigned requested_num_threads; + double log_edge_updates_factor; // A percentage of vertices that will be contracted for the hierarchy. // Offers a trade-off between preprocessing and query time. diff --git a/include/engine/api/route_parameters.hpp b/include/engine/api/route_parameters.hpp index bca6e4d1a47..0c2d1e31e00 100644 --- a/include/engine/api/route_parameters.hpp +++ b/include/engine/api/route_parameters.hpp @@ -69,6 +69,21 @@ struct RouteParameters : public BaseParameters RouteParameters() = default; + template + RouteParameters(const bool steps_, + const bool alternatives_, + const GeometriesType geometries_, + const OverviewType overview_, + const boost::optional continue_straight_, + Args... args_) + : BaseParameters{std::forward(args_)...}, steps{steps_}, alternatives{alternatives_}, + annotations{false}, geometries{geometries_}, overview{overview_}, + continue_straight{continue_straight_} + // Once we perfectly-forward `args` (see #2990) this constructor can delegate to the one below. + { + } + + // RouteParameters constructor adding the `annotations` setting in a API-compatible way. template RouteParameters(const bool steps_, const bool alternatives_, diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index 81cdde129fc..7c4b33bfc86 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -8,6 +8,7 @@ #include "extractor/external_memory_node.hpp" #include "extractor/guidance/turn_instruction.hpp" #include "extractor/guidance/turn_lane_types.hpp" +#include "extractor/original_edge_data.hpp" #include "engine/phantom_node.hpp" #include "util/exception.hpp" #include "util/guidance/bearing_class.hpp" @@ -66,24 +67,30 @@ class BaseDataFacade virtual EdgeID FindEdgeIndicateIfReverse(const NodeID from, const NodeID to, bool &result) const = 0; + virtual EdgeID FindSmallestEdge(const NodeID from, + const NodeID to, + const std::function filter) const = 0; + // node and edge information access virtual util::Coordinate GetCoordinateOfNode(const unsigned id) const = 0; virtual OSMNodeID GetOSMNodeIDOfNode(const unsigned id) const = 0; - virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const = 0; + virtual GeometryID GetGeometryIndexForEdgeID(const unsigned id) const = 0; + + virtual std::vector GetUncompressedForwardGeometry(const EdgeID id) const = 0; - virtual void GetUncompressedGeometry(const EdgeID id, - std::vector &result_nodes) const = 0; + virtual std::vector GetUncompressedReverseGeometry(const EdgeID id) const = 0; // Gets the weight values for each segment in an uncompressed geometry. // Should always be 1 shorter than GetUncompressedGeometry - virtual void GetUncompressedWeights(const EdgeID id, - std::vector &result_weights) const = 0; + virtual std::vector GetUncompressedForwardWeights(const EdgeID id) const = 0; + + virtual std::vector GetUncompressedReverseWeights(const EdgeID id) const = 0; // Returns the data source ids that were used to supply the edge // weights. Will return an empty array when only the base profile is used. - virtual void GetUncompressedDatasources(const EdgeID id, - std::vector &data_sources) const = 0; + virtual std::vector GetUncompressedForwardDatasources(const EdgeID id) const = 0; + virtual std::vector GetUncompressedReverseDatasources(const EdgeID id) const = 0; // Gets the name of a datasource virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const = 0; @@ -152,6 +159,8 @@ class BaseDataFacade virtual std::string GetNameForID(const unsigned name_id) const = 0; + virtual std::string GetRefForID(const unsigned name_id) const = 0; + virtual std::string GetPronunciationForID(const unsigned name_id) const = 0; virtual std::string GetDestinationsForID(const unsigned name_id) const = 0; diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp index e7dc965d887..3f1e9d0ba3a 100644 --- a/include/engine/datafacade/internal_datafacade.hpp +++ b/include/engine/datafacade/internal_datafacade.hpp @@ -76,7 +76,7 @@ class InternalDataFacade final : public BaseDataFacade util::ShM::vector m_coordinate_list; util::PackedVector m_osmnodeid_list; - util::ShM::vector m_via_node_list; + util::ShM::vector m_via_geometry_list; util::ShM::vector m_name_ID_list; util::ShM::vector m_turn_instruction_list; util::ShM::vector m_lane_data_id; @@ -189,7 +189,7 @@ class InternalDataFacade final : public BaseDataFacade boost::filesystem::ifstream edges_input_stream(edges_file, std::ios::binary); unsigned number_of_edges = 0; edges_input_stream.read((char *)&number_of_edges, sizeof(unsigned)); - m_via_node_list.resize(number_of_edges); + m_via_geometry_list.resize(number_of_edges); m_name_ID_list.resize(number_of_edges); m_turn_instruction_list.resize(number_of_edges); m_lane_data_id.resize(number_of_edges); @@ -201,7 +201,7 @@ class InternalDataFacade final : public BaseDataFacade { edges_input_stream.read((char *)&(current_edge_data), sizeof(extractor::OriginalEdgeData)); - m_via_node_list[i] = current_edge_data.via_node; + m_via_geometry_list[i] = current_edge_data.via_geometry; m_name_ID_list[i] = current_edge_data.name_id; m_turn_instruction_list[i] = current_edge_data.turn_instruction; m_lane_data_id[i] = current_edge_data.lane_data_id; @@ -472,6 +472,13 @@ class InternalDataFacade final : public BaseDataFacade return m_query_graph->FindEdgeIndicateIfReverse(from, to, result); } + EdgeID FindSmallestEdge(const NodeID from, + const NodeID to, + std::function filter) const override final + { + return m_query_graph->FindSmallestEdge(from, to, filter); + } + // node and edge information access util::Coordinate GetCoordinateOfNode(const unsigned id) const override final { @@ -636,6 +643,15 @@ class InternalDataFacade final : public BaseDataFacade return result; } + std::string GetRefForID(const unsigned name_id) const override final + { + // We store the ref after the name, destination and pronunciation of a street. + // We do this to get around the street length limit of 255 which would hit + // if we concatenate these. Order (see extractor_callbacks): + // name (0), destination (1), pronunciation (2), ref (3) + return GetNameForID(name_id + 3); + } + std::string GetPronunciationForID(const unsigned name_id) const override final { // We store the pronunciation after the name and destination of a street. @@ -654,9 +670,9 @@ class InternalDataFacade final : public BaseDataFacade return GetNameForID(name_id + 1); } - virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const override final + virtual GeometryID GetGeometryIndexForEdgeID(const unsigned id) const override final { - return m_via_node_list.at(id); + return m_via_geometry_list.at(id); } virtual std::size_t GetCoreSize() const override final { return m_is_core_node.size(); } @@ -673,47 +689,128 @@ class InternalDataFacade final : public BaseDataFacade } } - virtual void GetUncompressedGeometry(const EdgeID id, - std::vector &result_nodes) const override final + virtual std::vector GetUncompressedForwardGeometry(const EdgeID id) const override final { + /* + * NodeID's for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. + * */ const unsigned begin = m_geometry_indices.at(id); const unsigned end = m_geometry_indices.at(id + 1); - result_nodes.clear(); + std::vector result_nodes; + result_nodes.reserve(end - begin); + std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) { result_nodes.emplace_back(edge.node_id); }); + + return result_nodes; } - virtual void - GetUncompressedWeights(const EdgeID id, - std::vector &result_weights) const override final + virtual std::vector GetUncompressedReverseGeometry(const EdgeID id) const override final { + /* + * NodeID's for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. + * */ const unsigned begin = m_geometry_indices.at(id); const unsigned end = m_geometry_indices.at(id + 1); - result_weights.clear(); + std::vector result_nodes; + + result_nodes.reserve(end - begin); + + std::for_each(m_geometry_list.rbegin() + (m_geometry_list.size() - end), + m_geometry_list.rbegin() + (m_geometry_list.size() - begin), + [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) { + result_nodes.emplace_back(edge.node_id); + }); + + return result_nodes; + } + + virtual std::vector + GetUncompressedForwardWeights(const EdgeID id) const override final + { + /* + * EdgeWeights's for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. For + * forward weights of bi-directional edges, edges 2 to + * n of that edge need to be read. + */ + const unsigned begin = m_geometry_indices.at(id) + 1; + const unsigned end = m_geometry_indices.at(id + 1); + + std::vector result_weights; result_weights.reserve(end - begin); + std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) { - result_weights.emplace_back(edge.weight); + result_weights.emplace_back(edge.forward_weight); }); + + return result_weights; } - // Returns the data source ids that were used to supply the edge - // weights. - virtual void - GetUncompressedDatasources(const EdgeID id, - std::vector &result_datasources) const override final + virtual std::vector + GetUncompressedReverseWeights(const EdgeID id) const override final { + /* + * EdgeWeights for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. For + * reverse weights of bi-directional edges, edges 1 to + * n-1 of that edge need to be read in reverse. + */ const unsigned begin = m_geometry_indices.at(id); + const unsigned end = m_geometry_indices.at(id + 1) - 1; + + std::vector result_weights; + result_weights.reserve(end - begin); + + std::for_each(m_geometry_list.rbegin() + (m_geometry_list.size() - end), + m_geometry_list.rbegin() + (m_geometry_list.size() - begin), + [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) { + result_weights.emplace_back(edge.reverse_weight); + }); + + return result_weights; + } + + // Returns the data source ids that were used to supply the edge + // weights. + virtual std::vector + GetUncompressedForwardDatasources(const EdgeID id) const override final + { + /* + * Data sources for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. For + * forward datasources of bi-directional edges, edges 2 to + * n of that edge need to be read. + */ + const unsigned begin = m_geometry_indices.at(id) + 1; const unsigned end = m_geometry_indices.at(id + 1); - result_datasources.clear(); + std::vector result_datasources; result_datasources.reserve(end - begin); // If there was no datasource info, return an array of 0's. @@ -731,6 +828,47 @@ class InternalDataFacade final : public BaseDataFacade m_datasource_list.begin() + end, [&](const uint8_t &datasource_id) { result_datasources.push_back(datasource_id); }); } + + return result_datasources; + } + + // Returns the data source ids that were used to supply the edge + // weights. + virtual std::vector + GetUncompressedReverseDatasources(const EdgeID id) const override final + { + /* + * Datasources for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. For + * reverse datasources of bi-directional edges, edges 1 to + * n-1 of that edge need to be read in reverse. + */ + const unsigned begin = m_geometry_indices.at(id); + const unsigned end = m_geometry_indices.at(id + 1) - 1; + + std::vector result_datasources; + result_datasources.reserve(end - begin); + + // If there was no datasource info, return an array of 0's. + if (m_datasource_list.empty()) + { + for (unsigned i = 0; i < end - begin; ++i) + { + result_datasources.push_back(0); + } + } + else + { + std::for_each( + m_datasource_list.rbegin() + (m_datasource_list.size() - end), + m_datasource_list.rbegin() + (m_datasource_list.size() - begin), + [&](const uint8_t &datasource_id) { result_datasources.push_back(datasource_id); }); + } + + return result_datasources; } virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const override final diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp index 3622c0a0f67..4d21bbfe8cc 100644 --- a/include/engine/datafacade/shared_datafacade.hpp +++ b/include/engine/datafacade/shared_datafacade.hpp @@ -79,7 +79,7 @@ class SharedDataFacade final : public BaseDataFacade util::ShM::vector m_coordinate_list; util::PackedVector m_osmnodeid_list; - util::ShM::vector m_via_node_list; + util::ShM::vector m_via_geometry_list; util::ShM::vector m_name_ID_list; util::ShM::vector m_lane_data_id; util::ShM::vector m_turn_instruction_list; @@ -229,11 +229,12 @@ class SharedDataFacade final : public BaseDataFacade void LoadViaNodeList() { - auto via_node_list_ptr = data_layout->GetBlockPtr( + auto via_geometry_list_ptr = data_layout->GetBlockPtr( shared_memory, storage::SharedDataLayout::VIA_NODE_LIST); - util::ShM::vector via_node_list( - via_node_list_ptr, data_layout->num_entries[storage::SharedDataLayout::VIA_NODE_LIST]); - m_via_node_list = std::move(via_node_list); + util::ShM::vector via_geometry_list( + via_geometry_list_ptr, + data_layout->num_entries[storage::SharedDataLayout::VIA_NODE_LIST]); + m_via_geometry_list = std::move(via_geometry_list); } void LoadNames() @@ -507,6 +508,13 @@ class SharedDataFacade final : public BaseDataFacade return m_query_graph->FindEdgeIndicateIfReverse(from, to, result); } + EdgeID FindSmallestEdge(const NodeID from, + const NodeID to, + std::function filter) const override final + { + return m_query_graph->FindSmallestEdge(from, to, filter); + } + // node and edge information access util::Coordinate GetCoordinateOfNode(const NodeID id) const override final { @@ -518,40 +526,113 @@ class SharedDataFacade final : public BaseDataFacade return m_osmnodeid_list.at(id); } - virtual void GetUncompressedGeometry(const EdgeID id, - std::vector &result_nodes) const override final + virtual std::vector GetUncompressedForwardGeometry(const EdgeID id) const override final { + /* + * NodeID's for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. For + * forward geometries of bi-directional edges, edges 2 to + * n of that edge need to be read. + */ const unsigned begin = m_geometry_indices.at(id); const unsigned end = m_geometry_indices.at(id + 1); - result_nodes.clear(); + std::vector result_nodes; + result_nodes.reserve(end - begin); + std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) { result_nodes.emplace_back(edge.node_id); }); + + return result_nodes; } - virtual void - GetUncompressedWeights(const EdgeID id, - std::vector &result_weights) const override final + virtual std::vector GetUncompressedReverseGeometry(const EdgeID id) const override final { - const unsigned begin = m_geometry_indices.at(id); + /* + * NodeID's for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. + * */ + const signed begin = m_geometry_indices.at(id); + const signed end = m_geometry_indices.at(id + 1); + + std::vector result_nodes; + + result_nodes.reserve(end - begin); + + std::for_each(m_geometry_list.rbegin() + (m_geometry_list.size() - end), + m_geometry_list.rbegin() + (m_geometry_list.size() - begin), + [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) { + result_nodes.emplace_back(edge.node_id); + }); + + return result_nodes; + } + + virtual std::vector + GetUncompressedForwardWeights(const EdgeID id) const override final + { + /* + * EdgeWeights's for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. + * */ + const unsigned begin = m_geometry_indices.at(id) + 1; const unsigned end = m_geometry_indices.at(id + 1); - result_weights.clear(); + std::vector result_weights; result_weights.reserve(end - begin); + std::for_each(m_geometry_list.begin() + begin, m_geometry_list.begin() + end, [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) { - result_weights.emplace_back(edge.weight); + result_weights.emplace_back(edge.forward_weight); }); + + return result_weights; } - virtual unsigned GetGeometryIndexForEdgeID(const unsigned id) const override final + virtual std::vector + GetUncompressedReverseWeights(const EdgeID id) const override final { - return m_via_node_list.at(id); + /* + * EdgeWeights for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. For + * reverse weights of bi-directional edges, edges 1 to + * n-1 of that edge need to be read in reverse. + */ + const signed begin = m_geometry_indices.at(id); + const signed end = m_geometry_indices.at(id + 1) - 1; + + std::vector result_weights; + result_weights.reserve(end - begin); + + std::for_each(m_geometry_list.rbegin() + (m_geometry_list.size() - end), + m_geometry_list.rbegin() + (m_geometry_list.size() - begin), + [&](const osrm::extractor::CompressedEdgeContainer::CompressedEdge &edge) { + result_weights.emplace_back(edge.reverse_weight); + }); + + return result_weights; + } + + virtual GeometryID GetGeometryIndexForEdgeID(const unsigned id) const override final + { + return m_via_geometry_list.at(id); } extractor::guidance::TurnInstruction @@ -707,12 +788,21 @@ class SharedDataFacade final : public BaseDataFacade return result; } + std::string GetRefForID(const unsigned name_id) const override final + { + // We store the ref after the name, destination and pronunciation of a street. + // We do this to get around the street length limit of 255 which would hit + // if we concatenate these. Order (see extractor_callbacks): + // name (0), destination (1), pronunciation (2), ref (3) + return GetNameForID(name_id + 3); + } + std::string GetPronunciationForID(const unsigned name_id) const override final { // We store the pronunciation after the name and destination of a street. // We do this to get around the street length limit of 255 which would hit // if we concatenate these. Order (see extractor_callbacks): - // name (0), destination (1), pronunciation (2) + // name (0), destination (1), pronunciation (2), ref (3) return GetNameForID(name_id + 2); } @@ -721,7 +811,7 @@ class SharedDataFacade final : public BaseDataFacade // We store the destination after the name of a street. // We do this to get around the street length limit of 255 which would hit // if we concatenate these. Order (see extractor_callbacks): - // name (0), destination (1), pronunciation (2) + // name (0), destination (1), pronunciation (2), ref (3) return GetNameForID(name_id + 1); } @@ -739,14 +829,22 @@ class SharedDataFacade final : public BaseDataFacade // Returns the data source ids that were used to supply the edge // weights. - virtual void - GetUncompressedDatasources(const EdgeID id, - std::vector &result_datasources) const override final - { - const unsigned begin = m_geometry_indices.at(id); + virtual std::vector + GetUncompressedForwardDatasources(const EdgeID id) const override final + { + /* + * Data sources for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. For + * forward datasources of bi-directional edges, edges 2 to + * n of that edge need to be read. + */ + const unsigned begin = m_geometry_indices.at(id) + 1; const unsigned end = m_geometry_indices.at(id + 1); - result_datasources.clear(); + std::vector result_datasources; result_datasources.reserve(end - begin); // If there was no datasource info, return an array of 0's. @@ -764,6 +862,47 @@ class SharedDataFacade final : public BaseDataFacade m_datasource_list.begin() + end, [&](const uint8_t &datasource_id) { result_datasources.push_back(datasource_id); }); } + + return result_datasources; + } + + // Returns the data source ids that were used to supply the edge + // weights. + virtual std::vector + GetUncompressedReverseDatasources(const EdgeID id) const override final + { + /* + * Datasources for geometries are stored in one place for + * both forward and reverse segments along the same bi- + * directional edge. The m_geometry_indices stores + * refences to where to find the beginning of the bi- + * directional edge in the m_geometry_list vector. For + * reverse datasources of bi-directional edges, edges 1 to + * n-1 of that edge need to be read in reverse. + */ + const unsigned begin = m_geometry_indices.at(id); + const unsigned end = m_geometry_indices.at(id + 1) - 1; + + std::vector result_datasources; + result_datasources.reserve(end - begin); + + // If there was no datasource info, return an array of 0's. + if (m_datasource_list.empty()) + { + for (unsigned i = 0; i < end - begin; ++i) + { + result_datasources.push_back(0); + } + } + else + { + std::for_each( + m_datasource_list.rbegin() + (m_datasource_list.size() - end), + m_datasource_list.rbegin() + (m_datasource_list.size() - begin), + [&](const uint8_t &datasource_id) { result_datasources.push_back(datasource_id); }); + } + + return result_datasources; } virtual std::string GetDatasourceName(const uint8_t datasource_name_id) const override final diff --git a/include/engine/edge_unpacker.hpp b/include/engine/edge_unpacker.hpp new file mode 100644 index 00000000000..b2de9074c33 --- /dev/null +++ b/include/engine/edge_unpacker.hpp @@ -0,0 +1,107 @@ +#ifndef EDGE_UNPACKER_H +#define EDGE_UNPACKER_H + +#include "extractor/guidance/turn_instruction.hpp" +#include "extractor/travel_mode.hpp" +#include "engine/phantom_node.hpp" +#include "osrm/coordinate.hpp" +#include "util/guidance/turn_lanes.hpp" +#include "util/typedefs.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ + +/** + * Given a sequence of connected `NodeID`s in the CH graph, performs a depth-first unpacking of + * the shortcut + * edges. For every "original" edge found, it calls the `callback` with the two NodeIDs for the + * edge, and the EdgeData + * for that edge. + * + * The primary purpose of this unpacking is to expand a path through the CH into the original + * route through the + * pre-contracted graph. + * + * Because of the depth-first-search, the `callback` will effectively be called in sequence for + * the original route + * from beginning to end. + * + * @param packed_path_begin iterator pointing to the start of the NodeID list + * @param packed_path_end iterator pointing to the end of the NodeID list + * @param callback void(const std::pair, const EdgeData &) called for each + * original edge found. + */ + +template +inline void UnpackCHPath(const DataFacadeT &facade, + BidirectionalIterator packed_path_begin, + BidirectionalIterator packed_path_end, + Callback &&callback) +{ + // make sure we have at least something to unpack + if (packed_path_begin == packed_path_end) + return; + + using EdgeData = typename DataFacadeT::EdgeData; + + std::stack> recursion_stack; + + // We have to push the path in reverse order onto the stack because it's LIFO. + for (auto current = std::prev(packed_path_end); current != packed_path_begin; + current = std::prev(current)) + { + recursion_stack.emplace(*std::prev(current), *current); + } + + std::pair edge; + while (!recursion_stack.empty()) + { + edge = recursion_stack.top(); + recursion_stack.pop(); + + // Look for an edge on the forward CH graph (.forward) + EdgeID smaller_edge_id = facade.FindSmallestEdge( + edge.first, edge.second, [](const EdgeData &data) { return data.forward; }); + + // If we didn't find one there, the we might be looking at a part of the path that + // was found using the backward search. Here, we flip the node order (.second, .first) + // and only consider edges with the `.backward` flag. + if (SPECIAL_EDGEID == smaller_edge_id) + { + smaller_edge_id = facade.FindSmallestEdge( + edge.second, edge.first, [](const EdgeData &data) { return data.backward; }); + } + + // If we didn't find anything *still*, then something is broken and someone has + // called this function with bad values. + BOOST_ASSERT_MSG(smaller_edge_id != SPECIAL_EDGEID, "Invalid smaller edge ID"); + + const auto &data = facade.GetEdgeData(smaller_edge_id); + BOOST_ASSERT_MSG(data.distance != std::numeric_limits::max(), + "edge weight invalid"); + + // If the edge is a shortcut, we need to add the two halfs to the stack. + if (data.shortcut) + { // unpack + const NodeID middle_node_id = data.id; + // Note the order here - we're adding these to a stack, so we + // want the first->middle to get visited before middle->second + recursion_stack.emplace(middle_node_id, edge.second); + recursion_stack.emplace(edge.first, middle_node_id); + } + else + { + // We found an original edge, call our callback. + std::forward(callback)(edge, data); + } + } +} +} +} + +#endif // EDGE_UNPACKER_H diff --git a/include/engine/engine.hpp b/include/engine/engine.hpp index 46cbf528968..5f7011273cb 100644 --- a/include/engine/engine.hpp +++ b/include/engine/engine.hpp @@ -57,7 +57,7 @@ class Engine final // Needs to be public struct EngineLock; - explicit Engine(EngineConfig &config); + explicit Engine(const EngineConfig &config); Engine(Engine &&) noexcept; Engine &operator=(Engine &&) noexcept; @@ -65,13 +65,13 @@ class Engine final // Impl. in cpp since for unique_ptr of incomplete types ~Engine(); - Status Route(const api::RouteParameters ¶meters, util::json::Object &result); - Status Table(const api::TableParameters ¶meters, util::json::Object &result); - Status Nearest(const api::NearestParameters ¶meters, util::json::Object &result); - Status Trip(const api::TripParameters ¶meters, util::json::Object &result); - Status Match(const api::MatchParameters ¶meters, util::json::Object &result); - Status Tile(const api::TileParameters ¶meters, std::string &result); - Status Isochrone(const api::IsochroneParameters ¶meters, util::json::Object &result); + Status Route(const api::RouteParameters ¶meters, util::json::Object &result) const; + Status Table(const api::TableParameters ¶meters, util::json::Object &result) const; + Status Nearest(const api::NearestParameters ¶meters, util::json::Object &result) const; + Status Trip(const api::TripParameters ¶meters, util::json::Object &result) const; + Status Match(const api::MatchParameters ¶meters, util::json::Object &result) const; + Status Tile(const api::TileParameters ¶meters, std::string &result) const; + Status Isochrone(const api::IsochroneParameters ¶meters, util::json::Object &result) const; private: std::unique_ptr lock; @@ -84,7 +84,7 @@ class Engine final std::unique_ptr tile_plugin; std::unique_ptr isochrone_plugin; - std::unique_ptr query_data_facade; + std::shared_ptr query_data_facade; }; } } diff --git a/include/engine/engine_config.hpp b/include/engine/engine_config.hpp index 1c1c945cce4..c545f0bf5fd 100644 --- a/include/engine/engine_config.hpp +++ b/include/engine/engine_config.hpp @@ -51,6 +51,7 @@ namespace engine * - Route * - Table * - Match + * - Nearest * * In addition, shared memory can be used for datasets loaded with osrm-datastore. * @@ -65,6 +66,7 @@ struct EngineConfig final int max_locations_viaroute = -1; int max_locations_distance_table = -1; int max_locations_map_matching = -1; + int max_results_nearest = -1; bool use_shared_memory = true; bool use_isochrone = true; }; diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp index 1ff56500fc1..c00a2a11ea1 100644 --- a/include/engine/geospatial_query.hpp +++ b/include/engine/geospatial_query.hpp @@ -372,35 +372,25 @@ template class GeospatialQuery int forward_offset = 0, forward_weight = 0; int reverse_offset = 0, reverse_weight = 0; - if (data.forward_packed_geometry_id != SPECIAL_EDGEID) + const std::vector forward_weight_vector = datafacade.GetUncompressedForwardWeights(data.packed_geometry_id); + const std::vector reverse_weight_vector = datafacade.GetUncompressedReverseWeights(data.packed_geometry_id); + + for (std::size_t i = 0; i < data.fwd_segment_position; i++) { - std::vector forward_weight_vector; - datafacade.GetUncompressedWeights(data.forward_packed_geometry_id, - forward_weight_vector); - for (std::size_t i = 0; i < data.fwd_segment_position; i++) - { - forward_offset += forward_weight_vector[i]; - } - forward_weight = forward_weight_vector[data.fwd_segment_position]; + forward_offset += forward_weight_vector[i]; } + forward_weight = forward_weight_vector[data.fwd_segment_position]; + + BOOST_ASSERT(data.fwd_segment_position < reverse_weight_vector.size()); - if (data.reverse_packed_geometry_id != SPECIAL_EDGEID) + for (std::size_t i = 0; + i < reverse_weight_vector.size() - data.fwd_segment_position - 1; + i++) { - std::vector reverse_weight_vector; - datafacade.GetUncompressedWeights(data.reverse_packed_geometry_id, - reverse_weight_vector); - - BOOST_ASSERT(data.fwd_segment_position < reverse_weight_vector.size()); - - for (std::size_t i = 0; - i < reverse_weight_vector.size() - data.fwd_segment_position - 1; - i++) - { - reverse_offset += reverse_weight_vector[i]; - } - reverse_weight = - reverse_weight_vector[reverse_weight_vector.size() - data.fwd_segment_position - 1]; + reverse_offset += reverse_weight_vector[i]; } + reverse_weight = + reverse_weight_vector[reverse_weight_vector.size() - data.fwd_segment_position - 1]; ratio = std::min(1.0, std::max(0.0, ratio)); if (data.forward_segment_id.id != SPECIAL_SEGMENTID) @@ -479,31 +469,18 @@ template class GeospatialQuery bool forward_edge_valid = false; bool reverse_edge_valid = false; - if (segment.data.forward_packed_geometry_id != SPECIAL_EDGEID) + const std::vector forward_weight_vector = datafacade.GetUncompressedForwardWeights(segment.data.packed_geometry_id); + + if (forward_weight_vector[segment.data.fwd_segment_position] != INVALID_EDGE_WEIGHT) { - std::vector forward_weight_vector; - datafacade.GetUncompressedWeights(segment.data.forward_packed_geometry_id, - forward_weight_vector); - - if (forward_weight_vector[segment.data.fwd_segment_position] != INVALID_EDGE_WEIGHT) - { - forward_edge_valid = segment.data.forward_segment_id.enabled; - } + forward_edge_valid = segment.data.forward_segment_id.enabled; } - if (segment.data.reverse_packed_geometry_id != SPECIAL_EDGEID) + const std::vector reverse_weight_vector = datafacade.GetUncompressedReverseWeights(segment.data.packed_geometry_id); + if (reverse_weight_vector[reverse_weight_vector.size() - + segment.data.fwd_segment_position - 1] != INVALID_EDGE_WEIGHT) { - std::vector reverse_weight_vector; - datafacade.GetUncompressedWeights(segment.data.reverse_packed_geometry_id, - reverse_weight_vector); - - BOOST_ASSERT(segment.data.fwd_segment_position < reverse_weight_vector.size()); - - if (reverse_weight_vector[reverse_weight_vector.size() - - segment.data.fwd_segment_position - 1] != INVALID_EDGE_WEIGHT) - { - reverse_edge_valid = segment.data.reverse_segment_id.enabled; - } + reverse_edge_valid = segment.data.reverse_segment_id.enabled; } return std::make_pair(forward_edge_valid, reverse_edge_valid); diff --git a/include/engine/guidance/assemble_geometry.hpp b/include/engine/guidance/assemble_geometry.hpp index fb9e8fa43b6..09372c9b435 100644 --- a/include/engine/guidance/assemble_geometry.hpp +++ b/include/engine/guidance/assemble_geometry.hpp @@ -45,14 +45,10 @@ inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, // Need to get the node ID preceding the source phantom node // TODO: check if this was traversed in reverse? - std::vector reverse_geometry; - facade.GetUncompressedGeometry(source_node.reverse_packed_geometry_id, reverse_geometry); + const std::vector source_geometry = + facade.GetUncompressedForwardGeometry(source_node.packed_geometry_id); geometry.osm_node_ids.push_back(facade.GetOSMNodeIDOfNode( - reverse_geometry[reverse_geometry.size() - source_node.fwd_segment_position - 1])); - - std::vector forward_datasource_vector; - facade.GetUncompressedDatasources(source_node.forward_packed_geometry_id, - forward_datasource_vector); + source_geometry[source_node.fwd_segment_position])); auto cumulative_distance = 0.; auto current_distance = 0.; @@ -84,8 +80,8 @@ inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, // segment leading to the target node geometry.segment_distances.push_back(cumulative_distance); - std::vector forward_datasources; - facade.GetUncompressedDatasources(target_node.forward_packed_geometry_id, forward_datasources); + const std::vector forward_datasources = + facade.GetUncompressedForwardDatasources(target_node.packed_geometry_id); geometry.annotations.emplace_back( LegGeometry::Annotation{current_distance, @@ -96,10 +92,10 @@ inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, // Need to get the node ID following the destination phantom node // TODO: check if this was traversed in reverse?? - std::vector forward_geometry; - facade.GetUncompressedGeometry(target_node.forward_packed_geometry_id, forward_geometry); + const std::vector target_geometry = + facade.GetUncompressedForwardGeometry(target_node.packed_geometry_id); geometry.osm_node_ids.push_back( - facade.GetOSMNodeIDOfNode(forward_geometry[target_node.fwd_segment_position])); + facade.GetOSMNodeIDOfNode(target_geometry[target_node.fwd_segment_position + 1])); BOOST_ASSERT(geometry.segment_distances.size() == geometry.segment_offsets.size() - 1); BOOST_ASSERT(geometry.locations.size() > geometry.segment_distances.size()); diff --git a/include/engine/guidance/assemble_leg.hpp b/include/engine/guidance/assemble_leg.hpp index fe8832dc272..f2f8ad92d45 100644 --- a/include/engine/guidance/assemble_leg.hpp +++ b/include/engine/guidance/assemble_leg.hpp @@ -6,6 +6,11 @@ #include "engine/guidance/route_leg.hpp" #include "engine/guidance/route_step.hpp" #include "engine/internal_route_result.hpp" +#include "util/typedefs.hpp" + +#include +#include +#include #include #include @@ -107,7 +112,7 @@ std::array summarizeRoute(const std::vector summary; - std::fill(summary.begin(), summary.end(), 0); + std::fill(summary.begin(), summary.end(), EMPTY_NAMEID); std::transform(segments.begin(), segments.end(), summary.begin(), @@ -172,21 +177,28 @@ inline RouteLeg assembleLeg(const datafacade::BaseDataFacade &facade, { auto summary_array = detail::summarizeRoute( route_data, target_node, target_traversed_in_reverse); - if (route_data.empty()) - summary_array[0] = source_node.name_id; BOOST_ASSERT(detail::MAX_USED_SEGMENTS > 0); BOOST_ASSERT(summary_array.begin() != summary_array.end()); - summary = std::accumulate(std::next(summary_array.begin()), - summary_array.end(), - facade.GetNameForID(summary_array.front()), - [&facade](std::string previous, const std::uint32_t name_id) { - if (name_id != 0) - { - previous += ", " + facade.GetNameForID(name_id); - } - return previous; - }); + + // transform a name_id into a string containing either the name, or -if the name is empty- + // the reference. + const auto name_id_to_string = [&](const NameID name_id) { + const auto name = facade.GetNameForID(name_id); + if (!name.empty()) + return name; + else + { + const auto ref = facade.GetRefForID(name_id); + return ref; + } + }; + + const auto not_empty = [&](const std::string &name) { return !name.empty(); }; + + const auto summary_names = summary_array | boost::adaptors::transformed(name_id_to_string) | + boost::adaptors::filtered(not_empty); + summary = boost::algorithm::join(summary_names, ", "); } return RouteLeg{duration, distance, summary, {}}; diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp index c930dab5d3c..116312f220c 100644 --- a/include/engine/guidance/assemble_steps.hpp +++ b/include/engine/guidance/assemble_steps.hpp @@ -103,12 +103,14 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa { BOOST_ASSERT(segment_duration >= 0); const auto name = facade.GetNameForID(step_name_id); + const auto ref = facade.GetRefForID(step_name_id); const auto pronunciation = facade.GetPronunciationForID(step_name_id); const auto destinations = facade.GetDestinationsForID(step_name_id); const auto distance = leg_geometry.segment_distances[segment_index]; steps.push_back(RouteStep{step_name_id, std::move(name), + std::move(ref), std::move(pronunciation), std::move(destinations), NO_ROTARY_NAME, @@ -168,6 +170,7 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa BOOST_ASSERT(duration >= 0); steps.push_back(RouteStep{step_name_id, facade.GetNameForID(step_name_id), + facade.GetRefForID(step_name_id), facade.GetPronunciationForID(step_name_id), facade.GetDestinationsForID(step_name_id), NO_ROTARY_NAME, @@ -194,6 +197,7 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa steps.push_back(RouteStep{source_node.name_id, facade.GetNameForID(source_node.name_id), + facade.GetRefForID(source_node.name_id), facade.GetPronunciationForID(source_node.name_id), facade.GetDestinationsForID(source_node.name_id), NO_ROTARY_NAME, @@ -229,6 +233,7 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa BOOST_ASSERT(!leg_geometry.locations.empty()); steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id), + facade.GetRefForID(target_node.name_id), facade.GetPronunciationForID(target_node.name_id), facade.GetDestinationsForID(target_node.name_id), NO_ROTARY_NAME, diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp index b33694c0c1f..f7203214fa1 100644 --- a/include/engine/guidance/post_processing.hpp +++ b/include/engine/guidance/post_processing.hpp @@ -1,7 +1,6 @@ #ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP #define ENGINE_GUIDANCE_POST_PROCESSING_HPP -#include "engine/datafacade/datafacade_base.hpp" #include "engine/guidance/leg_geometry.hpp" #include "engine/guidance/route_step.hpp" #include "engine/phantom_node.hpp" diff --git a/include/engine/guidance/route_step.hpp b/include/engine/guidance/route_step.hpp index 6435137a700..5e15c8a667d 100644 --- a/include/engine/guidance/route_step.hpp +++ b/include/engine/guidance/route_step.hpp @@ -25,10 +25,10 @@ namespace guidance // a --> b --> c // this struct saves the information of the segment b,c. // Notable exceptions are Departure and Arrival steps. -// Departue: s --> a --> b. Represents the segment s,a with location being s. +// Departure: s --> a --> b. Represents the segment s,a with location being s. // Arrive: a --> b --> t. The segment (b,t) is already covered by the previous segment. -// A represenetation of intermediate intersections +// A representation of intermediate intersections struct Intersection { static const constexpr std::size_t NO_INDEX = std::numeric_limits::max(); @@ -58,6 +58,7 @@ struct RouteStep { unsigned name_id; std::string name; + std::string ref; std::string pronunciation; std::string destinations; std::string rotary_name; @@ -80,6 +81,7 @@ inline RouteStep getInvalidRouteStep() "", "", "", + "", 0, 0, TRAVEL_MODE_INACCESSIBLE, diff --git a/include/engine/hint.hpp b/include/engine/hint.hpp index a5fd860b64c..0819cb7effd 100644 --- a/include/engine/hint.hpp +++ b/include/engine/hint.hpp @@ -63,8 +63,8 @@ struct Hint friend std::ostream &operator<<(std::ostream &, const Hint &); }; -static_assert(sizeof(Hint) == 60 + 4, "Hint is bigger than expected"); -constexpr std::size_t ENCODED_HINT_SIZE = 88; +static_assert(sizeof(Hint) == 56 + 4, "Hint is bigger than expected"); +constexpr std::size_t ENCODED_HINT_SIZE = 80; static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint), "ENCODED_HINT_SIZE does not match size of Hint"); } diff --git a/include/engine/phantom_node.hpp b/include/engine/phantom_node.hpp index df8454a9d6b..ca0a9698d56 100644 --- a/include/engine/phantom_node.hpp +++ b/include/engine/phantom_node.hpp @@ -53,8 +53,7 @@ struct PhantomNode int reverse_weight, int forward_offset, int reverse_offset, - unsigned forward_packed_geometry_id_, - unsigned reverse_packed_geometry_id_, + unsigned packed_geometry_id_, bool is_tiny_component, unsigned component_id, util::Coordinate location, @@ -65,8 +64,7 @@ struct PhantomNode : forward_segment_id(forward_segment_id), reverse_segment_id(reverse_segment_id), name_id(name_id), forward_weight(forward_weight), reverse_weight(reverse_weight), forward_offset(forward_offset), reverse_offset(reverse_offset), - forward_packed_geometry_id(forward_packed_geometry_id_), - reverse_packed_geometry_id(reverse_packed_geometry_id_), + packed_geometry_id(packed_geometry_id_), component{component_id, is_tiny_component}, location(std::move(location)), input_location(std::move(input_location)), fwd_segment_position(fwd_segment_position), forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode) @@ -78,7 +76,7 @@ struct PhantomNode reverse_segment_id{SPECIAL_SEGMENTID, false}, name_id(std::numeric_limits::max()), forward_weight(INVALID_EDGE_WEIGHT), reverse_weight(INVALID_EDGE_WEIGHT), forward_offset(0), reverse_offset(0), - forward_packed_geometry_id(SPECIAL_EDGEID), reverse_packed_geometry_id(SPECIAL_EDGEID), + packed_geometry_id(SPECIAL_GEOMETRYID), component{INVALID_COMPONENTID, false}, fwd_segment_position(0), forward_travel_mode(TRAVEL_MODE_INACCESSIBLE), backward_travel_mode(TRAVEL_MODE_INACCESSIBLE) @@ -129,8 +127,7 @@ struct PhantomNode reverse_segment_id{other.reverse_segment_id}, name_id{other.name_id}, forward_weight{forward_weight_}, reverse_weight{reverse_weight_}, forward_offset{forward_offset_}, reverse_offset{reverse_offset_}, - forward_packed_geometry_id{other.forward_packed_geometry_id}, - reverse_packed_geometry_id{other.reverse_packed_geometry_id}, + packed_geometry_id{other.packed_geometry_id}, component{other.component.id, other.component.is_tiny}, location{location_}, input_location{input_location_}, fwd_segment_position{other.fwd_segment_position}, forward_travel_mode{other.forward_travel_mode}, @@ -145,8 +142,7 @@ struct PhantomNode int reverse_weight; int forward_offset; int reverse_offset; - unsigned forward_packed_geometry_id; - unsigned reverse_packed_geometry_id; + unsigned packed_geometry_id; struct ComponentType { std::uint32_t id : 31; @@ -163,7 +159,7 @@ struct PhantomNode extractor::TravelMode backward_travel_mode; }; -static_assert(sizeof(PhantomNode) == 60, "PhantomNode has more padding then expected"); +static_assert(sizeof(PhantomNode) == 56, "PhantomNode has more padding then expected"); using PhantomNodePair = std::pair; @@ -195,8 +191,7 @@ inline std::ostream &operator<<(std::ostream &out, const PhantomNode &pn) << "rev-w: " << pn.reverse_weight << ", " << "fwd-o: " << pn.forward_offset << ", " << "rev-o: " << pn.reverse_offset << ", " - << "fwd_geom: " << pn.forward_packed_geometry_id << ", " - << "rev_geom: " << pn.reverse_packed_geometry_id << ", " + << "geom: " << pn.packed_geometry_id << ", " << "comp: " << pn.component.is_tiny << " / " << pn.component.id << ", " << "pos: " << pn.fwd_segment_position << ", " << "loc: " << pn.location; diff --git a/include/engine/plugins/isochrone.hpp b/include/engine/plugins/isochrone.hpp index f4d7ebbaf25..0698ea40039 100644 --- a/include/engine/plugins/isochrone.hpp +++ b/include/engine/plugins/isochrone.hpp @@ -86,9 +86,11 @@ class IsochronePlugin final : public BasePlugin void update(IsochroneVector &s, IsochroneNode node); public: - explicit IsochronePlugin(datafacade::BaseDataFacade &facade, const std::string base); + explicit IsochronePlugin(const std::string base); - Status HandleRequest(const api::IsochroneParameters ¶ms, util::json::Object &json_result); + Status HandleRequest(const std::shared_ptr facade, + const api::IsochroneParameters ¶ms, + util::json::Object &json_result); }; } } diff --git a/include/engine/plugins/match.hpp b/include/engine/plugins/match.hpp index 333439531c2..264eb66fca5 100644 --- a/include/engine/plugins/match.hpp +++ b/include/engine/plugins/match.hpp @@ -27,13 +27,15 @@ class MatchPlugin : public BasePlugin static const constexpr double DEFAULT_GPS_PRECISION = 5; static const constexpr double RADIUS_MULTIPLIER = 3; - MatchPlugin(datafacade::BaseDataFacade &facade_, const int max_locations_map_matching) - : BasePlugin(facade_), map_matching(&facade_, heaps, DEFAULT_GPS_PRECISION), - shortest_path(&facade_, heaps), max_locations_map_matching(max_locations_map_matching) + MatchPlugin(const int max_locations_map_matching) + : map_matching(heaps, DEFAULT_GPS_PRECISION), shortest_path(heaps), + max_locations_map_matching(max_locations_map_matching) { } - Status HandleRequest(const api::MatchParameters ¶meters, util::json::Object &json_result); + Status HandleRequest(const std::shared_ptr facade, + const api::MatchParameters ¶meters, + util::json::Object &json_result); private: SearchEngineData heaps; diff --git a/include/engine/plugins/nearest.hpp b/include/engine/plugins/nearest.hpp index 657aec9284b..22290344e85 100644 --- a/include/engine/plugins/nearest.hpp +++ b/include/engine/plugins/nearest.hpp @@ -15,9 +15,14 @@ namespace plugins class NearestPlugin final : public BasePlugin { public: - explicit NearestPlugin(datafacade::BaseDataFacade &facade); + explicit NearestPlugin(const int max_results); - Status HandleRequest(const api::NearestParameters ¶ms, util::json::Object &result); + Status HandleRequest(const std::shared_ptr facade, + const api::NearestParameters ¶ms, + util::json::Object &result) const; + + private: + const int max_results; }; } } diff --git a/include/engine/plugins/plugin_base.hpp b/include/engine/plugins/plugin_base.hpp index 8b5a8706e1d..f008b2f04ee 100644 --- a/include/engine/plugins/plugin_base.hpp +++ b/include/engine/plugins/plugin_base.hpp @@ -26,10 +26,7 @@ namespace plugins class BasePlugin { protected: - datafacade::BaseDataFacade &facade; - BasePlugin(datafacade::BaseDataFacade &facade_) : facade(facade_) {} - - bool CheckAllCoordinates(const std::vector &coordinates) + bool CheckAllCoordinates(const std::vector &coordinates) const { return !std::any_of( std::begin(coordinates), std::end(coordinates), [](const util::Coordinate coordinate) { @@ -111,7 +108,8 @@ class BasePlugin // Falls back to default_radius for non-set radii std::vector> - GetPhantomNodesInRange(const api::BaseParameters ¶meters, + GetPhantomNodesInRange(const datafacade::BaseDataFacade &facade, + const api::BaseParameters ¶meters, const std::vector radiuses) const { std::vector> phantom_nodes( @@ -152,7 +150,9 @@ class BasePlugin } std::vector> - GetPhantomNodes(const api::BaseParameters ¶meters, unsigned number_of_results) + GetPhantomNodes(const datafacade::BaseDataFacade &facade, + const api::BaseParameters ¶meters, + unsigned number_of_results) const { std::vector> phantom_nodes( parameters.coordinates.size()); @@ -216,7 +216,8 @@ class BasePlugin return phantom_nodes; } - std::vector GetPhantomNodes(const api::BaseParameters ¶meters) + std::vector GetPhantomNodes(const datafacade::BaseDataFacade &facade, + const api::BaseParameters ¶meters) const { std::vector phantom_node_pairs(parameters.coordinates.size()); diff --git a/include/engine/plugins/table.hpp b/include/engine/plugins/table.hpp index 09f9761aece..e6daaa711d4 100644 --- a/include/engine/plugins/table.hpp +++ b/include/engine/plugins/table.hpp @@ -18,10 +18,11 @@ namespace plugins class TablePlugin final : public BasePlugin { public: - explicit TablePlugin(datafacade::BaseDataFacade &facade, - const int max_locations_distance_table); + explicit TablePlugin(const int max_locations_distance_table); - Status HandleRequest(const api::TableParameters ¶ms, util::json::Object &result); + Status HandleRequest(const std::shared_ptr facade, + const api::TableParameters ¶ms, + util::json::Object &result); private: SearchEngineData heaps; diff --git a/include/engine/plugins/tile.hpp b/include/engine/plugins/tile.hpp index d154eb5119a..1a84452fb81 100644 --- a/include/engine/plugins/tile.hpp +++ b/include/engine/plugins/tile.hpp @@ -3,6 +3,8 @@ #include "engine/api/tile_parameters.hpp" #include "engine/plugins/plugin_base.hpp" +#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/routing_algorithms/shortest_path.hpp" #include @@ -24,9 +26,9 @@ namespace plugins class TilePlugin final : public BasePlugin { public: - TilePlugin(datafacade::BaseDataFacade &facade) : BasePlugin(facade) {} - - Status HandleRequest(const api::TileParameters ¶meters, std::string &pbf_buffer); + Status HandleRequest(const std::shared_ptr facade, + const api::TileParameters ¶meters, + std::string &pbf_buffer) const; }; } } diff --git a/include/engine/plugins/trip.hpp b/include/engine/plugins/trip.hpp index 6135284d3f7..7abc3eb9fc8 100644 --- a/include/engine/plugins/trip.hpp +++ b/include/engine/plugins/trip.hpp @@ -34,17 +34,19 @@ class TripPlugin final : public BasePlugin routing_algorithms::ManyToManyRouting duration_table; int max_locations_trip; - InternalRouteResult ComputeRoute(const std::vector &phantom_node_list, + InternalRouteResult ComputeRoute(const datafacade::BaseDataFacade &facade, + const std::vector &phantom_node_list, const std::vector &trip); public: - explicit TripPlugin(datafacade::BaseDataFacade &facade_, const int max_locations_trip_) - : BasePlugin(facade_), shortest_path(&facade_, heaps), duration_table(&facade_, heaps), - max_locations_trip(max_locations_trip_) + explicit TripPlugin(const int max_locations_trip_) + : shortest_path(heaps), duration_table(heaps), max_locations_trip(max_locations_trip_) { } - Status HandleRequest(const api::TripParameters ¶meters, util::json::Object &json_result); + Status HandleRequest(const std::shared_ptr facade, + const api::TripParameters ¶meters, + util::json::Object &json_result); }; } } diff --git a/include/engine/plugins/viaroute.hpp b/include/engine/plugins/viaroute.hpp index cd45b49aaf7..6b4e4e51959 100644 --- a/include/engine/plugins/viaroute.hpp +++ b/include/engine/plugins/viaroute.hpp @@ -35,9 +35,10 @@ class ViaRoutePlugin final : public BasePlugin int max_locations_viaroute; public: - explicit ViaRoutePlugin(datafacade::BaseDataFacade &facade, int max_locations_viaroute); + explicit ViaRoutePlugin(int max_locations_viaroute); - Status HandleRequest(const api::RouteParameters &route_parameters, + Status HandleRequest(const std::shared_ptr facade, + const api::RouteParameters &route_parameters, util::json::Object &json_result); }; } diff --git a/include/engine/routing_algorithms/alternative_path.hpp b/include/engine/routing_algorithms/alternative_path.hpp index 8bffd260e05..a00c6a69596 100644 --- a/include/engine/routing_algorithms/alternative_path.hpp +++ b/include/engine/routing_algorithms/alternative_path.hpp @@ -50,18 +50,19 @@ class AlternativeRouting final return (2 * length + sharing) < (2 * other.length + other.sharing); } }; - DataFacadeT *facade; SearchEngineData &engine_working_data; public: - AlternativeRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) - : super(facade), facade(facade), engine_working_data(engine_working_data) + AlternativeRouting(SearchEngineData &engine_working_data) + : engine_working_data(engine_working_data) { } virtual ~AlternativeRouting() {} - void operator()(const PhantomNodes &phantom_node_pair, InternalRouteResult &raw_route_data) + void operator()(const DataFacadeT &facade, + const PhantomNodes &phantom_node_pair, + InternalRouteResult &raw_route_data) { std::vector alternative_path; std::vector via_node_candidate_list; @@ -69,12 +70,9 @@ class AlternativeRouting final std::vector reverse_search_space; // Init queues, semi-expensive because access to TSS invokes a sys-call - engine_working_data.InitializeOrClearFirstThreadLocalStorage( - super::facade->GetNumberOfNodes()); - engine_working_data.InitializeOrClearSecondThreadLocalStorage( - super::facade->GetNumberOfNodes()); - engine_working_data.InitializeOrClearThirdThreadLocalStorage( - super::facade->GetNumberOfNodes()); + engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes()); + engine_working_data.InitializeOrClearSecondThreadLocalStorage(facade.GetNumberOfNodes()); + engine_working_data.InitializeOrClearThirdThreadLocalStorage(facade.GetNumberOfNodes()); QueryHeap &forward_heap1 = *(engine_working_data.forward_heap_1); QueryHeap &reverse_heap1 = *(engine_working_data.reverse_heap_1); @@ -130,7 +128,8 @@ class AlternativeRouting final { if (0 < forward_heap1.Size()) { - AlternativeRoutingStep(forward_heap1, + AlternativeRoutingStep(facade, + forward_heap1, reverse_heap1, &middle_node, &upper_bound_to_shortest_path_distance, @@ -140,7 +139,8 @@ class AlternativeRouting final } if (0 < reverse_heap1.Size()) { - AlternativeRoutingStep(forward_heap1, + AlternativeRoutingStep(facade, + forward_heap1, reverse_heap1, &middle_node, &upper_bound_to_shortest_path_distance, @@ -286,7 +286,8 @@ class AlternativeRouting final for (const NodeID node : preselected_node_list) { int length_of_via_path = 0, sharing_of_via_path = 0; - ComputeLengthAndSharingOfViaPath(node, + ComputeLengthAndSharingOfViaPath(facade, + node, &length_of_via_path, &sharing_of_via_path, packed_shortest_path, @@ -306,7 +307,8 @@ class AlternativeRouting final NodeID s_v_middle = SPECIAL_NODEID, v_t_middle = SPECIAL_NODEID; for (const RankedCandidateNode &candidate : ranked_candidates_list) { - if (ViaNodeCandidatePassesTTest(forward_heap1, + if (ViaNodeCandidatePassesTTest(facade, + forward_heap1, reverse_heap1, forward_heap2, reverse_heap2, @@ -335,14 +337,14 @@ class AlternativeRouting final (packed_shortest_path.back() != phantom_node_pair.target_phantom.forward_segment_id.id)); - super::UnpackPath( - // -- packed input - packed_shortest_path.begin(), - packed_shortest_path.end(), - // -- start of route - phantom_node_pair, - // -- unpacked output - raw_route_data.unpacked_path_segments.front()); + super::UnpackPath(facade, + // -- packed input + packed_shortest_path.begin(), + packed_shortest_path.end(), + // -- start of route + phantom_node_pair, + // -- unpacked output + raw_route_data.unpacked_path_segments.front()); raw_route_data.shortest_path_length = upper_bound_to_shortest_path_distance; } @@ -366,7 +368,8 @@ class AlternativeRouting final phantom_node_pair.target_phantom.forward_segment_id.id)); // unpack the alternate path - super::UnpackPath(packed_alternate_path.begin(), + super::UnpackPath(facade, + packed_alternate_path.begin(), packed_alternate_path.end(), phantom_node_pair, raw_route_data.unpacked_alternative); @@ -405,14 +408,14 @@ class AlternativeRouting final // compute and unpack and by exploring search spaces // from v and intersecting against queues. only half-searches have to be // done at this stage - void ComputeLengthAndSharingOfViaPath(const NodeID via_node, + void ComputeLengthAndSharingOfViaPath(const DataFacadeT &facade, + const NodeID via_node, int *real_length_of_via_path, int *sharing_of_via_path, const std::vector &packed_shortest_path, const EdgeWeight min_edge_offset) { - engine_working_data.InitializeOrClearSecondThreadLocalStorage( - super::facade->GetNumberOfNodes()); + engine_working_data.InitializeOrClearSecondThreadLocalStorage(facade.GetNumberOfNodes()); QueryHeap &existing_forward_heap = *engine_working_data.forward_heap_1; QueryHeap &existing_reverse_heap = *engine_working_data.reverse_heap_1; @@ -433,7 +436,8 @@ class AlternativeRouting final const bool constexpr DO_NOT_FORCE_LOOPS = false; while (!new_reverse_heap.Empty()) { - super::RoutingStep(new_reverse_heap, + super::RoutingStep(facade, + new_reverse_heap, existing_forward_heap, s_v_middle, upper_bound_s_v_path_length, @@ -449,7 +453,8 @@ class AlternativeRouting final new_forward_heap.Insert(via_node, 0, via_node); while (!new_forward_heap.Empty()) { - super::RoutingStep(new_forward_heap, + super::RoutingStep(facade, + new_forward_heap, existing_reverse_heap, v_t_middle, upper_bound_of_v_t_path_length, @@ -481,18 +486,20 @@ class AlternativeRouting final if (packed_s_v_path[current_node] == packed_shortest_path[current_node] && packed_s_v_path[current_node + 1] == packed_shortest_path[current_node + 1]) { - EdgeID edgeID = facade->FindEdgeInEitherDirection( - packed_s_v_path[current_node], packed_s_v_path[current_node + 1]); - *sharing_of_via_path += facade->GetEdgeData(edgeID).distance; + EdgeID edgeID = facade.FindEdgeInEitherDirection(packed_s_v_path[current_node], + packed_s_v_path[current_node + 1]); + *sharing_of_via_path += facade.GetEdgeData(edgeID).distance; } else { if (packed_s_v_path[current_node] == packed_shortest_path[current_node]) { - super::UnpackEdge(packed_s_v_path[current_node], + super::UnpackEdge(facade, + packed_s_v_path[current_node], packed_s_v_path[current_node + 1], partially_unpacked_via_path); - super::UnpackEdge(packed_shortest_path[current_node], + super::UnpackEdge(facade, + packed_shortest_path[current_node], packed_shortest_path[current_node + 1], partially_unpacked_shortest_path); break; @@ -512,9 +519,9 @@ class AlternativeRouting final ++current_node) { EdgeID selected_edge = - facade->FindEdgeInEitherDirection(partially_unpacked_via_path[current_node], - partially_unpacked_via_path[current_node + 1]); - *sharing_of_via_path += facade->GetEdgeData(selected_edge).distance; + facade.FindEdgeInEitherDirection(partially_unpacked_via_path[current_node], + partially_unpacked_via_path[current_node + 1]); + *sharing_of_via_path += facade.GetEdgeData(selected_edge).distance; } // Second, partially unpack v-->t in reverse order until paths deviate and note lengths @@ -527,18 +534,20 @@ class AlternativeRouting final packed_shortest_path[shortest_path_index - 1] && packed_v_t_path[via_path_index] == packed_shortest_path[shortest_path_index]) { - EdgeID edgeID = facade->FindEdgeInEitherDirection( + EdgeID edgeID = facade.FindEdgeInEitherDirection( packed_v_t_path[via_path_index - 1], packed_v_t_path[via_path_index]); - *sharing_of_via_path += facade->GetEdgeData(edgeID).distance; + *sharing_of_via_path += facade.GetEdgeData(edgeID).distance; } else { if (packed_v_t_path[via_path_index] == packed_shortest_path[shortest_path_index]) { - super::UnpackEdge(packed_v_t_path[via_path_index - 1], + super::UnpackEdge(facade, + packed_v_t_path[via_path_index - 1], packed_v_t_path[via_path_index], partially_unpacked_via_path); - super::UnpackEdge(packed_shortest_path[shortest_path_index - 1], + super::UnpackEdge(facade, + packed_shortest_path[shortest_path_index - 1], packed_shortest_path[shortest_path_index], partially_unpacked_shortest_path); break; @@ -556,10 +565,10 @@ class AlternativeRouting final partially_unpacked_via_path[via_path_index] == partially_unpacked_shortest_path[shortest_path_index]) { - EdgeID edgeID = facade->FindEdgeInEitherDirection( + EdgeID edgeID = facade.FindEdgeInEitherDirection( partially_unpacked_via_path[via_path_index - 1], partially_unpacked_via_path[via_path_index]); - *sharing_of_via_path += facade->GetEdgeData(edgeID).distance; + *sharing_of_via_path += facade.GetEdgeData(edgeID).distance; } else { @@ -617,7 +626,8 @@ class AlternativeRouting final // todo: reorder parameters template - void AlternativeRoutingStep(QueryHeap &heap1, + void AlternativeRoutingStep(const DataFacadeT &facade, + QueryHeap &heap1, QueryHeap &heap2, NodeID *middle_node, int *upper_bound_to_shortest_path_distance, @@ -667,7 +677,7 @@ class AlternativeRouting final else { // check whether there is a loop present at the node - const auto loop_distance = super::GetLoopWeight(node); + const auto loop_distance = super::GetLoopWeight(facade, node); const int new_distance_with_loop = new_distance + loop_distance; if (loop_distance != INVALID_EDGE_WEIGHT && new_distance_with_loop <= *upper_bound_to_shortest_path_distance) @@ -679,15 +689,15 @@ class AlternativeRouting final } } - for (auto edge : facade->GetAdjacentEdgeRange(node)) + for (auto edge : facade.GetAdjacentEdgeRange(node)) { - const EdgeData &data = facade->GetEdgeData(edge); + const EdgeData &data = facade.GetEdgeData(edge); const bool edge_is_forward_directed = (is_forward_directed ? data.forward : data.backward); if (edge_is_forward_directed) { - const NodeID to = facade->GetTarget(edge); + const NodeID to = facade.GetTarget(edge); const int edge_weight = data.distance; BOOST_ASSERT(edge_weight > 0); @@ -711,7 +721,8 @@ class AlternativeRouting final } // conduct T-Test - bool ViaNodeCandidatePassesTTest(QueryHeap &existing_forward_heap, + bool ViaNodeCandidatePassesTTest(const DataFacadeT &facade, + QueryHeap &existing_forward_heap, QueryHeap &existing_reverse_heap, QueryHeap &new_forward_heap, QueryHeap &new_reverse_heap, @@ -735,7 +746,8 @@ class AlternativeRouting final const bool constexpr DO_NOT_FORCE_LOOPS = false; while (new_reverse_heap.Size() > 0) { - super::RoutingStep(new_reverse_heap, + super::RoutingStep(facade, + new_reverse_heap, existing_forward_heap, *s_v_middle, upper_bound_s_v_path_length, @@ -757,7 +769,8 @@ class AlternativeRouting final new_forward_heap.Insert(candidate.node, 0, candidate.node); while (new_forward_heap.Size() > 0) { - super::RoutingStep(new_forward_heap, + super::RoutingStep(facade, + new_forward_heap, existing_reverse_heap, *v_t_middle, upper_bound_of_v_t_path_length, @@ -800,8 +813,8 @@ class AlternativeRouting final for (std::size_t i = packed_s_v_path.size() - 1; (i > 0) && unpack_stack.empty(); --i) { const EdgeID current_edge_id = - facade->FindEdgeInEitherDirection(packed_s_v_path[i - 1], packed_s_v_path[i]); - const int length_of_current_edge = facade->GetEdgeData(current_edge_id).distance; + facade.FindEdgeInEitherDirection(packed_s_v_path[i - 1], packed_s_v_path[i]); + const int length_of_current_edge = facade.GetEdgeData(current_edge_id).distance; if ((length_of_current_edge + unpacked_until_distance) >= T_threshold) { unpack_stack.emplace(packed_s_v_path[i - 1], packed_s_v_path[i]); @@ -818,22 +831,22 @@ class AlternativeRouting final const SearchSpaceEdge via_path_edge = unpack_stack.top(); unpack_stack.pop(); EdgeID edge_in_via_path_id = - facade->FindEdgeInEitherDirection(via_path_edge.first, via_path_edge.second); + facade.FindEdgeInEitherDirection(via_path_edge.first, via_path_edge.second); if (SPECIAL_EDGEID == edge_in_via_path_id) { return false; } - const EdgeData ¤t_edge_data = facade->GetEdgeData(edge_in_via_path_id); + const EdgeData ¤t_edge_data = facade.GetEdgeData(edge_in_via_path_id); const bool current_edge_is_shortcut = current_edge_data.shortcut; if (current_edge_is_shortcut) { const NodeID via_path_middle_node_id = current_edge_data.id; - const EdgeID second_segment_edge_id = facade->FindEdgeInEitherDirection( - via_path_middle_node_id, via_path_edge.second); + const EdgeID second_segment_edge_id = + facade.FindEdgeInEitherDirection(via_path_middle_node_id, via_path_edge.second); const int second_segment_length = - facade->GetEdgeData(second_segment_edge_id).distance; + facade.GetEdgeData(second_segment_edge_id).distance; // attention: !unpacking in reverse! // Check if second segment is the one to go over treshold? if yes add second segment // to stack, else push first segment to stack and add distance of second one. @@ -864,8 +877,8 @@ class AlternativeRouting final ++i) { const EdgeID edgeID = - facade->FindEdgeInEitherDirection(packed_v_t_path[i], packed_v_t_path[i + 1]); - int length_of_current_edge = facade->GetEdgeData(edgeID).distance; + facade.FindEdgeInEitherDirection(packed_v_t_path[i], packed_v_t_path[i + 1]); + int length_of_current_edge = facade.GetEdgeData(edgeID).distance; if (length_of_current_edge + unpacked_until_distance >= T_threshold) { unpack_stack.emplace(packed_v_t_path[i], packed_v_t_path[i + 1]); @@ -882,20 +895,20 @@ class AlternativeRouting final const SearchSpaceEdge via_path_edge = unpack_stack.top(); unpack_stack.pop(); EdgeID edge_in_via_path_id = - facade->FindEdgeInEitherDirection(via_path_edge.first, via_path_edge.second); + facade.FindEdgeInEitherDirection(via_path_edge.first, via_path_edge.second); if (SPECIAL_EDGEID == edge_in_via_path_id) { return false; } - const EdgeData ¤t_edge_data = facade->GetEdgeData(edge_in_via_path_id); + const EdgeData ¤t_edge_data = facade.GetEdgeData(edge_in_via_path_id); const bool IsViaEdgeShortCut = current_edge_data.shortcut; if (IsViaEdgeShortCut) { const NodeID middleOfViaPath = current_edge_data.id; EdgeID edgeIDOfFirstSegment = - facade->FindEdgeInEitherDirection(via_path_edge.first, middleOfViaPath); - int lengthOfFirstSegment = facade->GetEdgeData(edgeIDOfFirstSegment).distance; + facade.FindEdgeInEitherDirection(via_path_edge.first, middleOfViaPath); + int lengthOfFirstSegment = facade.GetEdgeData(edgeIDOfFirstSegment).distance; // Check if first segment is the one to go over treshold? if yes first segment to // stack, else push second segment to stack and add distance of first one. if (unpacked_until_distance + lengthOfFirstSegment >= T_threshold) @@ -918,8 +931,7 @@ class AlternativeRouting final t_test_path_length += unpacked_until_distance; // Run actual T-Test query and compare if distances equal. - engine_working_data.InitializeOrClearThirdThreadLocalStorage( - super::facade->GetNumberOfNodes()); + engine_working_data.InitializeOrClearThirdThreadLocalStorage(facade.GetNumberOfNodes()); QueryHeap &forward_heap3 = *engine_working_data.forward_heap_3; QueryHeap &reverse_heap3 = *engine_working_data.reverse_heap_3; @@ -933,7 +945,8 @@ class AlternativeRouting final { if (!forward_heap3.Empty()) { - super::RoutingStep(forward_heap3, + super::RoutingStep(facade, + forward_heap3, reverse_heap3, middle, upper_bound, @@ -945,7 +958,8 @@ class AlternativeRouting final } if (!reverse_heap3.Empty()) { - super::RoutingStep(reverse_heap3, + super::RoutingStep(facade, + reverse_heap3, forward_heap3, middle, upper_bound, diff --git a/include/engine/routing_algorithms/direct_shortest_path.hpp b/include/engine/routing_algorithms/direct_shortest_path.hpp index 5b18eca64a2..881ea25f919 100644 --- a/include/engine/routing_algorithms/direct_shortest_path.hpp +++ b/include/engine/routing_algorithms/direct_shortest_path.hpp @@ -32,14 +32,15 @@ class DirectShortestPathRouting final SearchEngineData &engine_working_data; public: - DirectShortestPathRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) - : super(facade), engine_working_data(engine_working_data) + DirectShortestPathRouting(SearchEngineData &engine_working_data) + : engine_working_data(engine_working_data) { } ~DirectShortestPathRouting() {} - void operator()(const std::vector &phantom_nodes_vector, + void operator()(const DataFacadeT &facade, + const std::vector &phantom_nodes_vector, InternalRouteResult &raw_route_data) const { // Get distance to next pair of target nodes. @@ -50,8 +51,7 @@ class DirectShortestPathRouting final const auto &source_phantom = phantom_node_pair.source_phantom; const auto &target_phantom = phantom_node_pair.target_phantom; - engine_working_data.InitializeOrClearFirstThreadLocalStorage( - super::facade->GetNumberOfNodes()); + engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes()); QueryHeap &forward_heap = *(engine_working_data.forward_heap_1); QueryHeap &reverse_heap = *(engine_working_data.reverse_heap_1); forward_heap.Clear(); @@ -93,16 +93,17 @@ class DirectShortestPathRouting final const bool constexpr DO_NOT_FORCE_LOOPS = false; // prevents forcing of loops, since offsets are set correctly - if (super::facade->GetCoreSize() > 0) + if (facade.GetCoreSize() > 0) { engine_working_data.InitializeOrClearSecondThreadLocalStorage( - super::facade->GetNumberOfNodes()); + facade.GetNumberOfNodes()); QueryHeap &forward_core_heap = *(engine_working_data.forward_heap_2); QueryHeap &reverse_core_heap = *(engine_working_data.reverse_heap_2); forward_core_heap.Clear(); reverse_core_heap.Clear(); - super::SearchWithCore(forward_heap, + super::SearchWithCore(facade, + forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, @@ -113,7 +114,8 @@ class DirectShortestPathRouting final } else { - super::Search(forward_heap, + super::Search(facade, + forward_heap, reverse_heap, distance, packed_leg, @@ -138,7 +140,8 @@ class DirectShortestPathRouting final raw_route_data.target_traversed_in_reverse.push_back( (packed_leg.back() != phantom_node_pair.target_phantom.forward_segment_id.id)); - super::UnpackPath(packed_leg.begin(), + super::UnpackPath(facade, + packed_leg.begin(), packed_leg.end(), phantom_node_pair, raw_route_data.unpacked_path_segments.front()); diff --git a/include/engine/routing_algorithms/many_to_many.hpp b/include/engine/routing_algorithms/many_to_many.hpp index 6897197db93..668e75bbf6a 100644 --- a/include/engine/routing_algorithms/many_to_many.hpp +++ b/include/engine/routing_algorithms/many_to_many.hpp @@ -41,12 +41,13 @@ class ManyToManyRouting final using SearchSpaceWithBuckets = std::unordered_map>; public: - ManyToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) - : super(facade), engine_working_data(engine_working_data) + ManyToManyRouting(SearchEngineData &engine_working_data) + : engine_working_data(engine_working_data) { } - std::vector operator()(const std::vector &phantom_nodes, + std::vector operator()(const DataFacadeT &facade, + const std::vector &phantom_nodes, const std::vector &source_indices, const std::vector &target_indices) const { @@ -58,8 +59,7 @@ class ManyToManyRouting final std::vector result_table(number_of_entries, std::numeric_limits::max()); - engine_working_data.InitializeOrClearFirstThreadLocalStorage( - super::facade->GetNumberOfNodes()); + engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes()); QueryHeap &query_heap = *(engine_working_data.forward_heap_1); @@ -86,7 +86,7 @@ class ManyToManyRouting final // explore search space while (!query_heap.Empty()) { - BackwardRoutingStep(column_idx, query_heap, search_space_with_buckets); + BackwardRoutingStep(facade, column_idx, query_heap, search_space_with_buckets); } ++column_idx; }; @@ -113,7 +113,8 @@ class ManyToManyRouting final // explore search space while (!query_heap.Empty()) { - ForwardRoutingStep(row_idx, + ForwardRoutingStep(facade, + row_idx, number_of_targets, query_heap, search_space_with_buckets, @@ -157,7 +158,8 @@ class ManyToManyRouting final return result_table; } - void ForwardRoutingStep(const unsigned row_idx, + void ForwardRoutingStep(const DataFacadeT &facade, + const unsigned row_idx, const unsigned number_of_targets, QueryHeap &query_heap, const SearchSpaceWithBuckets &search_space_with_buckets, @@ -182,7 +184,7 @@ class ManyToManyRouting final const EdgeWeight new_distance = source_distance + target_distance; if (new_distance < 0) { - const EdgeWeight loop_weight = super::GetLoopWeight(node); + const EdgeWeight loop_weight = super::GetLoopWeight(facade, node); const int new_distance_with_loop = new_distance + loop_weight; if (loop_weight != INVALID_EDGE_WEIGHT && new_distance_with_loop >= 0) { @@ -195,14 +197,15 @@ class ManyToManyRouting final } } } - if (StallAtNode(node, source_distance, query_heap)) + if (StallAtNode(facade, node, source_distance, query_heap)) { return; } - RelaxOutgoingEdges(node, source_distance, query_heap); + RelaxOutgoingEdges(facade, node, source_distance, query_heap); } - void BackwardRoutingStep(const unsigned column_idx, + void BackwardRoutingStep(const DataFacadeT &facade, + const unsigned column_idx, QueryHeap &query_heap, SearchSpaceWithBuckets &search_space_with_buckets) const { @@ -212,25 +215,27 @@ class ManyToManyRouting final // store settled nodes in search space bucket search_space_with_buckets[node].emplace_back(column_idx, target_distance); - if (StallAtNode(node, target_distance, query_heap)) + if (StallAtNode(facade, node, target_distance, query_heap)) { return; } - RelaxOutgoingEdges(node, target_distance, query_heap); + RelaxOutgoingEdges(facade, node, target_distance, query_heap); } template - inline void - RelaxOutgoingEdges(const NodeID node, const EdgeWeight distance, QueryHeap &query_heap) const + inline void RelaxOutgoingEdges(const DataFacadeT &facade, + const NodeID node, + const EdgeWeight distance, + QueryHeap &query_heap) const { - for (auto edge : super::facade->GetAdjacentEdgeRange(node)) + for (auto edge : facade.GetAdjacentEdgeRange(node)) { - const auto &data = super::facade->GetEdgeData(edge); + const auto &data = facade.GetEdgeData(edge); const bool direction_flag = (forward_direction ? data.forward : data.backward); if (direction_flag) { - const NodeID to = super::facade->GetTarget(edge); + const NodeID to = facade.GetTarget(edge); const int edge_weight = data.distance; BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); @@ -254,16 +259,18 @@ class ManyToManyRouting final // Stalling template - inline bool - StallAtNode(const NodeID node, const EdgeWeight distance, QueryHeap &query_heap) const + inline bool StallAtNode(const DataFacadeT &facade, + const NodeID node, + const EdgeWeight distance, + QueryHeap &query_heap) const { - for (auto edge : super::facade->GetAdjacentEdgeRange(node)) + for (auto edge : facade.GetAdjacentEdgeRange(node)) { - const auto &data = super::facade->GetEdgeData(edge); + const auto &data = facade.GetEdgeData(edge); const bool reverse_flag = ((!forward_direction) ? data.forward : data.backward); if (reverse_flag) { - const NodeID to = super::facade->GetTarget(edge); + const NodeID to = facade.GetTarget(edge); const int edge_weight = data.distance; BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); if (query_heap.WasInserted(to)) diff --git a/include/engine/routing_algorithms/map_matching.hpp b/include/engine/routing_algorithms/map_matching.hpp index 343e26ab874..331da6a56f1 100644 --- a/include/engine/routing_algorithms/map_matching.hpp +++ b/include/engine/routing_algorithms/map_matching.hpp @@ -9,7 +9,6 @@ #include "util/coordinate_calculation.hpp" #include "util/for_each_pair.hpp" -#include "util/json_logger.hpp" #include @@ -64,17 +63,16 @@ class MapMatching final : public BasicRoutingInterface &trace_coordinates, const std::vector &trace_timestamps, const std::vector> &trace_gps_precision) const @@ -159,10 +157,8 @@ class MapMatching final : public BasicRoutingInterfaceGetNumberOfNodes()); - engine_working_data.InitializeOrClearSecondThreadLocalStorage( - super::facade->GetNumberOfNodes()); + engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes()); + engine_working_data.InitializeOrClearSecondThreadLocalStorage(facade.GetNumberOfNodes()); QueryHeap &forward_heap = *(engine_working_data.forward_heap_1); QueryHeap &reverse_heap = *(engine_working_data.reverse_heap_1); @@ -262,11 +258,12 @@ class MapMatching final : public BasicRoutingInterfaceGetCoreSize() > 0) + if (facade.GetCoreSize() > 0) { forward_core_heap.Clear(); reverse_core_heap.Clear(); network_distance = super::GetNetworkDistanceWithCore( + facade, forward_heap, reverse_heap, forward_core_heap, @@ -278,6 +275,7 @@ class MapMatching final : public BasicRoutingInterface class BasicRoutingInterface private: using EdgeData = typename DataFacadeT::EdgeData; - protected: - DataFacadeT *facade; - public: - explicit BasicRoutingInterface(DataFacadeT *facade) : facade(facade) {} - ~BasicRoutingInterface() {} - - BasicRoutingInterface(const BasicRoutingInterface &) = delete; - BasicRoutingInterface &operator=(const BasicRoutingInterface &) = delete; - /* min_edge_offset is needed in case we use multiple nodes as start/target nodes with different (even negative) offsets. @@ -71,7 +63,8 @@ template class BasicRoutingInterface Since we are dealing with a graph that contains _negative_ edges, we need to add an offset to the termination criterion. */ - void RoutingStep(SearchEngineData::QueryHeap &forward_heap, + void RoutingStep(const DataFacadeT &facade, + SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, NodeID &middle_node_id, std::int32_t &upper_bound, @@ -97,14 +90,14 @@ template class BasicRoutingInterface new_distance < 0) { // check whether there is a loop present at the node - for (const auto edge : facade->GetAdjacentEdgeRange(node)) + for (const auto edge : facade.GetAdjacentEdgeRange(node)) { - const EdgeData &data = facade->GetEdgeData(edge); + const EdgeData &data = facade.GetEdgeData(edge); bool forward_directionFlag = (forward_direction ? data.forward : data.backward); if (forward_directionFlag) { - const NodeID to = facade->GetTarget(edge); + const NodeID to = facade.GetTarget(edge); if (to == node) { const EdgeWeight edge_weight = data.distance; @@ -140,13 +133,13 @@ template class BasicRoutingInterface // Stalling if (stalling) { - for (const auto edge : facade->GetAdjacentEdgeRange(node)) + for (const auto edge : facade.GetAdjacentEdgeRange(node)) { - const EdgeData &data = facade->GetEdgeData(edge); + const EdgeData &data = facade.GetEdgeData(edge); const bool reverse_flag = ((!forward_direction) ? data.forward : data.backward); if (reverse_flag) { - const NodeID to = facade->GetTarget(edge); + const NodeID to = facade.GetTarget(edge); const EdgeWeight edge_weight = data.distance; BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); @@ -162,14 +155,14 @@ template class BasicRoutingInterface } } - for (const auto edge : facade->GetAdjacentEdgeRange(node)) + for (const auto edge : facade.GetAdjacentEdgeRange(node)) { - const EdgeData &data = facade->GetEdgeData(edge); + const EdgeData &data = facade.GetEdgeData(edge); bool forward_directionFlag = (forward_direction ? data.forward : data.backward); if (forward_directionFlag) { - const NodeID to = facade->GetTarget(edge); + const NodeID to = facade.GetTarget(edge); const EdgeWeight edge_weight = data.distance; BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); @@ -191,15 +184,15 @@ template class BasicRoutingInterface } } - inline EdgeWeight GetLoopWeight(NodeID node) const + inline EdgeWeight GetLoopWeight(const DataFacadeT &facade, NodeID node) const { EdgeWeight loop_weight = INVALID_EDGE_WEIGHT; - for (auto edge : facade->GetAdjacentEdgeRange(node)) + for (auto edge : facade.GetAdjacentEdgeRange(node)) { - const auto &data = facade->GetEdgeData(edge); + const auto &data = facade.GetEdgeData(edge); if (data.forward) { - const NodeID to = facade->GetTarget(edge); + const NodeID to = facade.GetTarget(edge); if (to == node) { loop_weight = std::min(loop_weight, data.distance); @@ -210,7 +203,8 @@ template class BasicRoutingInterface } template - void UnpackPath(RandomIter packed_path_begin, + void UnpackPath(const DataFacadeT &facade, + RandomIter packed_path_begin, RandomIter packed_path_end, const PhantomNodes &phantom_node_pair, std::vector &unpacked_path) const @@ -221,14 +215,6 @@ template class BasicRoutingInterface (*std::prev(packed_path_end) != phantom_node_pair.target_phantom.forward_segment_id.id); BOOST_ASSERT(std::distance(packed_path_begin, packed_path_end) > 0); - std::stack> recursion_stack; - - // We have to push the path in reverse order onto the stack because it's LIFO. - for (auto current = std::prev(packed_path_end); current != packed_path_begin; - current = std::prev(current)) - { - recursion_stack.emplace(*std::prev(current), *current); - } BOOST_ASSERT(*packed_path_begin == phantom_node_pair.source_phantom.forward_segment_id.id || *packed_path_begin == phantom_node_pair.source_phantom.reverse_segment_id.id); @@ -236,143 +222,112 @@ template class BasicRoutingInterface *std::prev(packed_path_end) == phantom_node_pair.target_phantom.forward_segment_id.id || *std::prev(packed_path_end) == phantom_node_pair.target_phantom.reverse_segment_id.id); - std::pair edge; - while (!recursion_stack.empty()) - { - // edge.first edge.second - // *------------------>* - // edge_id - edge = recursion_stack.top(); - recursion_stack.pop(); - - // Contraction might introduce double edges by inserting shortcuts - // this searching for the smallest upwards edge found by the forward search - EdgeID smaller_edge_id = SPECIAL_EDGEID; - EdgeWeight edge_weight = std::numeric_limits::max(); - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) - { - const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).forward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - - // edge.first edge.second - // *<------------------* - // edge_id - // if we don't find a forward edge, this edge must have been an downwards edge - // found by the reverse search. - if (SPECIAL_EDGEID == smaller_edge_id) - { - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) - { - const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).backward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - } - BOOST_ASSERT_MSG(edge_weight != INVALID_EDGE_WEIGHT, "edge id invalid"); - - const EdgeData &ed = facade->GetEdgeData(smaller_edge_id); - if (ed.shortcut) - { // unpack - const NodeID middle_node_id = ed.id; - // again, we need to this in reversed order - recursion_stack.emplace(middle_node_id, edge.second); - recursion_stack.emplace(edge.first, middle_node_id); - } - else - { - BOOST_ASSERT_MSG(!ed.shortcut, "original edge flagged as shortcut"); - unsigned name_index = facade->GetNameIndexFromEdgeID(ed.id); - const auto turn_instruction = facade->GetTurnInstructionForEdgeID(ed.id); + UnpackCHPath( + facade, + packed_path_begin, + packed_path_end, + [this, + &facade, + &unpacked_path, + &phantom_node_pair, + &start_traversed_in_reverse, + &target_traversed_in_reverse](std::pair & /* edge */, + const EdgeData &edge_data) { + + BOOST_ASSERT_MSG(!edge_data.shortcut, "original edge flagged as shortcut"); + const auto name_index = facade.GetNameIndexFromEdgeID(edge_data.id); + const auto turn_instruction = facade.GetTurnInstructionForEdgeID(edge_data.id); const extractor::TravelMode travel_mode = (unpacked_path.empty() && start_traversed_in_reverse) ? phantom_node_pair.source_phantom.backward_travel_mode - : facade->GetTravelModeForEdgeID(ed.id); + : facade.GetTravelModeForEdgeID(edge_data.id); - const auto geometry_index = facade->GetGeometryIndexForEdgeID(ed.id); + const auto geometry_index = facade.GetGeometryIndexForEdgeID(edge_data.id); std::vector id_vector; - facade->GetUncompressedGeometry(geometry_index, id_vector); - BOOST_ASSERT(id_vector.size() > 0); - std::vector weight_vector; - facade->GetUncompressedWeights(geometry_index, weight_vector); - BOOST_ASSERT(weight_vector.size() > 0); - std::vector datasource_vector; - facade->GetUncompressedDatasources(geometry_index, datasource_vector); - - auto total_weight = std::accumulate(weight_vector.begin(), weight_vector.end(), 0); - - BOOST_ASSERT(weight_vector.size() == id_vector.size()); - const bool is_first_segment = unpacked_path.empty(); - - const std::size_t start_index = - (is_first_segment - ? ((start_traversed_in_reverse) - ? id_vector.size() - - phantom_node_pair.source_phantom.fwd_segment_position - 1 - : phantom_node_pair.source_phantom.fwd_segment_position) - : 0); - const std::size_t end_index = id_vector.size(); - - BOOST_ASSERT(start_index >= 0); - BOOST_ASSERT(start_index < end_index); - for (std::size_t i = start_index; i < end_index; ++i) + if (geometry_index.forward) { - unpacked_path.push_back( - PathData{id_vector[i], - name_index, - weight_vector[i], - extractor::guidance::TurnInstruction::NO_TURN(), - {{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID}, - travel_mode, - INVALID_ENTRY_CLASSID, - datasource_vector[i]}); + id_vector = facade.GetUncompressedForwardGeometry(geometry_index.id); + weight_vector = facade.GetUncompressedForwardWeights(geometry_index.id); + datasource_vector = + facade.GetUncompressedForwardDatasources(geometry_index.id); } - BOOST_ASSERT(unpacked_path.size() > 0); - if (facade->hasLaneData(ed.id)) - unpacked_path.back().lane_data = facade->GetLaneData(ed.id); - - unpacked_path.back().entry_classid = facade->GetEntryClassID(ed.id); - unpacked_path.back().turn_instruction = turn_instruction; - unpacked_path.back().duration_until_turn += (ed.distance - total_weight); - } - } - std::size_t start_index = 0, end_index = 0; - std::vector id_vector; - std::vector weight_vector; - std::vector datasource_vector; - const bool is_local_path = (phantom_node_pair.source_phantom.forward_packed_geometry_id == - phantom_node_pair.target_phantom.forward_packed_geometry_id) && - unpacked_path.empty(); - - if (target_traversed_in_reverse) - { - facade->GetUncompressedGeometry( - phantom_node_pair.target_phantom.reverse_packed_geometry_id, id_vector); - - facade->GetUncompressedWeights( - phantom_node_pair.target_phantom.reverse_packed_geometry_id, weight_vector); - - facade->GetUncompressedDatasources( - phantom_node_pair.target_phantom.reverse_packed_geometry_id, datasource_vector); + else + { + id_vector = facade.GetUncompressedReverseGeometry(geometry_index.id); + weight_vector = facade.GetUncompressedReverseWeights(geometry_index.id); + datasource_vector = + facade.GetUncompressedReverseDatasources(geometry_index.id); + } + BOOST_ASSERT(id_vector.size() > 0); + BOOST_ASSERT(weight_vector.size() > 0); + BOOST_ASSERT(datasource_vector.size() > 0); + + const auto total_weight = + std::accumulate(weight_vector.begin(), weight_vector.end(), 0); + + BOOST_ASSERT(weight_vector.size() == id_vector.size() - 1); + const bool is_first_segment = unpacked_path.empty(); + + const std::size_t start_index = + (is_first_segment + ? ((start_traversed_in_reverse) + ? weight_vector.size() - + phantom_node_pair.source_phantom.fwd_segment_position - 1 + : phantom_node_pair.source_phantom.fwd_segment_position) + : 0); + const std::size_t end_index = weight_vector.size(); + + BOOST_ASSERT(start_index >= 0); + BOOST_ASSERT(start_index < end_index); + for (std::size_t segment_idx = start_index; segment_idx < end_index; ++segment_idx) + { + unpacked_path.push_back( + PathData{id_vector[segment_idx + 1], + name_index, + weight_vector[segment_idx], + extractor::guidance::TurnInstruction::NO_TURN(), + {{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID}, + travel_mode, + INVALID_ENTRY_CLASSID, + datasource_vector[segment_idx]}); + } + BOOST_ASSERT(unpacked_path.size() > 0); + if (facade.hasLaneData(edge_data.id)) + unpacked_path.back().lane_data = facade.GetLaneData(edge_data.id); + + unpacked_path.back().entry_classid = facade.GetEntryClassID(edge_data.id); + unpacked_path.back().turn_instruction = turn_instruction; + unpacked_path.back().duration_until_turn += (edge_data.distance - total_weight); + }); + + std::size_t start_index = 0, end_index = 0; + std::vector id_vector; + std::vector weight_vector; + std::vector datasource_vector; + const bool is_local_path = (phantom_node_pair.source_phantom.packed_geometry_id == + phantom_node_pair.target_phantom.packed_geometry_id) && + unpacked_path.empty(); + + if (target_traversed_in_reverse) + { + id_vector = facade.GetUncompressedReverseGeometry( + phantom_node_pair.target_phantom.packed_geometry_id); + + weight_vector = facade.GetUncompressedReverseWeights( + phantom_node_pair.target_phantom.packed_geometry_id); + + datasource_vector = facade.GetUncompressedReverseDatasources( + phantom_node_pair.target_phantom.packed_geometry_id); if (is_local_path) { start_index = - id_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position - 1; + weight_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position - 1; } end_index = - id_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position - 1; + weight_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position - 1; } else { @@ -381,14 +336,15 @@ template class BasicRoutingInterface start_index = phantom_node_pair.source_phantom.fwd_segment_position; } end_index = phantom_node_pair.target_phantom.fwd_segment_position; - facade->GetUncompressedGeometry( - phantom_node_pair.target_phantom.forward_packed_geometry_id, id_vector); - facade->GetUncompressedWeights( - phantom_node_pair.target_phantom.forward_packed_geometry_id, weight_vector); + id_vector = facade.GetUncompressedForwardGeometry( + phantom_node_pair.target_phantom.packed_geometry_id); + + weight_vector = facade.GetUncompressedForwardWeights( + phantom_node_pair.target_phantom.packed_geometry_id); - facade->GetUncompressedDatasources( - phantom_node_pair.target_phantom.forward_packed_geometry_id, datasource_vector); + datasource_vector = facade.GetUncompressedForwardDatasources( + phantom_node_pair.target_phantom.packed_geometry_id); } // Given the following compressed geometry: @@ -398,20 +354,20 @@ template class BasicRoutingInterface // t: fwd_segment 3 // -> (U, v), (v, w), (w, x) // note that (x, t) is _not_ included but needs to be added later. - for (std::size_t i = start_index; i != end_index; (start_index < end_index ? ++i : --i)) + for (std::size_t segment_idx = start_index; segment_idx != end_index; (start_index < end_index ? ++segment_idx : --segment_idx)) { - BOOST_ASSERT(i < id_vector.size()); + BOOST_ASSERT(segment_idx < id_vector.size() - 1); BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0); unpacked_path.push_back(PathData{ - id_vector[i], + id_vector[start_index < end_index ? segment_idx + 1 : segment_idx - 1], phantom_node_pair.target_phantom.name_id, - weight_vector[i], + weight_vector[segment_idx], extractor::guidance::TurnInstruction::NO_TURN(), {{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID}, target_traversed_in_reverse ? phantom_node_pair.target_phantom.backward_travel_mode : phantom_node_pair.target_phantom.forward_travel_mode, INVALID_ENTRY_CLASSID, - datasource_vector[i]}); + datasource_vector[segment_idx]}); } if (unpacked_path.size() > 0) @@ -455,61 +411,27 @@ template class BasicRoutingInterface } } - void UnpackEdge(const NodeID s, const NodeID t, std::vector &unpacked_path) const + /** + * Unpacks a single edge (NodeID->NodeID) from the CH graph down to it's original non-shortcut + * route. + * @param from the node the CH edge starts at + * @param to the node the CH edge finishes at + * @param unpacked_path the sequence of original NodeIDs that make up the expanded CH edge + */ + void UnpackEdge(const DataFacadeT &facade, + const NodeID from, + const NodeID to, + std::vector &unpacked_path) const { - std::stack> recursion_stack; - recursion_stack.emplace(s, t); - - std::pair edge; - while (!recursion_stack.empty()) - { - edge = recursion_stack.top(); - recursion_stack.pop(); - - EdgeID smaller_edge_id = SPECIAL_EDGEID; - EdgeWeight edge_weight = std::numeric_limits::max(); - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first)) - { - const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).forward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - - if (SPECIAL_EDGEID == smaller_edge_id) - { - for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second)) - { - const EdgeWeight weight = facade->GetEdgeData(edge_id).distance; - if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) && - facade->GetEdgeData(edge_id).backward) - { - smaller_edge_id = edge_id; - edge_weight = weight; - } - } - } - BOOST_ASSERT_MSG(edge_weight != std::numeric_limits::max(), - "edge weight invalid"); - - const EdgeData &ed = facade->GetEdgeData(smaller_edge_id); - if (ed.shortcut) - { // unpack - const NodeID middle_node_id = ed.id; - // again, we need to this in reversed order - recursion_stack.emplace(middle_node_id, edge.second); - recursion_stack.emplace(edge.first, middle_node_id); - } - else - { - BOOST_ASSERT_MSG(!ed.shortcut, "edge must be shortcut"); + std::array path{{from, to}}; + UnpackCHPath( + facade, + path.begin(), + path.end(), + [&unpacked_path](const std::pair &edge, const EdgeData & /* data */) { unpacked_path.emplace_back(edge.first); - } - } - unpacked_path.emplace_back(t); + }); + unpacked_path.emplace_back(to); } void RetrievePackedPathFromHeap(const SearchEngineData::QueryHeap &forward_heap, @@ -552,7 +474,8 @@ template class BasicRoutingInterface // && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) // requires // a force loop, if the heaps have been initialized with positive offsets. - void Search(SearchEngineData::QueryHeap &forward_heap, + void Search(const DataFacadeT &facade, + SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, std::int32_t &distance, std::vector &packed_leg, @@ -575,7 +498,8 @@ template class BasicRoutingInterface { if (!forward_heap.Empty()) { - RoutingStep(forward_heap, + RoutingStep(facade, + forward_heap, reverse_heap, middle, distance, @@ -587,7 +511,8 @@ template class BasicRoutingInterface } if (!reverse_heap.Empty()) { - RoutingStep(reverse_heap, + RoutingStep(facade, + reverse_heap, forward_heap, middle, distance, @@ -632,7 +557,8 @@ template class BasicRoutingInterface // && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) // requires // a force loop, if the heaps have been initialized with positive offsets. - void SearchWithCore(SearchEngineData::QueryHeap &forward_heap, + void SearchWithCore(const DataFacadeT &facade, + SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, SearchEngineData::QueryHeap &forward_core_heap, SearchEngineData::QueryHeap &reverse_core_heap, @@ -660,7 +586,7 @@ template class BasicRoutingInterface { if (!forward_heap.Empty()) { - if (facade->IsCoreNode(forward_heap.Min())) + if (facade.IsCoreNode(forward_heap.Min())) { const NodeID node = forward_heap.DeleteMin(); const int key = forward_heap.GetKey(node); @@ -668,7 +594,8 @@ template class BasicRoutingInterface } else { - RoutingStep(forward_heap, + RoutingStep(facade, + forward_heap, reverse_heap, middle, distance, @@ -681,7 +608,7 @@ template class BasicRoutingInterface } if (!reverse_heap.Empty()) { - if (facade->IsCoreNode(reverse_heap.Min())) + if (facade.IsCoreNode(reverse_heap.Min())) { const NodeID node = reverse_heap.DeleteMin(); const int key = reverse_heap.GetKey(node); @@ -689,7 +616,8 @@ template class BasicRoutingInterface } else { - RoutingStep(reverse_heap, + RoutingStep(facade, + reverse_heap, forward_heap, middle, distance, @@ -741,7 +669,8 @@ template class BasicRoutingInterface while (0 < forward_core_heap.Size() && 0 < reverse_core_heap.Size() && distance > (forward_core_heap.MinKey() + reverse_core_heap.MinKey())) { - RoutingStep(forward_core_heap, + RoutingStep(facade, + forward_core_heap, reverse_core_heap, middle, distance, @@ -751,7 +680,8 @@ template class BasicRoutingInterface force_loop_forward, force_loop_reverse); - RoutingStep(reverse_core_heap, + RoutingStep(facade, + reverse_core_heap, forward_core_heap, middle, distance, @@ -774,7 +704,7 @@ template class BasicRoutingInterface "no path found"); // we need to unpack sub path from core heaps - if (facade->IsCoreNode(middle)) + if (facade.IsCoreNode(middle)) { if (distance != forward_core_heap.GetKey(middle) + reverse_core_heap.GetKey(middle)) { @@ -833,7 +763,8 @@ template class BasicRoutingInterface target_phantom.GetReverseWeightPlusOffset(); } - double GetPathDistance(const std::vector &packed_path, + double GetPathDistance(const DataFacadeT &facade, + const std::vector &packed_path, const PhantomNode &source_phantom, const PhantomNode &target_phantom) const { @@ -841,7 +772,7 @@ template class BasicRoutingInterface PhantomNodes nodes; nodes.source_phantom = source_phantom; nodes.target_phantom = target_phantom; - UnpackPath(packed_path.begin(), packed_path.end(), nodes, unpacked_path); + UnpackPath(facade, packed_path.begin(), packed_path.end(), nodes, unpacked_path); using util::coordinate_calculation::detail::DEGREE_TO_RAD; using util::coordinate_calculation::detail::EARTH_RADIUS; @@ -854,7 +785,7 @@ template class BasicRoutingInterface double prev_cos = std::cos(prev_lat); for (const auto &p : unpacked_path) { - const auto current_coordinate = facade->GetCoordinateOfNode(p.turn_via_node); + const auto current_coordinate = facade.GetCoordinateOfNode(p.turn_via_node); const double current_lat = static_cast(toFloating(current_coordinate.lat)) * DEGREE_TO_RAD; @@ -893,7 +824,8 @@ template class BasicRoutingInterface // Requires the heaps for be empty // If heaps should be adjusted to be initialized outside of this function, // the addition of force_loop parameters might be required - double GetNetworkDistanceWithCore(SearchEngineData::QueryHeap &forward_heap, + double GetNetworkDistanceWithCore(const DataFacadeT &facade, + SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, SearchEngineData::QueryHeap &forward_core_heap, SearchEngineData::QueryHeap &reverse_core_heap, @@ -935,7 +867,8 @@ template class BasicRoutingInterface int duration = INVALID_EDGE_WEIGHT; std::vector packed_path; - SearchWithCore(forward_heap, + SearchWithCore(facade, + forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, @@ -948,7 +881,7 @@ template class BasicRoutingInterface double distance = std::numeric_limits::max(); if (duration != INVALID_EDGE_WEIGHT) { - return GetPathDistance(packed_path, source_phantom, target_phantom); + return GetPathDistance(facade, packed_path, source_phantom, target_phantom); } return distance; } @@ -956,7 +889,8 @@ template class BasicRoutingInterface // Requires the heaps for be empty // If heaps should be adjusted to be initialized outside of this function, // the addition of force_loop parameters might be required - double GetNetworkDistance(SearchEngineData::QueryHeap &forward_heap, + double GetNetworkDistance(const DataFacadeT &facade, + SearchEngineData::QueryHeap &forward_heap, SearchEngineData::QueryHeap &reverse_heap, const PhantomNode &source_phantom, const PhantomNode &target_phantom, @@ -996,7 +930,8 @@ template class BasicRoutingInterface int duration = INVALID_EDGE_WEIGHT; std::vector packed_path; - Search(forward_heap, + Search(facade, + forward_heap, reverse_heap, duration, packed_path, @@ -1009,7 +944,7 @@ template class BasicRoutingInterface return std::numeric_limits::max(); } - return GetPathDistance(packed_path, source_phantom, target_phantom); + return GetPathDistance(facade, packed_path, source_phantom, target_phantom); } }; } diff --git a/include/engine/routing_algorithms/shortest_path.hpp b/include/engine/routing_algorithms/shortest_path.hpp index c0a338b19ad..482a30d9097 100644 --- a/include/engine/routing_algorithms/shortest_path.hpp +++ b/include/engine/routing_algorithms/shortest_path.hpp @@ -28,8 +28,8 @@ class ShortestPathRouting final const static constexpr bool DO_NOT_FORCE_LOOP = false; public: - ShortestPathRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) - : super(facade), engine_working_data(engine_working_data) + ShortestPathRouting(SearchEngineData &engine_working_data) + : engine_working_data(engine_working_data) { } @@ -37,7 +37,8 @@ class ShortestPathRouting final // allows a uturn at the target_phantom // searches source forward/reverse -> target forward/reverse - void SearchWithUTurn(QueryHeap &forward_heap, + void SearchWithUTurn(const DataFacadeT &facade, + QueryHeap &forward_heap, QueryHeap &reverse_heap, QueryHeap &forward_core_heap, QueryHeap &reverse_core_heap, @@ -90,13 +91,14 @@ class ShortestPathRouting final is_oneway_source && super::NeedsLoopForward(source_phantom, target_phantom); auto needs_loop_backwards = is_oneway_target && super::NeedsLoopBackwards(source_phantom, target_phantom); - if (super::facade->GetCoreSize() > 0) + if (facade.GetCoreSize() > 0) { forward_core_heap.Clear(); reverse_core_heap.Clear(); BOOST_ASSERT(forward_core_heap.Size() == 0); BOOST_ASSERT(reverse_core_heap.Size() == 0); - super::SearchWithCore(forward_heap, + super::SearchWithCore(facade, + forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, @@ -107,7 +109,8 @@ class ShortestPathRouting final } else { - super::Search(forward_heap, + super::Search(facade, + forward_heap, reverse_heap, new_total_distance, leg_packed_path, @@ -124,7 +127,8 @@ class ShortestPathRouting final // searches shortest path between: // source forward/reverse -> target forward // source forward/reverse -> target reverse - void Search(QueryHeap &forward_heap, + void Search(const DataFacadeT &facade, + QueryHeap &forward_heap, QueryHeap &reverse_heap, QueryHeap &forward_core_heap, QueryHeap &reverse_core_heap, @@ -166,13 +170,14 @@ class ShortestPathRouting final BOOST_ASSERT(forward_heap.Size() > 0); BOOST_ASSERT(reverse_heap.Size() > 0); - if (super::facade->GetCoreSize() > 0) + if (facade.GetCoreSize() > 0) { forward_core_heap.Clear(); reverse_core_heap.Clear(); BOOST_ASSERT(forward_core_heap.Size() == 0); BOOST_ASSERT(reverse_core_heap.Size() == 0); - super::SearchWithCore(forward_heap, + super::SearchWithCore(facade, + forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, @@ -183,7 +188,8 @@ class ShortestPathRouting final } else { - super::Search(forward_heap, + super::Search(facade, + forward_heap, reverse_heap, new_total_distance_to_forward, leg_packed_path_forward, @@ -215,13 +221,14 @@ class ShortestPathRouting final } BOOST_ASSERT(forward_heap.Size() > 0); BOOST_ASSERT(reverse_heap.Size() > 0); - if (super::facade->GetCoreSize() > 0) + if (facade.GetCoreSize() > 0) { forward_core_heap.Clear(); reverse_core_heap.Clear(); BOOST_ASSERT(forward_core_heap.Size() == 0); BOOST_ASSERT(reverse_core_heap.Size() == 0); - super::SearchWithCore(forward_heap, + super::SearchWithCore(facade, + forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, @@ -232,7 +239,8 @@ class ShortestPathRouting final } else { - super::Search(forward_heap, + super::Search(facade, + forward_heap, reverse_heap, new_total_distance_to_reverse, leg_packed_path_reverse, @@ -242,7 +250,8 @@ class ShortestPathRouting final } } - void UnpackLegs(const std::vector &phantom_nodes_vector, + void UnpackLegs(const DataFacadeT &facade, + const std::vector &phantom_nodes_vector, const std::vector &total_packed_path, const std::vector &packed_leg_begin, const int shortest_path_length, @@ -257,7 +266,8 @@ class ShortestPathRouting final auto leg_begin = total_packed_path.begin() + packed_leg_begin[current_leg]; auto leg_end = total_packed_path.begin() + packed_leg_begin[current_leg + 1]; const auto &unpack_phantom_node_pair = phantom_nodes_vector[current_leg]; - super::UnpackPath(leg_begin, + super::UnpackPath(facade, + leg_begin, leg_end, unpack_phantom_node_pair, raw_route_data.unpacked_path_segments[current_leg]); @@ -271,18 +281,17 @@ class ShortestPathRouting final } } - void operator()(const std::vector &phantom_nodes_vector, + void operator()(const DataFacadeT &facade, + const std::vector &phantom_nodes_vector, const boost::optional continue_straight_at_waypoint, InternalRouteResult &raw_route_data) const { const bool allow_uturn_at_waypoint = !(continue_straight_at_waypoint ? *continue_straight_at_waypoint - : super::facade->GetContinueStraightDefault()); + : facade.GetContinueStraightDefault()); - engine_working_data.InitializeOrClearFirstThreadLocalStorage( - super::facade->GetNumberOfNodes()); - engine_working_data.InitializeOrClearSecondThreadLocalStorage( - super::facade->GetNumberOfNodes()); + engine_working_data.InitializeOrClearFirstThreadLocalStorage(facade.GetNumberOfNodes()); + engine_working_data.InitializeOrClearSecondThreadLocalStorage(facade.GetNumberOfNodes()); QueryHeap &forward_heap = *(engine_working_data.forward_heap_1); QueryHeap &reverse_heap = *(engine_working_data.reverse_heap_1); @@ -330,7 +339,8 @@ class ShortestPathRouting final { if (allow_uturn_at_waypoint) { - SearchWithUTurn(forward_heap, + SearchWithUTurn(facade, + forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, @@ -361,7 +371,8 @@ class ShortestPathRouting final } else { - Search(forward_heap, + Search(facade, + forward_heap, reverse_heap, forward_core_heap, reverse_core_heap, @@ -489,7 +500,8 @@ class ShortestPathRouting final packed_leg_to_reverse_begin.push_back(total_packed_path_to_reverse.size()); BOOST_ASSERT(packed_leg_to_reverse_begin.size() == phantom_nodes_vector.size() + 1); - UnpackLegs(phantom_nodes_vector, + UnpackLegs(facade, + phantom_nodes_vector, total_packed_path_to_reverse, packed_leg_to_reverse_begin, total_distance_to_reverse, @@ -501,7 +513,8 @@ class ShortestPathRouting final packed_leg_to_forward_begin.push_back(total_packed_path_to_forward.size()); BOOST_ASSERT(packed_leg_to_forward_begin.size() == phantom_nodes_vector.size() + 1); - UnpackLegs(phantom_nodes_vector, + UnpackLegs(facade, + phantom_nodes_vector, total_packed_path_to_forward, packed_leg_to_forward_begin, total_distance_to_forward, diff --git a/include/engine/trip/trip_brute_force.hpp b/include/engine/trip/trip_brute_force.hpp index 497c0bf6f2c..fcf4ebf8c8c 100644 --- a/include/engine/trip/trip_brute_force.hpp +++ b/include/engine/trip/trip_brute_force.hpp @@ -53,8 +53,7 @@ std::vector BruteForceTrip(const NodeIDIterator start, const auto component_size = std::distance(start, end); std::vector perm(start, end); - std::vector route; - route.reserve(component_size); + std::vector route = perm; EdgeWeight min_route_dist = INVALID_EDGE_WEIGHT; @@ -68,7 +67,10 @@ std::vector BruteForceTrip(const NodeIDIterator start, do { const auto new_distance = ReturnDistance(dist_table, perm, min_route_dist, component_size); - if (new_distance <= min_route_dist) + // we can use `<` instead of `<=` here, since all distances are `!=` INVALID_EDGE_WEIGHT + // In case we really sum up to invalid edge weight for all permutations, keeping the very + // first one is fine too. + if (new_distance < min_route_dist) { min_route_dist = new_distance; route = perm; diff --git a/include/engine/trip/trip_farthest_insertion.hpp b/include/engine/trip/trip_farthest_insertion.hpp index 038386a3b17..b0a6fc4279a 100644 --- a/include/engine/trip/trip_farthest_insertion.hpp +++ b/include/engine/trip/trip_farthest_insertion.hpp @@ -119,7 +119,7 @@ std::vector FindRoute(const std::size_t &number_of_locations, // add the location to the current trip such that it results in the shortest total // tour - if (insert_candidate.first >= farthest_distance) + if (insert_candidate.first > farthest_distance) { farthest_distance = insert_candidate.first; next_node = *i; diff --git a/include/extractor/compressed_edge_container.hpp b/include/extractor/compressed_edge_container.hpp index 9623d539ad4..7e986409fe7 100644 --- a/include/extractor/compressed_edge_container.hpp +++ b/include/extractor/compressed_edge_container.hpp @@ -16,12 +16,22 @@ namespace extractor class CompressedEdgeContainer { public: - struct CompressedEdge + struct OnewayCompressedEdge { public: NodeID node_id; // refers to an internal node-based-node EdgeWeight weight; // the weight of the edge leading to this node }; + + struct CompressedEdge + { + public: + NodeID node_id; + EdgeWeight forward_weight; + EdgeWeight reverse_weight; + }; + + using OnewayEdgeBucket = std::vector; using EdgeBucket = std::vector; CompressedEdgeContainer(); @@ -33,13 +43,20 @@ class CompressedEdgeContainer const EdgeWeight weight2); void - AddUncompressedEdge(const EdgeID edgei_id, const NodeID target_node, const EdgeWeight weight); + AddUncompressedEdge(const EdgeID edge_id, const NodeID target_node, const EdgeWeight weight); + + void InitializeBothwayVector(); + unsigned ZipEdges(const unsigned f_edge_pos, const unsigned r_edge_pos); bool HasEntryForID(const EdgeID edge_id) const; + bool HasZippedEntryForForwardID(const EdgeID edge_id) const; + bool HasZippedEntryForReverseID(const EdgeID edge_id) const; void PrintStatistics() const; void SerializeInternalVector(const std::string &path) const; unsigned GetPositionForID(const EdgeID edge_id) const; - const EdgeBucket &GetBucketReference(const EdgeID edge_id) const; + unsigned GetZippedPositionForForwardID(const EdgeID edge_id) const; + unsigned GetZippedPositionForReverseID(const EdgeID edge_id) const; + const OnewayEdgeBucket &GetBucketReference(const EdgeID edge_id) const; bool IsTrivial(const EdgeID edge_id) const; NodeID GetFirstEdgeTargetID(const EdgeID edge_id) const; NodeID GetLastEdgeTargetID(const EdgeID edge_id) const; @@ -49,9 +66,12 @@ class CompressedEdgeContainer int free_list_maximum = 0; void IncreaseFreeList(); + std::vector m_compressed_oneway_geometries; std::vector m_compressed_geometries; std::vector m_free_list; std::unordered_map m_edge_id_to_list_index_map; + std::unordered_map m_forward_edge_id_to_zipped_index_map; + std::unordered_map m_reverse_edge_id_to_zipped_index_map; }; } } diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index 130e07800ec..a3227d59ac9 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -84,7 +84,7 @@ class EdgeBasedGraphFactory EdgeBasedGraphFactory &operator=(const EdgeBasedGraphFactory &) = delete; explicit EdgeBasedGraphFactory(std::shared_ptr node_based_graph, - const CompressedEdgeContainer &compressed_edge_container, + CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, const std::unordered_set &traffic_lights, std::shared_ptr restriction_map, @@ -149,7 +149,7 @@ class EdgeBasedGraphFactory const std::unordered_set &m_barrier_nodes; const std::unordered_set &m_traffic_lights; - const CompressedEdgeContainer &m_compressed_edge_container; + CompressedEdgeContainer &m_compressed_edge_container; ProfileProperties profile_properties; diff --git a/include/extractor/edge_based_node.hpp b/include/extractor/edge_based_node.hpp index 6ea0349a23f..5ad1e1992f0 100644 --- a/include/extractor/edge_based_node.hpp +++ b/include/extractor/edge_based_node.hpp @@ -22,8 +22,8 @@ struct EdgeBasedNode EdgeBasedNode() : forward_segment_id{SPECIAL_SEGMENTID, false}, reverse_segment_id{SPECIAL_SEGMENTID, false}, u(SPECIAL_NODEID), v(SPECIAL_NODEID), - name_id(0), forward_packed_geometry_id(SPECIAL_EDGEID), - reverse_packed_geometry_id(SPECIAL_EDGEID), component{INVALID_COMPONENTID, false}, + name_id(0), packed_geometry_id(SPECIAL_GEOMETRYID), + component{INVALID_COMPONENTID, false}, fwd_segment_position(std::numeric_limits::max()), forward_travel_mode(TRAVEL_MODE_INACCESSIBLE), backward_travel_mode(TRAVEL_MODE_INACCESSIBLE) @@ -35,16 +35,14 @@ struct EdgeBasedNode NodeID u, NodeID v, unsigned name_id, - unsigned forward_geometry_id_, - unsigned reverse_geometry_id_, + unsigned packed_geometry_id_, bool is_tiny_component, unsigned component_id, unsigned short fwd_segment_position, TravelMode forward_travel_mode, TravelMode backward_travel_mode) : forward_segment_id(forward_segment_id_), reverse_segment_id(reverse_segment_id_), u(u), - v(v), name_id(name_id), forward_packed_geometry_id(forward_geometry_id_), - reverse_packed_geometry_id(reverse_geometry_id_), + v(v), name_id(name_id), packed_geometry_id(packed_geometry_id_), component{component_id, is_tiny_component}, fwd_segment_position(fwd_segment_position), forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode) { @@ -57,8 +55,7 @@ struct EdgeBasedNode NodeID v; // indices into the coordinates array unsigned name_id; // id of the edge name - unsigned forward_packed_geometry_id; - unsigned reverse_packed_geometry_id; + unsigned packed_geometry_id; struct { unsigned id : 31; diff --git a/include/extractor/extraction_containers.hpp b/include/extractor/extraction_containers.hpp index 16ba0f32ec2..5f3ede18bf1 100644 --- a/include/extractor/extraction_containers.hpp +++ b/include/extractor/extraction_containers.hpp @@ -39,10 +39,6 @@ class ExtractionContainers void WriteNodes(std::ofstream &file_out_stream) const; void WriteRestrictions(const std::string &restrictions_file_name) const; void WriteEdges(std::ofstream &file_out_stream) const; - void - WriteTurnLaneMasks(const std::string &file_name, - const stxxl::vector &turn_lane_offsets, - const stxxl::vector &turn_lane_masks) const; void WriteCharData(const std::string &file_name); public: diff --git a/include/extractor/extraction_way.hpp b/include/extractor/extraction_way.hpp index 9ac9da1c43f..8024f6b92d5 100644 --- a/include/extractor/extraction_way.hpp +++ b/include/extractor/extraction_way.hpp @@ -33,6 +33,7 @@ struct ExtractionWay is_startpoint = true; is_access_restricted = false; name.clear(); + ref.clear(); pronunciation.clear(); destinations.clear(); forward_travel_mode = TRAVEL_MODE_INACCESSIBLE; @@ -53,6 +54,7 @@ struct ExtractionWay double backward_speed; double duration; std::string name; + std::string ref; std::string pronunciation; std::string destinations; std::string turn_lanes_forward; diff --git a/include/extractor/extractor_callbacks.hpp b/include/extractor/extractor_callbacks.hpp index dd11c86cf74..e5cb86c32b4 100644 --- a/include/extractor/extractor_callbacks.hpp +++ b/include/extractor/extractor_callbacks.hpp @@ -16,6 +16,24 @@ class Node; class Way; } +namespace std +{ +template <> struct hash> +{ + std::size_t + operator()(const std::tuple &mk) const + noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, std::get<0>(mk)); + boost::hash_combine(seed, std::get<1>(mk)); + boost::hash_combine(seed, std::get<2>(mk)); + boost::hash_combine(seed, std::get<3>(mk)); + return seed; + } +}; +} + namespace osrm { namespace extractor @@ -27,7 +45,7 @@ struct ExtractionNode; struct ExtractionWay; /** - * This class is uses by the extractor with the results of the + * This class is used by the extractor with the results of the * osmium based parsing and the customization through the lua profile. * * It mediates between the multi-threaded extraction process and the external memory containers. @@ -36,10 +54,11 @@ struct ExtractionWay; class ExtractorCallbacks { private: - // used to deduplicate street names and street destinations: actually maps to name ids - using MapKey = std::pair; + // used to deduplicate street names, refs, destinations, pronunciation: actually maps to name + // ids + using MapKey = std::tuple; using MapVal = unsigned; - std::unordered_map> string_map; + std::unordered_map string_map; guidance::LaneDescriptionMap lane_description_map; ExtractionContainers &external_memory; diff --git a/include/extractor/guidance/roundabout_handler.hpp b/include/extractor/guidance/roundabout_handler.hpp index 9aee76975ee..5fd33dbc120 100644 --- a/include/extractor/guidance/roundabout_handler.hpp +++ b/include/extractor/guidance/roundabout_handler.hpp @@ -13,7 +13,7 @@ #include "util/node_based_graph.hpp" #include "util/typedefs.hpp" -#include +#include #include #include @@ -82,7 +82,8 @@ class RoundaboutHandler : public IntersectionHandler const bool can_exit_roundabout, Intersection intersection) const; - bool qualifiesAsRoundaboutIntersection(const std::set &roundabout_nodes) const; + bool + qualifiesAsRoundaboutIntersection(const std::unordered_set &roundabout_nodes) const; const CompressedEdgeContainer &compressed_edge_container; const ProfileProperties &profile_properties; diff --git a/include/extractor/guidance/toolkit.hpp b/include/extractor/guidance/toolkit.hpp index 0ac4f1b1c55..1a87f116238 100644 --- a/include/extractor/guidance/toolkit.hpp +++ b/include/extractor/guidance/toolkit.hpp @@ -11,7 +11,6 @@ #include "extractor/compressed_edge_container.hpp" #include "extractor/query_node.hpp" -#include "extractor/suffix_table.hpp" #include "extractor/guidance/discrete_angle.hpp" #include "extractor/guidance/intersection.hpp" @@ -26,7 +25,6 @@ #include #include -#include #include #include @@ -127,7 +125,7 @@ getRepresentativeCoordinate(const NodeID from_node, }; // Uncompressed roads are simple, return the coordinate at the end - if (!compressed_geometries.HasEntryForID(via_edge_id)) + if (!compressed_geometries.HasZippedEntryForForwardID(via_edge_id) && !compressed_geometries.HasZippedEntryForReverseID(via_edge_id)) { return extractCoordinateFromNode(traverse_in_reverse ? query_nodes[from_node] : query_nodes[to_node]); @@ -151,122 +149,6 @@ getRepresentativeCoordinate(const NodeID from_node, } } -inline std::pair getPrefixAndSuffix(const std::string &data) -{ - const auto suffix_pos = data.find_last_of(' '); - if (suffix_pos == std::string::npos) - return {}; - - const auto prefix_pos = data.find_first_of(' '); - auto result = std::make_pair(data.substr(0, prefix_pos), data.substr(suffix_pos + 1)); - boost::to_lower(result.first); - boost::to_lower(result.second); - return result; -} - -inline bool requiresNameAnnounced(const std::string &from, - const std::string &to, - const SuffixTable &suffix_table) -{ - // first is empty and the second is not - if (from.empty() && !to.empty()) - return true; - - // FIXME, handle in profile to begin with? - // this uses the encoding of references in the profile, which is very BAD - // Input for this function should be a struct separating streetname, suffix (e.g. road, - // boulevard, North, West ...), and a list of references - std::string from_name; - std::string from_ref; - std::string to_name; - std::string to_ref; - - // Split from the format "{name} ({ref})" -> name, ref - auto split = [](const std::string &name, std::string &out_name, std::string &out_ref) { - const auto ref_begin = name.find_first_of('('); - if (ref_begin != std::string::npos) - { - if (ref_begin != 0) - out_name = name.substr(0, ref_begin - 1); - const auto ref_end = name.find_first_of(')'); - out_ref = name.substr(ref_begin + 1, ref_end - ref_begin - 1); - } - else - { - out_name = name; - } - }; - - split(from, from_name, from_ref); - split(to, to_name, to_ref); - - // check similarity of names - const auto names_are_empty = from_name.empty() && to_name.empty(); - const auto name_is_contained = - boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name); - - const auto checkForPrefixOrSuffixChange = []( - const std::string &first, const std::string &second, const SuffixTable &suffix_table) { - - const auto first_prefix_and_suffixes = getPrefixAndSuffix(first); - const auto second_prefix_and_suffixes = getPrefixAndSuffix(second); - // reverse strings, get suffices and reverse them to get prefixes - const auto checkTable = [&](const std::string &str) { - return str.empty() || suffix_table.isSuffix(str); - }; - - const auto getOffset = [](const std::string &str) -> std::size_t { - if (str.empty()) - return 0; - else - return str.length() + 1; - }; - - const bool is_prefix_change = [&]() -> bool { - if (!checkTable(first_prefix_and_suffixes.first)) - return false; - if (!checkTable(second_prefix_and_suffixes.first)) - return false; - return !first.compare(getOffset(first_prefix_and_suffixes.first), - std::string::npos, - second, - getOffset(second_prefix_and_suffixes.first), - std::string::npos); - }(); - - const bool is_suffix_change = [&]() -> bool { - if (!checkTable(first_prefix_and_suffixes.second)) - return false; - if (!checkTable(second_prefix_and_suffixes.second)) - return false; - return !first.compare(0, - first.length() - getOffset(first_prefix_and_suffixes.second), - second, - 0, - second.length() - getOffset(second_prefix_and_suffixes.second)); - }(); - - return is_prefix_change || is_suffix_change; - }; - - const auto is_suffix_change = checkForPrefixOrSuffixChange(from_name, to_name, suffix_table); - const auto names_are_equal = from_name == to_name || name_is_contained || is_suffix_change; - const auto name_is_removed = !from_name.empty() && to_name.empty(); - // references are contained in one another - const auto refs_are_empty = from_ref.empty() && to_ref.empty(); - const auto ref_is_contained = - from_ref.empty() || to_ref.empty() || - (from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos); - const auto ref_is_removed = !from_ref.empty() && to_ref.empty(); - - const auto obvious_change = - (names_are_empty && refs_are_empty) || (names_are_equal && ref_is_contained) || - (names_are_equal && refs_are_empty) || (ref_is_contained && name_is_removed) || - (names_are_equal && ref_is_removed) || is_suffix_change; - - return !obvious_change; -} - // To simplify handling of Left/Right hand turns, we can mirror turns and write an intersection // handler only for one side. The mirror function turns a left-hand turn in a equivalent right-hand // turn and vice versa. diff --git a/include/extractor/original_edge_data.hpp b/include/extractor/original_edge_data.hpp index 558fcb6a070..9a2778aaaaf 100644 --- a/include/extractor/original_edge_data.hpp +++ b/include/extractor/original_edge_data.hpp @@ -15,26 +15,26 @@ namespace extractor struct OriginalEdgeData { - explicit OriginalEdgeData(NodeID via_node, + explicit OriginalEdgeData(GeometryID via_geometry, unsigned name_id, LaneDataID lane_data_id, guidance::TurnInstruction turn_instruction, EntryClassID entry_classid, TravelMode travel_mode) - : via_node(via_node), name_id(name_id), entry_classid(entry_classid), + : via_geometry(via_geometry), name_id(name_id), entry_classid(entry_classid), lane_data_id(lane_data_id), turn_instruction(turn_instruction), travel_mode(travel_mode) { } OriginalEdgeData() - : via_node(std::numeric_limits::max()), + : via_geometry{std::numeric_limits::max() >> 1, false}, name_id(std::numeric_limits::max()), entry_classid(INVALID_ENTRY_CLASSID), lane_data_id(INVALID_LANE_DATAID), turn_instruction(guidance::TurnInstruction::INVALID()), travel_mode(TRAVEL_MODE_INACCESSIBLE) { } - NodeID via_node; + GeometryID via_geometry; unsigned name_id; EntryClassID entry_classid; LaneDataID lane_data_id; diff --git a/include/extractor/restriction_parser.hpp b/include/extractor/restriction_parser.hpp index c5eede2f80f..732affc1f53 100644 --- a/include/extractor/restriction_parser.hpp +++ b/include/extractor/restriction_parser.hpp @@ -29,7 +29,7 @@ class ScriptingEnvironment; * * While this class does not directly invoke any lua code _per relation_ it does * load configuration values from the profile, that are saved in variables. - * Namely ```use_turn_restrictions``` and ```get_exceptions```. + * Namely ```use_turn_restrictions``` and ```get_restrictions```. * * The restriction is represented by the osm id of the from way, the osm id of the * to way and the osm id of the via node. This representation must be post-processed @@ -47,7 +47,7 @@ class RestrictionParser private: bool ShouldIgnoreRestriction(const std::string &except_tag_string) const; - std::vector restriction_exceptions; + std::vector restrictions; bool use_turn_restrictions; }; } diff --git a/include/extractor/scripting_environment.hpp b/include/extractor/scripting_environment.hpp index a450b3154f1..d7f3bd4f978 100644 --- a/include/extractor/scripting_environment.hpp +++ b/include/extractor/scripting_environment.hpp @@ -51,7 +51,7 @@ class ScriptingEnvironment virtual const ProfileProperties &GetProfileProperties() = 0; virtual std::vector GetNameSuffixList() = 0; - virtual std::vector GetExceptions() = 0; + virtual std::vector GetRestrictions() = 0; virtual void SetupSources() = 0; virtual int32_t GetTurnPenalty(double angle) = 0; virtual void ProcessSegment(const osrm::util::Coordinate &source, diff --git a/include/extractor/scripting_environment_lua.hpp b/include/extractor/scripting_environment_lua.hpp index c55a4b67cc7..b82fafb8c19 100644 --- a/include/extractor/scripting_environment_lua.hpp +++ b/include/extractor/scripting_environment_lua.hpp @@ -53,7 +53,7 @@ class LuaScriptingEnvironment final : public ScriptingEnvironment LuaScriptingContext &GetLuaContext(); std::vector GetNameSuffixList() override; - std::vector GetExceptions() override; + std::vector GetRestrictions() override; void SetupSources() override; int32_t GetTurnPenalty(double angle) override; void ProcessSegment(const osrm::util::Coordinate &source, diff --git a/include/extractor/suffix_table.hpp b/include/extractor/suffix_table.hpp index 31830b90e57..7785a5e144b 100644 --- a/include/extractor/suffix_table.hpp +++ b/include/extractor/suffix_table.hpp @@ -25,6 +25,7 @@ class SuffixTable final private: std::unordered_set suffix_set; }; + } /* namespace extractor */ } /* namespace osrm */ diff --git a/include/osrm/osrm.hpp b/include/osrm/osrm.hpp index e4d6801e698..4e9fa045432 100644 --- a/include/osrm/osrm.hpp +++ b/include/osrm/osrm.hpp @@ -84,7 +84,7 @@ class OSRM final * \return Status indicating success for the query or failure * \see Status, RouteParameters and json::Object */ - Status Route(const RouteParameters ¶meters, json::Object &result); + Status Route(const RouteParameters ¶meters, json::Object &result) const; /** * Distance tables for coordinates. @@ -93,7 +93,7 @@ class OSRM final * \return Status indicating success for the query or failure * \see Status, TableParameters and json::Object */ - Status Table(const TableParameters ¶meters, json::Object &result); + Status Table(const TableParameters ¶meters, json::Object &result) const; /** * Nearest street segment for coordinate. @@ -102,7 +102,7 @@ class OSRM final * \return Status indicating success for the query or failure * \see Status, NearestParameters and json::Object */ - Status Nearest(const NearestParameters ¶meters, json::Object &result); + Status Nearest(const NearestParameters ¶meters, json::Object &result) const; /** * Trip: shortest round trip between coordinates. @@ -111,7 +111,7 @@ class OSRM final * \return Status indicating success for the query or failure * \see Status, TripParameters and json::Object */ - Status Trip(const TripParameters ¶meters, json::Object &result); + Status Trip(const TripParameters ¶meters, json::Object &result) const; /** * Match: snaps noisy coordinate traces to the road network @@ -120,7 +120,7 @@ class OSRM final * \return Status indicating success for the query or failure * \see Status, MatchParameters and json::Object */ - Status Match(const MatchParameters ¶meters, json::Object &result); + Status Match(const MatchParameters ¶meters, json::Object &result) const; /** * Tile: vector tiles with internal graph representation @@ -129,10 +129,10 @@ class OSRM final * \return Status indicating success for the query or failure * \see Status, TileParameters and json::Object */ - Status Tile(const TileParameters ¶meters, std::string &result); + Status Tile(const TileParameters ¶meters, std::string &result) const; /** - * Isocrhone for given distance + * Isochrone for given distance * \param parameters nearest query specific parameters * \return Status indicating success for the query or failure * \see Status, NearestParameters and json::Object diff --git a/include/util/bearing.hpp b/include/util/bearing.hpp index 93006913120..de308adcee8 100644 --- a/include/util/bearing.hpp +++ b/include/util/bearing.hpp @@ -67,7 +67,7 @@ inline bool CheckInBounds(const int A, const int B, const int range) if (range >= 180) return true; - if (range <= 0) + if (range < 0) return false; // Map both bearings into positive modulo 360 space diff --git a/include/util/guidance/toolkit.hpp b/include/util/guidance/toolkit.hpp index 8582b805661..b7ca55c96d6 100644 --- a/include/util/guidance/toolkit.hpp +++ b/include/util/guidance/toolkit.hpp @@ -12,8 +12,13 @@ #include "util/simple_logger.hpp" #include +#include #include +#include +#include +#include + namespace osrm { namespace util @@ -113,7 +118,7 @@ inline bool entersRoundabout(const extractor::guidance::TurnInstruction instruct instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit || instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout || instruction.type == extractor::guidance::TurnType::EnterAndExitRotary || - instruction.type == extractor::guidance::TurnType::EnterAndExitRotary); + instruction.type == extractor::guidance::TurnType::EnterAndExitRoundaboutIntersection); } inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruction) @@ -131,6 +136,128 @@ inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruc return instruction.type == extractor::guidance::TurnType::StayOnRoundabout; } +// Name Change Logic +// Used both during Extraction as well as during Post-Processing + +inline std::pair getPrefixAndSuffix(const std::string &data) +{ + const auto suffix_pos = data.find_last_of(' '); + if (suffix_pos == std::string::npos) + return {}; + + const auto prefix_pos = data.find_first_of(' '); + auto result = std::make_pair(data.substr(0, prefix_pos), data.substr(suffix_pos + 1)); + boost::to_lower(result.first); + boost::to_lower(result.second); + return result; +} + +// Note: there is an overload without suffix checking below. +// (that's the reason we template the suffix table here) +template +inline bool requiresNameAnnounced(const std::string &from_name, + const std::string &from_ref, + const std::string &to_name, + const std::string &to_ref, + const SuffixTable &suffix_table) +{ + // first is empty and the second is not + if ((from_name.empty() && from_ref.empty()) && !(to_name.empty() && to_ref.empty())) + return true; + + // FIXME, handle in profile to begin with? + // Input for this function should be a struct separating streetname, suffix (e.g. road, + // boulevard, North, West ...), and a list of references + + // check similarity of names + const auto names_are_empty = from_name.empty() && to_name.empty(); + const auto name_is_contained = + boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name); + + const auto checkForPrefixOrSuffixChange = []( + const std::string &first, const std::string &second, const SuffixTable &suffix_table) { + + const auto first_prefix_and_suffixes = getPrefixAndSuffix(first); + const auto second_prefix_and_suffixes = getPrefixAndSuffix(second); + + // reverse strings, get suffices and reverse them to get prefixes + const auto checkTable = [&](const std::string &str) { + return str.empty() || suffix_table.isSuffix(str); + }; + + const auto getOffset = [](const std::string &str) -> std::size_t { + if (str.empty()) + return 0; + else + return str.length() + 1; + }; + + const bool is_prefix_change = [&]() -> bool { + if (!checkTable(first_prefix_and_suffixes.first)) + return false; + if (!checkTable(second_prefix_and_suffixes.first)) + return false; + return !first.compare(getOffset(first_prefix_and_suffixes.first), + std::string::npos, + second, + getOffset(second_prefix_and_suffixes.first), + std::string::npos); + }(); + + const bool is_suffix_change = [&]() -> bool { + if (!checkTable(first_prefix_and_suffixes.second)) + return false; + if (!checkTable(second_prefix_and_suffixes.second)) + return false; + return !first.compare(0, + first.length() - getOffset(first_prefix_and_suffixes.second), + second, + 0, + second.length() - getOffset(second_prefix_and_suffixes.second)); + }(); + + return is_prefix_change || is_suffix_change; + }; + + const auto is_suffix_change = checkForPrefixOrSuffixChange(from_name, to_name, suffix_table); + const auto names_are_equal = from_name == to_name || name_is_contained || is_suffix_change; + const auto name_is_removed = !from_name.empty() && to_name.empty(); + // references are contained in one another + const auto refs_are_empty = from_ref.empty() && to_ref.empty(); + const auto ref_is_contained = + from_ref.empty() || to_ref.empty() || + (from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos); + const auto ref_is_removed = !from_ref.empty() && to_ref.empty(); + + const auto obvious_change = + (names_are_empty && refs_are_empty) || (names_are_equal && ref_is_contained) || + (names_are_equal && refs_are_empty) || (ref_is_contained && name_is_removed) || + (names_are_equal && ref_is_removed) || is_suffix_change; + + const auto needs_announce = + // " (Ref)" -> "Name " + (from_name.empty() && !from_ref.empty() && !to_name.empty() && to_ref.empty()); + + return !obvious_change || needs_announce; +} + +// Overload without suffix checking +inline bool requiresNameAnnounced(const std::string &from_name, + const std::string &from_ref, + const std::string &to_name, + const std::string &to_ref) +{ + // Dummy since we need to provide a SuffixTable but do not have the data for it. + // (Guidance Post-Processing does not keep the suffix table around at the moment) + struct NopSuffixTable final + { + NopSuffixTable() {} + bool isSuffix(const std::string &) const { return false; } + } static const table; + + return requiresNameAnnounced(from_name, from_ref, to_name, to_ref, table); +} + } // namespace guidance } // namespace util } // namespace osrm diff --git a/include/util/json_deep_compare.hpp b/include/util/json_deep_compare.hpp index f5a6c8a1ef6..9c202a52793 100644 --- a/include/util/json_deep_compare.hpp +++ b/include/util/json_deep_compare.hpp @@ -17,7 +17,7 @@ namespace util namespace json { -struct Comparator : mapbox::util::static_visitor +struct Comparator { Comparator(std::string &reason_, const std::string &lhs_path_, const std::string &rhs_path_) : reason(reason_), lhs_path(lhs_path_), rhs_path(rhs_path_) diff --git a/include/util/json_logger.hpp b/include/util/json_logger.hpp deleted file mode 100644 index aa72b54cf36..00000000000 --- a/include/util/json_logger.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef JSON_LOGGER_HPP -#define JSON_LOGGER_HPP - -#include "osrm/json_container.hpp" - -#include - -#include -#include - -namespace osrm -{ -namespace util -{ -namespace json -{ - -// Used to append additional debugging information to the JSON response in a -// thread safe manner. -class Logger -{ - using MapT = std::unordered_map; - - public: - static Logger *get() - { - static Logger logger; - - bool return_logger = true; -#ifdef NDEBUG - return_logger = false; -#endif -#ifdef ENABLE_JSON_LOGGING - return_logger = true; -#endif - - if (return_logger) - { - return &logger; - } - - return nullptr; - } - - void initialize(const std::string &name) - { - if (!map.get()) - { - map.reset(new MapT()); - } - (*map)[name] = Object(); - } - - void render(const std::string &name, Object &obj) const { obj.values["debug"] = map->at(name); } - - boost::thread_specific_ptr map; -}; -} -} -} - -#endif /* JSON_LOGGER_HPP */ diff --git a/include/util/name_table.hpp b/include/util/name_table.hpp index 318cffca183..2f45a71e3b0 100644 --- a/include/util/name_table.hpp +++ b/include/util/name_table.hpp @@ -23,7 +23,13 @@ class NameTable public: NameTable(const std::string &filename); + + // This class provides a limited view over all the string data we serialize out. + // The following functions are a subset of what is available. + // See the data facades for they provide full access to this serialized string data. + // (at time of writing this: get{Name,Ref,Pronunciation,Destinations}ForID(name_id);) std::string GetNameForID(const unsigned name_id) const; + std::string GetRefForID(const unsigned name_id) const; }; } // namespace util } // namespace osrm diff --git a/include/util/shared_memory_vector_wrapper.hpp b/include/util/shared_memory_vector_wrapper.hpp index 2fec536be9b..f4a85dbbcc2 100644 --- a/include/util/shared_memory_vector_wrapper.hpp +++ b/include/util/shared_memory_vector_wrapper.hpp @@ -46,6 +46,34 @@ template class ShMemIterator : public std::iterator class ShMemReverseIterator : public std::iterator +{ + DataT *p; + + public: + explicit ShMemReverseIterator(DataT *x) : p(x) {} + ShMemReverseIterator(const ShMemReverseIterator &mit) : p(mit.p) {} + ShMemReverseIterator &operator++() + { + --p; + return *this; + } + ShMemReverseIterator operator++(int) + { + ShMemReverseIterator tmp(*this); + operator++(); + return tmp; + } + ShMemReverseIterator operator+(std::ptrdiff_t diff) + { + ShMemReverseIterator tmp(p - diff); + return tmp; + } + bool operator==(const ShMemReverseIterator &rhs) { return p == rhs.p; } + bool operator!=(const ShMemReverseIterator &rhs) { return p != rhs.p; } + DataT &operator*() { return *p; } +}; + template class SharedMemoryWrapper { private: @@ -71,6 +99,10 @@ template class SharedMemoryWrapper ShMemIterator end() const { return ShMemIterator(m_ptr + m_size); } + ShMemReverseIterator rbegin() const { return ShMemReverseIterator(m_ptr + m_size - 1); } + + ShMemReverseIterator rend() const { return ShMemReverseIterator(m_ptr - 1); } + std::size_t size() const { return m_size; } bool empty() const { return 0 == size(); } diff --git a/include/util/static_graph.hpp b/include/util/static_graph.hpp index 7b4fa8ecb0c..547ca243972 100644 --- a/include/util/static_graph.hpp +++ b/include/util/static_graph.hpp @@ -148,19 +148,32 @@ template class StaticGraph return SPECIAL_EDGEID; } - // searches for a specific edge - EdgeIterator FindSmallestEdge(const NodeIterator from, const NodeIterator to) const + /** + * Finds the edge with the smallest `.distance` going from `from` to `to` + * @param from the source node ID + * @param to the target node ID + * @param filter a functor that returns a `bool` that determines whether an edge should be + * tested or not. + * Takes `EdgeData` as a parameter. + * @return the ID of the smallest edge if any were found that satisfied *filter*, or + * `SPECIAL_EDGEID` if no + * matching edge is found. + */ + template + EdgeIterator + FindSmallestEdge(const NodeIterator from, const NodeIterator to, FilterFunction &&filter) const { EdgeIterator smallest_edge = SPECIAL_EDGEID; EdgeWeight smallest_weight = INVALID_EDGE_WEIGHT; for (auto edge : GetAdjacentEdgeRange(from)) { const NodeID target = GetTarget(edge); - const EdgeWeight weight = GetEdgeData(edge).distance; - if (target == to && weight < smallest_weight) + const auto &data = GetEdgeData(edge); + if (target == to && data.distance < smallest_weight && + std::forward(filter)(data)) { smallest_edge = edge; - smallest_weight = weight; + smallest_weight = data.distance; } } return smallest_edge; diff --git a/include/util/static_rtree.hpp b/include/util/static_rtree.hpp index fb4da056fca..1dedffcadc2 100644 --- a/include/util/static_rtree.hpp +++ b/include/util/static_rtree.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp index 36797eea0a8..b8cb0b4fa0a 100644 --- a/include/util/typedefs.hpp +++ b/include/util/typedefs.hpp @@ -77,6 +77,7 @@ static const EntryClassID INVALID_ENTRY_CLASSID = std::numeric_limits::max(); static const NodeID SPECIAL_SEGMENTID = std::numeric_limits::max() >> 1; +static const NodeID SPECIAL_GEOMETRYID = std::numeric_limits::max() >> 1; static const EdgeID SPECIAL_EDGEID = std::numeric_limits::max(); static const NameID INVALID_NAMEID = std::numeric_limits::max(); static const NameID EMPTY_NAMEID = 0; @@ -96,6 +97,22 @@ struct SegmentID std::uint32_t enabled : 1; }; +/* We need to bit pack here because the index for the via_node + * is given to us without knowing whether the geometry should + * be read forward or in reverse. The extra field `forward` + * indicates that to the routing engine + */ +struct GeometryID +{ + GeometryID(const NodeID id_, const bool forward_) : id{id_}, forward{forward_} {} + + GeometryID() : id(std::numeric_limits::max() >> 1), forward(false) {} + + NodeID id : 31; + std::uint32_t forward : 1; +}; + + static_assert(sizeof(SegmentID) == 4, "SegmentID needs to be 4 bytes big"); #endif /* TYPEDEFS_H */ diff --git a/include/util/vector_tile.hpp b/include/util/vector_tile.hpp index c0a8c10654d..f3c05791ef7 100644 --- a/include/util/vector_tile.hpp +++ b/include/util/vector_tile.hpp @@ -10,23 +10,30 @@ namespace util namespace vector_tile { -const constexpr std::uint32_t LAYER_TAG = 3; +const constexpr std::uint32_t ID_TAG = 1; const constexpr std::uint32_t NAME_TAG = 1; -const constexpr std::uint32_t VERSION_TAG = 15; -const constexpr std::uint32_t EXTEND_TAG = 5; const constexpr std::uint32_t FEATURE_TAG = 2; +const constexpr std::uint32_t LAYER_TAG = 3; const constexpr std::uint32_t GEOMETRY_TAG = 3; -const constexpr std::uint32_t VARIANT_TAG = 4; const constexpr std::uint32_t KEY_TAG = 3; -const constexpr std::uint32_t ID_TAG = 1; -const constexpr std::uint32_t GEOMETRY_TYPE_LINE = 2; +const constexpr std::uint32_t VARIANT_TAG = 4; +const constexpr std::uint32_t EXTENT_TAG = 5; +const constexpr std::uint32_t VERSION_TAG = 15; + const constexpr std::uint32_t FEATURE_ATTRIBUTES_TAG = 2; const constexpr std::uint32_t FEATURE_GEOMETRIES_TAG = 4; -const constexpr std::uint32_t VARIANT_TYPE_UINT32 = 5; -const constexpr std::uint32_t VARIANT_TYPE_BOOL = 7; + +const constexpr std::uint32_t GEOMETRY_TYPE_POINT = 1; +const constexpr std::uint32_t GEOMETRY_TYPE_LINE = 2; + const constexpr std::uint32_t VARIANT_TYPE_STRING = 1; +const constexpr std::uint32_t VARIANT_TYPE_FLOAT = 2; const constexpr std::uint32_t VARIANT_TYPE_DOUBLE = 3; +const constexpr std::uint32_t VARIANT_TYPE_UINT64 = 5; +const constexpr std::uint32_t VARIANT_TYPE_SINT64 = 6; +const constexpr std::uint32_t VARIANT_TYPE_BOOL = 7; + // Vector tiles are 4096 virtual pixels on each side const constexpr double EXTENT = 4096.0; const constexpr double BUFFER = 128.0; diff --git a/package.json b/package.json index 87d21ce8778..c78842fa42c 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,11 @@ "chalk": "^1.1.3", "cucumber": "^1.2.1", "d3-queue": "^2.0.3", + "mkdirp": "^0.5.1", "node-timeout": "0.0.4", "polyline": "^0.2.0", "request": "^2.69.0", + "rimraf": "^2.5.4", "xmlbuilder": "^4.2.1" }, "bin": { diff --git a/profiles/bicycle.lua b/profiles/bicycle.lua index cfdb50aaec8..9e68365e888 100644 --- a/profiles/bicycle.lua +++ b/profiles/bicycle.lua @@ -12,7 +12,7 @@ access_tag_restricted = { ["destination"] = true, ["delivery"] = true } access_tags_hierarchy = { "bicycle", "vehicle", "access" } cycleway_tags = {["track"]=true,["lane"]=true,["opposite"]=true,["opposite_lane"]=true,["opposite_track"]=true,["share_busway"]=true,["sharrow"]=true,["shared"]=true } service_tag_restricted = { ["parking_aisle"] = true } -restriction_exception_tags = { "bicycle", "vehicle", "access" } +restrictions = { "bicycle" } unsafe_highway_list = { ["primary"] = true, ["secondary"] = true, ["tertiary"] = true, ["primary_link"] = true, ["secondary_link"] = true, ["tertiary_link"] = true} local default_speed = 15 @@ -121,8 +121,8 @@ local function parse_maxspeed(source) return n end -function get_exceptions(vector) - for i,v in ipairs(restriction_exception_tags) do +function get_restrictions(vector) + for i,v in ipairs(restrictions) do vector:Add(v) end end @@ -211,19 +211,20 @@ function way_function (way, result) local bicycle = way:get_value_by_key("bicycle") -- name - if ref and "" ~= ref and name and "" ~= name then - result.name = name .. " (" .. ref .. ")" - elseif ref and "" ~= ref then - result.name = ref - elseif name and "" ~= name then + if name and "" ~= name then result.name = name -- TODO find a better solution for encoding way type elseif fallback_names and highway then -- if no name exists, use way type - -- this encoding scheme is excepted to be a temporary solution + -- this encoding scheme is expected to be a temporary solution result.name = "{highway:"..highway.."}" end + -- ref + if ref and "" ~= ref then + result.ref = ref + end + -- roundabout handling if junction and "roundabout" == junction then result.roundabout = true diff --git a/profiles/car.lua b/profiles/car.lua index 9daff8ff773..f56cf6c564f 100644 --- a/profiles/car.lua +++ b/profiles/car.lua @@ -12,7 +12,7 @@ access_tag_restricted = { ["destination"] = true, ["delivery"] = true } access_tags_hierarchy = { "motorcar", "motor_vehicle", "vehicle", "access" } service_tag_restricted = { ["parking_aisle"] = true } service_tag_forbidden = { ["emergency_access"] = true } -restriction_exception_tags = { "motorcar", "motor_vehicle", "vehicle" } +restrictions = { "motorcar", "motor_vehicle", "vehicle" } -- A list of suffixes to suppress in name change instructions suffix_list = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "North", "South", "West", "East" } @@ -172,8 +172,8 @@ function get_name_suffix_list(vector) end end -function get_exceptions(vector) - for i,v in ipairs(restriction_exception_tags) do +function get_restrictions(vector) + for i,v in ipairs(restrictions) do vector:Add(v) end end @@ -238,6 +238,10 @@ function way_function (way, result) return end + -- default to driving mode, may get overwritten below + result.forward_mode = mode.driving + result.backward_mode = mode.driving + -- we dont route over areas local area = way:get_value_by_key("area") if ignore_areas and area and "yes" == area then @@ -245,10 +249,58 @@ function way_function (way, result) end -- respect user-preference for HOV-only ways - local hov = way:get_value_by_key("hov") - if ignore_hov_ways and hov and "designated" == hov then - return - end + if ignore_hov_ways then + local hov = way:get_value_by_key("hov") + if hov and "designated" == hov then + return + end + + -- also respect user-preference for HOV-only ways when all lanes are HOV-designated + local function has_all_designated_hov_lanes(lanes) + local all = true + for lane in lanes:gmatch("(%w+)") do + if lane and lane ~= "designated" then + all = false + break + end + end + return all + end + + local hov_lanes = way:get_value_by_key("hov:lanes") + local hov_lanes_forward = way:get_value_by_key("hov:lanes:forward") + local hov_lanes_backward = way:get_value_by_key("hov:lanes:backward") + + local hov_all_designated = hov_lanes and hov_lanes ~= "" + and has_all_designated_hov_lanes(hov_lanes) + + local hov_all_designated_forward = hov_lanes_forward and hov_lanes_forward ~= "" + and has_all_designated_hov_lanes(hov_lanes_forward) + + local hov_all_designated_backward = hov_lanes_backward and hov_lanes_backward ~= "" + and has_all_designated_hov_lanes(hov_lanes_backward) + + -- forward/backward lane depend on a way's direction + local oneway = way:get_value_by_key("oneway") + local reverse = oneway and oneway == "-1" + + if hov_all_designated or hov_all_designated_forward then + if reverse then + result.backward_mode = mode.inaccessible + else + result.forward_mode = mode.inaccessible + end + end + + if hov_all_designated_backward then + if reverse then + result.forward_mode = mode.inaccessible + else + result.backward_mode = mode.inaccessible + end + end + + end -- hov handling -- respect user-preference for toll=yes ways local toll = way:get_value_by_key("toll") @@ -278,9 +330,6 @@ function way_function (way, result) return end - result.forward_mode = mode.driving - result.backward_mode = mode.driving - -- handling ferries and piers local route_speed = speed_profile[route] if (route_speed and route_speed > 0) then @@ -387,14 +436,14 @@ function way_function (way, result) local has_name = name and "" ~= name local has_pronunciation = pronunciation and "" ~= pronunciation - if has_name and has_ref then - result.name = name .. " (" .. ref .. ")" - elseif has_ref then - result.name = ref - elseif has_name then + if has_name then result.name = name end + if has_ref then + result.ref = ref + end + if has_pronunciation then result.pronunciation = pronunciation end @@ -456,10 +505,6 @@ function way_function (way, result) local destination = get_destination(way) local has_destination = destination and "" ~= destination - if has_destination and has_name and not has_ref then - result.name = name .. " (" .. destination .. ")" - end - result.destinations = destination end end diff --git a/profiles/foot.lua b/profiles/foot.lua index 002f06d7874..cf9c8f5d85f 100644 --- a/profiles/foot.lua +++ b/profiles/foot.lua @@ -10,7 +10,7 @@ access_tag_restricted = { ["destination"] = true, ["delivery"] = true } access_tags_hierarchy = { "foot", "access" } service_tag_restricted = { ["parking_aisle"] = true } ignore_in_grid = { ["ferry"] = true } -restriction_exception_tags = { "foot" } +restrictions = { "foot" } walking_speed = 5 @@ -71,8 +71,8 @@ properties.continue_straight_at_waypoint = false local fallback_names = true -function get_exceptions(vector) - for i,v in ipairs(restriction_exception_tags) do +function get_restrictions(vector) + for i,v in ipairs(restrictions) do vector:Add(v) end end @@ -150,16 +150,15 @@ function way_function (way, result) local surface = way:get_value_by_key("surface") -- name - if ref and "" ~= ref and name and "" ~= name then - result.name = name .. " (" .. ref .. ")" - elseif ref and "" ~= ref then - result.name = ref - elseif name and "" ~= name then + if name and "" ~= name then result.name = name elseif highway and fallback_names then result.name = "{highway:"..highway.."}" -- if no name exists, use way type -- this encoding scheme is excepted to be a temporary solution end + if ref and "" ~= ref then + result.ref = ref + end -- roundabouts if "roundabout" == junction then diff --git a/profiles/lhs.lua b/profiles/lhs.lua deleted file mode 100644 index 84f192428c8..00000000000 --- a/profiles/lhs.lua +++ /dev/null @@ -1,20 +0,0 @@ --- Testbot, with turn penalty --- Used for testing turn penalties - -require 'testbot' - -properties.left_hand_driving = true - -local turn_penalty = 50 -local turn_bias = properties.left_hand_driving and 1/1.2 or 1.2 - -function turn_function (angle) - ---- compute turn penalty as angle^2, with a left/right bias - -- multiplying by 10 converts to deci-seconds see issue #1318 - k = 10*turn_penalty/(90.0*90.0) - if angle>=0 then - return angle*angle*k/turn_bias - else - return angle*angle*k*turn_bias - end -end diff --git a/profiles/rasterbot.lua b/profiles/rasterbot.lua index 03ff8f269dc..bc2b2b247b9 100644 --- a/profiles/rasterbot.lua +++ b/profiles/rasterbot.lua @@ -21,8 +21,12 @@ function way_function (way, result) end function source_function () + local path = os.getenv('OSRM_RASTER_SOURCE') + if not path then + path = "rastersource.asc" + end raster_source = sources:load( - "../test/rastersource.asc", + path, 0, -- lon_min 0.1, -- lon_max 0, -- lat_min diff --git a/profiles/rasterbotinterp.lua b/profiles/rasterbotinterp.lua index 8266b07c495..f81e6e2f5a5 100644 --- a/profiles/rasterbotinterp.lua +++ b/profiles/rasterbotinterp.lua @@ -21,8 +21,12 @@ function way_function (way, result) end function source_function () + local path = os.getenv('OSRM_RASTER_SOURCE') + if not path then + path = "rastersource.asc" + end raster_source = sources:load( - "../test/rastersource.asc", + path, 0, -- lon_min 0.1, -- lon_max 0, -- lat_min diff --git a/profiles/rhs.lua b/profiles/rhs.lua deleted file mode 100644 index 022a1443a83..00000000000 --- a/profiles/rhs.lua +++ /dev/null @@ -1,20 +0,0 @@ --- Testbot, with turn penalty --- Used for testing turn penalties - -require 'testbot' - -properties.left_hand_driving = false - -local turn_penalty = 50 -local turn_bias = properties.left_hand_driving and 1/1.2 or 1.2 - -function turn_function (angle) - ---- compute turn penalty as angle^2, with a left/right bias - -- multiplying by 10 converts to deci-seconds see issue #1318 - k = 10*turn_penalty/(90.0*90.0) - if angle>=0 then - return angle*angle*k/turn_bias - else - return angle*angle*k*turn_bias - end -end diff --git a/scripts/travis/before_install.i686.sh b/scripts/travis/before_install.i686.sh index ce8414b435b..beaeb2f511c 100644 --- a/scripts/travis/before_install.i686.sh +++ b/scripts/travis/before_install.i686.sh @@ -7,4 +7,4 @@ export CXXFLAGS='-m32 -msse2 -mfpmath=sse' sudo dpkg --add-architecture i386 sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test && ( sudo apt-get update -qq --yes || true ) -sudo apt-get install -qq --yes --force-yes g++-5-multilib libxml2-dev:i386 libexpat1-dev:i386 libzip-dev:i386 libbz2-dev:i386 libstxxl-dev:i386 libtbb-dev:i386 lua5.2:i386 liblua5.2-dev:i386 libluabind-dev:i386 libboost-date-time-dev:i386 libboost-filesystem-dev:i386 libboost-iostreams-dev:i386 libboost-program-options-dev:i386 libboost-regex-dev:i386 libboost-system-dev:i386 libboost-thread-dev:i386 libboost-test-dev:i386 +sudo apt-get install -qq --yes --force-yes g++-6-multilib libxml2-dev:i386 libexpat1-dev:i386 libzip-dev:i386 libbz2-dev:i386 libstxxl-dev:i386 libtbb-dev:i386 lua5.2:i386 liblua5.2-dev:i386 libluabind-dev:i386 libboost-date-time-dev:i386 libboost-filesystem-dev:i386 libboost-iostreams-dev:i386 libboost-program-options-dev:i386 libboost-regex-dev:i386 libboost-system-dev:i386 libboost-thread-dev:i386 libboost-test-dev:i386 diff --git a/scripts/update_depdendencies.sh b/scripts/update_dependencies.sh similarity index 70% rename from scripts/update_depdendencies.sh rename to scripts/update_dependencies.sh index ec0210c6dea..7b2398af316 100755 --- a/scripts/update_depdendencies.sh +++ b/scripts/update_dependencies.sh @@ -1,7 +1,12 @@ #!/usr/bin/env bash +# Note: once the subtree merge from this script has been committed and pushed to +# a branch do not attempt to rebase the branch back onto master or the subdirectory +# structure will be lost. +# http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html + OSMIUM_REPO=https://github.com/osmcode/libosmium.git -OSMIUM_TAG=v2.6.1 +OSMIUM_TAG=v2.9.0 VARIANT_REPO=https://github.com/mapbox/variant.git VARIANT_TAG=v1.1.0 diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp index ca45a63aa14..efa114b81b9 100644 --- a/src/contractor/contractor.cpp +++ b/src/contractor/contractor.cpp @@ -81,6 +81,41 @@ inline EdgeWeight distanceAndSpeedToWeight(double distance_in_meters, double spe return std::max(1, static_cast(std::round(duration * 10))); } +// Returns updated edge weight +template +EdgeWeight getNewWeight(IterType speed_iter, + const double &segment_length, + const std::vector &segment_speed_filenames, + const EdgeWeight old_weight, + const double log_edge_updates_factor) +{ + const auto new_segment_weight = + (speed_iter->speed_source.speed > 0) + ? distanceAndSpeedToWeight(segment_length, speed_iter->speed_source.speed) + : INVALID_EDGE_WEIGHT; + // the check here is enabled by the `--edge-weight-updates-over-factor` flag + // it logs a warning if the new weight exceeds a heuristic of what a reasonable weight update is + if (log_edge_updates_factor > 0 && old_weight != 0) + { + auto new_secs = new_segment_weight / 10.0; + auto old_secs = old_weight / 10.0; + auto approx_original_speed = (segment_length / old_secs) * 3.6; + if (old_weight >= (new_segment_weight * log_edge_updates_factor)) + { + auto speed_file = segment_speed_filenames.at(speed_iter->speed_source.source - 1); + util::SimpleLogger().Write(logWARNING) + << "[weight updates] Edge weight update from " << old_secs << "s to " << new_secs + << "s New speed: " << speed_iter->speed_source.speed << " kph" + << ". Old speed: " << approx_original_speed << " kph" + << ". Segment length: " << segment_length << " m" + << ". Segment: " << speed_iter->segment.from << "," << speed_iter->segment.to + << " based on " << speed_file; + } + } + + return new_segment_weight; +} + int Contractor::Run() { #ifdef WIN32 @@ -113,7 +148,8 @@ int Contractor::Run() config.geometry_path, config.datasource_names_path, config.datasource_indexes_path, - config.rtree_leaf_path); + config.rtree_leaf_path, + config.log_edge_updates_factor); // Contracting the edge-expanded graph @@ -370,7 +406,8 @@ EdgeID Contractor::LoadEdgeExpandedGraph( const std::string &geometry_filename, const std::string &datasource_names_filename, const std::string &datasource_indexes_filename, - const std::string &rtree_leaf_filename) + const std::string &rtree_leaf_filename, + const double log_edge_updates_factor) { if (segment_speed_filenames.size() > 255 || turn_penalty_filenames.size() > 255) throw util::exception("Limit of 255 segment speed and turn penalty files each reached"); @@ -556,102 +593,66 @@ EdgeID Contractor::LoadEdgeExpandedGraph( extractor::QueryNode *u; extractor::QueryNode *v; - if (leaf_object.forward_packed_geometry_id != SPECIAL_EDGEID) + const unsigned forward_begin = + m_geometry_indices.at(leaf_object.packed_geometry_id); + const auto current_segment = + &(m_geometry_list[forward_begin + leaf_object.fwd_segment_position]); + + u = &(internal_to_external_node_map + [m_geometry_list[forward_begin + + leaf_object.fwd_segment_position] + .node_id]); + v = &(internal_to_external_node_map + [m_geometry_list[forward_begin + leaf_object.fwd_segment_position + 1] + .node_id]); + + const double segment_length = util::coordinate_calculation::greatCircleDistance( + util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat}); + + auto forward_speed_iter = + find(segment_speed_lookup, Segment{u->node_id, v->node_id}); + if (forward_speed_iter != segment_speed_lookup.end()) { - const unsigned forward_begin = - m_geometry_indices.at(leaf_object.forward_packed_geometry_id); - - if (leaf_object.fwd_segment_position == 0) - { - u = &(internal_to_external_node_map[leaf_object.u]); - v = &( - internal_to_external_node_map[m_geometry_list[forward_begin].node_id]); - } - else - { - u = &(internal_to_external_node_map - [m_geometry_list[forward_begin + - leaf_object.fwd_segment_position - 1] - .node_id]); - v = &(internal_to_external_node_map - [m_geometry_list[forward_begin + leaf_object.fwd_segment_position] - .node_id]); - } - const double segment_length = util::coordinate_calculation::greatCircleDistance( - util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat}); - - auto forward_speed_iter = - find(segment_speed_lookup, Segment{u->node_id, v->node_id}); - if (forward_speed_iter != segment_speed_lookup.end()) - { - auto new_segment_weight = - (forward_speed_iter->speed_source.speed > 0) - ? distanceAndSpeedToWeight(segment_length, - forward_speed_iter->speed_source.speed) - : INVALID_EDGE_WEIGHT; - m_geometry_list[forward_begin + leaf_object.fwd_segment_position].weight = - new_segment_weight; - m_geometry_datasource[forward_begin + leaf_object.fwd_segment_position] = - forward_speed_iter->speed_source.source; - - // count statistics for logging - counters[forward_speed_iter->speed_source.source] += 1; - } - else - { - // count statistics for logging - counters[LUA_SOURCE] += 1; - } + const auto new_segment_weight = getNewWeight(forward_speed_iter, + segment_length, + segment_speed_filenames, + current_segment->forward_weight, + log_edge_updates_factor); + + m_geometry_list[forward_begin + 1 + leaf_object.fwd_segment_position].forward_weight = + new_segment_weight; + m_geometry_datasource[forward_begin + 1 + leaf_object.fwd_segment_position] = + forward_speed_iter->speed_source.source; + + // count statistics for logging + counters[forward_speed_iter->speed_source.source] += 1; } - if (leaf_object.reverse_packed_geometry_id != SPECIAL_EDGEID) + else { - const unsigned reverse_begin = - m_geometry_indices.at(leaf_object.reverse_packed_geometry_id); - const unsigned reverse_end = - m_geometry_indices.at(leaf_object.reverse_packed_geometry_id + 1); - - int rev_segment_position = - (reverse_end - reverse_begin) - leaf_object.fwd_segment_position - 1; - if (rev_segment_position == 0) - { - u = &(internal_to_external_node_map[leaf_object.v]); - v = &( - internal_to_external_node_map[m_geometry_list[reverse_begin].node_id]); - } - else - { - u = &( - internal_to_external_node_map[m_geometry_list[reverse_begin + - rev_segment_position - 1] - .node_id]); - v = &(internal_to_external_node_map - [m_geometry_list[reverse_begin + rev_segment_position].node_id]); - } - const double segment_length = util::coordinate_calculation::greatCircleDistance( - util::Coordinate{u->lon, u->lat}, util::Coordinate{v->lon, v->lat}); + // count statistics for logging + counters[LUA_SOURCE] += 1; + } - auto reverse_speed_iter = - find(segment_speed_lookup, Segment{u->node_id, v->node_id}); - if (reverse_speed_iter != segment_speed_lookup.end()) - { - auto new_segment_weight = - (reverse_speed_iter->speed_source.speed > 0) - ? distanceAndSpeedToWeight(segment_length, - reverse_speed_iter->speed_source.speed) - : INVALID_EDGE_WEIGHT; - m_geometry_list[reverse_begin + rev_segment_position].weight = - new_segment_weight; - m_geometry_datasource[reverse_begin + rev_segment_position] = - reverse_speed_iter->speed_source.source; - - // count statistics for logging - counters[reverse_speed_iter->speed_source.source] += 1; - } - else - { - // count statistics for logging - counters[LUA_SOURCE] += 1; - } + const auto reverse_speed_iter = + find(segment_speed_lookup, Segment{v->node_id, u->node_id}); + if (reverse_speed_iter != segment_speed_lookup.end()) + { + const auto new_segment_weight = getNewWeight(reverse_speed_iter, + segment_length, + segment_speed_filenames, + current_segment->reverse_weight, + log_edge_updates_factor); + m_geometry_list[forward_begin + leaf_object.fwd_segment_position].reverse_weight = + new_segment_weight; + m_geometry_datasource[forward_begin + leaf_object.fwd_segment_position] = + reverse_speed_iter->speed_source.source; + + // count statistics for logging + counters[reverse_speed_iter->speed_source.source] += 1; + } + else + { + counters[LUA_SOURCE] += 1; } } }); // parallel_for_each @@ -783,7 +784,7 @@ EdgeID Contractor::LoadEdgeExpandedGraph( { if (speed_iter->speed_source.speed > 0) { - auto new_segment_weight = distanceAndSpeedToWeight( + const auto new_segment_weight = distanceAndSpeedToWeight( segmentblocks[i].segment_length, speed_iter->speed_source.speed); new_weight += new_segment_weight; } diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp index 9252bd0de02..7e30e000467 100644 --- a/src/engine/api/json_factory.cpp +++ b/src/engine/api/json_factory.cpp @@ -235,6 +235,8 @@ util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geo route_step.values["distance"] = std::round(step.distance * 10) / 10.; route_step.values["duration"] = std::round(step.duration * 10) / 10.; route_step.values["name"] = std::move(step.name); + if (!step.ref.empty()) + route_step.values["ref"] = std::move(step.ref); if (!step.pronunciation.empty()) route_step.values["pronunciation"] = std::move(step.pronunciation); if (!step.destinations.empty()) diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 257794b56ed..11c10df98d9 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -16,7 +16,6 @@ #include "engine/datafacade/shared_datafacade.hpp" #include "storage/shared_barriers.hpp" -#include "util/make_unique.hpp" #include "util/simple_logger.hpp" #include @@ -86,38 +85,33 @@ namespace // Abstracted away the query locking into a template function // Works the same for every plugin. template -osrm::engine::Status RunQuery(const std::unique_ptr &lock, - osrm::engine::datafacade::BaseDataFacade &facade, - const ParameterT ¶meters, - PluginT &plugin, - ResultT &result) +osrm::engine::Status +RunQuery(const std::unique_ptr &lock, + const std::shared_ptr &facade, + const ParameterT ¶meters, + PluginT &plugin, + ResultT &result) { if (!lock) { - return plugin.HandleRequest(parameters, result); + return plugin.HandleRequest(facade, parameters, result); } BOOST_ASSERT(lock); lock->IncreaseQueryCount(); - auto &shared_facade = static_cast(facade); + auto &shared_facade = static_cast(*facade); shared_facade.CheckAndReloadFacade(); // Get a shared data lock so that other threads won't update // things while the query is running boost::shared_lock data_lock{shared_facade.data_mutex}; - osrm::engine::Status status = plugin.HandleRequest(parameters, result); + osrm::engine::Status status = plugin.HandleRequest(facade, parameters, result); lock->DecreaseQueryCount(); return status; } -template -std::unique_ptr create(Facade &facade, Args... args) -{ - return osrm::util::make_unique(facade, std::forward(args)...); -} - } // anon. ns namespace osrm @@ -125,12 +119,12 @@ namespace osrm namespace engine { -Engine::Engine(EngineConfig &config) +Engine::Engine(const EngineConfig &config) { if (config.use_shared_memory) { - lock = util::make_unique(); - query_data_facade = util::make_unique(); + lock = std::make_unique(); + query_data_facade = std::make_shared(); } else { @@ -138,23 +132,21 @@ Engine::Engine(EngineConfig &config) { throw util::exception("Invalid file paths given!"); } - query_data_facade = - util::make_unique(config.storage_config); + query_data_facade = std::make_shared(config.storage_config); } // Register plugins using namespace plugins; - route_plugin = create(*query_data_facade, config.max_locations_viaroute); - table_plugin = create(*query_data_facade, config.max_locations_distance_table); - nearest_plugin = create(*query_data_facade); - trip_plugin = create(*query_data_facade, config.max_locations_trip); - match_plugin = create(*query_data_facade, config.max_locations_map_matching); - tile_plugin = create(*query_data_facade); + route_plugin = std::make_unique(config.max_locations_viaroute); + table_plugin = std::make_unique(config.max_locations_distance_table); + nearest_plugin = std::make_unique(config.max_results_nearest); + trip_plugin = std::make_unique(config.max_locations_trip); + match_plugin = std::make_unique(config.max_locations_map_matching); + tile_plugin = std::make_unique(); if (config.use_isochrone) { - isochrone_plugin = - create(*query_data_facade, config.storage_config.base.string()); + isochrone_plugin =std::make_unique(config.storage_config.base.string()); } } @@ -163,38 +155,38 @@ Engine::~Engine() = default; Engine::Engine(Engine &&) noexcept = default; Engine &Engine::operator=(Engine &&) noexcept = default; -Status Engine::Route(const api::RouteParameters ¶ms, util::json::Object &result) +Status Engine::Route(const api::RouteParameters ¶ms, util::json::Object &result) const { - return RunQuery(lock, *query_data_facade, params, *route_plugin, result); + return RunQuery(lock, query_data_facade, params, *route_plugin, result); } -Status Engine::Table(const api::TableParameters ¶ms, util::json::Object &result) +Status Engine::Table(const api::TableParameters ¶ms, util::json::Object &result) const { - return RunQuery(lock, *query_data_facade, params, *table_plugin, result); + return RunQuery(lock, query_data_facade, params, *table_plugin, result); } -Status Engine::Nearest(const api::NearestParameters ¶ms, util::json::Object &result) +Status Engine::Nearest(const api::NearestParameters ¶ms, util::json::Object &result) const { - return RunQuery(lock, *query_data_facade, params, *nearest_plugin, result); + return RunQuery(lock, query_data_facade, params, *nearest_plugin, result); } -Status Engine::Trip(const api::TripParameters ¶ms, util::json::Object &result) +Status Engine::Trip(const api::TripParameters ¶ms, util::json::Object &result) const { - return RunQuery(lock, *query_data_facade, params, *trip_plugin, result); + return RunQuery(lock, query_data_facade, params, *trip_plugin, result); } -Status Engine::Match(const api::MatchParameters ¶ms, util::json::Object &result) +Status Engine::Match(const api::MatchParameters ¶ms, util::json::Object &result) const { - return RunQuery(lock, *query_data_facade, params, *match_plugin, result); + return RunQuery(lock, query_data_facade, params, *match_plugin, result); } -Status Engine::Tile(const api::TileParameters ¶ms, std::string &result) +Status Engine::Tile(const api::TileParameters ¶ms, std::string &result) const { - return RunQuery(lock, *query_data_facade, params, *tile_plugin, result); + return RunQuery(lock, query_data_facade, params, *tile_plugin, result); } -Status Engine::Isochrone(const api::IsochroneParameters ¶ms, util::json::Object &result) +Status Engine::Isochrone(const api::IsochroneParameters ¶ms, util::json::Object &result) const { - return RunQuery(lock, *query_data_facade, params, *isochrone_plugin, result); + return RunQuery(lock, query_data_facade, params, *isochrone_plugin, result); } } // engine ns diff --git a/src/engine/engine_config.cpp b/src/engine/engine_config.cpp index e26a67eed9f..9d322486fe9 100644 --- a/src/engine/engine_config.cpp +++ b/src/engine/engine_config.cpp @@ -15,11 +15,15 @@ bool EngineConfig::IsValid() const storage_config.datasource_names_path.empty() && storage_config.datasource_indexes_path.empty() && storage_config.names_data_path.empty(); - const bool limits_valid = - (max_locations_distance_table == -1 || max_locations_distance_table > 2) && - (max_locations_map_matching == -1 || max_locations_map_matching > 2) && - (max_locations_trip == -1 || max_locations_trip > 2) && - (max_locations_viaroute == -1 || max_locations_viaroute > 2); + const auto unlimited_or_more_than = [](const int v, const int limit) { + return v == -1 || v > limit; + }; + + const bool limits_valid = unlimited_or_more_than(max_locations_distance_table, 2) && + unlimited_or_more_than(max_locations_map_matching, 2) && + unlimited_or_more_than(max_locations_trip, 2) && + unlimited_or_more_than(max_locations_viaroute, 2) && + unlimited_or_more_than(max_results_nearest, 0); return ((use_shared_memory && all_path_are_empty) || storage_config.IsValid()) && limits_valid; } diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp index 3dbe0a21459..7f02b664c3f 100644 --- a/src/engine/guidance/post_processing.cpp +++ b/src/engine/guidance/post_processing.cpp @@ -52,6 +52,8 @@ inline void forwardStepSignage(RouteStep &destination, const RouteStep &origin) destination.name = origin.name; destination.pronunciation = origin.pronunciation; destination.destinations = origin.destinations; + destination.destinations = origin.destinations; + destination.ref = origin.ref; } inline bool choiceless(const RouteStep &step, const RouteStep &previous) @@ -81,18 +83,6 @@ bool isCollapsableInstruction(const TurnInstruction instruction) bool compatible(const RouteStep &lhs, const RouteStep &rhs) { return lhs.mode == rhs.mode; } -double nameSegmentLength(std::size_t at, const std::vector &steps) -{ - double result = steps[at].distance; - while (at + 1 < steps.size() && steps[at + 1].name_id == steps[at].name_id) - { - ++at; - result += steps[at].distance; - } - - return result; -} - // invalidate a step and set its content to nothing void invalidateStep(RouteStep &step) { step = getInvalidRouteStep(); } @@ -127,6 +117,28 @@ double turn_angle(const double entry_bearing, const double exit_bearing) return angle > 360 ? angle - 360 : angle; } +// Checks if name change happens the user wants to know about. +// Treats e.g. "Name (Ref)" -> "Name" changes still as same name. +bool isNoticeableNameChange(const RouteStep &lhs, const RouteStep &rhs) +{ + // TODO: at some point we might want to think about pronunciation here. + // Also rotary_name is not handled at the moment. + return util::guidance::requiresNameAnnounced(lhs.name, lhs.ref, rhs.name, rhs.ref); +} + +double nameSegmentLength(std::size_t at, const std::vector &steps) +{ + BOOST_ASSERT(at < steps.size()); + + double result = steps[at].distance; + while (at + 1 < steps.size() && !isNoticeableNameChange(steps[at], steps[at + 1])) + { + at += 1; + result += steps[at].distance; + } + return result; +} + OSRM_ATTR_WARN_UNUSED RouteStep forwardInto(RouteStep destination, const RouteStep &source) { @@ -201,6 +213,8 @@ bool setUpRoundabout(RouteStep &step) instruction.type == TurnType::EnterRoundaboutAtExit || instruction.type == TurnType::EnterRoundaboutIntersectionAtExit) { + // Here we consider an actual entry, not an exit. We simply have to count the additional + // exit step.maneuver.exit = 1; // prevent futher special case handling of these two. if (instruction.type == TurnType::EnterRotaryAtExit) @@ -213,6 +227,7 @@ bool setUpRoundabout(RouteStep &step) if (leavesRoundabout(instruction)) { + // This set-up, even though it looks the same, is actually looking at entering AND exiting step.maneuver.exit = 1; // count the otherwise missing exit // prevent futher special case handling of these two. @@ -404,12 +419,16 @@ void collapseTurnAt(std::vector &steps, if (compatible(one_back_step, current_step)) { steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]); + if ((TurnType::Continue == one_back_step.maneuver.instruction.type || TurnType::Suppressed == one_back_step.maneuver.instruction.type) && - current_step.name_id != steps[two_back_index].name_id) + isNoticeableNameChange(steps[two_back_index], current_step)) + { + steps[one_back_index].maneuver.instruction.type = TurnType::Turn; + } else if (TurnType::Turn == one_back_step.maneuver.instruction.type && - current_step.name_id == steps[two_back_index].name_id) + !isNoticeableNameChange(steps[two_back_index], current_step)) { steps[one_back_index].maneuver.instruction.type = TurnType::Continue; @@ -455,7 +474,7 @@ void collapseTurnAt(std::vector &steps, { BOOST_ASSERT(two_back_index < steps.size()); // the simple case is a u-turn that changes directly into the in-name again - const bool direct_u_turn = steps[two_back_index].name_id == current_step.name_id; + const bool direct_u_turn = !isNoticeableNameChange(steps[two_back_index], current_step); // however, we might also deal with a dual-collapse scenario in which we have to // additionall collapse a name-change as welll @@ -466,22 +485,25 @@ void collapseTurnAt(std::vector &steps, isCollapsableInstruction(steps[next_step_index].maneuver.instruction)); const bool u_turn_with_name_change = continues_with_name_change && - steps[next_step_index].name_id == steps[two_back_index].name_id; + !isNoticeableNameChange(steps[two_back_index], steps[next_step_index]); if (direct_u_turn || u_turn_with_name_change) { steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]); invalidateStep(steps[step_index]); - if (u_turn_with_name_change) + if (u_turn_with_name_change && + compatible(steps[one_back_index], steps[next_step_index])) { steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[next_step_index]); invalidateStep(steps[next_step_index]); // will be skipped due to the // continue statement at the // beginning of this function - } - forwardStepSignage(steps[one_back_index], steps[two_back_index]); + forwardStepSignage(steps[one_back_index], steps[two_back_index]); + } + if (direct_u_turn) + forwardStepSignage(steps[one_back_index], steps[two_back_index]); steps[one_back_index].maneuver.instruction.type = TurnType::Continue; steps[one_back_index].maneuver.instruction.direction_modifier = DirectionModifier::UTurn; @@ -539,6 +561,7 @@ RouteStep elongate(RouteStep step, const RouteStep &by_step) { step.duration += by_step.duration; step.distance += by_step.distance; + BOOST_ASSERT(step.mode == by_step.mode); // by_step comes after step -> we append at the end if (step.geometry_end == by_step.geometry_begin + 1) @@ -736,6 +759,8 @@ std::vector collapseTurns(std::vector steps) if (steps[index].maneuver.instruction.type != TurnType::Suppressed && steps[index].maneuver.instruction.type != TurnType::NewName) return false; + if (index + 1 < end_index && !compatible(steps[index], steps[index + 1])) + return false; } return true; }; @@ -784,9 +809,8 @@ std::vector collapseTurns(std::vector steps) // Turn Types in the response depend on whether we find the same road name // (sliproad indcating a u-turn) or if we are turning onto a different road, in // which case we use a turn. - if (steps[getPreviousIndex(one_back_index)].name_id == - steps[step_index].name_id && - steps[step_index].name_id != EMPTY_NAMEID) + if (!isNoticeableNameChange(steps[getPreviousIndex(one_back_index)], + steps[step_index])) steps[one_back_index].maneuver.instruction.type = TurnType::Continue; else steps[one_back_index].maneuver.instruction.type = TurnType::Turn; @@ -820,7 +844,8 @@ std::vector collapseTurns(std::vector steps) // These have to be handled in post-processing else if (isCollapsableInstruction(current_step.maneuver.instruction) && current_step.maneuver.instruction.type != TurnType::Suppressed && - steps[getPreviousNameIndex(step_index)].name_id == current_step.name_id && + !isNoticeableNameChange(steps[getPreviousNameIndex(step_index)], current_step) && + // canCollapseAll is also checking for compatible(step,step+1) for all indices canCollapseAll(getPreviousNameIndex(step_index) + 1, next_step_index)) { BOOST_ASSERT(step_index > 0); @@ -844,8 +869,7 @@ std::vector collapseTurns(std::vector steps) const auto two_back_index = getPreviousIndex(one_back_index); BOOST_ASSERT(two_back_index < steps.size()); // valid, since one_back is collapsable or a turn and therefore not depart: - const auto &coming_from_name_id = steps[two_back_index].name_id; - if (current_step.name_id == coming_from_name_id) + if (!isNoticeableNameChange(steps[two_back_index], current_step)) { if (compatible(one_back_step, steps[two_back_index])) { @@ -861,29 +885,36 @@ std::vector collapseTurns(std::vector steps) else if (nameSegmentLength(one_back_index, steps) < name_segment_cutoff_length && isBasicNameChange(one_back_step) && isBasicNameChange(current_step)) { - steps[two_back_index] = - elongate(std::move(steps[two_back_index]), steps[one_back_index]); - invalidateStep(steps[one_back_index]); - if (nameSegmentLength(step_index, steps) < name_segment_cutoff_length) + if (compatible(steps[two_back_index], steps[one_back_index])) { steps[two_back_index] = - elongate(std::move(steps[two_back_index]), steps[step_index]); - invalidateStep(steps[step_index]); + elongate(std::move(steps[two_back_index]), steps[one_back_index]); + invalidateStep(steps[one_back_index]); + if (nameSegmentLength(step_index, steps) < name_segment_cutoff_length) + { + steps[two_back_index] = + elongate(std::move(steps[two_back_index]), steps[step_index]); + invalidateStep(steps[step_index]); + } } } else if (step_index + 2 < steps.size() && current_step.maneuver.instruction.type == TurnType::NewName && steps[next_step_index].maneuver.instruction.type == TurnType::NewName && - one_back_step.name_id == steps[next_step_index].name_id) + !isNoticeableNameChange(one_back_step, steps[next_step_index])) { - // if we are crossing an intersection and go immediately after into a name change, - // we don't wan't to collapse the initial intersection. - // a - b ---BRIDGE -- c - steps[one_back_index] = - elongate(std::move(steps[one_back_index]), - elongate(std::move(steps[step_index]), steps[next_step_index])); - invalidateStep(steps[step_index]); - invalidateStep(steps[next_step_index]); + if (compatible(steps[step_index], steps[next_step_index])) + { + // if we are crossing an intersection and go immediately after into a name + // change, + // we don't wan't to collapse the initial intersection. + // a - b ---BRIDGE -- c + steps[one_back_index] = + elongate(std::move(steps[one_back_index]), + elongate(std::move(steps[step_index]), steps[next_step_index])); + invalidateStep(steps[step_index]); + invalidateStep(steps[next_step_index]); + } } else if (choiceless(current_step, one_back_step) || one_back_step.distance <= MAX_COLLAPSE_DISTANCE) @@ -901,6 +932,7 @@ std::vector collapseTurns(std::vector steps) // check for one of the multiple collapse scenarios and, if possible, collapse the turn const auto two_back_index = getPreviousIndex(one_back_index); BOOST_ASSERT(two_back_index < steps.size()); + // all turns that are handled lower down are also compatible collapseTurnAt(steps, two_back_index, one_back_index, step_index); } } diff --git a/src/engine/plugins/isochrone.cpp b/src/engine/plugins/isochrone.cpp index aaaf46cbc79..60b99361a87 100644 --- a/src/engine/plugins/isochrone.cpp +++ b/src/engine/plugins/isochrone.cpp @@ -17,8 +17,8 @@ namespace engine namespace plugins { -IsochronePlugin::IsochronePlugin(datafacade::BaseDataFacade &facade, const std::string base) - : BasePlugin{facade}, base{base} +IsochronePlugin::IsochronePlugin(const std::string base) + : base{base} { // Loads Graph into memory @@ -33,7 +33,8 @@ IsochronePlugin::IsochronePlugin(datafacade::BaseDataFacade &facade, const std:: graph_edge_list.shrink_to_fit(); } -Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, +Status IsochronePlugin::HandleRequest(const std::shared_ptr facade, + const api::IsochroneParameters ¶ms, util::json::Object &json_result) { BOOST_ASSERT(params.IsValid()); @@ -57,18 +58,22 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, "InvalidOptions", "If concavehull is set, convexhull must be set too", json_result); } - auto phantomnodes = GetPhantomNodes(params, 1); + auto phantomnodes = GetPhantomNodes(*facade, params, 1); if (phantomnodes.front().size() <= 0) { return Error("PhantomNode", "PhantomNode couldnt be found for coordinate", json_result); } + util::SimpleLogger().Write() << "asdasd"; auto phantom = phantomnodes.front(); std::vector forward_id_vector; - facade.GetUncompressedGeometry(phantom.front().phantom_node.reverse_packed_geometry_id, - forward_id_vector); - auto source = forward_id_vector[0]; + + auto source = (*facade).GetUncompressedReverseGeometry(phantom.front().phantom_node.packed_geometry_id).front(); + +// (*facade).GetUncompressedGeometry(phantom.front().phantom_node.reverse_packed_geometry_id, +// forward_id_vector); +// auto source = phantom.; IsochroneVector isochroneVector; IsochroneVector convexhull; @@ -124,7 +129,7 @@ Status IsochronePlugin::HandleRequest(const api::IsochroneParameters ¶ms, } TIMER_START(RESPONSE); - api::IsochroneAPI isochroneAPI(facade, params); + api::IsochroneAPI isochroneAPI{*facade, params}; isochroneAPI.MakeResponse(isochroneVector, convexhull, concavehull, json_result); TIMER_STOP(RESPONSE); util::SimpleLogger().Write() << "RESPONSE took: " << TIMER_MSEC(RESPONSE) << "ms"; diff --git a/src/engine/plugins/match.cpp b/src/engine/plugins/match.cpp index 41e0d791564..de96abfd5ef 100644 --- a/src/engine/plugins/match.cpp +++ b/src/engine/plugins/match.cpp @@ -6,7 +6,6 @@ #include "engine/map_matching/bayes_classifier.hpp" #include "util/coordinate_calculation.hpp" #include "util/integer_range.hpp" -#include "util/json_logger.hpp" #include "util/json_util.hpp" #include "util/string_util.hpp" @@ -106,7 +105,8 @@ void filterCandidates(const std::vector &coordinates, } } -Status MatchPlugin::HandleRequest(const api::MatchParameters ¶meters, +Status MatchPlugin::HandleRequest(const std::shared_ptr facade, + const api::MatchParameters ¶meters, util::json::Object &json_result) { BOOST_ASSERT(parameters.IsValid()); @@ -151,7 +151,7 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters ¶meters, }); } - auto candidates_lists = GetPhantomNodesInRange(parameters, search_radiuses); + auto candidates_lists = GetPhantomNodesInRange(*facade, parameters, search_radiuses); filterCandidates(parameters.coordinates, candidates_lists); if (std::all_of(candidates_lists.begin(), @@ -166,8 +166,11 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters ¶meters, } // call the actual map matching - SubMatchingList sub_matchings = map_matching( - candidates_lists, parameters.coordinates, parameters.timestamps, parameters.radiuses); + SubMatchingList sub_matchings = map_matching(*facade, + candidates_lists, + parameters.coordinates, + parameters.timestamps, + parameters.radiuses); if (sub_matchings.size() == 0) { @@ -193,11 +196,12 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters ¶meters, // force uturns to be on, since we split the phantom nodes anyway and only have // bi-directional // phantom nodes for possible uturns - shortest_path(sub_routes[index].segment_end_coordinates, {false}, sub_routes[index]); + shortest_path( + *facade, sub_routes[index].segment_end_coordinates, {false}, sub_routes[index]); BOOST_ASSERT(sub_routes[index].shortest_path_length != INVALID_EDGE_WEIGHT); } - api::MatchAPI match_api{BasePlugin::facade, parameters}; + api::MatchAPI match_api{*facade, parameters}; match_api.MakeResponse(sub_matchings, sub_routes, json_result); return Status::Ok; diff --git a/src/engine/plugins/nearest.cpp b/src/engine/plugins/nearest.cpp index 3e54d3393fe..5ccd3e8647c 100644 --- a/src/engine/plugins/nearest.cpp +++ b/src/engine/plugins/nearest.cpp @@ -8,6 +8,7 @@ #include #include +#include namespace osrm { @@ -16,13 +17,23 @@ namespace engine namespace plugins { -NearestPlugin::NearestPlugin(datafacade::BaseDataFacade &facade) : BasePlugin{facade} {} +NearestPlugin::NearestPlugin(const int max_results_) : max_results{max_results_} {} -Status NearestPlugin::HandleRequest(const api::NearestParameters ¶ms, - util::json::Object &json_result) +Status NearestPlugin::HandleRequest(const std::shared_ptr facade, + const api::NearestParameters ¶ms, + util::json::Object &json_result) const { BOOST_ASSERT(params.IsValid()); + if (max_results > 0 && + (boost::numeric_cast(params.number_of_results) > max_results)) + { + return Error("TooBig", + "Number of results " + std::to_string(params.number_of_results) + + " is higher than current maximum (" + std::to_string(max_results) + ")", + json_result); + } + if (!CheckAllCoordinates(params.coordinates)) return Error("InvalidOptions", "Coordinates are invalid", json_result); @@ -31,7 +42,7 @@ Status NearestPlugin::HandleRequest(const api::NearestParameters ¶ms, return Error("InvalidOptions", "Only one input coordinate is supported", json_result); } - auto phantom_nodes = GetPhantomNodes(params, params.number_of_results); + auto phantom_nodes = GetPhantomNodes(*facade, params, params.number_of_results); if (phantom_nodes.front().size() == 0) { @@ -39,7 +50,7 @@ Status NearestPlugin::HandleRequest(const api::NearestParameters ¶ms, } BOOST_ASSERT(phantom_nodes.front().size() > 0); - api::NearestAPI nearest_api(facade, params); + api::NearestAPI nearest_api(*facade, params); nearest_api.MakeResponse(phantom_nodes, json_result); return Status::Ok; diff --git a/src/engine/plugins/table.cpp b/src/engine/plugins/table.cpp index 3cad405e06a..99ceee1e097 100644 --- a/src/engine/plugins/table.cpp +++ b/src/engine/plugins/table.cpp @@ -23,13 +23,14 @@ namespace engine namespace plugins { -TablePlugin::TablePlugin(datafacade::BaseDataFacade &facade, const int max_locations_distance_table) - : BasePlugin{facade}, distance_table(&facade, heaps), - max_locations_distance_table(max_locations_distance_table) +TablePlugin::TablePlugin(const int max_locations_distance_table) + : distance_table(heaps), max_locations_distance_table(max_locations_distance_table) { } -Status TablePlugin::HandleRequest(const api::TableParameters ¶ms, util::json::Object &result) +Status TablePlugin::HandleRequest(const std::shared_ptr facade, + const api::TableParameters ¶ms, + util::json::Object &result) { BOOST_ASSERT(params.IsValid()); @@ -58,15 +59,16 @@ Status TablePlugin::HandleRequest(const api::TableParameters ¶ms, util::json return Error("TooBig", "Too many table coordinates", result); } - auto snapped_phantoms = SnapPhantomNodes(GetPhantomNodes(params)); - auto result_table = distance_table(snapped_phantoms, params.sources, params.destinations); + auto snapped_phantoms = SnapPhantomNodes(GetPhantomNodes(*facade, params)); + auto result_table = + distance_table(*facade, snapped_phantoms, params.sources, params.destinations); if (result_table.empty()) { return Error("NoTable", "No table found", result); } - api::TableAPI table_api{facade, params}; + api::TableAPI table_api{*facade, params}; table_api.MakeResponse(result_table, snapped_phantoms, result); return Status::Ok; diff --git a/src/engine/plugins/tile.cpp b/src/engine/plugins/tile.cpp index 67ebc93d5cf..2f483e49eb9 100644 --- a/src/engine/plugins/tile.cpp +++ b/src/engine/plugins/tile.cpp @@ -1,4 +1,5 @@ #include "engine/plugins/tile.hpp" +#include "engine/edge_unpacker.hpp" #include "engine/plugins/plugin_base.hpp" #include "util/coordinate_calculation.hpp" @@ -13,7 +14,11 @@ #include #include +#include +#include #include +#include +#include #include #include @@ -26,8 +31,11 @@ namespace engine { namespace plugins { -namespace detail +namespace { +// TODO: Port all this encoding logic to https://github.com/mapbox/vector-tile, which wasn't +// available when this code was originally written. + // Simple container class for WGS84 coordinates template struct Point final { @@ -37,12 +45,7 @@ template struct Point final const T y; }; -// from mapnik-vector-tile -namespace pbf -{ -inline unsigned encode_length(const unsigned len) { return (len << 3u) | 2u; } -} - +// Simple container to hold a bounding box struct BBox final { BBox(const double _minx, const double _miny, const double _maxx, const double _maxy) @@ -68,9 +71,36 @@ struct point_type_i final const std::int64_t y; }; -using FixedLine = std::vector>; -using FloatLine = std::vector>; +// Used to accumulate all the information we want in the tile about +// a turn. +struct TurnData final +{ + TurnData(const util::Coordinate coordinate_, + const std::size_t _in, + const std::size_t _out, + const std::size_t _weight) + : coordinate(std::move(coordinate_)), in_angle_offset(_in), turn_angle_offset(_out), + weight_offset(_weight) + { + } + + const util::Coordinate coordinate; + const std::size_t in_angle_offset; + const std::size_t turn_angle_offset; + const std::size_t weight_offset; +}; + +using FixedPoint = Point; +using FloatPoint = Point; + +using FixedLine = std::vector; +using FloatLine = std::vector; + +constexpr const static int MIN_ZOOM_FOR_TURNS = 15; +// We use boost::geometry to clip lines/points that are outside or cross the boundary +// of the tile we're rendering. We need these types defined to use boosts clipping +// logic typedef boost::geometry::model::point point_t; typedef boost::geometry::model::linestring linestring_t; typedef boost::geometry::model::box box_t; @@ -92,15 +122,20 @@ inline bool encodeLinestring(const FixedLine &line, return false; } - const unsigned line_to_length = static_cast(line_size) - 1; + const unsigned lineto_count = static_cast(line_size) - 1; auto pt = line.begin(); - geometry.add_element(9); // move_to | (1 << 3) + const constexpr int MOVETO_COMMAND = 9; + geometry.add_element(MOVETO_COMMAND); // move_to | (1 << 3) geometry.add_element(protozero::encode_zigzag32(pt->x - start_x)); geometry.add_element(protozero::encode_zigzag32(pt->y - start_y)); start_x = pt->x; start_y = pt->y; - geometry.add_element(detail::pbf::encode_length(line_to_length)); + // This means LINETO repeated N times + // See: https://github.com/mapbox/vector-tile-spec/tree/master/2.1#example-command-integers + geometry.add_element((lineto_count << 3u) | 2u); + // Now that we've issued the LINETO REPEAT N command, we append + // N coordinate pairs immediately after the command. for (++pt; pt != line.end(); ++pt) { const std::int32_t dx = pt->x - start_x; @@ -113,9 +148,31 @@ inline bool encodeLinestring(const FixedLine &line, return true; } +// from mapnik-vctor-tile +// Encodes a point +inline void encodePoint(const FixedPoint &pt, protozero::packed_field_uint32 &geometry) +{ + const constexpr int MOVETO_COMMAND = 9; + geometry.add_element(MOVETO_COMMAND); + const std::int32_t dx = pt.x; + const std::int32_t dy = pt.y; + // Manual zigzag encoding. + geometry.add_element(protozero::encode_zigzag32(dx)); + geometry.add_element(protozero::encode_zigzag32(dy)); +} + +/** + * Returnx the x1,y1,x2,y2 pixel coordinates of a line in a given + * tile. + * + * @param start the first coordinate of the line + * @param target the last coordinate of the line + * @param tile_bbox the boundaries of the tile, in mercator coordinates + * @return a FixedLine with coordinates relative to the tile_bbox. + */ FixedLine coordinatesToTileLine(const util::Coordinate start, const util::Coordinate target, - const detail::BBox &tile_bbox) + const BBox &tile_bbox) { FloatLine geo_line; geo_line.emplace_back(static_cast(util::toFloating(start.lon)), @@ -162,9 +219,59 @@ FixedLine coordinatesToTileLine(const util::Coordinate start, return tile_line; } + +/** + * Converts lon/lat into coordinates inside a Mercator projection tile (x/y pixel values) + * + * @param point the lon/lat you want the tile coords for + * @param tile_bbox the mercator boundaries of the tile + * @return a point (x,y) on the tile defined by tile_bbox + */ +FixedPoint coordinatesToTilePoint(const util::Coordinate point, const BBox &tile_bbox) +{ + const FloatPoint geo_point{static_cast(util::toFloating(point.lon)), + static_cast(util::toFloating(point.lat))}; + + const double px_merc = geo_point.x * util::web_mercator::DEGREE_TO_PX; + const double py_merc = util::web_mercator::latToY(util::FloatLatitude{geo_point.y}) * + util::web_mercator::DEGREE_TO_PX; + + const auto px = static_cast(std::round( + ((px_merc - tile_bbox.minx) * util::web_mercator::TILE_SIZE / tile_bbox.width()) * + util::vector_tile::EXTENT / util::web_mercator::TILE_SIZE)); + const auto py = static_cast(std::round( + ((tile_bbox.maxy - py_merc) * util::web_mercator::TILE_SIZE / tile_bbox.height()) * + util::vector_tile::EXTENT / util::web_mercator::TILE_SIZE)); + + return FixedPoint{px, py}; +} + +/** + * Unpacks a single CH edge (NodeID->NodeID) down to the original edges, and returns a list of the + * edge data + * @param from the node the CH edge starts at + * @param to the node the CH edge finishes at + * @param unpacked_path the sequence of EdgeData objects along the unpacked path + */ +void UnpackEdgeToEdges(const datafacade::BaseDataFacade &facade, + const NodeID from, + const NodeID to, + std::vector &unpacked_path) +{ + std::array path{{from, to}}; + UnpackCHPath(facade, + path.begin(), + path.end(), + [&unpacked_path](const std::pair & /* edge */, + const datafacade::BaseDataFacade::EdgeData &data) { + unpacked_path.emplace_back(data); + }); } +} // namespace -Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::string &pbf_buffer) +Status TilePlugin::HandleRequest(const std::shared_ptr facade, + const api::TileParameters ¶meters, + std::string &pbf_buffer) const { BOOST_ASSERT(parameters.IsValid()); @@ -179,69 +286,361 @@ Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::str // Fetch all the segments that are in our bounding box. // This hits the OSRM StaticRTree - const auto edges = facade.GetEdgesInBox(southwest, northeast); - - std::vector used_weights; - std::unordered_map weight_offsets; - uint8_t max_datasource_id = 0; + const auto edges = facade->GetEdgesInBox(southwest, northeast); + + // Vector tiles encode properties as references to a common lookup table. + // When we add a property to a "feature", we actually attach the index of the value + // rather than the value itself. Thus, we need to keep a list of the unique + // values we need, and we add this list to the tile as a lookup table. This + // vector holds all the actual used values, the feature refernce offsets in + // this vector. + // for integer values + std::vector used_line_ints; + // While constructing the tile, we keep track of which integers we have in our table + // and their offsets, so multiple features can re-use the same values + std::unordered_map line_int_offsets; + + // Same idea for street names - one lookup table for names for all features std::vector names; std::unordered_map name_offsets; - // Loop over all edges once to tally up all the attributes we'll need. - // We need to do this so that we know the attribute offsets to use - // when we encode each feature in the tile. - for (const auto &edge : edges) - { - int forward_weight = 0, reverse_weight = 0; - uint8_t forward_datasource = 0; - uint8_t reverse_datasource = 0; + // And again for integer values used by points. + std::vector used_point_ints; + std::unordered_map point_int_offsets; + + // And again for float values used by points + std::vector used_point_floats; + std::unordered_map point_float_offsets; + + std::uint8_t max_datasource_id = 0; - if (edge.forward_packed_geometry_id != SPECIAL_EDGEID) + // This is where we accumulate information on turns + std::vector all_turn_data; + + // Helper function for adding a new value to the line_ints lookup table. Returns + // the index of the value in the table, adding the value if it doesn't already + // exist + const auto use_line_value = [&used_line_ints, &line_int_offsets](const int value) { + const auto found = line_int_offsets.find(value); + + if (found == line_int_offsets.end()) { - std::vector forward_weight_vector; - facade.GetUncompressedWeights(edge.forward_packed_geometry_id, forward_weight_vector); - forward_weight = forward_weight_vector[edge.fwd_segment_position]; + used_line_ints.push_back(value); + line_int_offsets[value] = used_line_ints.size() - 1; + } - std::vector forward_datasource_vector; - facade.GetUncompressedDatasources(edge.forward_packed_geometry_id, - forward_datasource_vector); - forward_datasource = forward_datasource_vector[edge.fwd_segment_position]; + return; + }; - if (weight_offsets.find(forward_weight) == weight_offsets.end()) - { - used_weights.push_back(forward_weight); - weight_offsets[forward_weight] = used_weights.size() - 1; - } + // Same again + const auto use_point_int_value = [&used_point_ints, &point_int_offsets](const int value) { + const auto found = point_int_offsets.find(value); + std::size_t offset; + + if (found == point_int_offsets.end()) + { + used_point_ints.push_back(value); + offset = used_point_ints.size() - 1; + point_int_offsets[value] = offset; } + else + { + offset = found->second; + } + + return offset; + }; - if (edge.reverse_packed_geometry_id != SPECIAL_EDGEID) + // And a third time, should probably template this.... + const auto use_point_float_value = [&used_point_floats, + &point_float_offsets](const float value) { + const auto found = point_float_offsets.find(value); + std::size_t offset; + + if (found == point_float_offsets.end()) + { + used_point_floats.push_back(value); + offset = used_point_floats.size() - 1; + point_float_offsets[value] = offset; + } + else { - std::vector reverse_weight_vector; - facade.GetUncompressedWeights(edge.reverse_packed_geometry_id, reverse_weight_vector); + offset = found->second; + } + + return offset; + }; - BOOST_ASSERT(edge.fwd_segment_position < reverse_weight_vector.size()); + // If we're zooming into 16 or higher, include turn data. Why? Because turns make the map + // really + // cramped, so we don't bother including the data for tiles that span a large area. + if (parameters.z >= MIN_ZOOM_FOR_TURNS) + { + // Struct to hold info on all the EdgeBasedNodes that are visible in our tile + // When we create these, we insure that (source, target) and packed_geometry_id + // are all pointed in the same direction. + struct EdgeBasedNodeInfo + { + unsigned source_intersection; // node-based-node ID + unsigned target_intersection; // node-based-node ID + unsigned packed_geometry_id; + }; + // Lookup table for edge-based-nodes + std::unordered_map edge_based_node_info; + std::unordered_map> outgoing_edges; + std::unordered_map> incoming_edges; + + // Now, loop over all the road segments we saw, and build up a mini + // graph for just the network that's visible. + for (const auto &edge : edges) + { + // Note: edge.u is the node-based node ID of the source intersection + // edge.v is the node-based node ID of the target intersection + // both these values can be directly looked up for coordinates + + // If forward travel is enabled on this road section, and we haven't seen this + // edge-based-node + // before + if (edge.forward_segment_id.enabled && + edge_based_node_info.count(edge.forward_segment_id.id) == 0) + { + // Add this edge-based-nodeid as an outgoing from the source intersection + const auto outgoing_itr_u = outgoing_edges.find(edge.u); + if (outgoing_itr_u != outgoing_edges.end()) + { + outgoing_itr_u->second.push_back(edge.forward_segment_id.id); + } + else + { + outgoing_edges[edge.u] = {edge.forward_segment_id.id}; + } - reverse_weight = - reverse_weight_vector[reverse_weight_vector.size() - edge.fwd_segment_position - 1]; + // Add this edge-based-nodeid as an incoming to the target intersection + const auto incoming_itr_v = incoming_edges.find(edge.v); + if (incoming_itr_v != incoming_edges.end()) + { + incoming_itr_v->second.push_back(edge.forward_segment_id.id); + } + else + { + incoming_edges[edge.v] = {edge.forward_segment_id.id}; + } - if (weight_offsets.find(reverse_weight) == weight_offsets.end()) + edge_based_node_info[edge.forward_segment_id.id] = { + edge.u, edge.v, edge.packed_geometry_id}; + } + // Same as previous block, but everything flipped + if (edge.reverse_segment_id.enabled && + edge_based_node_info.count(edge.reverse_segment_id.id) == 0) { - used_weights.push_back(reverse_weight); - weight_offsets[reverse_weight] = used_weights.size() - 1; + auto f = outgoing_edges.find(edge.v); + if (f != outgoing_edges.end()) + { + f->second.push_back(edge.reverse_segment_id.id); + } + else + { + outgoing_edges[edge.v] = {edge.reverse_segment_id.id}; + } + + f = incoming_edges.find(edge.u); + if (f != incoming_edges.end()) + { + f->second.push_back(edge.reverse_segment_id.id); + } + else + { + incoming_edges[edge.u] = {edge.reverse_segment_id.id}; + } + + // Save info about this edge-based-node, note reversal from forward + // block above. + edge_based_node_info[edge.reverse_segment_id.id] = { + edge.v, edge.u, edge.packed_geometry_id}; + } + } + + // Now, for every edge-based-node that we discovered (edge-based-nodes are sources + // and targets of turns). EBN is short for edge-based-node + std::vector first_geometry, second_geometry; + std::vector unpacked_shortcut; + std::vector forward_weight_vector; + for (const auto &source_ebn : edge_based_node_info) + { + // Grab a copy of the geometry leading up to the intersection. + first_geometry = facade->GetUncompressedForwardGeometry(source_ebn.second.packed_geometry_id); + + // We earlier saved the source and target intersection nodes for every road section. + // We can use the target node to find all road sections that lead away from + // the intersection, and thus + // in the graph after our main + for (const auto &target_ebn : outgoing_edges[source_ebn.second.target_intersection]) + { + // Ignore u-turns for now + if (edge_based_node_info.at(target_ebn).target_intersection == + source_ebn.second.source_intersection) + continue; + + // Find the connection between our source road and the target node + EdgeID smaller_edge_id = facade->FindSmallestEdge( + source_ebn.first, target_ebn, [](const contractor::QueryEdge::EdgeData &data) { + return data.forward; + }); + + // Depending on how the graph is constructed, we might have to look for + // a backwards edge instead. They're equivalent, just one is available for + // a forward routing search, and one is used for the backwards dijkstra + // steps. Their weight should be the same, we can use either one. + // If we didn't find a forward edge, try for a backward one + if (SPECIAL_EDGEID == smaller_edge_id) + { + smaller_edge_id = facade->FindSmallestEdge( + target_ebn, + source_ebn.first, + [](const contractor::QueryEdge::EdgeData &data) { return data.backward; }); + } + + // If no edge was found, it means that there's no connection between these nodes, + // due to oneways or turn restrictions. Given the edge-based-nodes that + // we're examining here, we *should* only find directly-connected edges, not + // shortcuts + if (smaller_edge_id != SPECIAL_EDGEID) + { + // Check to see if it was a shortcut edge we found. This can happen + // when exactly? Anyway, unpack it and get the first "real" edgedata + // out of it, which should represent the first hop, which is the one + // we want to find the turn. + const auto &data = [this, + &facade, + smaller_edge_id, + source_ebn, + target_ebn, + &unpacked_shortcut]() { + const auto inner_data = facade->GetEdgeData(smaller_edge_id); + if (inner_data.shortcut) + { + unpacked_shortcut.clear(); + UnpackEdgeToEdges( + *facade, source_ebn.first, target_ebn, unpacked_shortcut); + return unpacked_shortcut.front(); + } + else + return inner_data; + }(); + BOOST_ASSERT_MSG(!data.shortcut, "Connecting edge must not be a shortcut"); + + // This is the geometry leading away from the intersection + // (i.e. the geometry of the target edge-based-node) + second_geometry = facade->GetUncompressedReverseGeometry( + edge_based_node_info.at(target_ebn).packed_geometry_id); + + // Now, calculate the sum of the weight of all the segments. + forward_weight_vector = facade->GetUncompressedForwardWeights(source_ebn.second.packed_geometry_id); + const auto sum_node_weight = std::accumulate( + forward_weight_vector.begin(), forward_weight_vector.end(), EdgeWeight{0}); + + // The edge.distance is the whole edge weight, which includes the turn cost. + // The turn cost is the edge.distance minus the sum of the individual road + // segment weights. This might not be 100% accurate, because some + // intersections include stop signs, traffic signals and other penalties, + // but at this stage, we can't divide those out, so we just treat the whole + // lot as the "turn cost" that we'll stick on the map. + const auto turn_cost = data.distance - sum_node_weight; + + // Find the three nodes that make up the turn movement) + const auto node_from = first_geometry.size() > 1 + ? *(first_geometry.end() - 2) + : source_ebn.second.source_intersection; + const auto node_via = source_ebn.second.target_intersection; + const auto node_to = second_geometry.front(); + + const auto coord_from = facade->GetCoordinateOfNode(node_from); + const auto coord_via = facade->GetCoordinateOfNode(node_via); + const auto coord_to = facade->GetCoordinateOfNode(node_to); + + // Calculate the bearing that we approach the intersection at + const auto angle_in = static_cast( + util::coordinate_calculation::bearing(coord_from, coord_via)); + + // Add the angle to the values table for the vector tile, and get the index + // of that value in the table + const auto angle_in_index = use_point_int_value(angle_in); + + // Calculate the bearing leading away from the intersection + const auto exit_bearing = static_cast( + util::coordinate_calculation::bearing(coord_via, coord_to)); + + // Figure out the angle of the turn + auto turn_angle = exit_bearing - angle_in; + while (turn_angle > 180) + { + turn_angle -= 360; + } + while (turn_angle < -180) + { + turn_angle += 360; + } + + // Add the turn angle value to the value lookup table for the vector tile. + const auto turn_angle_index = use_point_int_value(turn_angle); + // And, same for the actual turn cost value - it goes in the lookup table, + // not directly on the feature itself. + const auto turn_cost_index = + use_point_float_value(turn_cost / 10.0); // Note conversion to float here + + // Save everything we need to later add all the points to the tile. + // We need the coordinate of the intersection, the angle in, the turn + // angle and the turn cost. + all_turn_data.emplace_back( + TurnData{coord_via, angle_in_index, turn_angle_index, turn_cost_index}); + } } - std::vector reverse_datasource_vector; - facade.GetUncompressedDatasources(edge.reverse_packed_geometry_id, - reverse_datasource_vector); - reverse_datasource = reverse_datasource_vector[reverse_datasource_vector.size() - - edge.fwd_segment_position - 1]; } + } + + // Vector tiles encode feature properties as indexes into a lookup table. So, we need to + // "pre-loop" over all the edges to create the lookup tables. Once we have those, we + // can then encode the features, and we'll know the indexes that feature properties + // need to refer to. + std::vector forward_weight_vector, reverse_weight_vector; + std::vector forward_datasource_vector, reverse_datasource_vector; + for (const auto &edge : edges) + { + int forward_weight = 0, reverse_weight = 0; + std::uint8_t forward_datasource = 0; + std::uint8_t reverse_datasource = 0; + // TODO this approach of writing at least an empty vector for any segment is probably stupid + // (inefficient) + + forward_weight_vector = + facade->GetUncompressedForwardWeights(edge.packed_geometry_id); + reverse_weight_vector = + facade->GetUncompressedReverseWeights(edge.packed_geometry_id); + + forward_weight = forward_weight_vector[edge.fwd_segment_position]; + + BOOST_ASSERT(edge.fwd_segment_position < reverse_weight_vector.size()); + reverse_weight = + reverse_weight_vector[reverse_weight_vector.size() - edge.fwd_segment_position - 1]; + + forward_datasource_vector.clear(); + forward_datasource_vector = facade->GetUncompressedForwardDatasources(edge.packed_geometry_id); + forward_datasource = forward_datasource_vector[edge.fwd_segment_position]; + + reverse_datasource_vector.clear(); + reverse_datasource_vector = + facade->GetUncompressedReverseDatasources(edge.packed_geometry_id); + reverse_datasource = reverse_datasource_vector[reverse_datasource_vector.size() - + edge.fwd_segment_position - 1]; + + use_line_value(reverse_weight); + // Keep track of the highest datasource seen so that we don't write unnecessary // data to the layer attribute values max_datasource_id = std::max(max_datasource_id, forward_datasource); max_datasource_id = std::max(max_datasource_id, reverse_datasource); - std::string name = facade.GetNameForID(edge.name_id); - + std::string name = facade->GetNameForID(edge.name_id); if (name_offsets.find(name) == name_offsets.end()) { names.push_back(name); @@ -249,247 +648,344 @@ Status TilePlugin::HandleRequest(const api::TileParameters ¶meters, std::str } } - // TODO: extract speed values for compressed and uncompressed geometries - // Convert tile coordinates into mercator coordinates util::web_mercator::xyzToMercator( parameters.x, parameters.y, parameters.z, min_lon, min_lat, max_lon, max_lat); - const detail::BBox tile_bbox{min_lon, min_lat, max_lon, max_lat}; + const BBox tile_bbox{min_lon, min_lat, max_lon, max_lat}; - // Protobuf serialized blocks when objects go out of scope, hence + // Protobuf serializes blocks when objects go out of scope, hence // the extra scoping below. protozero::pbf_writer tile_writer{pbf_buffer}; { - // Add a layer object to the PBF stream. 3=='layer' from the vector tile spec (2.1) - protozero::pbf_writer layer_writer(tile_writer, util::vector_tile::LAYER_TAG); - // TODO: don't write a layer if there are no features - - layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version - // Field 1 is the "layer name" field, it's a string - layer_writer.add_string(util::vector_tile::NAME_TAG, "speeds"); // name - // Field 5 is the tile extent. It's a uint32 and should be set to 4096 - // for normal vector tiles. - layer_writer.add_uint32(util::vector_tile::EXTEND_TAG, util::vector_tile::EXTENT); // extent - - // Begin the layer features block { - // Each feature gets a unique id, starting at 1 - unsigned id = 1; - for (const auto &edge : edges) + // Add a layer object to the PBF stream. 3=='layer' from the vector tile spec (2.1) + protozero::pbf_writer line_layer_writer(tile_writer, util::vector_tile::LAYER_TAG); + // TODO: don't write a layer if there are no features + + line_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version + // Field 1 is the "layer name" field, it's a string + line_layer_writer.add_string(util::vector_tile::NAME_TAG, "speeds"); // name + // Field 5 is the tile extent. It's a uint32 and should be set to 4096 + // for normal vector tiles. + line_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG, + util::vector_tile::EXTENT); // extent + + // Begin the layer features block { - // Get coordinates for start/end nodes of segmet (NodeIDs u and v) - const auto a = facade.GetCoordinateOfNode(edge.u); - const auto b = facade.GetCoordinateOfNode(edge.v); - // Calculate the length in meters - const double length = osrm::util::coordinate_calculation::haversineDistance(a, b); + // Each feature gets a unique id, starting at 1 + unsigned id = 1; + std::vector forward_weight_vector; + std::vector forward_datasource_vector; + std::vector reverse_datasource_vector; + for (const auto &edge : edges) + { + // Get coordinates for start/end nodes of segment (NodeIDs u and v) + const auto a = facade->GetCoordinateOfNode(edge.u); + const auto b = facade->GetCoordinateOfNode(edge.v); + // Calculate the length in meters + const double length = + osrm::util::coordinate_calculation::haversineDistance(a, b); + + int forward_weight = 0; + int reverse_weight = 0; + + std::uint8_t forward_datasource = 0; + std::uint8_t reverse_datasource = 0; + forward_weight_vector.clear(); + forward_weight_vector = + facade->GetUncompressedForwardWeights(edge.packed_geometry_id); + forward_weight = forward_weight_vector[edge.fwd_segment_position]; - int forward_weight = 0; - int reverse_weight = 0; + forward_datasource_vector.clear(); + forward_datasource_vector = + facade->GetUncompressedForwardDatasources(edge.packed_geometry_id); + forward_datasource = forward_datasource_vector[edge.fwd_segment_position]; - uint8_t forward_datasource = 0; - uint8_t reverse_datasource = 0; + reverse_weight_vector.clear(); + reverse_weight_vector = + facade->GetUncompressedReverseWeights(edge.packed_geometry_id); - std::string name = facade.GetNameForID(edge.name_id); + std::string name = facade->GetNameForID(edge.name_id); - if (edge.forward_packed_geometry_id != SPECIAL_EDGEID) - { - std::vector forward_weight_vector; - facade.GetUncompressedWeights(edge.forward_packed_geometry_id, - forward_weight_vector); + forward_weight_vector = facade->GetUncompressedForwardWeights(edge.packed_geometry_id); forward_weight = forward_weight_vector[edge.fwd_segment_position]; - std::vector forward_datasource_vector; - facade.GetUncompressedDatasources(edge.forward_packed_geometry_id, - forward_datasource_vector); + forward_datasource_vector = facade->GetUncompressedForwardDatasources(edge.packed_geometry_id); forward_datasource = forward_datasource_vector[edge.fwd_segment_position]; - } - - if (edge.reverse_packed_geometry_id != SPECIAL_EDGEID) - { - std::vector reverse_weight_vector; - facade.GetUncompressedWeights(edge.reverse_packed_geometry_id, - reverse_weight_vector); - BOOST_ASSERT(edge.fwd_segment_position < reverse_weight_vector.size()); + reverse_weight_vector = facade->GetUncompressedReverseWeights(edge.packed_geometry_id); reverse_weight = reverse_weight_vector[reverse_weight_vector.size() - edge.fwd_segment_position - 1]; - std::vector reverse_datasource_vector; - facade.GetUncompressedDatasources(edge.reverse_packed_geometry_id, - reverse_datasource_vector); + reverse_datasource_vector = facade->GetUncompressedReverseDatasources(edge.packed_geometry_id); + reverse_datasource = + reverse_datasource_vector[reverse_datasource_vector.size() - + edge.fwd_segment_position - 1]; + + // Keep track of the highest datasource seen so that we don't write unnecessary + // data to the layer attribute values + max_datasource_id = std::max(max_datasource_id, forward_datasource); + max_datasource_id = std::max(max_datasource_id, reverse_datasource); + + const auto encode_tile_line = [&line_layer_writer, + &edge, + &id, + &max_datasource_id, + &used_line_ints](const FixedLine &tile_line, + const std::uint32_t speed_kmh, + const std::size_t duration, + const DatasourceID datasource, + const std::size_t name, + std::int32_t &start_x, + std::int32_t &start_y) { + // Here, we save the two attributes for our feature: the speed and the + // is_small boolean. We only serve up speeds from 0-139, so all we do is + // save the first + protozero::pbf_writer feature_writer(line_layer_writer, + util::vector_tile::FEATURE_TAG); + // Field 3 is the "geometry type" field. Value 2 is "line" + feature_writer.add_enum( + util::vector_tile::GEOMETRY_TAG, + util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type + // Field 1 for the feature is the "id" field. + feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id + { + // When adding attributes to a feature, we have to write + // pairs of numbers. The first value is the index in the + // keys array (written later), and the second value is the + // index into the "values" array (also written later). We're + // not writing the actual speed or bool value here, we're saving + // an index into the "values" array. This means many features + // can share the same value data, leading to smaller tiles. + protozero::packed_field_uint32 field( + feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); + + field.add_element(0); // "speed" tag key offset + field.add_element( + std::min(speed_kmh, 127u)); // save the speed value, capped at 127 + field.add_element(1); // "is_small" tag key offset + field.add_element(128 + + (edge.component.is_tiny ? 0 : 1)); // is_small feature + field.add_element(2); // "datasource" tag key offset + field.add_element(130 + datasource); // datasource value offset + field.add_element(3); // "duration" tag key offset + field.add_element(130 + max_datasource_id + 1 + + duration); // duration value offset + field.add_element(4); // "name" tag key offset + + field.add_element(130 + max_datasource_id + 1 + used_line_ints.size() + + name); // name value offset + } + { + + // Encode the geometry for the feature + protozero::packed_field_uint32 geometry( + feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); + encodeLinestring(tile_line, geometry, start_x, start_y); + } + }; + + // If this is a valid forward edge, go ahead and add it to the tile + if (forward_weight != 0 && edge.forward_segment_id.enabled) + { + std::int32_t start_x = 0; + std::int32_t start_y = 0; + + // Calculate the speed for this line + std::uint32_t speed_kmh = + static_cast(round(length / forward_weight * 10 * 3.6)); + + auto tile_line = coordinatesToTileLine(a, b, tile_bbox); + if (!tile_line.empty()) + { + encode_tile_line(tile_line, + speed_kmh, + line_int_offsets[forward_weight], + forward_datasource, + name_offsets[name], + start_x, + start_y); + } + } + + // Repeat the above for the coordinates reversed and using the `reverse` + // properties + if (reverse_weight != 0 && edge.reverse_segment_id.enabled) + { + std::int32_t start_x = 0; + std::int32_t start_y = 0; + + // Calculate the speed for this line + std::uint32_t speed_kmh = + static_cast(round(length / reverse_weight * 10 * 3.6)); + + auto tile_line = coordinatesToTileLine(b, a, tile_bbox); + if (!tile_line.empty()) + { + encode_tile_line(tile_line, + speed_kmh, + line_int_offsets[reverse_weight], + reverse_datasource, + name_offsets[name], + start_x, + start_y); + } + } + reverse_datasource_vector.clear(); + reverse_datasource_vector = + facade->GetUncompressedReverseDatasources(edge.packed_geometry_id); reverse_datasource = reverse_datasource_vector[reverse_datasource_vector.size() - edge.fwd_segment_position - 1]; } + } + + // Field id 3 is the "keys" attribute + // We need two "key" fields, these are referred to with 0 and 1 (their array indexes) + // earlier + line_layer_writer.add_string(util::vector_tile::KEY_TAG, "speed"); + line_layer_writer.add_string(util::vector_tile::KEY_TAG, "is_small"); + line_layer_writer.add_string(util::vector_tile::KEY_TAG, "datasource"); + line_layer_writer.add_string(util::vector_tile::KEY_TAG, "duration"); + line_layer_writer.add_string(util::vector_tile::KEY_TAG, "name"); + + // Now, we write out the possible speed value arrays and possible is_tiny + // values. Field type 4 is the "values" field. It's a variable type field, + // so requires a two-step write (create the field, then write its value) + for (std::size_t i = 0; i < 128; i++) + { + // Writing field type 4 == variant type + protozero::pbf_writer values_writer(line_layer_writer, + util::vector_tile::VARIANT_TAG); + // Attribute value 5 == uint64 type + values_writer.add_uint64(util::vector_tile::VARIANT_TYPE_UINT64, i); + } + { + protozero::pbf_writer values_writer(line_layer_writer, + util::vector_tile::VARIANT_TAG); + // Attribute value 7 == bool type + values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, true); + } + { + protozero::pbf_writer values_writer(line_layer_writer, + util::vector_tile::VARIANT_TAG); + // Attribute value 7 == bool type + values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, false); + } + for (std::size_t i = 0; i <= max_datasource_id; i++) + { + // Writing field type 4 == variant type + protozero::pbf_writer values_writer(line_layer_writer, + util::vector_tile::VARIANT_TAG); + // Attribute value 1 == string type + values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, + facade->GetDatasourceName(i)); + } + for (auto value : used_line_ints) + { + // Writing field type 4 == variant type + protozero::pbf_writer values_writer(line_layer_writer, + util::vector_tile::VARIANT_TAG); + // Attribute value 2 == float type + // Durations come out of OSRM in integer deciseconds, so we convert them + // to seconds with a simple /10 for display + values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, value / 10.); + } + + for (const auto &name : names) + { + // Writing field type 4 == variant type + protozero::pbf_writer values_writer(line_layer_writer, + util::vector_tile::VARIANT_TAG); + // Attribute value 1 == string type + values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, name); + } + } - // Keep track of the highest datasource seen so that we don't write unnecessary - // data to the layer attribute values - max_datasource_id = std::max(max_datasource_id, forward_datasource); - max_datasource_id = std::max(max_datasource_id, reverse_datasource); - - const auto encode_tile_line = [&layer_writer, - &edge, - &id, - &max_datasource_id, - &used_weights](const detail::FixedLine &tile_line, - const std::uint32_t speed_kmh, - const std::size_t duration, - const DatasourceID datasource, - const std::size_t name, - std::int32_t &start_x, - std::int32_t &start_y) { - // Here, we save the two attributes for our feature: the speed and the - // is_small - // boolean. We onl serve up speeds from 0-139, so all we do is save the - // first - protozero::pbf_writer feature_writer(layer_writer, + // Only add the turn layer to the tile if it has some features (we sometimes won't + // for tiles of z<16, and tiles that don't show any intersections) + if (!all_turn_data.empty()) + { + // Now write the points layer for turn penalty data: + // Add a layer object to the PBF stream. 3=='layer' from the vector tile spec (2.1) + protozero::pbf_writer point_layer_writer(tile_writer, util::vector_tile::LAYER_TAG); + point_layer_writer.add_uint32(util::vector_tile::VERSION_TAG, 2); // version + point_layer_writer.add_string(util::vector_tile::NAME_TAG, "turns"); // name + point_layer_writer.add_uint32(util::vector_tile::EXTENT_TAG, + util::vector_tile::EXTENT); // extent + + // Begin writing the set of point features + { + // Start each features with an ID starting at 1 + int id = 1; + + // Helper function to encode a new point feature on a vector tile. + const auto encode_tile_point = [&point_layer_writer, &used_point_ints, &id]( + const FixedPoint &tile_point, const TurnData &point_turn_data) { + protozero::pbf_writer feature_writer(point_layer_writer, util::vector_tile::FEATURE_TAG); - // Field 3 is the "geometry type" field. Value 2 is "line" - feature_writer.add_enum(util::vector_tile::GEOMETRY_TAG, - util::vector_tile::GEOMETRY_TYPE_LINE); // geometry type - // Field 1 for the feature is the "id" field. + // Field 3 is the "geometry type" field. Value 1 is "point" + feature_writer.add_enum( + util::vector_tile::GEOMETRY_TAG, + util::vector_tile::GEOMETRY_TYPE_POINT); // geometry type feature_writer.add_uint64(util::vector_tile::ID_TAG, id++); // id { - // When adding attributes to a feature, we have to write - // pairs of numbers. The first value is the index in the - // keys array (written later), and the second value is the - // index into the "values" array (also written later). We're - // not writing the actual speed or bool value here, we're saving - // an index into the "values" array. This means many features - // can share the same value data, leading to smaller tiles. + // Write out the 3 properties we want on the feature. These + // refer to indexes in the properties lookup table, which we + // add to the tile after we add all features. protozero::packed_field_uint32 field( feature_writer, util::vector_tile::FEATURE_ATTRIBUTES_TAG); - - field.add_element(0); // "speed" tag key offset - field.add_element( - std::min(speed_kmh, 127u)); // save the speed value, capped at 127 - field.add_element(1); // "is_small" tag key offset - field.add_element(128 + - (edge.component.is_tiny ? 0 : 1)); // is_small feature - field.add_element(2); // "datasource" tag key offset - field.add_element(130 + datasource); // datasource value offset - field.add_element(3); // "duration" tag key offset - field.add_element(130 + max_datasource_id + 1 + - duration); // duration value offset - field.add_element(4); // "name" tag key offset - - field.add_element(130 + max_datasource_id + 1 + used_weights.size() + - name); // name value offset + field.add_element(0); // "bearing_in" tag key offset + field.add_element(point_turn_data.in_angle_offset); + field.add_element(1); // "turn_angle" tag key offset + field.add_element(point_turn_data.turn_angle_offset); + field.add_element(2); // "cost" tag key offset + field.add_element(used_point_ints.size() + point_turn_data.weight_offset); } { - - // Encode the geometry for the feature + // Add the geometry as the last field in this feature protozero::packed_field_uint32 geometry( feature_writer, util::vector_tile::FEATURE_GEOMETRIES_TAG); - encodeLinestring(tile_line, geometry, start_x, start_y); + encodePoint(tile_point, geometry); } }; - // If this is a valid forward edge, go ahead and add it to the tile - if (forward_weight != 0 && edge.forward_segment_id.enabled) - { - std::int32_t start_x = 0; - std::int32_t start_y = 0; - - // Calculate the speed for this line - std::uint32_t speed_kmh = - static_cast(round(length / forward_weight * 10 * 3.6)); - - auto tile_line = coordinatesToTileLine(a, b, tile_bbox); - if (!tile_line.empty()) - { - encode_tile_line(tile_line, - speed_kmh, - weight_offsets[forward_weight], - forward_datasource, - name_offsets[name], - start_x, - start_y); - } - } - - // Repeat the above for the coordinates reversed and using the `reverse` - // properties - if (reverse_weight != 0 && edge.reverse_segment_id.enabled) + // Loop over all the turns we found and add them as features to the layer + for (const auto &turndata : all_turn_data) { - std::int32_t start_x = 0; - std::int32_t start_y = 0; - - // Calculate the speed for this line - std::uint32_t speed_kmh = - static_cast(round(length / reverse_weight * 10 * 3.6)); - - auto tile_line = coordinatesToTileLine(b, a, tile_bbox); - if (!tile_line.empty()) + const auto tile_point = coordinatesToTilePoint(turndata.coordinate, tile_bbox); + if (!boost::geometry::within(point_t(tile_point.x, tile_point.y), clip_box)) { - encode_tile_line(tile_line, - speed_kmh, - weight_offsets[reverse_weight], - reverse_datasource, - name_offsets[name], - start_x, - start_y); + continue; } + encode_tile_point(tile_point, turndata); } } - } - // Field id 3 is the "keys" attribute - // We need two "key" fields, these are referred to with 0 and 1 (their array indexes) - // earlier - layer_writer.add_string(util::vector_tile::KEY_TAG, "speed"); - layer_writer.add_string(util::vector_tile::KEY_TAG, "is_small"); - layer_writer.add_string(util::vector_tile::KEY_TAG, "datasource"); - layer_writer.add_string(util::vector_tile::KEY_TAG, "duration"); - layer_writer.add_string(util::vector_tile::KEY_TAG, "name"); - - // Now, we write out the possible speed value arrays and possible is_tiny - // values. Field type 4 is the "values" field. It's a variable type field, - // so requires a two-step write (create the field, then write its value) - for (std::size_t i = 0; i < 128; i++) - { - // Writing field type 4 == variant type - protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); - // Attribute value 5 == uin64 type - values_writer.add_uint64(util::vector_tile::VARIANT_TYPE_UINT32, i); - } - { - protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); - // Attribute value 7 == bool type - values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, true); - } - { - protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); - // Attribute value 7 == bool type - values_writer.add_bool(util::vector_tile::VARIANT_TYPE_BOOL, false); - } - for (std::size_t i = 0; i <= max_datasource_id; i++) - { - // Writing field type 4 == variant type - protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); - // Attribute value 1 == string type - values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, - facade.GetDatasourceName(i)); - } - for (auto weight : used_weights) - { - // Writing field type 4 == variant type - protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); - // Attribute value 2 == float type - // Durations come out of OSRM in integer deciseconds, so we convert them - // to seconds with a simple /10 for display - values_writer.add_double(util::vector_tile::VARIANT_TYPE_DOUBLE, weight / 10.); - } + // Add the names of the three attributes we added to all the turn penalty + // features previously. The indexes used there refer to these keys. + point_layer_writer.add_string(util::vector_tile::KEY_TAG, "bearing_in"); + point_layer_writer.add_string(util::vector_tile::KEY_TAG, "turn_angle"); + point_layer_writer.add_string(util::vector_tile::KEY_TAG, "cost"); - for (const auto &name : names) - { - // Writing field type 4 == variant type - protozero::pbf_writer values_writer(layer_writer, util::vector_tile::VARIANT_TAG); - // Attribute value 1 == string type - values_writer.add_string(util::vector_tile::VARIANT_TYPE_STRING, name); + // Now, save the lists of integers and floats that our features refer to. + for (const auto &value : used_point_ints) + { + protozero::pbf_writer values_writer(point_layer_writer, + util::vector_tile::VARIANT_TAG); + values_writer.add_sint64(util::vector_tile::VARIANT_TYPE_SINT64, value); + } + for (const auto &value : used_point_floats) + { + protozero::pbf_writer values_writer(point_layer_writer, + util::vector_tile::VARIANT_TAG); + values_writer.add_float(util::vector_tile::VARIANT_TYPE_FLOAT, value); + } } } + // protozero serializes data during object destructors, so once the scope closes, + // our result buffer will have all the tile data encoded into it. return Status::Ok; } diff --git a/src/engine/plugins/trip.cpp b/src/engine/plugins/trip.cpp index 2b463b9d508..da6b94ba3be 100644 --- a/src/engine/plugins/trip.cpp +++ b/src/engine/plugins/trip.cpp @@ -114,7 +114,8 @@ SCC_Component SplitUnaccessibleLocations(const std::size_t number_of_locations, return SCC_Component(std::move(components), std::move(range)); } -InternalRouteResult TripPlugin::ComputeRoute(const std::vector &snapped_phantoms, +InternalRouteResult TripPlugin::ComputeRoute(const datafacade::BaseDataFacade &facade, + const std::vector &snapped_phantoms, const std::vector &trip) { InternalRouteResult min_route; @@ -134,13 +135,14 @@ InternalRouteResult TripPlugin::ComputeRoute(const std::vector &sna } BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size()); - shortest_path(min_route.segment_end_coordinates, {false}, min_route); + shortest_path(facade, min_route.segment_end_coordinates, {false}, min_route); BOOST_ASSERT_MSG(min_route.shortest_path_length < INVALID_EDGE_WEIGHT, "unroutable route"); return min_route; } -Status TripPlugin::HandleRequest(const api::TripParameters ¶meters, +Status TripPlugin::HandleRequest(const std::shared_ptr facade, + const api::TripParameters ¶meters, util::json::Object &json_result) { BOOST_ASSERT(parameters.IsValid()); @@ -157,7 +159,7 @@ Status TripPlugin::HandleRequest(const api::TripParameters ¶meters, return Error("InvalidValue", "Invalid coordinate value.", json_result); } - auto phantom_node_pairs = GetPhantomNodes(parameters); + auto phantom_node_pairs = GetPhantomNodes(*facade, parameters); if (phantom_node_pairs.size() != parameters.coordinates.size()) { return Error("NoSegment", @@ -173,7 +175,7 @@ Status TripPlugin::HandleRequest(const api::TripParameters ¶meters, // compute the duration table of all phantom nodes const auto result_table = util::DistTableWrapper( - duration_table(snapped_phantoms, {}, {}), number_of_locations); + duration_table(*facade, snapped_phantoms, {}, {}), number_of_locations); if (result_table.size() == 0) { @@ -231,10 +233,10 @@ Status TripPlugin::HandleRequest(const api::TripParameters ¶meters, routes.reserve(trips.size()); for (const auto &trip : trips) { - routes.push_back(ComputeRoute(snapped_phantoms, trip)); + routes.push_back(ComputeRoute(*facade, snapped_phantoms, trip)); } - api::TripAPI trip_api{BasePlugin::facade, parameters}; + api::TripAPI trip_api{*facade, parameters}; trip_api.MakeResponse(trips, routes, snapped_phantoms, json_result); return Status::Ok; diff --git a/src/engine/plugins/viaroute.cpp b/src/engine/plugins/viaroute.cpp index 10c3101e7b5..7057b13c958 100644 --- a/src/engine/plugins/viaroute.cpp +++ b/src/engine/plugins/viaroute.cpp @@ -21,13 +21,14 @@ namespace engine namespace plugins { -ViaRoutePlugin::ViaRoutePlugin(datafacade::BaseDataFacade &facade_, int max_locations_viaroute) - : BasePlugin(facade_), shortest_path(&facade_, heaps), alternative_path(&facade_, heaps), - direct_shortest_path(&facade_, heaps), max_locations_viaroute(max_locations_viaroute) +ViaRoutePlugin::ViaRoutePlugin(int max_locations_viaroute) + : shortest_path(heaps), alternative_path(heaps), direct_shortest_path(heaps), + max_locations_viaroute(max_locations_viaroute) { } -Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameters, +Status ViaRoutePlugin::HandleRequest(const std::shared_ptr facade, + const api::RouteParameters &route_parameters, util::json::Object &json_result) { BOOST_ASSERT(route_parameters.IsValid()); @@ -47,7 +48,7 @@ Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameter return Error("InvalidValue", "Invalid coordinate value.", json_result); } - auto phantom_node_pairs = GetPhantomNodes(route_parameters); + auto phantom_node_pairs = GetPhantomNodes(*facade, route_parameters); if (phantom_node_pairs.size() != route_parameters.coordinates.size()) { return Error("NoSegment", @@ -61,7 +62,7 @@ Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameter const bool continue_straight_at_waypoint = route_parameters.continue_straight ? *route_parameters.continue_straight - : facade.GetContinueStraightDefault(); + : facade->GetContinueStraightDefault(); InternalRouteResult raw_route; auto build_phantom_pairs = [&raw_route, continue_straight_at_waypoint]( @@ -85,26 +86,28 @@ Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameter if (1 == raw_route.segment_end_coordinates.size()) { - if (route_parameters.alternatives && facade.GetCoreSize() == 0) + if (route_parameters.alternatives && facade->GetCoreSize() == 0) { - alternative_path(raw_route.segment_end_coordinates.front(), raw_route); + alternative_path(*facade, raw_route.segment_end_coordinates.front(), raw_route); } else { - direct_shortest_path(raw_route.segment_end_coordinates, raw_route); + direct_shortest_path(*facade, raw_route.segment_end_coordinates, raw_route); } } else { - shortest_path( - raw_route.segment_end_coordinates, route_parameters.continue_straight, raw_route); + shortest_path(*facade, + raw_route.segment_end_coordinates, + route_parameters.continue_straight, + raw_route); } // we can only know this after the fact, different SCC ids still // allow for connection in one direction. if (raw_route.is_valid()) { - api::RouteAPI route_api{BasePlugin::facade, route_parameters}; + api::RouteAPI route_api{*facade, route_parameters}; route_api.MakeResponse(raw_route, json_result); } else diff --git a/src/engine/polyline_compressor.cpp b/src/engine/polyline_compressor.cpp index 1dc9b70453f..11954b5d73c 100644 --- a/src/engine/polyline_compressor.cpp +++ b/src/engine/polyline_compressor.cpp @@ -100,7 +100,7 @@ std::vector decodePolyline(const std::string &geometry_string) b = geometry_string.at(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; - } while (b >= 0x20); + } while (b >= 0x20 && index < len); int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); lat += dlat; @@ -111,7 +111,7 @@ std::vector decodePolyline(const std::string &geometry_string) b = geometry_string.at(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; - } while (b >= 0x20); + } while (b >= 0x20 && index < len); int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); lng += dlng; diff --git a/src/extractor/compressed_edge_container.cpp b/src/extractor/compressed_edge_container.cpp index c0529ad0085..0fd7f30ced7 100644 --- a/src/extractor/compressed_edge_container.cpp +++ b/src/extractor/compressed_edge_container.cpp @@ -23,7 +23,7 @@ CompressedEdgeContainer::CompressedEdgeContainer() void CompressedEdgeContainer::IncreaseFreeList() { - m_compressed_geometries.resize(m_compressed_geometries.size() + 100); + m_compressed_oneway_geometries.resize(m_compressed_oneway_geometries.size() + 100); for (unsigned i = 100; i > 0; --i) { m_free_list.emplace_back(free_list_maximum); @@ -37,17 +37,44 @@ bool CompressedEdgeContainer::HasEntryForID(const EdgeID edge_id) const return iter != m_edge_id_to_list_index_map.end(); } +bool CompressedEdgeContainer::HasZippedEntryForForwardID(const EdgeID edge_id) const +{ + auto iter = m_forward_edge_id_to_zipped_index_map.find(edge_id); + return iter != m_forward_edge_id_to_zipped_index_map.end(); +} + +bool CompressedEdgeContainer::HasZippedEntryForReverseID(const EdgeID edge_id) const +{ + auto iter = m_reverse_edge_id_to_zipped_index_map.find(edge_id); + return iter != m_reverse_edge_id_to_zipped_index_map.end(); +} + unsigned CompressedEdgeContainer::GetPositionForID(const EdgeID edge_id) const { auto map_iterator = m_edge_id_to_list_index_map.find(edge_id); BOOST_ASSERT(map_iterator != m_edge_id_to_list_index_map.end()); + BOOST_ASSERT(map_iterator->second < m_compressed_oneway_geometries.size()); + return map_iterator->second; +} + +unsigned CompressedEdgeContainer::GetZippedPositionForForwardID(const EdgeID edge_id) const +{ + auto map_iterator = m_forward_edge_id_to_zipped_index_map.find(edge_id); + BOOST_ASSERT(map_iterator != m_forward_edge_id_to_zipped_index_map.end()); BOOST_ASSERT(map_iterator->second < m_compressed_geometries.size()); return map_iterator->second; } -void CompressedEdgeContainer::SerializeInternalVector(const std::string &path) const +unsigned CompressedEdgeContainer::GetZippedPositionForReverseID(const EdgeID edge_id) const { + auto map_iterator = m_reverse_edge_id_to_zipped_index_map.find(edge_id); + BOOST_ASSERT(map_iterator != m_reverse_edge_id_to_zipped_index_map.end()); + BOOST_ASSERT(map_iterator->second < m_compressed_geometries.size()); + return map_iterator->second; +} +void CompressedEdgeContainer::SerializeInternalVector(const std::string &path) const +{ boost::filesystem::fstream geometry_out_stream(path, std::ios::binary | std::ios::out); const unsigned compressed_geometries = m_compressed_geometries.size() + 1; BOOST_ASSERT(std::numeric_limits::max() != compressed_geometries); @@ -135,15 +162,15 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, BOOST_ASSERT(iter != m_edge_id_to_list_index_map.end()); const unsigned edge_bucket_id1 = iter->second; BOOST_ASSERT(edge_bucket_id1 == GetPositionForID(edge_id_1)); - BOOST_ASSERT(edge_bucket_id1 < m_compressed_geometries.size()); + BOOST_ASSERT(edge_bucket_id1 < m_compressed_oneway_geometries.size()); - std::vector &edge_bucket_list1 = m_compressed_geometries[edge_bucket_id1]; + std::vector &edge_bucket_list1 = m_compressed_oneway_geometries[edge_bucket_id1]; // note we don't save the start coordinate: it is implicitly given by edge 1 // weight1 is the distance to the (currently) last coordinate in the bucket if (edge_bucket_list1.empty()) { - edge_bucket_list1.emplace_back(CompressedEdge{via_node_id, weight1}); + edge_bucket_list1.emplace_back(OnewayCompressedEdge{via_node_id, weight1}); } BOOST_ASSERT(0 < edge_bucket_list1.size()); @@ -153,10 +180,10 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, { // second edge is not atomic anymore const unsigned list_to_remove_index = GetPositionForID(edge_id_2); - BOOST_ASSERT(list_to_remove_index < m_compressed_geometries.size()); + BOOST_ASSERT(list_to_remove_index < m_compressed_oneway_geometries.size()); - std::vector &edge_bucket_list2 = - m_compressed_geometries[list_to_remove_index]; + std::vector &edge_bucket_list2 = + m_compressed_oneway_geometries[list_to_remove_index]; // found an existing list, append it to the list of edge_id_1 edge_bucket_list1.insert( @@ -174,7 +201,7 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1, else { // we are certain that the second edge is atomic. - edge_bucket_list1.emplace_back(CompressedEdge{target_node_id, weight2}); + edge_bucket_list1.emplace_back(OnewayCompressedEdge{target_node_id, weight2}); } } @@ -206,28 +233,65 @@ void CompressedEdgeContainer::AddUncompressedEdge(const EdgeID edge_id, BOOST_ASSERT(iter != m_edge_id_to_list_index_map.end()); const unsigned edge_bucket_id = iter->second; BOOST_ASSERT(edge_bucket_id == GetPositionForID(edge_id)); - BOOST_ASSERT(edge_bucket_id < m_compressed_geometries.size()); + BOOST_ASSERT(edge_bucket_id < m_compressed_oneway_geometries.size()); - std::vector &edge_bucket_list = m_compressed_geometries[edge_bucket_id]; + std::vector &edge_bucket_list = m_compressed_oneway_geometries[edge_bucket_id]; // note we don't save the start coordinate: it is implicitly given by edge_id // weight is the distance to the (currently) last coordinate in the bucket // Don't re-add this if it's already in there. if (edge_bucket_list.empty()) { - edge_bucket_list.emplace_back(CompressedEdge{target_node_id, weight}); + edge_bucket_list.emplace_back(OnewayCompressedEdge{target_node_id, weight}); } } +void CompressedEdgeContainer::InitializeBothwayVector() +{ + m_compressed_geometries.reserve(m_compressed_oneway_geometries.size() / 2); +} + +unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID r_edge_id) +{ + const auto &forward_bucket = GetBucketReference(f_edge_id); + const auto &reverse_bucket = GetBucketReference(r_edge_id); + + BOOST_ASSERT(forward_bucket.size() == reverse_bucket.size()); + + const unsigned zipped_geometry_id = m_compressed_geometries.size(); + m_forward_edge_id_to_zipped_index_map[f_edge_id] = zipped_geometry_id; + m_reverse_edge_id_to_zipped_index_map[r_edge_id] = zipped_geometry_id; + + std::vector zipped_edge_bucket; + const auto &first_node = reverse_bucket.back(); + zipped_edge_bucket.emplace_back(CompressedEdge{first_node.node_id, INVALID_EDGE_WEIGHT, first_node.weight}); + + for (std::size_t i = 0; i < forward_bucket.size() - 1; ++i) + { + const auto &fwd_node = forward_bucket.at(i); + const auto &rev_node = reverse_bucket.at(reverse_bucket.size() - 2 - i); + + BOOST_ASSERT(fwd_node.node_id == rev_node.node_id); + + zipped_edge_bucket.emplace_back(CompressedEdge{fwd_node.node_id, fwd_node.weight, rev_node.weight}); + } + + const auto &last_node = forward_bucket.back(); + zipped_edge_bucket.emplace_back(CompressedEdge{last_node.node_id, last_node.weight, INVALID_EDGE_WEIGHT}); + m_compressed_geometries.emplace_back(std::move(zipped_edge_bucket)); + + return zipped_geometry_id; +} + void CompressedEdgeContainer::PrintStatistics() const { - const uint64_t compressed_edges = m_compressed_geometries.size(); + const uint64_t compressed_edges = m_compressed_oneway_geometries.size(); BOOST_ASSERT(0 == compressed_edges % 2); - BOOST_ASSERT(m_compressed_geometries.size() + m_free_list.size() > 0); + BOOST_ASSERT(m_compressed_oneway_geometries.size() + m_free_list.size() > 0); uint64_t compressed_geometries = 0; uint64_t longest_chain_length = 0; - for (const std::vector ¤t_vector : m_compressed_geometries) + for (const std::vector ¤t_vector : m_compressed_oneway_geometries) { compressed_geometries += current_vector.size(); longest_chain_length = std::max(longest_chain_length, (uint64_t)current_vector.size()); @@ -243,11 +307,11 @@ void CompressedEdgeContainer::PrintStatistics() const << (float)compressed_geometries / std::max((uint64_t)1, compressed_edges); } -const CompressedEdgeContainer::EdgeBucket & +const CompressedEdgeContainer::OnewayEdgeBucket & CompressedEdgeContainer::GetBucketReference(const EdgeID edge_id) const { const unsigned index = m_edge_id_to_list_index_map.at(edge_id); - return m_compressed_geometries.at(index); + return m_compressed_oneway_geometries.at(index); } // Since all edges are technically in the compressed geometry container, diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index a6fa6c64e0d..c46f66c85cc 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -34,7 +34,7 @@ namespace extractor EdgeBasedGraphFactory::EdgeBasedGraphFactory( std::shared_ptr node_based_graph, - const CompressedEdgeContainer &compressed_edge_container, + CompressedEdgeContainer &compressed_edge_container, const std::unordered_set &barrier_nodes, const std::unordered_set &traffic_lights, std::shared_ptr restriction_map, @@ -124,10 +124,12 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI const auto &forward_geometry = m_compressed_edge_container.GetBucketReference(edge_id_1); BOOST_ASSERT(forward_geometry.size() == m_compressed_edge_container.GetBucketReference(edge_id_2).size()); - const auto geometry_size = forward_geometry.size(); + const auto segment_count = forward_geometry.size(); // There should always be some geometry - BOOST_ASSERT(0 != geometry_size); + BOOST_ASSERT(0 != segment_count); + + const unsigned packed_geometry_id = m_compressed_edge_container.ZipEdges(edge_id_1, edge_id_2); NodeID current_edge_source_coordinate_id = node_u; @@ -140,12 +142,12 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI return SegmentID{edge_based_node_id, true}; }; - // traverse arrays from start and end respectively - for (const auto i : util::irange(std::size_t{0}, geometry_size)) + // traverse arrays + for (const auto i : util::irange(std::size_t{0}, segment_count)) { BOOST_ASSERT( current_edge_source_coordinate_id == - m_compressed_edge_container.GetBucketReference(edge_id_2)[geometry_size - 1 - i] + m_compressed_edge_container.GetBucketReference(edge_id_2)[segment_count - 1 - i] .node_id); const NodeID current_edge_target_coordinate_id = forward_geometry[i].node_id; BOOST_ASSERT(current_edge_target_coordinate_id != current_edge_source_coordinate_id); @@ -156,8 +158,7 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI current_edge_source_coordinate_id, current_edge_target_coordinate_id, forward_data.name_id, - m_compressed_edge_container.GetPositionForID(edge_id_1), - m_compressed_edge_container.GetPositionForID(edge_id_2), + packed_geometry_id, false, INVALID_COMPONENTID, i, @@ -257,6 +258,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes() { util::Percent progress(m_node_based_graph->GetNumberOfNodes()); + m_compressed_edge_container.InitializeBothwayVector(); + // loop over all edges and generate new set of nodes for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes())) { @@ -442,14 +445,26 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( distance += turn_penalty; - BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_from_u)); - original_edge_data_vector.emplace_back( - m_compressed_edge_container.GetPositionForID(edge_from_u), - edge_data1.name_id, - turn.lane_data_id, - turn_instruction, - entry_class_id, - edge_data1.travel_mode); + const bool is_encoded_forwards = m_compressed_edge_container.HasZippedEntryForForwardID(edge_from_u); + const bool is_encoded_backwards = m_compressed_edge_container.HasZippedEntryForReverseID(edge_from_u); + BOOST_ASSERT(is_encoded_forwards || is_encoded_backwards); + if (is_encoded_forwards) { + original_edge_data_vector.emplace_back( + GeometryID{m_compressed_edge_container.GetZippedPositionForForwardID(edge_from_u), true}, + edge_data1.name_id, + turn.lane_data_id, + turn_instruction, + entry_class_id, + edge_data1.travel_mode); + } else if (is_encoded_backwards) { + original_edge_data_vector.emplace_back( + GeometryID{m_compressed_edge_container.GetZippedPositionForReverseID(edge_from_u), false}, + edge_data1.name_id, + turn.lane_data_id, + turn_instruction, + entry_class_id, + edge_data1.travel_mode); + } ++original_edges_counter; diff --git a/src/extractor/extraction_containers.cpp b/src/extractor/extraction_containers.cpp index 91dfe9aab70..020b682635d 100644 --- a/src/extractor/extraction_containers.cpp +++ b/src/extractor/extraction_containers.cpp @@ -110,7 +110,8 @@ ExtractionContainers::ExtractionContainers() // Check if stxxl can be instantiated stxxl::vector dummy_vector; - // Insert three empty strings offsets for name, destination and pronunciation + // Insert four empty strings offsets for name, ref, destination and pronunciation + name_offsets.push_back(0); name_offsets.push_back(0); name_offsets.push_back(0); name_offsets.push_back(0); diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 39af53615b3..add3f4c7d61 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -474,8 +474,6 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment, *node_based_graph, compressed_edge_container); - compressed_edge_container.SerializeInternalVector(config.geometry_output_path); - util::NameTable name_table(config.names_file_name); // could use some additional capacity? To avoid a copy during processing, though small data so @@ -505,6 +503,7 @@ Extractor::BuildEdgeExpandedGraph(ScriptingEnvironment &scripting_environment, config.generate_edge_lookup); WriteTurnLaneData(config.turn_lane_descriptions_file_name); + compressed_edge_container.SerializeInternalVector(config.geometry_output_path); edge_based_graph_factory.GetEdgeBasedEdges(edge_based_edge_list); edge_based_graph_factory.GetEdgeBasedNodes(node_based_edge_list); diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp index 55bfff4a652..850d58d0c97 100644 --- a/src/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -34,10 +34,8 @@ namespace TurnLaneType = guidance::TurnLaneType; ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers) : external_memory(extraction_containers) { - // we reserved 0, 1, 2 for the empty case - string_map[MapKey("", "")] = 0; - - // The map should be empty before we start initializing it + // we reserved 0, 1, 2, 3 for the empty case + string_map[MapKey("", "", "", "")] = 0; lane_description_map[TurnLaneDescription()] = 0; } @@ -144,7 +142,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti // FIXME this need to be moved into the profiles const guidance::RoadClassification road_classification = parsed_way.road_classification; - const auto laneStringToDescription = [](std::string lane_string) -> TurnLaneDescription { + const auto laneStringToDescription = [](const std::string &lane_string) -> TurnLaneDescription { if (lane_string.empty()) return {}; @@ -216,7 +214,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti // convert the lane description into an ID and, if necessary, remembr the description in the // description_map - const auto requestId = [&](std::string lane_string) { + const auto requestId = [&](const std::string &lane_string) { if (lane_string.empty()) return INVALID_LANE_DESCRIPTIONID; TurnLaneDescription lane_description = laneStringToDescription(std::move(lane_string)); @@ -235,16 +233,16 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti } }; - // Deduplicates street names and street destination names based on the street_map map. - // In case we do not already store the name, inserts (name, id) tuple and return id. + // Deduplicates street names, refs, destinations, pronunciation based on the string_map. + // In case we do not already store the key, inserts (key, id) tuple and return id. // Otherwise fetches the id based on the name and returns it without insertion. const auto turn_lane_id_forward = requestId(parsed_way.turn_lanes_forward); const auto turn_lane_id_backward = requestId(parsed_way.turn_lanes_backward); const constexpr auto MAX_STRING_LENGTH = 255u; - // Get the unique identifier for the street name - // Get the unique identifier for the street name and destination - const auto name_iterator = string_map.find(MapKey(parsed_way.name, parsed_way.destinations)); + // Get the unique identifier for the street name, destination, and ref + const auto name_iterator = string_map.find( + MapKey(parsed_way.name, parsed_way.destinations, parsed_way.ref, parsed_way.pronunciation)); unsigned name_id = EMPTY_NAMEID; if (string_map.end() == name_iterator) { @@ -253,12 +251,14 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti std::min(MAX_STRING_LENGTH, parsed_way.destinations.size()); const auto pronunciation_length = std::min(MAX_STRING_LENGTH, parsed_way.pronunciation.size()); + const auto ref_length = std::min(MAX_STRING_LENGTH, parsed_way.ref.size()); // name_offsets already has an offset of a new name, take the offset index as the name id name_id = external_memory.name_offsets.size() - 1; external_memory.name_char_data.reserve(external_memory.name_char_data.size() + name_length + - destinations_length + pronunciation_length); + destinations_length + pronunciation_length + + ref_length); std::copy(parsed_way.name.c_str(), parsed_way.name.c_str() + name_length, @@ -275,7 +275,13 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti std::back_inserter(external_memory.name_char_data)); external_memory.name_offsets.push_back(external_memory.name_char_data.size()); - auto k = MapKey{parsed_way.name, parsed_way.destinations}; + std::copy(parsed_way.ref.c_str(), + parsed_way.ref.c_str() + ref_length, + std::back_inserter(external_memory.name_char_data)); + external_memory.name_offsets.push_back(external_memory.name_char_data.size()); + + auto k = MapKey{ + parsed_way.name, parsed_way.destinations, parsed_way.ref, parsed_way.pronunciation}; auto v = MapVal{name_id}; string_map.emplace(std::move(k), std::move(v)); } diff --git a/src/extractor/guidance/intersection_generator.cpp b/src/extractor/guidance/intersection_generator.cpp index 7402dff1387..525149eb79c 100644 --- a/src/extractor/guidance/intersection_generator.cpp +++ b/src/extractor/guidance/intersection_generator.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -268,8 +269,11 @@ bool IntersectionGenerator::CanMerge(const NodeID node_at_intersection, return becomes_narrower; }; + const bool is_y_arm_first = isValidYArm(first_index, second_index); + const bool is_y_arm_second = isValidYArm(second_index, first_index); + // Only merge valid y-arms - if (!isValidYArm(first_index, second_index) || !isValidYArm(second_index, first_index)) + if (!is_y_arm_first || !is_y_arm_second) return false; if (angle_between < 60) @@ -577,10 +581,14 @@ IntersectionGenerator::GetActualNextIntersection(const NodeID starting_node, // to prevent endless loops const auto termination_node = node_based_graph.GetTarget(via_edge); - while (result.size() == 2 && - node_based_graph.GetEdgeData(via_edge).IsCompatibleTo( - node_based_graph.GetEdgeData(result[1].turn.eid))) + // using a maximum lookahead, we make sure not to end up in some form of loop + std::unordered_set visited_nodes; + while (visited_nodes.count(node_at_intersection) == 0 && + (result.size() == 2 && + node_based_graph.GetEdgeData(via_edge).IsCompatibleTo( + node_based_graph.GetEdgeData(result[1].turn.eid)))) { + visited_nodes.insert(node_at_intersection); node_at_intersection = node_based_graph.GetTarget(incoming_edge); incoming_edge = result[1].turn.eid; result = GetConnectedRoads(node_at_intersection, incoming_edge); diff --git a/src/extractor/guidance/intersection_handler.cpp b/src/extractor/guidance/intersection_handler.cpp index 1f521f63450..7365e926c25 100644 --- a/src/extractor/guidance/intersection_handler.cpp +++ b/src/extractor/guidance/intersection_handler.cpp @@ -90,9 +90,11 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t const auto &in_data = node_based_graph.GetEdgeData(via_edge); const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); if (in_data.name_id != out_data.name_id && - requiresNameAnnounced(name_table.GetNameForID(in_data.name_id), - name_table.GetNameForID(out_data.name_id), - street_name_suffix_table)) + util::guidance::requiresNameAnnounced(name_table.GetNameForID(in_data.name_id), + name_table.GetRefForID(in_data.name_id), + name_table.GetNameForID(out_data.name_id), + name_table.GetRefForID(out_data.name_id), + street_name_suffix_table)) { // obvious turn onto a through street is a merge if (through_street) @@ -107,10 +109,11 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t else if (in_data.road_classification.IsRampClass() && out_data.road_classification.IsRampClass()) { - if (in_mode == out_mode) - return {TurnType::Suppressed, getTurnDirection(road.turn.angle)}; - else - return {TurnType::Notification, getTurnDirection(road.turn.angle)}; + // This check is more a precaution than anything else. Our current travel modes + // cannot reach this, since all ramps are exposing the same travel type. But we + // could see toll-type at some point. + return {in_mode == out_mode ? TurnType::Suppressed : TurnType::Notification, + getTurnDirection(road.turn.angle)}; } else { @@ -137,15 +140,15 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t } else { - return {TurnType::NewName, getTurnDirection(road.turn.angle)}; + return {in_mode == out_mode ? TurnType::NewName : TurnType::Notification, + getTurnDirection(road.turn.angle)}; } } + // name has not changed, suppress a turn here or indicate mode change else { - if (in_mode == out_mode) - return {TurnType::Suppressed, getTurnDirection(road.turn.angle)}; - else - return {TurnType::Notification, getTurnDirection(road.turn.angle)}; + return {in_mode == out_mode ? TurnType::Suppressed : TurnType::Notification, + getTurnDirection(road.turn.angle)}; } } BOOST_ASSERT(type == TurnType::Continue); @@ -530,26 +533,48 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge, best_data.road_classification, right_data.road_classification); + // if the best turn isn't narrow, but there is a nearly straight turn, we don't consider the + // turn obvious + const auto check_narrow = [&intersection, best_deviation](const std::size_t index) { + return angularDeviation(intersection[index].turn.angle, STRAIGHT_ANGLE) <= + FUZZY_ANGLE_DIFFERENCE && + (best_deviation > NARROW_TURN_ANGLE || intersection[index].entry_allowed); + }; + // other narrow turns? - if (angularDeviation(intersection[right_index].turn.angle, STRAIGHT_ANGLE) <= - FUZZY_ANGLE_DIFFERENCE && - !obvious_to_right) + if (check_narrow(right_index) && !obvious_to_right) return 0; - if (angularDeviation(intersection[left_index].turn.angle, STRAIGHT_ANGLE) <= - FUZZY_ANGLE_DIFFERENCE && - !obvious_to_left) + if (check_narrow(left_index) && !obvious_to_left) return 0; - const bool distinct_to_left = - left_deviation / best_deviation >= DISTINCTION_RATIO || - (left_deviation > best_deviation && - (!intersection[left_index].entry_allowed && in_data.distance > 30)); - const bool distinct_to_right = - right_deviation / best_deviation >= DISTINCTION_RATIO || - (right_deviation > best_deviation && - (!intersection[right_index].entry_allowed && in_data.distance > 30)); - + // check if a turn is distinct enough + const auto isDistinct = [&](const std::size_t index, const double deviation) { + /* + If the neighbor is not possible to enter, we allow for a lower + distinction rate. If the road category is smaller, its also adjusted. Only + roads of the same priority require the full distinction ratio. + */ + const auto adjusted_distinction_ratio = [&]() { + // not allowed competitors are easily distinct + if (!intersection[index].entry_allowed) + return 0.7 * DISTINCTION_RATIO; + // a bit less obvious are road classes + else if (in_data.road_classification == best_data.road_classification && + best_data.road_classification.GetPriority() < + node_based_graph.GetEdgeData(intersection[index].turn.eid) + .road_classification.GetPriority()) + return 0.8 * DISTINCTION_RATIO; + // if road classes are the same, we use the full ratio + else + return DISTINCTION_RATIO; + }(); + return index == 0 || deviation / best_deviation >= adjusted_distinction_ratio || + (deviation <= NARROW_TURN_ANGLE && !intersection[index].entry_allowed); + }; + + const bool distinct_to_left = isDistinct(left_index, left_deviation); + const bool distinct_to_right = isDistinct(right_index, right_deviation); // Well distinct turn that is nearly straight if ((distinct_to_left || obvious_to_left) && (distinct_to_right || obvious_to_right)) return best; @@ -597,17 +622,18 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge, if (deviation_ratio < DISTINCTION_RATIO / 1.5) return 0; - // in comparison to another continuing road, we need a better distinction. This prevents - // situations where the turn is probably less obvious. An example are places that have a - // road with the same name entering/exiting: - // - // d - // / - // / - // a -- b - // \ - // \ - // c + /* in comparison to another continuing road, we need a better distinction. This prevents + situations where the turn is probably less obvious. An example are places that have a + road with the same name entering/exiting: + + d + / + / + a -- b + \ + \ + c + */ if (turn_data.name_id == continue_data.name_id && deviation_ratio < 1.5 * DISTINCTION_RATIO) diff --git a/src/extractor/guidance/roundabout_handler.cpp b/src/extractor/guidance/roundabout_handler.cpp index 24ca74e5f37..2b890e3c36c 100644 --- a/src/extractor/guidance/roundabout_handler.cpp +++ b/src/extractor/guidance/roundabout_handler.cpp @@ -8,7 +8,6 @@ #include #include -#include #include @@ -145,7 +144,7 @@ void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid, // Processing segregated roads would technically require an angle of the turn to be available // in postprocessing since we correct the turn-angle in turn-generaion. bool RoundaboutHandler::qualifiesAsRoundaboutIntersection( - const std::set &roundabout_nodes) const + const std::unordered_set &roundabout_nodes) const { // translate a node ID into its respective coordinate stored in the node_info_list const auto getCoordinate = [this](const NodeID node) { @@ -222,52 +221,46 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const std::unordered_set roundabout_name_ids; std::unordered_set connected_names; - const auto getNextOnRoundabout = - [this, &roundabout_name_ids, &connected_names](const NodeID node) { - EdgeID continue_edge = SPECIAL_EDGEID; - for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node)) + const auto getNextOnRoundabout = [this, &roundabout_name_ids, &connected_names]( + const NodeID node) { + EdgeID continue_edge = SPECIAL_EDGEID; + for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node)) + { + const auto &edge_data = node_based_graph.GetEdgeData(edge); + if (!edge_data.reversed && edge_data.roundabout) { - const auto &edge_data = node_based_graph.GetEdgeData(edge); - if (!edge_data.reversed && edge_data.roundabout) + if (SPECIAL_EDGEID != continue_edge) { - if (SPECIAL_EDGEID != continue_edge) - { - // fork in roundabout - return SPECIAL_EDGEID; - } - - if (EMPTY_NAMEID != edge_data.name_id) - { - bool add = true; - for (auto name_id : roundabout_name_ids) - { - - if (!requiresNameAnnounced(name_table.GetNameForID(name_id), - name_table.GetNameForID(edge_data.name_id), - street_name_suffix_table)) - { - add = false; - break; - } - } - if (add) - roundabout_name_ids.insert(edge_data.name_id); - } - - continue_edge = edge; + // fork in roundabout + return SPECIAL_EDGEID; } - else if (!edge_data.roundabout) + + if (EMPTY_NAMEID != edge_data.name_id) { - // remember all connected road names - connected_names.insert(edge_data.name_id); + + const auto announce = [&](unsigned id) { + return util::guidance::requiresNameAnnounced( + name_table.GetNameForID(id), + name_table.GetRefForID(id), + name_table.GetNameForID(edge_data.name_id), + name_table.GetRefForID(edge_data.name_id), + street_name_suffix_table); + }; + + if (std::all_of(begin(roundabout_name_ids), end(roundabout_name_ids), announce)) + roundabout_name_ids.insert(edge_data.name_id); } + + continue_edge = edge; } - return continue_edge; - }; - // the roundabout radius has to be the same for all locations we look at it from - // to guarantee this, we search the full roundabout for its vertices - // and select the three smallest ids - std::set roundabout_nodes; // needs to be sorted + else if (!edge_data.roundabout) + { + // remember all connected road names + connected_names.insert(edge_data.name_id); + } + } + return continue_edge; + }; // this value is a hard abort to deal with potential self-loops const auto countRoundaboutFlags = [&](const NodeID at_node) { @@ -283,6 +276,25 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const return count; }; + const auto getEdgeLength = [&](const NodeID source_node, EdgeID eid) { + double length = 0.; + auto last_coord = getCoordinate(source_node); + const auto &edge_bucket = compressed_edge_container.GetBucketReference(eid); + for (const auto &compressed_edge : edge_bucket) + { + const auto next_coord = getCoordinate(compressed_edge.node_id); + length += util::coordinate_calculation::haversineDistance(last_coord, next_coord); + last_coord = next_coord; + } + return length; + }; + + // the roundabout radius has to be the same for all locations we look at it from + // to guarantee this, we search the full roundabout for its vertices + // and select the three smallest ids + std::unordered_set roundabout_nodes; + double roundabout_length = 0.; + NodeID last_node = nid; while (0 == roundabout_nodes.count(last_node)) { @@ -301,6 +313,8 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const return RoundaboutType::None; } + roundabout_length += getEdgeLength(last_node, eid); + last_node = node_based_graph.GetTarget(eid); if (last_node == nid) @@ -317,31 +331,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const return RoundaboutType::RoundaboutIntersection; } - // calculate the radius of the roundabout/rotary. For two coordinates, we assume a minimal - // circle - // with both vertices right at the other side (so half their distance in meters). - // Otherwise, we construct a circle through the first tree vertices. - const auto getRadius = [&roundabout_nodes, &getCoordinate]() { - auto node_itr = roundabout_nodes.begin(); - if (roundabout_nodes.size() == 2) - { - const auto first = getCoordinate(*node_itr++), second = getCoordinate(*node_itr++); - return 0.5 * util::coordinate_calculation::haversineDistance(first, second); - } - else - { - const auto first = getCoordinate(*node_itr++), second = getCoordinate(*node_itr++), - third = getCoordinate(*node_itr++); - return util::coordinate_calculation::circleRadius(first, second, third); - } - }; - const double radius = getRadius(); - - // check whether the circle computation has gone wrong - // The radius computation can result in infinity, if the three coordinates are non-distinct. - // To stay on the safe side, we say its not a rotary - if (std::isinf(radius)) - return RoundaboutType::Roundabout; + const double radius = roundabout_length / (2 * M_PI); // Looks like a rotary: large roundabout with dedicated name // do we have a dedicated name for the rotary, if not its a roundabout diff --git a/src/extractor/guidance/sliproad_handler.cpp b/src/extractor/guidance/sliproad_handler.cpp index 169cc3efaba..a5767f47945 100644 --- a/src/extractor/guidance/sliproad_handler.cpp +++ b/src/extractor/guidance/sliproad_handler.cpp @@ -37,9 +37,9 @@ SliproadHandler::SliproadHandler(const IntersectionGenerator &intersection_gener // included for interface reasons only bool SliproadHandler::canProcess(const NodeID /*nid*/, const EdgeID /*via_eid*/, - const Intersection & /*intersection*/) const + const Intersection &intersection) const { - return true; + return intersection.size() > 2; } Intersection SliproadHandler:: @@ -56,13 +56,30 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) auto intersection = intersection_generator(at_node, road.turn.eid); auto in_edge = road.turn.eid; // skip over traffic lights - while (intersection.size() == 2) + // to prevent ending up in an endless loop, we remember all visited nodes. This is + // necessary, since merging of roads can actually create enterable loops of degree two + std::unordered_set visited_nodes; + auto node = at_node; + while (intersection.size() == 2 && visited_nodes.count(node) == 0) { - const auto node = node_based_graph.GetTarget(in_edge); + visited_nodes.insert(node); + node = node_based_graph.GetTarget(in_edge); + if (node == at_node) + { + // we ended up in a loop without exit + output_node = SPECIAL_NODEID; + intersection.clear(); + return intersection; + } in_edge = intersection[1].turn.eid; output_node = node_based_graph.GetTarget(in_edge); intersection = intersection_generator(node, in_edge); } + if (intersection.size() <= 2) + { + output_node = SPECIAL_NODEID; + intersection.clear(); + } return intersection; }; @@ -77,11 +94,15 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) // a one-sided sliproad, however, the non-sliproad can be considered `obvious`. Here we // assume that this could be the case and check for a potential sliproad/non-sliproad // situation. - NodeID intersection_node_one, intersection_node_two; + NodeID intersection_node_one = SPECIAL_NODEID, intersection_node_two = SPECIAL_NODEID; const auto intersection_following_index_one = findNextIntersectionForRoad( intersection_node_id, intersection[1], intersection_node_one); const auto intersection_following_index_two = findNextIntersectionForRoad( intersection_node_id, intersection[2], intersection_node_two); + // in case of broken roads, we return + if (intersection_following_index_one.empty() || + intersection_following_index_two.empty()) + return 0; // In case of loops at the end of the road, we will arrive back at the intersection // itself. If that is the case, the road is obviously not a sliproad. @@ -128,7 +149,6 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) return intersection; const auto source_edge_data = node_based_graph.GetEdgeData(source_edge_id); - // check whether the continue road is valid const auto check_valid = [this, source_edge_data](const ConnectedRoad &road) { const auto road_edge_data = node_based_graph.GetEdgeData(road.turn.eid); @@ -152,6 +172,9 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) const auto next_road_next_intersection = findNextIntersectionForRoad(intersection_node_id, next_road, next_intersection_node); + if (next_road_next_intersection.empty()) + return intersection; + // If we are at a traffic loop at the end of a road, don't consider it a sliproad if (intersection_node_id == next_intersection_node) return intersection; @@ -181,6 +204,22 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) return intersection; }(intersection_node_id); + const auto link_data = node_based_graph.GetEdgeData(road.turn.eid); + // Check if the road continues here + const bool is_through_street = + !target_intersection.empty() && + target_intersection.end() != + std::find_if(target_intersection.begin() + 1, + target_intersection.end(), + [this, &link_data](const ConnectedRoad &road) { + return node_based_graph.GetEdgeData(road.turn.eid).name_id == + link_data.name_id; + }); + + // if the sliproad candidate is a through street, we cannot handle it as a sliproad + if (is_through_street) + continue; + for (const auto &candidate_road : target_intersection) { const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.turn.eid); diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp index 99c081742fb..50b496ca152 100644 --- a/src/extractor/guidance/turn_analysis.cpp +++ b/src/extractor/guidance/turn_analysis.cpp @@ -94,7 +94,8 @@ Intersection TurnAnalysis::assignTurnTypes(const NodeID from_nid, } } // Handle sliproads - intersection = sliproad_handler(from_nid, via_eid, std::move(intersection)); + if (sliproad_handler.canProcess(from_nid, via_eid, intersection)) + intersection = sliproad_handler(from_nid, via_eid, std::move(intersection)); // Turn On Ramps Into Off Ramps, if we come from a motorway-like road if (node_based_graph.GetEdgeData(via_eid).road_classification.IsMotorwayClass()) diff --git a/src/extractor/guidance/turn_handler.cpp b/src/extractor/guidance/turn_handler.cpp index d867c826739..75ef5c609bb 100644 --- a/src/extractor/guidance/turn_handler.cpp +++ b/src/extractor/guidance/turn_handler.cpp @@ -164,30 +164,45 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection intersection[2].turn.instruction = {TurnType::OnRamp, DirectionModifier::Left}; } } - else + else if (obvious_index != 0) // has an obvious continuing road/obvious turn { + const auto direction_at_one = getTurnDirection(intersection[1].turn.angle); + const auto direction_at_two = getTurnDirection(intersection[2].turn.angle); if (obvious_index == 1) { intersection[1].turn.instruction = getInstructionForObvious( 3, via_edge, isThroughStreet(1, intersection), intersection[1]); + + const auto second_direction = (direction_at_one == direction_at_two && + direction_at_two == DirectionModifier::Straight) + ? DirectionModifier::SlightLeft + : direction_at_two; + + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + second_direction}; } else { - intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), - getTurnDirection(intersection[1].turn.angle)}; - } - - if (obvious_index == 2) - { + BOOST_ASSERT(obvious_index == 2); intersection[2].turn.instruction = getInstructionForObvious( 3, via_edge, isThroughStreet(2, intersection), intersection[2]); - } - else - { - intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), - getTurnDirection(intersection[2].turn.angle)}; + + const auto first_direction = (direction_at_one == direction_at_two && + direction_at_one == DirectionModifier::Straight) + ? DirectionModifier::SlightRight + : direction_at_one; + + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + first_direction}; } } + else // basic turn assignment + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + getTurnDirection(intersection[1].turn.angle)}; + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + getTurnDirection(intersection[2].turn.angle)}; + } return intersection; } @@ -207,18 +222,6 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection } } - // check whether there is a turn of the same name - const auto &in_data = node_based_graph.GetEdgeData(via_edge); - - const bool has_same_name_turn = [&]() { - for (std::size_t i = 1; i < intersection.size(); ++i) - { - if (node_based_graph.GetEdgeData(intersection[i].turn.eid).name_id == in_data.name_id) - return true; - } - return false; - }(); - // check whether the obvious choice is actually a through street if (obvious_index != 0) { @@ -227,26 +230,6 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection via_edge, isThroughStreet(obvious_index, intersection), intersection[obvious_index]); - if (has_same_name_turn && - node_based_graph.GetEdgeData(intersection[obvious_index].turn.eid).name_id != - in_data.name_id && - intersection[obvious_index].turn.instruction.type == TurnType::NewName) - { - // this is a special case that is necessary to correctly handle obvious turns on - // continuing streets. Right now osrm does not know about right of way. If a street - // turns to the left just like: - // - // a - // a - // aaaaaaa b b - // - // And another road exits here, we don't want to call it a new name, even though the - // turn is obvious and does not require steering. To correctly handle these situations - // in turn collapsing, we use the turn + straight combination here - intersection[obvious_index].turn.instruction.type = TurnType::Turn; - intersection[obvious_index].turn.instruction.direction_modifier = - DirectionModifier::Straight; - } // assign left/right turns intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index + 1); @@ -553,9 +536,26 @@ std::pair TurnHandler::findFork(const EdgeID via_edge, return true; }(); + // check if all entries in the fork range allow entry + const bool only_valid_entries = [&]() { + BOOST_ASSERT(right <= left && left < intersection.size()); + + // one past the end of the fork range + const auto end_itr = intersection.begin() + left + 1; + + const auto has_entry_forbidden = [](const ConnectedRoad &road) { + return !road.entry_allowed; + }; + + const auto first_disallowed_entry = + std::find_if(intersection.begin() + right, end_itr, has_entry_forbidden); + // if no entry was found that forbids entry, the intersection entries are all valid. + return first_disallowed_entry == end_itr; + }(); + // TODO check whether 2*NARROW_TURN is too large if (valid_indices && separated_at_left_side && separated_at_right_side && - not_more_than_three && !has_obvious && has_compatible_classes) + not_more_than_three && !has_obvious && has_compatible_classes && only_valid_entries) return std::make_pair(right, left); } return std::make_pair(std::size_t{0}, std::size_t{0}); diff --git a/src/extractor/guidance/turn_lane_augmentation.cpp b/src/extractor/guidance/turn_lane_augmentation.cpp index 564bd9d875a..3939489cc2c 100644 --- a/src/extractor/guidance/turn_lane_augmentation.cpp +++ b/src/extractor/guidance/turn_lane_augmentation.cpp @@ -1,5 +1,3 @@ -#include "util/debug.hpp" - #include "extractor/guidance/turn_lane_augmentation.hpp" #include "extractor/guidance/turn_lane_types.hpp" #include "util/simple_logger.hpp" diff --git a/src/extractor/guidance/turn_lane_matcher.cpp b/src/extractor/guidance/turn_lane_matcher.cpp index 7f2d46c38b1..5df9448eda7 100644 --- a/src/extractor/guidance/turn_lane_matcher.cpp +++ b/src/extractor/guidance/turn_lane_matcher.cpp @@ -108,7 +108,7 @@ double getMatchingQuality(const TurnLaneType::Mask tag, const ConnectedRoad &roa const auto modifier = getMatchingModifier(tag); BOOST_ASSERT(static_cast(modifier) < sizeof(idealized_turn_angles) / sizeof(*idealized_turn_angles)); - const auto idealized_angle = idealized_turn_angles[getMatchingModifier(tag)]; + const auto idealized_angle = idealized_turn_angles[modifier]; return angularDeviation(idealized_angle, road.turn.angle); } diff --git a/src/extractor/restriction_parser.cpp b/src/extractor/restriction_parser.cpp index 23e176f9b0a..7c466a5363c 100644 --- a/src/extractor/restriction_parser.cpp +++ b/src/extractor/restriction_parser.cpp @@ -29,20 +29,19 @@ RestrictionParser::RestrictionParser(ScriptingEnvironment &scripting_environment { if (use_turn_restrictions) { - restriction_exceptions = scripting_environment.GetExceptions(); - const unsigned exception_count = restriction_exceptions.size(); - if (exception_count) + restrictions = scripting_environment.GetRestrictions(); + const unsigned count = restrictions.size(); + if (count > 0) { - util::SimpleLogger().Write() << "Found " << exception_count - << " exceptions to turn restrictions:"; - for (const std::string &str : restriction_exceptions) + util::SimpleLogger().Write() << "Found " << count << " turn restriction tags:"; + for (const std::string &str : restrictions) { util::SimpleLogger().Write() << " " << str; } } else { - util::SimpleLogger().Write() << "Found no exceptions to turn restrictions"; + util::SimpleLogger().Write() << "Found no turn restriction tags"; } } } @@ -51,8 +50,9 @@ RestrictionParser::RestrictionParser(ScriptingEnvironment &scripting_environment * Tries to parse a relation as a turn restriction. This can fail for a number of * reasons. The return type is a boost::optional. * - * Some restrictions can also be ignored: See the ```get_exceptions``` function - * in the corresponding profile. + * Some restrictions can also be ignored: See the ```get_restrictions``` function + * in the corresponding profile. We use it for both namespacing restrictions, as in + * restriction:motorcar as well as whitelisting if its in except:motorcar. */ boost::optional RestrictionParser::TryParse(const osmium::Relation &relation) const @@ -63,13 +63,17 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const return {}; } - osmium::tags::KeyPrefixFilter filter(false); + osmium::tags::KeyFilter filter(false); filter.add(true, "restriction"); + // Not only use restriction= but also e.g. restriction:motorcar= + for (const auto &namespaced : restrictions) + filter.add(true, "restriction:" + namespaced); + const osmium::TagList &tag_list = relation.tags(); - osmium::tags::KeyPrefixFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end()); - osmium::tags::KeyPrefixFilter::iterator fi_end(filter, tag_list.end(), tag_list.end()); + osmium::tags::KeyFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end()); + osmium::tags::KeyFilter::iterator fi_end(filter, tag_list.end(), tag_list.end()); // if it's not a restriction, continue; if (std::distance(fi_begin, fi_end) == 0) @@ -105,22 +109,6 @@ RestrictionParser::TryParse(const osmium::Relation &relation) const { return {}; } - - // if the "restriction*" key is longer than 11 chars, it is a conditional exception (i.e. - // "restriction:") - if (key.size() > 11) - { - const auto ex_suffix = [&](const std::string &exception) { - return boost::algorithm::ends_with(key, exception); - }; - bool is_actually_restricted = - std::any_of(begin(restriction_exceptions), end(restriction_exceptions), ex_suffix); - - if (!is_actually_restricted) - { - return {}; - } - } } InputRestrictionContainer restriction_container(is_only_restriction); @@ -195,9 +183,8 @@ bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_st return std::any_of( std::begin(exceptions), std::end(exceptions), [&](const std::string ¤t_string) { - return std::end(restriction_exceptions) != std::find(std::begin(restriction_exceptions), - std::end(restriction_exceptions), - current_string); + return std::end(restrictions) != + std::find(std::begin(restrictions), std::end(restrictions), current_string); }); } } diff --git a/src/extractor/scripting_environment_lua.cpp b/src/extractor/scripting_environment_lua.cpp index f6c2cffe8eb..6452d775a2a 100644 --- a/src/extractor/scripting_environment_lua.cpp +++ b/src/extractor/scripting_environment_lua.cpp @@ -175,6 +175,7 @@ void LuaScriptingEnvironment::InitContext(LuaScriptingContext &context) .def_readwrite("forward_speed", &ExtractionWay::forward_speed) .def_readwrite("backward_speed", &ExtractionWay::backward_speed) .def_readwrite("name", &ExtractionWay::name) + .def_readwrite("ref", &ExtractionWay::ref) .def_readwrite("pronunciation", &ExtractionWay::pronunciation) .def_readwrite("destinations", &ExtractionWay::destinations) .def_readwrite("roundabout", &ExtractionWay::roundabout) @@ -326,18 +327,17 @@ std::vector LuaScriptingEnvironment::GetNameSuffixList() return suffixes_vector; } -std::vector LuaScriptingEnvironment::GetExceptions() +std::vector LuaScriptingEnvironment::GetRestrictions() { auto &context = GetLuaContext(); BOOST_ASSERT(context.state != nullptr); - std::vector restriction_exceptions; - if (util::luaFunctionExists(context.state, "get_exceptions")) + std::vector restrictions; + if (util::luaFunctionExists(context.state, "get_restrictions")) { // get list of turn restriction exceptions - luabind::call_function( - context.state, "get_exceptions", boost::ref(restriction_exceptions)); + luabind::call_function(context.state, "get_restrictions", boost::ref(restrictions)); } - return restriction_exceptions; + return restrictions; } void LuaScriptingEnvironment::SetupSources() diff --git a/src/osrm/osrm.cpp b/src/osrm/osrm.cpp index fa31cb3f445..67c3c5bf030 100644 --- a/src/osrm/osrm.cpp +++ b/src/osrm/osrm.cpp @@ -22,32 +22,34 @@ OSRM &OSRM::operator=(OSRM &&) noexcept = default; // Forward to implementation -engine::Status OSRM::Route(const engine::api::RouteParameters ¶ms, util::json::Object &result) +engine::Status OSRM::Route(const engine::api::RouteParameters ¶ms, + util::json::Object &result) const { return engine_->Route(params, result); } -engine::Status OSRM::Table(const engine::api::TableParameters ¶ms, json::Object &result) +engine::Status OSRM::Table(const engine::api::TableParameters ¶ms, json::Object &result) const { return engine_->Table(params, result); } -engine::Status OSRM::Nearest(const engine::api::NearestParameters ¶ms, json::Object &result) +engine::Status OSRM::Nearest(const engine::api::NearestParameters ¶ms, + json::Object &result) const { return engine_->Nearest(params, result); } -engine::Status OSRM::Trip(const engine::api::TripParameters ¶ms, json::Object &result) +engine::Status OSRM::Trip(const engine::api::TripParameters ¶ms, json::Object &result) const { return engine_->Trip(params, result); } -engine::Status OSRM::Match(const engine::api::MatchParameters ¶ms, json::Object &result) +engine::Status OSRM::Match(const engine::api::MatchParameters ¶ms, json::Object &result) const { return engine_->Match(params, result); } -engine::Status OSRM::Tile(const engine::api::TileParameters ¶ms, std::string &result) +engine::Status OSRM::Tile(const engine::api::TileParameters ¶ms, std::string &result) const { return engine_->Tile(params, result); } diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index c89ee631d41..f42c64fcf7d 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -497,7 +497,7 @@ int Storage::Run() } // load original edge information - NodeID *via_node_ptr = shared_layout_ptr->GetBlockPtr( + GeometryID *via_geometry_ptr = shared_layout_ptr->GetBlockPtr( shared_memory_ptr, SharedDataLayout::VIA_NODE_LIST); unsigned *name_id_ptr = shared_layout_ptr->GetBlockPtr( @@ -521,7 +521,7 @@ int Storage::Run() for (unsigned i = 0; i < number_of_original_edges; ++i) { edges_input_stream.read((char *)&(current_edge_data), sizeof(extractor::OriginalEdgeData)); - via_node_ptr[i] = current_edge_data.via_node; + via_geometry_ptr[i] = current_edge_data.via_geometry; name_id_ptr[i] = current_edge_data.name_id; travel_mode_ptr[i] = current_edge_data.travel_mode; lane_data_id_ptr[i] = current_edge_data.lane_data_id; diff --git a/src/tools/contract.cpp b/src/tools/contract.cpp index e7caee21ea2..bc7b3a7df5f 100644 --- a/src/tools/contract.cpp +++ b/src/tools/contract.cpp @@ -52,7 +52,12 @@ return_code parseArguments(int argc, char *argv[], contractor::ContractorConfig "level-cache,o", boost::program_options::value(&contractor_config.use_cached_priority) ->default_value(false), - "Use .level file to retain the contaction level for each node from the last run."); + "Use .level file to retain the contaction level for each node from the last run.")( + "edge-weight-updates-over-factor", + boost::program_options::value(&contractor_config.log_edge_updates_factor) + ->default_value(0.0), + "Use with `--segment-speed-file`. Provide an `x` factor, by which Extractor will log edge " + "weights updated by more than this factor"); // hidden options, will be allowed on command line, but will not be shown to the user boost::program_options::options_description hidden_options("Hidden options"); diff --git a/src/tools/routed.cpp b/src/tools/routed.cpp index 0b99b84ffe5..a2926bb3551 100644 --- a/src/tools/routed.cpp +++ b/src/tools/routed.cpp @@ -64,6 +64,7 @@ inline unsigned generateServerProgramOptions(const int argc, int &max_locations_viaroute, int &max_locations_distance_table, int &max_locations_map_matching, + int &max_results_nearest, bool &use_isochrone) { using boost::program_options::value; @@ -104,7 +105,10 @@ inline unsigned generateServerProgramOptions(const int argc, "Max. locations supported in distance table query") // ("max-matching-size", value(&max_locations_map_matching)->default_value(100), - "Max. locations supported in map matching query"); + "Max. locations supported in map matching query") // + ("max-nearest-size", + value(&max_results_nearest)->default_value(100), + "Max. results supported in nearest query"); // hidden options, will be allowed on command line, but will not be shown to the user boost::program_options::options_description hidden_options("Hidden options"); @@ -194,7 +198,8 @@ int main(int argc, const char *argv[]) try config.max_locations_viaroute, config.max_locations_distance_table, config.max_locations_map_matching, - config.use_isochrone); + config.max_results_nearest, + config.use_isochrone); if (init_result == INIT_OK_DO_NOT_START_ENGINE) { return EXIT_SUCCESS; diff --git a/src/util/name_table.cpp b/src/util/name_table.cpp index e15a35f840d..66a9bc4d8d4 100644 --- a/src/util/name_table.cpp +++ b/src/util/name_table.cpp @@ -22,6 +22,9 @@ NameTable::NameTable(const std::string &filename) name_stream >> m_name_table; + if (!name_stream) + throw exception("Unable to deserialize RangeTable for NameTable"); + unsigned number_of_chars = 0; name_stream.read(reinterpret_cast(&number_of_chars), sizeof(number_of_chars)); if (!name_stream) @@ -64,5 +67,23 @@ std::string NameTable::GetNameForID(const unsigned name_id) const } return result; } + +std::string NameTable::GetRefForID(const unsigned name_id) const +{ + // Way string data is stored in blocks based on `name_id` as follows: + // + // | name | destination | pronunciation | ref | + // ^ ^ + // [range) + // ^ name_id + 3 + // + // `name_id + offset` gives us the range of chars. + // + // Offset 0 is name, 1 is destination, 2 is pronunciation, 3 is ref. + // See datafacades and extractor callbacks for details. + const constexpr auto OFFSET_REF = 3u; + return GetNameForID(name_id + OFFSET_REF); +} + } // namespace util } // namespace osrm diff --git a/taginfo.json b/taginfo.json index 79079ad3871..ed64317c3ac 100644 --- a/taginfo.json +++ b/taginfo.json @@ -62,6 +62,24 @@ "object_types": [ "way" ], "description": "Roads with hov=designated are ignored by default." }, + { + "key": "hov:lanes", + "value": "designated", + "object_types": [ "way" ], + "description": "Roads with hov:lanes all-designated are ignored by default." + }, + { + "key": "hov:lanes:forward", + "value": "designated", + "object_types": [ "way" ], + "description": "Roads with hov:lanes:forward all-designated are ignored by default." + }, + { + "key": "hov:lanes:backward", + "value": "designated", + "object_types": [ "way" ], + "description": "Roads with hov:lanes:backward all-designated are ignored by default." + }, { "key": "toll", "value": "yes", diff --git a/third_party/libosmium/.gitignore b/third_party/libosmium/.gitignore index 50139035b7c..79e5e65d5c0 100644 --- a/third_party/libosmium/.gitignore +++ b/third_party/libosmium/.gitignore @@ -1,2 +1,4 @@ *.swp .ycm_extra_conf.pyc +/build +/libosmium-deps diff --git a/third_party/libosmium/.travis.yml b/third_party/libosmium/.travis.yml index ac0d270e2a4..9918112603e 100644 --- a/third_party/libosmium/.travis.yml +++ b/third_party/libosmium/.travis.yml @@ -4,24 +4,34 @@ # #----------------------------------------------------------------------------- -language: cpp +language: generic sudo: false +cache: + directories: + - $HOME/.ccache + +env: + global: + - CCACHE_TEMPDIR=/tmp/.ccache-temp + - CCACHE_COMPRESS=1 + - CASHER_TIME_OUT=1000 + matrix: include: # 1/ Linux Clang Builds - os: linux - compiler: clang + compiler: linux-clang35-release addons: apt: sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.5', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + packages: ['clang-3.5', 'cmake', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] env: COMPILER='clang++-3.5' BUILD_TYPE='Release' - os: linux - compiler: clang + compiler: linux-clang35-dev addons: apt: sources: ['llvm-toolchain-precise-3.5', 'ubuntu-toolchain-r-test', 'boost-latest'] @@ -30,42 +40,42 @@ matrix: - os: linux - compiler: clang + compiler: linux-clang37-release addons: apt: - sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.6' BUILD_TYPE='Release' + sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.7' BUILD_TYPE='Release' - os: linux - compiler: clang + compiler: linux-clang37-dev addons: apt: - sources: ['llvm-toolchain-precise-3.6', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.6', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.6' BUILD_TYPE='Dev' + sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.7' BUILD_TYPE='Dev' - os: linux - compiler: clang + compiler: linux-clang38-release addons: apt: - sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.7' BUILD_TYPE='Release' + sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.8' BUILD_TYPE='Release' - os: linux - compiler: clang + compiler: linux-clang38-dev addons: apt: - sources: ['llvm-toolchain-precise-3.7', 'ubuntu-toolchain-r-test', 'boost-latest'] - packages: ['clang-3.7', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] - env: COMPILER='clang++-3.7' BUILD_TYPE='Dev' + sources: ['llvm-toolchain-precise-3.8', 'ubuntu-toolchain-r-test', 'boost-latest'] + packages: ['clang-3.8', 'libboost1.55-all-dev', 'libgdal-dev', 'libgeos++-dev', 'libproj-dev', 'libsparsehash-dev', 'spatialite-bin'] + env: COMPILER='clang++-3.8' BUILD_TYPE='Dev' # 2/ Linux GCC Builds - os: linux - compiler: gcc + compiler: linux-gcc48-release addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -73,7 +83,7 @@ matrix: env: COMPILER='g++-4.8' COMPILER_FLAGS='-Wno-return-type' BUILD_TYPE='Release' - os: linux - compiler: gcc + compiler: linux-gcc48-dev addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -82,7 +92,7 @@ matrix: - os: linux - compiler: gcc + compiler: linux-gcc49-release addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -90,7 +100,7 @@ matrix: env: COMPILER='g++-4.9' BUILD_TYPE='Release' - os: linux - compiler: gcc + compiler: linux-gcc49-dev addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -99,7 +109,7 @@ matrix: - os: linux - compiler: gcc + compiler: linux-gcc50-release addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -107,7 +117,7 @@ matrix: env: COMPILER='g++-5' BUILD_TYPE='Release' - os: linux - compiler: gcc + compiler: linux-gcc50-dev addons: apt: sources: ['ubuntu-toolchain-r-test', 'boost-latest'] @@ -118,24 +128,24 @@ matrix: # 3/ OSX Clang Builds - os: osx osx_image: xcode6.4 - compiler: clang - env: COMPILER='clang++' BUILD_TYPE='Dev' + compiler: xcode64-clang-release + env: COMPILER='clang++' BUILD_TYPE='Release' - os: osx osx_image: xcode6.4 - compiler: clang - env: COMPILER='clang++' BUILD_TYPE='Release' + compiler: xcode64-clang-dev + env: COMPILER='clang++' BUILD_TYPE='Dev' - os: osx osx_image: xcode7 - compiler: clang - env: COMPILER='clang++' BUILD_TYPE='Dev' + compiler: xcode7-clang-release + env: COMPILER='clang++' BUILD_TYPE='Release' - os: osx osx_image: xcode7 - compiler: clang - env: COMPILER='clang++' BUILD_TYPE='Release' + compiler: xcode7-clang-dev + env: COMPILER='clang++' BUILD_TYPE='Dev' install: @@ -145,15 +155,15 @@ install: - | if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then brew remove gdal - brew install cmake boost google-sparsehash gdal + brew install cmake boost google-sparsehash gdal || true fi + - cmake --version before_script: - cd ${TRAVIS_BUILD_DIR} - mkdir build && cd build - - CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata" + - CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DBUILD_WITH_CCACHE=1 -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata" script: - - make VERBOSE=1 - - ctest --output-on-failure + - make VERBOSE=1 && ctest --output-on-failure diff --git a/third_party/libosmium/.ycm_extra_conf.py b/third_party/libosmium/.ycm_extra_conf.py index 2b8730616a0..6fc19376d5e 100644 --- a/third_party/libosmium/.ycm_extra_conf.py +++ b/third_party/libosmium/.ycm_extra_conf.py @@ -29,6 +29,11 @@ '-x', 'c++', +# workaround for https://github.com/Valloric/YouCompleteMe/issues/303 +# also see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=800618 +'-isystem', +'/usr/lib/ycmd/clang_includes/', + # libosmium include dirs '-I%s/include' % basedir, '-I%s/test/include' % basedir, diff --git a/third_party/libosmium/CHANGELOG.md b/third_party/libosmium/CHANGELOG.md index 394644890e9..0d132ef5916 100644 --- a/third_party/libosmium/CHANGELOG.md +++ b/third_party/libosmium/CHANGELOG.md @@ -13,6 +13,140 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +## [2.9.0] - 2016-09-15 + +### Added + +- Support for reading OPL files. +- For diff output OSM objects in buffers can be marked as only in one or the + other file. The OPL and debug output formats support diff output based on + this. +- Add documentation and range checks to `Tile` struct. +- More documentation. +- More examples and more extensive comments on examples. +- Support for a progress report in `osmium::io::Reader()` and a `ProgressBar` + utility class to use it. +- New `OSMObject::set_timestamp(const char*)` function. + +### Changed + +- Parse coordinates in scientific notations ourselves. +- Updated included protozero version to 1.4.2. +- Lots of one-argument constructors are now explicit. +- Timestamp parser now uses our own implementation instead of strptime. + This is faster and independant of locale settings. +- More cases of invalid areas with duplicate segments are reported as + errors. + +### Fixed + +- Fixed a problem limiting cache file sizes on Windows to 32 bit. +- Fixed includes. +- Exception messages for invalid areas do not report "area contains no rings" + any more, but "invalid area". + + +## [2.8.0] - 2016-08-04 + +### Added + +- EWKT support. +- Track `pop` type calls and queue underruns when `OSMIUM_DEBUG_QUEUE_SIZE` + environment variable is set. + +### Changed + +- Switched to newest protozero v1.4.0. This should deliver some speedups + when parsing PBF files. This also removes the DeltaEncodeIterator class, + which isn't needed any more. +- Uses `std::unordered_map` instead of `std::map` in PBF string table code + speeding up writing of PBF files considerably. +- Uses less memory when writing PBF files (smaller string table by default). +- Removes dependency on sparsehash and boost program options libraries for + examples. +- Cleaned up threaded queue code. + +### Fixed + +- A potentially very bad bug was fixed: When there are many and/or long strings + in tag keys and values and/or user names and/or relation roles, the string + table inside a PBF block would overflow. I have never seen this happen for + normal OSM data, but that doesn't mean it can't happen. The result is that + the strings will all be mixed up, keys for values, values for user names or + whatever. +- Automatically set correct SRID when creating WKB and GEOS geometries. + Note that this changes the behaviour of libosmium when creating GEOS + geometries. Before we created them with -1 as SRID unless set otherwise. + Manual setting of the SRID on the GEOSGeometryFactory is now deprecated. +- Allow coordinates of nodes in scientific notation when reading XML files. + This shouldn't be used really, but sometimes you can find them. + + +## [2.7.2] - 2016-06-08 + +### Changed + +- Much faster output of OSM files in XML, OPL, or debug formats. + +### Fixed + +- Parsing and output of coordinates now faster and always uses decimal dot + independant of locale setting. +- Do not output empty discussion elements in changeset XML output. +- Data corruption regression in mmap based indexes. + + +## [2.7.1] - 2016-06-01 + +### Fixes + +- Update version number in version.hpp. + + +## [2.7.0] - 2016-06-01 + +### Added + +- New functions for iterating over specific item types in buffers + (`osmium::memory::Buffer::select()`), over specific subitems + (`osmium::OSMObject::subitems()`), and for iterating over all rings of + an area (`osmium::Areas::outer_rings(`), `inner_rings()`). +- Debug output optionally prints CRC32 when `add_crc32` file option is set. + +### Changed + +- XML parser will not allow any XML entities which are usually not used in OSM + files anyway. This can help avoiding DOS attacks. +- Removed SortedQueue implementation which was never used. +- Also incorporate Locations in NodeRefs into CRC32 checksums. This means + all checksums will be different compared to earlier versions of libosmium. +- The completely new algorithm for assembling multipolygons is much faster, + has better error reporting, generates statistics and can build more complex + multipolygons correctly. The ProblemReporter classes have changed to make + this happen, if you have written your own, you have to fix it. +- Sparse node location stores are now only sorted if needed, ie. when nodes + come in unordered. + +### Fixed + +- Output operator for Location shows full precision. +- Undefined behaviour in WKB writer and `types_from_string()` function. +- Fix unsigned overflow in pool.hpp. +- OSM objects are now ordered by type (nodes, then ways, then relations), + then ID, then version, then timestamp. Ordering by timestamp is normally + not necessary, because there can't be two objects with same type, ID, and + version but different timestamp. But this can happen when diffs are + created from OSM extracts, so we check for this here. This change also + makes sure IDs are always ordered by absolute IDs, positives first, so + order is 0, 1, -1, 2, -2, ... +- Data corruption bug fixed in disk based indexes (used for the node + location store for instance). This only affected you, if you created + and index, closed it, and re-opened it (possibly in a different process) + and if there were missing nodes. If you looked up those nodes, you got + location (0,0) back instead of an error. +- Memory corruption bug showing up with GDAL 2. + + ## [2.6.1] - 2016-02-22 ### Added @@ -276,7 +410,12 @@ 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.9.0...HEAD +[2.9.0]: https://github.com/osmcode/libosmium/compare/v2.8.0...v2.9.0 +[2.8.0]: https://github.com/osmcode/libosmium/compare/v2.7.2...v2.8.0 +[2.7.2]: https://github.com/osmcode/libosmium/compare/v2.7.1...v2.7.2 +[2.7.1]: https://github.com/osmcode/libosmium/compare/v2.7.0...v2.7.1 +[2.7.0]: https://github.com/osmcode/libosmium/compare/v2.6.1...v2.7.0 [2.6.1]: https://github.com/osmcode/libosmium/compare/v2.6.0...v2.6.1 [2.6.0]: https://github.com/osmcode/libosmium/compare/v2.5.4...v2.6.0 [2.5.4]: https://github.com/osmcode/libosmium/compare/v2.5.3...v2.5.4 diff --git a/third_party/libosmium/CMakeLists.txt b/third_party/libosmium/CMakeLists.txt index bc9b12ea5d8..095137b3975 100644 --- a/third_party/libosmium/CMakeLists.txt +++ b/third_party/libosmium/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 9) +set(LIBOSMIUM_VERSION_PATCH 0) set(LIBOSMIUM_VERSION "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}") @@ -61,6 +61,27 @@ option(INSTALL_UTFCPP "also install utfcpp headers" OFF) option(WITH_PROFILING "add flags needed for profiling" OFF) +#----------------------------------------------------------------------------- +# +# CCache support +# +#----------------------------------------------------------------------------- + +option(BUILD_WITH_CCACHE "build using ccache" OFF) + +if(BUILD_WITH_CCACHE) + find_program(CCACHE_PROGRAM ccache) + if(CCACHE_PROGRAM) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "CCACHE_CPP2=1 ${CCACHE_PROGRAM}") + + # workaround for some clang versions + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + add_definitions(-Qunused-arguments) + endif() + endif() +endif() + + #----------------------------------------------------------------------------- # # Coverage support diff --git a/third_party/libosmium/CONTRIBUTING.md b/third_party/libosmium/CONTRIBUTING.md index 1064b94de98..a054f362e6f 100644 --- a/third_party/libosmium/CONTRIBUTING.md +++ b/third_party/libosmium/CONTRIBUTING.md @@ -1,143 +1,12 @@ -# Notes for Developers +Some rules for contributing to this project: -Read this if you want to contribute to Libosmium. +* Please open a separate issue for each problem, question, or comment you have. + Do not re-use existing issues for other topics, even if they are similar. This + keeps issues small and manageable and makes it much easier to follow through + and make sure each problem is taken care of. - -## Versioning - -Osmium is currently considered in beta and doesn't use versioning yet. Proper -versions will be introduced as soon as it is somewhat stable. - - -## Namespace - -All Osmium code MUST be in the `osmium` namespace or one of its sub-namespaces. - - -## Include-Only - -Osmium is a include-only library. You can't compile the library itself. There -is no libosmium.so. - -One drawback ist that you can't have static data in classes, because there -is no place to put this data. - -All free functions must be declared `inline`. - - -## Coding Conventions - -These coding conventions have been changing over time and some code is still -different. - -* All include files have `#ifdef` guards around them, macros are the path name - in all uppercase where the slashes (`/`) have been changed to underscore (`_`). -* Class names begin with uppercase chars and use CamelCase. Smaller helper - classes are usually defined as struct and have lowercase names. -* Macros (and only macros) are all uppercase. Use macros sparingly, usually - a simple (maybe constexpr) inline function is better. Undef macros after use - if possible. -* Macros should only be used for controlling which parts of the code should be - included when compiling or to avoid major code repetitions. -* Variables, attributes, and function names are lowercase with - `underscores_between_words`. -* Class attribute names start with `m_` (member). -* Use `descriptive_variable_names`, exceptions are well-established conventions - like `i` for a loop variable. Iterators are usually called `it`. -* Declare variables where they are first used (C++ style), not at the beginning - of a function (old C style). -* Names from external namespaces (even `std`) are always mentioned explicitly. - Do not use `using` (except for `std::swap`). This way we can't even by - accident pollute the namespace of the code using Osmium. -* Always use the standard swap idiom: `using std::swap; swap(foo, bar);`. -* `#include` directives appear in three "blocks" after the copyright notice. - The blocks are separated by blank lines. First block contains `#include`s for - standard C/C++ includes, second block for any external libs used, third - block for osmium internal includes. Within each block `#include`s are usually - sorted by path name. All `#include`s use `<>` syntax not `""`. -* Names not to be used from outside the library should be in a namespace - called `detail` under the namespace where they would otherwise appear. If - whole include files are never meant to be included from outside they should - be in a subdirectory called `detail`. -* All files have suffix `.hpp`. -* Closing } of all classes and namespaces should have a trailing comment - with the name of the class/namespace. -* All constructors with one (or more arguments if they have a default) should - be declared "explicit" unless there is a reason for them not to be. Document - that reason. -* If a class has any of the special methods (copy/move constructor/assigment, - destructor) it should have all of them, possibly marking them as default or - deleted. -* Typedefs have `names_like_this_type` which end in `_type`. Typedefs should - use the new `using foo_type = bar` syntax instead of the old - `typedef bar foo_type`. -* Template parameters are single uppercase letters or start with uppercase `T` - and use CamelCase. -* Always use `typename` in templates, not `class`: `template `. -* The ellipsis in variadic template never has a space to the left of it and - always has a space to the right: `template ` etc. - -Keep to the indentation and other styles used in the code. Use `make indent` -in the toplevel directory to fix indentation and styling. It calls `astyle` -with the right parameters. This program is in the `astyle` Debian package. - - -## C++11 - -Osmium uses C++11 and you can use its features such as auto, lambdas, -threading, etc. There are a few features we do not use, because even modern -compilers don't support them yet. This list might change as we get more data -about which compilers support which feature and what operating system versions -or distributions have which versions of these compilers installed. - -GCC 4.6 - too old, not supported (Ubuntu 12.04 LTS) -GCC 4.7.2 - can probably not be supported (Debian wheezy) -GCC 4.7.3 - probably works -GCC 4.8 - works and is supported from here on -clang 3.0 - too old, not supported (Debian wheezy, Ubuntu 12.04 LTS) -clang 3.2 - probably works -clang 3.5 - works and is supported from here on - -Use `include/osmium/util/compatibility.hpp` if there are compatibility problems -between compilers due to different C++11 support. - - -## Checking your code - -The Osmium makefiles use pretty draconian warning options for the compiler. -This is good. Code MUST never produce any warnings, even with those settings. -If absolutely necessary pragmas can be used to disable certain warnings in -specific areas of the code. - -If the static code checker `cppcheck` is installed, the CMake configuration -will add a new build target `cppcheck` that will check all `.cpp` and `.hpp` -files. Cppcheck finds some bugs that gcc/clang doesn't. But take the result -with a grain of salt, it also sometimes produces wrong warnings. - -Set `BUILD_HEADERS=ON` in the CMake config to enable compiling all include -files on their own to check whether dependencies are all okay. All include -files MUST include all other include files they depend on. - -Call `cmake/iwyu.sh` to check for proper includes and forward declarations. -This uses the clang-based `include-what-you-use` program. Note that it does -produce some false reports and crashes often. The `osmium.imp` file can be -used to define mappings for iwyu. See the IWYU tool at -. - - -## Testing - -There are a unit tests using the Catch Unit Test Framework in the `test` -directory and some data tests in `test/osm-testdata`. They are built by the -default cmake config. Run `ctest` to run them. Many more tests are needed. - - -## Documenting the code - -All namespaces, classes, functions, attributes, etc. should be documented. - -Osmium uses the Doxygen (www.doxygen.org) source code documentation system. -If it is installed, the CMake configuration will add a new build target, so -you can build it with `make doc`. +* We'd love for you to send pull requests for fixes you have made or new features + you have added. Please read the [notes for developers](NOTES_FOR_DEVELOPERS.md) + beforehand which contain some coding guidelines. diff --git a/third_party/libosmium/NOTES_FOR_DEVELOPERS.md b/third_party/libosmium/NOTES_FOR_DEVELOPERS.md new file mode 100644 index 00000000000..3c1f73b788d --- /dev/null +++ b/third_party/libosmium/NOTES_FOR_DEVELOPERS.md @@ -0,0 +1,143 @@ + +# Notes for Developers + +Read this if you want to contribute to Libosmium. + + +## Namespace + +All Osmium code MUST be in the `osmium` namespace or one of its sub-namespaces. + + +## Include-Only + +Osmium is a include-only library. You can't compile the library itself. There +is no libosmium.so. + +One drawback ist that you can't have static data in classes, because there +is no place to put this data. + +All free functions must be declared `inline`. + + +## Coding Conventions + +These coding conventions have been changing over time and some code is still +different. + +* All include files have `#ifdef` guards around them, macros are the path name + in all uppercase where the slashes (`/`) have been changed to underscore (`_`). +* Class names begin with uppercase chars and use CamelCase. Smaller helper + classes are usually defined as struct and have lowercase names. +* Macros (and only macros) are all uppercase. Use macros sparingly, usually + a simple (maybe constexpr) inline function is better. Undef macros after use + if possible. +* Macros should only be used for controlling which parts of the code should be + included when compiling or to avoid major code repetitions. +* Variables, attributes, and function names are lowercase with + `underscores_between_words`. +* Class attribute names start with `m_` (member). +* Use `descriptive_variable_names`, exceptions are well-established conventions + like `i` for a loop variable. Iterators are usually called `it`. +* Declare variables where they are first used (C++ style), not at the beginning + of a function (old C style). +* Names from external namespaces (even `std`) are always mentioned explicitly. + Do not use `using` (except for `std::swap`). This way we can't even by + accident pollute the namespace of the code using Osmium. +* Always use the standard swap idiom: `using std::swap; swap(foo, bar);`. +* `#include` directives appear in three "blocks" after the copyright notice. + The blocks are separated by blank lines. First block contains `#include`s for + standard C/C++ includes, second block for any external libs used, third + block for osmium internal includes. Within each block `#include`s are usually + sorted by path name. All `#include`s use `<>` syntax not `""`. +* Names not to be used from outside the library should be in a namespace + called `detail` under the namespace where they would otherwise appear. If + whole include files are never meant to be included from outside they should + be in a subdirectory called `detail`. +* All files have suffix `.hpp`. +* Closing } of all classes and namespaces should have a trailing comment + with the name of the class/namespace. +* All constructors with one (or more arguments if they have a default) should + be declared "explicit" unless there is a reason for them not to be. Document + that reason. +* If a class has any of the special methods (copy/move constructor/assigment, + destructor) it should have all of them, possibly marking them as default or + deleted. +* Typedefs have `names_like_this_type` which end in `_type`. Typedefs should + use the new `using foo_type = bar` syntax instead of the old + `typedef bar foo_type`. +* Template parameters are single uppercase letters or start with uppercase `T` + and use CamelCase. +* Always use `typename` in templates, not `class`: `template `. +* The ellipsis in variadic template never has a space to the left of it and + always has a space to the right: `template ` etc. + +Keep to the indentation and other styles used in the code. Use `make indent` +in the toplevel directory to fix indentation and styling. It calls `astyle` +with the right parameters. This program is in the `astyle` Debian package. + + +## C++11 + +Osmium uses C++11 and you can use its features such as auto, lambdas, +threading, etc. There are a few features we do not use, because even modern +compilers don't support them yet. This list might change as we get more data +about which compilers support which feature and what operating system versions +or distributions have which versions of these compilers installed. + +GCC 4.6 - too old, not supported (Ubuntu 12.04 LTS) +GCC 4.7.2 - can probably not be supported (Debian wheezy) +GCC 4.7.3 - probably works +GCC 4.8 - works and is supported from here on +clang 3.0 - too old, not supported (Debian wheezy, Ubuntu 12.04 LTS) +clang 3.2 - probably works +clang 3.5 - works and is supported from here on + +Use `include/osmium/util/compatibility.hpp` if there are compatibility problems +between compilers due to different C++11 support. + + +## Operating systems + +Usually all code must work on Linux, OSX, and Windows. Execptions are allowed +for some minor functionality, but please discuss this first. + + +## Checking your code + +The Osmium makefiles use pretty draconian warning options for the compiler. +This is good. Code MUST never produce any warnings, even with those settings. +If absolutely necessary pragmas can be used to disable certain warnings in +specific areas of the code. + +If the static code checker `cppcheck` is installed, the CMake configuration +will add a new build target `cppcheck` that will check all `.cpp` and `.hpp` +files. Cppcheck finds some bugs that gcc/clang doesn't. But take the result +with a grain of salt, it also sometimes produces wrong warnings. + +Set `BUILD_HEADERS=ON` in the CMake config to enable compiling all include +files on their own to check whether dependencies are all okay. All include +files MUST include all other include files they depend on. + +Call `cmake/iwyu.sh` to check for proper includes and forward declarations. +This uses the clang-based `include-what-you-use` program. Note that it does +produce some false reports and crashes often. The `osmium.imp` file can be +used to define mappings for iwyu. See the IWYU tool at +. + + +## Testing + +There are a unit tests using the Catch Unit Test Framework in the `test` +directory and some data tests in `test/osm-testdata`. They are built by the +default cmake config. Run `ctest` to run them. Many more tests are needed. + + +## Documenting the code + +All namespaces, classes, functions, attributes, etc. should be documented. + +Osmium uses the Doxygen (www.doxygen.org) source code documentation system. +If it is installed, the CMake configuration will add a new build target, so +you can build it with `make doc`. + diff --git a/third_party/libosmium/appveyor.yml b/third_party/libosmium/appveyor.yml index 8244d98e775..7e6060a7ff6 100644 --- a/third_party/libosmium/appveyor.yml +++ b/third_party/libosmium/appveyor.yml @@ -22,88 +22,5 @@ clone_folder: c:\projects\libosmium platform: x64 -install: - # show all available env vars - - set - - echo cmake on AppVeyor - - cmake -version - - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 - - set PATH=c:\projects\libosmium\cmake-3.1.0-win32-x86\bin;%PATH% - - set LODEPSDIR=c:\projects\libosmium\libosmium-deps - - set PROJ_LIB=%LODEPSDIR%\proj\share - - set GDAL_DATA=%LODEPSDIR%\gdal\data - #geos.dll - - set PATH=%LODEPSDIR%\geos\lib;%PATH% - #gdal.dll - - set PATH=%LODEPSDIR%\gdal\lib;%PATH% - #libexpat.dll - - set PATH=%LODEPSDIR%\expat\lib;%PATH% - #libtiff.dll - - set PATH=%LODEPSDIR%\libtiff\lib;%PATH% - #jpeg.dll - - set PATH=%LODEPSDIR%\jpeg\lib;%PATH% - #zlibwapi.dll - - set PATH=%LODEPSDIR%\zlib\lib;%PATH% - #convert backslashes in bzip2 path to forward slashes - #cmake cannot find it otherwise - - set LIBBZIP2=%LODEPSDIR%\bzip2\lib\libbz2.lib - - set LIBBZIP2=%LIBBZIP2:\=/% - - ps: Start-FileDownload https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/cmake-3.1.0-win32-x86.7z -FileName cm.7z - - ps: Start-FileDownload https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/libosmium-deps-win-14.0-x64.7z -FileName lodeps.7z - - 7z x cm.7z | %windir%\system32\find "ing archive" - - 7z x lodeps.7z | %windir%\system32\find "ing archive" - - echo %LODEPSDIR% - - dir %LODEPSDIR% - - echo our own cmake - - cmake -version - - cd c:\projects - - git clone --depth 1 https://github.com/osmcode/osm-testdata.git - build_script: - - cd c:\projects\libosmium - - mkdir build - - cd build - - echo %config% - # This will produce lots of LNK4099 warnings which can be ignored. - # Unfortunately they can't be disabled, see - # http://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings - - cmake -LA -G "Visual Studio 14 Win64" - -DOsmium_DEBUG=TRUE - -DCMAKE_BUILD_TYPE=%config% - -DBUILD_HEADERS=OFF - -DBOOST_ROOT=%LODEPSDIR%\boost - -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_58.lib - -DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib - -DBZIP2_LIBRARY_RELEASE=%LIBBZIP2% - -DCMAKE_PREFIX_PATH=%LODEPSDIR%\zlib;%LODEPSDIR%\expat;%LODEPSDIR%\bzip2;%LODEPSDIR%\geos;%LODEPSDIR%\gdal;%LODEPSDIR%\proj;%LODEPSDIR%\sparsehash;%LODEPSDIR%\wingetopt - .. - - msbuild libosmium.sln /p:Configuration=%config% /toolsversion:14.0 /p:Platform=x64 /p:PlatformToolset=v140 - #- cmake .. -LA -G "NMake Makefiles" - # -DOsmium_DEBUG=TRUE - # -DCMAKE_BUILD_TYPE=%config% - # -DBOOST_ROOT=%LODEPSDIR%\boost - # -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_57.lib - # -DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib - # -DZLIB_INCLUDE_DIR=%LODEPSDIR%\zlib\include - # -DEXPAT_LIBRARY=%LODEPSDIR%\expat\lib\libexpat.lib - # -DEXPAT_INCLUDE_DIR=%LODEPSDIR%\expat\include - # -DBZIP2_LIBRARIES=%LIBBZIP2% - # -DBZIP2_INCLUDE_DIR=%LODEPSDIR%\bzip2\include - # -DGDAL_LIBRARY=%LODEPSDIR%\gdal\lib\gdal_i.lib - # -DGDAL_INCLUDE_DIR=%LODEPSDIR%\gdal\include - # -DGEOS_LIBRARY=%LODEPSDIR%\geos\lib\geos.lib - # -DGEOS_INCLUDE_DIR=%LODEPSDIR%\geos\include - # -DPROJ_LIBRARY=%LODEPSDIR%\proj\lib\proj.lib - # -DPROJ_INCLUDE_DIR=%LODEPSDIR%\proj\include - # -DSPARSEHASH_INCLUDE_DIR=%LODEPSDIR%\sparsehash\include - # -DGETOPT_LIBRARY=%LODEPSDIR%\wingetopt\lib\wingetopt.lib - # -DGETOPT_INCLUDE_DIR=%LODEPSDIR%\wingetopt\include - #- nmake - -test_script: - # "-E testdata-overview" exempts one test we know fails on Appveyor - # because we currently don't have spatialite support. - - ctest --output-on-failure - -C %config% - -E testdata-overview - + - build-appveyor.bat diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp index d50c53dc814..1a16275f0df 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_count.cpp @@ -5,7 +5,9 @@ */ #include +#include #include +#include #include #include @@ -35,12 +37,12 @@ struct CountHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - exit(1); + std::exit(1); } - std::string input_filename = argv[1]; + const std::string input_filename{argv[1]}; - osmium::io::Reader reader(input_filename); + osmium::io::Reader reader{input_filename}; CountHandler handler; osmium::apply(reader, handler); diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp index 8fa696a4ee6..6062eccebc9 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_count_tag.cpp @@ -5,7 +5,9 @@ */ #include +#include #include +#include #include #include @@ -38,12 +40,12 @@ struct CountHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - exit(1); + std::exit(1); } - std::string input_filename = argv[1]; + const std::string input_filename{argv[1]}; - osmium::io::Reader reader(input_filename); + osmium::io::Reader reader{input_filename}; CountHandler handler; osmium::apply(reader, handler); diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp index 02578261808..2cf550ca9c5 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_index_map.cpp @@ -4,7 +4,9 @@ */ +#include #include +#include #include #include @@ -13,24 +15,24 @@ #include #include -typedef osmium::index::map::Map index_type; +using index_type = osmium::index::map::Map; -typedef osmium::handler::NodeLocationsForWays location_handler_type; +using location_handler_type = osmium::handler::NodeLocationsForWays; int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " OSMFILE FORMAT\n"; - exit(1); + std::exit(1); } - std::string input_filename = argv[1]; - std::string location_store = argv[2]; + const std::string input_filename{argv[1]}; + const std::string location_store{argv[2]}; - osmium::io::Reader reader(input_filename); + osmium::io::Reader reader{input_filename}; const auto& map_factory = osmium::index::MapFactory::instance(); std::unique_ptr index = map_factory.create_map(location_store); - location_handler_type location_handler(*index); + location_handler_type location_handler{*index}; location_handler.ignore_errors(); osmium::apply(reader, location_handler); diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp index 66e2a0bd918..e9f950c3168 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_static_vs_dynamic_index.cpp @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -29,23 +31,23 @@ #include #include -typedef osmium::index::map::SparseMemArray static_index_type; -const std::string location_store="sparse_mem_array"; +using static_index_type = osmium::index::map::SparseMemArray; +const std::string location_store{"sparse_mem_array"}; -typedef osmium::index::map::Map dynamic_index_type; +using dynamic_index_type = osmium::index::map::Map; -typedef osmium::handler::NodeLocationsForWays static_location_handler_type; -typedef osmium::handler::NodeLocationsForWays dynamic_location_handler_type; +using static_location_handler_type = osmium::handler::NodeLocationsForWays; +using dynamic_location_handler_type = osmium::handler::NodeLocationsForWays; int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - exit(1); + std::exit(1); } - std::string input_filename = argv[1]; + const std::string input_filename{argv[1]}; - osmium::memory::Buffer buffer = osmium::io::read_file(input_filename); + osmium::memory::Buffer buffer{osmium::io::read_file(input_filename)}; const auto& map_factory = osmium::index::MapFactory::instance(); @@ -67,20 +69,20 @@ int main(int argc, char* argv[]) { { // static index - osmium::memory::Buffer tmp_buffer(buffer.committed()); + osmium::memory::Buffer tmp_buffer{buffer.committed()}; for (const auto& item : buffer) { tmp_buffer.add_item(item); tmp_buffer.commit(); } static_index_type static_index; - static_location_handler_type static_location_handler(static_index); + static_location_handler_type static_location_handler{static_index}; - auto start = std::chrono::steady_clock::now(); + const auto start = std::chrono::steady_clock::now(); osmium::apply(tmp_buffer, static_location_handler); - auto end = std::chrono::steady_clock::now(); + const auto end = std::chrono::steady_clock::now(); - double duration = std::chrono::duration(end-start).count(); + const double duration = std::chrono::duration(end-start).count(); if (duration < static_min) static_min = duration; if (duration > static_max) static_max = duration; @@ -89,21 +91,21 @@ int main(int argc, char* argv[]) { { // dynamic index - osmium::memory::Buffer tmp_buffer(buffer.committed()); + osmium::memory::Buffer tmp_buffer{buffer.committed()}; for (const auto& item : buffer) { tmp_buffer.add_item(item); tmp_buffer.commit(); } std::unique_ptr index = map_factory.create_map(location_store); - dynamic_location_handler_type dynamic_location_handler(*index); + dynamic_location_handler_type dynamic_location_handler{*index}; dynamic_location_handler.ignore_errors(); - auto start = std::chrono::steady_clock::now(); + const auto start = std::chrono::steady_clock::now(); osmium::apply(tmp_buffer, dynamic_location_handler); - auto end = std::chrono::steady_clock::now(); + const auto end = std::chrono::steady_clock::now(); - double duration = std::chrono::duration(end-start).count(); + const double duration = std::chrono::duration(end-start).count(); if (duration < dynamic_min) dynamic_min = duration; if (duration > dynamic_max) dynamic_max = duration; @@ -111,21 +113,21 @@ int main(int argc, char* argv[]) { } } - double static_avg = static_sum/runs; - double dynamic_avg = dynamic_sum/runs; + const double static_avg = static_sum/runs; + const double dynamic_avg = dynamic_sum/runs; std::cout << "static min=" << static_min << "ms avg=" << static_avg << "ms max=" << static_max << "ms\n"; std::cout << "dynamic min=" << dynamic_min << "ms avg=" << dynamic_avg << "ms max=" << dynamic_max << "ms\n"; - double rfactor = 100.0; - double diff_min = std::round((dynamic_min - static_min) * rfactor) / rfactor; - double diff_avg = std::round((dynamic_avg - static_avg) * rfactor) / rfactor; - double diff_max = std::round((dynamic_max - static_max) * rfactor) / rfactor; + const double rfactor = 100.0; + const double diff_min = std::round((dynamic_min - static_min) * rfactor) / rfactor; + const double diff_avg = std::round((dynamic_avg - static_avg) * rfactor) / rfactor; + const double diff_max = std::round((dynamic_max - static_max) * rfactor) / rfactor; - double prfactor = 10.0; - double percent_min = std::round((100.0 * diff_min / static_min) * prfactor) / prfactor; - double percent_avg = std::round((100.0 * diff_avg / static_avg) * prfactor) / prfactor; - double percent_max = std::round((100.0 * diff_max / static_max) * prfactor) / prfactor; + const double prfactor = 10.0; + const double percent_min = std::round((100.0 * diff_min / static_min) * prfactor) / prfactor; + const double percent_avg = std::round((100.0 * diff_avg / static_avg) * prfactor) / prfactor; + const double percent_max = std::round((100.0 * diff_max / static_max) * prfactor) / prfactor; std::cout << "difference:"; std::cout << " min=" << diff_min << "ms (" << percent_min << "%)"; diff --git a/third_party/libosmium/benchmarks/osmium_benchmark_write_pbf.cpp b/third_party/libosmium/benchmarks/osmium_benchmark_write_pbf.cpp index 869f3a8f849..632dd26ebd9 100644 --- a/third_party/libosmium/benchmarks/osmium_benchmark_write_pbf.cpp +++ b/third_party/libosmium/benchmarks/osmium_benchmark_write_pbf.cpp @@ -4,8 +4,9 @@ */ -#include -#include +#include +#include +#include #include #include @@ -13,16 +14,16 @@ int main(int argc, char* argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " INPUT-FILE OUTPUT-FILE\n"; - exit(1); + std::exit(1); } - std::string input_filename = argv[1]; - std::string output_filename = argv[2]; + std::string input_filename{argv[1]}; + std::string output_filename{argv[2]}; - osmium::io::Reader reader(input_filename); - osmium::io::File output_file(output_filename, "pbf"); + osmium::io::Reader reader{input_filename}; + osmium::io::File output_file{output_filename, "pbf"}; osmium::io::Header header; - osmium::io::Writer writer(output_file, header, osmium::io::overwrite::allow); + osmium::io::Writer writer{output_file, header, osmium::io::overwrite::allow}; while (osmium::memory::Buffer buffer = reader.read()) { writer(std::move(buffer)); diff --git a/third_party/libosmium/build-appveyor.bat b/third_party/libosmium/build-appveyor.bat new file mode 100644 index 00000000000..27d387c33a3 --- /dev/null +++ b/third_party/libosmium/build-appveyor.bat @@ -0,0 +1,123 @@ +@ECHO OFF +SETLOCAL +SET EL=0 + +ECHO ~~~~~~ %~f0 ~~~~~~ + +SET CUSTOM_CMAKE=cmake-3.6.2-win64-x64 +::show all available env vars +SET +ECHO cmake on AppVeyor +cmake -version + +ECHO activating VS cmd prompt && CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +SET lodir=%CD% +SET PATH=%lodir%\%CUSTOM_CMAKE%\bin;%PATH% +SET LODEPSDIR=%lodir%\libosmium-deps +SET PROJ_LIB=%LODEPSDIR%\proj\share +SET GDAL_DATA=%LODEPSDIR%\gdal\data +::gdal.dll +SET PATH=%LODEPSDIR%\gdal\lib;%PATH% +::geos.dll +SET PATH=%LODEPSDIR%\geos\lib;%PATH% +::libtiff.dll +SET PATH=%LODEPSDIR%\libtiff\lib;%PATH% +::jpeg.dll +SET PATH=%LODEPSDIR%\jpeg\lib;%PATH% +::libexpat.dll +SET PATH=%LODEPSDIR%\expat\lib;%PATH% +::zlibwapi.dll +SET PATH=%LODEPSDIR%\zlib\lib;%PATH% +::convert backslashes in bzip2 path to forward slashes +::cmake cannot find it otherwise +SET LIBBZIP2=%LODEPSDIR%\bzip2\lib\libbz2.lib +SET LIBBZIP2=%LIBBZIP2:\=/% + +IF NOT EXIST cm.7z ECHO downloading cmake %CUSTOM_CMAKE% ... && powershell Invoke-WebRequest https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/%CUSTOM_CMAKE%.7z -OutFile cm.7z +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +IF NOT EXIST lodeps.7z ECHO downloading binary dependencies... && powershell Invoke-WebRequest https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/libosmium-deps-win-14.0-x64.7z -OutFile lodeps.7z +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +IF NOT EXIST %CUSTOM_CMAKE% ECHO extracting cmake... && 7z x cm.7z | %windir%\system32\find "ing archive" +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +IF NOT EXIST %LODEPSDIR% ECHO extracting binary dependencies... && 7z x lodeps.7z | %windir%\system32\find "ing archive" +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ECHO %LODEPSDIR% +DIR %LODEPSDIR% +::TREE %LODEPSDIR% + +::powershell (Get-ChildItem $env:LODEPSDIR\boost\lib -Filter *boost*.dll)[0].BaseName.split('_')[-1] +FOR /F "tokens=1 usebackq" %%i in (`powershell ^(Get-ChildItem %LODEPSDIR%\boost\lib -Filter *boost*.dll^)[0].BaseName.split^('_'^)[-1]`) DO SET BOOST_VERSION=%%i +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ECHO BOOST_VERSION^: %BOOST_VERSION% + +ECHO our own cmake +cmake -version + +CD %lodir%\.. + +IF NOT EXIST osm-testdata ECHO cloning osm-testdata && git clone --depth 1 https://github.com/osmcode/osm-testdata.git +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +CD osm-testdata +git fetch +IF %ERRORLEVEL% NEQ 0 GOTO ERROR +git pull +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +CD %lodir% +IF EXIST build ECHO deleting build dir... && RD /Q /S build +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +MKDIR build +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +CD build +ECHO config^: %config% + +::This will produce lots of LNK4099 warnings which can be ignored. +::Unfortunately they can't be disabled, see +::http://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings +SET CMAKE_CMD=cmake .. ^ +-LA -G "Visual Studio 14 Win64" ^ +-DOsmium_DEBUG=TRUE ^ +-DCMAKE_BUILD_TYPE=%config% ^ +-DBUILD_HEADERS=OFF ^ +-DBOOST_ROOT=%LODEPSDIR%\boost ^ +-DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib ^ +-DBZIP2_LIBRARY_RELEASE=%LIBBZIP2% ^ +-DCMAKE_PREFIX_PATH=%LODEPSDIR%\zlib;%LODEPSDIR%\expat;%LODEPSDIR%\bzip2;%LODEPSDIR%\geos;%LODEPSDIR%\gdal;%LODEPSDIR%\proj;%LODEPSDIR%\sparsehash;%LODEPSDIR%\wingetopt + +ECHO calling^: %CMAKE_CMD% +%CMAKE_CMD% +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +msbuild libosmium.sln ^ +/p:Configuration=%config% ^ +/toolsversion:14.0 ^ +/p:Platform=x64 ^ +/p:PlatformToolset=v140 +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +ctest --output-on-failure ^ +-C %config% ^ +-E testdata-overview +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + + +GOTO DONE + +:ERROR +ECHO ~~~~~~ ERROR %~f0 ~~~~~~ +SET EL=%ERRORLEVEL% + +:DONE +IF %EL% NEQ 0 ECHO. && ECHO !!! ERRORLEVEL^: %EL% !!! && ECHO. +ECHO ~~~~~~ DONE %~f0 ~~~~~~ + +EXIT /b %EL% diff --git a/third_party/libosmium/build-local.bat b/third_party/libosmium/build-local.bat new file mode 100644 index 00000000000..202d0b2fa68 --- /dev/null +++ b/third_party/libosmium/build-local.bat @@ -0,0 +1,43 @@ +@ECHO OFF +SETLOCAL +SET EL=0 + +ECHO ~~~~~~ %~f0 ~~~~~~ + +ECHO. +ECHO build-local ["config=Dev"] +ECHO default config^: RelWithDebInfo +ECHO. + +SET platform=x64 +SET config=RelWithDebInfo + +:: OVERRIDE PARAMETERS >>>>>>>> +:NEXT-ARG + +IF '%1'=='' GOTO ARGS-DONE +ECHO setting %1 +SET %1 +SHIFT +GOTO NEXT-ARG + +:ARGS-DONE +::<<<<< OVERRIDE PARAMETERS + +WHERE 7z +IF %ERRORLEVEL% NEQ 0 ECHO 7zip not on PATH && GOTO ERROR + +CALL build-appveyor.bat +IF %ERRORLEVEL% NEQ 0 GOTO ERROR + +GOTO DONE + +:ERROR +ECHO ~~~~~~ ERROR %~f0 ~~~~~~ +SET EL=%ERRORLEVEL% + +:DONE +IF %EL% NEQ 0 ECHO. && ECHO !!! ERRORLEVEL^: %EL% !!! && ECHO. +ECHO ~~~~~~ DONE %~f0 ~~~~~~ + +EXIT /b %EL% diff --git a/third_party/libosmium/cmake/iwyu.sh b/third_party/libosmium/cmake/iwyu.sh index ceea106c37b..b386159c444 100755 --- a/third_party/libosmium/cmake/iwyu.sh +++ b/third_party/libosmium/cmake/iwyu.sh @@ -6,6 +6,11 @@ # TODO: This script should be integrated with cmake in some way... # +# If these are set, the wrong compiler is used by iwyu and there will be +# errors about missing includes. +unset CC +unset CXX + cmdline="iwyu -Xiwyu --mapping_file=osmium.imp -std=c++11 -I include" log=build/iwyu.log diff --git a/third_party/libosmium/examples/CMakeLists.txt b/third_party/libosmium/examples/CMakeLists.txt index a04a843f247..b47cfdcf7b1 100644 --- a/third_party/libosmium/examples/CMakeLists.txt +++ b/third_party/libosmium/examples/CMakeLists.txt @@ -12,13 +12,17 @@ set(EXAMPLES area_test convert count - create_node_cache debug filter_discussions index + location_cache_create + location_cache_use + pub_names read + read_with_progress + road_length serdump - use_node_cache + tiles CACHE STRING "Example programs" ) @@ -28,7 +32,7 @@ set(EXAMPLES # Examples depending on wingetopt # #----------------------------------------------------------------------------- -set(GETOPT_EXAMPLES area_test convert serdump) +set(GETOPT_EXAMPLES area_test convert index serdump) if(NOT GETOPT_MISSING) foreach(example ${GETOPT_EXAMPLES}) list(APPEND EXAMPLE_LIBS_${example} ${GETOPT_LIBRARY}) @@ -42,36 +46,6 @@ else() endif() -#----------------------------------------------------------------------------- -# -# Examples depending on SparseHash -# -#----------------------------------------------------------------------------- -if(NOT SPARSEHASH_FOUND) - list(REMOVE_ITEM EXAMPLES area_test) - message(STATUS "Configuring examples - Skipping examples because Google SparseHash not found:") - message(STATUS " - osmium_area_test") -endif() - - -#----------------------------------------------------------------------------- -# -# Examples depending on Boost Program Options -# -#----------------------------------------------------------------------------- -unset(Boost_LIBRARIES) -unset(Boost_FOUND) -find_package(Boost 1.38 COMPONENTS program_options) - -if(Boost_PROGRAM_OPTIONS_FOUND) - list(APPEND EXAMPLE_LIBS_index ${Boost_PROGRAM_OPTIONS_LIBRARY}) -else() - list(REMOVE_ITEM EXAMPLES index) - message(STATUS "Configuring examples - Skipping examples because Boost program_options not found:") - message(STATUS " - osmium_index") -endif() - - #----------------------------------------------------------------------------- # # Configure examples diff --git a/third_party/libosmium/examples/README.md b/third_party/libosmium/examples/README.md new file mode 100644 index 00000000000..e2910329252 --- /dev/null +++ b/third_party/libosmium/examples/README.md @@ -0,0 +1,32 @@ + +# Osmium example programs + +The programs in this directory are intended as examples for developers. They +contain extensive comments explaining what's going on. Note that the examples +only cover a small part of what Osmium can do, you should also read the +documentation and API documentation. + +All programs can be run without arguments and they will tell you how to run +them. + +## Very simple examples + +* `osmium_read` +* `osmium_count` +* `osmium_debug` +* `osmium_tiles` + +## Still reasonably simple examples + +* `osmium_filter_discussions` +* `osmium_convert` + +## More advanced examples + +* `osmium_area_test` + +## License + +The code in these example files is released into the Public Domain. Feel free +to copy the code and build on it. + diff --git a/third_party/libosmium/examples/osmium_area_test.cpp b/third_party/libosmium/examples/osmium_area_test.cpp index e9b7a18add1..030337454cf 100644 --- a/third_party/libosmium/examples/osmium_area_test.cpp +++ b/third_party/libosmium/examples/osmium_area_test.cpp @@ -1,48 +1,81 @@ /* - This is an example tool that creates multipolygons from OSM data - and dumps them to stdout. - + EXAMPLE osmium_area_test + + Create multipolygons from OSM data and dump them to stdout in one of two + formats: WKT or using the built-in Dump format. + + DEMONSTRATES USE OF: + * file input + * location indexes and the NodeLocationsForWays handler + * the MultipolygonCollector and Assembler to assemble areas (multipolygons) + * your own handler that works with areas (multipolygons) + * the WKTFactory to write geometries in WKT format + * the Dump handler + * the DynamicHandler + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + * osmium_debug + + LICENSE The code in this example file is released into the Public Domain. */ -#include - -#include +#include // for std::exit +#include // for getopt_long +#include // for std::cout, std::cerr +// For assembling multipolygons #include #include + +// For the DynamicHandler class #include + +// For the WKT factory #include + +// For the Dump handler #include + +// For the NodeLocationForWays handler #include -#include -#include + +// Allow any format of input files (XML, PBF, ...) #include + +// For osmium::apply() #include -typedef osmium::index::map::Dummy index_neg_type; -typedef osmium::index::map::SparseMemArray index_pos_type; -typedef osmium::handler::NodeLocationsForWays location_handler_type; +// For the location index. There are different types of indexes available. +// This will work for small and medium sized input files. +#include -class WKTDump : public osmium::handler::Handler { +// The type of index used. This must match the include file above +using index_type = osmium::index::map::SparseMemArray; - osmium::geom::WKTFactory<> m_factory ; +// The location handler always depends on the index type +using location_handler_type = osmium::handler::NodeLocationsForWays; - std::ostream& m_out; +// This handler writes all area geometries out in WKT (Well Known Text) format. +class WKTDump : public osmium::handler::Handler { -public: + // This factory is used to create a geometry in WKT format from OSM + // objects. The template parameter is empty here, because we output WGS84 + // coordinates, but could be used for a projection. + osmium::geom::WKTFactory<> m_factory; - WKTDump(std::ostream& out) : - m_out(out) { - } +public: + // This callback is called by osmium::apply for each area in the data. void area(const osmium::Area& area) { try { - m_out << m_factory.create_multipolygon(area) << "\n"; - } catch (osmium::geometry_error& e) { - m_out << "GEOMETRY ERROR: " << e.what() << "\n"; + std::cout << m_factory.create_multipolygon(area) << "\n"; + } catch (const osmium::geometry_error& e) { + std::cout << "GEOMETRY ERROR: " << e.what() << "\n"; } } @@ -65,8 +98,13 @@ int main(int argc, char* argv[]) { {0, 0, 0, 0} }; + // Initialize an empty DynamicHandler. Later it will be associated + // with one of the handlers. You can think of the DynamicHandler as + // a kind of "variant handler" or a "pointer handler" pointing to the + // real handler. osmium::handler::DynamicHandler handler; + // Read options from command line. while (true) { int c = getopt_long(argc, argv, "hwo", long_options, 0); if (c == -1) { @@ -76,54 +114,81 @@ int main(int argc, char* argv[]) { switch (c) { case 'h': print_help(); - exit(0); + std::exit(0); case 'w': - handler.set(std::cout); + handler.set(); break; case 'o': handler.set(std::cout); break; default: - exit(1); + std::exit(1); } } int remaining_args = argc - optind; if (remaining_args != 1) { std::cerr << "Usage: " << argv[0] << " [OPTIONS] OSMFILE\n"; - exit(1); + std::exit(1); } - osmium::io::File infile(argv[optind]); + osmium::io::File input_file{argv[optind]}; + // Configuration for the multipolygon assembler. Here the default settings + // are used, but you could change multiple settings. osmium::area::Assembler::config_type assembler_config; - osmium::area::MultipolygonCollector collector(assembler_config); + // Initialize the MultipolygonCollector. Its job is to collect all + // relations and member ways needed for each area. It then calls an + // instance of the osmium::area::Assembler class (with the given config) + // to actually assemble one area. + osmium::area::MultipolygonCollector collector{assembler_config}; + + // We read the input file twice. In the first pass, only relations are + // read and fed into the multipolygon collector. std::cerr << "Pass 1...\n"; - osmium::io::Reader reader1(infile, osmium::osm_entity_bits::relation); + osmium::io::Reader reader1{input_file, osmium::osm_entity_bits::relation}; collector.read_relations(reader1); reader1.close(); std::cerr << "Pass 1 done\n"; + // Output the amount of main memory used so far. All multipolygon relations + // are in memory now. std::cerr << "Memory:\n"; collector.used_memory(); - index_pos_type index_pos; - index_neg_type index_neg; - location_handler_type location_handler(index_pos, index_neg); - location_handler.ignore_errors(); // XXX + // The index storing all node locations. + index_type index; + + // The handler that stores all node locations in the index and adds them + // to the ways. + location_handler_type location_handler{index}; + + // If a location is not available in the index, we ignore it. It might + // not be needed (if it is not part of a multipolygon relation), so why + // create an error? + location_handler.ignore_errors(); + // On the second pass we read all objects and run them first through the + // node location handler and then the multipolygon collector. The collector + // will put the areas it has created into the "buffer" which are then + // fed through our "handler". std::cerr << "Pass 2...\n"; - osmium::io::Reader reader2(infile); + osmium::io::Reader reader2{input_file}; osmium::apply(reader2, location_handler, collector.handler([&handler](osmium::memory::Buffer&& buffer) { osmium::apply(buffer, handler); })); reader2.close(); std::cerr << "Pass 2 done\n"; + // Output the amount of main memory used so far. All complete multipolygon + // relations have been cleaned up. std::cerr << "Memory:\n"; collector.used_memory(); + // If there were multipolgyon relations in the input, but some of their + // members are not in the input file (which often happens for extracts) + // this will write the IDs of the incomplete relations to stderr. std::vector incomplete_relations = collector.get_incomplete_relations(); if (!incomplete_relations.empty()) { std::cerr << "Warning! Some member ways missing for these multipolygon relations:"; diff --git a/third_party/libosmium/examples/osmium_convert.cpp b/third_party/libosmium/examples/osmium_convert.cpp index 4f2ba33185f..48a0823db82 100644 --- a/third_party/libosmium/examples/osmium_convert.cpp +++ b/third_party/libosmium/examples/osmium_convert.cpp @@ -1,16 +1,32 @@ /* + EXAMPLE osmium_convert + Convert OSM files from one format into another. + DEMONSTRATES USE OF: + * file input and output + * file types + * Osmium buffers + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + + LICENSE The code in this example file is released into the Public Domain. */ -#include -#include +#include // for std::exit +#include // for std::exception +#include // for getopt_long +#include // for std::cout, std::cerr +#include // for std::string +// Allow any format of input files (XML, PBF, ...) #include +// Allow any format of output files (XML, PBF, ...) #include void print_help() { @@ -43,9 +59,13 @@ int main(int argc, char* argv[]) { {0, 0, 0, 0} }; + // Input and output format are empty by default. Later this will mean that + // the format should be taken from the input and output file suffix, + // respectively. std::string input_format; std::string output_format; + // Read options from command line. while (true) { int c = getopt_long(argc, argv, "dhf:t:", long_options, 0); if (c == -1) { @@ -55,7 +75,7 @@ int main(int argc, char* argv[]) { switch (c) { case 'h': print_help(); - exit(0); + std::exit(0); case 'f': input_format = optarg; break; @@ -63,49 +83,73 @@ int main(int argc, char* argv[]) { output_format = optarg; break; default: - exit(1); + std::exit(1); } } - std::string input; - std::string output; int remaining_args = argc - optind; if (remaining_args > 2) { - std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]" << std::endl; - exit(1); - } else if (remaining_args == 2) { - input = argv[optind]; - output = argv[optind+1]; - } else if (remaining_args == 1) { - input = argv[optind]; + std::cerr << "Usage: " << argv[0] << " [OPTIONS] [INFILE [OUTFILE]]\n"; + std::exit(1); } - osmium::io::File infile(input, input_format); + // Get input file name from command line. + std::string input_file_name; + if (remaining_args >= 1) { + input_file_name = argv[optind]; + } - osmium::io::File outfile(output, output_format); + // Get output file name from command line. + std::string output_file_name; + if (remaining_args == 2) { + output_file_name = argv[optind+1]; + } - if (infile.has_multiple_object_versions() && !outfile.has_multiple_object_versions()) { + // This declares the input and output files using either the suffix of + // the file names or the format in the 2nd argument. It does not yet open + // the files. + osmium::io::File input_file{input_file_name, input_format}; + osmium::io::File output_file{output_file_name, output_format}; + + // Input and output files can be OSM data files (without history) or + // OSM history files. History files are detected if they use the '.osh' + // file suffix. + if ( input_file.has_multiple_object_versions() && + !output_file.has_multiple_object_versions()) { std::cerr << "Warning! You are converting from an OSM file with (potentially) several versions of the same object to one that is not marked as such.\n"; } - int exit_code = 0; - try { - osmium::io::Reader reader(infile); + // Initialize Reader + osmium::io::Reader reader{input_file}; + + // Get header from input file and change the "generator" setting to + // outselves. osmium::io::Header header = reader.header(); header.set("generator", "osmium_convert"); - osmium::io::Writer writer(outfile, header, osmium::io::overwrite::allow); + // Initialize Writer using the header from above and tell it that it + // is allowed to overwrite a possibly existing file. + osmium::io::Writer writer(output_file, header, osmium::io::overwrite::allow); + + // Copy the contents from the input to the output file one buffer at + // a time. This is much easier and faster than copying each object + // in the file. Buffers are moved around, so there is no cost for + // copying in memory. while (osmium::memory::Buffer buffer = reader.read()) { writer(std::move(buffer)); } + + // Explicitly close the writer and reader. Will throw an exception if + // there is a problem. If you wait for the destructor to close the writer + // and reader, you will not notice the problem, because destructors must + // not throw. writer.close(); reader.close(); - } catch (std::exception& e) { + } catch (const std::exception& e) { + // All exceptions used by the Osmium library derive from std::exception. std::cerr << e.what() << "\n"; - exit_code = 1; + std::exit(1); } - - return exit_code; } diff --git a/third_party/libosmium/examples/osmium_count.cpp b/third_party/libosmium/examples/osmium_count.cpp index baea153b88f..7de1b6b7c6e 100644 --- a/third_party/libosmium/examples/osmium_count.cpp +++ b/third_party/libosmium/examples/osmium_count.cpp @@ -1,56 +1,95 @@ /* - This is a small tool that counts the number of nodes, ways, and relations in - the input file. + EXAMPLE osmium_count + Counts the number of nodes, ways, and relations in the input file. + + DEMONSTRATES USE OF: + * OSM file input + * your own handler + * the memory usage utility class + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + + LICENSE The code in this example file is released into the Public Domain. */ -#include -#include +#include // for std::uint64_t +#include // for std::exit +#include // for std::cout, std::cerr +// Allow any format of input files (XML, PBF, ...) #include + +// We want to use the handler interface #include + +// Utility class gives us access to memory usage information +#include + +// For osmium::apply() #include +// Handler derive from the osmium::handler::Handler base class. Usually you +// overwrite functions node(), way(), and relation(). Other functions are +// available, too. Read the API documentation for details. struct CountHandler : public osmium::handler::Handler { - uint64_t nodes = 0; - uint64_t ways = 0; - uint64_t relations = 0; + std::uint64_t nodes = 0; + std::uint64_t ways = 0; + std::uint64_t relations = 0; - void node(osmium::Node&) { + // This callback is called by osmium::apply for each node in the data. + void node(const osmium::Node&) noexcept { ++nodes; } - void way(osmium::Way&) { + // This callback is called by osmium::apply for each way in the data. + void way(const osmium::Way&) noexcept { ++ways; } - void relation(osmium::Relation&) { + // This callback is called by osmium::apply for each relation in the data. + void relation(const osmium::Relation&) noexcept { ++relations; } -}; +}; // struct CountHandler int main(int argc, char* argv[]) { - if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - exit(1); + std::exit(1); } - osmium::io::File infile(argv[1]); - osmium::io::Reader reader(infile); + // The Reader is initialized here with an osmium::io::File, but could + // also be directly initialized with a file name. + osmium::io::File input_file{argv[1]}; + osmium::io::Reader reader{input_file}; + // Create an instance of our own CountHandler and push the data from the + // input file through it. CountHandler handler; osmium::apply(reader, handler); + + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. reader.close(); std::cout << "Nodes: " << handler.nodes << "\n"; std::cout << "Ways: " << handler.ways << "\n"; std::cout << "Relations: " << handler.relations << "\n"; + + // Because of the huge amount of OSM data, some Osmium-based programs + // (though not this one) can use huge amounts of data. So checking actual + // memore usage is often useful and can be done easily with this class. + // (Currently only works on Linux, not OSX and Windows.) + osmium::MemoryUsage memory; + + std::cout << "\nMemory used: " << memory.peak() << " MBytes\n"; } diff --git a/third_party/libosmium/examples/osmium_create_node_cache.cpp b/third_party/libosmium/examples/osmium_create_node_cache.cpp deleted file mode 100644 index 359fa1978a8..00000000000 --- a/third_party/libosmium/examples/osmium_create_node_cache.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - - This reads an OSM file and writes out the node locations to a cache - file. - - The code in this example file is released into the Public Domain. - -*/ - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -typedef osmium::index::map::Dummy index_neg_type; -//typedef osmium::index::map::DenseMmapArray index_pos_type; -typedef osmium::index::map::DenseFileArray index_pos_type; - -typedef osmium::handler::NodeLocationsForWays location_handler_type; - -int main(int argc, char* argv[]) { - if (argc != 3) { - std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n"; - return 1; - } - - std::string input_filename(argv[1]); - osmium::io::Reader reader(input_filename, osmium::osm_entity_bits::node); - - int fd = open(argv[2], O_RDWR | O_CREAT, 0666); - if (fd == -1) { - std::cerr << "Can not open node cache file '" << argv[2] << "': " << strerror(errno) << "\n"; - return 1; - } - - index_pos_type index_pos {fd}; - index_neg_type index_neg; - location_handler_type location_handler(index_pos, index_neg); - location_handler.ignore_errors(); - - osmium::apply(reader, location_handler); - reader.close(); - - return 0; -} - diff --git a/third_party/libosmium/examples/osmium_debug.cpp b/third_party/libosmium/examples/osmium_debug.cpp index 365fc729754..813349190d2 100644 --- a/third_party/libosmium/examples/osmium_debug.cpp +++ b/third_party/libosmium/examples/osmium_debug.cpp @@ -1,27 +1,46 @@ /* - This is a small tool to dump the contents of the input file. + EXAMPLE osmium_debug + Dump the contents of the input file in a debug format. + + DEMONSTRATES USE OF: + * file input reading only some types + * the dump handler + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + + LICENSE The code in this example file is released into the Public Domain. */ -#include +#include // for std::exit +#include // for std::cout, std::cerr +#include // for std::string +// The Dump handler #include + +// Allow any format of input files (XML, PBF, ...) #include int main(int argc, char* argv[]) { + // Speed up output (not Osmium-specific) std::ios_base::sync_with_stdio(false); if (argc < 2 || argc > 3) { std::cerr << "Usage: " << argv[0] << " OSMFILE [TYPES]\n"; std::cerr << "TYPES can be any combination of 'n', 'w', 'r', and 'c' to indicate what types of OSM entities you want (default: all).\n"; - exit(1); + std::exit(1); } + // Default is all entity types: nodes, ways, relations, and changesets osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all; + // Get entity types from command line if there is a 2nd argument. if (argc == 3) { read_types = osmium::osm_entity_bits::nothing; std::string types = argv[2]; @@ -31,20 +50,27 @@ int main(int argc, char* argv[]) { if (types.find('c') != std::string::npos) read_types |= osmium::osm_entity_bits::changeset; } - osmium::io::Reader reader(argv[1], read_types); - osmium::io::Header header = reader.header(); + // Initialize Reader with file name and the types of entities we want to + // read. + osmium::io::Reader reader{argv[1], read_types}; + // The file header can contain metadata such as the program that generated + // the file and the bounding box of the data. + osmium::io::Header header = reader.header(); std::cout << "HEADER:\n generator=" << header.get("generator") << "\n"; - for (auto& bbox : header.boxes()) { + for (const auto& bbox : header.boxes()) { std::cout << " bbox=" << bbox << "\n"; } - osmium::handler::Dump dump(std::cout); - while (osmium::memory::Buffer buffer = reader.read()) { - osmium::apply(buffer, dump); - } + // Initialize Dump handler. + osmium::handler::Dump dump{std::cout}; + + // Read from input and send everything to Dump handler. + osmium::apply(reader, dump); + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. reader.close(); } diff --git a/third_party/libosmium/examples/osmium_filter_discussions.cpp b/third_party/libosmium/examples/osmium_filter_discussions.cpp index bba25b75284..633498199c8 100644 --- a/third_party/libosmium/examples/osmium_filter_discussions.cpp +++ b/third_party/libosmium/examples/osmium_filter_discussions.cpp @@ -1,53 +1,73 @@ /* + EXAMPLE osmium_filter_discussions + Read OSM changesets with discussions from a changeset dump like the one you get from http://planet.osm.org/planet/discussions-latest.osm.bz2 and write out only those changesets which have discussions (ie comments). + DEMONSTRATES USE OF: + * file input and output + * setting file formats using the osmium::io::File class + * OSM file headers + * input and output iterators + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + + LICENSE The code in this example file is released into the Public Domain. */ #include // for std::copy_if -#include // for std::cout, std::cerr +#include // for std::exit +#include // for std::cout, std::cerr -// we want to read OSM files in XML format -// (other formats don't support full changesets, so only XML is needed here) +// We want to read OSM files in XML format +// (other formats don't support full changesets, so only XML is needed here). #include -#include -// we want to write OSM files in XML format +// We want to write OSM files in XML format. #include + +// We want to use input and output iterators for easy integration with the +// algorithms of the standard library. +#include #include -// we want to support any compressioon (.gz2 and .bz2) +// We want to support any compression (none, gzip, and bzip2). #include int main(int argc, char* argv[]) { if (argc != 3) { std::cout << "Usage: " << argv[0] << " INFILE OUTFILE\n"; - exit(1); + std::exit(1); } - // The input file, deduce file format from file suffix - osmium::io::File infile(argv[1]); + // The input file, deduce file format from file suffix. + osmium::io::File input_file{argv[1]}; - // The output file, force class XML OSM file format - osmium::io::File outfile(argv[2], "osm"); + // The output file, force XML OSM file format. + osmium::io::File output_file{argv[2], "osm"}; // Initialize Reader for the input file. // Read only changesets (will ignore nodes, ways, and // relations if there are any). - osmium::io::Reader reader(infile, osmium::osm_entity_bits::changeset); + osmium::io::Reader reader{input_file, osmium::osm_entity_bits::changeset}; - // Get the header from the input file + // Get the header from the input file. osmium::io::Header header = reader.header(); + // Set the "generator" on the header to ourselves. + header.set("generator", "osmium_filter_discussions"); + // Initialize writer for the output file. Use the header from the input // file for the output file. This will copy over some header information. // The last parameter will tell the writer that it is allowed to overwrite // an existing file. Without it, it will refuse to do so. - osmium::io::Writer writer(outfile, header, osmium::io::overwrite::allow); + osmium::io::Writer writer(output_file, header, osmium::io::overwrite::allow); // Create range of input iterators that will iterator over all changesets // delivered from input file through the "reader". diff --git a/third_party/libosmium/examples/osmium_index.cpp b/third_party/libosmium/examples/osmium_index.cpp index b61214097b0..478b6c6b605 100644 --- a/third_party/libosmium/examples/osmium_index.cpp +++ b/third_party/libosmium/examples/osmium_index.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include @@ -31,7 +31,7 @@ class IndexSearch { void dump_dense() { dense_index_type index(m_fd); - for (size_t i = 0; i < index.size(); ++i) { + for (std::size_t i = 0; i < index.size(); ++i) { if (index.get(i) != TValue()) { std::cout << i << " " << index.get(i) << "\n"; } @@ -51,9 +51,9 @@ class IndexSearch { try { TValue value = index.get(key); - std::cout << key << " " << value << std::endl; + std::cout << key << " " << value << "\n"; } catch (...) { - std::cout << key << " not found" << std::endl; + std::cout << key << " not found\n"; return false; } @@ -69,7 +69,7 @@ class IndexSearch { return lhs.first < rhs.first; }); if (positions.first == positions.second) { - std::cout << key << " not found" << std::endl; + std::cout << key << " not found\n"; return false; } @@ -103,7 +103,7 @@ class IndexSearch { } } - bool search(std::vector keys) { + bool search(const std::vector& keys) { bool found_all = true; for (const auto key : keys) { @@ -124,82 +124,105 @@ enum return_code : int { fatal = 3 }; -namespace po = boost::program_options; - class Options { - po::variables_map vm; + std::vector m_ids; + std::string m_type; + std::string m_filename; + bool m_dump = false; + bool m_array_format = false; + bool m_list_format = false; + + void print_help() { + std::cout << "Usage: osmium_index [OPTIONS]\n\n" + << "-h, --help Print this help message\n" + << "-a, --array=FILE Read given index file in array format\n" + << "-l, --list=FILE Read given index file in list format\n" + << "-d, --dump Dump contents of index file to STDOUT\n" + << "-s, --search=ID Search for given id (Option can appear multiple times)\n" + << "-t, --type=TYPE Type of value ('location' or 'offset')\n" + ; + } public: Options(int argc, char* argv[]) { - try { - po::options_description desc("Allowed options"); - desc.add_options() - ("help,h", "Print this help message") - ("array,a", po::value(), "Read given index file in array format") - ("list,l", po::value(), "Read given index file in list format") - ("dump,d", "Dump contents of index file to STDOUT") - ("search,s", po::value>(), "Search for given id (Option can appear multiple times)") - ("type,t", po::value(), "Type of value ('location' or 'offset')") - ; - - po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); - - if (vm.count("help")) { - std::cout << desc << "\n"; - exit(return_code::okay); + static struct option long_options[] = { + {"array", required_argument, 0, 'a'}, + {"dump", no_argument, 0, 'd'}, + {"help", no_argument, 0, 'h'}, + {"list", required_argument, 0, 'l'}, + {"search", required_argument, 0, 's'}, + {"type", required_argument, 0, 't'}, + {0, 0, 0, 0} + }; + + while (true) { + int c = getopt_long(argc, argv, "a:dhl:s:t:", long_options, 0); + if (c == -1) { + break; } - if (vm.count("array") && vm.count("list")) { - std::cerr << "Only option --array or --list allowed." << std::endl; - exit(return_code::fatal); - } - - if (!vm.count("array") && !vm.count("list")) { - std::cerr << "Need one of option --array or --list." << std::endl; - exit(return_code::fatal); + switch (c) { + case 'a': + m_array_format = true; + m_filename = optarg; + break; + case 'd': + m_dump = true; + break; + case 'h': + print_help(); + std::exit(return_code::okay); + case 'l': + m_list_format = true; + m_filename = optarg; + break; + case 's': + m_ids.push_back(std::atoll(optarg)); + break; + case 't': + m_type = optarg; + if (m_type != "location" && m_type != "offset") { + std::cerr << "Unknown type '" << m_type << "'. Must be 'location' or 'offset'.\n"; + std::exit(return_code::fatal); + } + break; + default: + std::exit(return_code::fatal); } + } - if (!vm.count("type")) { - std::cerr << "Need --type argument." << std::endl; - exit(return_code::fatal); - } + if (m_array_format == m_list_format) { + std::cerr << "Need option --array or --list, but not both\n"; + std::exit(return_code::fatal); + } - const std::string& type = vm["type"].as(); - if (type != "location" && type != "offset") { - std::cerr << "Unknown type '" << type << "'. Must be 'location' or 'offset'." << std::endl; - exit(return_code::fatal); - } - } catch (boost::program_options::error& e) { - std::cerr << "Error parsing command line: " << e.what() << std::endl; - exit(return_code::fatal); + if (m_type.empty()) { + std::cerr << "Need --type argument.\n"; + std::exit(return_code::fatal); } + } - const std::string& filename() const { - if (vm.count("array")) { - return vm["array"].as(); - } else { - return vm["list"].as(); - } + const std::string& filename() const noexcept { + return m_filename; } - bool dense_format() const { - return vm.count("array") != 0; + bool dense_format() const noexcept { + return m_array_format; } - bool do_dump() const { - return vm.count("dump") != 0; + bool do_dump() const noexcept { + return m_dump; } - std::vector search_keys() const { - return vm["search"].as>(); + const std::vector& search_keys() const noexcept { + return m_ids; } - bool type_is(const char* type) const { - return vm["type"].as() == type; + bool type_is(const char* type) const noexcept { + return m_type == type; } }; // class Options @@ -232,6 +255,6 @@ int main(int argc, char* argv[]) { } } - exit(result_okay ? return_code::okay : return_code::not_found); + std::exit(result_okay ? return_code::okay : return_code::not_found); } diff --git a/third_party/libosmium/examples/osmium_location_cache_create.cpp b/third_party/libosmium/examples/osmium_location_cache_create.cpp new file mode 100644 index 00000000000..9de41d12f4d --- /dev/null +++ b/third_party/libosmium/examples/osmium_location_cache_create.cpp @@ -0,0 +1,87 @@ +/* + + EXAMPLE osmium_location_cache_create + + Reads nodes from an OSM file and writes out their locations to a cache + file. The cache file can then be read with osmium_location_cache_use. + + Warning: The locations cache file will get huge (>32GB) if you are using + the DenseFileArray index even if the input file is small, because + it depends on the *largest* node ID, not the number of nodes. + + DEMONSTRATES USE OF: + * file input + * location indexes and the NodeLocationsForWays handler + * location indexes on disk + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + * osmium_road_length + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for errno +#include // for std::exit +#include // for strerror +#include // for open +#include // for std::cout, std::cerr +#include // for std::string +#include // for open +#include // for open + +// Allow any format of input files (XML, PBF, ...) +#include + +// For the location index. There are different types of index implementation +// available. These implementations put the index on disk. See below. +#include +#include + +// For the NodeLocationForWays handler +#include + +// For osmium::apply() +#include + +// Chose one of these two. "sparse" is best used for small and medium extracts, +// the "dense" index for large extracts or the whole planet. +using index_type = osmium::index::map::SparseFileArray; +//using index_type = osmium::index::map::DenseFileArray; + +// The location handler always depends on the index type +using location_handler_type = osmium::handler::NodeLocationsForWays; + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n"; + std::exit(1); + } + + const std::string input_filename{argv[1]}; + const std::string cache_filename{argv[2]}; + + // Construct Reader reading only nodes + osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::node}; + + // Initialize location index on disk creating a new file. + const int fd = open(cache_filename.c_str(), O_RDWR | O_CREAT, 0666); + if (fd == -1) { + std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; + std::exit(1); + } + index_type index{fd}; + + // The handler that stores all node locations in the index. + location_handler_type location_handler{index}; + + // Feed all nodes through the location handler. + osmium::apply(reader, location_handler); + + // Explicitly close input so we get notified of any errors. + reader.close(); +} + diff --git a/third_party/libosmium/examples/osmium_location_cache_use.cpp b/third_party/libosmium/examples/osmium_location_cache_use.cpp new file mode 100644 index 00000000000..f5db0becf27 --- /dev/null +++ b/third_party/libosmium/examples/osmium_location_cache_use.cpp @@ -0,0 +1,101 @@ +/* + + EXAMPLE osmium_location_cache_use + + This reads ways from an OSM file and writes out the way node locations + it got from a location cache generated with osmium_location_cache_create. + + Warning: The locations cache file will get huge (>32GB) if you are using + the DenseFileArray index even if the input file is small, because + it depends on the *largest* node ID, not the number of nodes. + + DEMONSTRATES USE OF: + * file input + * location indexes and the NodeLocationsForWays handler + * location indexes on disk + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + * osmium_road_length + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for errno +#include // for std::exit +#include // for strerror +#include // for open +#include // for std::cout, std::cerr +#include // for std::string +#include // for open +#include // for open + +// Allow any format of input files (XML, PBF, ...) +#include + +// For the location index. There are different types of index implementation +// available. These implementations put the index on disk. See below. +#include +#include + +// For the NodeLocationForWays handler +#include + +// For osmium::apply() +#include + +// Chose one of these two. "sparse" is best used for small and medium extracts, +// the "dense" index for large extracts or the whole planet. +using index_type = osmium::index::map::SparseFileArray; +//using index_type = osmium::index::map::DenseFileArray; + +// The location handler always depends on the index type +using location_handler_type = osmium::handler::NodeLocationsForWays; + +// This handler only implements the way() function which prints out the way +// ID and all nodes IDs and locations in those ways. +struct MyHandler : public osmium::handler::Handler { + + void way(const osmium::Way& way) { + std::cout << "way " << way.id() << "\n"; + for (const auto& nr : way.nodes()) { + std::cout << " node " << nr.ref() << " " << nr.location() << "\n"; + } + } + +}; // struct MyHandler + +int main(int argc, char* argv[]) { + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n"; + std::exit(1); + } + + const std::string input_filename{argv[1]}; + const std::string cache_filename{argv[2]}; + + // Construct Reader reading only ways + osmium::io::Reader reader{input_filename, osmium::osm_entity_bits::way}; + + // Initialize location index on disk using an existing file + const int fd = open(cache_filename.c_str(), O_RDWR); + if (fd == -1) { + std::cerr << "Can not open location cache file '" << cache_filename << "': " << std::strerror(errno) << "\n"; + return 1; + } + index_type index{fd}; + + // The handler that adds node locations from the index to the ways. + location_handler_type location_handler{index}; + + // Feed all ways through the location handler and then our own handler. + MyHandler handler; + osmium::apply(reader, location_handler, handler); + + // Explicitly close input so we get notified of any errors. + reader.close(); +} + diff --git a/third_party/libosmium/examples/osmium_pub_names.cpp b/third_party/libosmium/examples/osmium_pub_names.cpp new file mode 100755 index 00000000000..dbc37c3328e --- /dev/null +++ b/third_party/libosmium/examples/osmium_pub_names.cpp @@ -0,0 +1,89 @@ +/* + + EXAMPLE osmium_pub_names + + Show the names and addresses of all pubs found in an OSM file. + + DEMONSTRATES USE OF: + * file input + * your own handler + * access to tags + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for std::exit +#include // for std::strncmp +#include // for std::cout, std::cerr + +// Allow any format of input files (XML, PBF, ...) +#include + +// We want to use the handler interface +#include + +// For osmium::apply() +#include + +class NamesHandler : public osmium::handler::Handler { + + void output_pubs(const osmium::OSMObject& object) { + const osmium::TagList& tags = object.tags(); + if (tags.has_tag("amenity", "pub")) { + + // Print name of the pub if it is set. + const char* name = tags["name"]; + if (name) { + std::cout << name << "\n"; + } else { + std::cout << "pub with unknown name\n"; + } + + // Iterate over all tags finding those which start with "addr:" + // and print them. + for (const osmium::Tag& tag : tags) { + if (!std::strncmp(tag.key(), "addr:", 5)) { + std::cout << " " << tag.key() << ": " << tag.value() << "\n"; + } + } + } + } + +public: + + // Nodes can be tagged amenity=pub. + void node(const osmium::Node& node) { + output_pubs(node); + } + + // Ways can be tagged amenity=pub, too (typically buildings). + void way(const osmium::Way& way) { + output_pubs(way); + } + +}; // class NamesHandler + +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; + std::exit(1); + } + + // Construct the handler defined above + NamesHandler names_handler; + + // Initialize the reader with the filename from the command line and + // tell it to only read nodes and ways. We are ignoring multipolygon + // relations in this simple example. + osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::node | osmium::osm_entity_bits::way}; + + // Apply input data to our own handler + osmium::apply(reader, names_handler); +} + diff --git a/third_party/libosmium/examples/osmium_read.cpp b/third_party/libosmium/examples/osmium_read.cpp index 653600684a1..9f391c874f0 100644 --- a/third_party/libosmium/examples/osmium_read.cpp +++ b/third_party/libosmium/examples/osmium_read.cpp @@ -1,30 +1,42 @@ /* - This is a small tool that reads and discards the contents of the input file. - (Used for timing.) + EXAMPLE osmium_read + Reads and discards the contents of the input file. + (It can be used for timing.) + + DEMONSTRATES USE OF: + * file input + + LICENSE The code in this example file is released into the Public Domain. */ -#include +#include // for std::exit +#include // for std::cerr +// Allow any format of input files (XML, PBF, ...) #include int main(int argc, char* argv[]) { - if (argc != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; - exit(1); + std::exit(1); } - osmium::io::File infile(argv[1]); - osmium::io::Reader reader(infile); + // The Reader is initialized here with an osmium::io::File, but could + // also be directly initialized with a file name. + osmium::io::File input_file{argv[1]}; + osmium::io::Reader reader{input_file}; + // OSM data comes in buffers, read until there are no more. while (osmium::memory::Buffer buffer = reader.read()) { // do nothing } + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. reader.close(); } diff --git a/third_party/libosmium/examples/osmium_read_with_progress.cpp b/third_party/libosmium/examples/osmium_read_with_progress.cpp new file mode 100644 index 00000000000..81437a665df --- /dev/null +++ b/third_party/libosmium/examples/osmium_read_with_progress.cpp @@ -0,0 +1,56 @@ +/* + + EXAMPLE osmium_read_with_progress + + Reads the contents of the input file showing a progress bar. + + DEMONSTRATES USE OF: + * file input + * ProgressBar utility function + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for std::exit +#include // for std::cerr + +// Allow any format of input files (XML, PBF, ...) +#include + +// Get access to isatty utility function and progress bar utility class. +#include +#include + +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; + std::exit(1); + } + + // The Reader is initialized here with an osmium::io::File, but could + // also be directly initialized with a file name. + osmium::io::File input_file{argv[1]}; + osmium::io::Reader reader{input_file}; + + // Initialize progress bar, enable it only if STDERR is a TTY. + osmium::ProgressBar progress{reader.file_size(), osmium::util::isatty(2)}; + + // OSM data comes in buffers, read until there are no more. + while (osmium::memory::Buffer buffer = reader.read()) { + // Update progress bar for each buffer. + progress.update(reader.offset()); + } + + // Progress bar is done. + progress.done(); + + // You do not have to close the Reader explicitly, but because the + // destructor can't throw, you will not see any errors otherwise. + reader.close(); +} + diff --git a/third_party/libosmium/examples/osmium_road_length.cpp b/third_party/libosmium/examples/osmium_road_length.cpp new file mode 100755 index 00000000000..2e1be908037 --- /dev/null +++ b/third_party/libosmium/examples/osmium_road_length.cpp @@ -0,0 +1,92 @@ +/* + + EXAMPLE osmium_road_length + + Calculate the length of the road network (everything tagged `highway=*`) + from the given OSM file. + + DEMONSTRATES USE OF: + * file input + * location indexes and the NodeLocationsForWays handler + * length calculation on the earth using the haversine function + + SIMPLER EXAMPLES you might want to understand first: + * osmium_read + * osmium_count + * osmium_pub_names + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for std::exit +#include // for std::cout, std::cerr + +// Allow any format of input files (XML, PBF, ...) +#include + +// For the osmium::geom::haversine::distance() function +#include + +// For osmium::apply() +#include + +// For the location index. There are different types of indexes available. +// This will work for small and medium sized input files. +#include + +// For the NodeLocationForWays handler +#include + +// The type of index used. This must match the include file above +using index_type = osmium::index::map::SparseMemArray; + +// The location handler always depends on the index type +using location_handler_type = osmium::handler::NodeLocationsForWays; + +// This handler only implements the way() function, we are not interested in +// any other objects. +struct RoadLengthHandler : public osmium::handler::Handler { + + double length = 0; + + // If the way has a "highway" tag, find its length and add it to the + // overall length. + void way(const osmium::Way& way) { + const char* highway = way.tags()["highway"]; + if (highway) { + length += osmium::geom::haversine::distance(way.nodes()); + } + } + +}; // struct RoadLengthHandler + +int main(int argc, char* argv[]) { + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " OSMFILE\n"; + std::exit(1); + } + + // Initialize the reader with the filename from the command line and + // tell it to only read nodes and ways. + osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::node | osmium::osm_entity_bits::way}; + + // The index to hold node locations. + index_type index; + + // The location handler will add the node locations to the index and then + // to the ways + location_handler_type location_handler{index}; + + // Our handler defined above + RoadLengthHandler road_length_handler; + + // Apply input data to first the location handler and then our own handler + osmium::apply(reader, location_handler, road_length_handler); + + // Output the length. The haversine function calculates it in meters, + // so we first devide by 1000 to get kilometers. + std::cout << "Length: " << road_length_handler.length / 1000 << " km\n"; +} + diff --git a/third_party/libosmium/examples/osmium_serdump.cpp b/third_party/libosmium/examples/osmium_serdump.cpp index 9ab26e4eeee..81a6d0cc575 100644 --- a/third_party/libosmium/examples/osmium_serdump.cpp +++ b/third_party/libosmium/examples/osmium_serdump.cpp @@ -69,9 +69,9 @@ int main(int argc, char* argv[]) { switch (c) { case 'h': print_help(); - exit(0); + std::exit(0); default: - exit(2); + std::exit(2); } } @@ -79,7 +79,7 @@ int main(int argc, char* argv[]) { if (remaining_args != 2) { std::cerr << "Usage: " << argv[0] << " OSMFILE DIR\n"; - exit(2); + std::exit(2); } std::string dir(argv[optind+1]); @@ -90,14 +90,14 @@ int main(int argc, char* argv[]) { #endif if (result == -1 && errno != EEXIST) { std::cerr << "Problem creating directory '" << dir << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } std::string data_file(dir + "/data.osm.ser"); int data_fd = ::open(data_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (data_fd < 0) { std::cerr << "Can't open data file '" << data_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } offset_index_type node_index; @@ -127,7 +127,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open nodes index file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } node_index.dump_as_list(fd); close(fd); @@ -138,7 +138,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open ways index file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } way_index.dump_as_list(fd); close(fd); @@ -149,7 +149,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open relations index file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } relation_index.dump_as_list(fd); close(fd); @@ -161,7 +161,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open node->way map file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } map_node2way.dump_as_list(fd); close(fd); @@ -173,7 +173,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open node->rel map file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } map_node2relation.dump_as_list(fd); close(fd); @@ -185,7 +185,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open way->rel map file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } map_way2relation.dump_as_list(fd); close(fd); @@ -197,7 +197,7 @@ int main(int argc, char* argv[]) { int fd = ::open(index_file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666); if (fd < 0) { std::cerr << "Can't open rel->rel map file '" << index_file << "': " << strerror(errno) << "\n"; - exit(2); + std::exit(2); } map_relation2relation.dump_as_list(fd); close(fd); diff --git a/third_party/libosmium/examples/osmium_tiles.cpp b/third_party/libosmium/examples/osmium_tiles.cpp new file mode 100644 index 00000000000..7dbeb3e29ae --- /dev/null +++ b/third_party/libosmium/examples/osmium_tiles.cpp @@ -0,0 +1,72 @@ +/* + + EXAMPLE osmium_tiles + + Convert WGS84 longitude and latitude to Mercator coordinates and tile + coordinates. + + DEMONSTRATES USE OF: + * the Location and Coordinates classes + * the Mercator projection function + * the Tile class + + LICENSE + The code in this example file is released into the Public Domain. + +*/ + +#include // for std::exit, std::atoi, std::atof +#include // for std::cout, std::cerr + +// The Location contains a longitude and latitude and is usually used inside +// a node to store its location in the world. +#include + +// Needed for the Mercator projection function. Osmium supports the Mercator +// projection out of the box, or pretty much any projection using the Proj.4 +// library (with the osmium::geom::Projection class). +#include + +// The Tile class handles tile coordinates and zoom levels. +#include + +int main(int argc, char* argv[]) { + if (argc != 4) { + std::cerr << "Usage: " << argv[0] << " ZOOM LON LAT\n"; + std::exit(1); + } + + const int zoom = std::atoi(argv[1]); + + if (zoom < 0 || zoom > 30) { + std::cerr << "ERROR: Zoom must be between 0 and 30\n"; + std::exit(1); + } + + const double lon = std::atof(argv[2]); + const double lat = std::atof(argv[3]); + + // Create location from WGS84 coordinates. In Osmium the order of + // coordinate values is always x/longitude first, then y/latitude. + const osmium::Location location{lon, lat}; + + std::cout << "WGS84: lon=" << lon << " lat=" << lat << "\n"; + + // A location can store some invalid locations, ie locations outside the + // -180 to 180 and -90 to 90 degree range. This function checks for that. + if (!location.valid()) { + std::cerr << "ERROR: Location is invalid\n"; + std::exit(1); + } + + // Project the coordinates using a helper function. You can also use the + // osmium::geom::MercatorProjection class. + const osmium::geom::Coordinates c = osmium::geom::lonlat_to_mercator(location); + std::cout << "Mercator: x=" << c.x << " y=" << c.y << "\n"; + + // Create a tile at this location. This will also internally use the + // Mercator projection and then calculate the tile coordinates. + const osmium::geom::Tile tile{uint32_t(zoom), location}; + std::cout << "Tile: zoom=" << tile.z << " x=" << tile.x << " y=" << tile.y << "\n"; +} + diff --git a/third_party/libosmium/examples/osmium_use_node_cache.cpp b/third_party/libosmium/examples/osmium_use_node_cache.cpp deleted file mode 100644 index cfee6df4683..00000000000 --- a/third_party/libosmium/examples/osmium_use_node_cache.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - - This reads ways from an OSM file and writes out the node locations - it got from a node cache generated with osmium_create_node_cache. - - The code in this example file is released into the Public Domain. - -*/ - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include - -typedef osmium::index::map::Dummy index_neg_type; -//typedef osmium::index::map::DenseMmapArray index_pos_type; -typedef osmium::index::map::DenseFileArray index_pos_type; - -typedef osmium::handler::NodeLocationsForWays location_handler_type; - -class MyHandler : public osmium::handler::Handler { - -public: - - void way(osmium::Way& way) { - for (auto& nr : way.nodes()) { - std::cout << nr << "\n"; - } - } - -}; // class MyHandler - -int main(int argc, char* argv[]) { - if (argc != 3) { - std::cerr << "Usage: " << argv[0] << " OSM_FILE CACHE_FILE\n"; - return 1; - } - - std::string input_filename(argv[1]); - osmium::io::Reader reader(input_filename, osmium::osm_entity_bits::way); - - int fd = open(argv[2], O_RDWR); - if (fd == -1) { - std::cerr << "Can not open node cache file '" << argv[2] << "': " << strerror(errno) << "\n"; - return 1; - } - - index_pos_type index_pos {fd}; - index_neg_type index_neg; - location_handler_type location_handler(index_pos, index_neg); - location_handler.ignore_errors(); - - MyHandler handler; - osmium::apply(reader, location_handler, handler); - reader.close(); - - return 0; -} - diff --git a/third_party/libosmium/include/gdalcpp.hpp b/third_party/libosmium/include/gdalcpp.hpp index 1502f2f0cb6..4f3d480961a 100644 --- a/third_party/libosmium/include/gdalcpp.hpp +++ b/third_party/libosmium/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 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 m_feature; public: Feature(Layer& layer, std::unique_ptr&& 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 Feature& set_field(int n, T&& arg) { - m_feature.SetField(n, std::forward(arg)); + m_feature->SetField(n, std::forward(arg)); return *this; } template Feature& set_field(const char* name, T&& arg) { - m_feature.SetField(name, std::forward(arg)); + m_feature->SetField(name, std::forward(arg)); return *this; } diff --git a/third_party/libosmium/include/osmium/area/assembler.hpp b/third_party/libosmium/include/osmium/area/assembler.hpp index a2d1c8ebd85..d5bf8d8daae 100644 --- a/third_party/libosmium/include/osmium/area/assembler.hpp +++ b/third_party/libosmium/include/osmium/area/assembler.hpp @@ -35,6 +35,8 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include #include #include #include @@ -42,95 +44,251 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include +#include #include #include #include #include +#include #include +#include #include +#include +#include +#include #include +#include +#include +#include #include #include #include #include +#include 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; + OSMIUM_DEPRECATED void enable_debug_output(bool d = true) { + debug_level = d; } }; // struct AssemblerConfig + namespace detail { + + using open_ring_its_type = std::list::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; + + 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 - // The way segments + // 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 m_rings; + // The rings we are building from the segments + std::list m_rings; + + // All node locations + std::vector m_locations; - std::vector m_outer_rings; - std::vector m_inner_rings; + // All locations where more than two segments start/end + std::vector m_split_locations; - int m_inner_outer_mismatches { 0 }; + // Statistics + area_stats m_stats; - bool debug() const { - return m_config.debug; + // The number of members the multipolygon relation has + size_t m_num_members = 0; + + 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& ways) const { @@ -144,13 +302,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 +327,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 +353,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 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 +384,1087 @@ namespace osmium { } } + template + 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(builder, ring); + for (const detail::ProtoRing* inner : ring.inner_rings()) { + build_ring_from_proto_ring(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 way_rings; + std::unordered_set 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; + + 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); + + 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"; + } + const 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 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() { + auto 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 create_location_to_ring_map(open_ring_its_type& open_ring_its) { + std::vector 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 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> 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& candidates, std::unordered_set& loc_done, const std::vector& 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"; + } + + 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 (it->to_left_of(min_node.location())) { - ++count; + 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 xrings = create_location_to_ring_map(open_ring_its); + + const auto ring_min = std::min_element(xrings.begin(), xrings.end(), [](const location_to_ring_map& a, const location_to_ring_map& b) { + return a.ring().min_segment() < b.ring().min_segment(); + }); + + 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 loc_done; + + loc_done.insert(cand.stop_location); + + std::vector 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 + const auto chosen_cand = ring_min_is_outer ? + std::min_element(candidates.cbegin(), candidates.cend(), [](const candidate& a, const candidate& b) { + return std::abs(a.sum) < std::abs(b.sum); + }) : + std::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 + auto 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; + } + + /** + * Checks if any ways were completely removed in the + * erase_duplicate_segments step. + */ + bool ways_were_lost() { + std::unordered_set ways_in_segments; + + for (const auto& segment : m_segment_list) { + ways_in_segments.insert(segment.way()); } + + return ways_in_segments.size() < m_num_members; } /** * 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 one or more complete ways was removed because of + // duplicate segments, this isn't a valid area. + if (ways_were_lost()) { + if (debug()) { + std::cerr << " Complete ways removed because of duplicate segments\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& members) { + m_num_members = members.size(); + 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 +1472,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. */ - void operator()(const osmium::Relation& relation, const std::vector& members, const osmium::memory::Buffer& in_buffer, osmium::memory::Buffer& out_buffer) { + OSMIUM_DEPRECATED void operator()(const osmium::Relation& relation, const std::vector& members, const osmium::memory::Buffer& in_buffer, osmium::memory::Buffer& out_buffer) { + std::vector ways; + for (size_t offset : members) { + const osmium::Way& way = in_buffer.get(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& 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(area_offset).tags(); // tags of the area we just built @@ -748,27 +1536,33 @@ namespace osmium { // just built, add them to a list and later build areas for // them, too. std::vector 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(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 +1572,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/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp b/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp index 7c1555a33e1..b131a431756 100644 --- a/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp +++ b/third_party/libosmium/include/osmium/area/detail/node_ref_segment.hpp @@ -34,11 +34,12 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include -#include #include #include +#include #include #include @@ -53,108 +54,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 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 +233,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()) { + const vec p0{lhs.first().location()}; + const vec p1{lhs.second().location()}; + const vec q0{rhs.first().location()}; + const vec q1{rhs.second().location()}; + const vec p = p1 - p0; + const vec q = q1 - q0; + + if (p.x == 0 && q.x == 0) { + return p.y < q.y; + } + + const auto a = p.y * q.x; + const auto b = q.y * p.x; + if (a == b) { + return p.x < q.x; + } + return a > b; + } + return lhs.first().location() < rhs.first().location(); } inline bool operator>(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept { @@ -184,7 +276,10 @@ namespace osmium { template inline std::basic_ostream& operator<<(std::basic_ostream& 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 +289,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 m1 = std::minmax(s1.first().location().y(), s1.second().location().y()); const std::pair m2 = std::minmax(s2.first().location().y(), s2.second().location().y()); if (m1.first > m2.second || m2.first > m1.second) { @@ -210,55 +305,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/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp b/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp index c4edf40183f..bce681711fb 100644 --- a/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp +++ b/third_party/libosmium/include/osmium/area/detail/proto_ring.hpp @@ -34,10 +34,9 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include -#include #include -#include #include #include @@ -47,6 +46,8 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + class Way; + namespace area { namespace detail { @@ -58,214 +59,155 @@ namespace osmium { public: - typedef std::vector segments_type; + using segments_type = std::vector; 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 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(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; - } - - segments_type& segments() noexcept { - return m_segments; - } + // If this is an inner ring, points to the outer ring. + ProtoRing* m_outer_ring; - const segments_type& segments() const noexcept { - return m_segments; - } + int64_t m_sum; - 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& 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(segment.first().location().x()) * static_cast(segment.second().location().y()) - - static_cast(segment.second().location().x()) * static_cast(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& 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& 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(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& 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/third_party/libosmium/include/osmium/area/detail/segment_list.hpp b/third_party/libosmium/include/osmium/area/detail/segment_list.hpp index 05e0cd8d481..a4361e0e130 100644 --- a/third_party/libosmium/include/osmium/area/detail/segment_list.hpp +++ b/third_party/libosmium/include/osmium/area/detail/segment_list.hpp @@ -35,12 +35,16 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include #include +#include +#include #include -#include #include -#include +#include +#include #include #include #include @@ -52,6 +56,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 + inline void for_each_member(const osmium::Relation& relation, const std::vector& 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 +86,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& 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 +151,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 +201,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 +209,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& 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(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& members) { + assert(relation.members().size() >= members.size()); + + const 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 +248,35 @@ 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 or if they don't both have + // the role "inner". Those cases are definitely wrong. + // If the duplicate segments belong to different + // "inner" ways, they could be touching inner rings + // which are perfectly okay. Note that for this check + // the role has to be correct in the member data. + if (it->way() == std::next(it)->way() || !it->role_inner() || !std::next(it)->role_inner()) { + ++duplicate_segments; + if (problem_reporter) { + problem_reporter->report_duplicate_segment(it->first(), it->second()); + } + } m_segments.erase(it, it+2); } + + return duplicate_segments; } /** @@ -182,14 +286,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 +307,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/third_party/libosmium/include/osmium/area/detail/vector.hpp b/third_party/libosmium/include/osmium/area/detail/vector.hpp new file mode 100644 index 00000000000..44983cc7547 --- /dev/null +++ b/third_party/libosmium/include/osmium/area/detail/vector.hpp @@ -0,0 +1,121 @@ +#ifndef OSMIUM_AREA_DETAIL_VECTOR_HPP +#define OSMIUM_AREA_DETAIL_VECTOR_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include + +#include +#include + +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 + inline std::basic_ostream& operator<<(std::basic_ostream& 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/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp b/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp index 81a28b24b97..8b370521b87 100644 --- a/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp +++ b/third_party/libosmium/include/osmium/area/multipolygon_collector.hpp @@ -34,11 +34,11 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include #include #include +#include #include #include #include @@ -47,7 +47,6 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include namespace osmium { @@ -74,13 +73,15 @@ namespace osmium { template class MultipolygonCollector : public osmium::relations::Collector, false, true, false> { - typedef typename osmium::relations::Collector, false, true, false> collector_type; + using collector_type = osmium::relations::Collector, 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 +108,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 +126,7 @@ namespace osmium { return false; } - if ((!strcmp(type, "multipolygon")) || (!strcmp(type, "boundary"))) { + if ((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) { return true; } @@ -155,26 +160,32 @@ 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&) { + } catch (const osmium::invalid_location&) { // XXX ignore } } void complete_relation(osmium::relations::RelationMeta& relation_meta) { const osmium::Relation& relation = this->get_relation(relation_meta); - std::vector offsets; + const osmium::memory::Buffer& buffer = this->members_buffer(); + + std::vector ways; for (const auto& member : relation.members()) { if (member.ref() != 0) { - offsets.push_back(this->get_offset(member.type(), member.ref())); + const size_t offset = this->get_offset(member.type(), member.ref()); + ways.push_back(&buffer.get(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&) { + } catch (const osmium::invalid_location&) { // XXX ignore } } diff --git a/third_party/libosmium/include/osmium/area/problem_reporter.hpp b/third_party/libosmium/include/osmium/area/problem_reporter.hpp index 1dde85ba9d8..6c562314e1d 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter.hpp @@ -33,12 +33,17 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include #include #include namespace osmium { + class NodeRef; + class Way; + namespace area { /** @@ -62,6 +67,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 +87,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,13 +99,23 @@ 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. * @@ -109,21 +131,33 @@ namespace osmium { osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) { } + /** + * Report a duplicate segments. Two or more segments are directly + * on top of each other. This can be a problem, if there is a + * spike for instance, or it could be okay, if there are touching + * inner rings. + * + * @param nr1 NodeRef of one end of the segment. + * @param nr2 NodeRef of the other end of the segment. + */ + virtual void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) { + } + /** * Report 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 +172,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/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp b/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp index abadd965363..009a5f4baf2 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_exception.hpp @@ -42,6 +42,9 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + class NodeRef; + class Way; + namespace area { class ProblemReporterException : public ProblemReporterStream { @@ -62,6 +65,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 +78,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 +102,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/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp b/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp index d58fe559cca..0889a580916 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_ogr.hpp @@ -49,8 +49,12 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include +#include +#include #include +#include 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(id1)); - feature.set_field("id2", static_cast(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 ogr_point1 = m_ogr_factory.create_point(loc1); - std::unique_ptr ogr_point2 = m_ogr_factory.create_point(loc2); - std::unique_ptr ogr_linestring = std::unique_ptr(new OGRLineString()); - ogr_linestring->addPoint(ogr_point1.get()); - ogr_linestring->addPoint(ogr_point2.get()); + auto ogr_linestring = std::unique_ptr{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(id1)); feature.set_field("id2", static_cast(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 (const osmium::geometry_error&) { + // XXX + } + } + + void report_inner_with_same_tags(const osmium::Way& way) override { + if (way.nodes().size() < 2) { + return; + } + try { + gdalcpp::Feature feature(m_layer_lerror, m_ogr_factory.create_linestring(way)); + set_object(feature); + feature.set_field("id1", int32_t(way.id())); + feature.set_field("id2", 0); + feature.set_field("problem", "inner_with_same_tags"); + feature.add_to_layer(); + } catch (const osmium::geometry_error&) { + // XXX + } + } + + void report_way(const osmium::Way& way) override { + if (way.nodes().empty()) { + return; + } + if (way.nodes().size() == 1) { + const auto& first_nr = way.nodes()[0]; + write_point("single_node_in_way", way.id(), first_nr.ref(), first_nr.location()); + return; + } + try { + gdalcpp::Feature feature(m_layer_ways, m_ogr_factory.create_linestring(way)); + set_object(feature); + feature.set_field("way_id", int32_t(way.id())); + feature.add_to_layer(); + } catch (const osmium::geometry_error&) { + // XXX + } } }; // class ProblemReporterOGR diff --git a/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp b/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp index ffd94a605f3..c601867b263 100644 --- a/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp +++ b/third_party/libosmium/include/osmium/area/problem_reporter_stream.hpp @@ -38,7 +38,9 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include +#include 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/third_party/libosmium/include/osmium/area/stats.hpp b/third_party/libosmium/include/osmium/area/stats.hpp new file mode 100644 index 00000000000..79cc0026ab8 --- /dev/null +++ b/third_party/libosmium/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 and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include + +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 + inline std::basic_ostream& operator<<(std::basic_ostream& 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/third_party/libosmium/include/osmium/builder/attr.hpp b/third_party/libosmium/include/osmium/builder/attr.hpp index d9831c28f3f..2a5b6907048 100644 --- a/third_party/libosmium/include/osmium/builder/attr.hpp +++ b/third_party/libosmium/include/osmium/builder/attr.hpp @@ -46,8 +46,15 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include namespace osmium { @@ -261,6 +268,34 @@ namespace osmium { }; // class member_type + class member_type_string { + + osmium::item_type m_type; + osmium::object_id_type m_ref; + std::string m_role; + + public: + + member_type_string(osmium::item_type type, osmium::object_id_type ref, std::string&& role) : + m_type(type), + m_ref(ref), + m_role(std::move(role)) { + } + + osmium::item_type type() const noexcept { + return m_type; + } + + osmium::object_id_type ref() const noexcept { + return m_ref; + } + + const char* role() const noexcept { + return m_role.c_str(); + } + + }; // class member_type_string + class comment_type { osmium::Timestamp m_date; diff --git a/third_party/libosmium/include/osmium/builder/builder.hpp b/third_party/libosmium/include/osmium/builder/builder.hpp index 869fe44e532..1b274ada562 100644 --- a/third_party/libosmium/include/osmium/builder/builder.hpp +++ b/third_party/libosmium/include/osmium/builder/builder.hpp @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include #include @@ -101,7 +100,7 @@ namespace osmium { * */ void add_padding(bool self = false) { - auto padding = osmium::memory::align_bytes - (size() % osmium::memory::align_bytes); + const auto padding = osmium::memory::align_bytes - (size() % osmium::memory::align_bytes); if (padding != osmium::memory::align_bytes) { std::fill_n(m_buffer.reserve_space(padding), padding, 0); if (self) { diff --git a/third_party/libosmium/include/osmium/builder/builder_helper.hpp b/third_party/libosmium/include/osmium/builder/builder_helper.hpp index e1b7c52ff92..5e0f2181d47 100644 --- a/third_party/libosmium/include/osmium/builder/builder_helper.hpp +++ b/third_party/libosmium/include/osmium/builder/builder_helper.hpp @@ -56,7 +56,7 @@ namespace osmium { * Use osmium::builder::add_way_node_list() instead. */ OSMIUM_DEPRECATED inline const osmium::WayNodeList& build_way_node_list(osmium::memory::Buffer& buffer, const std::initializer_list& nodes) { - size_t pos = buffer.committed(); + const size_t pos = buffer.committed(); { osmium::builder::WayNodeListBuilder wnl_builder(buffer); for (const auto& node_ref : nodes) { @@ -72,7 +72,7 @@ namespace osmium { * Use osmium::builder::add_tag_list() instead. */ inline const osmium::TagList& build_tag_list(osmium::memory::Buffer& buffer, const std::initializer_list>& tags) { - size_t pos = buffer.committed(); + const size_t pos = buffer.committed(); { osmium::builder::TagListBuilder tl_builder(buffer); for (const auto& p : tags) { @@ -88,7 +88,7 @@ namespace osmium { * Use osmium::builder::add_tag_list() instead. */ inline const osmium::TagList& build_tag_list_from_map(osmium::memory::Buffer& buffer, const std::map& tags) { - size_t pos = buffer.committed(); + const size_t pos = buffer.committed(); { osmium::builder::TagListBuilder tl_builder(buffer); for (const auto& p : tags) { @@ -104,7 +104,7 @@ namespace osmium { * Use osmium::builder::add_tag_list() instead. */ inline const osmium::TagList& build_tag_list_from_func(osmium::memory::Buffer& buffer, std::function func) { - size_t pos = buffer.committed(); + const size_t pos = buffer.committed(); { osmium::builder::TagListBuilder tl_builder(buffer); func(tl_builder); diff --git a/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp b/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp index d4b54f8fc11..e7a82988b88 100644 --- a/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp +++ b/third_party/libosmium/include/osmium/builder/osm_object_builder.hpp @@ -34,7 +34,6 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include #include #include @@ -44,16 +43,23 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include #include #include #include #include +#include +#include +#include +#include +#include +#include namespace osmium { + class Node; + namespace memory { class Buffer; } // namespace memory @@ -186,9 +192,9 @@ namespace osmium { }; // class NodeRefListBuilder - typedef NodeRefListBuilder WayNodeListBuilder; - typedef NodeRefListBuilder OuterRingBuilder; - typedef NodeRefListBuilder InnerRingBuilder; + using WayNodeListBuilder = NodeRefListBuilder; + using OuterRingBuilder = NodeRefListBuilder; + using InnerRingBuilder = NodeRefListBuilder; class RelationMemberListBuilder : public ObjectBuilder { @@ -353,8 +359,8 @@ namespace osmium { }; // class OSMObjectBuilder - typedef OSMObjectBuilder NodeBuilder; - typedef OSMObjectBuilder RelationBuilder; + using NodeBuilder = OSMObjectBuilder; + using RelationBuilder = OSMObjectBuilder; class WayBuilder : public OSMObjectBuilder { @@ -398,7 +404,7 @@ namespace osmium { }; // class AreaBuilder - typedef ObjectBuilder ChangesetBuilder; + using ChangesetBuilder = ObjectBuilder; } // namespace builder diff --git a/third_party/libosmium/include/osmium/diff_iterator.hpp b/third_party/libosmium/include/osmium/diff_iterator.hpp index 6e0ba4bbf62..fc92b36ce43 100644 --- a/third_party/libosmium/include/osmium/diff_iterator.hpp +++ b/third_party/libosmium/include/osmium/diff_iterator.hpp @@ -34,8 +34,10 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include +#include #include @@ -49,7 +51,7 @@ namespace osmium { * underlying OSMObjects. */ template - class DiffIterator : public std::iterator { + class DiffIterator { static_assert(std::is_base_of::value, "TBasicIterator::value_type must derive from osmium::OSMObject"); @@ -76,6 +78,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/third_party/libosmium/include/osmium/dynamic_handler.hpp b/third_party/libosmium/include/osmium/dynamic_handler.hpp index e7c9900fa34..01d15546b2a 100644 --- a/third_party/libosmium/include/osmium/dynamic_handler.hpp +++ b/third_party/libosmium/include/osmium/dynamic_handler.hpp @@ -36,11 +36,16 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include namespace osmium { + class Node; + class Way; + class Relation; + class Area; + class Changeset; + namespace handler { namespace detail { @@ -143,7 +148,7 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) -> class DynamicHandler : public osmium::handler::Handler { - typedef std::unique_ptr impl_ptr; + using impl_ptr = std::unique_ptr; impl_ptr m_impl; public: diff --git a/third_party/libosmium/include/osmium/experimental/flex_reader.hpp b/third_party/libosmium/include/osmium/experimental/flex_reader.hpp index 8059ea32a4a..0a2a6683749 100644 --- a/third_party/libosmium/include/osmium/experimental/flex_reader.hpp +++ b/third_party/libosmium/include/osmium/experimental/flex_reader.hpp @@ -34,11 +34,12 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include #include -#include +#include // IWYU pragma: keep #include #include #include diff --git a/third_party/libosmium/include/osmium/geom/factory.hpp b/third_party/libosmium/include/osmium/geom/factory.hpp index 394228eb920..14c51dfdf96 100644 --- a/third_party/libosmium/include/osmium/geom/factory.hpp +++ b/third_party/libosmium/include/osmium/geom/factory.hpp @@ -46,6 +46,8 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include #include namespace osmium { @@ -148,7 +150,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()) { @@ -169,7 +171,7 @@ namespace osmium { template explicit GeometryFactory(TArgs&&... args) : m_projection(), - m_impl(std::forward(args)...) { + m_impl(m_projection.epsg(), std::forward(args)...) { } /** @@ -179,15 +181,16 @@ namespace osmium { template explicit GeometryFactory(TProjection&& projection, TArgs&&... args) : m_projection(std::move(projection)), - m_impl(std::forward(args)...) { + m_impl(m_projection.epsg(), std::forward(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(); @@ -280,13 +283,13 @@ namespace osmium { } if (num_points < 2) { - throw osmium::geometry_error("need at least two points for linestring"); + throw osmium::geometry_error{"need at least two points for linestring"}; } return linestring_finish(num_points); } - linestring_type create_linestring(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir=direction::forward) { + linestring_type create_linestring(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir = direction::forward) { try { return create_linestring(way.nodes(), un, dir); } catch (osmium::geometry_error& e) { @@ -354,13 +357,13 @@ namespace osmium { } if (num_points < 4) { - throw osmium::geometry_error("need at least four points for polygon"); + throw osmium::geometry_error{"need at least four points for polygon"}; } return polygon_finish(num_points); } - polygon_type create_polygon(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir=direction::forward) { + polygon_type create_polygon(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir = direction::forward) { try { return create_polygon(way.nodes(), un, dir); } catch (osmium::geometry_error& e) { @@ -378,8 +381,8 @@ namespace osmium { m_impl.multipolygon_start(); for (auto it = area.cbegin(); it != area.cend(); ++it) { - const osmium::OuterRing& ring = static_cast(*it); if (it->type() == osmium::item_type::outer_ring) { + auto& ring = static_cast(*it); if (num_polygons > 0) { m_impl.multipolygon_polygon_finish(); } @@ -390,6 +393,7 @@ namespace osmium { ++num_rings; ++num_polygons; } else if (it->type() == osmium::item_type::inner_ring) { + auto& ring = static_cast(*it); m_impl.multipolygon_inner_ring_start(); add_points(ring); m_impl.multipolygon_inner_ring_finish(); @@ -399,7 +403,7 @@ namespace osmium { // if there are no rings, this area is invalid if (num_rings == 0) { - throw osmium::geometry_error("area contains no rings"); + throw osmium::geometry_error{"invalid area"}; } m_impl.multipolygon_polygon_finish(); diff --git a/third_party/libosmium/include/osmium/geom/geojson.hpp b/third_party/libosmium/include/osmium/geom/geojson.hpp index 044a89f865c..e9f722fa951 100644 --- a/third_party/libosmium/include/osmium/geom/geojson.hpp +++ b/third_party/libosmium/include/osmium/geom/geojson.hpp @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include @@ -53,13 +54,13 @@ 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) : + GeoJSONFactoryImpl(int /* srid */, int precision = 7) : m_precision(precision) { } diff --git a/third_party/libosmium/include/osmium/geom/geos.hpp b/third_party/libosmium/include/osmium/geom/geos.hpp index f9026e5a99b..f406076a184 100644 --- a/third_party/libosmium/include/osmium/geom/geos.hpp +++ b/third_party/libosmium/include/osmium/geom/geos.hpp @@ -42,9 +42,14 @@ DEALINGS IN THE SOFTWARE. * @attention If you include this file, you'll need to link with `libgeos`. */ +#include +#include +#include +#include #include #include #include +#include #include #include @@ -59,11 +64,13 @@ DEALINGS IN THE SOFTWARE. #include #include +#include // MSVC doesn't support throw_with_nested yet #ifdef _MSC_VER # define THROW throw #else +# include # define THROW std::throw_with_nested #endif @@ -71,8 +78,8 @@ namespace osmium { struct geos_geometry_error : public geometry_error { - geos_geometry_error(const char* message) : - geometry_error(std::string("geometry creation failed in GEOS library: ") + message) { + explicit geos_geometry_error(const char* message) : + geometry_error(std::string{"geometry creation failed in GEOS library: "} + message) { } }; // struct geos_geometry_error @@ -93,19 +100,29 @@ namespace osmium { public: - typedef std::unique_ptr point_type; - typedef std::unique_ptr linestring_type; - typedef std::unique_ptr polygon_type; - typedef std::unique_ptr multipolygon_type; - typedef std::unique_ptr ring_type; + using point_type = std::unique_ptr; + using linestring_type = std::unique_ptr; + using polygon_type = std::unique_ptr; + using multipolygon_type = std::unique_ptr; + using ring_type = std::unique_ptr; - explicit GEOSFactoryImpl(geos::geom::GeometryFactory& geos_factory) : + explicit GEOSFactoryImpl(int /* srid */, geos::geom::GeometryFactory& geos_factory) : m_precision_model(nullptr), m_our_geos_factory(nullptr), m_geos_factory(&geos_factory) { } - explicit GEOSFactoryImpl(int srid = -1) : + /** + * @deprecated Do not set SRID explicitly. It will be set to the + * correct value automatically. + */ + OSMIUM_DEPRECATED explicit GEOSFactoryImpl(int /* srid */, int srid) : + m_precision_model(new geos::geom::PrecisionModel), + m_our_geos_factory(new geos::geom::GeometryFactory(m_precision_model.get(), srid)), + m_geos_factory(m_our_geos_factory.get()) { + } + + explicit GEOSFactoryImpl(int srid) : m_precision_model(new geos::geom::PrecisionModel), m_our_geos_factory(new geos::geom::GeometryFactory(m_precision_model.get(), srid)), m_geos_factory(m_our_geos_factory.get()) { @@ -116,7 +133,7 @@ namespace osmium { point_type make_point(const osmium::geom::Coordinates& xy) const { try { return point_type(m_geos_factory->createPoint(geos::geom::Coordinate(xy.x, xy.y))); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -126,7 +143,7 @@ namespace osmium { void linestring_start() { try { m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -134,7 +151,7 @@ namespace osmium { void linestring_add_location(const osmium::geom::Coordinates& xy) { try { m_coordinate_sequence->add(geos::geom::Coordinate(xy.x, xy.y)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -142,7 +159,7 @@ namespace osmium { linestring_type linestring_finish(size_t /* num_points */) { try { return linestring_type(m_geos_factory->createLineString(m_coordinate_sequence.release())); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -166,7 +183,7 @@ namespace osmium { }); m_polygons.emplace_back(m_geos_factory->createPolygon(m_rings[0].release(), inner_rings)); m_rings.clear(); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -174,7 +191,7 @@ namespace osmium { void multipolygon_outer_ring_start() { try { m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -182,7 +199,7 @@ namespace osmium { void multipolygon_outer_ring_finish() { try { m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release())); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -190,7 +207,7 @@ namespace osmium { void multipolygon_inner_ring_start() { try { m_coordinate_sequence.reset(m_geos_factory->getCoordinateSequenceFactory()->create(static_cast(0), 2)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -198,7 +215,7 @@ namespace osmium { void multipolygon_inner_ring_finish() { try { m_rings.emplace_back(m_geos_factory->createLinearRing(m_coordinate_sequence.release())); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -206,7 +223,7 @@ namespace osmium { void multipolygon_add_location(const osmium::geom::Coordinates& xy) { try { m_coordinate_sequence->add(geos::geom::Coordinate(xy.x, xy.y)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } @@ -219,7 +236,7 @@ namespace osmium { }); m_polygons.clear(); return multipolygon_type(m_geos_factory->createMultiPolygon(polygons)); - } catch (geos::util::GEOSException& e) { + } catch (const geos::util::GEOSException& e) { THROW(osmium::geos_geometry_error(e.what())); } } diff --git a/third_party/libosmium/include/osmium/geom/haversine.hpp b/third_party/libosmium/include/osmium/geom/haversine.hpp index 632bc16129a..ef3f1ff787d 100644 --- a/third_party/libosmium/include/osmium/geom/haversine.hpp +++ b/third_party/libosmium/include/osmium/geom/haversine.hpp @@ -56,7 +56,7 @@ namespace osmium { namespace haversine { /// @brief Earth's quadratic mean radius for WGS84 - constexpr double EARTH_RADIUS_IN_METERS = 6372797.560856; + constexpr const double EARTH_RADIUS_IN_METERS = 6372797.560856; /** * Calculate distance in meters between two sets of coordinates. diff --git a/third_party/libosmium/include/osmium/geom/ogr.hpp b/third_party/libosmium/include/osmium/geom/ogr.hpp index b8e9ef3ab30..a91fbe5c564 100644 --- a/third_party/libosmium/include/osmium/geom/ogr.hpp +++ b/third_party/libosmium/include/osmium/geom/ogr.hpp @@ -62,33 +62,34 @@ namespace osmium { public: - typedef std::unique_ptr point_type; - typedef std::unique_ptr linestring_type; - typedef std::unique_ptr polygon_type; - typedef std::unique_ptr multipolygon_type; - typedef std::unique_ptr ring_type; + using point_type = std::unique_ptr; + using linestring_type = std::unique_ptr; + using polygon_type = std::unique_ptr; + using multipolygon_type = std::unique_ptr; + using ring_type = std::unique_ptr; 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: - OGRFactoryImpl() = default; + explicit OGRFactoryImpl(int /* srid */) { + } /* 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(new OGRLineString()); + m_linestring.reset(new OGRLineString{}); } void linestring_add_location(const osmium::geom::Coordinates& xy) { @@ -97,13 +98,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(new OGRLinearRing()); + m_ring.reset(new OGRLinearRing{}); } void polygon_add_location(const osmium::geom::Coordinates& xy) { @@ -112,7 +114,7 @@ namespace osmium { } polygon_type polygon_finish(size_t /* num_points */) { - std::unique_ptr polygon = std::unique_ptr(new OGRPolygon()); + auto polygon = std::unique_ptr{new OGRPolygon{}}; polygon->addRingDirectly(m_ring.release()); return polygon; } @@ -120,11 +122,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 +136,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 +146,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/third_party/libosmium/include/osmium/geom/projection.hpp b/third_party/libosmium/include/osmium/geom/projection.hpp index 42d95b27e0c..eaa9b53f361 100644 --- a/third_party/libosmium/include/osmium/geom/projection.hpp +++ b/third_party/libosmium/include/osmium/geom/projection.hpp @@ -70,15 +70,19 @@ namespace osmium { public: - CRS(const std::string& crs) : + explicit CRS(const std::string& crs) : m_crs(pj_init_plus(crs.c_str()), ProjCRSDeleter()) { if (!m_crs) { - throw osmium::projection_error(std::string("creation of CRS failed: ") + pj_strerrno(*pj_get_errno_ref())); + throw osmium::projection_error(std::string{"creation of CRS failed: "} + pj_strerrno(*pj_get_errno_ref())); } } - CRS(int epsg) : - CRS(std::string("+init=epsg:") + std::to_string(epsg)) { + explicit CRS(const char* crs) : + CRS(std::string{crs}) { + } + + explicit CRS(int epsg) : + CRS(std::string{"+init=epsg:"} + std::to_string(epsg)) { } /** @@ -127,13 +131,19 @@ namespace osmium { public: - Projection(const std::string& proj_string) : + explicit Projection(const std::string& proj_string) : + m_epsg(-1), + m_proj_string(proj_string), + m_crs_user(proj_string) { + } + + explicit Projection(const char* proj_string) : m_epsg(-1), m_proj_string(proj_string), m_crs_user(proj_string) { } - Projection(int epsg) : + explicit Projection(int epsg) : m_epsg(epsg), m_proj_string(std::string("+init=epsg:") + std::to_string(epsg)), m_crs_user(epsg) { diff --git a/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp b/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp index 87e13110feb..7817389c96d 100644 --- a/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp +++ b/third_party/libosmium/include/osmium/geom/rapid_geojson.hpp @@ -33,6 +33,8 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include #include @@ -53,13 +55,13 @@ 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) : + RapidGeoJSONFactoryImpl(int /* srid */, TWriter& writer) : m_writer(&writer) { } diff --git a/third_party/libosmium/include/osmium/geom/tile.hpp b/third_party/libosmium/include/osmium/geom/tile.hpp index e35c2ee59e5..672ae5416db 100644 --- a/third_party/libosmium/include/osmium/geom/tile.hpp +++ b/third_party/libosmium/include/osmium/geom/tile.hpp @@ -33,9 +33,12 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include +#include namespace osmium { @@ -57,24 +60,64 @@ namespace osmium { */ struct Tile { + /// x coordinate uint32_t x; + + /// y coordinate uint32_t y; + + /// Zoom level uint32_t z; + /** + * Create a tile with the given zoom level and x any y tile + * coordinates. + * + * The values are not checked for validity. + * + * @pre @code zoom <= 30 && x < 2^zoom && y < 2^zoom @endcode + */ explicit Tile(uint32_t zoom, uint32_t tx, uint32_t ty) noexcept : x(tx), y(ty), z(zoom) { + assert(zoom <= 30u); + assert(x < (1u << zoom)); + assert(y < (1u << zoom)); } + /** + * Create a tile with the given zoom level that contains the given + * location. + * + * The values are not checked for validity. + * + * @pre @code location.valid() && zoom <= 30 @endcode + */ explicit Tile(uint32_t zoom, const osmium::Location& location) : z(zoom) { - osmium::geom::Coordinates c = lonlat_to_mercator(location); + assert(zoom <= 30u); + assert(location.valid()); + const osmium::geom::Coordinates c = lonlat_to_mercator(location); const int32_t n = 1 << zoom; const double scale = detail::max_coordinate_epsg3857 * 2 / n; x = uint32_t(detail::restrict_to_range(int32_t((c.x + detail::max_coordinate_epsg3857) / scale), 0, n-1)); y = uint32_t(detail::restrict_to_range(int32_t((detail::max_coordinate_epsg3857 - c.y) / scale), 0, n-1)); } + /** + * Check whether this tile is valid. For a tile to be valid the + * zoom level must be between 0 and 30 and the coordinates must + * each be between 0 and 2^zoom-1. + */ + bool valid() const noexcept { + if (z > 30) { + return false; + } + const uint32_t max = 1 << z; + return x < max && y < max; + } + }; // struct Tile + /// Tiles are equal if all their attributes are equal. inline bool operator==(const Tile& a, const Tile& b) { return a.z == b.z && a.x == b.x && a.y == b.y; } diff --git a/third_party/libosmium/include/osmium/geom/util.hpp b/third_party/libosmium/include/osmium/geom/util.hpp index fa9c8bc3687..b5682f92f29 100644 --- a/third_party/libosmium/include/osmium/geom/util.hpp +++ b/third_party/libosmium/include/osmium/geom/util.hpp @@ -44,11 +44,11 @@ namespace osmium { */ struct projection_error : public std::runtime_error { - projection_error(const std::string& what) : + explicit projection_error(const std::string& what) : std::runtime_error(what) { } - projection_error(const char* what) : + explicit projection_error(const char* what) : std::runtime_error(what) { } diff --git a/third_party/libosmium/include/osmium/geom/wkb.hpp b/third_party/libosmium/include/osmium/geom/wkb.hpp index 19c12165ee0..d6b779879c0 100644 --- a/third_party/libosmium/include/osmium/geom/wkb.hpp +++ b/third_party/libosmium/include/osmium/geom/wkb.hpp @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -60,14 +61,13 @@ namespace osmium { template inline void str_push(std::string& str, T data) { - size_t size = str.size(); - str.resize(size + sizeof(T)); - std::copy_n(reinterpret_cast(&data), sizeof(T), &str[size]); + str.append(reinterpret_cast(&data), sizeof(T)); } inline std::string convert_to_hex(const std::string& str) { static const char* lookup_hex = "0123456789ABCDEF"; std::string out; + out.reserve(str.size() * 2); for (char c : str) { out += lookup_hex[(c >> 4) & 0xf]; @@ -79,9 +79,6 @@ namespace osmium { class WKBFactoryImpl { - /// OSM data always uses SRID 4326 (WGS84). - static constexpr uint32_t srid = 4326; - /** * Type of WKB geometry. * These definitions are from @@ -112,6 +109,7 @@ namespace osmium { std::string m_data; uint32_t m_points {0}; + int m_srid; wkb_type m_wkb_type; out_type m_out_type; @@ -130,11 +128,11 @@ namespace osmium { #endif if (m_wkb_type == wkb_type::ewkb) { str_push(str, type | wkbSRID); - str_push(str, srid); + str_push(str, m_srid); } else { str_push(str, type); } - size_t offset = str.size(); + const size_t offset = str.size(); if (add_length) { str_push(str, static_cast(0)); } @@ -142,18 +140,20 @@ namespace osmium { } void set_size(const size_t offset, const size_t size) { - *reinterpret_cast(&m_data[offset]) = static_cast_with_assert(size); + uint32_t s = static_cast_with_assert(size); + std::copy_n(reinterpret_cast(&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) : + explicit WKBFactoryImpl(int srid, wkb_type wtype = wkb_type::wkb, out_type otype = out_type::binary) : + m_srid(srid), m_wkb_type(wtype), m_out_type(otype) { } diff --git a/third_party/libosmium/include/osmium/geom/wkt.hpp b/third_party/libosmium/include/osmium/geom/wkt.hpp index ae73b960407..e360f712b53 100644 --- a/third_party/libosmium/include/osmium/geom/wkt.hpp +++ b/third_party/libosmium/include/osmium/geom/wkt.hpp @@ -45,29 +45,44 @@ namespace osmium { namespace geom { + enum class wkt_type : bool { + wkt = false, + ewkt = true + }; // enum class wkt_type + namespace detail { class WKTFactoryImpl { + std::string m_srid_prefix; std::string m_str; int m_precision; + wkt_type m_wkt_type; 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; - - WKTFactoryImpl(int precision = 7) : - m_precision(precision) { + 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 srid, int precision = 7, wkt_type wtype = wkt_type::wkt) : + m_srid_prefix(), + m_precision(precision), + m_wkt_type(wtype) { + if (m_wkt_type == wkt_type::ewkt) { + m_srid_prefix = "SRID="; + m_srid_prefix += std::to_string(srid); + m_srid_prefix += ';'; + } } /* Point */ point_type make_point(const osmium::geom::Coordinates& xy) const { - std::string str {"POINT"}; + std::string str {m_srid_prefix}; + str += "POINT"; xy.append_to_string(str, '(', ' ', ')', m_precision); return str; } @@ -75,7 +90,8 @@ namespace osmium { /* LineString */ void linestring_start() { - m_str = "LINESTRING("; + m_str = m_srid_prefix; + m_str += "LINESTRING("; } void linestring_add_location(const osmium::geom::Coordinates& xy) { @@ -97,7 +113,8 @@ namespace osmium { /* MultiPolygon */ void multipolygon_start() { - m_str = "MULTIPOLYGON("; + m_str = m_srid_prefix; + m_str += "MULTIPOLYGON("; } void multipolygon_polygon_start() { diff --git a/third_party/libosmium/include/osmium/handler.hpp b/third_party/libosmium/include/osmium/handler.hpp index 3620d654697..1263910d4ef 100644 --- a/third_party/libosmium/include/osmium/handler.hpp +++ b/third_party/libosmium/include/osmium/handler.hpp @@ -33,56 +33,82 @@ DEALINGS IN THE SOFTWARE. */ -#include - namespace osmium { + class Area; + class Changeset; + class ChangesetDiscussion; + class InnerRing; + class Node; + class OSMObject; + class OuterRing; + class Relation; + class RelationMemberList; + class TagList; + class Way; + class WayNodeList; + /** * @brief Osmium handlers provide callbacks for OSM objects */ namespace handler { + /** + * Handler base class. Never used directly. Derive your own class from + * this class and "overwrite" the functions. Your functions must be + * named the same, but don't have to be const or noexcept or take + * their argument as const. + * + * Usually you will overwrite the node(), way(), and relation() + * functions. If your program supports multipolygons, also the area() + * function. You can also use the osm_object() function which is + * called for all OSM objects (nodes, ways, relations, and areas) + * right before each of their specific callbacks is called. + * + * If you are working with changesets, implement the changeset() + * function. + */ class Handler { public: - void osm_object(const osmium::OSMObject&) const { + void osm_object(const osmium::OSMObject&) const noexcept { } - void node(const osmium::Node&) const { + void node(const osmium::Node&) const noexcept { } - void way(const osmium::Way&) const { + void way(const osmium::Way&) const noexcept { } - void relation(const osmium::Relation&) const { + void relation(const osmium::Relation&) const noexcept { } - void area(const osmium::Area&) const { + void area(const osmium::Area&) const noexcept { } - void changeset(const osmium::Changeset&) const { + void changeset(const osmium::Changeset&) const noexcept { } - void tag_list(const osmium::TagList&) const { + void tag_list(const osmium::TagList&) const noexcept { } - void way_node_list(const osmium::WayNodeList&) const { + void way_node_list(const osmium::WayNodeList&) const noexcept { } - void relation_member_list(const osmium::RelationMemberList&) const { + void relation_member_list(const osmium::RelationMemberList&) const noexcept { } - void outer_ring(const osmium::OuterRing&) const { + void outer_ring(const osmium::OuterRing&) const noexcept { } - void inner_ring(const osmium::InnerRing&) const { + void inner_ring(const osmium::InnerRing&) const noexcept { } - void changeset_discussion(const osmium::ChangesetDiscussion&) const { + void changeset_discussion(const osmium::ChangesetDiscussion&) const noexcept { } - void flush() const { + void flush() const noexcept { } }; // class Handler diff --git a/third_party/libosmium/include/osmium/handler/chain.hpp b/third_party/libosmium/include/osmium/handler/chain.hpp index e59eb05b9b2..109e3537acb 100644 --- a/third_party/libosmium/include/osmium/handler/chain.hpp +++ b/third_party/libosmium/include/osmium/handler/chain.hpp @@ -67,7 +67,7 @@ namespace osmium { template class ChainHandler : public osmium::handler::Handler { - typedef std::tuple handlers_type; + using handlers_type = std::tuple; handlers_type m_handlers; template diff --git a/third_party/libosmium/include/osmium/handler/check_order.hpp b/third_party/libosmium/include/osmium/handler/check_order.hpp index 143794bd57f..a9b6834a434 100644 --- a/third_party/libosmium/include/osmium/handler/check_order.hpp +++ b/third_party/libosmium/include/osmium/handler/check_order.hpp @@ -51,11 +51,11 @@ namespace osmium { */ struct out_of_order_error : public std::runtime_error { - out_of_order_error(const std::string& what) : + explicit out_of_order_error(const std::string& what) : std::runtime_error(what) { } - out_of_order_error(const char* what) : + explicit out_of_order_error(const char* what) : std::runtime_error(what) { } diff --git a/third_party/libosmium/include/osmium/handler/disk_store.hpp b/third_party/libosmium/include/osmium/handler/disk_store.hpp index b8ab229a314..4e955734a7c 100644 --- a/third_party/libosmium/include/osmium/handler/disk_store.hpp +++ b/third_party/libosmium/include/osmium/handler/disk_store.hpp @@ -57,7 +57,7 @@ namespace osmium { */ class DiskStore : public osmium::handler::Handler { - typedef osmium::index::map::Map offset_index_type; + using offset_index_type = osmium::index::map::Map; size_t m_offset = 0; int m_data_fd; diff --git a/third_party/libosmium/include/osmium/handler/node_locations_for_ways.hpp b/third_party/libosmium/include/osmium/handler/node_locations_for_ways.hpp index f62a4db7cd6..a490f9e28eb 100644 --- a/third_party/libosmium/include/osmium/handler/node_locations_for_ways.hpp +++ b/third_party/libosmium/include/osmium/handler/node_locations_for_ways.hpp @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include @@ -50,7 +51,7 @@ namespace osmium { namespace handler { - typedef osmium::index::map::Dummy dummy_type; + using dummy_type = osmium::index::map::Dummy; /** * 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( 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::max(); } bool error = false; for (auto& node_ref : way.nodes()) { @@ -152,7 +160,7 @@ namespace osmium { if (!node_ref.location()) { error = true; } - } catch (osmium::not_found&) { + } catch (const osmium::not_found&) { error = true; } } diff --git a/third_party/libosmium/include/osmium/handler/object_relations.hpp b/third_party/libosmium/include/osmium/handler/object_relations.hpp index 4afcf6a6d2a..279365d08ef 100644 --- a/third_party/libosmium/include/osmium/handler/object_relations.hpp +++ b/third_party/libosmium/include/osmium/handler/object_relations.hpp @@ -52,7 +52,7 @@ namespace osmium { */ class ObjectRelations : public osmium::handler::Handler { - typedef osmium::index::multimap::Multimap index_type; + using index_type = osmium::index::multimap::Multimap; index_type& m_index_n2w; index_type& m_index_n2r; diff --git a/third_party/libosmium/include/osmium/index/detail/create_map_with_fd.hpp b/third_party/libosmium/include/osmium/index/detail/create_map_with_fd.hpp index 640b3c693ac..72b326a1b14 100644 --- a/third_party/libosmium/include/osmium/index/detail/create_map_with_fd.hpp +++ b/third_party/libosmium/include/osmium/index/detail/create_map_with_fd.hpp @@ -54,9 +54,9 @@ namespace osmium { } assert(config.size() > 1); const std::string& filename = config[1]; - int fd = ::open(filename.c_str(), O_CREAT | O_RDWR, 0644); + const int fd = ::open(filename.c_str(), O_CREAT | O_RDWR, 0644); if (fd == -1) { - throw std::runtime_error(std::string("can't open file '") + filename + "': " + strerror(errno)); + throw std::runtime_error(std::string("can't open file '") + filename + "': " + std::strerror(errno)); } return new T(fd); } diff --git a/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp b/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp index aadeef0b543..8f52e98495d 100644 --- a/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp +++ b/third_party/libosmium/include/osmium/index/detail/mmap_vector_base.hpp @@ -33,10 +33,13 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include #include #include // IWYU pragma: keep #include +#include #include 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()); + 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()); } ~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,20 +128,21 @@ namespace osmium { } void shrink_to_fit() { - // XXX do something here + while (m_size > 0 && data()[m_size - 1] == osmium::index::empty_value()) { + --m_size; + } } void push_back(const T& value) { - if (m_size >= capacity()) { - resize(m_size+1); - } - data()[m_size] = value; - ++m_size; + resize(m_size+1); + data()[m_size-1] = value; } void reserve(size_t new_capacity) { if (new_capacity > capacity()) { + const size_t old_capacity = capacity(); m_mapping.resize(new_capacity); + std::fill(data() + old_capacity, data() + new_capacity, osmium::index::empty_value()); } } @@ -141,9 +150,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/third_party/libosmium/include/osmium/index/detail/mmap_vector_file.hpp b/third_party/libosmium/include/osmium/index/detail/mmap_vector_file.hpp index 666a409d4b1..9e72f5a8cea 100644 --- a/third_party/libosmium/include/osmium/index/detail/mmap_vector_file.hpp +++ b/third_party/libosmium/include/osmium/index/detail/mmap_vector_file.hpp @@ -33,6 +33,11 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include +#include +#include + #include #include #include @@ -48,6 +53,16 @@ namespace osmium { template class mmap_vector_file : public mmap_vector_base { + size_t filesize(int fd) const { + 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 +74,8 @@ namespace osmium { explicit mmap_vector_file(int fd) : mmap_vector_base( 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/third_party/libosmium/include/osmium/index/detail/vector_map.hpp b/third_party/libosmium/include/osmium/index/detail/vector_map.hpp index 0fa59dd4882..ac87c2fc918 100644 --- a/third_party/libosmium/include/osmium/index/detail/vector_map.hpp +++ b/third_party/libosmium/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() { @@ -88,7 +88,7 @@ namespace osmium { not_found_error(id); } return value; - } catch (std::out_of_range&) { + } catch (const std::out_of_range&) { not_found_error(id); } } @@ -146,10 +146,10 @@ namespace osmium { public: - typedef typename std::pair element_type; - typedef TVector vector_type; - typedef typename vector_type::iterator iterator; - typedef typename vector_type::const_iterator const_iterator; + using element_type = typename std::pair; + using vector_type = TVector; + using iterator = typename vector_type::iterator; + using const_iterator = typename vector_type::const_iterator; private: diff --git a/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp b/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp index 35a4cc182b8..f859f5b4559 100644 --- a/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp +++ b/third_party/libosmium/include/osmium/index/detail/vector_multimap.hpp @@ -52,10 +52,10 @@ namespace osmium { public: - typedef typename std::pair element_type; - typedef TVector vector_type; - typedef typename vector_type::iterator iterator; - typedef typename vector_type::const_iterator const_iterator; + using element_type = typename std::pair; + using vector_type = TVector; + using iterator = typename vector_type::iterator; + using const_iterator = typename vector_type::const_iterator; private: @@ -127,7 +127,7 @@ namespace osmium { } void remove(const TId id, const TValue value) { - auto r = get_all(id); + const auto r = get_all(id); for (auto it = r.first; it != r.second; ++it) { if (it->second == value) { it->second = 0; diff --git a/third_party/libosmium/include/osmium/index/index.hpp b/third_party/libosmium/include/osmium/index/index.hpp index 37d022d1bd2..c3deeadf80c 100644 --- a/third_party/libosmium/include/osmium/index/index.hpp +++ b/third_party/libosmium/include/osmium/index/index.hpp @@ -49,11 +49,11 @@ namespace osmium { */ struct not_found : public std::runtime_error { - not_found(const std::string& what) : + explicit not_found(const std::string& what) : std::runtime_error(what) { } - not_found(const char* what) : + explicit not_found(const char* what) : std::runtime_error(what) { } diff --git a/third_party/libosmium/include/osmium/index/map.hpp b/third_party/libosmium/include/osmium/index/map.hpp index f90a895d16a..1d2d5aa3421 100644 --- a/third_party/libosmium/include/osmium/index/map.hpp +++ b/third_party/libosmium/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 map_type; - typedef std::function&)> create_map_func; + using id_type = TId; + using value_type = TValue; + using map_type = osmium::index::map::Map; + using create_map_func = std::function&)>; private: @@ -207,7 +207,7 @@ namespace osmium { } bool has_map_type(const std::string& map_type_name) const { - return m_callbacks.count(map_type_name); + return m_callbacks.count(map_type_name) != 0; } std::vector map_types() const { diff --git a/third_party/libosmium/include/osmium/index/map/sparse_mem_map.hpp b/third_party/libosmium/include/osmium/index/map/sparse_mem_map.hpp index 41351c86551..1e3c58cc66a 100644 --- a/third_party/libosmium/include/osmium/index/map/sparse_mem_map.hpp +++ b/third_party/libosmium/include/osmium/index/map/sparse_mem_map.hpp @@ -37,7 +37,6 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include @@ -98,7 +97,7 @@ namespace osmium { } void dump_as_list(const int fd) final { - typedef typename std::map::value_type t; + using t = typename std::map::value_type; std::vector v; v.reserve(m_elements.size()); std::copy(m_elements.cbegin(), m_elements.cend(), std::back_inserter(v)); diff --git a/third_party/libosmium/include/osmium/index/multimap.hpp b/third_party/libosmium/include/osmium/index/multimap.hpp index de6aa1eb436..9361d6a7695 100644 --- a/third_party/libosmium/include/osmium/index/multimap.hpp +++ b/third_party/libosmium/include/osmium/index/multimap.hpp @@ -52,7 +52,7 @@ namespace osmium { static_assert(std::is_integral::value && std::is_unsigned::value, "TId template parameter for class Multimap must be unsigned integral type"); - typedef typename std::pair element_type; + using element_type = typename std::pair; 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 get_all(const TId id) const = 0; diff --git a/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp b/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp index ba9430232db..06a5f6e7b8c 100644 --- a/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp +++ b/third_party/libosmium/include/osmium/index/multimap/hybrid.hpp @@ -50,10 +50,10 @@ namespace osmium { template class HybridIterator { - typedef SparseMemArray main_map_type; - typedef SparseMemMultimap extra_map_type; + using main_map_type = SparseMemArray; + using extra_map_type = SparseMemMultimap; - typedef typename std::pair element_type; + using element_type = typename std::pair; typename main_map_type::iterator m_begin_main; typename main_map_type::iterator m_end_main; @@ -120,16 +120,16 @@ namespace osmium { template class Hybrid : public Multimap { - typedef SparseMemArray main_map_type; - typedef SparseMemMultimap extra_map_type; + using main_map_type = SparseMemArray; + using extra_map_type = SparseMemMultimap; main_map_type m_main; extra_map_type m_extra; public: - typedef HybridIterator iterator; - typedef const HybridIterator const_iterator; + using iterator = HybridIterator; + using const_iterator = const HybridIterator; Hybrid() : m_main(), diff --git a/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp b/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp index 0859fca21a8..7a67cc7ccbc 100644 --- a/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp +++ b/third_party/libosmium/include/osmium/index/multimap/sparse_mem_multimap.hpp @@ -63,12 +63,11 @@ namespace osmium { public: - typedef typename std::multimap 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 element_type; + using collection_type = typename std::multimap; + 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; private: diff --git a/third_party/libosmium/include/osmium/io/any_input.hpp b/third_party/libosmium/include/osmium/io/any_input.hpp index dd8445132e7..e4617e8b5a2 100644 --- a/third_party/libosmium/include/osmium/io/any_input.hpp +++ b/third_party/libosmium/include/osmium/io/any_input.hpp @@ -45,8 +45,9 @@ DEALINGS IN THE SOFTWARE. #include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export -#include // IWYU pragma: export #endif // OSMIUM_IO_ANY_INPUT_HPP diff --git a/third_party/libosmium/include/osmium/io/bzip2_compression.hpp b/third_party/libosmium/include/osmium/io/bzip2_compression.hpp index e5cad0be3a6..63b4cadf923 100644 --- a/third_party/libosmium/include/osmium/io/bzip2_compression.hpp +++ b/third_party/libosmium/include/osmium/io/bzip2_compression.hpp @@ -43,10 +43,9 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include -#include #include +#include #include @@ -55,6 +54,7 @@ DEALINGS IN THE SOFTWARE. #endif #include +#include #include #include #include @@ -109,7 +109,7 @@ namespace osmium { explicit Bzip2Compressor(int fd, fsync sync) : Compressor(sync), - m_file(fdopen(dup(fd), "wb")), + m_file(fdopen(::dup(fd), "wb")), m_bzerror(BZ_OK), m_bzfile(::BZ2_bzWriteOpen(&m_bzerror, m_file, 6, 0, 0)) { if (!m_bzfile) { @@ -165,7 +165,7 @@ namespace osmium { explicit Bzip2Decompressor(int fd) : Decompressor(), - m_file(fdopen(dup(fd), "rb")), + m_file(fdopen(::dup(fd), "rb")), m_bzerror(BZ_OK), m_bzfile(::BZ2_bzReadOpen(&m_bzerror, m_file, 0, 0, nullptr, 0)) { if (!m_bzfile) { @@ -215,6 +215,8 @@ namespace osmium { buffer.resize(static_cast(nread)); } + set_offset(size_t(ftell(m_file))); + return buffer; } diff --git a/third_party/libosmium/include/osmium/io/compression.hpp b/third_party/libosmium/include/osmium/io/compression.hpp index 7d95661bdae..e7f93bd5ea0 100644 --- a/third_party/libosmium/include/osmium/io/compression.hpp +++ b/third_party/libosmium/include/osmium/io/compression.hpp @@ -33,11 +33,12 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include #include #include -#include #include #include #include @@ -54,6 +55,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { @@ -86,6 +88,9 @@ namespace osmium { class Decompressor { + std::atomic m_file_size {0}; + std::atomic m_offset {0}; + public: static constexpr unsigned int input_buffer_size = 1024 * 1024; @@ -105,6 +110,22 @@ namespace osmium { virtual void close() = 0; + size_t file_size() const noexcept { + return m_file_size; + } + + void set_file_size(size_t size) noexcept { + m_file_size = size; + } + + size_t offset() const noexcept { + return m_offset; + } + + void set_offset(size_t offset) noexcept { + m_offset = offset; + } + }; // class Decompressor /** @@ -118,16 +139,16 @@ namespace osmium { public: - typedef std::function create_compressor_type; - typedef std::function create_decompressor_type_fd; - typedef std::function create_decompressor_type_buffer; + using create_compressor_type = std::function; + using create_decompressor_type_fd = std::function; + using create_decompressor_type_buffer = std::function; private: - typedef std::map> compression_map_type; + using compression_map_type = std::map>; compression_map_type m_callbacks; @@ -182,7 +203,9 @@ namespace osmium { auto it = m_callbacks.find(compression); if (it != m_callbacks.end()) { - return std::unique_ptr(std::get<1>(it->second)(fd)); + auto p = std::unique_ptr(std::get<1>(it->second)(fd)); + p->set_file_size(osmium::util::file_size(fd)); + return p; } error(compression); @@ -241,6 +264,7 @@ namespace osmium { int m_fd; const char *m_buffer; size_t m_buffer_size; + size_t m_offset = 0; public: @@ -284,6 +308,9 @@ namespace osmium { buffer.resize(std::string::size_type(nread)); } + m_offset += buffer.size(); + set_offset(m_offset); + return buffer; } diff --git a/third_party/libosmium/include/osmium/io/detail/debug_output_format.hpp b/third_party/libosmium/include/osmium/io/detail/debug_output_format.hpp index 3f7537ea624..dc5323ad3ec 100644 --- a/third_party/libosmium/include/osmium/io/detail/debug_output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/debug_output_format.hpp @@ -34,29 +34,35 @@ DEALINGS IN THE SOFTWARE. */ #include -#include -#include -#include -#include +#include +#include #include #include #include -#include #include +#include + #include +#include +#include +#include #include +#include #include -#include +#include #include #include +#include #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -66,8 +72,6 @@ namespace osmium { namespace io { - class File; - namespace detail { constexpr const char* color_bold = "\x1b[1m"; @@ -80,6 +84,10 @@ namespace osmium { constexpr const char* color_magenta = "\x1b[35m"; constexpr const char* color_cyan = "\x1b[36m"; constexpr const char* color_white = "\x1b[37m"; + + constexpr const char* color_backg_red = "\x1b[41m"; + constexpr const char* color_backg_green = "\x1b[42m"; + constexpr const char* color_reset = "\x1b[0m"; struct debug_output_options { @@ -90,6 +98,11 @@ namespace osmium { /// Output with ANSI colors? bool use_color; + /// Add CRC32 checksum to each object? + bool add_crc32; + + /// Write in form of a diff file? + bool format_as_diff; }; /** @@ -102,16 +115,47 @@ namespace osmium { const char* m_utf8_prefix = ""; const char* m_utf8_suffix = ""; + char m_diff_char = '\0'; + void append_encoded_string(const char* data) { append_debug_encoded_string(*m_out, data, m_utf8_prefix, m_utf8_suffix); } + template + void output_formatted(const char* format, TArgs&&... args) { + append_printf_formatted_string(*m_out, format, std::forward(args)...); + } + void write_color(const char* color) { if (m_options.use_color) { *m_out += color; } } + void write_diff() { + if (!m_diff_char) { + return; + } + if (m_options.use_color) { + if (m_diff_char == '-') { + *m_out += color_backg_red; + *m_out += color_white; + *m_out += color_bold; + *m_out += '-'; + *m_out += color_reset; + return; + } else if (m_diff_char == '+') { + *m_out += color_backg_green; + *m_out += color_white; + *m_out += color_bold; + *m_out += '+'; + *m_out += color_reset; + return; + } + } + *m_out += m_diff_char; + } + void write_string(const char* string) { *m_out += '"'; write_color(color_blue); @@ -121,6 +165,7 @@ namespace osmium { } void write_object_type(const char* object_type, bool visible = true) { + write_diff(); if (visible) { write_color(color_bold); } else { @@ -132,6 +177,7 @@ namespace osmium { } void write_fieldname(const char* name) { + write_diff(); *m_out += " "; write_color(color_cyan); *m_out += name; @@ -161,7 +207,9 @@ namespace osmium { void write_timestamp(const osmium::Timestamp& timestamp) { if (timestamp.valid()) { *m_out += timestamp.to_iso(); - output_formatted(" (%d)", timestamp.seconds_since_epoch()); + *m_out += " ("; + output_int(timestamp.seconds_since_epoch()); + *m_out += ')'; } else { write_error("NOT SET"); } @@ -169,53 +217,63 @@ namespace osmium { } void write_meta(const osmium::OSMObject& object) { - output_formatted("%" PRId64 "\n", object.id()); + output_int(object.id()); + *m_out += '\n'; if (m_options.add_metadata) { write_fieldname("version"); - output_formatted(" %d", object.version()); + *m_out += " "; + output_int(object.version()); if (object.visible()) { *m_out += " visible\n"; } else { write_error(" deleted\n"); } write_fieldname("changeset"); - output_formatted("%d\n", object.changeset()); + output_int(object.changeset()); + *m_out += '\n'; write_fieldname("timestamp"); write_timestamp(object.timestamp()); write_fieldname("user"); - output_formatted(" %d ", object.uid()); + *m_out += " "; + output_int(object.uid()); + *m_out += ' '; write_string(object.user()); *m_out += '\n'; } } void write_tags(const osmium::TagList& tags, const char* padding="") { - if (!tags.empty()) { - write_fieldname("tags"); - *m_out += padding; - output_formatted(" %d\n", tags.size()); - - osmium::max_op max; - for (const auto& tag : tags) { - max.update(std::strlen(tag.key())); - } - for (const auto& tag : tags) { - *m_out += " "; - write_string(tag.key()); - auto spacing = max() - std::strlen(tag.key()); - while (spacing--) { - *m_out += " "; - } - *m_out += " = "; - write_string(tag.value()); - *m_out += '\n'; + if (tags.empty()) { + return; + } + write_fieldname("tags"); + *m_out += padding; + *m_out += " "; + output_int(tags.size()); + *m_out += '\n'; + + osmium::max_op max; + for (const auto& tag : tags) { + max.update(std::strlen(tag.key())); + } + for (const auto& tag : tags) { + write_diff(); + *m_out += " "; + write_string(tag.key()); + auto spacing = max() - std::strlen(tag.key()); + while (spacing--) { + *m_out += " "; } + *m_out += " = "; + write_string(tag.value()); + *m_out += '\n'; } } void write_location(const osmium::Location& location) { write_fieldname("lon/lat"); - output_formatted(" %.7f,%.7f", location.lon_without_check(), location.lat_without_check()); + *m_out += " "; + location.as_string_without_check(std::back_inserter(*m_out)); if (!location.valid()) { write_error(" INVALID LOCATION!"); } @@ -230,13 +288,30 @@ namespace osmium { } const auto& bl = box.bottom_left(); const auto& tr = box.top_right(); - output_formatted("%.7f,%.7f %.7f,%.7f", bl.lon_without_check(), bl.lat_without_check(), tr.lon_without_check(), tr.lat_without_check()); + bl.as_string(std::back_inserter(*m_out)); + *m_out += ' '; + tr.as_string(std::back_inserter(*m_out)); if (!box.valid()) { write_error(" INVALID BOX!"); } *m_out += '\n'; } + template + void write_crc32(const T& object) { + write_fieldname("crc32"); + osmium::CRC crc32; + crc32.update(object); + output_formatted(" %x\n", crc32().checksum()); + } + + void write_crc32(const osmium::Changeset& object) { + write_fieldname("crc32"); + osmium::CRC crc32; + crc32.update(object); + output_formatted(" %x\n", crc32().checksum()); + } + public: DebugOutputBlock(osmium::memory::Buffer&& buffer, const debug_output_options& options) : @@ -265,6 +340,8 @@ namespace osmium { } void node(const osmium::Node& node) { + m_diff_char = m_options.format_as_diff ? node.diff_as_char() : '\0'; + write_object_type("node", node.visible()); write_meta(node); @@ -274,17 +351,24 @@ namespace osmium { write_tags(node.tags()); + if (m_options.add_crc32) { + write_crc32(node); + } + *m_out += '\n'; } void way(const osmium::Way& way) { + m_diff_char = m_options.format_as_diff ? way.diff_as_char() : '\0'; + write_object_type("way", way.visible()); write_meta(way); write_tags(way.tags()); write_fieldname("nodes"); - output_formatted(" %d", way.nodes().size()); + *m_out += " "; + output_int(way.nodes().size()); if (way.nodes().size() < 2) { write_error(" LESS THAN 2 NODES!\n"); } else if (way.nodes().size() > 2000) { @@ -295,32 +379,45 @@ namespace osmium { *m_out += " (open)\n"; } - int width = int(log10(way.nodes().size())) + 1; + const int width = int(std::log10(way.nodes().size())) + 1; int n = 0; for (const auto& node_ref : way.nodes()) { + write_diff(); write_counter(width, n++); output_formatted("%10" PRId64, node_ref.ref()); if (node_ref.location().valid()) { - output_formatted(" (%.7f,%.7f)", node_ref.location().lon_without_check(), node_ref.location().lat_without_check()); + *m_out += " ("; + node_ref.location().as_string(std::back_inserter(*m_out)); + *m_out += ')'; } *m_out += '\n'; } + if (m_options.add_crc32) { + write_crc32(way); + } + *m_out += '\n'; } void relation(const osmium::Relation& relation) { static const char* short_typename[] = { "node", "way ", "rel " }; + + m_diff_char = m_options.format_as_diff ? relation.diff_as_char() : '\0'; + write_object_type("relation", relation.visible()); write_meta(relation); write_tags(relation.tags()); write_fieldname("members"); - output_formatted(" %d\n", relation.members().size()); + *m_out += " "; + output_int(relation.members().size()); + *m_out += '\n'; - int width = int(log10(relation.members().size())) + 1; + const int width = int(std::log10(relation.members().size())) + 1; int n = 0; for (const auto& member : relation.members()) { + write_diff(); write_counter(width, n++); *m_out += short_typename[item_type_to_nwr_index(member.type())]; output_formatted(" %10" PRId64 " ", member.ref()); @@ -328,15 +425,20 @@ namespace osmium { *m_out += '\n'; } + if (m_options.add_crc32) { + write_crc32(relation); + } + *m_out += '\n'; } void changeset(const osmium::Changeset& changeset) { write_object_type("changeset"); - output_formatted("%d\n", changeset.id()); + output_int(changeset.id()); + *m_out += '\n'; write_fieldname("num changes"); - output_formatted("%d", changeset.num_changes()); + output_int(changeset.num_changes()); if (changeset.num_changes() == 0) { write_error(" NO CHANGES!"); } @@ -355,7 +457,9 @@ namespace osmium { } write_fieldname("user"); - output_formatted(" %d ", changeset.uid()); + *m_out += " "; + output_int(changeset.uid()); + *m_out += ' '; write_string(changeset.user()); *m_out += '\n'; @@ -364,9 +468,11 @@ namespace osmium { if (changeset.num_comments() > 0) { write_fieldname("comments"); - output_formatted(" %d\n", changeset.num_comments()); + *m_out += " "; + output_int(changeset.num_comments()); + *m_out += '\n'; - int width = int(log10(changeset.num_comments())) + 1; + const int width = int(std::log10(changeset.num_comments())) + 1; int n = 0; for (const auto& comment : changeset.discussion()) { write_counter(width, n++); @@ -376,7 +482,8 @@ namespace osmium { output_formatted(" %*s", width, ""); write_comment_field("user"); - output_formatted("%d ", comment.uid()); + output_int(comment.uid()); + *m_out += ' '; write_string(comment.user()); output_formatted("\n %*s", width, ""); @@ -386,6 +493,10 @@ namespace osmium { } } + if (m_options.add_crc32) { + write_crc32(changeset); + } + *m_out += '\n'; } @@ -412,8 +523,10 @@ namespace osmium { DebugOutputFormat(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_color = file.is_true("color"); + 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"); + m_options.format_as_diff = file.is_true("diff"); } DebugOutputFormat(const DebugOutputFormat&) = delete; @@ -422,6 +535,10 @@ namespace osmium { ~DebugOutputFormat() noexcept final = default; void write_header(const osmium::io::Header& header) final { + if (m_options.format_as_diff) { + return; + } + std::string out; if (m_options.use_color) { @@ -439,9 +556,9 @@ namespace osmium { out += '\n'; for (const auto& box : header.boxes()) { out += " "; - box.bottom_left().as_string(std::back_inserter(out), ','); - out += " "; - box.top_right().as_string(std::back_inserter(out), ','); + box.bottom_left().as_string(std::back_inserter(out)); + out += ' '; + box.top_right().as_string(std::back_inserter(out)); out += '\n'; } write_fieldname(out, "options"); diff --git a/third_party/libosmium/include/osmium/io/detail/input_format.hpp b/third_party/libosmium/include/osmium/io/detail/input_format.hpp index 0d788513161..98081e3c984 100644 --- a/third_party/libosmium/include/osmium/io/detail/input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/input_format.hpp @@ -38,11 +38,11 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include #include +#include #include #include #include @@ -154,18 +154,14 @@ namespace osmium { public: - typedef std::function< - std::unique_ptr( - future_string_queue_type&, - future_buffer_queue_type&, - std::promise& header_promise, - osmium::osm_entity_bits::type read_which_entities - ) - > create_parser_type; + using create_parser_type = std::function(future_string_queue_type&, + future_buffer_queue_type&, + std::promise& header_promise, + osmium::osm_entity_bits::type read_which_entities)>; private: - typedef std::map map_type; + using map_type = std::map; map_type m_callbacks; diff --git a/third_party/libosmium/include/osmium/io/detail/o5m_input_format.hpp b/third_party/libosmium/include/osmium/io/detail/o5m_input_format.hpp index 7293cfa7324..d6428790a9e 100644 --- a/third_party/libosmium/include/osmium/io/detail/o5m_input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/o5m_input_format.hpp @@ -42,9 +42,9 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include -#include #include #include #include @@ -52,19 +52,26 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include #include #include +#include #include +#include +#include #include +#include #include #include #include namespace osmium { + namespace builder { + class Builder; + } // namespace builder + /** * Exception thrown when the o5m deocder failed. The exception contains * (if available) information about the place where the error happened @@ -529,7 +536,7 @@ namespace osmium { uint64_t length = 0; try { length = protozero::decode_varint(&m_data, m_end); - } catch (protozero::end_of_buffer_exception&) { + } catch (const protozero::end_of_buffer_exception&) { throw o5m_error("premature end of file"); } diff --git a/third_party/libosmium/include/osmium/io/detail/opl_input_format.hpp b/third_party/libosmium/include/osmium/io/detail/opl_input_format.hpp new file mode 100644 index 00000000000..15a31f38a5f --- /dev/null +++ b/third_party/libosmium/include/osmium/io/detail/opl_input_format.hpp @@ -0,0 +1,156 @@ +#ifndef OSMIUM_IO_DETAIL_OPL_INPUT_FORMAT_HPP +#define OSMIUM_IO_DETAIL_OPL_INPUT_FORMAT_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osmium { + + namespace io { + + namespace detail { + + class OPLParser : public Parser { + + osmium::memory::Buffer m_buffer{1024*1024}; + const char* m_data = nullptr; + uint64_t m_line_count = 0; + + void maybe_flush() { + if (m_buffer.committed() > 800*1024) { + osmium::memory::Buffer buffer{1024*1024}; + using std::swap; + swap(m_buffer, buffer); + send_to_output_queue(std::move(buffer)); + + } + } + + void parse_line() { + if (opl_parse_line(m_line_count, m_data, m_buffer, read_types())) { + maybe_flush(); + } + ++m_line_count; + } + + public: + + OPLParser(future_string_queue_type& input_queue, + future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_types) : + Parser(input_queue, output_queue, header_promise, read_types) { + set_header_value(osmium::io::Header{}); + } + + ~OPLParser() noexcept final = default; + + void run() final { + osmium::thread::set_thread_name("_osmium_opl_in"); + + std::string rest; + while (!input_done()) { + std::string input = get_input(); + std::string::size_type ppos = 0; + + if (!rest.empty()) { + ppos = input.find('\n'); + if (ppos == std::string::npos) { + rest.append(input); + continue; + } + rest.append(input.substr(0, ppos)); + m_data = rest.data(); + parse_line(); + rest.clear(); + } + + std::string::size_type pos = input.find('\n', ppos); + while (pos != std::string::npos) { + m_data = &input[ppos]; + input[pos] = '\0'; + parse_line(); + ppos = pos + 1; + if (ppos >= input.size()) { + break; + } + pos = input.find('\n', ppos); + } + rest = input.substr(ppos); + } + + if (m_buffer.committed() > 0) { + send_to_output_queue(std::move(m_buffer)); + } + } + + }; // class OPLParser + + // we want the register_parser() function to run, setting + // the variable is only a side-effect, it will never be used + const bool registered_opl_parser = ParserFactory::instance().register_parser( + file_format::opl, + [](future_string_queue_type& input_queue, + future_buffer_queue_type& output_queue, + std::promise& header_promise, + osmium::osm_entity_bits::type read_which_entities) { + return std::unique_ptr(new OPLParser(input_queue, output_queue, header_promise, read_which_entities)); + }); + + // dummy function to silence the unused variable warning from above + inline bool get_registered_opl_parser() noexcept { + return registered_opl_parser; + } + + } // namespace detail + + } // namespace io + +} // namespace osmium + + +#endif // OSMIUM_IO_DETAIL_OPL_INPUT_FORMAT_HPP diff --git a/third_party/libosmium/include/osmium/io/detail/opl_output_format.hpp b/third_party/libosmium/include/osmium/io/detail/opl_output_format.hpp index 3fedf7e9c7d..acdfe29afb1 100644 --- a/third_party/libosmium/include/osmium/io/detail/opl_output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/opl_output_format.hpp @@ -33,26 +33,26 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include #include -#include -#include #include #include #include -#include #include #include +#include +#include +#include #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -65,8 +65,6 @@ namespace osmium { namespace io { - class File; - namespace detail { struct opl_output_options { @@ -74,6 +72,12 @@ namespace osmium { /// Should metadata of objects be added? bool add_metadata; + /// Should node locations be added to ways? + bool locations_on_ways; + + /// Write in form of a diff file? + bool format_as_diff; + }; /** @@ -87,38 +91,71 @@ namespace osmium { osmium::io::detail::append_utf8_encoded_string(*m_out, data); } + void write_field_int(char c, int64_t value) { + *m_out += c; + output_int(value); + } + + void write_field_timestamp(char c, const osmium::Timestamp& timestamp) { + *m_out += c; + *m_out += timestamp.to_iso(); + } + + void write_tags(const osmium::TagList& tags) { + *m_out += " T"; + + if (tags.empty()) { + return; + } + + auto it = tags.begin(); + append_encoded_string(it->key()); + *m_out += '='; + append_encoded_string(it->value()); + + for (++it; it != tags.end(); ++it) { + *m_out += ','; + append_encoded_string(it->key()); + *m_out += '='; + append_encoded_string(it->value()); + } + } + void write_meta(const osmium::OSMObject& object) { - output_formatted("%" PRId64, object.id()); + output_int(object.id()); if (m_options.add_metadata) { - output_formatted(" v%d d", object.version()); + *m_out += ' '; + write_field_int('v', object.version()); + *m_out += " d"; *m_out += (object.visible() ? 'V' : 'D'); - output_formatted(" c%d t", object.changeset()); - *m_out += object.timestamp().to_iso(); - output_formatted(" i%d u", object.uid()); + *m_out += ' '; + write_field_int('c', object.changeset()); + *m_out += ' '; + write_field_timestamp('t', object.timestamp()); + *m_out += ' '; + write_field_int('i', object.uid()); + *m_out += " u"; append_encoded_string(object.user()); } - *m_out += " T"; - bool first = true; - for (const auto& tag : object.tags()) { - if (first) { - first = false; - } else { - *m_out += ','; - } - append_encoded_string(tag.key()); - *m_out += '='; - append_encoded_string(tag.value()); - } + write_tags(object.tags()); } void write_location(const osmium::Location& location, const char x, const char y) { + *m_out += ' '; + *m_out += x; if (location) { - output_formatted(" %c%.7f %c%.7f", x, location.lon_without_check(), y, location.lat_without_check()); - } else { - *m_out += ' '; - *m_out += x; - *m_out += ' '; - *m_out += y; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(*m_out), location.x()); + } + *m_out += ' '; + *m_out += y; + if (location) { + osmium::detail::append_location_coordinate_to_string(std::back_inserter(*m_out), location.y()); + } + } + + void write_diff(const osmium::OSMObject& object) { + if (m_options.format_as_diff) { + *m_out += object.diff_as_char(); } } @@ -148,70 +185,93 @@ namespace osmium { } void node(const osmium::Node& node) { + write_diff(node); *m_out += 'n'; write_meta(node); write_location(node.location(), 'x', 'y'); *m_out += '\n'; } + void write_field_ref(const osmium::NodeRef& node_ref) { + write_field_int('n', node_ref.ref()); + *m_out += 'x'; + if (node_ref.location()) { + node_ref.location().as_string(std::back_inserter(*m_out), 'y'); + } else { + *m_out += 'y'; + } + } + void way(const osmium::Way& way) { + write_diff(way); *m_out += 'w'; write_meta(way); *m_out += " N"; - bool first = true; - for (const auto& node_ref : way.nodes()) { - if (first) { - first = false; + + if (!way.nodes().empty()) { + auto it = way.nodes().begin(); + if (m_options.locations_on_ways) { + write_field_ref(*it); + for (++it; it != way.nodes().end(); ++it) { + *m_out += ','; + write_field_ref(*it); + } } else { - *m_out += ','; + write_field_int('n', it->ref()); + for (++it; it != way.nodes().end(); ++it) { + *m_out += ','; + write_field_int('n', it->ref()); + } } - output_formatted("n%" PRId64, node_ref.ref()); } + *m_out += '\n'; } + void relation_member(const osmium::RelationMember& member) { + *m_out += item_type_to_char(member.type()); + output_int(member.ref()); + *m_out += '@'; + append_encoded_string(member.role()); + } + void relation(const osmium::Relation& relation) { + write_diff(relation); *m_out += 'r'; write_meta(relation); *m_out += " M"; - bool first = true; - for (const auto& member : relation.members()) { - if (first) { - first = false; - } else { + + if (!relation.members().empty()) { + auto it = relation.members().begin(); + relation_member(*it); + for (++it; it != relation.members().end(); ++it) { *m_out += ','; + relation_member(*it); } - *m_out += item_type_to_char(member.type()); - output_formatted("%" PRId64 "@", member.ref()); - append_encoded_string(member.role()); } + *m_out += '\n'; } void changeset(const osmium::Changeset& changeset) { - output_formatted("c%d k%d s", changeset.id(), changeset.num_changes()); - *m_out += changeset.created_at().to_iso(); - *m_out += " e"; - *m_out += changeset.closed_at().to_iso(); - output_formatted(" d%d i%d u", changeset.num_comments(), changeset.uid()); + write_field_int('c', changeset.id()); + *m_out += ' '; + write_field_int('k', changeset.num_changes()); + *m_out += ' '; + write_field_timestamp('s', changeset.created_at()); + *m_out += ' '; + write_field_timestamp('e', changeset.closed_at()); + *m_out += ' '; + write_field_int('d', changeset.num_comments()); + *m_out += ' '; + write_field_int('i', changeset.uid()); + *m_out += " u"; append_encoded_string(changeset.user()); write_location(changeset.bounds().bottom_left(), 'x', 'y'); write_location(changeset.bounds().top_right(), 'X', 'Y'); - *m_out += " T"; - bool first = true; - for (const auto& tag : changeset.tags()) { - if (first) { - first = false; - } else { - *m_out += ','; - } - append_encoded_string(tag.key()); - *m_out += '='; - append_encoded_string(tag.value()); - } - + write_tags(changeset.tags()); *m_out += '\n'; } @@ -226,7 +286,9 @@ 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"); + m_options.format_as_diff = file.is_true("diff"); } OPLOutputFormat(const OPLOutputFormat&) = delete; diff --git a/third_party/libosmium/include/osmium/io/detail/opl_parser_functions.hpp b/third_party/libosmium/include/osmium/io/detail/opl_parser_functions.hpp new file mode 100644 index 00000000000..97c59275a09 --- /dev/null +++ b/third_party/libosmium/include/osmium/io/detail/opl_parser_functions.hpp @@ -0,0 +1,747 @@ +#ifndef OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP +#define OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osmium { + + namespace builder { + class Builder; + } // namespace builder + + /** + * Exception thrown when there was a problem with parsing the OPL format + * of a file. + */ + struct opl_error : public io_error { + + uint64_t line = 0; + uint64_t column = 0; + const char* data; + std::string msg; + + explicit opl_error(const std::string& what, const char* d = nullptr) : + io_error(std::string("OPL error: ") + what), + data(d), + msg("OPL error: ") { + msg.append(what); + } + + explicit opl_error(const char* what, const char* d = nullptr) : + io_error(std::string("OPL error: ") + what), + data(d), + msg("OPL error: ") { + msg.append(what); + } + + void set_pos(uint64_t l, uint64_t col) { + line = l; + column = col; + msg.append(" on line "); + msg.append(std::to_string(line)); + msg.append(" column "); + msg.append(std::to_string(column)); + } + + const char* what() const noexcept override { + return msg.c_str(); + } + + }; // struct opl_error + + namespace io { + + namespace detail { + + /** + * Consume consecutive space and tab characters. There must be + * at least one. + */ + inline void opl_parse_space(const char** s) { + if (**s != ' ' && **s != '\t') { + throw opl_error{"expected space or tab character", *s}; + } + do { + ++*s; + } while (**s == ' ' || **s == '\t'); + } + + /** + * Check whether s points to something else than the end of the + * string or a space or tab. + */ + inline bool opl_non_empty(const char *s) { + return *s != '\0' && *s != ' ' && *s != '\t'; + } + + /** + * Skip to the next space or tab character or the end of the + * string. + */ + inline const char* opl_skip_section(const char** s) noexcept { + while (opl_non_empty(*s)) { + ++*s; + } + return *s; + } + + /** + * Parse OPL-escaped strings with hex code with a '%' at the end. + * Appends resulting unicode character to the result string. + * + * Returns a pointer to next character that needs to be consumed. + */ + inline void opl_parse_escaped(const char** data, std::string& result) { + const char* s = *data; + uint32_t value = 0; + const int max_length = sizeof(value) * 2 /* hex chars per byte */; + int length = 0; + while (++length <= max_length) { + if (*s == '\0') { + throw opl_error{"eol", s}; + } + if (*s == '%') { + ++s; + utf8::utf32to8(&value, &value + 1, std::back_inserter(result)); + *data = s; + return; + } + value <<= 4; + if (*s >= '0' && *s <= '9') { + value += *s - '0'; + } else if (*s >= 'a' && *s <= 'f') { + value += *s - 'a' + 10; + } else if (*s >= 'A' && *s <= 'F') { + value += *s - 'A' + 10; + } else { + throw opl_error{"not a hex char", s}; + } + ++s; + } + throw opl_error{"hex escape too long", s}; + } + + /** + * Parse a string up to end of string or next space, tab, comma, or + * equal sign. + * + * Appends characters to the result string. + * + * Returns a pointer to next character that needs to be consumed. + */ + inline void opl_parse_string(const char** data, std::string& result) { + const char* s = *data; + while (true) { + if (*s == '\0' || *s == ' ' || *s == '\t' || *s == ',' || *s == '=') { + break; + } else if (*s == '%') { + ++s; + opl_parse_escaped(&s, result); + } else { + result += *s; + ++s; + } + } + *data = s; + } + + // Arbitrary limit how long integers can get + constexpr const int max_int_len = 16; + + template + inline T opl_parse_int(const char** s) { + if (**s == '\0') { + throw opl_error{"expected integer", *s}; + } + const bool negative = (**s == '-'); + if (negative) { + ++*s; + } + + int64_t value = 0; + + int n = max_int_len; + while (**s >= '0' && **s <= '9') { + if (--n == 0) { + throw opl_error{"integer too long", *s}; + } + value *= 10; + value += **s - '0'; + ++*s; + } + + if (n == max_int_len) { + throw opl_error{"expected integer", *s}; + } + + if (negative) { + value = -value; + if (value < std::numeric_limits::min()) { + throw opl_error{"integer too long", *s}; + } + } else { + if (value > std::numeric_limits::max()) { + throw opl_error{"integer too long", *s}; + } + } + + return T(value); + } + + inline osmium::object_id_type opl_parse_id(const char** s) { + return opl_parse_int(s); + } + + inline osmium::changeset_id_type opl_parse_changeset_id(const char** s) { + return opl_parse_int(s); + } + + inline osmium::object_version_type opl_parse_version(const char** s) { + return opl_parse_int(s); + } + + inline bool opl_parse_visible(const char** data) { + if (**data == 'V') { + ++*data; + return true; + } + if (**data == 'D') { + ++*data; + return false; + } + throw opl_error{"invalid visible flag", *data}; + } + + inline osmium::user_id_type opl_parse_uid(const char** s) { + return opl_parse_int(s); + } + + inline osmium::Timestamp opl_parse_timestamp(const char** s) { + try { + if (**s == '\0' || **s == ' ' || **s == '\t') { + return osmium::Timestamp{}; + } + osmium::Timestamp timestamp{*s}; + *s += 20; + return timestamp; + } catch (const std::invalid_argument&) { + throw opl_error{"can not parse timestamp", *s}; + } + } + + /** + * Check if data points to given character and consume it. + * Throw error otherwise. + */ + inline void opl_parse_char(const char** data, char c) { + if (**data == c) { + ++*data; + return; + } + std::string msg = "expected '"; + msg += c; + msg += "'"; + throw opl_error{msg, *data}; + } + + /** + * Parse a list of tags in the format 'key=value,key=value,...' + * + * Tags will be added to the buffer using a TagListBuilder. + */ + inline void opl_parse_tags(const char* s, osmium::memory::Buffer& buffer, osmium::builder::Builder* parent_builder = nullptr) { + osmium::builder::TagListBuilder builder{buffer, parent_builder}; + std::string key; + std::string value; + while (true) { + opl_parse_string(&s, key); + opl_parse_char(&s, '='); + opl_parse_string(&s, value); + builder.add_tag(key, value); + if (*s == ' ' || *s == '\t' || *s == '\0') { + break; + } + opl_parse_char(&s, ','); + key.clear(); + value.clear(); + } + } + + /** + * Parse a number of nodes in the format "nID,nID,nID..." + * + * Nodes will be added to the buffer using a WayNodeListBuilder. + */ + inline void opl_parse_way_nodes(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::WayBuilder* parent_builder = nullptr) { + if (s == e) { + return; + } + osmium::builder::WayNodeListBuilder builder{buffer, parent_builder}; + + while (s < e) { + opl_parse_char(&s, 'n'); + if (s == e) { + throw opl_error{"expected integer", s}; + } + + const osmium::object_id_type ref = opl_parse_id(&s); + if (s == e) { + builder.add_node_ref(ref); + return; + } + + osmium::Location location; + if (*s == 'x') { + ++s; + location.set_lon_partial(&s); + if (*s == 'y') { + ++s; + location.set_lat_partial(&s); + } + } + + builder.add_node_ref(ref, location); + + if (s == e) { + return; + } + + opl_parse_char(&s, ','); + } + } + + inline void opl_parse_node(const char** data, osmium::memory::Buffer& buffer) { + osmium::builder::NodeBuilder builder{buffer}; + osmium::Node& node = builder.object(); + + node.set_id(opl_parse_id(data)); + + const char* tags_begin = nullptr; + + std::string user; + osmium::Location location; + while (**data) { + opl_parse_space(data); + const char c = **data; + if (c == '\0') { + break; + } + ++(*data); + switch (c) { + case 'v': + node.set_version(opl_parse_version(data)); + break; + case 'd': + node.set_visible(opl_parse_visible(data)); + break; + case 'c': + node.set_changeset(opl_parse_changeset_id(data)); + break; + case 't': + node.set_timestamp(opl_parse_timestamp(data)); + break; + case 'i': + node.set_uid(opl_parse_uid(data)); + break; + case 'u': + opl_parse_string(data, user); + break; + case 'T': + if (opl_non_empty(*data)) { + tags_begin = *data; + opl_skip_section(data); + } + break; + case 'x': + if (opl_non_empty(*data)) { + location.set_lon_partial(data); + } + break; + case 'y': + if (opl_non_empty(*data)) { + location.set_lat_partial(data); + } + break; + default: + --(*data); + throw opl_error{"unknown attribute", *data}; + } + } + + if (location.valid()) { + node.set_location(location); + } + + builder.add_user(user); + + if (tags_begin) { + opl_parse_tags(tags_begin, buffer, &builder); + } + + buffer.commit(); + } + + inline void opl_parse_way(const char** data, osmium::memory::Buffer& buffer) { + osmium::builder::WayBuilder builder{buffer}; + osmium::Way& way = builder.object(); + + way.set_id(opl_parse_id(data)); + + const char* tags_begin = nullptr; + + const char* nodes_begin = nullptr; + const char* nodes_end = nullptr; + + std::string user; + while (**data) { + opl_parse_space(data); + const char c = **data; + if (c == '\0') { + break; + } + ++(*data); + switch (c) { + case 'v': + way.set_version(opl_parse_version(data)); + break; + case 'd': + way.set_visible(opl_parse_visible(data)); + break; + case 'c': + way.set_changeset(opl_parse_changeset_id(data)); + break; + case 't': + way.set_timestamp(opl_parse_timestamp(data)); + break; + case 'i': + way.set_uid(opl_parse_uid(data)); + break; + case 'u': + opl_parse_string(data, user); + break; + case 'T': + if (opl_non_empty(*data)) { + tags_begin = *data; + opl_skip_section(data); + } + break; + case 'N': + nodes_begin = *data; + nodes_end = opl_skip_section(data); + break; + default: + --(*data); + throw opl_error{"unknown attribute", *data}; + } + } + + builder.add_user(user); + + if (tags_begin) { + opl_parse_tags(tags_begin, buffer, &builder); + } + + opl_parse_way_nodes(nodes_begin, nodes_end, buffer, &builder); + + buffer.commit(); + } + + inline void opl_parse_relation_members(const char* s, const char* e, osmium::memory::Buffer& buffer, osmium::builder::RelationBuilder* parent_builder = nullptr) { + if (s == e) { + return; + } + osmium::builder::RelationMemberListBuilder builder{buffer, parent_builder}; + + while (s < e) { + osmium::item_type type = osmium::char_to_item_type(*s); + if (type != osmium::item_type::node && + type != osmium::item_type::way && + type != osmium::item_type::relation) { + throw opl_error{"unknown object type", s}; + } + ++s; + + if (s == e) { + throw opl_error{"expected integer", s}; + } + osmium::object_id_type ref = opl_parse_id(&s); + opl_parse_char(&s, '@'); + if (s == e) { + builder.add_member(type, ref, ""); + return; + } + std::string role; + opl_parse_string(&s, role); + builder.add_member(type, ref, role); + + if (s == e) { + return; + } + opl_parse_char(&s, ','); + } + } + + inline void opl_parse_relation(const char** data, osmium::memory::Buffer& buffer) { + osmium::builder::RelationBuilder builder{buffer}; + osmium::Relation& relation = builder.object(); + + relation.set_id(opl_parse_id(data)); + + const char* tags_begin = nullptr; + + const char* members_begin = nullptr; + const char* members_end = nullptr; + + std::string user; + while (**data) { + opl_parse_space(data); + const char c = **data; + if (c == '\0') { + break; + } + ++(*data); + switch (c) { + case 'v': + relation.set_version(opl_parse_version(data)); + break; + case 'd': + relation.set_visible(opl_parse_visible(data)); + break; + case 'c': + relation.set_changeset(opl_parse_changeset_id(data)); + break; + case 't': + relation.set_timestamp(opl_parse_timestamp(data)); + break; + case 'i': + relation.set_uid(opl_parse_uid(data)); + break; + case 'u': + opl_parse_string(data, user); + break; + case 'T': + if (opl_non_empty(*data)) { + tags_begin = *data; + opl_skip_section(data); + } + break; + case 'M': + members_begin = *data; + members_end = opl_skip_section(data); + break; + default: + --(*data); + throw opl_error{"unknown attribute", *data}; + } + } + + builder.add_user(user); + + if (tags_begin) { + opl_parse_tags(tags_begin, buffer, &builder); + } + + if (members_begin != members_end) { + opl_parse_relation_members(members_begin, members_end, buffer, &builder); + } + + buffer.commit(); + } + + inline void opl_parse_changeset(const char** data, osmium::memory::Buffer& buffer) { + osmium::builder::ChangesetBuilder builder{buffer}; + osmium::Changeset& changeset = builder.object(); + + changeset.set_id(opl_parse_changeset_id(data)); + + const char* tags_begin = nullptr; + + osmium::Location location1; + osmium::Location location2; + std::string user; + while (**data) { + opl_parse_space(data); + const char c = **data; + if (c == '\0') { + break; + } + ++(*data); + switch (c) { + case 'k': + changeset.set_num_changes(opl_parse_int(data)); + break; + case 's': + changeset.set_created_at(opl_parse_timestamp(data)); + break; + case 'e': + changeset.set_closed_at(opl_parse_timestamp(data)); + break; + case 'd': + changeset.set_num_comments(opl_parse_int(data)); + break; + case 'i': + changeset.set_uid(opl_parse_uid(data)); + break; + case 'u': + opl_parse_string(data, user); + break; + case 'x': + if (opl_non_empty(*data)) { + location1.set_lon_partial(data); + } + break; + case 'y': + if (opl_non_empty(*data)) { + location1.set_lat_partial(data); + } + break; + case 'X': + if (opl_non_empty(*data)) { + location2.set_lon_partial(data); + } + break; + case 'Y': + if (opl_non_empty(*data)) { + location2.set_lat_partial(data); + } + break; + case 'T': + if (opl_non_empty(*data)) { + tags_begin = *data; + opl_skip_section(data); + } + break; + default: + --(*data); + throw opl_error{"unknown attribute", *data}; + } + + } + + if (location1.valid() && location2.valid()) { + changeset.bounds().extend(location1); + changeset.bounds().extend(location2); + } + + builder.add_user(user); + + if (tags_begin) { + opl_parse_tags(tags_begin, buffer, &builder); + } + + buffer.commit(); + } + + inline bool opl_parse_line(uint64_t line_count, + const char* data, + osmium::memory::Buffer& buffer, + osmium::osm_entity_bits::type read_types = osmium::osm_entity_bits::all) { + const char* start_of_line = data; + try { + switch (*data) { + case '\0': + // ignore empty lines + break; + case '#': + // ignore lines starting with # + break; + case 'n': + if (read_types & osmium::osm_entity_bits::node) { + ++data; + opl_parse_node(&data, buffer); + return true; + } + break; + case 'w': + if (read_types & osmium::osm_entity_bits::way) { + ++data; + opl_parse_way(&data, buffer); + return true; + } + break; + case 'r': + if (read_types & osmium::osm_entity_bits::relation) { + ++data; + opl_parse_relation(&data, buffer); + return true; + } + break; + case 'c': + if (read_types & osmium::osm_entity_bits::changeset) { + ++data; + opl_parse_changeset(&data, buffer); + return true; + } + break; + default: + throw opl_error{"unknown type", data}; + } + } catch (opl_error& e) { + e.set_pos(line_count, e.data ? e.data - start_of_line : 0); + throw; + } + + return false; + } + + } // namespace detail + + } // namespace io + +} // namespace osmium + + +#endif // OSMIUM_IO_DETAIL_OPL_PARSER_FUNCTIONS_HPP diff --git a/third_party/libosmium/include/osmium/io/detail/output_format.hpp b/third_party/libosmium/include/osmium/io/detail/output_format.hpp index 34ca82f2448..0fe49158bb2 100644 --- a/third_party/libosmium/include/osmium/io/detail/output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/output_format.hpp @@ -33,16 +33,16 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include -#include #include #include #include #include -#include +#include #include #include #include @@ -70,9 +70,28 @@ namespace osmium { m_out(std::make_shared()) { } - template - void output_formatted(const char* format, TArgs&&... args) { - append_printf_formatted_string(*m_out, format, std::forward(args)...); + // Simple function to convert integer to string. This is much + // faster than using sprintf, but could be further optimized. + // See https://github.com/miloyip/itoa-benchmark . + void output_int(int64_t value) { + if (value < 0) { + *m_out += '-'; + value = -value; + } + + char temp[20]; + char *t = temp; + do { + *t++ = char(value % 10) + '0'; + value /= 10; + } while (value > 0); + + const auto old_size = m_out->size(); + m_out->resize(old_size + (t - temp)); + char* data = &(*m_out)[old_size]; + do { + *data++ += *--t; + } while (t != temp); } }; // class OutputBlock; @@ -133,11 +152,11 @@ namespace osmium { public: - typedef std::function create_output_type; + using create_output_type = std::function; private: - typedef std::map map_type; + using map_type = std::map; map_type m_callbacks; diff --git a/third_party/libosmium/include/osmium/io/detail/pbf.hpp b/third_party/libosmium/include/osmium/io/detail/pbf.hpp index 88c499326b0..e23f8b9bf13 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf.hpp @@ -78,7 +78,7 @@ namespace osmium { // between representation as double and as int const int64_t lonlat_resolution = 1000 * 1000 * 1000; - const int64_t resolution_convert = lonlat_resolution / osmium::Location::coordinate_precision; + const int64_t resolution_convert = lonlat_resolution / osmium::detail::coordinate_precision; } // namespace detail diff --git a/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp b/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp index 43de1c18e74..5df191db65e 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf_decoder.hpp @@ -33,10 +33,8 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include -#include #include #include #include @@ -44,35 +42,47 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include +#include #include #include // IWYU pragma: export #include #include #include +#include +#include +#include +#include #include #include +#include +#include +#include #include -#include -#include +#include #include #include namespace osmium { + namespace builder { + class Builder; + } // namespace builder + namespace io { namespace detail { - using ptr_len_type = std::pair; + using protozero::data_view; using osm_string_len_type = std::pair; class PBFPrimitiveBlockDecoder { - static constexpr size_t initial_buffer_size = 2 * 1024 * 1024; + static constexpr const size_t initial_buffer_size = 2 * 1024 * 1024; - ptr_len_type m_data; + data_view m_data; std::vector m_stringtable; int64_t m_lon_offset = 0; @@ -84,18 +94,18 @@ namespace osmium { osmium::memory::Buffer m_buffer { initial_buffer_size }; - void decode_stringtable(const ptr_len_type& data) { + void decode_stringtable(const data_view& data) { if (!m_stringtable.empty()) { throw osmium::pbf_error("more than one stringtable in pbf file"); } protozero::pbf_message pbf_string_table(data); while (pbf_string_table.next(OSMFormat::StringTable::repeated_bytes_s)) { - auto str_len = pbf_string_table.get_data(); - if (str_len.second > osmium::max_osm_string_length) { + const auto str_view = pbf_string_table.get_view(); + if (str_view.size() > osmium::max_osm_string_length) { throw osmium::pbf_error("overlong string in string table"); } - m_stringtable.emplace_back(str_len.first, osmium::string_size_type(str_len.second)); + m_stringtable.emplace_back(str_view.data(), osmium::string_size_type(str_view.size())); } } @@ -104,7 +114,7 @@ namespace osmium { while (pbf_primitive_block.next()) { switch (pbf_primitive_block.tag()) { case OSMFormat::PrimitiveBlock::required_StringTable_stringtable: - decode_stringtable(pbf_primitive_block.get_data()); + decode_stringtable(pbf_primitive_block.get_view()); break; case OSMFormat::PrimitiveBlock::optional_int32_granularity: m_granularity = pbf_primitive_block.get_int32(); @@ -132,28 +142,28 @@ namespace osmium { switch (pbf_primitive_group.tag()) { case OSMFormat::PrimitiveGroup::repeated_Node_nodes: if (m_read_types & osmium::osm_entity_bits::node) { - decode_node(pbf_primitive_group.get_data()); + decode_node(pbf_primitive_group.get_view()); } else { pbf_primitive_group.skip(); } break; case OSMFormat::PrimitiveGroup::optional_DenseNodes_dense: if (m_read_types & osmium::osm_entity_bits::node) { - decode_dense_nodes(pbf_primitive_group.get_data()); + decode_dense_nodes(pbf_primitive_group.get_view()); } else { pbf_primitive_group.skip(); } break; case OSMFormat::PrimitiveGroup::repeated_Way_ways: if (m_read_types & osmium::osm_entity_bits::way) { - decode_way(pbf_primitive_group.get_data()); + decode_way(pbf_primitive_group.get_view()); } else { pbf_primitive_group.skip(); } break; case OSMFormat::PrimitiveGroup::repeated_Relation_relations: if (m_read_types & osmium::osm_entity_bits::relation) { - decode_relation(pbf_primitive_group.get_data()); + decode_relation(pbf_primitive_group.get_view()); } else { pbf_primitive_group.skip(); } @@ -165,7 +175,7 @@ namespace osmium { } } - osm_string_len_type decode_info(const ptr_len_type& data, osmium::OSMObject& object) { + osm_string_len_type decode_info(const data_view& data, osmium::OSMObject& object) { osm_string_len_type user = std::make_pair("", 0); protozero::pbf_message pbf_info(data); @@ -173,7 +183,7 @@ namespace osmium { switch (pbf_info.tag()) { case OSMFormat::Info::optional_int32_version: { - auto version = pbf_info.get_int32(); + const auto version = pbf_info.get_int32(); if (version < 0) { throw osmium::pbf_error("object version must not be negative"); } @@ -185,7 +195,7 @@ namespace osmium { break; case OSMFormat::Info::optional_int64_changeset: { - auto changeset_id = pbf_info.get_int64(); + const auto changeset_id = pbf_info.get_int64(); if (changeset_id < 0) { throw osmium::pbf_error("object changeset_id must not be negative"); } @@ -209,15 +219,15 @@ namespace osmium { return user; } - using kv_type = std::pair; + using kv_type = protozero::iterator_range; void build_tag_list(osmium::builder::Builder& builder, const kv_type& keys, const kv_type& vals) { - if (keys.first != keys.second) { + if (!keys.empty()) { osmium::builder::TagListBuilder tl_builder(m_buffer, &builder); - auto kit = keys.first; - auto vit = vals.first; - while (kit != keys.second) { - if (vit == vals.second) { + auto kit = keys.begin(); + auto vit = vals.begin(); + while (kit != keys.end()) { + if (vit == vals.end()) { // this is against the spec, must have same number of elements throw osmium::pbf_error("PBF format error"); } @@ -232,7 +242,7 @@ namespace osmium { return int32_t((c * m_granularity + m_lon_offset) / resolution_convert); } - void decode_node(const ptr_len_type& data) { + void decode_node(const data_view& data) { osmium::builder::NodeBuilder builder(m_buffer); osmium::Node& node = builder.object(); @@ -256,7 +266,7 @@ namespace osmium { vals = pbf_node.get_packed_uint32(); break; case OSMFormat::Node::optional_Info_info: - user = decode_info(pbf_node.get_data(), builder.object()); + user = decode_info(pbf_node.get_view(), builder.object()); break; case OSMFormat::Node::required_sint64_lat: lat = pbf_node.get_sint64(); @@ -287,12 +297,14 @@ namespace osmium { m_buffer.commit(); } - void decode_way(const ptr_len_type& data) { + void decode_way(const data_view& data) { osmium::builder::WayBuilder builder(m_buffer); kv_type keys; kv_type vals; - std::pair refs; + protozero::iterator_range refs; + protozero::iterator_range lats; + protozero::iterator_range lons; osm_string_len_type user = { "", 0 }; @@ -309,11 +321,17 @@ namespace osmium { vals = pbf_way.get_packed_uint32(); break; case OSMFormat::Way::optional_Info_info: - user = decode_info(pbf_way.get_data(), builder.object()); + user = decode_info(pbf_way.get_view(), builder.object()); break; 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(); } @@ -321,11 +339,26 @@ namespace osmium { builder.add_user(user.first, user.second); - if (refs.first != refs.second) { + if (!refs.empty()) { osmium::builder::WayNodeListBuilder wnl_builder(m_buffer, &builder); osmium::util::DeltaDecode ref; - while (refs.first != refs.second) { - wnl_builder.add_node_ref(ref.update(*refs.first++)); + if (lats.empty()) { + for (const auto& ref_value : refs) { + wnl_builder.add_node_ref(ref.update(ref_value)); + } + } else { + osmium::util::DeltaDecode lon; + osmium::util::DeltaDecode lat; + while (!refs.empty() && !lons.empty() && !lats.empty()) { + wnl_builder.add_node_ref( + ref.update(refs.front()), + osmium::Location{convert_pbf_coordinate(lon.update(lons.front())), + convert_pbf_coordinate(lat.update(lats.front()))} + ); + refs.drop_front(); + lons.drop_front(); + lats.drop_front(); + } } } @@ -334,14 +367,14 @@ namespace osmium { m_buffer.commit(); } - void decode_relation(const ptr_len_type& data) { + void decode_relation(const data_view& data) { osmium::builder::RelationBuilder builder(m_buffer); kv_type keys; kv_type vals; - std::pair roles; - std::pair refs; - std::pair types; + protozero::iterator_range roles; + protozero::iterator_range refs; + protozero::iterator_range types; osm_string_len_type user = { "", 0 }; @@ -358,7 +391,7 @@ namespace osmium { vals = pbf_relation.get_packed_uint32(); break; case OSMFormat::Relation::optional_Info_info: - user = decode_info(pbf_relation.get_data(), builder.object()); + user = decode_info(pbf_relation.get_view(), builder.object()); break; case OSMFormat::Relation::packed_int32_roles_sid: roles = pbf_relation.get_packed_int32(); @@ -376,21 +409,24 @@ namespace osmium { builder.add_user(user.first, user.second); - if (refs.first != refs.second) { + if (!refs.empty()) { osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder); osmium::util::DeltaDecode ref; - while (roles.first != roles.second && refs.first != refs.second && types.first != types.second) { - const auto& r = m_stringtable.at(*roles.first++); - int type = *types.first++; + while (!roles.empty() && !refs.empty() && !types.empty()) { + const auto& r = m_stringtable.at(roles.front()); + const int type = types.front(); if (type < 0 || type > 2) { throw osmium::pbf_error("unknown relation member type"); } rml_builder.add_member( osmium::item_type(type + 1), - ref.update(*refs.first++), + ref.update(refs.front()), r.first, r.second ); + roles.drop_front(); + refs.drop_front(); + types.drop_front(); } } @@ -399,22 +435,22 @@ namespace osmium { m_buffer.commit(); } - void decode_dense_nodes(const ptr_len_type& data) { + void decode_dense_nodes(const data_view& data) { bool has_info = false; bool has_visibles = false; - std::pair ids; - std::pair lats; - std::pair lons; + protozero::iterator_range ids; + protozero::iterator_range lats; + protozero::iterator_range lons; - std::pair tags; + protozero::iterator_range tags; - std::pair versions; - std::pair timestamps; - std::pair changesets; - std::pair uids; - std::pair user_sids; - std::pair visibles; + protozero::iterator_range versions; + protozero::iterator_range timestamps; + protozero::iterator_range changesets; + protozero::iterator_range uids; + protozero::iterator_range user_sids; + protozero::iterator_range visibles; protozero::pbf_message pbf_dense_nodes(data); while (pbf_dense_nodes.next()) { @@ -475,11 +511,11 @@ namespace osmium { osmium::util::DeltaDecode dense_changeset; osmium::util::DeltaDecode dense_timestamp; - auto tag_it = tags.first; + auto tag_it = tags.begin(); - while (ids.first != ids.second) { - if (lons.first == lons.second || - lats.first == lats.second) { + while (!ids.empty()) { + if (lons.empty() || + lats.empty()) { // this is against the spec, must have same number of elements throw osmium::pbf_error("PBF format error"); } @@ -489,43 +525,50 @@ namespace osmium { osmium::builder::NodeBuilder builder(m_buffer); osmium::Node& node = builder.object(); - node.set_id(dense_id.update(*ids.first++)); + node.set_id(dense_id.update(ids.front())); + ids.drop_front(); if (has_info) { - if (versions.first == versions.second || - changesets.first == changesets.second || - timestamps.first == timestamps.second || - uids.first == uids.second || - user_sids.first == user_sids.second) { + if (versions.empty() || + changesets.empty() || + timestamps.empty() || + uids.empty() || + user_sids.empty()) { // this is against the spec, must have same number of elements throw osmium::pbf_error("PBF format error"); } - auto version = *versions.first++; + const auto version = versions.front(); + versions.drop_front(); if (version < 0) { throw osmium::pbf_error("object version must not be negative"); } node.set_version(static_cast(version)); - auto changeset_id = dense_changeset.update(*changesets.first++); + const auto changeset_id = dense_changeset.update(changesets.front()); + changesets.drop_front(); if (changeset_id < 0) { throw osmium::pbf_error("object changeset_id must not be negative"); } node.set_changeset(static_cast(changeset_id)); - node.set_timestamp(dense_timestamp.update(*timestamps.first++) * m_date_factor / 1000); - node.set_uid_from_signed(static_cast(dense_uid.update(*uids.first++))); + node.set_timestamp(dense_timestamp.update(timestamps.front()) * m_date_factor / 1000); + timestamps.drop_front(); + node.set_uid_from_signed(static_cast(dense_uid.update(uids.front()))); + uids.drop_front(); if (has_visibles) { - if (visibles.first == visibles.second) { + if (visibles.empty()) { // this is against the spec, must have same number of elements throw osmium::pbf_error("PBF format error"); } - visible = (*visibles.first++) != 0; + visible = (visibles.front() != 0); + visibles.drop_front(); } node.set_visible(visible); - const auto& u = m_stringtable.at(dense_user_sid.update(*user_sids.first++)); + const auto& u = m_stringtable.at(dense_user_sid.update(user_sids.front())); + user_sids.drop_front(); builder.add_user(u.first, u.second); } else { builder.add_user(""); @@ -533,8 +576,10 @@ namespace osmium { // even if the node isn't visible, there's still a record // of its lat/lon in the dense arrays. - const auto lon = dense_longitude.update(*lons.first++); - const auto lat = dense_latitude.update(*lats.first++); + const auto lon = dense_longitude.update(lons.front()); + lons.drop_front(); + const auto lat = dense_latitude.update(lats.front()); + lats.drop_front(); if (visible) { builder.object().set_location(osmium::Location( convert_pbf_coordinate(lon), @@ -542,18 +587,18 @@ namespace osmium { )); } - if (tag_it != tags.second) { + if (tag_it != tags.end()) { osmium::builder::TagListBuilder tl_builder(m_buffer, &builder); - while (tag_it != tags.second && *tag_it != 0) { + while (tag_it != tags.end() && *tag_it != 0) { const auto& k = m_stringtable.at(*tag_it++); - if (tag_it == tags.second) { + if (tag_it == tags.end()) { throw osmium::pbf_error("PBF format error"); // this is against the spec, keys/vals must come in pairs } const auto& v = m_stringtable.at(*tag_it++); tl_builder.add_tag(k.first, k.second, v.first, v.second); } - if (tag_it != tags.second) { + if (tag_it != tags.end()) { ++tag_it; } } @@ -565,7 +610,7 @@ namespace osmium { public: - PBFPrimitiveBlockDecoder(const ptr_len_type& data, osmium::osm_entity_bits::type read_types) : + PBFPrimitiveBlockDecoder(const data_view& data, osmium::osm_entity_bits::type read_types) : m_data(data), m_read_types(read_types) { } @@ -582,7 +627,7 @@ namespace osmium { try { decode_primitive_block_metadata(); decode_primitive_block_data(); - } catch (std::out_of_range&) { + } catch (const std::out_of_range&) { throw osmium::pbf_error("string id out of range"); } @@ -591,17 +636,17 @@ namespace osmium { }; // class PBFPrimitiveBlockDecoder - inline ptr_len_type decode_blob(const std::string& blob_data, std::string& output) { + inline data_view decode_blob(const std::string& blob_data, std::string& output) { int32_t raw_size = 0; - std::pair zlib_data = {nullptr, 0}; + protozero::data_view zlib_data; protozero::pbf_message pbf_blob(blob_data); while (pbf_blob.next()) { switch (pbf_blob.tag()) { case FileFormat::Blob::optional_bytes_raw: { - auto data_len = pbf_blob.get_data(); - if (data_len.second > max_uncompressed_blob_size) { + auto data_len = pbf_blob.get_view(); + if (data_len.size() > max_uncompressed_blob_size) { throw osmium::pbf_error("illegal blob size"); } return data_len; @@ -613,7 +658,7 @@ namespace osmium { } break; case FileFormat::Blob::optional_bytes_zlib_data: - zlib_data = pbf_blob.get_data(); + zlib_data = pbf_blob.get_view(); break; case FileFormat::Blob::optional_bytes_lzma_data: throw osmium::pbf_error("lzma blobs not implemented"); @@ -622,10 +667,10 @@ namespace osmium { } } - if (zlib_data.second != 0 && raw_size != 0) { + if (zlib_data.size() != 0 && raw_size != 0) { return osmium::io::detail::zlib_uncompress_string( - zlib_data.first, - static_cast(zlib_data.second), + zlib_data.data(), + static_cast(zlib_data.size()), static_cast(raw_size), output ); @@ -634,7 +679,7 @@ namespace osmium { throw osmium::pbf_error("blob contains no data"); } - inline osmium::Box decode_header_bbox(const ptr_len_type& data) { + inline osmium::Box decode_header_bbox(const data_view& data) { int64_t left = std::numeric_limits::max(); int64_t right = std::numeric_limits::max(); int64_t top = std::numeric_limits::max(); @@ -674,7 +719,7 @@ namespace osmium { return box; } - inline osmium::io::Header decode_header_block(const ptr_len_type& data) { + inline osmium::io::Header decode_header_block(const data_view& data) { osmium::io::Header header; int i = 0; @@ -682,20 +727,20 @@ namespace osmium { while (pbf_header_block.next()) { switch (pbf_header_block.tag()) { case OSMFormat::HeaderBlock::optional_HeaderBBox_bbox: - header.add_box(decode_header_bbox(pbf_header_block.get_data())); + header.add_box(decode_header_bbox(pbf_header_block.get_view())); break; case OSMFormat::HeaderBlock::repeated_string_required_features: { - auto feature = pbf_header_block.get_data(); - if (!strncmp("OsmSchema-V0.6", feature.first, feature.second)) { + auto feature = pbf_header_block.get_view(); + if (!std::strncmp("OsmSchema-V0.6", feature.data(), feature.size())) { // intentionally left blank - } else if (!strncmp("DenseNodes", feature.first, feature.second)) { + } else if (!std::strncmp("DenseNodes", feature.data(), feature.size())) { header.set("pbf_dense_nodes", true); - } else if (!strncmp("HistoricalInformation", feature.first, feature.second)) { + } else if (!std::strncmp("HistoricalInformation", feature.data(), feature.size())) { header.set_has_multiple_object_versions(true); } else { std::string msg("required feature not supported: "); - msg.append(feature.first, feature.second); + msg.append(feature.data(), feature.size()); throw osmium::pbf_error(msg); } } @@ -708,7 +753,7 @@ namespace osmium { break; case OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp: { - auto timestamp = osmium::Timestamp(pbf_header_block.get_int64()).to_iso(); + const auto timestamp = osmium::Timestamp(pbf_header_block.get_int64()).to_iso(); header.set("osmosis_replication_timestamp", timestamp); header.set("timestamp", timestamp); } diff --git a/third_party/libosmium/include/osmium/io/detail/pbf_input_format.hpp b/third_party/libosmium/include/osmium/io/detail/pbf_input_format.hpp index 0c6a9ef546d..1253447f3cf 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf_input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf_input_format.hpp @@ -38,25 +38,22 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include -#include #include -#include #include #include +#include #include #include // IWYU pragma: export #include #include -#include -#include +#include #include -#include +#include #include -#include -#include #include #include #include @@ -80,7 +77,7 @@ namespace osmium { */ std::string read_from_input_queue(size_t size) { while (m_input_buffer.size() < size) { - std::string new_data = get_input(); + const std::string new_data = get_input(); if (input_done()) { throw osmium::pbf_error("truncated data (EOF encountered)"); } @@ -106,7 +103,7 @@ namespace osmium { try { const std::string input_data = read_from_input_queue(sizeof(size_in_network_byte_order)); size_in_network_byte_order = *reinterpret_cast(input_data.data()); - } catch (osmium::pbf_error&) { + } catch (const osmium::pbf_error&) { return 0; // EOF } @@ -123,13 +120,13 @@ namespace osmium { * type. Return the size of the following Blob. */ size_t decode_blob_header(protozero::pbf_message&& pbf_blob_header, const char* expected_type) { - std::pair blob_header_type; + protozero::data_view blob_header_type; size_t blob_header_datasize = 0; while (pbf_blob_header.next()) { switch (pbf_blob_header.tag()) { case FileFormat::BlobHeader::required_string_type: - blob_header_type = pbf_blob_header.get_data(); + blob_header_type = pbf_blob_header.get_view(); break; case FileFormat::BlobHeader::required_int32_datasize: blob_header_datasize = pbf_blob_header.get_int32(); @@ -143,7 +140,7 @@ namespace osmium { throw osmium::pbf_error("PBF format error: BlobHeader.datasize missing or zero."); } - if (strncmp(expected_type, blob_header_type.first, blob_header_type.second)) { + if (std::strncmp(expected_type, blob_header_type.data(), blob_header_type.size())) { throw osmium::pbf_error("blob does not have expected type (OSMHeader in first blob, OSMData in following blobs)"); } diff --git a/third_party/libosmium/include/osmium/io/detail/pbf_output_format.hpp b/third_party/libosmium/include/osmium/io/detail/pbf_output_format.hpp index 878d7b4ec12..43aa8cc4d73 100644 --- a/third_party/libosmium/include/osmium/io/detail/pbf_output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/pbf_output_format.hpp @@ -41,30 +41,34 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include #include +#include +#include #include #include #include // IWYU pragma: export #include +#include #include #include #include #include #include #include -#include +#include #include #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -101,6 +105,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 +490,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,20 +522,24 @@ 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"); + const std::string osmosis_replication_timestamp = header.get("osmosis_replication_timestamp"); if (!osmosis_replication_timestamp.empty()) { osmium::Timestamp ts(osmosis_replication_timestamp.c_str()); pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp, uint32_t(ts)); } - std::string osmosis_replication_sequence_number = header.get("osmosis_replication_sequence_number"); + const std::string osmosis_replication_sequence_number = header.get("osmosis_replication_sequence_number"); if (!osmosis_replication_sequence_number.empty()) { pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_sequence_number, std::atoll(osmosis_replication_sequence_number.c_str())); } - std::string osmosis_replication_base_url = header.get("osmosis_replication_base_url"); + const std::string osmosis_replication_base_url = header.get("osmosis_replication_base_url"); if (!osmosis_replication_base_url.empty()) { pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_osmosis_replication_base_url, osmosis_replication_base_url); } @@ -571,15 +583,30 @@ namespace osmium { pbf_way.add_int64(OSMFormat::Way::required_int64_id, way.id()); add_meta(way, pbf_way); - static auto map_node_ref = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> osmium::object_id_type { - return node_ref->ref(); - }; - typedef osmium::util::DeltaEncodeIterator it_type; + { + osmium::util::DeltaEncode delta_id; + protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_refs)}; + for (const auto& node_ref : way.nodes()) { + field.add_element(delta_id.update(node_ref.ref())); + } + } - const auto& nodes = way.nodes(); - 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) { + { + osmium::util::DeltaEncode delta_id; + protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_lon)}; + for (const auto& node_ref : way.nodes()) { + field.add_element(delta_id.update(lonlat2int(node_ref.location().lon_without_check()))); + } + } + { + osmium::util::DeltaEncode delta_id; + protozero::packed_field_sint64 field{pbf_way, protozero::pbf_tag_type(OSMFormat::Way::packed_sint64_lat)}; + for (const auto& node_ref : way.nodes()) { + field.add_element(delta_id.update(lonlat2int(node_ref.location().lat_without_check()))); + } + } + } } void relation(const osmium::Relation& relation) { @@ -596,14 +623,13 @@ namespace osmium { } } - static auto map_member_ref = [](osmium::RelationMemberList::const_iterator member) noexcept -> osmium::object_id_type { - return member->ref(); - }; - typedef osmium::util::DeltaEncodeIterator it_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 }; - pbf_relation.add_packed_sint64(OSMFormat::Relation::packed_sint64_memids, first, last); + { + osmium::util::DeltaEncode delta_id; + protozero::packed_field_sint64 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_sint64_memids)}; + for (const auto& member : relation.members()) { + field.add_element(delta_id.update(member.ref())); + } + } { protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_MemberType_types)}; diff --git a/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp b/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp index bdaabbaec84..3eb790240b8 100644 --- a/third_party/libosmium/include/osmium/io/detail/protobuf_tags.hpp +++ b/third_party/libosmium/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/third_party/libosmium/include/osmium/io/detail/queue_util.hpp b/third_party/libosmium/include/osmium/io/detail/queue_util.hpp index af845441137..bc4702000e9 100644 --- a/third_party/libosmium/include/osmium/io/detail/queue_util.hpp +++ b/third_party/libosmium/include/osmium/io/detail/queue_util.hpp @@ -47,6 +47,9 @@ namespace osmium { namespace detail { + template + using future_queue_type = osmium::thread::Queue>; + /** * 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>; + using future_buffer_queue_type = future_queue_type; /** * 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>; + using future_string_queue_type = future_queue_type; template - inline void add_to_queue(osmium::thread::Queue>& queue, T&& data) { + inline void add_to_queue(future_queue_type& queue, T&& data) { std::promise promise; queue.push(promise.get_future()); promise.set_value(std::forward(data)); } template - inline void add_to_queue(osmium::thread::Queue>& queue, std::exception_ptr&& exception) { + inline void add_to_queue(future_queue_type& queue, std::exception_ptr&& exception) { std::promise promise; queue.push(promise.get_future()); promise.set_exception(std::move(exception)); } template - inline void add_end_of_data_to_queue(osmium::thread::Queue>& queue) { + inline void add_end_of_data_to_queue(future_queue_type& queue) { add_to_queue(queue, T{}); } @@ -103,14 +106,12 @@ namespace osmium { template class queue_wrapper { - using queue_type = osmium::thread::Queue>; - - queue_type& m_queue; + future_queue_type& m_queue; bool m_has_reached_end_of_data; public: - explicit queue_wrapper(queue_type& queue) : + explicit queue_wrapper(future_queue_type& queue) : m_queue(queue), m_has_reached_end_of_data(false) { } diff --git a/third_party/libosmium/include/osmium/io/detail/read_write.hpp b/third_party/libosmium/include/osmium/io/detail/read_write.hpp index 769e2b39ab1..a086e5b78b6 100644 --- a/third_party/libosmium/include/osmium/io/detail/read_write.hpp +++ b/third_party/libosmium/include/osmium/io/detail/read_write.hpp @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include #include @@ -84,7 +83,7 @@ namespace osmium { #ifdef _WIN32 flags |= O_BINARY; #endif - int fd = ::open(filename.c_str(), flags, 0666); + const int fd = ::open(filename.c_str(), flags, 0666); if (fd < 0) { throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'"); } @@ -108,7 +107,7 @@ namespace osmium { #ifdef _WIN32 flags |= O_BINARY; #endif - int fd = ::open(filename.c_str(), flags); + const int fd = ::open(filename.c_str(), flags); if (fd < 0) { throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'"); } @@ -133,7 +132,7 @@ namespace osmium { if (write_count > max_write) { write_count = max_write; } - auto length = ::write(fd, output_buffer + offset, static_cast(write_count)); + const auto length = ::write(fd, output_buffer + offset, static_cast(write_count)); if (length < 0) { throw std::system_error(errno, std::system_category(), "Write failed"); } diff --git a/third_party/libosmium/include/osmium/io/detail/string_table.hpp b/third_party/libosmium/include/osmium/io/detail/string_table.hpp index 55c622681e8..f1ddc8715c6 100644 --- a/third_party/libosmium/include/osmium/io/detail/string_table.hpp +++ b/third_party/libosmium/include/osmium/io/detail/string_table.hpp @@ -34,13 +34,14 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include -#include #include #include #include -#include #include +#include +#include #include @@ -69,8 +70,8 @@ namespace osmium { std::list m_chunks; void add_chunk() { - m_chunks.push_front(std::string()); - m_chunks.front().reserve(m_chunk_size); + m_chunks.emplace_back(); + m_chunks.back().reserve(m_chunk_size); } public: @@ -82,6 +83,7 @@ namespace osmium { } void clear() noexcept { + assert(!m_chunks.empty()); m_chunks.erase(std::next(m_chunks.begin()), m_chunks.end()); m_chunks.front().clear(); } @@ -93,31 +95,38 @@ namespace osmium { * allocated. */ const char* add(const char* string) { - size_t len = std::strlen(string) + 1; + const size_t len = std::strlen(string) + 1; assert(len <= m_chunk_size); - size_t chunk_len = m_chunks.front().size(); - if (chunk_len + len > m_chunks.front().capacity()) { + size_t chunk_len = m_chunks.back().size(); + if (chunk_len + len > m_chunks.back().capacity()) { add_chunk(); chunk_len = 0; } - m_chunks.front().append(string); - m_chunks.front().append(1, '\0'); + m_chunks.back().append(string); + m_chunks.back().append(1, '\0'); - return m_chunks.front().c_str() + chunk_len; + return m_chunks.back().c_str() + chunk_len; } - class const_iterator : public std::iterator { + class const_iterator { + + using it_type = std::list::const_iterator; - typedef std::list::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), @@ -126,7 +135,7 @@ namespace osmium { const_iterator& operator++() { assert(m_it != m_last); - auto last_pos = m_it->c_str() + m_it->size(); + const auto last_pos = m_it->c_str() + m_it->size(); while (m_pos != last_pos && *m_pos) ++m_pos; if (m_pos != last_pos) ++m_pos; if (m_pos == last_pos) { @@ -184,18 +193,33 @@ namespace osmium { } size_t get_used_bytes_in_last_chunk() const noexcept { - return m_chunks.front().size(); + return m_chunks.back().size(); } }; // class StringStore - struct StrComp { + struct str_equal { + + bool operator()(const char* lhs, const char* rhs) const noexcept { + return lhs == rhs || std::strcmp(lhs, rhs) == 0; + } + + }; // struct str_equal - bool operator()(const char* lhs, const char* rhs) const { - return strcmp(lhs, rhs) < 0; + struct djb2_hash { + + size_t operator()(const char* str) const noexcept { + size_t hash = 5381; + int c; + + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + + return hash; } - }; // struct StrComp + }; // struct djb2_hash class StringTable { @@ -206,14 +230,23 @@ namespace osmium { // Blob. static constexpr const uint32_t max_entries = max_uncompressed_blob_size; + // There is one string table per PBF primitive block. Most of + // them are really small, because most blocks are full of nodes + // with no tags. But string tables can get really large for + // ways with many tags or for large relations. + // The chosen size is enough so that 99% of all string tables + // in typical OSM files will only need a single memory + // allocation. + static constexpr const size_t default_stringtable_chunk_size = 100 * 1024; + StringStore m_strings; - std::map m_index; + std::unordered_map m_index; uint32_t m_size; public: - StringTable() : - m_strings(1024 * 1024), + explicit StringTable(size_t size = default_stringtable_chunk_size) : + m_strings(size), m_index(), m_size(0) { m_strings.add(""); @@ -231,7 +264,7 @@ namespace osmium { } uint32_t add(const char* s) { - auto f = m_index.find(s); + const auto f = m_index.find(s); if (f != m_index.end()) { return uint32_t(f->second); } diff --git a/third_party/libosmium/include/osmium/io/detail/string_util.hpp b/third_party/libosmium/include/osmium/io/detail/string_util.hpp index f80088e6324..0334b0e98ef 100644 --- a/third_party/libosmium/include/osmium/io/detail/string_util.hpp +++ b/third_party/libosmium/include/osmium/io/detail/string_util.hpp @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include #include @@ -100,36 +101,57 @@ namespace osmium { static const size_t max_size = 0; #endif - size_t old_size = out.size(); + const size_t old_size = out.size(); - int len = string_snprintf(out, - old_size, - max_size, - format, - std::forward(args)...); + const int len = string_snprintf(out, + old_size, + max_size, + format, + std::forward(args)...); assert(len > 0); if (size_t(len) >= max_size) { #ifndef NDEBUG - int len2 = + const int len2 = #endif - string_snprintf(out, - old_size, - size_t(len) + 1, - format, - std::forward(args)...); + string_snprintf(out, + old_size, + size_t(len) + 1, + format, + std::forward(args)...); assert(len2 == len); } out.resize(old_size + size_t(len)); } + // Write out the value with exactly two hex digits. + inline void append_2_hex_digits(std::string& out, uint32_t value, const char* const hex_digits) { + out += hex_digits[(value >> 4) & 0xf]; + out += hex_digits[ value & 0xf]; + } + + // Write out the value with four or more hex digits. + inline void append_min_4_hex_digits(std::string& out, uint32_t value, const char* const hex_digits) { + auto + v = value & 0xf0000000; if (v) out += hex_digits[v >> 28]; + v = value & 0x0f000000; if (v) out += hex_digits[v >> 24]; + v = value & 0x00f00000; if (v) out += hex_digits[v >> 20]; + v = value & 0x000f0000; if (v) out += hex_digits[v >> 16]; + + out += hex_digits[(value >> 12) & 0xf]; + out += hex_digits[(value >> 8) & 0xf]; + out += hex_digits[(value >> 4) & 0xf]; + out += hex_digits[ value & 0xf]; + } + inline void append_utf8_encoded_string(std::string& out, const char* data) { + static const char* lookup_hex = "0123456789abcdef"; const char* end = data + std::strlen(data); while (data != end) { const char* last = data; - uint32_t c = utf8::next(data, end); + const uint32_t c = utf8::next(data, end); // This is a list of Unicode code points that we let // through instead of escaping them. It is incomplete @@ -148,9 +170,9 @@ namespace osmium { } else { out += '%'; if (c <= 0xff) { - append_printf_formatted_string(out, "%02x", c); + append_2_hex_digits(out, c, lookup_hex); } else { - append_printf_formatted_string(out, "%04x", c); + append_min_4_hex_digits(out, c, lookup_hex); } out += '%'; } @@ -174,6 +196,7 @@ namespace osmium { } inline void append_debug_encoded_string(std::string& out, const char* data, const char* prefix, const char* suffix) { + static const char* lookup_hex = "0123456789ABCDEF"; const char* end = data + std::strlen(data); while (data != end) { @@ -194,7 +217,9 @@ namespace osmium { out.append(last, data); } else { out.append(prefix); - append_printf_formatted_string(out, "", c); + out.append(""); out.append(suffix); } } diff --git a/third_party/libosmium/include/osmium/io/detail/write_thread.hpp b/third_party/libosmium/include/osmium/io/detail/write_thread.hpp index 79604863819..85ef811a760 100644 --- a/third_party/libosmium/include/osmium/io/detail/write_thread.hpp +++ b/third_party/libosmium/include/osmium/io/detail/write_thread.hpp @@ -36,6 +36,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include diff --git a/third_party/libosmium/include/osmium/io/detail/xml_input_format.hpp b/third_party/libosmium/include/osmium/io/detail/xml_input_format.hpp index 8cb5efe8a17..d8c57d86e74 100644 --- a/third_party/libosmium/include/osmium/io/detail/xml_input_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/xml_input_format.hpp @@ -34,10 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include -#include -#include #include -#include #include #include #include @@ -53,15 +50,19 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include #include +#include #include #include #include +#include +#include #include +#include +#include #include #include -#include +#include #include #include @@ -192,6 +193,17 @@ namespace osmium { static_cast(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 +214,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 +253,11 @@ namespace osmium { osmium::Location location; check_attributes(attrs, [&location, &user, &object](const XML_Char* name, const XML_Char* value) { - if (!strcmp(name, "lon")) { - location.set_lon(std::atof(value)); // XXX doesn't detect garbage after the number - } else if (!strcmp(name, "lat")) { - location.set_lat(std::atof(value)); // XXX doesn't detect garbage after the number - } else if (!strcmp(name, "user")) { + if (!std::strcmp(name, "lon")) { + location.set_lon(value); + } else if (!std::strcmp(name, "lat")) { + location.set_lat(value); + } else if (!std::strcmp(name, "user")) { user = value; } else { object.set_attribute(name, value); @@ -265,15 +278,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")) { - min.set_lon(atof(value)); - } else if (!strcmp(name, "min_lat")) { - min.set_lat(atof(value)); - } else if (!strcmp(name, "max_lon")) { - max.set_lon(atof(value)); - } else if (!strcmp(name, "max_lat")) { - max.set_lat(atof(value)); - } else if (!strcmp(name, "user")) { + if (!std::strcmp(name, "min_lon")) { + min.set_lon(value); + } else if (!std::strcmp(name, "min_lat")) { + min.set_lat(value); + } else if (!std::strcmp(name, "max_lon")) { + max.set_lon(value); + } else if (!std::strcmp(name, "max_lat")) { + max.set_lat(value); + } else if (!std::strcmp(name, "user")) { user = value; } else { new_changeset.set_attribute(name, value); @@ -309,17 +322,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 +346,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(new osmium::builder::NodeBuilder(m_buffer)); @@ -342,7 +355,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(new osmium::builder::WayBuilder(m_buffer)); @@ -351,7 +364,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(new osmium::builder::RelationBuilder(m_buffer)); @@ -360,7 +373,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(new osmium::builder::ChangesetBuilder(m_buffer)); @@ -369,50 +382,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")) { - min.set_lon(atof(value)); - } else if (!strcmp(name, "minlat")) { - min.set_lat(atof(value)); - } else if (!strcmp(name, "maxlon")) { - max.set_lon(atof(value)); - } else if (!strcmp(name, "maxlat")) { - max.set_lat(atof(value)); + if (!std::strcmp(name, "minlon")) { + min.set_lon(value); + } else if (!std::strcmp(name, "minlat")) { + min.set_lat(value); + } else if (!std::strcmp(name, "maxlon")) { + max.set_lon(value); + } else if (!std::strcmp(name, "maxlat")) { + max.set_lat(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(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(value); + } else if (!std::strcmp(name, "lat")) { + nr.location().set_lat(value); } }); - } 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 +439,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 +450,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(value); } }); @@ -446,37 +465,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(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(value); } }); @@ -484,7 +503,7 @@ namespace osmium { } break; case context::comment: - if (!strcmp(element, "text")) { + if (!std::strcmp(element, "text")) { m_context = context::comment_text; } break; @@ -510,15 +529,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 +545,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 +554,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 +563,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 +572,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 +588,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/third_party/libosmium/include/osmium/io/detail/xml_output_format.hpp b/third_party/libosmium/include/osmium/io/detail/xml_output_format.hpp index 09bd6b3ce8c..3f47b0fb188 100644 --- a/third_party/libosmium/include/osmium/io/detail/xml_output_format.hpp +++ b/third_party/libosmium/include/osmium/io/detail/xml_output_format.hpp @@ -34,27 +34,26 @@ DEALINGS IN THE SOFTWARE. */ #include -#include -#include -#include -#include #include #include #include -#include #include +#include #include +#include +#include #include #include #include #include -#include +#include #include #include #include #include #include +#include #include #include #include @@ -86,8 +85,26 @@ namespace osmium { */ bool use_change_ops; + /// Should node locations be added to ways? + bool locations_on_ways; }; + namespace detail { + + inline void append_lat_lon_attributes(std::string& out, const char* lat, const char* lon, const osmium::Location& location) { + out += ' '; + out += lat; + out += "=\""; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(out), location.y()); + out += "\" "; + out += lon; + out += "=\""; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(out), location.x()); + out += "\""; + } + + } // namespace detail + class XMLOutputBlock : public OutputBlock { // operation (create, modify, delete) for osc files @@ -116,12 +133,21 @@ namespace osmium { write_spaces(prefix_spaces()); } + template + void write_attribute(const char* name, T value) { + *m_out += ' '; + *m_out += name; + *m_out += "=\""; + output_int(value); + *m_out += '"'; + } + void write_meta(const osmium::OSMObject& object) { - output_formatted(" id=\"%" PRId64 "\"", object.id()); + write_attribute("id", object.id()); if (m_options.add_metadata) { if (object.version()) { - output_formatted(" version=\"%d\"", object.version()); + write_attribute("version", object.version()); } if (object.timestamp()) { @@ -131,13 +157,14 @@ namespace osmium { } if (!object.user_is_anonymous()) { - output_formatted(" uid=\"%d\" user=\"", object.uid()); + write_attribute("uid", object.uid()); + *m_out += " user=\""; append_xml_encoded_string(*m_out, object.user()); *m_out += "\""; } if (object.changeset()) { - output_formatted(" changeset=\"%d\"", object.changeset()); + write_attribute("changeset", object.changeset()); } if (m_options.add_visible_flag) { @@ -162,8 +189,11 @@ namespace osmium { } void write_discussion(const osmium::ChangesetDiscussion& comments) { + *m_out += " \n"; for (const auto& comment : comments) { - output_formatted(" \n", node_ref.ref()); + if (m_options.locations_on_ways) { + for (const auto& node_ref : way.nodes()) { + write_prefix(); + *m_out += " \n"; } @@ -332,7 +374,7 @@ namespace osmium { void changeset(const osmium::Changeset& changeset) { *m_out += " 0) { - *m_out += " \n"; + if (!changeset.discussion().empty()) { write_discussion(changeset.discussion()); } @@ -394,9 +434,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; @@ -425,10 +466,9 @@ namespace osmium { for (const auto& box : header.boxes()) { out += " \n", box.top_right().lat()); + detail::append_lat_lon_attributes(out, "minlat", "minlon", box.bottom_left()); + detail::append_lat_lon_attributes(out, "maxlat", "maxlon", box.top_right()); + out += "/>\n"; } send_to_output_queue(std::move(out)); diff --git a/third_party/libosmium/include/osmium/io/detail/zlib.hpp b/third_party/libosmium/include/osmium/io/detail/zlib.hpp index 2b6dddb941c..15ece7c5a1f 100644 --- a/third_party/libosmium/include/osmium/io/detail/zlib.hpp +++ b/third_party/libosmium/include/osmium/io/detail/zlib.hpp @@ -33,12 +33,12 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include #include #include +#include + #include #include @@ -62,7 +62,7 @@ namespace osmium { std::string output(output_size, '\0'); - auto result = ::compress( + const auto result = ::compress( reinterpret_cast(const_cast(output.data())), &output_size, reinterpret_cast(input.data()), @@ -89,10 +89,10 @@ namespace osmium { * @param output Uncompressed result data. * @returns Pointer and size to incompressed data. */ - inline std::pair zlib_uncompress_string(const char* input, unsigned long input_size, unsigned long raw_size, std::string& output) { + inline protozero::data_view zlib_uncompress_string(const char* input, unsigned long input_size, unsigned long raw_size, std::string& output) { output.resize(raw_size); - auto result = ::uncompress( + const auto result = ::uncompress( reinterpret_cast(&*output.begin()), &raw_size, reinterpret_cast(input), @@ -103,7 +103,7 @@ namespace osmium { throw io_error(std::string("failed to uncompress data: ") + zError(result)); } - return std::make_pair(output.data(), output.size()); + return protozero::data_view{output.data(), output.size()}; } } // namespace detail diff --git a/third_party/libosmium/include/osmium/io/file.hpp b/third_party/libosmium/include/osmium/io/file.hpp index 56fc8d5133e..812c49429a0 100644 --- a/third_party/libosmium/include/osmium/io/file.hpp +++ b/third_party/libosmium/include/osmium/io/file.hpp @@ -34,7 +34,6 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include #include #include @@ -43,7 +42,6 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include namespace osmium { @@ -115,7 +113,7 @@ namespace osmium { } // if filename is a URL, default to XML format - std::string protocol = m_filename.substr(0, m_filename.find_first_of(':')); + const std::string protocol = m_filename.substr(0, m_filename.find_first_of(':')); if (protocol == "http" || protocol == "https") { m_file_format = file_format::xml; } @@ -174,7 +172,7 @@ namespace osmium { } for (auto& option : options) { - size_t pos = option.find_first_of('='); + const size_t pos = option.find_first_of('='); if (pos == std::string::npos) { set(option, true); } else { diff --git a/third_party/libosmium/include/osmium/io/gzip_compression.hpp b/third_party/libosmium/include/osmium/io/gzip_compression.hpp index e6ca010de79..5e3e2331aef 100644 --- a/third_party/libosmium/include/osmium/io/gzip_compression.hpp +++ b/third_party/libosmium/include/osmium/io/gzip_compression.hpp @@ -42,15 +42,20 @@ DEALINGS IN THE SOFTWARE. * @attention If you include this file, you'll need to link with `libz`. */ -#include +#include #include +#ifndef _MSC_VER +# include +#endif + #include #include #include #include #include +#include #include #include #include @@ -102,7 +107,7 @@ namespace osmium { explicit GzipCompressor(int fd, fsync sync) : Compressor(sync), - m_fd(dup(fd)), + m_fd(::dup(fd)), m_gzfile(::gzdopen(fd, "w")) { if (!m_gzfile) { detail::throw_gzip_error(m_gzfile, "write initialization failed"); @@ -171,6 +176,9 @@ namespace osmium { detail::throw_gzip_error(m_gzfile, "read failed"); } buffer.resize(static_cast(nread)); +#if ZLIB_VERNUM >= 0x1240 + set_offset(size_t(::gzoffset(m_gzfile))); +#endif return buffer; } diff --git a/third_party/libosmium/include/osmium/io/input_iterator.hpp b/third_party/libosmium/include/osmium/io/input_iterator.hpp index 8be9759303f..4cde92f5172 100644 --- a/third_party/libosmium/include/osmium/io/input_iterator.hpp +++ b/third_party/libosmium/include/osmium/io/input_iterator.hpp @@ -41,7 +41,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include namespace osmium { @@ -57,7 +56,7 @@ namespace osmium { static_assert(std::is_base_of::value, "TItem must derive from osmium::buffer::Item"); - typedef typename osmium::memory::Buffer::t_iterator item_iterator; + using item_iterator = typename osmium::memory::Buffer::t_iterator; TSource* m_source; std::shared_ptr m_buffer; @@ -69,20 +68,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(); - } while (m_iter == m_buffer->end()); + m_iter = m_buffer->select().begin(); + } while (m_iter == m_buffer->select().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 +98,7 @@ namespace osmium { assert(m_buffer); assert(m_iter); ++m_iter; - if (m_iter == m_buffer->end()) { + if (m_iter == m_buffer->select().end()) { update_buffer(); } return *this; diff --git a/third_party/libosmium/include/osmium/io/opl_input.hpp b/third_party/libosmium/include/osmium/io/opl_input.hpp new file mode 100644 index 00000000000..ee9e447d5be --- /dev/null +++ b/third_party/libosmium/include/osmium/io/opl_input.hpp @@ -0,0 +1,46 @@ +#ifndef OSMIUM_IO_OPL_INPUT_HPP +#define OSMIUM_IO_OPL_INPUT_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +/** + * @file + * + * Include this file if you want to read OSM OPL files. + * + */ + +#include // IWYU pragma: export +#include // IWYU pragma: export + +#endif // OSMIUM_IO_OPL_INPUT_HPP diff --git a/third_party/libosmium/include/osmium/io/output_iterator.hpp b/third_party/libosmium/include/osmium/io/output_iterator.hpp index ce050ebab61..cf9291df0e8 100644 --- a/third_party/libosmium/include/osmium/io/output_iterator.hpp +++ b/third_party/libosmium/include/osmium/io/output_iterator.hpp @@ -35,10 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include -#include -#include -#include #include #include @@ -51,12 +48,18 @@ namespace osmium { namespace io { template - class OutputIterator : public std::iterator { + 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/third_party/libosmium/include/osmium/io/overwrite.hpp b/third_party/libosmium/include/osmium/io/overwrite.hpp index 5361698a5f0..bc6a5038ce5 100644 --- a/third_party/libosmium/include/osmium/io/overwrite.hpp +++ b/third_party/libosmium/include/osmium/io/overwrite.hpp @@ -34,6 +34,6 @@ DEALINGS IN THE SOFTWARE. */ #pragma message("Including overwrite.hpp is deprecated, #include instead.") -#include +#include // IWYU pragma: keep #endif // OSMIUM_IO_OVERWRITE_HPP diff --git a/third_party/libosmium/include/osmium/io/reader.hpp b/third_party/libosmium/include/osmium/io/reader.hpp index 7c60511af86..12f97b8e66f 100644 --- a/third_party/libosmium/include/osmium/io/reader.hpp +++ b/third_party/libosmium/include/osmium/io/reader.hpp @@ -62,11 +62,26 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { namespace io { + namespace detail { + + inline size_t get_input_queue_size() noexcept { + const size_t n = osmium::config::get_max_queue_size("INPUT", 20); + return n > 2 ? n : 2; + } + + inline size_t get_osmdata_queue_size() noexcept { + const size_t n = osmium::config::get_max_queue_size("OSMDATA", 20); + return n > 2 ? n : 2; + } + + } // namespace detail + /** * This is the user-facing interface for reading OSM files. Instantiate * an object of this class with a file name or osmium::io::File object @@ -75,9 +90,6 @@ namespace osmium { */ class Reader { - static constexpr size_t max_input_queue_size = 20; // XXX - static constexpr size_t max_osmdata_queue_size = 20; // XXX - osmium::io::File m_file; osmium::osm_entity_bits::type m_read_which_entities; @@ -104,6 +116,8 @@ namespace osmium { osmium::thread::thread_handler m_thread; + size_t m_file_size; + // This function will run in a separate thread. static void parser_thread(const osmium::io::File& file, detail::future_string_queue_type& input_queue, @@ -111,8 +125,8 @@ namespace osmium { std::promise&& header_promise, osmium::osm_entity_bits::type read_which_entities) { std::promise promise = std::move(header_promise); - auto creator = detail::ParserFactory::instance().get_creator_function(file); - auto parser = creator(input_queue, osmdata_queue, promise, read_which_entities); + const auto creator = detail::ParserFactory::instance().get_creator_function(file); + const auto parser = creator(input_queue, osmdata_queue, promise, read_which_entities); parser->parse(); } @@ -133,7 +147,7 @@ namespace osmium { if (pipe(pipefd) < 0) { throw std::system_error(errno, std::system_category(), "opening pipe failed"); } - pid_t pid = fork(); + const pid_t pid = fork(); if (pid < 0) { throw std::system_error(errno, std::system_category(), "fork failed"); } @@ -202,16 +216,17 @@ namespace osmium { m_read_which_entities(read_which_entities), m_status(status::okay), m_childpid(0), - m_input_queue(max_input_queue_size, "raw_input"), + m_input_queue(detail::get_input_queue_size(), "raw_input"), m_decompressor(m_file.buffer() ? osmium::io::CompressionFactory::instance().create_decompressor(file.compression(), m_file.buffer(), m_file.buffer_size()) : osmium::io::CompressionFactory::instance().create_decompressor(file.compression(), open_input_file_or_url(m_file.filename(), &m_childpid))), m_read_thread_manager(*m_decompressor, m_input_queue), - m_osmdata_queue(max_osmdata_queue_size, "parser_results"), + m_osmdata_queue(detail::get_osmdata_queue_size(), "parser_results"), m_osmdata_queue_wrapper(m_osmdata_queue), m_header_future(), m_header(), - m_thread() { + m_thread(), + m_file_size(m_decompressor->file_size()) { std::promise header_promise; m_header_future = header_promise.get_future(); m_thread = osmium::thread::thread_handler{parser_thread, std::ref(m_file), std::ref(m_input_queue), std::ref(m_osmdata_queue), std::move(header_promise), read_which_entities}; @@ -263,7 +278,7 @@ namespace osmium { #ifndef _WIN32 if (m_childpid) { int status; - pid_t pid = ::waitpid(m_childpid, &status, 0); + const pid_t pid = ::waitpid(m_childpid, &status, 0); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { @@ -350,6 +365,32 @@ namespace osmium { return m_status == status::eof || m_status == status::closed; } + /** + * Get the size of the input file. Returns 0 if the file size + * is not available (for instance when reading from stdin). + */ + size_t file_size() const noexcept { + return m_file_size; + } + + /** + * Returns the current offset into the input file. Returns 0 if + * the offset is not available (for instance when reading from + * stdin). + * + * The offset can be used together with the result of file_size() + * to estimate how much of the file has been read. Note that due + * to buffering inside Osmium, this value will be larger than + * the amount of data actually available to the application. + * + * Do not call this function too often, certainly not for every + * object you are reading. Depending on the file type it might + * do an expensive system call. + */ + size_t offset() const noexcept { + return m_decompressor->offset(); + } + }; // class Reader /** diff --git a/third_party/libosmium/include/osmium/io/writer.hpp b/third_party/libosmium/include/osmium/io/writer.hpp index b389698fc8b..c12d31776bf 100644 --- a/third_party/libosmium/include/osmium/io/writer.hpp +++ b/third_party/libosmium/include/osmium/io/writer.hpp @@ -34,11 +34,13 @@ DEALINGS IN THE SOFTWARE. */ #include +#include +#include +#include #include +#include #include -#include #include -#include #include #include @@ -52,11 +54,25 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { + namespace memory { + class Item; + } //namespace memory + namespace io { + namespace detail { + + inline size_t get_output_queue_size() noexcept { + size_t n = osmium::config::get_max_queue_size("OUTPUT", 20); + return n > 2 ? n : 2; + } + + } // namespace detail + /** * This is the user-facing interface for writing OSM files. Instantiate * an object of this class with a file name or osmium::io::File object @@ -194,7 +210,7 @@ namespace osmium { template explicit Writer(const osmium::io::File& file, TArgs&&... args) : m_file(file.check()), - m_output_queue(20, "raw_output"), // XXX + m_output_queue(detail::get_output_queue_size(), "raw_output"), m_output(osmium::io::detail::OutputFormatFactory::instance().create_output(m_file, m_output_queue)), m_buffer(), m_buffer_size(default_buffer_size), @@ -304,7 +320,7 @@ namespace osmium { } try { m_buffer.push_back(item); - } catch (osmium::buffer_is_full&) { + } catch (const osmium::buffer_is_full&) { do_flush(); m_buffer.push_back(item); } diff --git a/third_party/libosmium/include/osmium/memory/buffer.hpp b/third_party/libosmium/include/osmium/memory/buffer.hpp index 07a31fed90e..8c246db02ca 100644 --- a/third_party/libosmium/include/osmium/memory/buffer.hpp +++ b/third_party/libosmium/include/osmium/memory/buffer.hpp @@ -517,6 +517,16 @@ namespace osmium { */ using const_iterator = t_const_iterator; + template + ItemIteratorRange select() { + return ItemIteratorRange{m_data, m_data + m_committed}; + } + + template + ItemIteratorRange select() const { + return ItemIteratorRange{m_data, m_data + m_committed}; + } + /** * Get iterator for iterating over all items of type T in the * buffer. diff --git a/third_party/libosmium/include/osmium/memory/collection.hpp b/third_party/libosmium/include/osmium/memory/collection.hpp index 17ace7025a3..2a2c0408897 100644 --- a/third_party/libosmium/include/osmium/memory/collection.hpp +++ b/third_party/libosmium/include/osmium/memory/collection.hpp @@ -44,17 +44,23 @@ namespace osmium { namespace memory { template - class CollectionIterator : public std::iterator { + 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::value, const unsigned char*, unsigned char*>::type data_type; + using data_type = typename std::conditional::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 iterator; - typedef CollectionIterator const_iterator; - typedef TMember value_type; + using iterator = CollectionIterator; + using const_iterator = CollectionIterator; + using value_type = TMember; static constexpr osmium::item_type itemtype = TCollectionItemType; diff --git a/third_party/libosmium/include/osmium/memory/item.hpp b/third_party/libosmium/include/osmium/memory/item.hpp index fd404ce93e6..2df33c7f5da 100644 --- a/third_party/libosmium/include/osmium/memory/item.hpp +++ b/third_party/libosmium/include/osmium/memory/item.hpp @@ -33,8 +33,10 @@ DEALINGS IN THE SOFTWARE. */ +#include #include -#include + +#include namespace osmium { @@ -45,16 +47,21 @@ namespace osmium { class Builder; } // namespace builder + enum class diff_indicator_type { + none = 0, + left = 1, + right = 2, + both = 3 + }; // diff_indicator_type + 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; - template - inline T padded_length(T length) noexcept { - static_assert(std::is_integral::value && std::is_unsigned::value, "Template parameter must be unsigned integral type"); + inline std::size_t padded_length(std::size_t length) noexcept { return (length + align_bytes - 1) & ~(align_bytes - 1); } @@ -100,7 +107,8 @@ namespace osmium { item_size_type m_size; item_type m_type; uint16_t m_removed : 1; - uint16_t m_padding : 15; + uint16_t m_diff : 2; + uint16_t m_padding : 13; template friend class CollectionIterator; @@ -121,6 +129,7 @@ namespace osmium { m_size(size), m_type(type), m_removed(false), + m_diff(0), m_padding(0) { } @@ -150,7 +159,7 @@ namespace osmium { } item_size_type padded_size() const { - return padded_length(m_size); + return static_cast_with_assert(padded_length(m_size)); } item_type type() const noexcept { @@ -165,6 +174,19 @@ namespace osmium { m_removed = removed; } + diff_indicator_type diff() const noexcept { + return diff_indicator_type(m_diff); + } + + char diff_as_char() const noexcept { + static constexpr const char* diff_chars = "*-+ "; + return diff_chars[m_diff]; + } + + void set_diff(diff_indicator_type diff) noexcept { + m_diff = uint16_t(diff); + } + }; // class Item static_assert(sizeof(Item) == 8, "Class osmium::Item has wrong size!"); diff --git a/third_party/libosmium/include/osmium/memory/item_iterator.hpp b/third_party/libosmium/include/osmium/memory/item_iterator.hpp index 3886c98705f..27ebc59f054 100644 --- a/third_party/libosmium/include/osmium/memory/item_iterator.hpp +++ b/third_party/libosmium/include/osmium/memory/item_iterator.hpp @@ -34,16 +34,29 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include #include #include -#include #include #include namespace osmium { + class Area; + class Changeset; + class InnerRing; + class Node; + class OSMEntity; + class OSMObject; + class OuterRing; + class Relation; + class RelationMemberList; + class TagList; + class Way; + class WayNodeList; + namespace memory { namespace detail { @@ -116,19 +129,19 @@ namespace osmium { } // namespace detail template - class ItemIterator : public std::iterator { + class ItemIterator { static_assert(std::is_base_of::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::value, const unsigned char*, unsigned char*>::type data_type; + using data_type = typename std::conditional::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::type>(reinterpret_cast(m_data)->type())) { m_data = reinterpret_cast(m_data)->next(); @@ -137,23 +150,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 - ItemIterator cast() const { + ItemIterator cast() const noexcept { return ItemIterator(m_data, m_end); } - ItemIterator& operator++() { + ItemIterator& operator++() noexcept { assert(m_data); assert(m_data != m_end); m_data = reinterpret_cast(m_data)->next(); @@ -166,45 +185,50 @@ namespace osmium { * types. Do not use this unless you know what you are * doing. */ - ItemIterator& advance_once() { + ItemIterator& advance_once() noexcept { assert(m_data); assert(m_data != m_end); m_data = reinterpret_cast(m_data)->next(); return *static_cast*>(this); } - ItemIterator operator++(int) { + ItemIterator operator++(int) noexcept { ItemIterator tmp(*this); operator++(); return tmp; } - bool operator==(const ItemIterator& rhs) const { + bool operator==(const ItemIterator& rhs) const noexcept { return m_data == rhs.m_data && m_end == rhs.m_end; } - bool operator!=(const ItemIterator& rhs) const { + bool operator!=(const ItemIterator& 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(m_data); } - TMember* operator->() const { + TMember* operator->() const noexcept { assert(m_data); assert(m_data != m_end); return reinterpret_cast(m_data); } - explicit operator bool() const { + explicit operator bool() const noexcept { return (m_data != nullptr) && (m_data != m_end); } @@ -221,6 +245,77 @@ namespace osmium { return out; } + template + class ItemIteratorRange { + + static_assert(std::is_base_of::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::value, const unsigned char*, unsigned char*>::type; + + data_type m_begin; + data_type m_end; + + public: + + using iterator = ItemIterator; + using const_iterator = ItemIterator; + + 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/third_party/libosmium/include/osmium/object_pointer_collection.hpp b/third_party/libosmium/include/osmium/object_pointer_collection.hpp index 09a52934e04..4db1c084d1a 100644 --- a/third_party/libosmium/include/osmium/object_pointer_collection.hpp +++ b/third_party/libosmium/include/osmium/object_pointer_collection.hpp @@ -40,11 +40,9 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include // IWYU pragma: no_forward_declare osmium::OSMObject -// IWYU pragma: no_forward_declare osmium::memory::Item namespace osmium { @@ -70,8 +68,8 @@ namespace osmium { public: - typedef boost::indirect_iterator::iterator, osmium::OSMObject> iterator; - typedef boost::indirect_iterator::const_iterator, const osmium::OSMObject> const_iterator; + using iterator = boost::indirect_iterator::iterator, osmium::OSMObject>; + using const_iterator = boost::indirect_iterator::const_iterator, const osmium::OSMObject>; ObjectPointerCollection() noexcept : m_objects() { diff --git a/third_party/libosmium/include/osmium/opl.hpp b/third_party/libosmium/include/osmium/opl.hpp new file mode 100644 index 00000000000..5666fa0de94 --- /dev/null +++ b/third_party/libosmium/include/osmium/opl.hpp @@ -0,0 +1,67 @@ +#ifndef OSMIUM_OPL_HPP +#define OSMIUM_OPL_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include +#include + +namespace osmium { + + /** + * Parses one line in OPL format. The line must not have a newline + * character at the end. Buffer.commit() is called automatically if the + * write succeeded. + * + * @param data Line must be in this zero-delimited string. + * @param buffer Result will be written to this buffer. + * + * @returns true if an entity was parsed, false otherwise (for instance + * when the line is empty). + * @throws osmium::opl_error If the parsing fails. + */ + inline bool opl_parse(const char* data, osmium::memory::Buffer& buffer) { + try { + const bool wrote_something = osmium::io::detail::opl_parse_line(0, data, buffer); + buffer.commit(); + return wrote_something; + } catch (const osmium::opl_error&) { + buffer.rollback(); + throw; + } + } + +} // namespace osmium + + +#endif // OSMIUM_OPL_HPP diff --git a/third_party/libosmium/include/osmium/osm.hpp b/third_party/libosmium/include/osmium/osm.hpp index 594db7572ac..fa8a92da451 100644 --- a/third_party/libosmium/include/osmium/osm.hpp +++ b/third_party/libosmium/include/osmium/osm.hpp @@ -33,11 +33,20 @@ DEALINGS IN THE SOFTWARE. */ -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export /** * @brief Namespace for everything in the Osmium library. diff --git a/third_party/libosmium/include/osmium/osm/area.hpp b/third_party/libosmium/include/osmium/osm/area.hpp index ee232f0bd6f..490fbe95b62 100644 --- a/third_party/libosmium/include/osmium/osm/area.hpp +++ b/third_party/libosmium/include/osmium/osm/area.hpp @@ -35,14 +35,17 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include #include +#include #include #include #include #include +#include namespace osmium { @@ -144,8 +147,8 @@ namespace osmium { * * @returns Pair (number outer rings, number inner rings) */ - std::pair num_rings() const { - std::pair counter { 0, 0 }; + std::pair num_rings() const { + std::pair counter { 0, 0 }; for (auto it = cbegin(); it != cend(); ++it) { switch (it->type()) { @@ -185,27 +188,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 inner_ring_cbegin(const osmium::memory::ItemIterator& it) const { + OSMIUM_DEPRECATED osmium::memory::ItemIterator inner_ring_cbegin(const osmium::memory::ItemIterator& it) const { return it.cast(); } /** + * @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 inner_ring_cend(const osmium::memory::ItemIterator& it) const { + OSMIUM_DEPRECATED osmium::memory::ItemIterator inner_ring_cend(const osmium::memory::ItemIterator& it) const { return std::next(it).cast(); } + /** + * 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 outer_rings() const { + return subitems(); + } + + /** + * 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 inner_rings(const osmium::OuterRing& outer) const { + osmium::memory::ItemIteratorRange outer_range{outer.data(), next()}; + return osmium::memory::ItemIteratorRange{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/third_party/libosmium/include/osmium/osm/box.hpp b/third_party/libosmium/include/osmium/osm/box.hpp index 6fcf48d6ca4..52ca93dd171 100644 --- a/third_party/libosmium/include/osmium/osm/box.hpp +++ b/third_party/libosmium/include/osmium/osm/box.hpp @@ -36,7 +36,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include namespace osmium { diff --git a/third_party/libosmium/include/osmium/osm/changeset.hpp b/third_party/libosmium/include/osmium/osm/changeset.hpp index f59db480845..8a503caaca4 100644 --- a/third_party/libosmium/include/osmium/osm/changeset.hpp +++ b/third_party/libosmium/include/osmium/osm/changeset.hpp @@ -33,7 +33,9 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include #include @@ -131,7 +133,7 @@ namespace osmium { public: - typedef size_t size_type; + using size_type = size_t; ChangesetDiscussion() : osmium::memory::Collection() { @@ -185,6 +187,11 @@ namespace osmium { public: + // Dummy to avoid warning because of unused private fields. Do not use. + int32_t do_not_use() const noexcept { + return m_padding1 + m_padding2; + } + /// Get ID of this changeset changeset_id_type id() const noexcept { return m_id; @@ -369,23 +376,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 iterator; - typedef osmium::memory::CollectionIterator const_iterator; + using iterator = osmium::memory::CollectionIterator; + using const_iterator = osmium::memory::CollectionIterator; iterator begin() { return iterator(subitems_position()); diff --git a/third_party/libosmium/include/osmium/osm/crc.hpp b/third_party/libosmium/include/osmium/osm/crc.hpp index ff39996f5e2..2abeac4a1d8 100644 --- a/third_party/libosmium/include/osmium/osm/crc.hpp +++ b/third_party/libosmium/include/osmium/osm/crc.hpp @@ -36,11 +36,17 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include +#include #include #include +#include #include +#include #include +#include +#include #include #include @@ -71,8 +77,8 @@ namespace osmium { # if defined(__GNUC__) || defined(__clang__) return __builtin_bswap64(value); # else - uint64_t val1 = byte_swap_32(value & 0xFFFFFFFF); - uint64_t val2 = byte_swap_32(value >> 32); + const uint64_t val1 = byte_swap_32(value & 0xFFFFFFFF); + const uint64_t val2 = byte_swap_32(value >> 32); return (val1 << 32) | val2; # endif } @@ -86,11 +92,11 @@ namespace osmium { public: - TCRC& operator()() { + TCRC& operator()() noexcept { return m_crc; } - const TCRC& operator()() const { + const TCRC& operator()() const noexcept { return m_crc; } @@ -106,7 +112,7 @@ namespace osmium { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint16_t)); #else - uint16_t v = osmium::util::byte_swap_16(value); + const uint16_t v = osmium::util::byte_swap_16(value); m_crc.process_bytes(&v, sizeof(uint16_t)); #endif } @@ -115,7 +121,7 @@ namespace osmium { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint32_t)); #else - uint32_t v = osmium::util::byte_swap_32(value); + const uint32_t v = osmium::util::byte_swap_32(value); m_crc.process_bytes(&v, sizeof(uint32_t)); #endif } @@ -124,7 +130,7 @@ namespace osmium { #if __BYTE_ORDER == __LITTLE_ENDIAN m_crc.process_bytes(&value, sizeof(uint64_t)); #else - uint64_t v = osmium::util::byte_swap_64(value); + const uint64_t v = osmium::util::byte_swap_64(value); m_crc.process_bytes(&v, sizeof(uint64_t)); #endif } @@ -151,6 +157,7 @@ namespace osmium { void update(const NodeRef& node_ref) { update_int64(node_ref.ref()); + update(node_ref.location()); } void update(const NodeRefList& node_refs) { @@ -205,10 +212,10 @@ namespace osmium { void update(const osmium::Area& area) { update(static_cast(area)); - for (auto it = area.cbegin(); it != area.cend(); ++it) { - if (it->type() == osmium::item_type::outer_ring || - it->type() == osmium::item_type::inner_ring) { - update(static_cast(*it)); + for (const auto& subitem : area) { + if (subitem.type() == osmium::item_type::outer_ring || + subitem.type() == osmium::item_type::inner_ring) { + update(static_cast(subitem)); } } } diff --git a/third_party/libosmium/include/osmium/osm/diff_object.hpp b/third_party/libosmium/include/osmium/osm/diff_object.hpp index 609ab74531c..21cf139a84f 100644 --- a/third_party/libosmium/include/osmium/osm/diff_object.hpp +++ b/third_party/libosmium/include/osmium/osm/diff_object.hpp @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include -#include #include #include #include @@ -43,6 +42,10 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + class Node; + class Way; + class Relation; + /** * A DiffObject holds pointers to three OSMObjects, the current object, * the previous, and the next. They always have the same type (Node, Way, diff --git a/third_party/libosmium/include/osmium/osm/entity_bits.hpp b/third_party/libosmium/include/osmium/osm/entity_bits.hpp index 50b3e4cb082..b8e9ddba830 100644 --- a/third_party/libosmium/include/osmium/osm/entity_bits.hpp +++ b/third_party/libosmium/include/osmium/osm/entity_bits.hpp @@ -33,12 +33,15 @@ DEALINGS IN THE SOFTWARE. */ +#include +#include + #include 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(0x1 << (static_cast(item_type) - 1)); + auto ut = static_cast::type>(item_type); + assert(ut <= 0x05); + if (ut == 0) { + return nothing; + } + return static_cast(0x1 << (ut - 1)); } } // namespace osm_entity_bits diff --git a/third_party/libosmium/include/osmium/osm/location.hpp b/third_party/libosmium/include/osmium/osm/location.hpp index 85f4b162aaf..c5da620cb39 100644 --- a/third_party/libosmium/include/osmium/osm/location.hpp +++ b/third_party/libosmium/include/osmium/osm/location.hpp @@ -35,15 +35,14 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include #include +#include +#include #include #include -#include - -#include -#include - namespace osmium { /** @@ -62,6 +61,184 @@ namespace osmium { }; // struct invalid_location + namespace detail { + + constexpr const int coordinate_precision = 10000000; + + // Convert string with a floating point number into integer suitable + // for use as coordinate in a Location. + inline int32_t string_to_location_coordinate(const char** data) { + const char* str = *data; + const char* full = str; + + int64_t result = 0; + int sign = 1; + + // one more than significant digits to allow rounding + int64_t scale = 8; + + // paranoia check for maximum number of digits + int max_digits = 10; + + // optional minus sign + if (*str == '-') { + sign = -1; + ++str; + } + + // there has to be at least one digit + if (*str >= '0' && *str <= '9') { + result = *str - '0'; + ++str; + } else { + goto error; + } + + // optional additional digits before decimal point + while (*str >= '0' && *str <= '9' && max_digits > 0) { + result = result * 10 + (*str - '0'); + ++str; + --max_digits; + } + + if (max_digits == 0) { + goto error; + } + + // optional decimal point + if (*str == '.') { + ++str; + + // read significant digits + for (; scale > 0 && *str >= '0' && *str <= '9'; --scale, ++str) { + result = result * 10 + (*str - '0'); + } + + // ignore non-significant digits + max_digits = 20; + while (*str >= '0' && *str <= '9' && max_digits > 0) { + ++str; + --max_digits; + } + + if (max_digits == 0) { + goto error; + } + } + + // optional exponent in scientific notation + if (*str == 'e' || *str == 'E') { + ++str; + + int esign = 1; + // optional minus sign + if (*str == '-') { + esign = -1; + ++str; + } + + int64_t eresult = 0; + + // there has to be at least one digit in exponent + if (*str >= '0' && *str <= '9') { + eresult = *str - '0'; + ++str; + } else { + goto error; + } + + // optional additional digits in exponent + max_digits = 5; + while (*str >= '0' && *str <= '9' && max_digits > 0) { + eresult = eresult * 10 + (*str - '0'); + ++str; + --max_digits; + } + + if (max_digits == 0) { + goto error; + } + + scale += eresult * esign; + } + + if (scale < 0) { + result = 0; + } else { + for (; scale > 0; --scale) { + result *= 10; + } + + result = (result + 5) / 10 * sign; + + if (result > std::numeric_limits::max() || + result < std::numeric_limits::min()) { + goto error; + } + } + + *data = str; + return static_cast(result); + + error: + + throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"}; + } + + // Convert integer as used by location for coordinates into a string. + template + inline T append_location_coordinate_to_string(T iterator, int32_t value) { + // handle negative values + if (value < 0) { + *iterator++ = '-'; + value = -value; + } + + // write digits into temporary buffer + int32_t v = value; + char temp[10]; + char* t = temp; + do { + *t++ = char(v % 10) + '0'; + v /= 10; + } while (v != 0); + + while (t-temp < 7) { + *t++ = '0'; + } + + // write out digits before decimal point + if (value >= coordinate_precision) { + if (value >= 10 * coordinate_precision) { + if (value >= 100 * coordinate_precision) { + *iterator++ = *--t; + } + *iterator++ = *--t; + } + *iterator++ = *--t; + } else { + *iterator++ = '0'; + } + + // remove trailing zeros + const char* tn = temp; + while (tn < t && *tn == '0') { + ++tn; + } + + // decimal point + if (t != tn) { + *iterator++ = '.'; + while (t != tn) { + *iterator++ = *--t; + } + } + + return iterator; + } + + } // namespace detail + /** * Locations define a place on earth. * @@ -89,14 +266,12 @@ namespace osmium { // static constexpr int32_t undefined_coordinate = std::numeric_limits::max(); static constexpr int32_t undefined_coordinate = 2147483647; - static constexpr int coordinate_precision = 10000000; - static int32_t double_to_fix(const double c) noexcept { - return static_cast(std::round(c * coordinate_precision)); + return static_cast(std::round(c * detail::coordinate_precision)); } static constexpr double fix_to_double(const int32_t c) noexcept { - return static_cast(c) / coordinate_precision; + return static_cast(c) / detail::coordinate_precision; } /** @@ -154,10 +329,10 @@ namespace osmium { * usual bounds (-180<=lon<=180, -90<=lat<=90). */ constexpr bool valid() const noexcept { - return m_x >= -180 * coordinate_precision - && m_x <= 180 * coordinate_precision - && m_y >= -90 * coordinate_precision - && m_y <= 90 * coordinate_precision; + return m_x >= -180 * detail::coordinate_precision + && m_x <= 180 * detail::coordinate_precision + && m_y >= -90 * detail::coordinate_precision + && m_y <= 90 * detail::coordinate_precision; } constexpr int32_t x() const noexcept { @@ -226,11 +401,47 @@ namespace osmium { return *this; } + Location& set_lon(const char* str) { + const char** data = &str; + m_x = detail::string_to_location_coordinate(data); + if (**data != '\0') { + throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"}; + } + return *this; + } + + Location& set_lat(const char* str) { + const char** data = &str; + m_y = detail::string_to_location_coordinate(data); + if (**data != '\0') { + throw invalid_location{std::string{"characters after coordinate: '"} + *data + "'"}; + } + return *this; + } + + Location& set_lon_partial(const char** str) { + m_x = detail::string_to_location_coordinate(str); + return *this; + } + + Location& set_lat_partial(const char** str) { + m_y = detail::string_to_location_coordinate(str); + return *this; + } + template - T as_string(T iterator, const char separator) const { - iterator = osmium::util::double2string(iterator, lon(), 7); + T as_string_without_check(T iterator, const char separator = ',') const { + iterator = detail::append_location_coordinate_to_string(iterator, x()); *iterator++ = separator; - return osmium::util::double2string(iterator, lat(), 7); + return detail::append_location_coordinate_to_string(iterator, y()); + } + + template + T as_string(T iterator, const char separator = ',') const { + if (!valid()) { + throw osmium::invalid_location("invalid location"); + } + return as_string_without_check(iterator, separator); } }; // class Location @@ -273,13 +484,52 @@ namespace osmium { template inline std::basic_ostream& operator<<(std::basic_ostream& out, const osmium::Location& location) { if (location) { - out << '(' << location.lon() << ',' << location.lat() << ')'; + out << '('; + location.as_string(std::ostream_iterator(out), ','); + out << ')'; } else { out << "(undefined,undefined)"; } return out; } + namespace detail { + + template + 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 { + using argument_type = osmium::Location; + using result_type = size_t; + size_t operator()(const osmium::Location& location) const noexcept { + return osmium::detail::hash(location); + } + }; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +} // namespace std + #endif // OSMIUM_OSM_LOCATION_HPP diff --git a/third_party/libosmium/include/osmium/osm/node_ref_list.hpp b/third_party/libosmium/include/osmium/osm/node_ref_list.hpp index 84edc076090..6cfdf229587 100644 --- a/third_party/libosmium/include/osmium/osm/node_ref_list.hpp +++ b/third_party/libosmium/include/osmium/osm/node_ref_list.hpp @@ -39,6 +39,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include namespace osmium { @@ -66,7 +67,7 @@ namespace osmium { * Returns the number of NodeRefs in the collection. */ size_t size() const noexcept { - auto size_node_refs = byte_size() - sizeof(NodeRefList); + const auto size_node_refs = byte_size() - sizeof(NodeRefList); assert(size_node_refs % sizeof(NodeRef) == 0); return size_node_refs / sizeof(NodeRef); } diff --git a/third_party/libosmium/include/osmium/osm/object.hpp b/third_party/libosmium/include/osmium/osm/object.hpp index 6d1de6f6799..caa6fbcdd70 100644 --- a/third_party/libosmium/include/osmium/osm/object.hpp +++ b/third_party/libosmium/include/osmium/osm/object.hpp @@ -33,11 +33,10 @@ DEALINGS IN THE SOFTWARE. */ -#include -#include #include #include #include +#include #include #include @@ -49,6 +48,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include namespace osmium { @@ -172,9 +172,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')"); @@ -286,6 +286,20 @@ namespace osmium { return *this; } + /** + * Set the timestamp when this object last changed. + * + * @param timestamp Timestamp in ISO format. + * @returns Reference to object to make calls chainable. + */ + OSMObject& set_timestamp(const char* timestamp) { + m_timestamp = detail::parse_timestamp(timestamp); + if (timestamp[20] != '\0') { + throw std::invalid_argument{"can not parse timestamp"}; + } + return *this; + } + /// Get user name for this object. const char* user() const noexcept { return reinterpret_cast(data() + sizeof_object()); @@ -311,25 +325,28 @@ namespace osmium { * * @param attr Name of the attribute (must be one of "id", "version", "changeset", "timestamp", "uid", "visible") * @param value Value of the attribute + * @returns Reference to object to make calls chainable. */ - void set_attribute(const char* attr, const char* value) { - if (!strcmp(attr, "id")) { + OSMObject& set_attribute(const char* attr, const char* value) { + 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")) { - set_timestamp(osmium::Timestamp(value)); - } else if (!strcmp(attr, "uid")) { + } else if (!std::strcmp(attr, "timestamp")) { + set_timestamp(value); + } else if (!std::strcmp(attr, "uid")) { set_uid(value); - } else if (!strcmp(attr, "visible")) { + } else if (!std::strcmp(attr, "visible")) { set_visible(value); } + + return *this; } - typedef osmium::memory::CollectionIterator iterator; - typedef osmium::memory::CollectionIterator const_iterator; + using iterator = osmium::memory::CollectionIterator; + using const_iterator = osmium::memory::CollectionIterator; iterator begin() { return iterator(subitems_position()); @@ -355,6 +372,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 + osmium::memory::ItemIteratorRange subitems() { + return osmium::memory::ItemIteratorRange{subitems_position(), next()}; + } + + /** + * Get a range of subitems of a specific type. + * + * @tparam The type (must be derived from osmium::memory::Item. + */ + template + osmium::memory::ItemIteratorRange subitems() const { + return osmium::memory::ItemIteratorRange{subitems_position(), next()}; + } + template using t_iterator = osmium::memory::ItemIterator; @@ -399,8 +436,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 +446,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/third_party/libosmium/include/osmium/osm/object_comparisons.hpp b/third_party/libosmium/include/osmium/osm/object_comparisons.hpp index fe3529b4ace..aa0241d2625 100644 --- a/third_party/libosmium/include/osmium/osm/object_comparisons.hpp +++ b/third_party/libosmium/include/osmium/osm/object_comparisons.hpp @@ -33,12 +33,17 @@ DEALINGS IN THE SOFTWARE. */ +#include + #include +#include +#include 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 +58,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 +75,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 +91,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/third_party/libosmium/include/osmium/osm/relation.hpp b/third_party/libosmium/include/osmium/osm/relation.hpp index 9c4e69cc734..2aa9caaf152 100644 --- a/third_party/libosmium/include/osmium/osm/relation.hpp +++ b/third_party/libosmium/include/osmium/osm/relation.hpp @@ -33,13 +33,13 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include #include // IWYU pragma: keep #include +#include #include #include #include @@ -149,7 +149,7 @@ namespace osmium { public: - typedef size_t size_type; + using size_type = size_t; RelationMemberList() : osmium::memory::Collection() { diff --git a/third_party/libosmium/include/osmium/osm/segment.hpp b/third_party/libosmium/include/osmium/osm/segment.hpp index d35f9706609..c36533e0cf7 100644 --- a/third_party/libosmium/include/osmium/osm/segment.hpp +++ b/third_party/libosmium/include/osmium/osm/segment.hpp @@ -37,7 +37,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include namespace osmium { diff --git a/third_party/libosmium/include/osmium/osm/tag.hpp b/third_party/libosmium/include/osmium/osm/tag.hpp index 3f1a29826d3..cd2a913b7a3 100644 --- a/third_party/libosmium/include/osmium/osm/tag.hpp +++ b/third_party/libosmium/include/osmium/osm/tag.hpp @@ -34,7 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include -#include +#include #include #include #include @@ -87,7 +87,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 +104,72 @@ namespace osmium { class TagList : public osmium::memory::Collection { + 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() { } + /** + * Returns the number of tags in this tag list. + */ size_type size() const noexcept { return static_cast(std::distance(begin(), end())); } + /** + * Get tag value for the given tag key. If the key is not set, returns + * the default_value. + * + * @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/third_party/libosmium/include/osmium/osm/timestamp.hpp b/third_party/libosmium/include/osmium/osm/timestamp.hpp index 613752e68dc..5f5243028bf 100644 --- a/third_party/libosmium/include/osmium/osm/timestamp.hpp +++ b/third_party/libosmium/include/osmium/osm/timestamp.hpp @@ -40,12 +40,71 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include // IWYU pragma: keep namespace osmium { + namespace detail { + + inline time_t parse_timestamp(const char* str) { + static const int mon_lengths[] = { + 31, 29, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 + }; + if (str[ 0] >= '0' && str[ 0] <= '9' && + str[ 1] >= '0' && str[ 1] <= '9' && + str[ 2] >= '0' && str[ 2] <= '9' && + str[ 3] >= '0' && str[ 3] <= '9' && + str[ 4] == '-' && + str[ 5] >= '0' && str[ 5] <= '9' && + str[ 6] >= '0' && str[ 6] <= '9' && + str[ 7] == '-' && + str[ 8] >= '0' && str[ 8] <= '9' && + str[ 9] >= '0' && str[ 9] <= '9' && + str[10] == 'T' && + str[11] >= '0' && str[11] <= '9' && + str[12] >= '0' && str[12] <= '9' && + str[13] == ':' && + str[14] >= '0' && str[14] <= '9' && + str[15] >= '0' && str[15] <= '9' && + str[16] == ':' && + str[17] >= '0' && str[17] <= '9' && + str[18] >= '0' && str[18] <= '9' && + str[19] == 'Z') { + struct tm tm; + tm.tm_year = (str[ 0] - '0') * 1000 + + (str[ 1] - '0') * 100 + + (str[ 2] - '0') * 10 + + (str[ 3] - '0') - 1900; + tm.tm_mon = (str[ 5] - '0') * 10 + (str[ 6] - '0') - 1; + tm.tm_mday = (str[ 8] - '0') * 10 + (str[ 9] - '0'); + tm.tm_hour = (str[11] - '0') * 10 + (str[12] - '0'); + tm.tm_min = (str[14] - '0') * 10 + (str[15] - '0'); + tm.tm_sec = (str[17] - '0') * 10 + (str[18] - '0'); + tm.tm_wday = 0; + tm.tm_yday = 0; + tm.tm_isdst = 0; + if (tm.tm_year >= 0 && + tm.tm_mon >= 0 && tm.tm_mon <= 11 && + tm.tm_mday >= 1 && tm.tm_mday <= mon_lengths[tm.tm_mon] && + tm.tm_hour >= 0 && tm.tm_hour <= 23 && + tm.tm_min >= 0 && tm.tm_min <= 59 && + tm.tm_sec >= 0 && tm.tm_sec <= 60) { +#ifndef _WIN32 + return timegm(&tm); +#else + return _mkgmtime(&tm); +#endif + } + } + throw std::invalid_argument{"can not parse timestamp"}; + } + + } // namespace detail + /** * A timestamp. Internal representation is an unsigned 32bit integer * holding seconds since epoch (1970-01-01T00:00:00Z), so this will @@ -56,7 +115,7 @@ namespace osmium { class Timestamp { // length of ISO timestamp string yyyy-mm-ddThh:mm:ssZ\0 - static constexpr int timestamp_length = 20 + 1; + static constexpr const int timestamp_length = 20 + 1; // The timestamp format for OSM timestamps in strftime(3) format. // This is the ISO-Format "yyyy-mm-ddThh:mm:ssZ". @@ -97,27 +156,7 @@ namespace osmium { * @throws std::invalid_argument if the timestamp can not be parsed. */ explicit Timestamp(const char* timestamp) { -#ifndef _WIN32 - struct tm tm { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - if (strptime(timestamp, timestamp_format(), &tm) == nullptr) { - throw std::invalid_argument("can't parse timestamp"); - } - m_timestamp = static_cast(timegm(&tm)); -#else - struct tm tm; - int n = sscanf(timestamp, "%4d-%2d-%2dT%2d:%2d:%2dZ", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); - if (n != 6) { - throw std::invalid_argument("can't parse timestamp"); - } - tm.tm_year -= 1900; - tm.tm_mon--; - tm.tm_wday = 0; - tm.tm_yday = 0; - tm.tm_isdst = 0; - m_timestamp = static_cast(_mkgmtime(&tm)); -#endif + m_timestamp = static_cast(detail::parse_timestamp(timestamp)); } /** diff --git a/third_party/libosmium/include/osmium/osm/types.hpp b/third_party/libosmium/include/osmium/osm/types.hpp index 984dd135c1a..ec46bb2cd20 100644 --- a/third_party/libosmium/include/osmium/osm/types.hpp +++ b/third_party/libosmium/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/third_party/libosmium/include/osmium/osm/types_from_string.hpp b/third_party/libosmium/include/osmium/osm/types_from_string.hpp index aed0648984b..190dd2983bc 100644 --- a/third_party/libosmium/include/osmium/osm/types_from_string.hpp +++ b/third_party/libosmium/include/osmium/osm/types_from_string.hpp @@ -35,13 +35,14 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include +#include #include #include #include +#include #include #include @@ -50,7 +51,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 +71,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 string_to_object_id(const char* input, osmium::osm_entity_bits::type types) { + inline std::pair + 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 +133,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/third_party/libosmium/include/osmium/osm/way.hpp b/third_party/libosmium/include/osmium/osm/way.hpp index 3bc30b0c6fa..f6713fef46f 100644 --- a/third_party/libosmium/include/osmium/osm/way.hpp +++ b/third_party/libosmium/include/osmium/osm/way.hpp @@ -33,12 +33,13 @@ DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include -#include -#include #include #include +#include namespace osmium { diff --git a/third_party/libosmium/include/osmium/relations/collector.hpp b/third_party/libosmium/include/osmium/relations/collector.hpp index 7d7d14d0bad..b8455b4ebe9 100644 --- a/third_party/libosmium/include/osmium/relations/collector.hpp +++ b/third_party/libosmium/include/osmium/relations/collector.hpp @@ -39,13 +39,12 @@ DEALINGS IN THE SOFTWARE. #include #include #include -//#include +#include #include -#include #include #include -#include // IWYU pragma: keep +#include #include #include #include @@ -57,6 +56,9 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + class Node; + class Way; + /** * @brief Code related to the assembly of OSM relations */ @@ -64,13 +66,6 @@ namespace osmium { namespace detail { - template - inline typename std::iterator_traits::difference_type count_not_removed(const R& range) { - return std::count_if(range.begin(), range.end(), [](MemberMeta& mm) { - return !mm.removed(); - }); - } - } // namespace detail /** @@ -113,7 +108,7 @@ namespace osmium { public: - HandlerPass1(TCollector& collector) noexcept : + explicit HandlerPass1(TCollector& collector) noexcept : m_collector(collector) { } @@ -136,7 +131,7 @@ namespace osmium { public: - HandlerPass2(TCollector& collector) noexcept : + explicit HandlerPass2(TCollector& collector) noexcept : m_collector(collector) { } @@ -193,14 +188,14 @@ namespace osmium { int m_count_complete = 0; - typedef std::function callback_func_type; + using callback_func_type = std::function; callback_func_type m_callback; static constexpr size_t initial_buffer_size = 1024 * 1024; iterator_range find_member_meta(osmium::item_type type, osmium::object_id_type id) { auto& mmv = member_meta(type); - return iterator_range{std::equal_range(mmv.begin(), mmv.end(), MemberMeta(id))}; + return make_range(std::equal_range(mmv.begin(), mmv.end(), MemberMeta(id))); } public: @@ -313,6 +308,7 @@ namespace osmium { } const osmium::Relation& get_relation(size_t offset) const { + assert(m_relations_buffer.committed() > offset); return m_relations_buffer.get(offset); } @@ -323,7 +319,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(offset); } @@ -360,7 +364,6 @@ namespace osmium { } else { m_relations_buffer.commit(); m_relations.push_back(std::move(relation_meta)); -// std::cerr << "added relation id=" << relation.id() << "\n"; } } @@ -369,15 +372,17 @@ namespace osmium { * search on them. */ void sort_member_meta() { -/* std::cerr << "relations: " << m_relations.size() << "\n"; - std::cerr << "node members: " << m_member_meta[0].size() << "\n"; - std::cerr << "way members: " << m_member_meta[1].size() << "\n"; - std::cerr << "relation members: " << m_member_meta[2].size() << "\n";*/ std::sort(m_member_meta[0].begin(), m_member_meta[0].end()); std::sort(m_member_meta[1].begin(), m_member_meta[1].end()); std::sort(m_member_meta[2].begin(), m_member_meta[2].end()); } + static typename iterator_range::iterator::difference_type count_not_removed(const iterator_range& 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 +393,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; } @@ -409,9 +414,7 @@ namespace osmium { assert(member_meta.member_id() == object.id()); assert(member_meta.relation_pos() < m_relations.size()); RelationMeta& relation_meta = m_relations[member_meta.relation_pos()]; -// std::cerr << " => " << member_meta.member_pos() << " < " << get_relation(relation_meta).members().size() << " (id=" << get_relation(relation_meta).id() << ")\n"; assert(member_meta.member_pos() < get_relation(relation_meta).members().size()); -// std::cerr << " add way " << member_meta.member_id() << " to rel " << get_relation(relation_meta).id() << " at pos " << member_meta.member_pos() << "\n"; relation_meta.got_one_member(); if (relation_meta.has_all_members()) { const size_t relation_offset = member_meta.relation_pos(); @@ -429,17 +432,17 @@ namespace osmium { const osmium::Relation& relation = get_relation(relation_meta); for (const auto& member : relation.members()) { if (member.ref() != 0) { - auto range = find_member_meta(member.type(), member.ref()); + const auto range = find_member_meta(member.type(), member.ref()); assert(!range.empty()); // 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/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp b/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp index f0e9c362516..b28dca1d01c 100644 --- a/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp +++ b/third_party/libosmium/include/osmium/relations/detail/member_meta.hpp @@ -33,10 +33,8 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include -#include #include @@ -132,7 +130,7 @@ namespace osmium { template inline std::basic_ostream& operator<<(std::basic_ostream& 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/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp b/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp index 93aa41cf213..b71c5a5f72e 100644 --- a/third_party/libosmium/include/osmium/relations/detail/relation_meta.hpp +++ b/third_party/libosmium/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/third_party/libosmium/include/osmium/tags/filter.hpp b/third_party/libosmium/include/osmium/tags/filter.hpp index 407992e6d70..27a836061f7 100644 --- a/third_party/libosmium/include/osmium/tags/filter.hpp +++ b/third_party/libosmium/include/osmium/tags/filter.hpp @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -76,8 +77,8 @@ namespace osmium { template , typename TValueComp=match_value> class Filter { - typedef TKey key_type; - typedef typename std::conditional::value, bool, TValue>::type value_type; + using key_type = TKey; + using value_type = typename std::conditional::value, bool, TValue>::type; struct Rule { key_type key; @@ -106,10 +107,10 @@ namespace osmium { public: - typedef Filter filter_type; - typedef const osmium::Tag& argument_type; - typedef bool result_type; - typedef boost::filter_iterator iterator; + using filter_type = Filter; + using argument_type = const osmium::Tag&; + using result_type = bool; + using iterator = boost::filter_iterator; explicit Filter(bool default_result = false) : m_default_result(default_result) { @@ -151,9 +152,9 @@ namespace osmium { }; // class Filter - typedef Filter KeyValueFilter; - typedef Filter KeyFilter; - typedef Filter KeyPrefixFilter; + using KeyValueFilter = Filter; + using KeyFilter = Filter; + using KeyPrefixFilter = Filter; } // namespace tags diff --git a/third_party/libosmium/include/osmium/tags/regex_filter.hpp b/third_party/libosmium/include/osmium/tags/regex_filter.hpp index 8ea6d60c370..3df94dd0eac 100644 --- a/third_party/libosmium/include/osmium/tags/regex_filter.hpp +++ b/third_party/libosmium/include/osmium/tags/regex_filter.hpp @@ -49,7 +49,7 @@ namespace osmium { } }; // struct match_value - typedef Filter RegexFilter; + using RegexFilter = Filter; } // namespace tags diff --git a/third_party/libosmium/include/osmium/tags/taglist.hpp b/third_party/libosmium/include/osmium/tags/taglist.hpp index b1f346fe142..d7862798cc0 100644 --- a/third_party/libosmium/include/osmium/tags/taglist.hpp +++ b/third_party/libosmium/include/osmium/tags/taglist.hpp @@ -34,7 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include -#include +#include // IWYU pragma: keep #include diff --git a/third_party/libosmium/include/osmium/thread/pool.hpp b/third_party/libosmium/include/osmium/thread/pool.hpp index 207f5551427..613f2272cf4 100644 --- a/third_party/libosmium/include/osmium/thread/pool.hpp +++ b/third_party/libosmium/include/osmium/thread/pool.hpp @@ -34,9 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include -#include #include -#include #include #include #include @@ -66,7 +64,7 @@ namespace osmium { } if (num_threads < 0) { - num_threads += hardware_concurrency; + num_threads += int(hardware_concurrency); } if (num_threads < 1) { @@ -78,6 +76,11 @@ namespace osmium { return num_threads; } + inline size_t get_work_queue_size() noexcept { + const size_t n = osmium::config::get_max_queue_size("WORK", 10); + return n > 2 ? n : 2; + } + } // namespace detail /** @@ -118,13 +121,11 @@ namespace osmium { osmium::thread::set_thread_name("_osmium_worker"); while (true) { function_wrapper task; - m_work_queue.wait_and_pop_with_timeout(task); - if (task) { - if (task()) { - // The called tasks returns true only when the - // worker thread should shut down. - return; - } + m_work_queue.wait_and_pop(task); + if (task && task()) { + // The called tasks returns true only when the + // worker thread should shut down. + return; } } } @@ -160,10 +161,9 @@ namespace osmium { public: static constexpr int default_num_threads = 0; - static constexpr size_t max_work_queue_size = 10; static Pool& instance() { - static Pool pool(default_num_threads, max_work_queue_size); + static Pool pool(default_num_threads, detail::get_work_queue_size()); return pool; } @@ -176,7 +176,6 @@ namespace osmium { ~Pool() { shutdown_all_workers(); - m_work_queue.shutdown(); } size_t queue_size() const { @@ -190,7 +189,7 @@ namespace osmium { template std::future::type> submit(TFunction&& func) { - typedef typename std::result_of::type result_type; + using result_type = typename std::result_of::type; std::packaged_task task(std::forward(func)); std::future future_result(task.get_future()); diff --git a/third_party/libosmium/include/osmium/thread/queue.hpp b/third_party/libosmium/include/osmium/thread/queue.hpp index 771735883a0..6f4f7b1d95d 100644 --- a/third_party/libosmium/include/osmium/thread/queue.hpp +++ b/third_party/libosmium/include/osmium/thread/queue.hpp @@ -33,7 +33,6 @@ DEALINGS IN THE SOFTWARE. */ -#include #include #include #include @@ -41,7 +40,12 @@ DEALINGS IN THE SOFTWARE. #include #include #include -#include // IWYU pragma: keep (for std::move) +#include // IWYU pragma: keep + +#ifdef OSMIUM_DEBUG_QUEUE_SIZE +# include +# include +#endif namespace osmium { @@ -69,8 +73,6 @@ namespace osmium { /// Used to signal readers when data is available in the queue. std::condition_variable m_data_available; - std::atomic m_done; - #ifdef OSMIUM_DEBUG_QUEUE_SIZE /// The largest size the queue has been so far. size_t m_largest_size; @@ -81,6 +83,16 @@ namespace osmium { /// The number of times the queue was full and a thread pushing /// to the queue was blocked. std::atomic m_full_counter; + + /** + * The number of times wait_and_pop(with_timeout)() was called + * on the queue. + */ + std::atomic m_pop_counter; + + /// The number of times the queue was full and a thread pushing + /// to the queue was blocked. + std::atomic m_empty_counter; #endif public: @@ -97,21 +109,21 @@ namespace osmium { m_name(name), m_mutex(), m_queue(), - m_data_available(), - m_done(false) + m_data_available() #ifdef OSMIUM_DEBUG_QUEUE_SIZE , m_largest_size(0), m_push_counter(0), - m_full_counter(0) + m_full_counter(0), + m_pop_counter(0), + m_empty_counter(0) #endif { } ~Queue() { - shutdown(); #ifdef OSMIUM_DEBUG_QUEUE_SIZE - std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times in " << m_push_counter << " push() calls\n"; + std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times in " << m_push_counter << " push() calls and was empty " << m_empty_counter << " times in " << m_pop_counter << " pop() calls\n"; #endif } @@ -141,15 +153,18 @@ namespace osmium { m_data_available.notify_one(); } - void shutdown() { - m_done = true; - m_data_available.notify_all(); - } - void wait_and_pop(T& value) { +#ifdef OSMIUM_DEBUG_QUEUE_SIZE + ++m_pop_counter; +#endif std::unique_lock lock(m_mutex); +#ifdef OSMIUM_DEBUG_QUEUE_SIZE + if (m_queue.empty()) { + ++m_empty_counter; + } +#endif m_data_available.wait(lock, [this] { - return !m_queue.empty() || m_done; + return !m_queue.empty(); }); if (!m_queue.empty()) { value = std::move(m_queue.front()); @@ -157,22 +172,15 @@ namespace osmium { } } - void wait_and_pop_with_timeout(T& value) { - std::unique_lock lock(m_mutex); - if (!m_data_available.wait_for(lock, std::chrono::seconds(1), [this] { - return !m_queue.empty() || m_done; - })) { - return; - } - if (!m_queue.empty()) { - value = std::move(m_queue.front()); - m_queue.pop(); - } - } - bool try_pop(T& value) { +#ifdef OSMIUM_DEBUG_QUEUE_SIZE + ++m_pop_counter; +#endif std::lock_guard lock(m_mutex); if (m_queue.empty()) { +#ifdef OSMIUM_DEBUG_QUEUE_SIZE + ++m_empty_counter; +#endif return false; } value = std::move(m_queue.front()); diff --git a/third_party/libosmium/include/osmium/thread/sorted_queue.hpp b/third_party/libosmium/include/osmium/thread/sorted_queue.hpp deleted file mode 100644 index 5478643c9c8..00000000000 --- a/third_party/libosmium/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 and others (see README). - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. - -*/ - -#include -#include -#include -#include - -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 - class SortedQueue { - - typedef typename std::deque::size_type size_type; - - mutable std::mutex m_mutex; - std::deque 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 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 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 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 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 lock(m_mutex); - return m_queue.size(); - } - - }; // class SortedQueue - - } // namespace thread - -} // namespace osmium - -#endif // OSMIUM_THREAD_SORTED_QUEUE_HPP diff --git a/third_party/libosmium/include/osmium/thread/util.hpp b/third_party/libosmium/include/osmium/thread/util.hpp index 2ef331a2c8f..2eeb9997ece 100644 --- a/third_party/libosmium/include/osmium/thread/util.hpp +++ b/third_party/libosmium/include/osmium/thread/util.hpp @@ -35,6 +35,8 @@ DEALINGS IN THE SOFTWARE. #include #include +#include +#include #ifdef __linux__ # include diff --git a/third_party/libosmium/include/osmium/util/config.hpp b/third_party/libosmium/include/osmium/util/config.hpp index c40512322d2..e0412350cda 100644 --- a/third_party/libosmium/include/osmium/util/config.hpp +++ b/third_party/libosmium/include/osmium/util/config.hpp @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #ifdef _MSC_VER # define strcasecmp _stricmp @@ -44,7 +45,7 @@ namespace osmium { namespace config { - inline int get_pool_threads() { + inline int get_pool_threads() noexcept { const char* env = getenv("OSMIUM_POOL_THREADS"); if (env) { return std::atoi(env); @@ -52,7 +53,7 @@ namespace osmium { return 0; } - inline bool use_pool_threads_for_pbf_parsing() { + inline bool use_pool_threads_for_pbf_parsing() noexcept { const char* env = getenv("OSMIUM_USE_POOL_THREADS_FOR_PBF_PARSING"); if (env) { if (!strcasecmp(env, "off") || @@ -65,6 +66,18 @@ namespace osmium { return true; } + inline size_t get_max_queue_size(const char* queue_name, size_t default_value) noexcept { + std::string name {"OSMIUM_MAX_"}; + name += queue_name; + name += "_QUEUE_SIZE"; + const char* env = getenv(name.c_str()); + if (env) { + auto value = std::atoi(env); + return value == 0 ? default_value : value; + } + return default_value; + } + } // namespace config } // namespace osmium diff --git a/third_party/libosmium/include/osmium/util/delta.hpp b/third_party/libosmium/include/osmium/util/delta.hpp index 34c4eb22821..8894dd73323 100644 --- a/third_party/libosmium/include/osmium/util/delta.hpp +++ b/third_party/libosmium/include/osmium/util/delta.hpp @@ -33,7 +33,7 @@ DEALINGS IN THE SOFTWARE. */ -#include +#include #include #include @@ -118,55 +118,6 @@ namespace osmium { }; // class DeltaDecode - template - class DeltaEncodeIterator : public std::iterator { - - TBaseIterator m_it; - TBaseIterator m_end; - TTransform m_trans; - DeltaEncode m_value; - TDelta m_delta; - - public: - - using value_type = TValue; - using delta_type = TDelta; - - DeltaEncodeIterator(TBaseIterator first, TBaseIterator last, TTransform& trans) : - m_it(first), - m_end(last), - m_trans(trans), - m_value(m_it != m_end ? m_trans(m_it) : 0), - m_delta(static_cast_with_assert(m_value.value())) { - } - - DeltaEncodeIterator& operator++() { - if (++m_it != m_end) { - m_delta = m_value.update(m_trans(m_it)); - } - return *this; - } - - DeltaEncodeIterator operator++(int) { - DeltaEncodeIterator tmp(*this); - operator++(); - return tmp; - } - - TDelta operator*() { - return m_delta; - } - - bool operator==(const DeltaEncodeIterator& rhs) const { - return m_it == rhs.m_it && m_end == rhs.m_end; - } - - bool operator!=(const DeltaEncodeIterator& rhs) const { - return !(*this == rhs); - } - - }; // class DeltaEncodeIterator - } // namespace util } // namespace osmium diff --git a/third_party/libosmium/include/osmium/util/double.hpp b/third_party/libosmium/include/osmium/util/double.hpp index 1352c5fb1b5..9714bf63420 100644 --- a/third_party/libosmium/include/osmium/util/double.hpp +++ b/third_party/libosmium/include/osmium/util/double.hpp @@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE. #include #include -#include #include #include #include @@ -44,7 +43,7 @@ namespace osmium { namespace util { - constexpr int max_double_length = 20; // should fit any double + constexpr const int max_double_length = 20; // should fit any double /** * Write double to iterator, removing superfluous '0' characters at diff --git a/third_party/libosmium/include/osmium/util/file.hpp b/third_party/libosmium/include/osmium/util/file.hpp index 86b93ff722d..4c951e7e658 100644 --- a/third_party/libosmium/include/osmium/util/file.hpp +++ b/third_party/libosmium/include/osmium/util/file.hpp @@ -36,6 +36,7 @@ DEALINGS IN THE SOFTWARE. #include #include #include +#include #include #include #include @@ -47,9 +48,6 @@ DEALINGS IN THE SOFTWARE. #ifndef _MSC_VER # include -#else -// https://msdn.microsoft.com/en-us/library/whx354w1.aspx -# define ftruncate _chsize_s #endif #include @@ -70,7 +68,7 @@ namespace osmium { #ifdef _MSC_VER // Windows implementation // https://msdn.microsoft.com/en-us/library/dfbc2kec.aspx - auto size = ::_filelengthi64(fd); + const auto size = ::_filelengthi64(fd); if (size == -1L) { throw std::system_error(errno, std::system_category(), "_filelengthi64 failed"); } @@ -85,6 +83,44 @@ namespace osmium { #endif } + /** + * Get file size. + * This is a small wrapper around a system call. + * + * @param name File name + * @returns file size + * @throws std::system_error If system call failed + */ + inline size_t file_size(const char* name) { +#ifdef _MSC_VER + // Windows implementation + // https://msdn.microsoft.com/en-us/library/14h5k7ff.aspx + struct _stat64 s; + if (::_stati64(name, &s) != 0) { + throw std::system_error(errno, std::system_category(), "_stati64 failed"); + } +#else + // Unix implementation + struct stat s; + if (::stat(name, &s) != 0) { + throw std::system_error(errno, std::system_category(), "stat failed"); + } +#endif + return size_t(s.st_size); + } + + /** + * Get file size. + * This is a small wrapper around a system call. + * + * @param name File name + * @returns file size + * @throws std::system_error If system call failed + */ + inline size_t file_size(const std::string& name) { + return file_size(name.c_str()); + } + /** * Resize file. * Small wrapper around ftruncate(2) system call. @@ -94,8 +130,13 @@ namespace osmium { * @throws std::system_error If ftruncate(2) call failed */ inline void resize_file(int fd, size_t new_size) { +#ifdef _WIN32 + // https://msdn.microsoft.com/en-us/library/whx354w1.aspx + if (::_chsize_s(fd, static_cast_with_assert<__int64>(new_size)) != 0) { +#else if (::ftruncate(fd, static_cast_with_assert(new_size)) != 0) { - throw std::system_error(errno, std::system_category(), "ftruncate failed"); +#endif + throw std::system_error(errno, std::system_category(), "resizing file failed"); } } @@ -114,6 +155,37 @@ namespace osmium { #endif } + /** + * Get current offset into file. + * + * @param fd Open file descriptor. + * @returns File offset or 0 if it is not available. + */ + inline size_t file_offset(int fd) { +#ifdef _MSC_VER + // https://msdn.microsoft.com/en-us/library/1yee101t.aspx + auto offset = _lseeki64(fd, 0, SEEK_CUR); +#else + auto offset = ::lseek(fd, 0, SEEK_CUR); +#endif + if (offset == -1) { + return 0; + } + return size_t(offset); + } + + /** + * Check whether the file descriptor refers to a TTY. + */ + inline bool isatty(int fd) { +#ifdef _MSC_VER + // https://msdn.microsoft.com/en-us/library/f4s0ddew.aspx + return _isatty(fd) != 0; +#else + return ::isatty(fd) != 0; +#endif + } + } // namespace util } // namespace osmium diff --git a/third_party/libosmium/include/osmium/util/iterator.hpp b/third_party/libosmium/include/osmium/util/iterator.hpp index 4cef51950ce..42d23e830fb 100644 --- a/third_party/libosmium/include/osmium/util/iterator.hpp +++ b/third_party/libosmium/include/osmium/util/iterator.hpp @@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE. */ #include +#include #include namespace osmium { @@ -43,10 +44,10 @@ namespace osmium { using iterator = It; - iterator_range(P&& p) : + explicit iterator_range(P&& p) : P(std::forward

(p)) { } - +/* It begin() { return this->first; } @@ -54,7 +55,7 @@ namespace osmium { It end() { return this->second; } - +*/ It begin() const { return this->first; } @@ -67,7 +68,16 @@ namespace osmium { return begin() == end(); } - }; + }; // struct iterator_range + + /** + * Helper function to create iterator_range from std::pair. + */ + template + inline iterator_range make_range(P&& p) { + static_assert(std::is_same>::value, "make_range needs pair of iterators as argument"); + return iterator_range(std::forward

(p)); + } } // namespace osmium diff --git a/third_party/libosmium/include/osmium/util/memory_mapping.hpp b/third_party/libosmium/include/osmium/util/memory_mapping.hpp index 67e944e7061..4fc8f019ebb 100644 --- a/third_party/libosmium/include/osmium/util/memory_mapping.hpp +++ b/third_party/libosmium/include/osmium/util/memory_mapping.hpp @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. #include #include +#include #include #include @@ -126,22 +127,18 @@ namespace osmium { 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; } @@ -218,7 +215,7 @@ namespace osmium { ~MemoryMapping() noexcept { try { unmap(); - } catch (std::system_error&) { + } catch (const std::system_error&) { // Ignore any exceptions because destructor must not throw. } } @@ -306,7 +303,7 @@ namespace osmium { public: - AnonymousMemoryMapping(size_t size) : + explicit AnonymousMemoryMapping(size_t size) : MemoryMapping(size, mapping_mode::write_private) { } @@ -342,7 +339,7 @@ namespace osmium { * @param size Number of objects of type T to be mapped * @throws std::system_error if the mapping fails */ - TypedMemoryMapping(size_t size) : + explicit TypedMemoryMapping(size_t size) : m_mapping(sizeof(T) * size, MemoryMapping::mapping_mode::write_private) { } @@ -491,7 +488,7 @@ namespace osmium { public: - AnonymousTypedMemoryMapping(size_t size) : + explicit AnonymousTypedMemoryMapping(size_t size) : TypedMemoryMapping(size) { } @@ -550,7 +547,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 +686,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/third_party/libosmium/include/osmium/util/misc.hpp b/third_party/libosmium/include/osmium/util/misc.hpp new file mode 100644 index 00000000000..8acecb560e1 --- /dev/null +++ b/third_party/libosmium/include/osmium/util/misc.hpp @@ -0,0 +1,52 @@ +#ifndef OSMIUM_UTIL_MISC_HPP +#define OSMIUM_UTIL_MISC_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include + +namespace osmium { + + /** + * Like std::tie(), but takes its arguments as const references. Used + * as a helper function when sorting. + */ + template + inline std::tuple + const_tie(const Ts&... args) noexcept { + return std::tuple(args...); + } + +} // namespace osmium + +#endif // OSMIUM_UTIL_MISC_HPP diff --git a/third_party/libosmium/include/osmium/util/options.hpp b/third_party/libosmium/include/osmium/util/options.hpp index 9b00b481123..79818a96ab4 100644 --- a/third_party/libosmium/include/osmium/util/options.hpp +++ b/third_party/libosmium/include/osmium/util/options.hpp @@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include #include @@ -107,7 +108,7 @@ namespace osmium { * be set to "true". */ void set(std::string data) { - size_t pos = data.find_first_of('='); + const size_t pos = data.find_first_of('='); if (pos == std::string::npos) { m_options[data] = "true"; } else { @@ -122,7 +123,7 @@ namespace osmium { * empty string) is returned. */ std::string get(const std::string& key, const std::string& default_value="") const noexcept { - auto it = m_options.find(key); + const auto it = m_options.find(key); if (it == m_options.end()) { return default_value; } @@ -134,7 +135,7 @@ namespace osmium { * Will return false if the value is unset. */ bool is_true(const std::string& key) const noexcept { - std::string value = get(key); + const std::string value = get(key); return (value == "true" || value == "yes"); } @@ -143,7 +144,7 @@ namespace osmium { * Will return true if the value is unset. */ bool is_not_false(const std::string& key) const noexcept { - std::string value = get(key); + const std::string value = get(key); return !(value == "false" || value == "no"); } diff --git a/third_party/libosmium/include/osmium/util/progress_bar.hpp b/third_party/libosmium/include/osmium/util/progress_bar.hpp new file mode 100644 index 00000000000..814aa2c0e2b --- /dev/null +++ b/third_party/libosmium/include/osmium/util/progress_bar.hpp @@ -0,0 +1,179 @@ +#ifndef OSMIUM_UTIL_PROGRESS_BAR_HPP +#define OSMIUM_UTIL_PROGRESS_BAR_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include + +namespace osmium { + + /** + * Displays a progress bar on STDERR. Can be used together with the + * osmium::io::Reader class for instance. + */ + class ProgressBar { + + static const char* bar() noexcept { + return "======================================================================"; + } + + static const char* spc() noexcept { + return " "; + } + + static constexpr const size_t length = 70; + + // The max size is the file size if there is a single file and the + // sum of all file sizes if there are multiple files. It corresponds + // to 100%. + size_t m_max_size; + + // The sum of the file sizes already done. + size_t m_done_size = 0; + + // The currently read size in the current file. + size_t m_current_size = 0; + + // The percentage calculated when it was last displayed. Used to decide + // whether we need to update the display. Start setting is one that + // will always be different from any legal setting. + size_t m_prev_percent = 100 + 1; + + // Is the progress bar enabled at all? + bool m_enable; + + // Used to make sure we do cleanup in the destructor if it was not + // already done. + bool m_do_cleanup = true; + + void display() { + const size_t percent = 100 * (m_done_size + m_current_size) / m_max_size; + if (m_prev_percent == percent) { + return; + } + m_prev_percent = percent; + + const size_t num = size_t(percent * (length / 100.0)); + std::cerr << '['; + if (num >= length) { + std::cerr << bar(); + } else { + std::cerr << (bar() + length - num) << '>' << (spc() + num); + } + std::cerr << "] "; + if (percent < 10) { + std::cerr << ' '; + } + if (percent < 100) { + std::cerr << ' '; + } + std::cerr << percent << "% \r"; + } + + public: + + /** + * Initializes the progress bar. No output yet. + * + * @param max_size Max size equivalent to 100%. + * @param enable Set to false to disable (for instance if stderr is + * not a TTY). + */ + ProgressBar(size_t max_size, bool enable) noexcept : + m_max_size(max_size), + m_enable(max_size > 0 && enable) { + } + + ~ProgressBar() { + if (m_do_cleanup) { + try { + done(); + } catch (...) { + // Swallow any exceptions, because a destructor should + // not throw. + } + } + } + + /** + * Call this function to update the progress bar. Actual update will + * only happen if the percentage changed from the last time this + * function was called. + * + * @param current_size Current size. Used together with the max_size + * from constructor to calculate the percentage. + */ + void update(size_t current_size) { + if (!m_enable) { + return; + } + + m_current_size = current_size; + + display(); + } + + /** + * If you are reading multiple files, call this function after each + * file is finished. + * + * @param file_size The size of the file just finished. + */ + void file_done(size_t file_size) { + if (m_enable) { + m_done_size += file_size; + m_current_size = 0; + display(); + } + } + + /** + * Call this at the end. Will update the progress bar to 100% and + * print a final line feed. If this is not called explicitly the + * destructor will also call this. + */ + void done() { + m_do_cleanup = false; + if (m_enable) { + m_done_size = m_max_size; + m_current_size = 0; + display(); + std::cerr << '\n'; + } + } + + }; // class ProgressBar + +} // namespace osmium + +#endif // OSMIUM_UTIL_PROGRESS_BAR_HPP diff --git a/third_party/libosmium/include/osmium/util/string.hpp b/third_party/libosmium/include/osmium/util/string.hpp index 1198288c0fc..2cdb9830e6b 100644 --- a/third_party/libosmium/include/osmium/util/string.hpp +++ b/third_party/libosmium/include/osmium/util/string.hpp @@ -33,9 +33,9 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include -#include namespace osmium { diff --git a/third_party/libosmium/include/osmium/util/timer.hpp b/third_party/libosmium/include/osmium/util/timer.hpp new file mode 100644 index 00000000000..8cae80addd2 --- /dev/null +++ b/third_party/libosmium/include/osmium/util/timer.hpp @@ -0,0 +1,98 @@ +#ifndef OSMIUM_UTIL_TIMER_HPP +#define OSMIUM_UTIL_TIMER_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#include + +#ifdef OSMIUM_WITH_TIMER + +#include + +namespace osmium { + + class Timer { + + using clock = std::chrono::high_resolution_clock; + std::chrono::time_point m_start; + std::chrono::time_point m_stop; + + public: + + Timer() : + m_start(clock::now()) { + } + + void start() { + m_start = clock::now(); + } + + void stop() { + m_stop = clock::now(); + } + + int64_t elapsed_microseconds() const { + return std::chrono::duration_cast(m_stop - m_start).count(); + } + + }; + +} // namespace osmium + +#else + +namespace osmium { + + class Timer { + + public: + + Timer() = default; + + void start() { + } + + void stop() { + } + + int64_t elapsed_microseconds() const { + return 0; + } + + }; + +} // namespace osmium + +#endif + +#endif // OSMIUM_UTIL_TIMER_HPP diff --git a/third_party/libosmium/include/osmium/util/verbose_output.hpp b/third_party/libosmium/include/osmium/util/verbose_output.hpp index c7677a4367a..f85d2654ef7 100644 --- a/third_party/libosmium/include/osmium/util/verbose_output.hpp +++ b/third_party/libosmium/include/osmium/util/verbose_output.hpp @@ -33,11 +33,11 @@ DEALINGS IN THE SOFTWARE. */ -#include - +#include #include #include #include +#include namespace osmium { @@ -75,9 +75,9 @@ namespace osmium { */ void start_line() { if (m_newline) { - time_t elapsed = runtime(); + const time_t elapsed = runtime(); - char old_fill = std::cerr.fill(); + const char old_fill = std::cerr.fill(); std::cerr << '[' << std::setw(2) << (elapsed / 60) << ':' << std::setw(2) << std::setfill('0') << (elapsed % 60) << "] "; std::cerr.fill(old_fill); diff --git a/third_party/libosmium/include/osmium/version.hpp b/third_party/libosmium/include/osmium/version.hpp new file mode 100644 index 00000000000..6f3b0a368dc --- /dev/null +++ b/third_party/libosmium/include/osmium/version.hpp @@ -0,0 +1,42 @@ +#ifndef OSMIUM_VERSION_HPP +#define OSMIUM_VERSION_HPP + +/* + +This file is part of Osmium (http://osmcode.org/libosmium). + +Copyright 2013-2016 Jochen Topf and others (see README). + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +#define LIBOSMIUM_VERSION_MAJOR 2 +#define LIBOSMIUM_VERSION_MINOR 9 +#define LIBOSMIUM_VERSION_PATCH 0 + +#define LIBOSMIUM_VERSION_STRING "2.9.0" + +#endif // OSMIUM_VERSION_HPP diff --git a/third_party/libosmium/include/protozero/byteswap.hpp b/third_party/libosmium/include/protozero/byteswap.hpp index a018c1c1795..06ba6ded9a3 100644 --- a/third_party/libosmium/include/protozero/byteswap.hpp +++ b/third_party/libosmium/include/protozero/byteswap.hpp @@ -28,7 +28,7 @@ namespace protozero { * be specialized to actually work. */ template -inline void byteswap(const char* /*data*/, char* /*result*/) { +inline void byteswap(const char* /*data*/, char* /*result*/) noexcept { static_assert(N == 1, "Can only swap 4 or 8 byte values"); } @@ -36,7 +36,7 @@ inline void byteswap(const char* /*data*/, char* /*result*/) { * Swap 4 byte value (int32_t, uint32_t, float) between endianness formats. */ template <> -inline void byteswap<4>(const char* data, char* result) { +inline void byteswap<4>(const char* data, char* result) noexcept { #ifdef PROTOZERO_USE_BUILTIN_BSWAP *reinterpret_cast(result) = __builtin_bswap32(*reinterpret_cast(data)); #else @@ -51,7 +51,7 @@ inline void byteswap<4>(const char* data, char* result) { * Swap 8 byte value (int64_t, uint64_t, double) between endianness formats. */ template <> -inline void byteswap<8>(const char* data, char* result) { +inline void byteswap<8>(const char* data, char* result) noexcept { #ifdef PROTOZERO_USE_BUILTIN_BSWAP *reinterpret_cast(result) = __builtin_bswap64(*reinterpret_cast(data)); #else diff --git a/third_party/libosmium/include/protozero/exception.hpp b/third_party/libosmium/include/protozero/exception.hpp index 5c7ab547815..ca4340e90d6 100644 --- a/third_party/libosmium/include/protozero/exception.hpp +++ b/third_party/libosmium/include/protozero/exception.hpp @@ -29,7 +29,7 @@ namespace protozero { */ struct exception : std::exception { /// Returns the explanatory string. - const char *what() const noexcept override { return "pbf exception"; } + const char* what() const noexcept override { return "pbf exception"; } }; /** @@ -38,7 +38,7 @@ struct exception : std::exception { */ struct varint_too_long_exception : exception { /// Returns the explanatory string. - const char *what() const noexcept override { return "varint too long exception"; } + const char* what() const noexcept override { return "varint too long exception"; } }; /** @@ -47,7 +47,7 @@ struct varint_too_long_exception : exception { */ struct unknown_pbf_wire_type_exception : exception { /// Returns the explanatory string. - const char *what() const noexcept override { return "unknown pbf field type exception"; } + const char* what() const noexcept override { return "unknown pbf field type exception"; } }; /** @@ -60,7 +60,7 @@ struct unknown_pbf_wire_type_exception : exception { */ struct end_of_buffer_exception : exception { /// Returns the explanatory string. - const char *what() const noexcept override { return "end of buffer exception"; } + const char* what() const noexcept override { return "end of buffer exception"; } }; } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/iterators.hpp b/third_party/libosmium/include/protozero/iterators.hpp new file mode 100644 index 00000000000..00ba9193537 --- /dev/null +++ b/third_party/libosmium/include/protozero/iterators.hpp @@ -0,0 +1,373 @@ +#ifndef PROTOZERO_ITERATORS_HPP +#define PROTOZERO_ITERATORS_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +/** + * @file iterators.hpp + * + * @brief Contains the iterators for access to packed repeated fields. + */ + +#include +#include +#include + +#include +#include + +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN +# include +#endif + +namespace protozero { + +namespace detail { + + // Copy N bytes from src to dest on little endian machines, on big + // endian swap the bytes in the process. + template + inline void copy_or_byteswap(const char* src, void* dest) noexcept { +#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN + std::memcpy(dest, src, N); +#else + byteswap(src, reinterpret_cast(dest)); +#endif + } + +} // end namespace detail + +/** + * A range of iterators based on std::pair. Created from beginning and + * end iterators. Used as a return type from some pbf_reader methods + * that is easy to use with range-based for loops. + */ +template > +class iterator_range : +#ifdef PROTOZERO_STRICT_API + protected +#else + public +#endif + P { + +public: + + /// The type of the iterators in this range. + using iterator = T; + + /// The value type of the underlying iterator. + using value_type = typename std::iterator_traits::value_type; + + /** + * Default constructor. Create empty iterator_range. + */ + constexpr iterator_range() : + P(iterator{}, iterator{}) { + } + + /** + * Create iterator range from two iterators. + * + * @param first_iterator Iterator to beginning or range. + * @param last_iterator Iterator to end or range. + */ + constexpr iterator_range(iterator&& first_iterator, iterator&& last_iterator) : + P(std::forward(first_iterator), + std::forward(last_iterator)) { + } + + /// Return iterator to beginning of range. + constexpr iterator begin() const noexcept { + return this->first; + } + + /// Return iterator to end of range. + constexpr iterator end() const noexcept { + return this->second; + } + + /// Return iterator to beginning of range. + constexpr iterator cbegin() const noexcept { + return this->first; + } + + /// Return iterator to end of range. + constexpr iterator cend() const noexcept { + return this->second; + } + + /// Return true if this range is empty. + constexpr std::size_t empty() const noexcept { + return begin() == end(); + } + + /** + * Get element at the beginning of the range. + * + * @pre Range must not be empty. + */ + value_type front() const { + protozero_assert(!empty()); + return *(this->first); + } + + /** + * Advance beginning of range by one. + * + * @pre Range must not be empty. + */ + void drop_front() { + protozero_assert(!empty()); + ++this->first; + } + + /** + * Swap the contents of this range with the other. + * + * @param other Other range to swap data with. + */ + void swap(iterator_range& other) noexcept { + using std::swap; + swap(this->first, other.first); + swap(this->second, other.second); + } + +}; // struct iterator_range + +/** + * Swap two iterator_ranges. + * + * @param lhs First range. + * @param rhs Second range. + */ +template +inline void swap(iterator_range& lhs, iterator_range& rhs) noexcept { + lhs.swap(rhs); +} + +#ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED + +template +using const_fixed_iterator = const T*; + +/** + * Create iterator_range from char pointers to beginning and end of range. + * + * @param first Beginning of range. + * @param last End of range. + */ +template +inline iterator_range> create_fixed_iterator_range(const char* first, const char* last) { + return iterator_range>{reinterpret_cast(first), + reinterpret_cast(last)}; +} + +#else + +/** + * A forward iterator used for accessing packed repeated fields of fixed + * length (fixed32, sfixed32, float, double). + */ +template +class const_fixed_iterator { + + /// Pointer to current iterator position + const char* m_data; + + /// Pointer to end iterator position + const char* m_end; + +public: + + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + const_fixed_iterator() noexcept : + m_data(nullptr), + m_end(nullptr) { + } + + const_fixed_iterator(const char* data, const char* end) noexcept : + m_data(data), + m_end(end) { + } + + const_fixed_iterator(const const_fixed_iterator&) noexcept = default; + const_fixed_iterator(const_fixed_iterator&&) noexcept = default; + + const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default; + const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default; + + ~const_fixed_iterator() noexcept = default; + + value_type operator*() const { + value_type result; + detail::copy_or_byteswap(m_data , &result); + return result; + } + + const_fixed_iterator& operator++() { + m_data += sizeof(value_type); + return *this; + } + + const_fixed_iterator operator++(int) { + const const_fixed_iterator tmp(*this); + ++(*this); + return tmp; + } + + bool operator==(const const_fixed_iterator& rhs) const noexcept { + return m_data == rhs.m_data && m_end == rhs.m_end; + } + + bool operator!=(const const_fixed_iterator& rhs) const noexcept { + return !(*this == rhs); + } + +}; // class const_fixed_iterator + +/** + * Create iterator_range from char pointers to beginning and end of range. + * + * @param first Beginning of range. + * @param last End of range. + */ +template +inline iterator_range> create_fixed_iterator_range(const char* first, const char* last) { + return iterator_range>{const_fixed_iterator(first, last), + const_fixed_iterator(last, last)}; +} + +#endif + +/** + * A forward iterator used for accessing packed repeated varint fields + * (int32, uint32, int64, uint64, bool, enum). + */ +template +class const_varint_iterator { + +protected: + + /// Pointer to current iterator position + const char* m_data; + + /// Pointer to end iterator position + const char* m_end; + +public: + + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + const_varint_iterator() noexcept : + m_data(nullptr), + m_end(nullptr) { + } + + const_varint_iterator(const char* data, const char* end) noexcept : + m_data(data), + m_end(end) { + } + + const_varint_iterator(const const_varint_iterator&) noexcept = default; + const_varint_iterator(const_varint_iterator&&) noexcept = default; + + const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default; + const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default; + + ~const_varint_iterator() noexcept = default; + + value_type operator*() const { + const char* d = m_data; // will be thrown away + return static_cast(decode_varint(&d, m_end)); + } + + const_varint_iterator& operator++() { + skip_varint(&m_data, m_end); + return *this; + } + + const_varint_iterator operator++(int) { + const const_varint_iterator tmp(*this); + ++(*this); + return tmp; + } + + bool operator==(const const_varint_iterator& rhs) const noexcept { + return m_data == rhs.m_data && m_end == rhs.m_end; + } + + bool operator!=(const const_varint_iterator& rhs) const noexcept { + return !(*this == rhs); + } + +}; // class const_varint_iterator + +/** + * A forward iterator used for accessing packed repeated svarint fields + * (sint32, sint64). + */ +template +class const_svarint_iterator : public const_varint_iterator { + +public: + + using iterator_category = std::forward_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + const_svarint_iterator() noexcept : + const_varint_iterator() { + } + + const_svarint_iterator(const char* data, const char* end) noexcept : + const_varint_iterator(data, end) { + } + + const_svarint_iterator(const const_svarint_iterator&) = default; + const_svarint_iterator(const_svarint_iterator&&) = default; + + const_svarint_iterator& operator=(const const_svarint_iterator&) = default; + const_svarint_iterator& operator=(const_svarint_iterator&&) = default; + + ~const_svarint_iterator() = default; + + value_type operator*() const { + const char* d = this->m_data; // will be thrown away + return static_cast(decode_zigzag64(decode_varint(&d, this->m_end))); + } + + const_svarint_iterator& operator++() { + skip_varint(&this->m_data, this->m_end); + return *this; + } + + const_svarint_iterator operator++(int) { + const const_svarint_iterator tmp(*this); + ++(*this); + return tmp; + } + +}; // class const_svarint_iterator + +} // end namespace protozero + +#endif // PROTOZERO_ITERATORS_HPP diff --git a/third_party/libosmium/include/protozero/pbf_builder.hpp b/third_party/libosmium/include/protozero/pbf_builder.hpp index 548f4cecb89..39af53f3f26 100644 --- a/third_party/libosmium/include/protozero/pbf_builder.hpp +++ b/third_party/libosmium/include/protozero/pbf_builder.hpp @@ -57,7 +57,7 @@ class pbf_builder : public pbf_writer { /// @cond INTERNAL #define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \ - inline void add_##name(T tag, type value) { \ + void add_##name(T tag, type value) { \ pbf_writer::add_##name(pbf_tag_type(tag), value); \ } @@ -79,38 +79,38 @@ class pbf_builder : public pbf_writer { #undef PROTOZERO_WRITER_WRAP_ADD_SCALAR /// @endcond - inline void add_bytes(T tag, const char* value, std::size_t size) { + void add_bytes(T tag, const char* value, std::size_t size) { pbf_writer::add_bytes(pbf_tag_type(tag), value, size); } - inline void add_bytes(T tag, const std::string& value) { + void add_bytes(T tag, const std::string& value) { pbf_writer::add_bytes(pbf_tag_type(tag), value); } - inline void add_string(T tag, const char* value, std::size_t size) { + void add_string(T tag, const char* value, std::size_t size) { pbf_writer::add_string(pbf_tag_type(tag), value, size); } - inline void add_string(T tag, const std::string& value) { + void add_string(T tag, const std::string& value) { pbf_writer::add_string(pbf_tag_type(tag), value); } - inline void add_string(T tag, const char* value) { + void add_string(T tag, const char* value) { pbf_writer::add_string(pbf_tag_type(tag), value); } - inline void add_message(T tag, const char* value, std::size_t size) { + void add_message(T tag, const char* value, std::size_t size) { pbf_writer::add_message(pbf_tag_type(tag), value, size); } - inline void add_message(T tag, const std::string& value) { + void add_message(T tag, const std::string& value) { pbf_writer::add_message(pbf_tag_type(tag), value); } /// @cond INTERNAL #define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \ template \ - inline void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ + void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \ } @@ -132,7 +132,7 @@ class pbf_builder : public pbf_writer { #undef PROTOZERO_WRITER_WRAP_ADD_PACKED /// @endcond -}; +}; // class pbf_builder } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/pbf_message.hpp b/third_party/libosmium/include/protozero/pbf_message.hpp index 45f01c149a3..05577340812 100644 --- a/third_party/libosmium/include/protozero/pbf_message.hpp +++ b/third_party/libosmium/include/protozero/pbf_message.hpp @@ -13,7 +13,7 @@ documentation. /** * @file pbf_message.hpp * - * @brief Contains the pbf_message class. + * @brief Contains the pbf_message template class. */ #include @@ -75,19 +75,19 @@ class pbf_message : public pbf_reader { pbf_reader(std::forward(args)...) { } - inline bool next() { + bool next() { return pbf_reader::next(); } - inline bool next(T tag) { + bool next(T tag) { return pbf_reader::next(pbf_tag_type(tag)); } - inline T tag() const noexcept { + T tag() const noexcept { return T(pbf_reader::tag()); } -}; +}; // class pbf_message } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/pbf_reader.hpp b/third_party/libosmium/include/protozero/pbf_reader.hpp index 58b3884550d..2f4054d2d25 100644 --- a/third_party/libosmium/include/protozero/pbf_reader.hpp +++ b/third_party/libosmium/include/protozero/pbf_reader.hpp @@ -18,13 +18,12 @@ documentation. #include #include -#include -#include #include #include #include #include +#include #include #include @@ -55,16 +54,16 @@ namespace protozero { * * All methods of the pbf_reader class except get_bytes() and get_string() * provide the strong exception guarantee, ie they either succeed or do not - * change the pbf_reader object they are called on. Use the get_data() method + * change the pbf_reader object they are called on. Use the get_view() method * instead of get_bytes() or get_string(), if you need this guarantee. */ class pbf_reader { // A pointer to the next unread data. - const char *m_data = nullptr; + const char* m_data = nullptr; // A pointer to one past the end of data. - const char *m_end = nullptr; + const char* m_end = nullptr; // The wire type of the current field. pbf_wire_type m_wire_type = pbf_wire_type::unknown; @@ -72,118 +71,83 @@ class pbf_reader { // The tag of the current field. pbf_tag_type m_tag = 0; - // Copy N bytes from src to dest on little endian machines, on big endian - // swap the bytes in the process. - template - static void copy_or_byteswap(const char* src, void* dest) noexcept { -#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN - memcpy(dest, src, N); -#else - byteswap(src, reinterpret_cast(dest)); -#endif - } - template - inline T get_fixed() { + T get_fixed() { T result; skip_bytes(sizeof(T)); - copy_or_byteswap(m_data - sizeof(T), &result); + detail::copy_or_byteswap(m_data - sizeof(T), &result); return result; } -#ifdef PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED - template - using const_fixed_iterator = const T*; + iterator_range> packed_fixed() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + const auto len = get_len_and_skip(); + protozero_assert(len % sizeof(T) == 0); + return create_fixed_iterator_range(m_data - len, m_data); + } template - inline std::pair, const_fixed_iterator> create_fixed_iterator_pair(const char* first, const char* last) { - return std::make_pair(reinterpret_cast(first), - reinterpret_cast(last)); + T get_varint() { + return static_cast(decode_varint(&m_data, m_end)); } -#else - template - class const_fixed_iterator : public std::iterator { - - const char* m_data; - const char* m_end; - - public: - - const_fixed_iterator() noexcept : - m_data(nullptr), - m_end(nullptr) { - } - - const_fixed_iterator(const char *data, const char* end) noexcept : - m_data(data), - m_end(end) { - } - - const_fixed_iterator(const const_fixed_iterator&) noexcept = default; - const_fixed_iterator(const_fixed_iterator&&) noexcept = default; - - const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default; - const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default; - - ~const_fixed_iterator() noexcept = default; - - T operator*() { - T result; - copy_or_byteswap(m_data , &result); - return result; - } - - const_fixed_iterator& operator++() { - m_data += sizeof(T); - return *this; - } - - const_fixed_iterator operator++(int) { - const const_fixed_iterator tmp(*this); - ++(*this); - return tmp; - } + T get_svarint() { + protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint"); + return static_cast(decode_zigzag64(decode_varint(&m_data, m_end))); + } - bool operator==(const const_fixed_iterator& rhs) const noexcept { - return m_data == rhs.m_data && m_end == rhs.m_end; - } + pbf_length_type get_length() { + return get_varint(); + } - bool operator!=(const const_fixed_iterator& rhs) const noexcept { - return !(*this == rhs); + void skip_bytes(pbf_length_type len) { + if (m_data + len > m_end) { + throw end_of_buffer_exception(); } + m_data += len; - }; // class const_fixed_iterator - - template - inline std::pair, const_fixed_iterator> create_fixed_iterator_pair(const char* first, const char* last) { - return std::make_pair(const_fixed_iterator(first, last), - const_fixed_iterator(last, last)); + // In debug builds reset the tag to zero so that we can detect (some) + // wrong code. +#ifndef NDEBUG + m_tag = 0; +#endif } -#endif + pbf_length_type get_len_and_skip() { + const auto len = get_length(); + skip_bytes(len); + return len; + } template - inline std::pair, const_fixed_iterator> packed_fixed() { + iterator_range get_packed() { protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - protozero_assert(len % sizeof(T) == 0); - return create_fixed_iterator_pair(m_data-len, m_data); + const auto len = get_len_and_skip(); + return iterator_range{T{m_data - len, m_data}, + T{m_data, m_data}}; } - template inline T get_varint(); - template inline T get_svarint(); - - inline pbf_length_type get_length() { return get_varint(); } - - inline void skip_bytes(pbf_length_type len); - - inline pbf_length_type get_len_and_skip(); - public: + /** + * Construct a pbf_reader message from a data_view. The pointer from the + * data_view will be stored inside the pbf_reader object, no data is + * copied. So you must* make sure the view stays valid as long as the + * pbf_reader object is used. + * + * The buffer must contain a complete protobuf message. + * + * @post There is no current field. + */ + explicit pbf_reader(const data_view& view) noexcept + : m_data(view.data()), + m_end(view.data() + view.size()), + m_wire_type(pbf_wire_type::unknown), + m_tag(0) { + } + /** * Construct a pbf_reader message from a data pointer and a length. The pointer * will be stored inside the pbf_reader object, no data is copied. So you must @@ -193,7 +157,12 @@ class pbf_reader { * * @post There is no current field. */ - inline pbf_reader(const char *data, std::size_t length) noexcept; + pbf_reader(const char* data, std::size_t length) noexcept + : m_data(data), + m_end(data + length), + m_wire_type(pbf_wire_type::unknown), + m_tag(0) { + } /** * Construct a pbf_reader message from a data pointer and a length. The pointer @@ -204,7 +173,12 @@ class pbf_reader { * * @post There is no current field. */ - inline pbf_reader(std::pair data) noexcept; + pbf_reader(std::pair data) noexcept + : m_data(data.first), + m_end(data.first + data.second), + m_wire_type(pbf_wire_type::unknown), + m_tag(0) { + } /** * Construct a pbf_reader message from a std::string. A pointer to the string @@ -216,33 +190,53 @@ class pbf_reader { * * @post There is no current field. */ - inline pbf_reader(const std::string& data) noexcept; + pbf_reader(const std::string& data) noexcept + : m_data(data.data()), + m_end(data.data() + data.size()), + m_wire_type(pbf_wire_type::unknown), + m_tag(0) { + } /** * pbf_reader can be default constructed and behaves like it has an empty * buffer. */ - inline pbf_reader() noexcept = default; + pbf_reader() noexcept = default; /// pbf_reader messages can be copied trivially. - inline pbf_reader(const pbf_reader&) noexcept = default; + pbf_reader(const pbf_reader&) noexcept = default; /// pbf_reader messages can be moved trivially. - inline pbf_reader(pbf_reader&&) noexcept = default; + pbf_reader(pbf_reader&&) noexcept = default; /// pbf_reader messages can be copied trivially. - inline pbf_reader& operator=(const pbf_reader& other) noexcept = default; + pbf_reader& operator=(const pbf_reader& other) noexcept = default; /// pbf_reader messages can be moved trivially. - inline pbf_reader& operator=(pbf_reader&& other) noexcept = default; + pbf_reader& operator=(pbf_reader&& other) noexcept = default; + + ~pbf_reader() = default; - inline ~pbf_reader() = default; + /** + * Swap the contents of this object with the other. + * + * @param other Other object to swap data with. + */ + void swap(pbf_reader& other) noexcept { + using std::swap; + swap(m_data, other.m_data); + swap(m_end, other.m_end); + swap(m_wire_type, other.m_wire_type); + swap(m_tag, other.m_tag); + } /** * In a boolean context the pbf_reader class evaluates to `true` if there are * still fields available and to `false` if the last field has been read. */ - inline operator bool() const noexcept; + operator bool() const noexcept { + return m_data < m_end; + } /** * Return the length in bytes of the current message. If you have @@ -272,7 +266,31 @@ class pbf_reader { * @pre There must be no current field. * @post If it returns `true` there is a current field now. */ - inline bool next(); + bool next() { + if (m_data == m_end) { + return false; + } + + const auto value = get_varint(); + m_tag = pbf_tag_type(value >> 3); + + // tags 0 and 19000 to 19999 are not allowed as per + // https://developers.google.com/protocol-buffers/docs/proto + protozero_assert(((m_tag > 0 && m_tag < 19000) || (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range"); + + m_wire_type = pbf_wire_type(value & 0x07); + switch (m_wire_type) { + case pbf_wire_type::varint: + case pbf_wire_type::fixed64: + case pbf_wire_type::length_delimited: + case pbf_wire_type::fixed32: + break; + default: + throw unknown_pbf_wire_type_exception(); + } + + return true; + } /** * Set next field with given tag in the message as the current field. @@ -299,7 +317,16 @@ class pbf_reader { * @pre There must be no current field. * @post If it returns `true` there is a current field now with the given tag. */ - inline bool next(pbf_tag_type tag); + bool next(pbf_tag_type tag) { + while (next()) { + if (m_tag == tag) { + return true; + } else { + skip(); + } + } + return false; + } /** * The tag of the current field. The tag is the field number from the @@ -310,7 +337,9 @@ class pbf_reader { * @returns tag of the current field. * @pre There must be a current field (ie. next() must have returned `true`). */ - inline pbf_tag_type tag() const noexcept; + pbf_tag_type tag() const noexcept { + return m_tag; + } /** * Get the wire type of the current field. The wire types are: @@ -327,7 +356,9 @@ class pbf_reader { * @returns wire type of the current field. * @pre There must be a current field (ie. next() must have returned `true`). */ - inline pbf_wire_type wire_type() const noexcept; + pbf_wire_type wire_type() const noexcept { + return m_wire_type; + } /** * Check the wire type of the current field. @@ -335,7 +366,9 @@ class pbf_reader { * @returns `true` if the current field has the given wire type. * @pre There must be a current field (ie. next() must have returned `true`). */ - inline bool has_wire_type(pbf_wire_type type) const noexcept; + bool has_wire_type(pbf_wire_type type) const noexcept { + return wire_type() == type; + } /** * Consume the current field. @@ -343,7 +376,25 @@ class pbf_reader { * @pre There must be a current field (ie. next() must have returned `true`). * @post The current field was consumed and there is no current field now. */ - inline void skip(); + void skip() { + protozero_assert(tag() != 0 && "call next() before calling skip()"); + switch (wire_type()) { + case pbf_wire_type::varint: + skip_varint(&m_data, m_end); + break; + case pbf_wire_type::fixed64: + skip_bytes(8); + break; + case pbf_wire_type::length_delimited: + skip_bytes(get_length()); + break; + case pbf_wire_type::fixed32: + skip_bytes(4); + break; + default: + protozero_assert(false && "can not be here because next() should have thrown already"); + } + } ///@{ /** @@ -357,7 +408,13 @@ class pbf_reader { * @pre The current field must be of type "bool". * @post The current field was consumed and there is no current field now. */ - inline bool get_bool(); + bool get_bool() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); + protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint"); + skip_bytes(1); + return m_data[-1] != 0; // -1 okay because we incremented m_data the line before + } /** * Consume and return value of current "enum" field. @@ -366,7 +423,7 @@ class pbf_reader { * @pre The current field must be of type "enum". * @post The current field was consumed and there is no current field now. */ - inline int32_t get_enum() { + int32_t get_enum() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_varint(); } @@ -378,7 +435,7 @@ class pbf_reader { * @pre The current field must be of type "int32". * @post The current field was consumed and there is no current field now. */ - inline int32_t get_int32() { + int32_t get_int32() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_varint(); } @@ -390,7 +447,7 @@ class pbf_reader { * @pre The current field must be of type "sint32". * @post The current field was consumed and there is no current field now. */ - inline int32_t get_sint32() { + int32_t get_sint32() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_svarint(); } @@ -402,7 +459,7 @@ class pbf_reader { * @pre The current field must be of type "uint32". * @post The current field was consumed and there is no current field now. */ - inline uint32_t get_uint32() { + uint32_t get_uint32() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_varint(); } @@ -414,7 +471,7 @@ class pbf_reader { * @pre The current field must be of type "int64". * @post The current field was consumed and there is no current field now. */ - inline int64_t get_int64() { + int64_t get_int64() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_varint(); } @@ -426,7 +483,7 @@ class pbf_reader { * @pre The current field must be of type "sint64". * @post The current field was consumed and there is no current field now. */ - inline int64_t get_sint64() { + int64_t get_sint64() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_svarint(); } @@ -438,7 +495,7 @@ class pbf_reader { * @pre The current field must be of type "uint64". * @post The current field was consumed and there is no current field now. */ - inline uint64_t get_uint64() { + uint64_t get_uint64() { protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); return get_varint(); } @@ -450,7 +507,11 @@ class pbf_reader { * @pre The current field must be of type "fixed32". * @post The current field was consumed and there is no current field now. */ - inline uint32_t get_fixed32(); + uint32_t get_fixed32() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); + return get_fixed(); + } /** * Consume and return value of current "sfixed32" field. @@ -459,7 +520,11 @@ class pbf_reader { * @pre The current field must be of type "sfixed32". * @post The current field was consumed and there is no current field now. */ - inline int32_t get_sfixed32(); + int32_t get_sfixed32() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); + return get_fixed(); + } /** * Consume and return value of current "fixed64" field. @@ -468,7 +533,11 @@ class pbf_reader { * @pre The current field must be of type "fixed64". * @post The current field was consumed and there is no current field now. */ - inline uint64_t get_fixed64(); + uint64_t get_fixed64() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); + return get_fixed(); + } /** * Consume and return value of current "sfixed64" field. @@ -477,7 +546,11 @@ class pbf_reader { * @pre The current field must be of type "sfixed64". * @post The current field was consumed and there is no current field now. */ - inline int64_t get_sfixed64(); + int64_t get_sfixed64() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); + return get_fixed(); + } /** * Consume and return value of current "float" field. @@ -486,7 +559,11 @@ class pbf_reader { * @pre The current field must be of type "float". * @post The current field was consumed and there is no current field now. */ - inline float get_float(); + float get_float() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); + return get_fixed(); + } /** * Consume and return value of current "double" field. @@ -495,8 +572,29 @@ class pbf_reader { * @pre The current field must be of type "double". * @post The current field was consumed and there is no current field now. */ - inline double get_double(); + double get_double() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); + return get_fixed(); + } + + /** + * Consume and return value of current "bytes", "string", or "message" + * field. + * + * @returns A data_view object. + * @pre There must be a current field (ie. next() must have returned `true`). + * @pre The current field must be of type "bytes", "string", or "message". + * @post The current field was consumed and there is no current field now. + */ + data_view get_view() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message"); + const auto len = get_len_and_skip(); + return data_view{m_data-len, len}; + } +#ifndef PROTOZERO_STRICT_API /** * Consume and return value of current "bytes" or "string" field. * @@ -505,7 +603,13 @@ class pbf_reader { * @pre The current field must be of type "bytes" or "string". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_data(); + std::pair get_data() { + protozero_assert(tag() != 0 && "call next() before accessing field value"); + protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message"); + const auto len = get_len_and_skip(); + return std::make_pair(m_data-len, len); + } +#endif /** * Consume and return value of current "bytes" field. @@ -514,7 +618,9 @@ class pbf_reader { * @pre The current field must be of type "bytes". * @post The current field was consumed and there is no current field now. */ - inline std::string get_bytes(); + std::string get_bytes() { + return std::string(get_view()); + } /** * Consume and return value of current "string" field. @@ -523,7 +629,9 @@ class pbf_reader { * @pre The current field must be of type "string". * @post The current field was consumed and there is no current field now. */ - inline std::string get_string(); + std::string get_string() { + return std::string(get_view()); + } /** * Consume and return value of current "message" field. @@ -532,136 +640,35 @@ class pbf_reader { * @pre The current field must be of type "message". * @post The current field was consumed and there is no current field now. */ - inline pbf_reader get_message() { - return pbf_reader(get_data()); + pbf_reader get_message() { + return pbf_reader(get_view()); } ///@} -private: - - template - class const_varint_iterator : public std::iterator { - - protected: - - const char* m_data; - const char* m_end; - - public: - - const_varint_iterator() noexcept : - m_data(nullptr), - m_end(nullptr) { - } - - const_varint_iterator(const char *data, const char* end) noexcept : - m_data(data), - m_end(end) { - } - - const_varint_iterator(const const_varint_iterator&) noexcept = default; - const_varint_iterator(const_varint_iterator&&) noexcept = default; - - const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default; - const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default; - - ~const_varint_iterator() noexcept = default; - - T operator*() { - const char* d = m_data; // will be thrown away - return static_cast(decode_varint(&d, m_end)); - } - - const_varint_iterator& operator++() { - // Ignore the result, we call decode_varint() just for the - // side-effect of updating m_data. - decode_varint(&m_data, m_end); - return *this; - } - - const_varint_iterator operator++(int) { - const const_varint_iterator tmp(*this); - ++(*this); - return tmp; - } - - bool operator==(const const_varint_iterator& rhs) const noexcept { - return m_data == rhs.m_data && m_end == rhs.m_end; - } - - bool operator!=(const const_varint_iterator& rhs) const noexcept { - return !(*this == rhs); - } - - }; // class const_varint_iterator - - template - class const_svarint_iterator : public const_varint_iterator { - - public: - - const_svarint_iterator() noexcept : - const_varint_iterator() { - } - - const_svarint_iterator(const char *data, const char* end) noexcept : - const_varint_iterator(data, end) { - } - - const_svarint_iterator(const const_svarint_iterator&) = default; - const_svarint_iterator(const_svarint_iterator&&) = default; - - const_svarint_iterator& operator=(const const_svarint_iterator&) = default; - const_svarint_iterator& operator=(const_svarint_iterator&&) = default; - - ~const_svarint_iterator() = default; - - T operator*() { - const char* d = this->m_data; // will be thrown away - return static_cast(decode_zigzag64(decode_varint(&d, this->m_end))); - } - - const_svarint_iterator& operator++() { - // Ignore the result, we call decode_varint() just for the - // side-effect of updating m_data. - decode_varint(&this->m_data, this->m_end); - return *this; - } - - const_svarint_iterator operator++(int) { - const const_svarint_iterator tmp(*this); - ++(*this); - return tmp; - } - - }; // class const_svarint_iterator - -public: - /// Forward iterator for iterating over bool (int32 varint) values. - typedef const_varint_iterator< int32_t> const_bool_iterator; + using const_bool_iterator = const_varint_iterator< int32_t>; /// Forward iterator for iterating over enum (int32 varint) values. - typedef const_varint_iterator< int32_t> const_enum_iterator; + using const_enum_iterator = const_varint_iterator< int32_t>; /// Forward iterator for iterating over int32 (varint) values. - typedef const_varint_iterator< int32_t> const_int32_iterator; + using const_int32_iterator = const_varint_iterator< int32_t>; /// Forward iterator for iterating over sint32 (varint) values. - typedef const_svarint_iterator const_sint32_iterator; + using const_sint32_iterator = const_svarint_iterator; /// Forward iterator for iterating over uint32 (varint) values. - typedef const_varint_iterator const_uint32_iterator; + using const_uint32_iterator = const_varint_iterator; /// Forward iterator for iterating over int64 (varint) values. - typedef const_varint_iterator< int64_t> const_int64_iterator; + using const_int64_iterator = const_varint_iterator< int64_t>; /// Forward iterator for iterating over sint64 (varint) values. - typedef const_svarint_iterator const_sint64_iterator; + using const_sint64_iterator = const_svarint_iterator; /// Forward iterator for iterating over uint64 (varint) values. - typedef const_varint_iterator const_uint64_iterator; + using const_uint64_iterator = const_varint_iterator; ///@{ /** @@ -677,7 +684,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed bool". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_bool(); + iterator_range get_packed_bool() { + return get_packed(); + } /** * Consume current "repeated packed enum" field. @@ -688,7 +697,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed enum". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_enum(); + iterator_range get_packed_enum() { + return get_packed(); + } /** * Consume current "repeated packed int32" field. @@ -699,7 +710,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed int32". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_int32(); + iterator_range get_packed_int32() { + return get_packed(); + } /** * Consume current "repeated packed sint32" field. @@ -710,7 +723,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed sint32". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_sint32(); + iterator_range get_packed_sint32() { + return get_packed(); + } /** * Consume current "repeated packed uint32" field. @@ -721,7 +736,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed uint32". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_uint32(); + iterator_range get_packed_uint32() { + return get_packed(); + } /** * Consume current "repeated packed int64" field. @@ -732,7 +749,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed int64". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_int64(); + iterator_range get_packed_int64() { + return get_packed(); + } /** * Consume current "repeated packed sint64" field. @@ -743,7 +762,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed sint64". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_sint64(); + iterator_range get_packed_sint64() { + return get_packed(); + } /** * Consume current "repeated packed uint64" field. @@ -754,7 +775,9 @@ class pbf_reader { * @pre The current field must be of type "repeated packed uint64". * @post The current field was consumed and there is no current field now. */ - inline std::pair get_packed_uint64(); + iterator_range get_packed_uint64() { + return get_packed(); + } /** * Consume current "repeated packed fixed32" field. @@ -765,7 +788,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed fixed32". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_fixed32() -> decltype(packed_fixed()) { + auto get_packed_fixed32() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -778,7 +801,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed sfixed32". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_sfixed32() -> decltype(packed_fixed()) { + auto get_packed_sfixed32() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -791,7 +814,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed fixed64". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_fixed64() -> decltype(packed_fixed()) { + auto get_packed_fixed64() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -804,7 +827,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed sfixed64". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_sfixed64() -> decltype(packed_fixed()) { + auto get_packed_sfixed64() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -817,7 +840,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed float". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_float() -> decltype(packed_fixed()) { + auto get_packed_float() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -830,7 +853,7 @@ class pbf_reader { * @pre The current field must be of type "repeated packed double". * @post The current field was consumed and there is no current field now. */ - inline auto get_packed_double() -> decltype(packed_fixed()) { + auto get_packed_double() -> decltype(packed_fixed()) { return packed_fixed(); } @@ -838,238 +861,14 @@ class pbf_reader { }; // class pbf_reader -pbf_reader::pbf_reader(const char *data, std::size_t length) noexcept - : m_data(data), - m_end(data + length), - m_wire_type(pbf_wire_type::unknown), - m_tag(0) { -} - -pbf_reader::pbf_reader(std::pair data) noexcept - : m_data(data.first), - m_end(data.first + data.second), - m_wire_type(pbf_wire_type::unknown), - m_tag(0) { -} - -pbf_reader::pbf_reader(const std::string& data) noexcept - : m_data(data.data()), - m_end(data.data() + data.size()), - m_wire_type(pbf_wire_type::unknown), - m_tag(0) { -} - -pbf_reader::operator bool() const noexcept { - return m_data < m_end; -} - -bool pbf_reader::next() { - if (m_data == m_end) { - return false; - } - - auto value = get_varint(); - m_tag = value >> 3; - - // tags 0 and 19000 to 19999 are not allowed as per - // https://developers.google.com/protocol-buffers/docs/proto - protozero_assert(((m_tag > 0 && m_tag < 19000) || (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range"); - - m_wire_type = pbf_wire_type(value & 0x07); - switch (m_wire_type) { - case pbf_wire_type::varint: - case pbf_wire_type::fixed64: - case pbf_wire_type::length_delimited: - case pbf_wire_type::fixed32: - break; - default: - throw unknown_pbf_wire_type_exception(); - } - - return true; -} - -bool pbf_reader::next(pbf_tag_type requested_tag) { - while (next()) { - if (m_tag == requested_tag) { - return true; - } else { - skip(); - } - } - return false; -} - -pbf_tag_type pbf_reader::tag() const noexcept { - return m_tag; -} - -pbf_wire_type pbf_reader::wire_type() const noexcept { - return m_wire_type; -} - -bool pbf_reader::has_wire_type(pbf_wire_type type) const noexcept { - return wire_type() == type; -} - -void pbf_reader::skip_bytes(pbf_length_type len) { - if (m_data + len > m_end) { - throw end_of_buffer_exception(); - } - m_data += len; - -// In debug builds reset the tag to zero so that we can detect (some) -// wrong code. -#ifndef NDEBUG - m_tag = 0; -#endif -} - -void pbf_reader::skip() { - protozero_assert(tag() != 0 && "call next() before calling skip()"); - switch (wire_type()) { - case pbf_wire_type::varint: - (void)get_uint32(); // called for the side-effect of skipping value - break; - case pbf_wire_type::fixed64: - skip_bytes(8); - break; - case pbf_wire_type::length_delimited: - skip_bytes(get_length()); - break; - case pbf_wire_type::fixed32: - skip_bytes(4); - break; - default: - protozero_assert(false && "can not be here because next() should have thrown already"); - } -} - -pbf_length_type pbf_reader::get_len_and_skip() { - auto len = get_length(); - skip_bytes(len); - return len; -} - -template -T pbf_reader::get_varint() { - return static_cast(decode_varint(&m_data, m_end)); -} - -template -T pbf_reader::get_svarint() { - protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint"); - return static_cast(decode_zigzag64(decode_varint(&m_data, m_end))); -} - -uint32_t pbf_reader::get_fixed32() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); - return get_fixed(); -} - -int32_t pbf_reader::get_sfixed32() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); - return get_fixed(); -} - -uint64_t pbf_reader::get_fixed64() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); - return get_fixed(); -} - -int64_t pbf_reader::get_sfixed64() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); - return get_fixed(); -} - -float pbf_reader::get_float() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed"); - return get_fixed(); -} - -double pbf_reader::get_double() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed"); - return get_fixed(); -} - -bool pbf_reader::get_bool() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); - protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint"); - skip_bytes(1); - return m_data[-1] != 0; // -1 okay because we incremented m_data the line before -} - -std::pair pbf_reader::get_data() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message"); - auto len = get_len_and_skip(); - return std::make_pair(m_data-len, len); -} - -std::string pbf_reader::get_bytes() { - auto d = get_data(); - return std::string(d.first, d.second); -} - -std::string pbf_reader::get_string() { - return get_bytes(); -} - -std::pair pbf_reader::get_packed_bool() { - return get_packed_int32(); -} - -std::pair pbf_reader::get_packed_enum() { - return get_packed_int32(); -} - -std::pair pbf_reader::get_packed_int32() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_int32_iterator(m_data-len, m_data), - pbf_reader::const_int32_iterator(m_data, m_data)); -} - -std::pair pbf_reader::get_packed_uint32() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_uint32_iterator(m_data-len, m_data), - pbf_reader::const_uint32_iterator(m_data, m_data)); -} - -std::pair pbf_reader::get_packed_sint32() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_sint32_iterator(m_data-len, m_data), - pbf_reader::const_sint32_iterator(m_data, m_data)); -} - -std::pair pbf_reader::get_packed_int64() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_int64_iterator(m_data-len, m_data), - pbf_reader::const_int64_iterator(m_data, m_data)); -} - -std::pair pbf_reader::get_packed_uint64() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_uint64_iterator(m_data-len, m_data), - pbf_reader::const_uint64_iterator(m_data, m_data)); -} - -std::pair pbf_reader::get_packed_sint64() { - protozero_assert(tag() != 0 && "call next() before accessing field value"); - auto len = get_len_and_skip(); - return std::make_pair(pbf_reader::const_sint64_iterator(m_data-len, m_data), - pbf_reader::const_sint64_iterator(m_data, m_data)); +/** + * Swap two pbf_reader objects. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline void swap(pbf_reader& lhs, pbf_reader& rhs) noexcept { + lhs.swap(rhs); } } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/pbf_writer.hpp b/third_party/libosmium/include/protozero/pbf_writer.hpp index 422e1477232..bce1c3ffcbf 100644 --- a/third_party/libosmium/include/protozero/pbf_writer.hpp +++ b/third_party/libosmium/include/protozero/pbf_writer.hpp @@ -22,6 +22,7 @@ documentation. #include #include #include +#include #include #include @@ -68,38 +69,38 @@ class pbf_writer { // parent to the position where the data of the submessage is written to. std::size_t m_pos = 0; - inline void add_varint(uint64_t value) { + void add_varint(uint64_t value) { protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); protozero_assert(m_data); write_varint(std::back_inserter(*m_data), value); } - inline void add_field(pbf_tag_type tag, pbf_wire_type type) { + void add_field(pbf_tag_type tag, pbf_wire_type type) { protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1 << 29) - 1))) && "tag out of range"); - uint32_t b = (tag << 3) | uint32_t(type); + const uint32_t b = (tag << 3) | uint32_t(type); add_varint(b); } - inline void add_tagged_varint(pbf_tag_type tag, uint64_t value) { + void add_tagged_varint(pbf_tag_type tag, uint64_t value) { add_field(tag, pbf_wire_type::varint); add_varint(value); } template - inline void add_fixed(T value) { + void add_fixed(T value) { protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); protozero_assert(m_data); #if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN m_data->append(reinterpret_cast(&value), sizeof(T)); #else - auto size = m_data->size(); + const auto size = m_data->size(); m_data->resize(size + sizeof(T)); byteswap(reinterpret_cast(&value), const_cast(m_data->data() + size)); #endif } template - inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) { + void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) { if (first == last) { return; } @@ -112,12 +113,12 @@ class pbf_writer { } template - inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) { + void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) { if (first == last) { return; } - auto length = std::distance(first, last); + const auto length = std::distance(first, last); add_length_varint(tag, sizeof(T) * pbf_length_type(length)); reserve(sizeof(T) * std::size_t(length)); @@ -127,7 +128,7 @@ class pbf_writer { } template - inline void add_packed_varint(pbf_tag_type tag, It first, It last) { + void add_packed_varint(pbf_tag_type tag, It first, It last) { if (first == last) { return; } @@ -140,7 +141,7 @@ class pbf_writer { } template - inline void add_packed_svarint(pbf_tag_type tag, It first, It last) { + void add_packed_svarint(pbf_tag_type tag, It first, It last) { if (first == last) { return; } @@ -155,14 +156,18 @@ class pbf_writer { // The number of bytes to reserve for the varint holding the length of // a length-delimited field. The length has to fit into pbf_length_type, // and a varint needs 8 bit for every 7 bit. - static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1; + enum constant_reserve_bytes : int { + reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1 + }; // If m_rollpack_pos is set to this special value, it means that when // the submessage is closed, nothing needs to be done, because the length // of the submessage has already been written correctly. - static const std::size_t size_is_known = std::numeric_limits::max(); + enum constant_size_is_known : std::size_t { + size_is_known = std::numeric_limits::max() + }; - inline void open_submessage(pbf_tag_type tag, std::size_t size) { + void open_submessage(pbf_tag_type tag, std::size_t size) { protozero_assert(m_pos == 0); protozero_assert(m_data); if (size == 0) { @@ -177,7 +182,7 @@ class pbf_writer { m_pos = m_data->size(); } - inline void rollback_submessage() { + void rollback_submessage() { protozero_assert(m_pos != 0); protozero_assert(m_rollback_pos != size_is_known); protozero_assert(m_data); @@ -185,20 +190,20 @@ class pbf_writer { m_pos = 0; } - inline void commit_submessage() { + void commit_submessage() { protozero_assert(m_pos != 0); protozero_assert(m_rollback_pos != size_is_known); protozero_assert(m_data); - auto length = pbf_length_type(m_data->size() - m_pos); + const auto length = pbf_length_type(m_data->size() - m_pos); protozero_assert(m_data->size() >= m_pos - reserve_bytes); - auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length); + const auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length); m_data->erase(m_data->begin() + long(m_pos) - reserve_bytes + n, m_data->begin() + long(m_pos)); m_pos = 0; } - inline void close_submessage() { + void close_submessage() { protozero_assert(m_data); if (m_pos == 0 || m_rollback_pos == size_is_known) { return; @@ -210,7 +215,7 @@ class pbf_writer { } } - inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) { + void add_length_varint(pbf_tag_type tag, pbf_length_type length) { add_field(tag, pbf_wire_type::length_delimited); add_varint(length); } @@ -222,7 +227,7 @@ class pbf_writer { * stores a reference to that string and adds all data to it. The string * doesn't have to be empty. The pbf_writer will just append data. */ - inline explicit pbf_writer(std::string& data) noexcept : + explicit pbf_writer(std::string& data) noexcept : m_data(&data), m_parent_writer(nullptr), m_pos(0) { @@ -232,7 +237,7 @@ class pbf_writer { * Create a writer without a data store. In this form the writer can not * be used! */ - inline pbf_writer() noexcept : + pbf_writer() noexcept : m_data(nullptr), m_parent_writer(nullptr), m_pos(0) { @@ -248,7 +253,7 @@ class pbf_writer { * Setting this allows some optimizations but is only possible in * a few very specific cases. */ - inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) : + pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) : m_data(parent_writer.m_data), m_parent_writer(&parent_writer), m_pos(0) { @@ -262,17 +267,30 @@ class pbf_writer { pbf_writer& operator=(const pbf_writer&) noexcept = default; /// A pbf_writer object can be moved - inline pbf_writer(pbf_writer&&) noexcept = default; + pbf_writer(pbf_writer&&) noexcept = default; /// A pbf_writer object can be moved - inline pbf_writer& operator=(pbf_writer&&) noexcept = default; + pbf_writer& operator=(pbf_writer&&) noexcept = default; - inline ~pbf_writer() { + ~pbf_writer() { if (m_parent_writer) { m_parent_writer->close_submessage(); } } + /** + * Swap the contents of this object with the other. + * + * @param other Other object to swap data with. + */ + void swap(pbf_writer& other) noexcept { + using std::swap; + swap(m_data, other.m_data); + swap(m_parent_writer, other.m_parent_writer); + swap(m_rollback_pos, other.m_rollback_pos); + swap(m_pos, other.m_pos); + } + /** * Reserve size bytes in the underlying message store in addition to * whatever the message store already holds. So unlike @@ -286,7 +304,14 @@ class pbf_writer { m_data->reserve(m_data->size() + size); } - inline void rollback() { + /** + * Cancel writing of this submessage. The complete submessage will be + * removed as if it was never created and no fields were added. + * + * @pre Must be a pbf_writer of a submessage, ie one opened with the + * pbf_writer constructor taking a parent message. + */ + void rollback() { protozero_assert(m_parent_writer && "you can't call rollback() on a pbf_writer without a parent"); protozero_assert(m_pos == 0 && "you can't call rollback() on a pbf_writer that has an open nested submessage"); m_parent_writer->rollback_submessage(); @@ -304,7 +329,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_bool(pbf_tag_type tag, bool value) { + void add_bool(pbf_tag_type tag, bool value) { add_field(tag, pbf_wire_type::varint); protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); protozero_assert(m_data); @@ -317,7 +342,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_enum(pbf_tag_type tag, int32_t value) { + void add_enum(pbf_tag_type tag, int32_t value) { add_tagged_varint(tag, uint64_t(value)); } @@ -327,7 +352,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_int32(pbf_tag_type tag, int32_t value) { + void add_int32(pbf_tag_type tag, int32_t value) { add_tagged_varint(tag, uint64_t(value)); } @@ -337,7 +362,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_sint32(pbf_tag_type tag, int32_t value) { + void add_sint32(pbf_tag_type tag, int32_t value) { add_tagged_varint(tag, encode_zigzag32(value)); } @@ -347,7 +372,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_uint32(pbf_tag_type tag, uint32_t value) { + void add_uint32(pbf_tag_type tag, uint32_t value) { add_tagged_varint(tag, value); } @@ -357,7 +382,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_int64(pbf_tag_type tag, int64_t value) { + void add_int64(pbf_tag_type tag, int64_t value) { add_tagged_varint(tag, uint64_t(value)); } @@ -367,7 +392,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_sint64(pbf_tag_type tag, int64_t value) { + void add_sint64(pbf_tag_type tag, int64_t value) { add_tagged_varint(tag, encode_zigzag64(value)); } @@ -377,7 +402,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_uint64(pbf_tag_type tag, uint64_t value) { + void add_uint64(pbf_tag_type tag, uint64_t value) { add_tagged_varint(tag, value); } @@ -387,7 +412,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_fixed32(pbf_tag_type tag, uint32_t value) { + void add_fixed32(pbf_tag_type tag, uint32_t value) { add_field(tag, pbf_wire_type::fixed32); add_fixed(value); } @@ -398,7 +423,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_sfixed32(pbf_tag_type tag, int32_t value) { + void add_sfixed32(pbf_tag_type tag, int32_t value) { add_field(tag, pbf_wire_type::fixed32); add_fixed(value); } @@ -409,7 +434,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_fixed64(pbf_tag_type tag, uint64_t value) { + void add_fixed64(pbf_tag_type tag, uint64_t value) { add_field(tag, pbf_wire_type::fixed64); add_fixed(value); } @@ -420,7 +445,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_sfixed64(pbf_tag_type tag, int64_t value) { + void add_sfixed64(pbf_tag_type tag, int64_t value) { add_field(tag, pbf_wire_type::fixed64); add_fixed(value); } @@ -431,7 +456,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_float(pbf_tag_type tag, float value) { + void add_float(pbf_tag_type tag, float value) { add_field(tag, pbf_wire_type::fixed32); add_fixed(value); } @@ -442,7 +467,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_double(pbf_tag_type tag, double value) { + void add_double(pbf_tag_type tag, double value) { add_field(tag, pbf_wire_type::fixed64); add_fixed(value); } @@ -454,7 +479,7 @@ class pbf_writer { * @param value Pointer to value to be written * @param size Number of bytes to be written */ - inline void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) { + void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) { protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); protozero_assert(m_data); protozero_assert(size <= std::numeric_limits::max()); @@ -468,7 +493,17 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_bytes(pbf_tag_type tag, const std::string& value) { + void add_bytes(pbf_tag_type tag, const data_view& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "bytes" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_bytes(pbf_tag_type tag, const std::string& value) { add_bytes(tag, value.data(), value.size()); } @@ -479,7 +514,7 @@ class pbf_writer { * @param value Pointer to value to be written * @param size Number of bytes to be written */ - inline void add_string(pbf_tag_type tag, const char* value, std::size_t size) { + void add_string(pbf_tag_type tag, const char* value, std::size_t size) { add_bytes(tag, value, size); } @@ -489,7 +524,17 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written */ - inline void add_string(pbf_tag_type tag, const std::string& value) { + void add_string(pbf_tag_type tag, const data_view& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "string" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_string(pbf_tag_type tag, const std::string& value) { add_bytes(tag, value.data(), value.size()); } @@ -500,7 +545,7 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Pointer to value to be written */ - inline void add_string(pbf_tag_type tag, const char* value) { + void add_string(pbf_tag_type tag, const char* value) { add_bytes(tag, value, std::strlen(value)); } @@ -511,7 +556,7 @@ class pbf_writer { * @param value Pointer to message to be written * @param size Length of the message */ - inline void add_message(pbf_tag_type tag, const char* value, std::size_t size) { + void add_message(pbf_tag_type tag, const char* value, std::size_t size) { add_bytes(tag, value, size); } @@ -521,7 +566,17 @@ class pbf_writer { * @param tag Tag (field number) of the field * @param value Value to be written. The value must be a complete message. */ - inline void add_message(pbf_tag_type tag, const std::string& value) { + void add_message(pbf_tag_type tag, const data_view& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "message" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written. The value must be a complete message. + */ + void add_message(pbf_tag_type tag, const std::string& value) { add_bytes(tag, value.data(), value.size()); } @@ -535,126 +590,126 @@ class pbf_writer { /** * Add "repeated packed bool" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to bool. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed enum" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed int32" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed sint32" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_svarint(tag, first, last); } /** * Add "repeated packed uint32" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to uint32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed int64" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int64_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed sint64" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int64_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_svarint(tag, first, last); } /** * Add "repeated packed uint64" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to uint64_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_varint(tag, first, last); } /** * Add "repeated packed fixed32" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to uint32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -662,14 +717,14 @@ class pbf_writer { /** * Add "repeated packed sfixed32" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int32_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -677,14 +732,14 @@ class pbf_writer { /** * Add "repeated packed fixed64" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to uint64_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -692,14 +747,14 @@ class pbf_writer { /** * Add "repeated packed sfixed64" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to int64_t. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -707,14 +762,14 @@ class pbf_writer { /** * Add "repeated packed float" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to float. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -722,14 +777,14 @@ class pbf_writer { /** * Add "repeated packed double" field to data. * - * @tparam InputIterator An type satisfying the InputIterator concept. + * @tparam InputIterator A type satisfying the InputIterator concept. * Dereferencing the iterator must yield a type assignable to double. * @param tag Tag (field number) of the field * @param first Iterator pointing to the beginning of the data * @param last Iterator pointing one past the end of data */ template - inline void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) { + void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) { add_packed_fixed(tag, first, last, typename std::iterator_traits::iterator_category()); } @@ -742,6 +797,16 @@ class pbf_writer { }; // class pbf_writer +/** + * Swap two pbf_writer objects. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline void swap(pbf_writer& lhs, pbf_writer& rhs) noexcept { + lhs.swap(rhs); +} + namespace detail { class packed_field { @@ -817,19 +882,46 @@ namespace detail { } // end namespace detail +/// Class for generating packed repeated bool fields. using packed_field_bool = detail::packed_field_varint; + +/// Class for generating packed repeated enum fields. using packed_field_enum = detail::packed_field_varint; + +/// Class for generating packed repeated int32 fields. using packed_field_int32 = detail::packed_field_varint; + +/// Class for generating packed repeated sint32 fields. using packed_field_sint32 = detail::packed_field_svarint; + +/// Class for generating packed repeated uint32 fields. using packed_field_uint32 = detail::packed_field_varint; + +/// Class for generating packed repeated int64 fields. using packed_field_int64 = detail::packed_field_varint; + +/// Class for generating packed repeated sint64 fields. using packed_field_sint64 = detail::packed_field_svarint; + +/// Class for generating packed repeated uint64 fields. using packed_field_uint64 = detail::packed_field_varint; + +/// Class for generating packed repeated fixed32 fields. using packed_field_fixed32 = detail::packed_field_fixed; + +/// Class for generating packed repeated sfixed32 fields. using packed_field_sfixed32 = detail::packed_field_fixed; + +/// Class for generating packed repeated fixed64 fields. using packed_field_fixed64 = detail::packed_field_fixed; + +/// Class for generating packed repeated sfixed64 fields. using packed_field_sfixed64 = detail::packed_field_fixed; + +/// Class for generating packed repeated float fields. using packed_field_float = detail::packed_field_fixed; + +/// Class for generating packed repeated double fields. using packed_field_double = detail::packed_field_fixed; } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/types.hpp b/third_party/libosmium/include/protozero/types.hpp index 6856b3dde79..8b046387e78 100644 --- a/third_party/libosmium/include/protozero/types.hpp +++ b/third_party/libosmium/include/protozero/types.hpp @@ -16,33 +16,173 @@ documentation. * @brief Contains the declaration of low-level types used in the pbf format. */ +#include #include +#include +#include +#include + +#include namespace protozero { +/** + * The type used for field tags (field numbers). + */ +using pbf_tag_type = uint32_t; + +/** + * The type used to encode type information. + * See the table on + * https://developers.google.com/protocol-buffers/docs/encoding + */ +enum class pbf_wire_type : uint32_t { + varint = 0, // int32/64, uint32/64, sint32/64, bool, enum + fixed64 = 1, // fixed64, sfixed64, double + length_delimited = 2, // string, bytes, embedded messages, + // packed repeated fields + fixed32 = 5, // fixed32, sfixed32, float + unknown = 99 // used for default setting in this library +}; + +/** + * The type used for length values, such as the length of a field. + */ +using pbf_length_type = uint32_t; + +#ifdef PROTOZERO_USE_VIEW +using data_view = PROTOZERO_USE_VIEW; +#else + +/** + * Holds a pointer to some data and a length. + * + * This class is supposed to be compatible with the std::string_view + * that will be available in C++17. + */ +class data_view { + + const char* m_data; + std::size_t m_size; + +public: + + /** + * Default constructor. Construct an empty data_view. + */ + constexpr data_view() noexcept + : m_data(nullptr), + m_size(0) { + } + + /** + * Create data_view from pointer and size. + * + * @param data Pointer to the data. + * @param size Length of the data. + */ + constexpr data_view(const char* data, std::size_t size) noexcept + : m_data(data), + m_size(size) { + } + /** - * The type used for field tags (field numbers). + * Create data_view from string. + * + * @param str String with the data. */ - typedef uint32_t pbf_tag_type; + data_view(const std::string& str) noexcept + : m_data(str.data()), + m_size(str.size()) { + } /** - * The type used to encode type information. - * See the table on - * https://developers.google.com/protocol-buffers/docs/encoding + * Create data_view from zero-terminated string. + * + * @param data Pointer to the data. */ - enum class pbf_wire_type : uint32_t { - varint = 0, // int32/64, uint32/64, sint32/64, bool, enum - fixed64 = 1, // fixed64, sfixed64, double - length_delimited = 2, // string, bytes, embedded messages, - // packed repeated fields - fixed32 = 5, // fixed32, sfixed32, float - unknown = 99 // used for default setting in this library - }; + data_view(const char* data) noexcept + : m_data(data), + m_size(std::strlen(data)) { + } /** - * The type used for length values, such as the length of a field. + * Swap the contents of this object with the other. + * + * @param other Other object to swap data with. */ - typedef uint32_t pbf_length_type; + void swap(data_view& other) noexcept { + using std::swap; + swap(m_data, other.m_data); + swap(m_size, other.m_size); + } + + /// Return pointer to data. + constexpr const char* data() const noexcept { + return m_data; + } + + /// Return length of data in bytes. + constexpr std::size_t size() const noexcept { + return m_size; + } + + /** + * Convert data view to string. + * + * @pre Must not be default constructed data_view. + */ + std::string to_string() const { + protozero_assert(m_data); + return std::string{m_data, m_size}; + } + + /** + * Convert data view to string. + * + * @pre Must not be default constructed data_view. + */ + explicit operator std::string() const { + protozero_assert(m_data); + return std::string{m_data, m_size}; + } + +}; // class data_view + +/** + * Swap two data_view objects. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline void swap(data_view& lhs, data_view& rhs) noexcept { + lhs.swap(rhs); +} + +/** + * Two data_view instances are equal if they have the same size and the + * same content. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline bool operator==(data_view& lhs, data_view& rhs) noexcept { + return lhs.size() == rhs.size() && !std::strcmp(lhs.data(), rhs.data()); +} + +/** + * Two data_view instances are not equal if they have different sizes or the + * content differs. + * + * @param lhs First object. + * @param rhs Second object. + */ +inline bool operator!=(data_view& lhs, data_view& rhs) noexcept { + return !(lhs == rhs); +} + +#endif + } // end namespace protozero diff --git a/third_party/libosmium/include/protozero/varint.hpp b/third_party/libosmium/include/protozero/varint.hpp index 4242df95142..f6142d1c17f 100644 --- a/third_party/libosmium/include/protozero/varint.hpp +++ b/third_party/libosmium/include/protozero/varint.hpp @@ -23,13 +23,54 @@ documentation. namespace protozero { /** - * The maximum length of a 64bit varint. + * The maximum length of a 64 bit varint. */ constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1; -// from https://github.com/facebook/folly/blob/master/folly/Varint.h +namespace detail { + + // from https://github.com/facebook/folly/blob/master/folly/Varint.h + inline uint64_t decode_varint_impl(const char** data, const char* end) { + const int8_t* begin = reinterpret_cast(*data); + const int8_t* iend = reinterpret_cast(end); + const int8_t* p = begin; + uint64_t val = 0; + + if (iend - begin >= max_varint_length) { // fast path + do { + int64_t b; + b = *p++; val = uint64_t((b & 0x7f) ); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 7); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 14); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 21); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 28); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 35); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 42); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 49); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 56); if (b >= 0) break; + b = *p++; val |= uint64_t((b & 0x7f) << 63); if (b >= 0) break; + throw varint_too_long_exception(); + } while (false); + } else { + int shift = 0; + while (p != iend && *p < 0) { + val |= uint64_t(*p++ & 0x7f) << shift; + shift += 7; + } + if (p == iend) { + throw end_of_buffer_exception(); + } + val |= uint64_t(*p++) << shift; + } + + *data = reinterpret_cast(p); + return val; + } + +} // end namespace detail + /** - * Decode a 64bit varint. + * Decode a 64 bit varint. * * Strong exception guarantee: if there is an exception the data pointer will * not be changed. @@ -39,54 +80,68 @@ constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1; * @param[in] end Pointer one past the end of the input data. * @returns The decoded integer * @throws varint_too_long_exception if the varint is longer then the maximum - * length that would fit in a 64bit int. Usually this means your data + * length that would fit in a 64 bit int. Usually this means your data * is corrupted or you are trying to read something as a varint that * isn't. * @throws end_of_buffer_exception if the *end* of the buffer was reached * before the end of the varint. */ inline uint64_t decode_varint(const char** data, const char* end) { + // If this is a one-byte varint, decode it here. + if (end != *data && ((**data & 0x80) == 0)) { + uint64_t val = uint64_t(**data); + ++(*data); + return val; + } + // If this varint is more than one byte, defer to complete implementation. + return detail::decode_varint_impl(data, end); +} + +/** + * Skip over a varint. + * + * Strong exception guarantee: if there is an exception the data pointer will + * not be changed. + * + * @param[in,out] data Pointer to pointer to the input data. After the function + * returns this will point to the next data to be read. + * @param[in] end Pointer one past the end of the input data. + * @throws end_of_buffer_exception if the *end* of the buffer was reached + * before the end of the varint. + */ +inline void skip_varint(const char** data, const char* end) { const int8_t* begin = reinterpret_cast(*data); const int8_t* iend = reinterpret_cast(end); const int8_t* p = begin; - uint64_t val = 0; - - if (iend - begin >= max_varint_length) { // fast path - do { - int64_t b; - b = *p++; val = uint64_t((b & 0x7f) ); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 7); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 14); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 21); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 28); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 35); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 42); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 49); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 56); if (b >= 0) break; - b = *p++; val |= uint64_t((b & 0x7f) << 63); if (b >= 0) break; - throw varint_too_long_exception(); - } while (false); - } else { - int shift = 0; - while (p != iend && *p < 0) { - val |= uint64_t(*p++ & 0x7f) << shift; - shift += 7; - } - if (p == iend) { - throw end_of_buffer_exception(); - } - val |= uint64_t(*p++) << shift; + + while (p != iend && *p < 0) { + ++p; + } + + if (p >= begin + max_varint_length) { + throw varint_too_long_exception(); } + if (p == iend) { + throw end_of_buffer_exception(); + } + + ++p; + *data = reinterpret_cast(p); - return val; } /** - * Varint-encode a 64bit integer. + * Varint encode a 64 bit integer. + * + * @tparam T An output iterator type. + * @param data Output iterator the varint encoded value will be written to + * byte by byte. + * @param value The integer that will be encoded. + * @throws Any exception thrown by increment or dereference operator on data. */ -template -inline int write_varint(OutputIterator data, uint64_t value) { +template +inline int write_varint(T data, uint64_t value) { int n=1; while (value >= 0x80) { diff --git a/third_party/libosmium/include/protozero/version.hpp b/third_party/libosmium/include/protozero/version.hpp index 7b60e2eda8d..127e6497255 100644 --- a/third_party/libosmium/include/protozero/version.hpp +++ b/third_party/libosmium/include/protozero/version.hpp @@ -10,13 +10,26 @@ documentation. *****************************************************************************/ +/** + * @file version.hpp + * + * @brief Contains macros defining the protozero version. + */ + +/// The major version number #define PROTOZERO_VERSION_MAJOR 1 -#define PROTOZERO_VERSION_MINOR 3 -#define PROTOZERO_VERSION_PATCH 0 +/// The minor version number +#define PROTOZERO_VERSION_MINOR 4 + +/// The patch number +#define PROTOZERO_VERSION_PATCH 2 + +/// The complete version number #define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH) -#define PROTOZERO_VERSION_STRING "1.3.0" +/// Version number as string +#define PROTOZERO_VERSION_STRING "1.4.2" #endif // PROTOZERO_VERSION_HPP diff --git a/third_party/libosmium/osmium.imp b/third_party/libosmium/osmium.imp index c45794d08eb..77c5c37aaaf 100644 --- a/third_party/libosmium/osmium.imp +++ b/third_party/libosmium/osmium.imp @@ -2,10 +2,15 @@ # # Configuration for Include-What-You-Use tool # -# https://code.google.com/p/include-what-you-use/ +# http://include-what-you-use.org/ # #----------------------------------------------------------------------------- [ { "include": ["", "private", "", "public"] }, - { "include": ["", "public", "", "public"] } + { "include": ["", "private", "", "public"] }, + { "include": ["", "public", "", "public"] }, + { "include": ['"utf8/checked.h"', "private", "", "public"] }, + { "include": ['"utf8/unchecked.h"', "private", "", "public"] }, + { "include": ["", "public", "", "public"] }, + { "include": ["", "public", "", "public"] } ] diff --git a/third_party/libosmium/test/CMakeLists.txt b/third_party/libosmium/test/CMakeLists.txt index cea67ecd893..6230cde23da 100644 --- a/third_party/libosmium/test/CMakeLists.txt +++ b/third_party/libosmium/test/CMakeLists.txt @@ -85,6 +85,40 @@ function(add_unit_test _tgroup _tname) endif() endfunction() + +#----------------------------------------------------------------------------- +# +# Prepare some variables so querying it for tests work properly. +# +#----------------------------------------------------------------------------- + +if(NOT GEOS_FOUND) + set(GEOS_FOUND FALSE) +endif() + +if(NOT PROJ_FOUND) + set(PROJ_FOUND FALSE) +endif() + +if(NOT SPARSEHASH_FOUND) + set(SPARSEHASH_FOUND FALSE) +endif() + +if(NOT BZIP2_FOUND) + set(BZIP2_FOUND FALSE) +endif() + +if(NOT Threads_FOUND) + set(Threads_FOUND FALSE) +endif() + +if(GEOS_FOUND AND PROJ_FOUND) + set(GEOS_AND_PROJ_FOUND TRUE) +else() + set(GEOS_AND_PROJ_FOUND FALSE) +endif() + + #----------------------------------------------------------------------------- # # Add all tests. @@ -93,6 +127,7 @@ endfunction() add_unit_test(area test_area_id) add_unit_test(area test_node_ref_segment) +add_unit_test(basic test_area) add_unit_test(basic test_box) add_unit_test(basic test_changeset) add_unit_test(basic test_crc) @@ -112,11 +147,6 @@ add_unit_test(buffer test_buffer_purge) add_unit_test(builder test_attr) -if(GEOS_FOUND AND PROJ_FOUND) - set(GEOS_AND_PROJ_FOUND TRUE) -else() - set(GEOS_AND_PROJ_FOUND FALSE) -endif() add_unit_test(geom test_factory_with_projection ENABLE_IF ${GEOS_AND_PROJ_FOUND} LIBS ${GEOS_LIBRARY} ${PROJ_LIBRARY}) @@ -129,17 +159,19 @@ add_unit_test(geom test_geos_wkb ENABLE_IF ${GEOS_FOUND} LIBS ${GEOS_LIBRARY}) add_unit_test(geom test_mercator) add_unit_test(geom test_ogr ENABLE_IF ${GDAL_FOUND} LIBS ${GDAL_LIBRARY}) add_unit_test(geom test_projection ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY}) -add_unit_test(geom test_tile) +add_unit_test(geom test_tile ENABLE_IF ${GEOS_FOUND}) 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) add_unit_test(io test_reader LIBS "${OSMIUM_XML_LIBRARIES};${OSMIUM_PBF_LIBRARIES}") add_unit_test(io test_reader_with_mock_decompression ENABLE_IF ${Threads_FOUND} LIBS ${OSMIUM_XML_LIBRARIES}) add_unit_test(io test_reader_with_mock_parser ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT}) +add_unit_test(io test_opl_parser) add_unit_test(io test_output_utils) add_unit_test(io test_output_iterator ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT}) add_unit_test(io test_string_table) diff --git a/third_party/libosmium/test/README b/third_party/libosmium/test/README index 8195824b48e..868b533f29e 100644 --- a/third_party/libosmium/test/README +++ b/third_party/libosmium/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/third_party/libosmium/test/data-tests/include/common.hpp b/third_party/libosmium/test/data-tests/include/common.hpp index a6fd3df6162..4dad07d32ef 100644 --- a/third_party/libosmium/test/data-tests/include/common.hpp +++ b/third_party/libosmium/test/data-tests/include/common.hpp @@ -10,9 +10,9 @@ #include #include -typedef osmium::index::map::Dummy index_neg_type; -typedef osmium::index::map::SparseMemArray index_pos_type; -typedef osmium::handler::NodeLocationsForWays location_handler_type; +using index_neg_type = osmium::index::map::Dummy; +using index_pos_type = osmium::index::map::SparseMemArray; +using location_handler_type = osmium::handler::NodeLocationsForWays; #include "check_basics_handler.hpp" #include "check_wkt_handler.hpp" diff --git a/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp b/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp index cf4fc521a82..6d0328c8d59 100644 --- a/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp +++ b/third_party/libosmium/test/data-tests/testdata-multipolygon.cpp @@ -1,7 +1,9 @@ +#include #include #include #include +#include #include @@ -17,21 +19,20 @@ #include #include -typedef osmium::index::map::SparseMemArray index_type; - -typedef osmium::handler::NodeLocationsForWays location_handler_type; +using index_type = osmium::index::map::SparseMemArray; +using location_handler_type = osmium::handler::NodeLocationsForWays; struct less_charptr { - bool operator()(const char* a, const char* b) const { + bool operator()(const char* a, const char* b) const noexcept { return std::strcmp(a, b) < 0; } }; // less_charptr -typedef std::map tagmap_type; +using tagmap_type = std::map; -inline tagmap_type create_map(const osmium::TagList& taglist) { +tagmap_type create_map(const osmium::TagList& taglist) { tagmap_type map; for (auto& tag : taglist) { @@ -52,7 +53,7 @@ class TestHandler : public osmium::handler::Handler { std::ofstream m_out; - bool m_first_out {true}; + bool m_first_out{true}; public: @@ -77,7 +78,7 @@ class TestHandler : public osmium::handler::Handler { } void node(const osmium::Node& node) { - gdalcpp::Feature feature(m_layer_point, m_ogr_factory.create_point(node)); + gdalcpp::Feature feature{m_layer_point, m_ogr_factory.create_point(node)}; feature.set_field("id", static_cast(node.id())); feature.set_field("type", node.tags().get_value_by_key("type")); feature.add_to_layer(); @@ -85,11 +86,11 @@ class TestHandler : public osmium::handler::Handler { void way(const osmium::Way& way) { try { - gdalcpp::Feature feature(m_layer_lines, m_ogr_factory.create_linestring(way)); + gdalcpp::Feature feature{m_layer_lines, m_ogr_factory.create_linestring(way)}; feature.set_field("id", static_cast(way.id())); feature.set_field("type", way.tags().get_value_by_key("type")); feature.add_to_layer(); - } catch (osmium::geometry_error&) { + } catch (const osmium::geometry_error&) { std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n"; } } @@ -103,10 +104,10 @@ class TestHandler : public osmium::handler::Handler { } m_out << "{\n \"test_id\": " << (area.orig_id() / 1000) << ",\n \"area_id\": " << area.id() << ",\n \"from_id\": " << area.orig_id() << ",\n \"from_type\": \"" << (area.from_way() ? "way" : "relation") << "\",\n \"wkt\": \""; try { - std::string wkt = m_wkt_factory.create_multipolygon(area); + const std::string wkt = m_wkt_factory.create_multipolygon(area); m_out << wkt << "\",\n \"tags\": {"; - auto tagmap = create_map(area.tags()); + const auto tagmap = create_map(area.tags()); bool first = true; for (auto& tag : tagmap) { if (first) { @@ -117,11 +118,11 @@ class TestHandler : public osmium::handler::Handler { m_out << '"' << tag.first << "\": \"" << tag.second << '"'; } m_out << "}\n}"; - } catch (osmium::geometry_error&) { + } catch (const osmium::geometry_error&) { m_out << "INVALID\"\n}"; } try { - gdalcpp::Feature feature(m_layer_mpoly, m_ogr_factory.create_multipolygon(area)); + gdalcpp::Feature feature{m_layer_mpoly, m_ogr_factory.create_multipolygon(area)}; feature.set_field("id", static_cast(area.orig_id())); std::string from_type; @@ -132,7 +133,7 @@ class TestHandler : public osmium::handler::Handler { } feature.set_field("from_type", from_type.c_str()); feature.add_to_layer(); - } catch (osmium::geometry_error&) { + } catch (const osmium::geometry_error&) { std::cerr << "Ignoring illegal geometry for area " << area.id() << " created from " << (area.from_way() ? "way" : "relation") << " with id=" << area.orig_id() << ".\n"; } } @@ -144,35 +145,38 @@ class TestHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " INFILE\n"; - exit(1); + std::exit(1); } - std::string output_format("SQLite"); - std::string input_filename(argv[1]); - std::string output_filename("multipolygon.db"); + const std::string output_format{"SQLite"}; + const std::string input_filename{argv[1]}; + const std::string output_filename{"multipolygon.db"}; CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); - gdalcpp::Dataset dataset{output_format, output_filename, gdalcpp::SRS{}, { "SPATIALITE=TRUE" }}; + gdalcpp::Dataset dataset{output_format, output_filename, gdalcpp::SRS{}, {"SPATIALITE=TRUE"}}; - osmium::area::ProblemReporterOGR problem_reporter(dataset); - osmium::area::Assembler::config_type assembler_config(&problem_reporter); - assembler_config.enable_debug_output(); - osmium::area::MultipolygonCollector collector(assembler_config); + osmium::area::ProblemReporterOGR problem_reporter{dataset}; + osmium::area::Assembler::config_type assembler_config; + assembler_config.problem_reporter = &problem_reporter; + assembler_config.check_roles = true; + assembler_config.create_empty_areas = true; + assembler_config.debug_level = 2; + osmium::area::MultipolygonCollector collector{assembler_config}; std::cerr << "Pass 1...\n"; - osmium::io::Reader reader1(input_filename); + osmium::io::Reader reader1{input_filename}; collector.read_relations(reader1); reader1.close(); std::cerr << "Pass 1 done\n"; index_type index; - location_handler_type location_handler(index); + location_handler_type location_handler{index}; location_handler.ignore_errors(); - TestHandler test_handler(dataset); + TestHandler test_handler{dataset}; std::cerr << "Pass 2...\n"; - osmium::io::Reader reader2(input_filename); + osmium::io::Reader reader2{input_filename}; osmium::apply(reader2, location_handler, test_handler, collector.handler([&test_handler](const osmium::memory::Buffer& area_buffer) { osmium::apply(area_buffer, test_handler); })); diff --git a/third_party/libosmium/test/data-tests/testdata-overview.cpp b/third_party/libosmium/test/data-tests/testdata-overview.cpp index 43d672dd1c3..4994f23efa1 100644 --- a/third_party/libosmium/test/data-tests/testdata-overview.cpp +++ b/third_party/libosmium/test/data-tests/testdata-overview.cpp @@ -1,6 +1,7 @@ /* The code in this file is released into the Public Domain. */ #include +#include #include @@ -12,8 +13,8 @@ #include #include -typedef osmium::index::map::SparseMemArray index_type; -typedef osmium::handler::NodeLocationsForWays location_handler_type; +using index_type = osmium::index::map::SparseMemArray; +using location_handler_type = osmium::handler::NodeLocationsForWays; class TestOverviewHandler : public osmium::handler::Handler { @@ -64,7 +65,7 @@ class TestOverviewHandler : public osmium::handler::Handler { } feature.add_to_layer(); - } catch (osmium::geometry_error&) { + } catch (const osmium::geometry_error&) { std::cerr << "Ignoring illegal geometry for way " << way.id() << ".\n"; } } @@ -76,24 +77,24 @@ class TestOverviewHandler : public osmium::handler::Handler { int main(int argc, char* argv[]) { if (argc != 2) { std::cerr << "Usage: " << argv[0] << " INFILE\n"; - exit(1); + std::exit(1); } - std::string output_format("SQLite"); - std::string input_filename(argv[1]); - std::string output_filename("testdata-overview.db"); + const std::string output_format{"SQLite"}; + const std::string input_filename{argv[1]}; + const std::string output_filename{"testdata-overview.db"}; ::unlink(output_filename.c_str()); CPLSetConfigOption("OGR_SQLITE_SYNCHRONOUS", "FALSE"); - gdalcpp::Dataset dataset(output_format, output_filename, gdalcpp::SRS{}, { "SPATIALITE=TRUE" }); + gdalcpp::Dataset dataset{output_format, output_filename, gdalcpp::SRS{}, {"SPATIALITE=TRUE"}}; - osmium::io::Reader reader(input_filename); + osmium::io::Reader reader{input_filename}; index_type index; - location_handler_type location_handler(index); + location_handler_type location_handler{index}; location_handler.ignore_errors(); - TestOverviewHandler handler(dataset); + TestOverviewHandler handler{dataset}; osmium::apply(reader, location_handler, handler); reader.close(); diff --git a/third_party/libosmium/test/data-tests/testdata-testcases.cpp b/third_party/libosmium/test/data-tests/testdata-testcases.cpp index 0ea7fc859f3..d05c28327a4 100644 --- a/third_party/libosmium/test/data-tests/testdata-testcases.cpp +++ b/third_party/libosmium/test/data-tests/testdata-testcases.cpp @@ -15,11 +15,9 @@ int main(int argc, char* argv[]) { std::cerr << "Running tests from '" << dirname << "' (from TESTCASES_DIR environment variable)\n"; } else { std::cerr << "Please set TESTCASES_DIR environment variable.\n"; - exit(1); + std::exit(1); } - int result = Catch::Session().run(argc, argv); - - return result; + return Catch::Session().run(argc, argv); } diff --git a/third_party/libosmium/test/data-tests/testdata-xml.cpp b/third_party/libosmium/test/data-tests/testdata-xml.cpp index b5a0e9028ed..0d2739a3a81 100644 --- a/third_party/libosmium/test/data-tests/testdata-xml.cpp +++ b/third_party/libosmium/test/data-tests/testdata-xml.cpp @@ -5,7 +5,9 @@ #include #include +#include #include +#include #include #include @@ -14,14 +16,14 @@ #include std::string S_(const char* s) { - return std::string(s); + return std::string{s}; } std::string filename(const char* test_id, const char* suffix = "osm") { const char* testdir = getenv("TESTDIR"); if (!testdir) { std::cerr << "You have to set TESTDIR environment variable before running testdata-xml\n"; - exit(2); + std::exit(2); } std::string f; @@ -47,11 +49,11 @@ struct header_buffer_type { // file contents fit into small buffers. std::string read_file(const char* test_id) { - int fd = osmium::io::detail::open_for_reading(filename(test_id)); + const int fd = osmium::io::detail::open_for_reading(filename(test_id)); assert(fd >= 0); std::string input(10000, '\0'); - auto n = ::read(fd, reinterpret_cast(const_cast(input.data())), 10000); + const auto n = ::read(fd, reinterpret_cast(const_cast(input.data())), 10000); assert(n >= 0); input.resize(static_cast(n)); @@ -61,10 +63,10 @@ std::string read_file(const char* test_id) { } std::string read_gz_file(const char* test_id, const char* suffix) { - int fd = osmium::io::detail::open_for_reading(filename(test_id, suffix)); + const int fd = osmium::io::detail::open_for_reading(filename(test_id, suffix)); assert(fd >= 0); - osmium::io::GzipDecompressor gzip_decompressor(fd); + osmium::io::GzipDecompressor gzip_decompressor{fd}; std::string input = gzip_decompressor.read(); gzip_decompressor.close(); @@ -81,7 +83,7 @@ header_buffer_type parse_xml(std::string input) { osmium::io::detail::add_to_queue(input_queue, std::move(input)); osmium::io::detail::add_to_queue(input_queue, std::string{}); - osmium::io::detail::XMLParser parser(input_queue, output_queue, header_promise, osmium::osm_entity_bits::all); + osmium::io::detail::XMLParser parser{input_queue, output_queue, header_promise, osmium::osm_entity_bits::all}; parser.parse(); header_buffer_type result; @@ -117,9 +119,9 @@ TEST_CASE("Reading OSM XML 100") { } SECTION("Using Reader") { - osmium::io::Reader reader(filename("100-correct_but_no_data")); + osmium::io::Reader reader{filename("100-correct_but_no_data")}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); osmium::memory::Buffer buffer = reader.read(); @@ -129,9 +131,9 @@ TEST_CASE("Reading OSM XML 100") { } SECTION("Using Reader asking for header only") { - osmium::io::Reader reader(filename("100-correct_but_no_data"), osmium::osm_entity_bits::nothing); + osmium::io::Reader reader{filename("100-correct_but_no_data"), osmium::osm_entity_bits::nothing}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); reader.close(); } @@ -146,15 +148,15 @@ TEST_CASE("Reading OSM XML 101") { REQUIRE_THROWS_AS(read_xml("101-missing_version"), osmium::format_version_error); try { read_xml("101-missing_version"); - } catch (osmium::format_version_error& e) { + } catch (const osmium::format_version_error& e) { REQUIRE(e.version.empty()); } } SECTION("Using Reader") { REQUIRE_THROWS_AS({ - osmium::io::Reader reader(filename("101-missing_version")); - osmium::io::Header header = reader.header(); + osmium::io::Reader reader{filename("101-missing_version")}; + const osmium::io::Header header{reader.header()}; osmium::memory::Buffer buffer = reader.read(); reader.close(); }, osmium::format_version_error); @@ -170,16 +172,16 @@ TEST_CASE("Reading OSM XML 102") { REQUIRE_THROWS_AS(read_xml("102-wrong_version"), osmium::format_version_error); try { read_xml("102-wrong_version"); - } catch (osmium::format_version_error& e) { + } catch (const osmium::format_version_error& e) { REQUIRE(e.version == "0.1"); } } SECTION("Using Reader") { REQUIRE_THROWS_AS({ - osmium::io::Reader reader(filename("102-wrong_version")); + osmium::io::Reader reader{filename("102-wrong_version")}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; osmium::memory::Buffer buffer = reader.read(); reader.close(); }, osmium::format_version_error); @@ -195,15 +197,15 @@ TEST_CASE("Reading OSM XML 103") { REQUIRE_THROWS_AS(read_xml("103-old_version"), osmium::format_version_error); try { read_xml("103-old_version"); - } catch (osmium::format_version_error& e) { + } catch (const osmium::format_version_error& e) { REQUIRE(e.version == "0.5"); } } SECTION("Using Reader") { REQUIRE_THROWS_AS({ - osmium::io::Reader reader(filename("103-old_version")); - osmium::io::Header header = reader.header(); + osmium::io::Reader reader{filename("103-old_version")}; + const osmium::io::Header header{reader.header()}; osmium::memory::Buffer buffer = reader.read(); reader.close(); }, osmium::format_version_error); @@ -219,7 +221,7 @@ TEST_CASE("Reading OSM XML 104") { REQUIRE_THROWS_AS(read_xml("104-empty_file"), osmium::xml_error); try { read_xml("104-empty_file"); - } catch (osmium::xml_error& e) { + } catch (const osmium::xml_error& e) { REQUIRE(e.line == 1); REQUIRE(e.column == 0); } @@ -227,8 +229,8 @@ TEST_CASE("Reading OSM XML 104") { SECTION("Using Reader") { REQUIRE_THROWS_AS({ - osmium::io::Reader reader(filename("104-empty_file")); - osmium::io::Header header = reader.header(); + osmium::io::Reader reader{filename("104-empty_file")}; + const osmium::io::Header header{reader.header()}; osmium::memory::Buffer buffer = reader.read(); reader.close(); }, osmium::xml_error); @@ -245,8 +247,8 @@ TEST_CASE("Reading OSM XML 105") { SECTION("Using Reader") { REQUIRE_THROWS_AS({ - osmium::io::Reader reader(filename("105-incomplete_xml_file")); - osmium::io::Header header = reader.header(); + osmium::io::Reader reader{filename("105-incomplete_xml_file")}; + const osmium::io::Header header{reader.header()}; osmium::memory::Buffer buffer = reader.read(); reader.close(); }, osmium::xml_error); @@ -270,9 +272,9 @@ TEST_CASE("Reading OSM XML 120") { } SECTION("Using Reader") { - osmium::io::Reader reader(filename("120-correct_gzip_file_without_data", "osm.gz")); + osmium::io::Reader reader{filename("120-correct_gzip_file_without_data", "osm.gz")}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); osmium::memory::Buffer buffer = reader.read(); @@ -296,8 +298,8 @@ TEST_CASE("Reading OSM XML 121") { SECTION("Using Reader") { // can throw osmium::gzip_error or osmium::xml_error REQUIRE_THROWS({ - osmium::io::Reader reader(filename("121-truncated_gzip_file", "osm.gz")); - osmium::io::Header header = reader.header(); + osmium::io::Reader reader{filename("121-truncated_gzip_file", "osm.gz")}; + const osmium::io::Header header{reader.header()}; osmium::memory::Buffer buffer = reader.read(); reader.close(); }); @@ -317,8 +319,8 @@ TEST_CASE("Reading OSM XML 122") { SECTION("Using Reader") { REQUIRE_THROWS_AS({ - osmium::io::Reader reader(filename("122-no_osm_element")); - osmium::io::Header header = reader.header(); + osmium::io::Reader reader{filename("122-no_osm_element")}; + const osmium::io::Header header{reader.header()}; osmium::memory::Buffer buffer = reader.read(); reader.close(); }, osmium::xml_error); @@ -331,19 +333,19 @@ TEST_CASE("Reading OSM XML 122") { TEST_CASE("Reading OSM XML 140") { SECTION("Using Reader") { - osmium::io::Reader reader(filename("140-unicode")); + osmium::io::Reader reader{filename("140-unicode")}; osmium::memory::Buffer buffer = reader.read(); reader.close(); int count = 0; - for (auto it = buffer.begin(); it != buffer.end(); ++it) { + for (const auto& node : buffer.select()) { ++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"]; - auto len = atoi(t["unicode_utf8_length"]); + const auto len = atoi(t["unicode_utf8_length"]); REQUIRE(len == strlen(uc)); REQUIRE(S_(uc) == t["unicode_xml"]); @@ -382,7 +384,7 @@ TEST_CASE("Reading OSM XML 140") { TEST_CASE("Reading OSM XML 141") { SECTION("Using Reader") { - osmium::io::Reader reader(filename("141-entities")); + osmium::io::Reader reader{filename("141-entities")}; osmium::memory::Buffer buffer = reader.read(); reader.close(); REQUIRE(buffer.committed() > 0); @@ -406,40 +408,40 @@ TEST_CASE("Reading OSM XML 141") { TEST_CASE("Reading OSM XML 142") { SECTION("Using Reader to read nodes") { - osmium::io::Reader reader(filename("142-whitespace")); + osmium::io::Reader reader{filename("142-whitespace")}; osmium::memory::Buffer buffer = reader.read(); reader.close(); int count = 0; - for (auto it = buffer.begin(); it != buffer.end(); ++it) { + for (const auto& node : buffer.select()) { ++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; @@ -451,12 +453,12 @@ TEST_CASE("Reading OSM XML 142") { } SECTION("Using Reader to read relation") { - osmium::io::Reader reader(filename("142-whitespace")); + osmium::io::Reader reader{filename("142-whitespace")}; osmium::memory::Buffer buffer = reader.read(); reader.close(); - auto it = buffer.begin(); - REQUIRE(it != buffer.end()); + auto it = buffer.select().begin(); + REQUIRE(it != buffer.select().end()); REQUIRE(it->id() == 21); const auto& members = it->members(); REQUIRE(members.size() == 5); @@ -505,9 +507,9 @@ TEST_CASE("Reading OSM XML 200") { } SECTION("Using Reader") { - osmium::io::Reader reader(filename("200-nodes")); + osmium::io::Reader reader{filename("200-nodes")}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); osmium::memory::Buffer buffer = reader.read(); @@ -519,9 +521,9 @@ TEST_CASE("Reading OSM XML 200") { } SECTION("Using Reader asking for nodes") { - osmium::io::Reader reader(filename("200-nodes"), osmium::osm_entity_bits::node); + osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::node}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); osmium::memory::Buffer buffer = reader.read(); @@ -533,9 +535,9 @@ TEST_CASE("Reading OSM XML 200") { } SECTION("Using Reader asking for header only") { - osmium::io::Reader reader(filename("200-nodes"), osmium::osm_entity_bits::nothing); + osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::nothing}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); REQUIRE_THROWS({ @@ -546,9 +548,9 @@ TEST_CASE("Reading OSM XML 200") { } SECTION("Using Reader asking for ways") { - osmium::io::Reader reader(filename("200-nodes"), osmium::osm_entity_bits::way); + osmium::io::Reader reader{filename("200-nodes"), osmium::osm_entity_bits::way}; - osmium::io::Header header = reader.header(); + const osmium::io::Header header{reader.header()}; REQUIRE(header.get("generator") == "testdata"); osmium::memory::Buffer buffer = reader.read(); diff --git a/third_party/libosmium/test/include/catch.hpp b/third_party/libosmium/test/include/catch.hpp index f04214990ce..879fc5b1d11 100644 --- a/third_party/libosmium/test/include/catch.hpp +++ b/third_party/libosmium/test/include/catch.hpp @@ -1,6 +1,6 @@ /* - * Catch v1.3.3 - * Generated: 2016-01-22 07:51:36.661106 + * Catch v1.5.6 + * Generated: 2016-06-09 19:20:41.460328 * ---------------------------------------------------------- * 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,18 @@ // All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 +#ifdef __cplusplus + +# if __cplusplus >= 201103L +# define CATCH_CPP11_OR_GREATER +# endif + +# if __cplusplus >= 201402L +# define CATCH_CPP14_OR_GREATER +# endif + +#endif + #ifdef __clang__ # if __has_feature(cxx_nullptr) @@ -112,6 +128,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 +156,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 +197,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 +277,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 +710,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 +739,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 +1333,37 @@ namespace Internal { template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs) { - return opCast( lhs ) == opCast( rhs ); + return bool( opCast( lhs ) == opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) != opCast( rhs ); + return bool( opCast( lhs ) != opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) < opCast( rhs ); + return bool( opCast( lhs ) < opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) > opCast( rhs ); + return bool( opCast( lhs ) > opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) >= opCast( rhs ); + return bool( opCast( lhs ) >= opCast( rhs ) ); } }; template struct Evaluator { static bool evaluate( T1 const& lhs, T2 const& rhs ) { - return opCast( lhs ) <= opCast( rhs ); + return bool( opCast( lhs ) <= opCast( rhs ) ); } }; @@ -2020,13 +2066,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 && !!(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 +2610,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 +3380,11 @@ namespace Catch { InLexicographicalOrder, InRandomOrder }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; class TestSpec; @@ -3350,7 +3404,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; }; } @@ -3404,7 +3458,7 @@ namespace Catch { }; class DebugOutStream : public IStream { - std::auto_ptr m_streamBuf; + CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; mutable std::ostream m_os; public: DebugOutStream(); @@ -3439,14 +3493,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 +3513,6 @@ namespace Catch { bool noThrow; bool showHelp; bool showInvisibles; - bool forceColour; bool filenamesAsTags; int abortAfter; @@ -3469,6 +3522,7 @@ namespace Catch { WarnAbout::What warnings; ShowDurations::OrNot showDurations; RunTests::InWhatOrder runOrder; + UseColour::YesOrNo useColour; std::string outputFilename; std::string name; @@ -3534,7 +3588,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: @@ -3552,7 +3606,7 @@ namespace Catch { } ConfigData m_data; - std::auto_ptr m_stream; + CATCH_AUTO_PTR( IStream const ) m_stream; TestSpec m_testSpec; }; @@ -3572,6 +3626,8 @@ namespace Catch { #define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { // #included from: ../external/clara.h +// Version 0.0.2.4 + // 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 +3652,7 @@ namespace Catch { #include #include #include +#include // Use optional outer namespace #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE @@ -3730,15 +3787,165 @@ 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_ 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 +#else +# define CLARA_AUTO_PTR( T ) std::auto_ptr +#endif + +#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// ----------- end of #include from clara_compilers.h ----------- +// ........... back in clara.h + #include -#include #include #include +#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CLARA_PLATFORM_WINDOWS +#endif + // Use optional outer namespace #ifdef STITCH_CLARA_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE @@ -3797,23 +4004,15 @@ namespace Clara { else throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); } - inline void convertInto( bool _source, bool& _dest ) { - _dest = _source; - } - template - inline void convertInto( bool, T& ) { - throw std::runtime_error( "Invalid conversion" ); - } template 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; virtual IArgFunction* clone() const = 0; }; @@ -3821,11 +4020,11 @@ namespace Clara { template class BoundArgFunction { public: - BoundArgFunction() : functionObj( CATCH_NULL ) {} + BoundArgFunction() : functionObj( CLARA_NULL ) {} BoundArgFunction( IArgFunction* _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* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL; + IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; delete functionObj; functionObj = newFunctionObj; return *this; @@ -3835,13 +4034,10 @@ namespace Clara { void set( ConfigT& config, std::string const& value ) const { functionObj->set( config, value ); } - void setFlag( ConfigT& config ) const { - functionObj->setFlag( config ); - } bool takesArg() const { return functionObj->takesArg(); } bool isSet() const { - return functionObj != CATCH_NULL; + return functionObj != CLARA_NULL; } private: IArgFunction* functionObj; @@ -3850,7 +4046,6 @@ namespace Clara { template struct NullBinder : IArgFunction{ virtual void set( C&, std::string const& ) const {} - virtual void setFlag( C& ) const {} virtual bool takesArg() const { return true; } virtual IArgFunction* clone() const { return new NullBinder( *this ); } }; @@ -3861,9 +4056,6 @@ namespace Clara { virtual void set( C& p, std::string const& stringValue ) const { convertInto( stringValue, p.*member ); } - virtual void setFlag( C& p ) const { - convertInto( true, p.*member ); - } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } M C::* member; @@ -3876,11 +4068,6 @@ namespace Clara { convertInto( stringValue, value ); (p.*member)( value ); } - virtual void setFlag( C& p ) const { - typename RemoveConstRef::type value; - convertInto( true, value ); - (p.*member)( value ); - } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } void (C::*member)( M ); @@ -3894,9 +4081,6 @@ namespace Clara { if( value ) (p.*member)(); } - virtual void setFlag( C& p ) const { - (p.*member)(); - } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } void (C::*member)(); @@ -3911,9 +4095,6 @@ namespace Clara { if( value ) function( obj ); } - virtual void setFlag( C& p ) const { - function( p ); - } virtual bool takesArg() const { return false; } virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } void (*function)( C& ); @@ -3927,11 +4108,6 @@ namespace Clara { convertInto( stringValue, value ); function( obj, value ); } - virtual void setFlag( C& obj ) const { - typename RemoveConstRef::type value; - convertInto( true, value ); - function( obj, value ); - } virtual bool takesArg() const { return !IsBool::value; } virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } void (*function)( C&, T ); @@ -3939,8 +4115,20 @@ namespace Clara { } // namespace Detail - struct Parser { - Parser() : separators( " \t=:" ) {} + inline std::vector argsToVector( int argc, char const* const* const argv ) { + std::vector args( static_cast( argc ) ); + for( std::size_t i = 0; i < static_cast( argc ); ++i ) + args[i] = argv[i]; + + return args; + } + + class Parser { + enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; + Mode mode; + std::size_t from; + bool inQuotes; + public: struct Token { enum Type { Positional, ShortOpt, LongOpt }; @@ -3949,38 +4137,75 @@ namespace Clara { std::string data; }; - void parseIntoTokens( int argc, char const * const * argv, std::vector& tokens ) const { + Parser() : mode( None ), from( 0 ), inQuotes( false ){} + + void parseIntoTokens( std::vector const& args, std::vector& tokens ) { const std::string doubleDash = "--"; - for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) - parseIntoTokens( argv[i] , tokens); - } - void parseIntoTokens( std::string arg, std::vector& tokens ) const { - while( !arg.empty() ) { - Parser::Token token( Parser::Token::Positional, arg ); - arg = ""; - if( token.data[0] == '-' ) { - if( token.data.size() > 1 && token.data[1] == '-' ) { - token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); - } - else { - token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); - if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { - arg = "-" + token.data.substr( 1 ); - token.data = token.data.substr( 0, 1 ); - } - } - } - if( token.type != Parser::Token::Positional ) { - std::size_t pos = token.data.find_first_of( separators ); - if( pos != std::string::npos ) { - arg = token.data.substr( pos+1 ); - token.data = token.data.substr( 0, pos ); - } - } - tokens.push_back( token ); + for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) + parseIntoTokens( args[i], tokens); + } + + void parseIntoTokens( std::string const& arg, std::vector& tokens ) { + for( std::size_t i = 0; i <= arg.size(); ++i ) { + char c = arg[i]; + if( c == '"' ) + inQuotes = !inQuotes; + mode = handleMode( i, c, arg, tokens ); + } + } + Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + switch( mode ) { + case None: return handleNone( i, c ); + case MaybeShortOpt: return handleMaybeShortOpt( i, c ); + case ShortOpt: + case LongOpt: + case SlashOpt: return handleOpt( i, c, arg, tokens ); + case Positional: return handlePositional( i, c, arg, tokens ); + default: throw std::logic_error( "Unknown mode" ); + } + } + + Mode handleNone( std::size_t i, char c ) { + if( inQuotes ) { + from = i; + return Positional; + } + switch( c ) { + case '-': return MaybeShortOpt; +#ifdef CLARA_PLATFORM_WINDOWS + case '/': from = i+1; return SlashOpt; +#endif + default: from = i; return Positional; + } + } + Mode handleMaybeShortOpt( std::size_t i, char c ) { + switch( c ) { + case '-': from = i+1; return LongOpt; + default: from = i; return ShortOpt; } } - std::string separators; + Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) + return mode; + + std::string optName = arg.substr( from, i-from ); + if( mode == ShortOpt ) + for( std::size_t j = 0; j < optName.size(); ++j ) + tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); + else if( mode == SlashOpt && optName.size() == 1 ) + tokens.push_back( Token( Token::ShortOpt, optName ) ); + else + tokens.push_back( Token( Token::LongOpt, optName ) ); + return None; + } + Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) + return mode; + + std::string data = arg.substr( from, i-from ); + tokens.push_back( Token( Token::Positional, data ) ); + return None; + } }; template @@ -4059,7 +4284,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 +4360,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 +4444,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,21 +4508,21 @@ namespace Clara { return oss.str(); } - ConfigT parse( int argc, char const * const * argv ) const { + ConfigT parse( std::vector const& args ) const { ConfigT config; - parseInto( argc, argv, config ); + parseInto( args, config ); return config; } - std::vector parseInto( int argc, char const * const * argv, ConfigT& config ) const { - std::string processName = argv[0]; + std::vector parseInto( std::vector const& args, ConfigT& config ) const { + std::string processName = args[0]; std::size_t lastSlash = processName.find_last_of( "/\\" ); if( lastSlash != std::string::npos ) processName = processName.substr( lastSlash+1 ); m_boundProcessName.set( config, processName ); std::vector tokens; Parser parser; - parser.parseIntoTokens( argc, argv, tokens ); + parser.parseIntoTokens( args, tokens ); return populate( tokens, config ); } @@ -4328,7 +4553,7 @@ namespace Clara { arg.boundField.set( config, tokens[++i].data ); } else { - arg.boundField.setFlag( config ); + arg.boundField.set( config, "true" ); } break; } @@ -4471,6 +4696,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 +4797,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 +4825,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; } @@ -5017,6 +5261,8 @@ namespace Catch bool aborting; }; + class MultipleReporters; + struct IStreamingReporter : IShared { virtual ~IStreamingReporter(); @@ -5044,6 +5290,8 @@ namespace Catch virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } }; struct IReporterFactory : IShared { @@ -5261,6 +5509,10 @@ namespace TestCaseTracking { virtual void addChild( Ptr const& child ) = 0; virtual ITracker* findChild( std::string const& name ) = 0; virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isIndexTracker() const = 0; }; class TrackerContext { @@ -5385,6 +5637,10 @@ namespace TestCaseTracking { m_parent->openChild(); } } + + virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } + virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } + void open() { m_runState = Executing; moveToThis(); @@ -5448,13 +5704,16 @@ namespace TestCaseTracking { {} virtual ~SectionTracker(); + virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } + static SectionTracker& acquire( TrackerContext& ctx, std::string const& name ) { SectionTracker* section = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( name ) ) { - section = dynamic_cast( childTracker ); - assert( section ); + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = static_cast( childTracker ); } else { section = new SectionTracker( name, ctx, ¤tTracker ); @@ -5479,13 +5738,16 @@ namespace TestCaseTracking { {} virtual ~IndexTracker(); + virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } + static IndexTracker& acquire( TrackerContext& ctx, std::string const& name, int size ) { IndexTracker* tracker = CATCH_NULL; ITracker& currentTracker = ctx.currentTracker(); if( ITracker* childTracker = currentTracker.findChild( name ) ) { - tracker = dynamic_cast( childTracker ); - assert( tracker ); + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = static_cast( childTracker ); } else { tracker = new IndexTracker( name, ctx, ¤tTracker, size ); @@ -5692,6 +5954,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,10 +6350,10 @@ 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* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { try { m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); - m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); + m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); if( m_configData.showHelp ) showHelp( m_configData.processName ); m_config.reset(); @@ -6110,7 +6377,7 @@ namespace Catch { m_config.reset(); } - int run( int argc, char const* const argv[] ) { + int run( int argc, char const* const* const argv ) { int returnCode = applyCommandLine( argc, argv ); if( returnCode == 0 ) @@ -6180,13 +6447,31 @@ namespace Catch { #include #include +#ifdef CATCH_CPP14_OR_GREATER +#include +#endif + namespace Catch { - struct LexSort { - bool operator() (TestCase i,TestCase j) const { return (i + static void shuffle( V& vector ) { + RandomNumberGenerator rng; +#ifdef CATCH_CPP14_OR_GREATER + std::shuffle( vector.begin(), vector.end(), rng ); +#else + std::random_shuffle( vector.begin(), vector.end(), rng ); +#endif + } }; inline std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { @@ -6195,14 +6480,12 @@ namespace Catch { switch( config.runOrder() ) { case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end(), LexSort() ); + std::sort( sorted.begin(), sorted.end() ); break; case RunTests::InRandomOrder: { seedRng( config ); - - RandomNumberGenerator rng; - std::random_shuffle( sorted.begin(), sorted.end(), rng ); + RandomNumberGenerator::shuffle( sorted ); } break; case RunTests::InDeclarationOrder: @@ -6221,13 +6504,15 @@ namespace Catch { it != itEnd; ++it ) { std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); - if( !prev.second ){ - Catch::cerr() - << Colour( Colour::Red ) - << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" - << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; - exit(1); + if( !prev.second ) { + std::ostringstream ss; + + ss << Colour( Colour::Red ) + << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; + + throw std::runtime_error(ss.str()); } } } @@ -6815,7 +7100,18 @@ namespace { IColourImpl* platformColourInstance() { static Win32ColourImpl s_instance; - return &s_instance; + + Ptr 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 +7162,14 @@ namespace { IColourImpl* platformColourInstance() { Ptr 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 +7194,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 +7571,7 @@ namespace Catch { return os; } - Version libraryVersion( 1, 3, 3, "", 0 ); + Version libraryVersion( 1, 5, 6, "", 0 ); } @@ -8249,13 +8550,18 @@ class MultipleReporters : public SharedImpl { ++it ) (*it)->skipTest( testInfo ); } + + virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { + return this; + } + }; Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { Ptr resultingReporter; if( existingReporter ) { - MultipleReporters* multi = dynamic_cast( existingReporter.get() ); + MultipleReporters* multi = existingReporter->tryAsMulti(); if( !multi ) { multi = new MultipleReporters; resultingReporter = Ptr( multi ); @@ -8435,7 +8741,7 @@ namespace Catch { virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} - virtual bool assertionEnded( AssertionStats const& assertionStats ) { + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { assert( !m_sectionStack.empty() ); SectionNode& sectionNode = *m_sectionStack.back(); sectionNode.assertions.push_back( assertionStats ); @@ -9566,7 +9872,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/third_party/libosmium/test/t/area/test_node_ref_segment.cpp b/third_party/libosmium/test/t/area/test_node_ref_segment.cpp index 3261c24e143..5e33c2baef3 100644 --- a/third_party/libosmium/test/t/area/test_node_ref_segment.cpp +++ b/third_party/libosmium/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/third_party/libosmium/test/t/basic/test_area.cpp b/third_party/libosmium/test/t/basic/test_area.cpp new file mode 100644 index 00000000000..f2847dcc166 --- /dev/null +++ b/third_party/libosmium/test/t/basic/test_area.cpp @@ -0,0 +1,78 @@ +#include "catch.hpp" + +#include + +#include +#include +#include + +using namespace osmium::builder::attr; + +TEST_CASE("Build area") { + osmium::memory::Buffer buffer(10000); + + osmium::builder::add_area(buffer, + _id(17), + _version(3), + _visible(), + _cid(333), + _uid(21), + _timestamp(time_t(123)), + _user("foo"), + _tag("landuse", "forest"), + _tag("name", "Sherwood Forest"), + _outer_ring({ + {1, {3.2, 4.2}}, + {2, {3.5, 4.7}}, + {3, {3.6, 4.9}}, + {1, {3.2, 4.2}} + }), + _inner_ring({ + {5, {1.0, 1.0}}, + {6, {8.0, 1.0}}, + {7, {8.0, 8.0}}, + {8, {1.0, 8.0}}, + {5, {1.0, 1.0}} + }) + ); + + const osmium::Area& area = buffer.get(0); + + REQUIRE(17 == area.id()); + REQUIRE(3 == area.version()); + REQUIRE(true == area.visible()); + REQUIRE(333 == area.changeset()); + REQUIRE(21 == area.uid()); + REQUIRE(std::string("foo") == area.user()); + REQUIRE(123 == uint32_t(area.timestamp())); + REQUIRE(2 == area.tags().size()); + + int inner = 0; + int outer = 0; + for (const auto& subitem : area) { + switch (subitem.type()) { + case osmium::item_type::outer_ring: { + const auto& ring = static_cast(subitem); + REQUIRE(ring.size() == 4); + ++outer; + } + break; + case osmium::item_type::inner_ring: { + const auto& ring = static_cast(subitem); + REQUIRE(ring.size() == 5); + ++inner; + } + break; + default: + break; + } + } + + REQUIRE(outer == 1); + REQUIRE(inner == 1); + + osmium::CRC crc32; + crc32.update(area); + REQUIRE(crc32().checksum() == 0x2b2b7fa0); +} + diff --git a/third_party/libosmium/test/t/basic/test_entity_bits.cpp b/third_party/libosmium/test/t/basic/test_entity_bits.cpp index f15068b1c27..13de94b5fae 100644 --- a/third_party/libosmium/test/t/basic/test_entity_bits.cpp +++ b/third_party/libosmium/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/third_party/libosmium/test/t/basic/test_location.cpp b/third_party/libosmium/test/t/basic/test_location.cpp index 3fd8d155a51..dc5b37872e8 100644 --- a/third_party/libosmium/test/t/basic/test_location.cpp +++ b/third_party/libosmium/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,221 @@ TEST_CASE("Location") { } +TEST_CASE("Location hash") { + if (sizeof(size_t) == 8) { + REQUIRE(std::hash{}({0, 0}) == 0); + REQUIRE(std::hash{}({0, 1}) == 1); + REQUIRE(std::hash{}({1, 0}) == 0x100000000); + REQUIRE(std::hash{}({1, 1}) == 0x100000001); + } else { + REQUIRE(std::hash{}({0, 0}) == 0); + REQUIRE(std::hash{}({0, 1}) == 1); + REQUIRE(std::hash{}({1, 0}) == 1); + REQUIRE(std::hash{}({1, 1}) == 0); + } +} + +#define CR(s, v, r) { \ + const char* strm = "-" s; \ + const char* strp = strm + 1; \ + REQUIRE(std::atof(strp) == Approx( v / 10000000.0)); \ + REQUIRE(std::atof(strm) == Approx(-v / 10000000.0)); \ + const char** data = &strp; \ + REQUIRE(osmium::detail::string_to_location_coordinate(data) == v); \ + REQUIRE(std::string{*data} == r); \ + data = &strm; \ + REQUIRE(osmium::detail::string_to_location_coordinate(data) == -v); \ + REQUIRE(std::string{*data} == r); \ + } + +#define C(s, v) CR(s, v, "") + +#define F(s) { \ + const char* strm = "-" s; \ + const char* strp = strm + 1; \ + const char** data = &strp; \ + REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); \ + data = &strm; \ + REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(data), osmium::invalid_location); \ + } + +TEST_CASE("Parsing coordinates from strings") { + F("x"); + F("."); + F("--"); + F(""); + F(" "); + F(" 123"); + + CR("123 ", 1230000000, " "); + CR("123x", 1230000000, "x"); + CR("1.2x", 12000000, "x"); + + C("0", 0); + + C("1", 10000000); + C("2", 20000000); + + C("9", 90000000); + C("10", 100000000); + C("11", 110000000); + + C("90", 900000000); + C("100", 1000000000); + C("101", 1010000000); + + C("00", 0); + C("01", 10000000); + C("001", 10000000); + C("0001", 10000000); + + F("1234"); + F("1234."); + F("12345678901234567890"); + + C("0.", 0); + C("0.0", 0); + C("1.", 10000000); + C("1.0", 10000000); + C("1.2", 12000000); + C("0.1", 1000000); + C("0.01", 100000); + C("0.001", 10000); + C("0.0001", 1000); + C("0.00001", 100); + C("0.000001", 10); + C("0.0000001", 1); + + C("1.1234567", 11234567); + C("1.12345670", 11234567); + C("1.12345674", 11234567); + C("1.123456751", 11234568); + C("1.12345679", 11234568); + C("1.12345680", 11234568); + C("1.12345681", 11234568); + + C("180.0000000", 1800000000); + C("180.0000001", 1800000001); + C("179.9999999", 1799999999); + C("179.99999999", 1800000000); + C("200.123", 2001230000); + + C("1e2", 1000000000); + C("1e1", 100000000); + C("1e0", 10000000); + C("1e-1", 1000000); + C("1e-2", 100000); + C("1e-3", 10000); + C("1e-4", 1000); + C("1e-5", 100); + C("1e-6", 10); + C("1e-7", 1); + + C("1.0e2", 1000000000); + C("1.1e1", 110000000); + C("0.1e1", 10000000); + C("1.2e0", 12000000); + C("1.9e-1", 1900000); + C("2.0e-2", 200000); + C("2.1e-3", 21000); + C("9.0e-4", 9000); + C("9.1e-5", 910); + C("1.0e-6", 10); + C("1.0e-7", 1); + C("1.4e-7", 1); + C("1.5e-7", 2); + C("1.9e-7", 2); + C("0.5e-7", 1); + C("0.1e-7", 0); + C("0.0e-7", 0); + C("1.9e-8", 0); + C("1.9e-9", 0); + C("1.9e-10", 0); + + F("e"); + F(" e"); + F(" 1.1e2"); + F("1.0e3"); + F("5e4"); + F("5.0e2"); + F("3e2"); + F("1e"); + F("0.5e"); + F("1e10"); + + CR("1e2 ", 1000000000, " "); + CR("1.1e2 ", 1100000000, " "); + CR("1.1e2x", 1100000000, "x"); + CR("1.1e2:", 1100000000, ":"); +} + +#undef C +#undef CR +#undef F + +#define CW(v, s) buffer.clear(); \ + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), v); \ + CHECK(buffer == s); \ + buffer.clear(); \ + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), -v); \ + CHECK(buffer == "-" s); + +TEST_CASE("Writing coordinates into string") { + std::string buffer; + + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), 0); + CHECK(buffer == "0"); + + CW( 10000000, "1"); + CW( 90000000, "9"); + CW( 100000000, "10"); + CW(1000000000, "100"); + CW(2000000000, "200"); + + CW( 1000000, "0.1"); + CW( 100000, "0.01"); + CW( 10000, "0.001"); + CW( 1000, "0.0001"); + CW( 100, "0.00001"); + CW( 10, "0.000001"); + CW( 1, "0.0000001"); + + CW( 1230000, "0.123"); + CW( 9999999, "0.9999999"); + CW( 40101010, "4.010101"); + CW( 494561234, "49.4561234"); + CW(1799999999, "179.9999999"); +} + +#undef CW + +TEST_CASE("set lon/lat from string") { + osmium::Location loc; + loc.set_lon("1.2"); + loc.set_lat("3.4"); + REQUIRE(loc.lon() == Approx(1.2)); + REQUIRE(loc.lat() == Approx(3.4)); +} + +TEST_CASE("set lon/lat from string with trailing characters") { + osmium::Location loc; + REQUIRE_THROWS_AS({ + loc.set_lon("1.2x"); + }, osmium::invalid_location); + REQUIRE_THROWS_AS({ + loc.set_lat("3.4e1 "); + }, osmium::invalid_location); +} + +TEST_CASE("set lon/lat from string with trailing characters using partial") { + osmium::Location loc; + const char* x = "1.2x"; + const char* y = "3.4 "; + loc.set_lon_partial(&x); + loc.set_lat_partial(&y); + REQUIRE(loc.lon() == Approx(1.2)); + REQUIRE(loc.lat() == Approx(3.4)); + REQUIRE(*x == 'x'); + REQUIRE(*y == ' '); +} + diff --git a/third_party/libosmium/test/t/basic/test_node.cpp b/third_party/libosmium/test/t/basic/test_node.cpp index b7f4c6cce4f..e5dbe8a7ca2 100644 --- a/third_party/libosmium/test/t/basic/test_node.cpp +++ b/third_party/libosmium/test/t/basic/test_node.cpp @@ -9,7 +9,7 @@ using namespace osmium::builder::attr; TEST_CASE("Build node") { - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; osmium::builder::add_node(buffer, _id(17), @@ -36,7 +36,7 @@ TEST_CASE("Build node") { REQUIRE(false == node.deleted()); REQUIRE(333 == node.changeset()); REQUIRE(21 == node.uid()); - REQUIRE(std::string("foo") == node.user()); + REQUIRE(std::string{"foo"} == node.user()); REQUIRE(123 == uint32_t(node.timestamp())); REQUIRE(osmium::Location(3.5, 4.7) == node.location()); REQUIRE(2 == node.tags().size()); @@ -51,7 +51,7 @@ TEST_CASE("Build node") { } TEST_CASE("default values for node attributes") { - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; osmium::builder::add_node(buffer, _id(0)); @@ -62,22 +62,23 @@ TEST_CASE("default values for node attributes") { REQUIRE(true == node.visible()); REQUIRE(0 == node.changeset()); REQUIRE(0 == node.uid()); - REQUIRE(std::string("") == node.user()); + REQUIRE(std::string{} == node.user()); REQUIRE(0 == uint32_t(node.timestamp())); REQUIRE(osmium::Location() == node.location()); REQUIRE(0 == node.tags().size()); } TEST_CASE("set node attributes from strings") { - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; osmium::builder::add_node(buffer, _id(0)); osmium::Node& node = buffer.get(0); node.set_id("-17") .set_version("3") - .set_visible(true) + .set_visible("true") .set_changeset("333") + .set_timestamp("2014-03-17T16:23:08Z") .set_uid("21"); REQUIRE(-17l == node.id()); @@ -85,11 +86,52 @@ TEST_CASE("set node attributes from strings") { REQUIRE(3 == node.version()); REQUIRE(true == node.visible()); REQUIRE(333 == node.changeset()); + REQUIRE(std::string{"2014-03-17T16:23:08Z"} == node.timestamp().to_iso()); REQUIRE(21 == node.uid()); } +TEST_CASE("set node attributes from strings using set_attribute()") { + osmium::memory::Buffer buffer{10000}; + + osmium::builder::add_node(buffer, _id(0)); + + osmium::Node& node = buffer.get(0); + node.set_attribute("id", "-17") + .set_attribute("version", "3") + .set_attribute("visible", "true") + .set_attribute("changeset", "333") + .set_attribute("timestamp", "2014-03-17T16:23:08Z") + .set_attribute("uid", "21"); + + REQUIRE(-17l == node.id()); + REQUIRE(17ul == node.positive_id()); + REQUIRE(3 == node.version()); + REQUIRE(true == node.visible()); + REQUIRE(333 == node.changeset()); + REQUIRE(std::string{"2014-03-17T16:23:08Z"} == node.timestamp().to_iso()); + REQUIRE(21 == node.uid()); +} + +TEST_CASE("Setting attributes from bad data on strings should fail") { + osmium::memory::Buffer buffer{10000}; + + osmium::builder::add_node(buffer, _id(0)); + + osmium::Node& node = buffer.get(0); + REQUIRE_THROWS(node.set_id("bar")); + REQUIRE_THROWS(node.set_id("123x")); + REQUIRE_THROWS(node.set_version("123x")); + REQUIRE_THROWS(node.set_visible("foo")); + REQUIRE_THROWS(node.set_changeset("123x")); + REQUIRE_THROWS(node.set_changeset("NULL")); + REQUIRE_THROWS(node.set_timestamp("2014-03-17T16:23:08Zx")); + REQUIRE_THROWS(node.set_timestamp("2014-03-17T16:23:99Z")); + REQUIRE_THROWS(node.set_uid("123x")); + REQUIRE_THROWS(node.set_uid("anonymous")); +} + TEST_CASE("set large id") { - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; int64_t id = 3000000000l; osmium::builder::add_node(buffer, _id(id)); @@ -104,7 +146,7 @@ TEST_CASE("set large id") { } TEST_CASE("set tags on node") { - osmium::memory::Buffer buffer(10000); + osmium::memory::Buffer buffer{10000}; osmium::builder::add_node(buffer, _user("foo"), @@ -114,11 +156,11 @@ TEST_CASE("set tags on node") { const osmium::Node& node = buffer.get(0); REQUIRE(nullptr == node.tags().get_value_by_key("fail")); - REQUIRE(std::string("pub") == node.tags().get_value_by_key("amenity")); - REQUIRE(std::string("pub") == node.get_value_by_key("amenity")); + REQUIRE(std::string{"pub"} == node.tags().get_value_by_key("amenity")); + REQUIRE(std::string{"pub"} == node.get_value_by_key("amenity")); - REQUIRE(std::string("default") == node.tags().get_value_by_key("fail", "default")); - REQUIRE(std::string("pub") == node.tags().get_value_by_key("amenity", "default")); - REQUIRE(std::string("pub") == node.get_value_by_key("amenity", "default")); + REQUIRE(std::string{"default"} == node.tags().get_value_by_key("fail", "default")); + REQUIRE(std::string{"pub"} == node.tags().get_value_by_key("amenity", "default")); + REQUIRE(std::string{"pub"} == node.get_value_by_key("amenity", "default")); } diff --git a/third_party/libosmium/test/t/basic/test_object_comparisons.cpp b/third_party/libosmium/test/t/basic/test_object_comparisons.cpp index 461969fe917..42bcbff6364 100644 --- a/third_party/libosmium/test/t/basic/test_object_comparisons.cpp +++ b/third_party/libosmium/test/t/basic/test_object_comparisons.cpp @@ -1,76 +1,80 @@ #include "catch.hpp" +#include +#include +#include + #include #include #include #include -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(*it); - osmium::Node& node2 = static_cast(*(++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> nodes; + + SECTION("nodes are ordered by id, version, and timestamp") { + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 0), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 1), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10), _version(2), _timestamp("2016-01-01T00:01:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10), _version(3), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:01:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 15), _version(1), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(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(*it); - const osmium::Node& node2 = static_cast(*(++it)); - const osmium::Node& node3 = static_cast(*(++it)); - const osmium::Way& way = static_cast(*(++it)); - const osmium::Relation& relation = static_cast(*(++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::builder::add_node(buffer, _id(1), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(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::builder::add_node(buffer, _id( 0)))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 1)))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( -1)))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10)))); + nodes.emplace_back(buffer.get(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::builder::add_node(buffer, _id( 0), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 1), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10), _version(3), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 10), _version(2), _timestamp("2016-01-01T00:01:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:01:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 12), _version(2), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(osmium::builder::add_node(buffer, _id( 15), _version(1), _timestamp("2016-01-01T00:00:00Z")))); + nodes.emplace_back(buffer.get(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> objects; + + SECTION("types are ordered nodes, then ways, then relations") { + objects.emplace_back(buffer.get( osmium::builder::add_node( buffer, _id(3)))); + objects.emplace_back(buffer.get( osmium::builder::add_way( buffer, _id(2)))); + objects.emplace_back(buffer.get(osmium::builder::add_relation(buffer, _id(1)))); + + REQUIRE(std::is_sorted(objects.cbegin(), objects.cend())); + } + +} + diff --git a/third_party/libosmium/test/t/basic/test_timestamp.cpp b/third_party/libosmium/test/t/basic/test_timestamp.cpp index f80ffcf5f29..561a705b55e 100644 --- a/third_party/libosmium/test/t/basic/test_timestamp.cpp +++ b/third_party/libosmium/test/t/basic/test_timestamp.cpp @@ -75,3 +75,47 @@ TEST_CASE("Timestamp") { } } + +TEST_CASE("Valid timestamps") { + + std::vector test_cases = { + "1970-01-01T00:00:01Z", + "2000-01-01T00:00:00Z", + "2006-12-31T23:59:59Z", + "2030-12-31T23:59:59Z", + "2016-02-28T23:59:59Z", + "2016-03-31T23:59:59Z" + }; + + for (const auto& tc : test_cases) { + osmium::Timestamp t{tc}; + REQUIRE(tc == t.to_iso()); + } + +} + +TEST_CASE("Invalid timestamps") { + REQUIRE_THROWS_AS(osmium::Timestamp{""}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"x"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"xxxxxxxxxxxxxxxxxxxx"}, std::invalid_argument); + + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01x00:00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:00:00x"}, std::invalid_argument); + + REQUIRE_THROWS_AS(osmium::Timestamp{"2000x01-01T00:00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01x01T00:00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00x00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:00x00Z"}, std::invalid_argument); + + REQUIRE_THROWS_AS(osmium::Timestamp{"0000-00-01T00:00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-00-01T00:00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-00T00:00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T24:00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:60:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-01T00:00:61Z"}, std::invalid_argument); + + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-01-32T00:00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-02-30T00:00:00Z"}, std::invalid_argument); + REQUIRE_THROWS_AS(osmium::Timestamp{"2000-03-32T00:00:00Z"}, std::invalid_argument); +} + diff --git a/third_party/libosmium/test/t/basic/test_types_from_string.cpp b/third_party/libosmium/test/t/basic/test_types_from_string.cpp index 2481ae87649..2866b8f74d5 100644 --- a/third_party/libosmium/test/t/basic/test_types_from_string.cpp +++ b/third_party/libosmium/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/third_party/libosmium/test/t/basic/test_way.cpp b/third_party/libosmium/test/t/basic/test_way.cpp index c4fa9d54257..005ef300061 100644 --- a/third_party/libosmium/test/t/basic/test_way.cpp +++ b/third_party/libosmium/test/t/basic/test_way.cpp @@ -46,7 +46,7 @@ TEST_CASE("Build way") { osmium::CRC crc32; crc32.update(way); - REQUIRE(crc32().checksum() == 0x7676d0c2); + REQUIRE(crc32().checksum() == 0x65f6ba91); } TEST_CASE("build closed way") { diff --git a/third_party/libosmium/test/t/builder/test_attr.cpp b/third_party/libosmium/test/t/builder/test_attr.cpp index 258ae7a911b..dedde77a7d3 100644 --- a/third_party/libosmium/test/t/builder/test_attr.cpp +++ b/third_party/libosmium/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(); + auto it = buffer.select().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()); + REQUIRE(it == buffer.select().cend()); } SECTION("order of attributes doesn't matter") { diff --git a/third_party/libosmium/test/t/geom/test_geojson.cpp b/third_party/libosmium/test/t/geom/test_geojson.cpp index a9e839822c8..725be7fb940 100644 --- a/third_party/libosmium/test/t/geom/test_geojson.cpp +++ b/third_party/libosmium/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(), area.cend()) == area.num_rings().first); + REQUIRE(area.subitems().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(), area.cend()) == area.num_rings().first); - REQUIRE(std::distance(area.cbegin(), area.cend()) == area.num_rings().second); + REQUIRE(area.subitems().size() == area.num_rings().first); + REQUIRE(area.subitems().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(), area.cend()) == area.num_rings().first); - REQUIRE(std::distance(area.cbegin(), area.cend()) == area.num_rings().second); + REQUIRE(area.subitems().size() == area.num_rings().first); + REQUIRE(area.subitems().size() == area.num_rings().second); int outer_ring=0; int inner_ring=0; - for (auto it_outer = area.cbegin(); it_outer != area.cend(); ++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/third_party/libosmium/test/t/geom/test_geos.cpp b/third_party/libosmium/test/t/geom/test_geos.cpp index 8bf11c93e92..f74027c24b0 100644 --- a/third_party/libosmium/test/t/geom/test_geos.cpp +++ b/third_party/libosmium/test/t/geom/test_geos.cpp @@ -1,6 +1,7 @@ #include "catch.hpp" #include +#include #include "area_helper.hpp" #include "wnl_helper.hpp" @@ -11,16 +12,16 @@ TEST_CASE("GEOS geometry factory - create point") { std::unique_ptr point {factory.create_point(osmium::Location(3.2, 4.2))}; REQUIRE(3.2 == point->getX()); REQUIRE(4.2 == point->getY()); - REQUIRE(-1 == point->getSRID()); + REQUIRE(4326 == point->getSRID()); } -TEST_CASE("GEOS geometry factory - create point with non-default srid") { - osmium::geom::GEOSFactory<> factory(4326); +TEST_CASE("GEOS geometry factory - create point in web mercator") { + osmium::geom::GEOSFactory factory; std::unique_ptr point {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(3.2 == point->getX()); - REQUIRE(4.2 == point->getY()); - REQUIRE(4326 == point->getSRID()); + REQUIRE(Approx(356222.3705384755l) == point->getX()); + REQUIRE(Approx(467961.143605213l) == point->getY()); + REQUIRE(3857 == point->getSRID()); } TEST_CASE("GEOS geometry factory - create point with externally created GEOS factory") { @@ -89,6 +90,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(mp->getGeometryN(0)); + REQUIRE(p0); REQUIRE(0 == p0->getNumInteriorRing()); const geos::geom::LineString* l0e = p0->getExteriorRing(); @@ -108,6 +110,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(mp->getGeometryN(0)); + REQUIRE(p0); REQUIRE(1 == p0->getNumInteriorRing()); const geos::geom::LineString* l0e = p0->getExteriorRing(); @@ -127,12 +130,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(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(mp->getGeometryN(1)); + REQUIRE(p1); REQUIRE(0 == p1->getNumInteriorRing()); const geos::geom::LineString* l1e = p1->getExteriorRing(); diff --git a/third_party/libosmium/test/t/geom/test_ogr.cpp b/third_party/libosmium/test/t/geom/test_ogr.cpp index 26d34c13abc..3490c5759d9 100644 --- a/third_party/libosmium/test/t/geom/test_ogr.cpp +++ b/third_party/libosmium/test/t/geom/test_ogr.cpp @@ -68,6 +68,7 @@ SECTION("area_1outer_0inner") { REQUIRE(1 == mp->getNumGeometries()); const OGRPolygon* p0 = dynamic_cast(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(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(mp->getGeometryRef(0)); + REQUIRE(p0); REQUIRE(2 == p0->getNumInteriorRings()); const OGRLineString* l0e = p0->getExteriorRing(); REQUIRE(5 == l0e->getNumPoints()); const OGRPolygon* p1 = dynamic_cast(mp->getGeometryRef(1)); + REQUIRE(p1); REQUIRE(0 == p1->getNumInteriorRings()); const OGRLineString* l1e = p1->getExteriorRing(); diff --git a/third_party/libosmium/test/t/geom/test_tile.cpp b/third_party/libosmium/test/t/geom/test_tile.cpp index e80cb960410..5454fed7909 100644 --- a/third_party/libosmium/test/t/geom/test_tile.cpp +++ b/third_party/libosmium/test/t/geom/test_tile.cpp @@ -8,86 +8,96 @@ #include "test_tile_data.hpp" -TEST_CASE("Tile") { +TEST_CASE("Tile from x0.0 y0.0 at zoom 0") { + osmium::Location l{0.0, 0.0}; - SECTION("x0.0 y0.0 zoom 0") { - osmium::Location l(0.0, 0.0); + osmium::geom::Tile t{0, l}; - osmium::geom::Tile t(0, l); + REQUIRE(t.x == 0); + REQUIRE(t.y == 0); + REQUIRE(t.z == 0); + REQUIRE(t.valid()); +} - REQUIRE(t.x == 0); - REQUIRE(t.y == 0); - REQUIRE(t.z == 0); - } +TEST_CASE("Tile from x180.0 y90.0 at zoom 0") { + osmium::Location l{180.0, 90.0}; - SECTION("x180.0 y90.0 zoom 0") { - osmium::Location l(180.0, 90.0); + osmium::geom::Tile t{0, l}; - osmium::geom::Tile t(0, l); + REQUIRE(t.x == 0); + REQUIRE(t.y == 0); + REQUIRE(t.z == 0); + REQUIRE(t.valid()); +} - REQUIRE(t.x == 0); - REQUIRE(t.y == 0); - REQUIRE(t.z == 0); - } +TEST_CASE("Tile from x180.0 y90.0 at zoom 4") { + osmium::Location l{180.0, 90.0}; - SECTION("x180.0 y90.0 zoom 4") { - osmium::Location l(180.0, 90.0); + osmium::geom::Tile t{4, l}; - osmium::geom::Tile t(4, l); + REQUIRE(t.x == (1 << 4) - 1); + REQUIRE(t.y == 0); + REQUIRE(t.z == 4); + REQUIRE(t.valid()); +} - REQUIRE(t.x == (1 << 4) - 1); - REQUIRE(t.y == 0); - REQUIRE(t.z == 4); - } +TEST_CASE("Tile from x0.0 y0.0 at zoom 4") { + osmium::Location l{0.0, 0.0}; - SECTION("x0.0 y0.0 zoom 4") { - osmium::Location l(0.0, 0.0); + osmium::geom::Tile t{4, l}; - osmium::geom::Tile t(4, l); + auto n = 1 << (4-1); + REQUIRE(t.x == n); + REQUIRE(t.y == n); + REQUIRE(t.z == 4); + REQUIRE(t.valid()); +} - auto n = 1 << (4-1); - REQUIRE(t.x == n); - REQUIRE(t.y == n); - REQUIRE(t.z == 4); - } +TEST_CASE("Tile from max values at zoom 4") { + osmium::geom::Tile t{4u, 15u, 15u}; + REQUIRE(t.valid()); +} - SECTION("equality") { - osmium::geom::Tile a(4, 3, 4); - osmium::geom::Tile b(4, 3, 4); - osmium::geom::Tile c(4, 4, 3); - REQUIRE(a == b); - REQUIRE(a != c); - REQUIRE(b != c); - } +TEST_CASE("Tile from max values at zoom 30") { + osmium::geom::Tile t{30u, (1u<<30) - 1, (1u<<30) - 1}; + REQUIRE(t.valid()); +} - SECTION("order") { - osmium::geom::Tile a(2, 3, 4); - osmium::geom::Tile b(4, 3, 4); - osmium::geom::Tile c(4, 4, 3); - osmium::geom::Tile d(4, 4, 2); - REQUIRE(a < b); - REQUIRE(a < c); - REQUIRE(b < c); - REQUIRE(d < c); - } +TEST_CASE("Tile equality") { + osmium::geom::Tile a{4, 3, 4}; + osmium::geom::Tile b{4, 3, 4}; + osmium::geom::Tile c{4, 4, 3}; + REQUIRE(a == b); + REQUIRE(a != c); + REQUIRE(b != c); +} - SECTION("tilelist") { - std::istringstream input_data(s); - while (input_data) { - double lon, lat; - uint32_t x, y, zoom; - input_data >> lon; - input_data >> lat; - input_data >> x; - input_data >> y; - input_data >> zoom; - - osmium::Location l(lon, lat); - osmium::geom::Tile t(zoom, l); - REQUIRE(t.x == x); - REQUIRE(t.y == y); - } - } +TEST_CASE("Tile order") { + osmium::geom::Tile a{4, 3, 4}; + osmium::geom::Tile b{6, 3, 4}; + osmium::geom::Tile c{6, 4, 3}; + osmium::geom::Tile d{6, 4, 2}; + REQUIRE(a < b); + REQUIRE(a < c); + REQUIRE(b < c); + REQUIRE(d < c); +} +TEST_CASE("Check a random list of tiles") { + std::istringstream input_data(s); + while (input_data) { + double lon, lat; + uint32_t x, y, zoom; + input_data >> lon; + input_data >> lat; + input_data >> x; + input_data >> y; + input_data >> zoom; + + osmium::Location l{lon, lat}; + osmium::geom::Tile t{zoom, l}; + REQUIRE(t.x == x); + REQUIRE(t.y == y); + } } diff --git a/third_party/libosmium/test/t/geom/test_wkb.cpp b/third_party/libosmium/test/t/geom/test_wkb.cpp index 49710cbc2f5..d4d9228689f 100644 --- a/third_party/libosmium/test/t/geom/test_wkb.cpp +++ b/third_party/libosmium/test/t/geom/test_wkb.cpp @@ -1,115 +1,135 @@ #include "catch.hpp" +#include #include #include "wnl_helper.hpp" #if __BYTE_ORDER == __LITTLE_ENDIAN -TEST_CASE("WKB_Geometry_byte_order_dependent") { +TEST_CASE("WKB geometry factory (byte-order-dependant), points") { + const osmium::Location loc{3.2, 4.2}; -SECTION("point") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - - std::string wkb {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(std::string{"01010000009A99999999990940CDCCCCCCCCCC1040"} == wkb); -} + SECTION("point") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); -SECTION("point_ewkb") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); + const std::string wkb{factory.create_point(loc)}; + REQUIRE(wkb == "01010000009A99999999990940CDCCCCCCCCCC1040"); + } - std::string wkb {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(std::string{"0101000020E61000009A99999999990940CDCCCCCCCCCC1040"} == wkb); -} + SECTION("point in web mercator") { + osmium::geom::WKBFactory factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); -SECTION("linestring") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + const std::string wkb{factory.create_point(loc)}; + REQUIRE(wkb == "010100000028706E7BF9BD1541B03E0D93E48F1C41"); + } - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_okay(buffer); + SECTION("point in ewkb") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); - { - std::string wkb {factory.create_linestring(wnl)}; - REQUIRE(std::string{"0102000000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"} == wkb); + const std::string wkb{factory.create_point(loc)}; + REQUIRE(wkb == "0101000020E61000009A99999999990940CDCCCCCCCCCC1040"); } - { - std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; - REQUIRE(std::string{"010200000003000000CDCCCCCCCCCC0C409A999999999913400000000000000C40CDCCCCCCCCCC12409A99999999990940CDCCCCCCCCCC1040"} == wkb); - } + SECTION("point in ewkb in web mercator") { + osmium::geom::WKBFactory factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); - { - std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; - REQUIRE(std::string{"0102000000040000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"} == wkb); + const std::string wkb{factory.create_point(loc)}; + REQUIRE(wkb == "0101000020110F000028706E7BF9BD1541B03E0D93E48F1C41"); } - { - std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; - REQUIRE(std::string{"010200000004000000CDCCCCCCCCCC0C409A999999999913400000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC12409A99999999990940CDCCCCCCCCCC1040"} == wkb); - } } -SECTION("linestring_ewkb") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); +TEST_CASE("WKB geometry factory (byte-order-dependant)") { - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_okay(buffer); + osmium::memory::Buffer buffer{10000}; - std::string ewkb {factory.create_linestring(wnl)}; - REQUIRE(std::string{"0102000020E6100000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"} == ewkb); -} + SECTION("linestring") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + const auto& wnl = create_test_wnl_okay(buffer); -SECTION("linestring_with_two_same_locations") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + { + const std::string wkb{factory.create_linestring(wnl)}; + REQUIRE(wkb == "0102000000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"); + } - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_same_location(buffer); + { + const std::string wkb{factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; + REQUIRE(wkb == "010200000003000000CDCCCCCCCCCC0C409A999999999913400000000000000C40CDCCCCCCCCCC12409A99999999990940CDCCCCCCCCCC1040"); + } - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + { + const std::string wkb{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + REQUIRE(wkb == "0102000000040000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"); + } - { - std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; - REQUIRE(std::string{"0102000000020000000000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240"} == wkb); + { + const std::string wkb{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + REQUIRE(wkb == "010200000004000000CDCCCCCCCCCC0C409A999999999913400000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC12409A99999999990940CDCCCCCCCCCC1040"); + } } - { - std::string wkb {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; - REQUIRE(std::string{"0102000000020000000000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240"} == wkb); + SECTION("linestring as ewkb") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex); + + const auto& wnl = create_test_wnl_okay(buffer); + + const std::string ewkb{factory.create_linestring(wnl)}; + REQUIRE(ewkb == "0102000020E6100000030000009A99999999990940CDCCCCCCCCCC10400000000000000C40CDCCCCCCCCCC1240CDCCCCCCCCCC0C409A99999999991340"); } -} -SECTION("linestring_with_undefined_location") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + SECTION("linestring with two same locations") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_undefined_location(buffer); + const auto& wnl = create_test_wnl_same_location(buffer); - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); -} + SECTION("unique forwards (default)") { + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); + } -} + SECTION("unique backwards") { + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + } -#endif + SECTION("all forwards") { + const std::string wkb{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + REQUIRE(wkb == "0102000000020000000000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240"); + } + + SECTION("all backwards") { + const std::string wkb{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + REQUIRE(wkb == "0102000000020000000000000000000C40CDCCCCCCCCCC12400000000000000C40CDCCCCCCCCCC1240"); + } + } -TEST_CASE("WKB_Geometry_byte_order_independent") { + SECTION("linestring with undefined location") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); -SECTION("empty_point") { - osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); + const auto& wnl = create_test_wnl_undefined_location(buffer); + + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); + } - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); } -SECTION("empty_linestring") { +#endif + +TEST_CASE("WKB geometry (byte-order-independent)") { + osmium::geom::WKBFactory<> factory(osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex); - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_empty(buffer); + SECTION("empty point") { + REQUIRE_THROWS_AS(factory.create_point(osmium::Location{}), osmium::invalid_location); + } - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), osmium::geometry_error); -} + SECTION("empty linestring") { + osmium::memory::Buffer buffer{10000}; + const auto& wnl = create_test_wnl_empty(buffer); + + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), osmium::geometry_error); + } } diff --git a/third_party/libosmium/test/t/geom/test_wkt.cpp b/third_party/libosmium/test/t/geom/test_wkt.cpp index 55ccb4ada59..f6913c40b81 100644 --- a/third_party/libosmium/test/t/geom/test_wkt.cpp +++ b/third_party/libosmium/test/t/geom/test_wkt.cpp @@ -1,136 +1,133 @@ #include "catch.hpp" +#include #include #include "area_helper.hpp" #include "wnl_helper.hpp" -TEST_CASE("WKT_Geometry") { +TEST_CASE("WKT geometry for point") { -SECTION("point") { osmium::geom::WKTFactory<> factory; - std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))}; - REQUIRE(std::string{"POINT(3.2 4.2)"} == wkt); -} + SECTION("point") { + const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(wkt == "POINT(3.2 4.2)"); + } -SECTION("empty_point") { - osmium::geom::WKTFactory<> factory; + SECTION("empty point") { + REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); + } - REQUIRE_THROWS_AS(factory.create_point(osmium::Location()), osmium::invalid_location); } -SECTION("linestring") { - osmium::geom::WKTFactory<> factory; - - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_okay(buffer); - - { - std::string wkt {factory.create_linestring(wnl)}; - REQUIRE(std::string{"LINESTRING(3.2 4.2,3.5 4.7,3.6 4.9)"} == wkt); - } +TEST_CASE("WKT geometry for point in ekwt") { + osmium::geom::WKTFactory<> factory(7, osmium::geom::wkt_type::ewkt); - { - std::string wkt {factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; - REQUIRE(std::string{"LINESTRING(3.6 4.9,3.5 4.7,3.2 4.2)"} == wkt); - } + const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(wkt == "SRID=4326;POINT(3.2 4.2)"); +} - { - std::string wkt {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; - REQUIRE(std::string{"LINESTRING(3.2 4.2,3.5 4.7,3.5 4.7,3.6 4.9)"} == wkt); - } +TEST_CASE("WKT geometry for point in ekwt in web mercator") { + osmium::geom::WKTFactory factory(2, osmium::geom::wkt_type::ewkt); - { - std::string wkt {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; - REQUIRE(std::string{"LINESTRING(3.6 4.9,3.5 4.7,3.5 4.7,3.2 4.2)"} == wkt); - } + const std::string wkt{factory.create_point(osmium::Location{3.2, 4.2})}; + REQUIRE(wkt == "SRID=3857;POINT(356222.37 467961.14)"); } -SECTION("empty_linestring") { +TEST_CASE("WKT geometry factory") { osmium::geom::WKTFactory<> factory; - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_empty(buffer); + osmium::memory::Buffer buffer{10000}; - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), osmium::geometry_error); - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), osmium::geometry_error); -} + SECTION("linestring") { + const auto& wnl = create_test_wnl_okay(buffer); -SECTION("linestring_with_two_same_locations") { - osmium::geom::WKTFactory<> factory; + SECTION("unique forwards (default)") { + const std::string wkt{factory.create_linestring(wnl)}; + REQUIRE(wkt == "LINESTRING(3.2 4.2,3.5 4.7,3.6 4.9)"); + } - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_same_location(buffer); + SECTION("unique backwards") { + const std::string wkt{factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward)}; + REQUIRE(wkt == "LINESTRING(3.6 4.9,3.5 4.7,3.2 4.2)"); + } - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); + SECTION("all forwards") { + const std::string wkt{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + REQUIRE(wkt == "LINESTRING(3.2 4.2,3.5 4.7,3.5 4.7,3.6 4.9)"); + } - try { - factory.create_linestring(wnl); - } catch (osmium::geometry_error& e) { - REQUIRE(e.id() == 0); - REQUIRE(std::string(e.what()) == "need at least two points for linestring"); + SECTION("all backwards") { + const std::string wkt{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + REQUIRE(wkt == "LINESTRING(3.6 4.9,3.5 4.7,3.5 4.7,3.2 4.2)"); + } } - REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + SECTION("empty linestring") { + const auto& wnl = create_test_wnl_empty(buffer); - { - std::string wkt {factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; - REQUIRE(std::string{"LINESTRING(3.5 4.7,3.5 4.7)"} == wkt); + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all), osmium::geometry_error); + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward), osmium::geometry_error); } - { - std::string wkt {factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; - REQUIRE(std::string{"LINESTRING(3.5 4.7,3.5 4.7)"} == wkt); + SECTION("linestring with two same locations") { + const auto& wnl = create_test_wnl_same_location(buffer); + + SECTION("unique forwards") { + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error); + + try { + factory.create_linestring(wnl); + } catch (const osmium::geometry_error& e) { + REQUIRE(e.id() == 0); + REQUIRE(std::string(e.what()) == "need at least two points for linestring"); + } + } + + SECTION("unique backwards") { + REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error); + } + + SECTION("all forwards") { + const std::string wkt{factory.create_linestring(wnl, osmium::geom::use_nodes::all)}; + REQUIRE(wkt == "LINESTRING(3.5 4.7,3.5 4.7)"); + } + + SECTION("all backwards") { + const std::string wkt{factory.create_linestring(wnl, osmium::geom::use_nodes::all, osmium::geom::direction::backward)}; + REQUIRE(wkt == "LINESTRING(3.5 4.7,3.5 4.7)"); + } } -} - -SECTION("linestring_with_undefined_location") { - osmium::geom::WKTFactory<> factory; - osmium::memory::Buffer buffer(10000); - auto &wnl = create_test_wnl_undefined_location(buffer); + SECTION("linestring with undefined location") { + const auto& wnl = create_test_wnl_undefined_location(buffer); - REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); -} - -SECTION("area_1outer_0inner") { - osmium::geom::WKTFactory<> factory; + REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::invalid_location); + } - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_1outer_0inner(buffer); + SECTION("area with one outer and no inner rings") { + const osmium::Area& area = create_test_area_1outer_0inner(buffer); - { - std::string wkt {factory.create_multipolygon(area)}; - REQUIRE(std::string{"MULTIPOLYGON(((3.2 4.2,3.5 4.7,3.6 4.9,3.2 4.2)))"} == wkt); + const std::string wkt{factory.create_multipolygon(area)}; + REQUIRE(wkt == "MULTIPOLYGON(((3.2 4.2,3.5 4.7,3.6 4.9,3.2 4.2)))"); } -} - -SECTION("area_1outer_1inner") { - osmium::geom::WKTFactory<> factory; - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_1outer_1inner(buffer); + SECTION("area with one outer and one inner ring") { + const osmium::Area& area = create_test_area_1outer_1inner(buffer); - { - std::string wkt {factory.create_multipolygon(area)}; - REQUIRE(std::string{"MULTIPOLYGON(((0.1 0.1,9.1 0.1,9.1 9.1,0.1 9.1,0.1 0.1),(1 1,8 1,8 8,1 8,1 1)))"} == wkt); + const std::string wkt{factory.create_multipolygon(area)}; + REQUIRE(wkt == "MULTIPOLYGON(((0.1 0.1,9.1 0.1,9.1 9.1,0.1 9.1,0.1 0.1),(1 1,8 1,8 8,1 8,1 1)))"); } -} -SECTION("area_2outer_2inner") { - osmium::geom::WKTFactory<> factory; - - osmium::memory::Buffer buffer(10000); - const osmium::Area& area = create_test_area_2outer_2inner(buffer); + SECTION("area with two outer and two inner rings") { + const osmium::Area& area = create_test_area_2outer_2inner(buffer); - { - std::string wkt {factory.create_multipolygon(area)}; - REQUIRE(std::string{"MULTIPOLYGON(((0.1 0.1,9.1 0.1,9.1 9.1,0.1 9.1,0.1 0.1),(1 1,4 1,4 4,1 4,1 1),(5 5,5 7,7 7,5 5)),((10 10,11 10,11 11,10 11,10 10)))"} == wkt); + const std::string wkt{factory.create_multipolygon(area)}; + REQUIRE(wkt == "MULTIPOLYGON(((0.1 0.1,9.1 0.1,9.1 9.1,0.1 9.1,0.1 0.1),(1 1,4 1,4 4,1 4,1 1),(5 5,5 7,7 7,5 5)),((10 10,11 10,11 11,10 11,10 10)))"); } -} } diff --git a/third_party/libosmium/test/t/index/test_file_based_index.cpp b/third_party/libosmium/test/t/index/test_file_based_index.cpp new file mode 100644 index 00000000000..42cf5745680 --- /dev/null +++ b/third_party/libosmium/test/t/index/test_file_based_index.cpp @@ -0,0 +1,155 @@ + +#include "catch.hpp" + +#include +#include +#include +#include + +#include +#include + +#include + +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; + 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; + 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/third_party/libosmium/test/t/index/test_id_to_location.cpp b/third_party/libosmium/test/t/index/test_id_to_location.cpp index 4aca238b690..810ef3be97e 100644 --- a/third_party/libosmium/test/t/index/test_id_to_location.cpp +++ b/third_party/libosmium/test/t/index/test_id_to_location.cpp @@ -19,8 +19,8 @@ template void test_func_all(TIndex& index) { osmium::unsigned_object_id_type id1 = 12; osmium::unsigned_object_id_type id2 = 3; - osmium::Location loc1(1.2, 4.5); - osmium::Location loc2(3.5, -7.2); + osmium::Location loc1{1.2, 4.5}; + osmium::Location loc2{3.5, -7.2}; REQUIRE_THROWS_AS(index.get(id1), osmium::not_found); @@ -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); } @@ -37,8 +39,8 @@ template void test_func_real(TIndex& index) { osmium::unsigned_object_id_type id1 = 12; osmium::unsigned_object_id_type id2 = 3; - osmium::Location loc1(1.2, 4.5); - osmium::Location loc2(3.5, -7.2); + osmium::Location loc1{1.2, 4.5}; + osmium::Location loc2{3.5, -7.2}; index.set(id1, loc1); index.set(id2, loc2); @@ -48,18 +50,26 @@ 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") { SECTION("Dummy") { - typedef osmium::index::map::Dummy index_type; + using index_type = osmium::index::map::Dummy; index_type index1; @@ -73,7 +83,7 @@ TEST_CASE("IdToLocation") { } SECTION("DenseMemArray") { - typedef osmium::index::map::DenseMemArray index_type; + using index_type = osmium::index::map::DenseMemArray; index_type index1; index1.reserve(1000); @@ -86,7 +96,7 @@ TEST_CASE("IdToLocation") { #ifdef __linux__ SECTION("DenseMmapArray") { - typedef osmium::index::map::DenseMmapArray index_type; + using index_type = osmium::index::map::DenseMmapArray; index_type index1; test_func_all(index1); @@ -99,7 +109,7 @@ TEST_CASE("IdToLocation") { #endif SECTION("DenseFileArray") { - typedef osmium::index::map::DenseFileArray index_type; + using index_type = osmium::index::map::DenseFileArray; index_type index1; test_func_all(index1); @@ -111,7 +121,7 @@ TEST_CASE("IdToLocation") { #ifdef OSMIUM_WITH_SPARSEHASH SECTION("SparseMemTable") { - typedef osmium::index::map::SparseMemTable index_type; + using index_type = osmium::index::map::SparseMemTable; index_type index1; test_func_all(index1); @@ -123,7 +133,7 @@ TEST_CASE("IdToLocation") { #endif SECTION("SparseMemMap") { - typedef osmium::index::map::SparseMemMap index_type; + using index_type = osmium::index::map::SparseMemMap; index_type index1; test_func_all(index1); @@ -133,7 +143,7 @@ TEST_CASE("IdToLocation") { } SECTION("SparseMemArray") { - typedef osmium::index::map::SparseMemArray index_type; + using index_type = osmium::index::map::SparseMemArray; index_type index1; @@ -149,10 +159,10 @@ TEST_CASE("IdToLocation") { } SECTION("Dynamic map choice") { - typedef osmium::index::map::Map map_type; + using map_type = osmium::index::map::Map; const auto& map_factory = osmium::index::MapFactory::instance(); - std::vector map_type_names = map_factory.map_types(); + const std::vector map_type_names = map_factory.map_types(); REQUIRE(map_type_names.size() >= 5); for (const auto& map_type_name : map_type_names) { diff --git a/third_party/libosmium/test/t/io/test_opl_parser.cpp b/third_party/libosmium/test/t/io/test_opl_parser.cpp new file mode 100644 index 00000000000..9ad6eeb754e --- /dev/null +++ b/third_party/libosmium/test/t/io/test_opl_parser.cpp @@ -0,0 +1,1075 @@ + +#include +#include + +#include "catch.hpp" + +#include +#include + +namespace oid = osmium::io::detail; + +TEST_CASE("Parse OPL: base exception") { + osmium::opl_error e{"foo"}; + REQUIRE(e.data == nullptr); + REQUIRE(e.line == 0); + REQUIRE(e.column == 0); + REQUIRE(e.msg == "OPL error: foo"); + REQUIRE(std::string{e.what()} == "OPL error: foo"); +} + +TEST_CASE("Parse OPL: exception with line and column") { + const char* d = "data"; + osmium::opl_error e{"bar", d}; + e.set_pos(17, 23); + REQUIRE(e.data == d); + REQUIRE(e.line == 17); + REQUIRE(e.column == 23); + REQUIRE(e.msg == "OPL error: bar on line 17 column 23"); + REQUIRE(std::string{e.what()} == "OPL error: bar on line 17 column 23"); +} + +TEST_CASE("Parse OPL: space") { + std::string d{"a b \t c"}; + + const char* s = d.data(); + REQUIRE_THROWS_AS({ + oid::opl_parse_space(&s); + }, osmium::opl_error); + + s = d.data() + 1; + oid::opl_parse_space(&s); + REQUIRE(*s == 'b'); + + REQUIRE_THROWS_AS({ + oid::opl_parse_space(&s); + }, osmium::opl_error); + + ++s; + oid::opl_parse_space(&s); + REQUIRE(*s == 'c'); +} + +TEST_CASE("Parse OPL: check for space") { + REQUIRE(oid::opl_non_empty("aaa")); + REQUIRE(oid::opl_non_empty("a b")); + REQUIRE_FALSE(oid::opl_non_empty(" ")); + REQUIRE_FALSE(oid::opl_non_empty(" x")); + REQUIRE_FALSE(oid::opl_non_empty("\tx")); + REQUIRE_FALSE(oid::opl_non_empty("")); +} + +TEST_CASE("Parse OPL: skip section") { + std::string d{"abcd efgh"}; + const char* skip1 = d.data() + 4; + const char* skip2 = d.data() + 9; + const char* s = d.data(); + REQUIRE(oid::opl_skip_section(&s) == skip1); + REQUIRE(s == skip1); + ++s; + REQUIRE(oid::opl_skip_section(&s) == skip2); + REQUIRE(s == skip2); +} + +TEST_CASE("Parse OPL: parse escaped") { + std::string result; + + SECTION("Empty string") { + const char* s = ""; + REQUIRE_THROWS_WITH({ + oid::opl_parse_escaped(&s, result); + }, "OPL error: eol"); + } + + SECTION("Illegal character for hex") { + const char* s = "x"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_escaped(&s, result); + }, "OPL error: not a hex char"); + } + + SECTION("Illegal character for hex after legal hex characters") { + const char* s = "0123x"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_escaped(&s, result); + }, "OPL error: not a hex char"); + } + + SECTION("Too long") { + const char* s = "123456780"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_escaped(&s, result); + }, "OPL error: hex escape too long"); + } + + SECTION("No data") { + const char* s = "%"; + const char* e = s + std::strlen(s); + oid::opl_parse_escaped(&s, result); + REQUIRE(result.size() == 1); + REQUIRE(result[0] == '\0'); + REQUIRE(s == e); + } + + SECTION("One hex char") { + const char* s = "9%"; + const char* e = s + std::strlen(s); + oid::opl_parse_escaped(&s, result); + REQUIRE(result.size() == 1); + REQUIRE(result == "\t"); + REQUIRE(s == e); + } + + SECTION("Two hex chars (lowercase)") { + const char* s = "3c%"; + const char* e = s + std::strlen(s); + oid::opl_parse_escaped(&s, result); + REQUIRE(result.size() == 1); + REQUIRE(result == "<"); + REQUIRE(s == e); + } + + SECTION("Two hex char (uppercase)") { + const char* s = "3C%"; + const char* e = s + std::strlen(s); + oid::opl_parse_escaped(&s, result); + REQUIRE(result.size() == 1); + REQUIRE(result == "<"); + REQUIRE(s == e); + } + + SECTION("Longer unicode characters") { + const char* s1 = "30dc%"; + oid::opl_parse_escaped(&s1, result); + result.append("_"); + const char* s2 = "1d11e%"; + oid::opl_parse_escaped(&s2, result); + result.append("_"); + const char* s3 = "1f6eb%"; + oid::opl_parse_escaped(&s3, result); + REQUIRE(result == u8"\u30dc_\U0001d11e_\U0001f6eb"); + } + + SECTION("Data after %") { + const char* s = "5a%abc"; + oid::opl_parse_escaped(&s, result); + REQUIRE(result.size() == 1); + REQUIRE(result == "Z"); + REQUIRE(std::string{s} == "abc"); + } + +} + +TEST_CASE("Parse OPL: parse string") { + std::string result; + + SECTION("empty string") { + const char* s = ""; + const char* e = s + std::strlen(s); + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 0); + REQUIRE(result == ""); + REQUIRE(s == e); + } + + SECTION("normal string") { + const char* s = "foo"; + const char* e = s + std::strlen(s); + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 3); + REQUIRE(result == "foo"); + REQUIRE(s == e); + } + + SECTION("string with space") { + const char* s = "foo bar"; + const char* e = s + 3; + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 3); + REQUIRE(result == "foo"); + REQUIRE(s == e); + } + + SECTION("string with tab") { + const char* s = "foo\tbar"; + const char* e = s + 3; + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 3); + REQUIRE(result == "foo"); + REQUIRE(s == e); + } + + SECTION("string with comma") { + const char* s = "foo,bar"; + const char* e = s + 3; + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 3); + REQUIRE(result == "foo"); + REQUIRE(s == e); + } + + SECTION("string with equal sign") { + const char* s = "foo=bar"; + const char* e = s + 3; + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 3); + REQUIRE(result == "foo"); + REQUIRE(s == e); + } + + SECTION("string with escaped characters") { + const char* s = "foo%3d%bar"; + const char* e = s + std::strlen(s); + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 7); + REQUIRE(result == "foo=bar"); + REQUIRE(s == e); + } + + SECTION("string with escaped characters at end") { + const char* s = "foo%3d%"; + const char* e = s + std::strlen(s); + oid::opl_parse_string(&s, result); + REQUIRE(result.size() == 4); + REQUIRE(result == "foo="); + REQUIRE(s == e); + } + + SECTION("string with invalid escaping") { + const char* s = "foo%"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_string(&s, result); + }, "OPL error: eol"); + } + + SECTION("string with invalid escaped characters") { + const char* s = "foo%x%"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_string(&s, result); + }, "OPL error: not a hex char"); + } + +} + +template +T test_parse_int(const char* s) { + auto r = oid::opl_parse_int(&s); + REQUIRE(*s == 'x'); + return r; +} + +TEST_CASE("Parse OPL: integer") { + REQUIRE(test_parse_int("0x") == 0); + REQUIRE(test_parse_int("-0x") == 0); + REQUIRE(test_parse_int("1x") == 1); + REQUIRE(test_parse_int("17x") == 17); + REQUIRE(test_parse_int("-1x") == -1); + REQUIRE(test_parse_int("1234567890123x") == 1234567890123); + REQUIRE(test_parse_int("-1234567890123x") == -1234567890123); + + REQUIRE_THROWS_WITH({ + test_parse_int(""); + }, "OPL error: expected integer"); + + REQUIRE_THROWS_WITH({ + test_parse_int("-x"); + }, "OPL error: expected integer"); + + REQUIRE_THROWS_WITH({ + test_parse_int(" 1"); + }, "OPL error: expected integer"); + + REQUIRE_THROWS_WITH({ + test_parse_int("x"); + }, "OPL error: expected integer"); + + REQUIRE_THROWS_WITH({ + test_parse_int("99999999999999999999999x"); + }, "OPL error: integer too long"); +} + +TEST_CASE("Parse OPL: int32_t") { + REQUIRE(test_parse_int("0x") == 0); + REQUIRE(test_parse_int("123x") == 123); + REQUIRE(test_parse_int("-123x") == -123); + + REQUIRE_THROWS_WITH({ + test_parse_int("12345678901x"); + }, "OPL error: integer too long"); + REQUIRE_THROWS_WITH({ + test_parse_int("-12345678901x"); + }, "OPL error: integer too long"); +} + +TEST_CASE("Parse OPL: uint32_t") { + REQUIRE(test_parse_int("0x") == 0); + REQUIRE(test_parse_int("123x") == 123); + + REQUIRE_THROWS_WITH({ + test_parse_int("-123x"); + }, "OPL error: integer too long"); + + REQUIRE_THROWS_WITH({ + test_parse_int("12345678901x"); + }, "OPL error: integer too long"); + + REQUIRE_THROWS_WITH({ + test_parse_int("-12345678901x"); + }, "OPL error: integer too long"); +} + +TEST_CASE("Parse OPL: visible flag") { + const char* data = "V"; + const char* e = data + std::strlen(data); + REQUIRE(oid::opl_parse_visible(&data)); + REQUIRE(e == data); + +} + +TEST_CASE("Parse OPL: deleted flag") { + const char* data = "D"; + const char* e = data + std::strlen(data); + REQUIRE_FALSE(oid::opl_parse_visible(&data)); + REQUIRE(e == data); +} + +TEST_CASE("Parse OPL: invalid visible flag") { + const char* data = "x"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_visible(&data); + }, "OPL error: invalid visible flag"); +} + +TEST_CASE("Parse OPL: timestamp (empty)") { + const char* data = ""; + const char* e = data + std::strlen(data); + REQUIRE(oid::opl_parse_timestamp(&data) == osmium::Timestamp{}); + REQUIRE(e == data); +} + +TEST_CASE("Parse OPL: timestamp (space)") { + const char* data = " "; + const char* e = data; + REQUIRE(oid::opl_parse_timestamp(&data) == osmium::Timestamp{}); + REQUIRE(e == data); +} + +TEST_CASE("Parse OPL: timestamp (tab)") { + const char* data = "\t"; + const char* e = data; + REQUIRE(oid::opl_parse_timestamp(&data) == osmium::Timestamp{}); + REQUIRE(e == data); +} + +TEST_CASE("Parse OPL: timestamp (invalid)") { + const char* data = "abc"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_timestamp(&data); + }, "OPL error: can not parse timestamp"); +} + +TEST_CASE("Parse OPL: timestamp (valid)") { + const char* data = "2016-03-04T17:28:03Z"; + const char* e = data + std::strlen(data); + REQUIRE(oid::opl_parse_timestamp(&data) == osmium::Timestamp{"2016-03-04T17:28:03Z"}); + REQUIRE(e == data); +} + +TEST_CASE("Parse OPL: valid timestamp with trailing data") { + const char* data = "2016-03-04T17:28:03Zxxx"; + REQUIRE(oid::opl_parse_timestamp(&data) == osmium::Timestamp{"2016-03-04T17:28:03Z"}); + REQUIRE(std::string{data} == "xxx"); +} + +TEST_CASE("Parse OPL: tags") { + osmium::memory::Buffer buffer{1024}; + + SECTION("Empty") { + const char* data = ""; + REQUIRE_THROWS_WITH({ + oid::opl_parse_tags(data, buffer); + }, "OPL error: expected '='"); + } + + SECTION("One tag") { + const char* data = "foo=bar"; + oid::opl_parse_tags(data, buffer); + const auto& taglist = buffer.get(0); + REQUIRE(taglist.size() == 1); + REQUIRE(std::string{taglist.begin()->key()} == "foo"); + REQUIRE(std::string{taglist.begin()->value()} == "bar"); + } + + SECTION("Empty key and value are allowed") { + const char* data = "="; + oid::opl_parse_tags(data, buffer); + const auto& taglist = buffer.get(0); + REQUIRE(taglist.size() == 1); + REQUIRE(std::string{taglist.begin()->key()} == ""); + REQUIRE(std::string{taglist.begin()->value()} == ""); + } + + SECTION("Multiple tags") { + const char* data = "highway=residential,oneway=yes,maxspeed=30"; + oid::opl_parse_tags(data, buffer); + const auto& taglist = buffer.get(0); + REQUIRE(taglist.size() == 3); + auto it = taglist.cbegin(); + REQUIRE(std::string{it->key()} == "highway"); + REQUIRE(std::string{it->value()} == "residential"); + ++it; + REQUIRE(std::string{it->key()} == "oneway"); + REQUIRE(std::string{it->value()} == "yes"); + ++it; + REQUIRE(std::string{it->key()} == "maxspeed"); + REQUIRE(std::string{it->value()} == "30"); + ++it; + REQUIRE(it == taglist.cend()); + } + + SECTION("No equal signs") { + const char* data = "a"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_tags(data, buffer); + }, "OPL error: expected '='"); + } + + SECTION("Two equal signs") { + const char* data = "a=b=c"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_tags(data, buffer); + }, "OPL error: expected ','"); + } + +} + +TEST_CASE("Parse OPL: nodes") { + osmium::memory::Buffer buffer{1024}; + + SECTION("Empty") { + const char* const s = ""; + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() == 0); + } + + SECTION("Invalid format, missing n") { + const char* const s = "xyz"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + }, "OPL error: expected 'n'"); + } + + SECTION("Invalid format, missing ID") { + const char* const s = "nx"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + }, "OPL error: expected integer"); + } + + SECTION("Valid format: one node") { + const char* const s = "n123"; + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& wnl = buffer.get(0); + REQUIRE(wnl.size() == 1); + REQUIRE(wnl.begin()->ref() == 123); + } + + SECTION("Valid format: two nodes") { + const char* const s = "n123,n456"; + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& wnl = buffer.get(0); + REQUIRE(wnl.size() == 2); + auto it = wnl.begin(); + REQUIRE(it->ref() == 123); + ++it; + REQUIRE(it->ref() == 456); + ++it; + REQUIRE(it == wnl.end()); + } + + SECTION("Trailing comma") { + const char* const s = "n123,n456,"; + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& wnl = buffer.get(0); + REQUIRE(wnl.size() == 2); + auto it = wnl.begin(); + REQUIRE(it->ref() == 123); + ++it; + REQUIRE(it->ref() == 456); + ++it; + REQUIRE(it == wnl.end()); + } + + SECTION("Way nodes with coordinates") { + const char* const s = "n123x1.2y3.4,n456x33y0.1"; + oid::opl_parse_way_nodes(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& wnl = buffer.get(0); + REQUIRE(wnl.size() == 2); + auto it = wnl.begin(); + REQUIRE(it->ref() == 123); + const osmium::Location loc1{1.2, 3.4}; + REQUIRE(it->location() == loc1); + ++it; + REQUIRE(it->ref() == 456); + const osmium::Location loc2{33.0, 0.1}; + REQUIRE(it->location() == loc2); + ++it; + REQUIRE(it == wnl.end()); + } + +} + +TEST_CASE("Parse OPL: members") { + osmium::memory::Buffer buffer{1024}; + + SECTION("Empty") { + const char* const s = ""; + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() == 0); + } + + SECTION("Invalid: unknown object type") { + const char* const s = "x123@foo"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + }, "OPL error: unknown object type"); + } + + SECTION("Invalid: illegal ref") { + const char* const s = "wx"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + }, "OPL error: expected integer"); + } + + SECTION("Invalid: missing @") { + const char* const s = "n123foo"; + REQUIRE_THROWS_WITH({ + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + }, "OPL error: expected '@'"); + } + + SECTION("Valid format: one member") { + const char* const s = "n123@foo"; + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& rml = buffer.get(0); + REQUIRE(rml.size() == 1); + auto it = rml.begin(); + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string{it->role()} == "foo"); + ++it; + REQUIRE(it == rml.end()); + } + + SECTION("Valid format: one member without role") { + const char* const s = "n123@"; + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& rml = buffer.get(0); + REQUIRE(rml.size() == 1); + auto it = rml.begin(); + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string{it->role()} == ""); + ++it; + REQUIRE(it == rml.end()); + } + + SECTION("Valid format: three members") { + const char* const s = "n123@,w456@abc,r78@type"; + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& rml = buffer.get(0); + REQUIRE(rml.size() == 3); + auto it = rml.begin(); + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string{it->role()} == ""); + ++it; + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 456); + REQUIRE(std::string{it->role()} == "abc"); + ++it; + REQUIRE(it->type() == osmium::item_type::relation); + REQUIRE(it->ref() == 78); + REQUIRE(std::string{it->role()} == "type"); + ++it; + REQUIRE(it == rml.end()); + } + + SECTION("Trailing comma") { + const char* const s = "n123@,w456@abc,r78@type,"; + oid::opl_parse_relation_members(s, s + std::strlen(s), buffer); + REQUIRE(buffer.written() > 0); + const auto& rml = buffer.get(0); + REQUIRE(rml.size() == 3); + auto it = rml.begin(); + REQUIRE(it->type() == osmium::item_type::node); + REQUIRE(it->ref() == 123); + REQUIRE(std::string{it->role()} == ""); + ++it; + REQUIRE(it->type() == osmium::item_type::way); + REQUIRE(it->ref() == 456); + REQUIRE(std::string{it->role()} == "abc"); + ++it; + REQUIRE(it->type() == osmium::item_type::relation); + REQUIRE(it->ref() == 78); + REQUIRE(std::string{it->role()} == "type"); + ++it; + REQUIRE(it == rml.end()); + } + + +} + +TEST_CASE("Parse node") { + osmium::memory::Buffer buffer{1024}; + + SECTION("Node with id only") { + const char* s = "17"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 17); + } + + SECTION("Node with trailing space") { + const char* s = "17 "; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 17); + } + + SECTION("Node with id and version") { + const char* s = "17 v23"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 17); + REQUIRE(node.version() == 23); + } + + SECTION("Node with multiple spaces") { + const char* s = "17 v23"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 17); + REQUIRE(node.version() == 23); + } + + SECTION("Node with tab instead of space") { + const char* s = "17\tv23"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 17); + REQUIRE(node.version() == 23); + } + + SECTION("Full node (no tags)") { + const char* s = "125799 v6 dV c7711393 t2011-03-29T21:43:10Z i45445 uUScha T x8.7868047 y53.0749415"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 125799); + REQUIRE(node.version() == 6); + REQUIRE(node.visible()); + REQUIRE(node.changeset() == 7711393); + REQUIRE(node.timestamp() == osmium::Timestamp{"2011-03-29T21:43:10Z"}); + REQUIRE(node.uid() == 45445); + REQUIRE(std::string{node.user()} == "UScha"); + osmium::Location loc{8.7868047, 53.0749415}; + REQUIRE(node.location() == loc); + REQUIRE(node.tags().empty()); + } + + SECTION("Node with tags)") { + const char* s = "123 v1 c456 Thighway=residential,oneway=true,name=High%20%Street"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 123); + REQUIRE(node.version() == 1); + REQUIRE(node.changeset() == 456); + REQUIRE(node.tags().size() == 3); + + auto it = node.tags().cbegin(); + REQUIRE(std::string{it->key()} == "highway"); + REQUIRE(std::string{it->value()} == "residential"); + ++it; + REQUIRE(std::string{it->key()} == "oneway"); + REQUIRE(std::string{it->value()} == "true"); + ++it; + REQUIRE(std::string{it->key()} == "name"); + REQUIRE(std::string{it->value()} == "High Street"); + ++it; + REQUIRE(it == node.tags().cend()); + } + + SECTION("Order does not matter") { + const char* s = "125799 c7711393 dV v6 i45445 uUScha T t2011-03-29T21:43:10Z y53.0749415 x8.7868047"; + const char* e = s + std::strlen(s); + oid::opl_parse_node(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Node& node = buffer.get(0); + REQUIRE(node.id() == 125799); + REQUIRE(node.version() == 6); + REQUIRE(node.visible()); + REQUIRE(node.changeset() == 7711393); + REQUIRE(node.timestamp() == osmium::Timestamp{"2011-03-29T21:43:10Z"}); + REQUIRE(node.uid() == 45445); + REQUIRE(std::string{node.user()} == "UScha"); + osmium::Location loc{8.7868047, 53.0749415}; + REQUIRE(node.location() == loc); + REQUIRE(node.tags().empty()); + } + +} + +TEST_CASE("Parse way") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Way with id only") { + const char* s = "17"; + const char* e = s + std::strlen(s); + oid::opl_parse_way(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Way& way = buffer.get(0); + REQUIRE(way.id() == 17); + } + + SECTION("Complete way") { + const char* s = "78216 v12 dV c35895909 t2015-12-11T22:01:57Z i7412 umjulius Tdestination=Interlaken;%20%Kandersteg;%20%Zweisimmen,highway=motorway_link,name=Thun%20%Süd,oneway=yes,ref=17,surface=asphalt Nn1011242,n2569390773,n2569390769,n255308687,n2569390761,n255308689,n255308691,n1407526499,n255308692,n3888362655,n255308693,n255308694,n255308695,n255308686"; + const char* e = s + std::strlen(s); + oid::opl_parse_way(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Way& way = buffer.get(0); + REQUIRE(way.id() == 78216); + REQUIRE(way.version() == 12); + REQUIRE(way.visible()); + REQUIRE(way.changeset() == 35895909); + REQUIRE(way.timestamp() == osmium::Timestamp{"2015-12-11T22:01:57Z"}); + REQUIRE(way.uid() == 7412); + REQUIRE(std::string{way.user()} == "mjulius"); + REQUIRE(way.tags().size() == 6); + + auto it = way.tags().cbegin(); + REQUIRE(std::string{it->key()} == "destination"); + REQUIRE(std::string{it->value()} == "Interlaken; Kandersteg; Zweisimmen"); + ++it; + REQUIRE(std::string{it->key()} == "highway"); + REQUIRE(std::string{it->value()} == "motorway_link"); + ++it; + REQUIRE(std::string{it->key()} == "name"); + REQUIRE(std::string{it->value()} == "Thun Süd"); + ++it; + REQUIRE(std::string{it->key()} == "oneway"); + REQUIRE(std::string{it->value()} == "yes"); + ++it; + REQUIRE(std::string{it->key()} == "ref"); + REQUIRE(std::string{it->value()} == "17"); + ++it; + REQUIRE(std::string{it->key()} == "surface"); + REQUIRE(std::string{it->value()} == "asphalt"); + ++it; + REQUIRE(it == way.tags().cend()); + + REQUIRE(way.nodes().size() == 14); + std::vector ids = { + 1011242, 2569390773, 2569390769, 255308687, 2569390761, 255308689, + 255308691, 1407526499, 255308692, 3888362655, 255308693, 255308694, + 255308695, 255308686 + }; + REQUIRE(std::equal(way.nodes().cbegin(), way.nodes().cend(), ids.cbegin())); + } + +} + +TEST_CASE("Parse relation") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Relation with id only") { + const char* s = "17"; + const char* e = s + std::strlen(s); + oid::opl_parse_relation(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Relation& relation = buffer.get(0); + REQUIRE(relation.id() == 17); + } + + SECTION("Complete relation") { + const char* s = "1074 v45 dV c20048094 t2014-01-17T10:27:04Z i86566 uwisieb Ttype=multipolygon,landuse=forest Mw255722275@inner,w256126142@outer,w24402792@inner,w256950103@outer,w255722279@outer"; + const char* e = s + std::strlen(s); + oid::opl_parse_relation(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Relation& relation = buffer.get(0); + REQUIRE(relation.id() == 1074); + REQUIRE(relation.version() == 45); + REQUIRE(relation.visible()); + REQUIRE(relation.changeset() == 20048094); + REQUIRE(relation.timestamp() == osmium::Timestamp{"2014-01-17T10:27:04Z"}); + REQUIRE(relation.uid() == 86566); + REQUIRE(std::string{relation.user()} == "wisieb"); + REQUIRE(relation.tags().size() == 2); + + auto it = relation.tags().cbegin(); + REQUIRE(std::string{it->key()} == "type"); + REQUIRE(std::string{it->value()} == "multipolygon"); + ++it; + REQUIRE(std::string{it->key()} == "landuse"); + REQUIRE(std::string{it->value()} == "forest"); + ++it; + REQUIRE(it == relation.tags().cend()); + + REQUIRE(relation.members().size() == 5); + auto mit = relation.members().cbegin(); + REQUIRE(mit->type() == osmium::item_type::way); + REQUIRE(mit->ref() == 255722275); + REQUIRE(std::string{mit->role()} == "inner"); + ++mit; + REQUIRE(mit->type() == osmium::item_type::way); + REQUIRE(mit->ref() == 256126142); + REQUIRE(std::string{mit->role()} == "outer"); + ++mit; + ++mit; + ++mit; + ++mit; + REQUIRE(mit == relation.members().cend()); + } + +} + +TEST_CASE("Parse changeset") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Changeset with id only") { + const char* s = "17"; + const char* e = s + std::strlen(s); + oid::opl_parse_changeset(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Changeset& changeset = buffer.get(0); + REQUIRE(changeset.id() == 17); + } + + SECTION("Complete changeset") { + const char* s = "873494 k1 s2009-04-21T08:52:49Z e2009-04-21T09:52:49Z d0 i13093 uTiberiusNero x13.923302 y50.957069 X14.0337519 Y50.9824084 Tcreated_by=Potlatch%20%0.11"; + const char* e = s + std::strlen(s); + oid::opl_parse_changeset(&s, buffer); + REQUIRE(s == e); + REQUIRE(buffer.written() > 0); + const osmium::Changeset& changeset = buffer.get(0); + REQUIRE(changeset.id() == 873494); + REQUIRE(changeset.created_at() == osmium::Timestamp{"2009-04-21T08:52:49Z"}); + REQUIRE(changeset.closed_at() == osmium::Timestamp{"2009-04-21T09:52:49Z"}); + REQUIRE(changeset.num_changes() == 1); + REQUIRE(changeset.num_comments() == 0); + REQUIRE(changeset.uid() == 13093); + REQUIRE(std::string{changeset.user()} == "TiberiusNero"); + REQUIRE(changeset.tags().size() == 1); + + auto it = changeset.tags().cbegin(); + REQUIRE(std::string{it->key()} == "created_by"); + REQUIRE(std::string{it->value()} == "Potlatch 0.11"); + ++it; + REQUIRE(it == changeset.tags().cend()); + + osmium::Box box{13.923302, 50.957069, 14.0337519, 50.9824084}; + REQUIRE(box == changeset.bounds()); + } + +} + +TEST_CASE("Parse line") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Empty line") { + const char* s = ""; + REQUIRE_FALSE(oid::opl_parse_line(0, "", buffer)); + REQUIRE(buffer.written() == 0); + } + + SECTION("Comment line") { + REQUIRE_FALSE(oid::opl_parse_line(0, "# abc", buffer)); + REQUIRE(buffer.written() == 0); + } + + SECTION("Fail") { + REQUIRE_THROWS_WITH({ + oid::opl_parse_line(0, "X", buffer); + }, "OPL error: unknown type on line 0 column 0"); + REQUIRE(buffer.written() == 0); + } + + SECTION("New line at end not allowed") { + REQUIRE_THROWS_WITH({ + oid::opl_parse_line(0, "n12 v3\n", buffer); + }, "OPL error: expected space or tab character on line 0 column 6"); + } + + SECTION("Node, but not asking for nodes") { + REQUIRE_FALSE(oid::opl_parse_line(0, "n12 v1", buffer, osmium::osm_entity_bits::way)); + REQUIRE(buffer.written() == 0); + } + + SECTION("Node") { + REQUIRE(oid::opl_parse_line(0, "n12 v3", buffer)); + REQUIRE(buffer.written() > 0); + auto& item = buffer.get(0); + REQUIRE(item.type() == osmium::item_type::node); + } + + SECTION("Way") { + REQUIRE(oid::opl_parse_line(0, "w12 v3", buffer)); + REQUIRE(buffer.written() > 0); + auto& item = buffer.get(0); + REQUIRE(item.type() == osmium::item_type::way); + } + + SECTION("Relation") { + REQUIRE(oid::opl_parse_line(0, "r12 v3", buffer)); + REQUIRE(buffer.written() > 0); + auto& item = buffer.get(0); + REQUIRE(item.type() == osmium::item_type::relation); + } + + SECTION("Changeset") { + REQUIRE(oid::opl_parse_line(0, "c12", buffer)); + REQUIRE(buffer.written() > 0); + auto& item = buffer.get(0); + REQUIRE(item.type() == osmium::item_type::changeset); + } + +} + +TEST_CASE("Get context for errors") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Unknown object type") { + bool error = false; + try { + oid::opl_parse_line(0, "~~~", buffer); + } catch (const osmium::opl_error& e) { + error = true; + REQUIRE(e.line == 0); + REQUIRE(e.column == 0); + REQUIRE(std::string{e.data} == "~~~"); + } + REQUIRE(error); + } + + SECTION("Node id") { + bool error = false; + try { + oid::opl_parse_line(0, "n~~~", buffer); + } catch (const osmium::opl_error& e) { + error = true; + REQUIRE(e.line == 0); + REQUIRE(e.column == 1); + REQUIRE(std::string{e.data} == "~~~"); + } + REQUIRE(error); + } + + SECTION("Node expect space") { + bool error = false; + try { + oid::opl_parse_line(1, "n123~~~", buffer); + } catch (const osmium::opl_error& e) { + error = true; + REQUIRE(e.line == 1); + REQUIRE(e.column == 4); + REQUIRE(std::string{e.data} == "~~~"); + } + REQUIRE(error); + } + + SECTION("Node unknown attribute") { + bool error = false; + try { + oid::opl_parse_line(2, "n123 ~~~", buffer); + } catch (const osmium::opl_error& e) { + error = true; + REQUIRE(e.line == 2); + REQUIRE(e.column == 5); + REQUIRE(std::string{e.data} == "~~~"); + } + REQUIRE(error); + } + + SECTION("Node version not an int") { + bool error = false; + try { + oid::opl_parse_line(3, "n123 v~~~", buffer); + } catch (const osmium::opl_error& e) { + error = true; + REQUIRE(e.line == 3); + REQUIRE(e.column == 6); + REQUIRE(std::string{e.data} == "~~~"); + } + REQUIRE(error); + } + +} + +TEST_CASE("Parse line with external interface") { + + osmium::memory::Buffer buffer{1024}; + + SECTION("Node") { + REQUIRE(osmium::opl_parse("n12 v3", buffer)); + REQUIRE(buffer.committed() > 0); + REQUIRE(buffer.written() == buffer.committed()); + const auto& item = buffer.get(0); + REQUIRE(item.type() == osmium::item_type::node); + REQUIRE(static_cast(item).id() == 12); + } + + SECTION("Empty line") { + REQUIRE_FALSE(osmium::opl_parse("", buffer)); + REQUIRE(buffer.written() == 0); + REQUIRE(buffer.committed() == 0); + } + + SECTION("Failure") { + REQUIRE_THROWS_WITH({ + osmium::opl_parse("x", buffer); + }, "OPL error: unknown type on line 0 column 0"); + REQUIRE(buffer.written() == 0); + REQUIRE(buffer.committed() == 0); + } + +} + diff --git a/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp b/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp index 566295aff24..63b8bd2978f 100644 --- a/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp +++ b/third_party/libosmium/test/t/io/test_reader_with_mock_decompression.cpp @@ -19,7 +19,7 @@ class MockDecompressor : public osmium::io::Decompressor { public: - MockDecompressor(const std::string& fail_in) : + explicit MockDecompressor(const std::string& fail_in) : Decompressor(), m_fail_in(fail_in) { if (m_fail_in == "constructor") { @@ -87,7 +87,7 @@ TEST_CASE("Test Reader using MockDecompressor") { try { osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz")); REQUIRE(false); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error constructor"); } } @@ -99,7 +99,7 @@ TEST_CASE("Test Reader using MockDecompressor") { osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz")); reader.read(); REQUIRE(false); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error first read"); } } @@ -112,7 +112,7 @@ TEST_CASE("Test Reader using MockDecompressor") { reader.read(); reader.read(); REQUIRE(false); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error second read"); } } @@ -127,7 +127,7 @@ TEST_CASE("Test Reader using MockDecompressor") { reader.read(); reader.close(); REQUIRE(false); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error close"); } } diff --git a/third_party/libosmium/test/t/io/test_reader_with_mock_parser.cpp b/third_party/libosmium/test/t/io/test_reader_with_mock_parser.cpp index c71847ce513..c5c99759831 100644 --- a/third_party/libosmium/test/t/io/test_reader_with_mock_parser.cpp +++ b/third_party/libosmium/test/t/io/test_reader_with_mock_parser.cpp @@ -78,7 +78,7 @@ TEST_CASE("Test Reader using MockParser") { try { osmium::io::Reader reader(with_data_dir("t/io/data.osm")); reader.header(); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error in header"); } } @@ -89,7 +89,7 @@ TEST_CASE("Test Reader using MockParser") { reader.header(); try { reader.read(); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error in read"); } reader.close(); @@ -101,7 +101,7 @@ TEST_CASE("Test Reader using MockParser") { reader.header(); try { throw std::runtime_error("error in user code"); - } catch (std::runtime_error& e) { + } catch (const std::runtime_error& e) { REQUIRE(std::string{e.what()} == "error in user code"); } REQUIRE(reader.read()); diff --git a/third_party/libosmium/test/t/io/test_string_table.cpp b/third_party/libosmium/test/t/io/test_string_table.cpp index ab977e8bc47..1e762451301 100644 --- a/third_party/libosmium/test/t/io/test_string_table.cpp +++ b/third_party/libosmium/test/t/io/test_string_table.cpp @@ -7,6 +7,8 @@ TEST_CASE("String store") { SECTION("empty") { REQUIRE(ss.begin() == ss.end()); + REQUIRE(ss.get_chunk_size() == 100); + REQUIRE(ss.get_chunk_count() == 1); } SECTION("add zero-length string") { @@ -17,6 +19,8 @@ TEST_CASE("String store") { REQUIRE(s1 == *it); REQUIRE(std::string(*it) == ""); REQUIRE(++it == ss.end()); + + REQUIRE(ss.get_chunk_count() == 1); } SECTION("add strings") { @@ -30,6 +34,9 @@ TEST_CASE("String store") { REQUIRE(s1 == *it++); REQUIRE(s2 == *it++); REQUIRE(it == ss.end()); + + ss.clear(); + REQUIRE(ss.begin() == ss.end()); } SECTION("add zero-length string and longer strings") { @@ -45,9 +52,9 @@ TEST_CASE("String store") { } SECTION("add many strings") { - for (const char* teststring : {"a", "abc", "abcd", "abcde"}) { + for (const char* teststring : {"", "a", "abc", "abcd", "abcde"}) { int i = 0; - for (; i < 100; ++i) { + for (; i < 200; ++i) { ss.add(teststring); } @@ -57,7 +64,9 @@ TEST_CASE("String store") { } REQUIRE(i == 0); + REQUIRE(ss.get_chunk_count() > 1); ss.clear(); + REQUIRE(ss.get_chunk_count() == 1); } } @@ -90,5 +99,32 @@ TEST_CASE("String table") { REQUIRE(st.size() == 1); } + SECTION("add empty string") { + REQUIRE(st.add("") == 1); + REQUIRE(st.size() == 2); + REQUIRE(st.add("") == 1); + REQUIRE(st.size() == 2); + } + +} + +TEST_CASE("lots of strings in string table so chunk overflows") { + osmium::io::detail::StringTable st{100}; + REQUIRE(st.size() == 1); + + const int n = 1000; + for (int i = 0; i < n; ++i) { + auto s = std::to_string(i); + st.add(s.c_str()); + } + + REQUIRE(st.size() == n + 1); + + auto it = st.begin(); + REQUIRE(std::string{} == *it++); + for (int i = 0; i < n; ++i) { + REQUIRE(atoi(*it++) == i); + } + REQUIRE(it == st.end()); } diff --git a/third_party/libosmium/test/t/io/test_writer.cpp b/third_party/libosmium/test/t/io/test_writer.cpp index b56dfeb1368..d3c28368b53 100644 --- a/third_party/libosmium/test/t/io/test_writer.cpp +++ b/third_party/libosmium/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(), buffer.cend()); + auto num = std::distance(buffer.select().cbegin(), buffer.select().cend()); REQUIRE(num > 0); - REQUIRE(buffer.cbegin()->id() == 1); + REQUIRE(buffer.select().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(), buffer_check.cend()) == num); - REQUIRE(buffer_check.cbegin()->id() == 1); - + REQUIRE(buffer_check.select().size() == num); + REQUIRE(buffer_check.select().cbegin()->id() == 1); } SECTION("Interrupted writer after open") { diff --git a/third_party/libosmium/test/t/io/test_writer_with_mock_compression.cpp b/third_party/libosmium/test/t/io/test_writer_with_mock_compression.cpp index c2d3bbd492d..a28d537fee8 100644 --- a/third_party/libosmium/test/t/io/test_writer_with_mock_compression.cpp +++ b/third_party/libosmium/test/t/io/test_writer_with_mock_compression.cpp @@ -15,7 +15,7 @@ class MockCompressor : public osmium::io::Compressor { public: - MockCompressor(const std::string& fail_in) : + explicit MockCompressor(const std::string& fail_in) : Compressor(osmium::io::fsync::no), m_fail_in(fail_in) { if (m_fail_in == "constructor") { @@ -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(), buffer.cend()); - REQUIRE(num > 0); + REQUIRE(buffer.select().size() > 0); SECTION("fail on construction") { diff --git a/third_party/libosmium/test/t/io/test_writer_with_mock_encoder.cpp b/third_party/libosmium/test/t/io/test_writer_with_mock_encoder.cpp index a43d59183f1..d059f6b8703 100644 --- a/third_party/libosmium/test/t/io/test_writer_with_mock_encoder.cpp +++ b/third_party/libosmium/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(), buffer.cend()); - REQUIRE(num > 0); + REQUIRE(buffer.select().size() > 0); SECTION("error in header") { diff --git a/third_party/libosmium/test/t/tags/test_filter.cpp b/third_party/libosmium/test/t/tags/test_filter.cpp index fa21de1c5b9..260a4badb24 100644 --- a/third_party/libosmium/test/t/tags/test_filter.cpp +++ b/third_party/libosmium/test/t/tags/test_filter.cpp @@ -24,7 +24,7 @@ void check_filter(const osmium::TagList& tag_list, const TFilter filter, const s } const osmium::TagList& make_tag_list(osmium::memory::Buffer& buffer, std::initializer_list> tags) { - auto pos = osmium::builder::add_tag_list(buffer, osmium::builder::attr::_tags(tags)); + const auto pos = osmium::builder::add_tag_list(buffer, osmium::builder::attr::_tags(tags)); return buffer.get(pos); } @@ -42,7 +42,7 @@ TEST_CASE("Filter") { { "source", "GPS" } // no match }); - std::vector results = { true, false, false }; + const std::vector results = { true, false, false }; check_filter(tag_list, filter, results); } @@ -84,7 +84,7 @@ TEST_CASE("Filter") { { "source", "GPS" } }); - std::vector results = {true, true, false}; + const std::vector results = {true, true, false}; check_filter(tag_list, filter, results); } diff --git a/third_party/libosmium/test/t/tags/test_tag_list.cpp b/third_party/libosmium/test/t/tags/test_tag_list.cpp index 470a2000211..3bdf5e96764 100644 --- a/third_party/libosmium/test/t/tags/test_tag_list.cpp +++ b/third_party/libosmium/test/t/tags/test_tag_list.cpp @@ -167,10 +167,16 @@ TEST_CASE("create tag list") { }); } - const osmium::TagList& tl = *buffer.begin(); + const osmium::TagList& tl = *buffer.select().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/third_party/libosmium/test/t/util/test_cast_with_assert.cpp b/third_party/libosmium/test/t/util/test_cast_with_assert.cpp index 0231f30e14e..044176eef9d 100644 --- a/third_party/libosmium/test/t/util/test_cast_with_assert.cpp +++ b/third_party/libosmium/test/t/util/test_cast_with_assert.cpp @@ -3,7 +3,7 @@ // Define assert() to throw this error. This enables the tests to check that // the assert() fails. struct assert_error : public std::runtime_error { - assert_error(const char* what_arg) : std::runtime_error(what_arg) { + explicit assert_error(const char* what_arg) : std::runtime_error(what_arg) { } }; #define assert(x) if (!(x)) { throw(assert_error(#x)); } diff --git a/third_party/libosmium/test/t/util/test_delta.cpp b/third_party/libosmium/test/t/util/test_delta.cpp index 667c9b4433f..27bd8be72d7 100644 --- a/third_party/libosmium/test/t/util/test_delta.cpp +++ b/third_party/libosmium/test/t/util/test_delta.cpp @@ -70,25 +70,3 @@ TEST_CASE("delta encode and decode") { } -TEST_CASE("delta encode iterator") { - std::vector data = { 4, 5, 13, 22, 12 }; - - auto l = [](std::vector::const_iterator it) -> int { - return *it; - }; - - typedef osmium::util::DeltaEncodeIterator::const_iterator, decltype(l), int> it_type; - it_type it(data.begin(), data.end(), l); - it_type end(data.end(), data.end(), l); - - REQUIRE(*it == 4); - ++it; - REQUIRE(*it++ == 1); - REQUIRE(*it == 8); - ++it; - REQUIRE(*it++ == 9); - REQUIRE(*it == -10); - ++it; - REQUIRE(it == end); -} - diff --git a/third_party/libosmium/test/t/util/test_memory_mapping.cpp b/third_party/libosmium/test/t/util/test_memory_mapping.cpp index 29893f7c70b..647d2a05d98 100644 --- a/third_party/libosmium/test/t/util/test_memory_mapping.cpp +++ b/third_party/libosmium/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") { diff --git a/unit_tests/library/limits.cpp b/unit_tests/library/limits.cpp index ad4b3fbe96f..b8f721b8d9e 100644 --- a/unit_tests/library/limits.cpp +++ b/unit_tests/library/limits.cpp @@ -4,6 +4,7 @@ #include "args.hpp" #include "osrm/match_parameters.hpp" +#include "osrm/nearest_parameters.hpp" #include "osrm/route_parameters.hpp" #include "osrm/table_parameters.hpp" #include "osrm/trip_parameters.hpp" @@ -136,4 +137,33 @@ BOOST_AUTO_TEST_CASE(test_match_limits) BOOST_CHECK(code == "TooBig"); // per the New-Server API spec } +BOOST_AUTO_TEST_CASE(test_nearest_limits) +{ + const auto args = get_args(); + BOOST_REQUIRE_EQUAL(args.size(), 1); + + using namespace osrm; + + EngineConfig config; + config.storage_config = {args[0]}; + config.use_shared_memory = false; + config.max_results_nearest = 2; + + OSRM osrm{config}; + + NearestParameters params; + params.coordinates.emplace_back(util::FloatLongitude{}, util::FloatLatitude{}); + params.number_of_results = 10000; + + json::Object result; + + const auto rc = osrm.Nearest(params, result); + + BOOST_CHECK(rc == Status::Error); + + // Make sure we're not accidentally hitting a guard code path before + const auto code = result.values["code"].get().value; + BOOST_CHECK(code == "TooBig"); // per the New-Server API spec +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/library/tile.cpp b/unit_tests/library/tile.cpp index 258735eedca..237ee166221 100644 --- a/unit_tests/library/tile.cpp +++ b/unit_tests/library/tile.cpp @@ -40,8 +40,6 @@ BOOST_AUTO_TEST_CASE(test_tile) protozero::pbf_reader layer_message = tile_message.get_message(); const auto check_feature = [](protozero::pbf_reader feature_message) { - protozero::pbf_reader::const_uint32_iterator value_begin; - protozero::pbf_reader::const_uint32_iterator value_end; feature_message.next(); // advance parser to first entry BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::GEOMETRY_TAG); BOOST_CHECK_EQUAL(feature_message.get_enum(), util::vector_tile::GEOMETRY_TYPE_LINE); @@ -53,7 +51,9 @@ BOOST_AUTO_TEST_CASE(test_tile) feature_message.next(); // advance to next entry BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG); // properties - std::tie(value_begin, value_end) = feature_message.get_packed_uint32(); + auto property_iter_pair = feature_message.get_packed_uint32(); + auto value_begin = property_iter_pair.begin(); + auto value_end = property_iter_pair.end(); BOOST_CHECK_EQUAL(std::distance(value_begin, value_end), 10); auto iter = value_begin; BOOST_CHECK_EQUAL(*iter++, 0); // speed key @@ -73,8 +73,8 @@ BOOST_AUTO_TEST_CASE(test_tile) BOOST_CHECK(iter == value_end); // geometry feature_message.next(); - std::tie(value_begin, value_end) = feature_message.get_packed_uint32(); - BOOST_CHECK_GT(std::distance(value_begin, value_end), 1); + auto geometry_iter_pair = feature_message.get_packed_uint32(); + BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1); }; const auto check_value = [](protozero::pbf_reader value) { @@ -88,18 +88,24 @@ BOOST_AUTO_TEST_CASE(test_tile) case util::vector_tile::VARIANT_TYPE_DOUBLE: value.get_double(); break; + case util::vector_tile::VARIANT_TYPE_FLOAT: + value.get_float(); + break; case util::vector_tile::VARIANT_TYPE_STRING: value.get_string(); break; - case util::vector_tile::VARIANT_TYPE_UINT32: - value.get_uint32(); + case util::vector_tile::VARIANT_TYPE_UINT64: + value.get_uint64(); + break; + case util::vector_tile::VARIANT_TYPE_SINT64: + value.get_sint64(); break; } } }; - auto number_of_keys = 0u; - auto number_of_values = 0u; + auto number_of_speed_keys = 0u; + auto number_of_speed_values = 0u; while (layer_message.next()) { @@ -111,7 +117,7 @@ BOOST_AUTO_TEST_CASE(test_tile) case util::vector_tile::NAME_TAG: BOOST_CHECK_EQUAL(layer_message.get_string(), "speeds"); break; - case util::vector_tile::EXTEND_TAG: + case util::vector_tile::EXTENT_TAG: BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT); break; case util::vector_tile::FEATURE_TAG: @@ -119,11 +125,76 @@ BOOST_AUTO_TEST_CASE(test_tile) break; case util::vector_tile::KEY_TAG: layer_message.get_string(); - number_of_keys++; + number_of_speed_keys++; + break; + case util::vector_tile::VARIANT_TAG: + check_value(layer_message.get_message()); + number_of_speed_values++; + break; + default: + BOOST_CHECK(false); // invalid tag + break; + } + } + + BOOST_CHECK_EQUAL(number_of_speed_keys, 5); + BOOST_CHECK_GT(number_of_speed_values, 128); // speed value resolution + + tile_message.next(); + layer_message = tile_message.get_message(); + + const auto check_turn_feature = [](protozero::pbf_reader feature_message) { + feature_message.next(); // advance parser to first entry + BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::GEOMETRY_TAG); + BOOST_CHECK_EQUAL(feature_message.get_enum(), util::vector_tile::GEOMETRY_TYPE_POINT); + + feature_message.next(); // advance to next entry + BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::ID_TAG); + feature_message.get_uint64(); // id + + feature_message.next(); // advance to next entry + BOOST_CHECK_EQUAL(feature_message.tag(), util::vector_tile::FEATURE_ATTRIBUTES_TAG); + // properties + auto feature_iter_pair = feature_message.get_packed_uint32(); + BOOST_CHECK_EQUAL(std::distance(feature_iter_pair.begin(), feature_iter_pair.end()), 6); + auto iter = feature_iter_pair.begin(); + BOOST_CHECK_EQUAL(*iter++, 0); // bearing_in key + *iter++; + BOOST_CHECK_EQUAL(*iter++, 1); // turn_angle key + *iter++; + BOOST_CHECK_EQUAL(*iter++, 2); // cost key + *iter++; // skip value check, can be valud uint32 + BOOST_CHECK(iter == feature_iter_pair.end()); + // geometry + feature_message.next(); + auto geometry_iter_pair = feature_message.get_packed_uint32(); + BOOST_CHECK_GT(std::distance(geometry_iter_pair.begin(), geometry_iter_pair.end()), 1); + }; + + auto number_of_turn_keys = 0u; + + while (layer_message.next()) + { + switch (layer_message.tag()) + { + case util::vector_tile::VERSION_TAG: + BOOST_CHECK_EQUAL(layer_message.get_uint32(), 2); + break; + case util::vector_tile::NAME_TAG: + BOOST_CHECK_EQUAL(layer_message.get_string(), "turns"); + break; + case util::vector_tile::EXTENT_TAG: + BOOST_CHECK_EQUAL(layer_message.get_uint32(), util::vector_tile::EXTENT); + break; + case util::vector_tile::FEATURE_TAG: + check_turn_feature(layer_message.get_message()); + break; + case util::vector_tile::KEY_TAG: + layer_message.get_string(); + number_of_turn_keys++; break; case util::vector_tile::VARIANT_TAG: check_value(layer_message.get_message()); - number_of_values++; break; default: BOOST_CHECK(false); // invalid tag @@ -131,8 +202,7 @@ BOOST_AUTO_TEST_CASE(test_tile) } } - BOOST_CHECK_EQUAL(number_of_keys, 5); - BOOST_CHECK_GT(number_of_values, 128); // speed value resolution + BOOST_CHECK_EQUAL(number_of_turn_keys, 3); } BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp index f18b9b53963..6e5b8741aa3 100644 --- a/unit_tests/mocks/mock_datafacade.hpp +++ b/unit_tests/mocks/mock_datafacade.hpp @@ -41,6 +41,14 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade { return SPECIAL_EDGEID; } + + EdgeID FindSmallestEdge(const NodeID /* from */, + const NodeID /* to */, + std::function /* filter */) const override + { + return SPECIAL_EDGEID; + } + EdgeID FindEdgeIndicateIfReverse(const NodeID /* from */, const NodeID /* to */, bool & /* result */) const override @@ -53,23 +61,39 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade } OSMNodeID GetOSMNodeIDOfNode(const unsigned /* id */) const override { return OSMNodeID{0}; } bool EdgeIsCompressed(const unsigned /* id */) const { return false; } - unsigned GetGeometryIndexForEdgeID(const unsigned /* id */) const override + GeometryID GetGeometryIndexForEdgeID(const unsigned /* id */) const override { - return SPECIAL_NODEID; + return GeometryID{SPECIAL_GEOMETRYID, false}; } - void GetUncompressedGeometry(const EdgeID /* id */, - std::vector & /* result_nodes */) const override + std::vector GetUncompressedForwardGeometry(const EdgeID /* id */) const override { + return {}; + } + std::vector GetUncompressedReverseGeometry(const EdgeID /* id */) const override + { + return {}; } - void GetUncompressedWeights(const EdgeID /* id */, - std::vector &result_weights) const override + std::vector GetUncompressedForwardWeights(const EdgeID /* id */) const override { + std::vector result_weights; result_weights.resize(1); result_weights[0] = 1; + return result_weights; } - void GetUncompressedDatasources(const EdgeID /*id*/, - std::vector & /*data_sources*/) const override + std::vector GetUncompressedReverseWeights(const EdgeID /* id */) const override { + std::vector result_weights; + result_weights.resize(1); + result_weights[0] = 1; + return result_weights; + } + std::vector GetUncompressedForwardDatasources(const EdgeID /*id*/) const override + { + return {}; + } + std::vector GetUncompressedReverseDatasources(const EdgeID /*id*/) const override + { + return {}; } std::string GetDatasourceName(const uint8_t /*datasource_name_id*/) const override { @@ -175,6 +199,7 @@ class MockDataFacade final : public engine::datafacade::BaseDataFacade bool IsCoreNode(const NodeID /* id */) const override { return false; } unsigned GetNameIndexFromEdgeID(const unsigned /* id */) const override { return 0; } std::string GetNameForID(const unsigned /* name_id */) const override { return ""; } + std::string GetRefForID(const unsigned /* name_id */) const override { return ""; } std::string GetPronunciationForID(const unsigned /* name_id */) const override { return ""; } std::string GetDestinationsForID(const unsigned /* name_id */) const override { return ""; } std::size_t GetCoreSize() const override { return 0; } diff --git a/unit_tests/server/parameters_parser.cpp b/unit_tests/server/parameters_parser.cpp index 101fd2bfbc7..193abb5b89d 100644 --- a/unit_tests/server/parameters_parser.cpp +++ b/unit_tests/server/parameters_parser.cpp @@ -74,6 +74,15 @@ BOOST_AUTO_TEST_CASE(invalid_table_urls) BOOST_CHECK_EQUAL(testInvalidOptions("1,2;3,4?destinations=foo"), 21UL); } +BOOST_AUTO_TEST_CASE(valid_route_hint) +{ + auto hint = engine::Hint::FromBase64( + "XAYAgP___3-QAAAABAAAACEAAAA_AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAgUAAQE0h8Z2"); + BOOST_CHECK_EQUAL( + hint.phantom.input_location, + util::Coordinate(util::FloatLongitude{7.432251}, util::FloatLatitude{43.745995})); +} + BOOST_AUTO_TEST_CASE(valid_route_urls) { std::vector coords_1 = {{util::FloatLongitude{1}, util::FloatLatitude{2}}, @@ -137,15 +146,12 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) CHECK_EQUAL_RANGE(reference_3.hints, result_3->hints); std::vector> hints_4 = { - engine::Hint::FromBase64("DAIAgP___" - "38AAAAAAAAAAAIAAAAAAAAAEAAAAOgDAAD0AwAAGwAAAOUacQBQP5sCshpxAB0_" - "mwIAAAEBl-Umfg=="), - engine::Hint::FromBase64("cgAAgP___" - "39jAAAADgAAACIAAABeAAAAkQAAANoDAABOAgAAGwAAAFVGcQCiRJsCR0VxAOZFmw" - "IFAAEBl-Umfg=="), - engine::Hint::FromBase64("3gAAgP___" - "39KAAAAHgAAACEAAAAAAAAAGAAAAE0BAABOAQAAGwAAAIAzcQBkUJsC1zNxAHBQmw" - "IAAAEBl-Umfg==")}; + engine::Hint::FromBase64( + "XAYAgP___3-QAAAABAAAACEAAAA_AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAgUAAQE0h8Z2"), + engine::Hint::FromBase64( + "lgQAgP___3-QAAAADwAAABMAAAAoAAAALAAAADQAAAAUAAAAmWFxAL1zmwLcYXEAu3ObAgQAAQE0h8Z2"), + engine::Hint::FromBase64( + "OAUAgMUFAIAAAAAADwAAAAIAAAAAAAAAnQAAALwEAAAUAAAAgz5xAE9WmwKIPnEAUFabAgAAAQE0h8Z2")}; RouteParameters reference_4{false, false, false, @@ -158,9 +164,9 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) std::vector>{}}; auto result_4 = parseParameters( "1,2;3,4?steps=false&hints=" - "DAIAgP___38AAAAAAAAAAAIAAAAAAAAAEAAAAOgDAAD0AwAAGwAAAOUacQBQP5sCshpxAB0_mwIAAAEBl-Umfg==;" - "cgAAgP___39jAAAADgAAACIAAABeAAAAkQAAANoDAABOAgAAGwAAAFVGcQCiRJsCR0VxAOZFmwIFAAEBl-Umfg==;" - "3gAAgP___39KAAAAHgAAACEAAAAAAAAAGAAAAE0BAABOAQAAGwAAAIAzcQBkUJsC1zNxAHBQmwIAAAEBl-Umfg=="); + "XAYAgP___3-QAAAABAAAACEAAAA_AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAgUAAQE0h8Z2;" + "lgQAgP___3-QAAAADwAAABMAAAAoAAAALAAAADQAAAAUAAAAmWFxAL1zmwLcYXEAu3ObAgQAAQE0h8Z2;" + "OAUAgMUFAIAAAAAADwAAAAIAAAAAAAAAnQAAALwEAAAUAAAAgz5xAE9WmwKIPnEAUFabAgAAAQE0h8Z2"); BOOST_CHECK(result_4); BOOST_CHECK_EQUAL(reference_4.steps, result_4->steps); BOOST_CHECK_EQUAL(reference_4.alternatives, result_4->alternatives); @@ -255,13 +261,11 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) {util::FloatLongitude{5}, util::FloatLatitude{6}}, {util::FloatLongitude{7}, util::FloatLatitude{8}}}; std::vector> hints_10 = { - engine::Hint::FromBase64("DAIAgP___" - "38AAAAAAAAAAAIAAAAAAAAAEAAAAOgDAAD0AwAAGwAAAOUacQBQP5sCshpxAB0_" - "mwIAAAEBl-Umfg=="), + engine::Hint::FromBase64( + "XAYAgP___3-QAAAABAAAACEAAAA_AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAgUAAQE0h8Z2"), boost::none, - engine::Hint::FromBase64("cgAAgP___" - "39jAAAADgAAACIAAABeAAAAkQAAANoDAABOAgAAGwAAAFVGcQCiRJsCR0VxAOZFmw" - "IFAAEBl-Umfg=="), + engine::Hint::FromBase64( + "lgQAgP___3-QAAAADwAAABMAAAAoAAAALAAAADQAAAAUAAAAmWFxAL1zmwLcYXEAu3ObAgQAAQE0h8Z2"), boost::none}; RouteParameters reference_10{false, false, @@ -275,8 +279,8 @@ BOOST_AUTO_TEST_CASE(valid_route_urls) std::vector>{}}; auto result_10 = parseParameters( "1,2;3,4;5,6;7,8?steps=false&hints=" - "DAIAgP___38AAAAAAAAAAAIAAAAAAAAAEAAAAOgDAAD0AwAAGwAAAOUacQBQP5sCshpxAB0_mwIAAAEBl-Umfg==;;" - "cgAAgP___39jAAAADgAAACIAAABeAAAAkQAAANoDAABOAgAAGwAAAFVGcQCiRJsCR0VxAOZFmwIFAAEBl-Umfg==" + "XAYAgP___3-QAAAABAAAACEAAAA_AAAAHgAAAHsFAAAUAAAAaWhxALeCmwI7aHEAy4KbAgUAAQE0h8Z2;;" + "lgQAgP___3-QAAAADwAAABMAAAAoAAAALAAAADQAAAAUAAAAmWFxAL1zmwLcYXEAu3ObAgQAAQE0h8Z2" ";"); BOOST_CHECK(result_10); BOOST_CHECK_EQUAL(reference_10.steps, result_10->steps); diff --git a/unit_tests/util/bearing.cpp b/unit_tests/util/bearing.cpp index 4a953c90d48..2d7fc9fce87 100644 --- a/unit_tests/util/bearing.cpp +++ b/unit_tests/util/bearing.cpp @@ -42,6 +42,9 @@ BOOST_AUTO_TEST_CASE(bearing_range_test) BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(-721, 5, 10)); BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(719, 5, 10)); + + BOOST_CHECK_EQUAL(false, bearing::CheckInBounds(1, 1, -1)); + BOOST_CHECK_EQUAL(true, bearing::CheckInBounds(1, 1, 0)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/util/static_rtree.cpp b/unit_tests/util/static_rtree.cpp index 7024492833c..3b5de011c15 100644 --- a/unit_tests/util/static_rtree.cpp +++ b/unit_tests/util/static_rtree.cpp @@ -166,8 +166,7 @@ struct GraphFixture d.forward_segment_id = {pair.second, true}; d.reverse_segment_id = {pair.first, true}; d.fwd_segment_position = 0; - d.forward_packed_geometry_id = 0; - d.reverse_packed_geometry_id = 0; + d.packed_geometry_id = 0; edges.emplace_back(d); } }