From 237b1a8a351a20be1f397ecd7d1a1c8d8a82a7c3 Mon Sep 17 00:00:00 2001 From: Pablo Hoch Date: Thu, 25 May 2023 19:16:54 +0200 Subject: [PATCH] Indoor Routing (#35) --- .pkg | 6 +- .pkg.lock | 6 +- CMakeLists.txt | 1 + include/ppr/backend/request_parser.h | 49 +++++ include/ppr/backend/requests.h | 5 +- include/ppr/common/area.h | 5 + include/ppr/common/data.h | 2 +- include/ppr/common/edge.h | 2 + .../common/geometry/serializable_polygon.h | 8 +- include/ppr/common/mlock.h | 11 +- include/ppr/common/routing_graph.h | 31 ++- include/ppr/common/timing.h | 2 +- include/ppr/output/geojson/edge_info.h | 3 + include/ppr/output/geojson/graph.h | 5 + include/ppr/output/json.h | 8 + .../ppr/preprocessing/int_graph/int_area.h | 11 + include/ppr/preprocessing/osm/level.h | 15 ++ include/ppr/preprocessing/osm/parse.h | 37 +++- .../ppr/preprocessing/osm_graph/osm_area.h | 5 + include/ppr/preprocessing/thread_pool.h | 3 +- include/ppr/routing/additional_edges.h | 11 +- include/ppr/routing/costs.h | 3 +- include/ppr/routing/directed_edge.h | 19 +- include/ppr/routing/input_location.h | 45 ++++ include/ppr/routing/input_pt.h | 21 +- include/ppr/routing/label.h | 7 + include/ppr/routing/labels_to_route.h | 3 +- include/ppr/routing/pareto_dijkstra.h | 8 +- include/ppr/routing/route.h | 6 +- include/ppr/routing/routing_options.h | 27 +++ include/ppr/routing/routing_query.h | 21 ++ include/ppr/routing/search.h | 7 +- src/backend/http_server.cc | 9 +- src/backend/output/route_response.cc | 4 + src/cmd/backend/main.cc | 4 +- src/cmd/footrouting/main.cc | 4 +- src/preprocessing/build_routing_graph.cc | 28 +-- src/preprocessing/osm/way_info.cc | 10 + src/preprocessing/osm_graph/areas.cc | 2 + src/preprocessing/osm_graph/extractor.cc | 6 +- src/routing/costs.cc | 4 +- src/routing/input_areas.cc | 2 +- src/routing/input_pt.cc | 194 +++++++++++++----- src/routing/search.cc | 113 +++++----- 44 files changed, 578 insertions(+), 195 deletions(-) create mode 100644 include/ppr/preprocessing/osm/level.h create mode 100644 include/ppr/routing/input_location.h create mode 100644 include/ppr/routing/routing_options.h create mode 100644 include/ppr/routing/routing_query.h diff --git a/.pkg b/.pkg index dbaf5f5..a81a8c7 100644 --- a/.pkg +++ b/.pkg @@ -1,7 +1,7 @@ [cista] url=git@github.com:felixguendling/cista.git branch=master - commit=d14b70b9f45d249d1c9a20bcc13877cc4b771188 + commit=fce53787252d749727eddf2bfbb40c679b4306ba [conf] url=git@github.com:motis-project/conf.git branch=master @@ -44,8 +44,8 @@ commit=2a557cafb2e9e7c872358a83a63c62a7e14330b3 [unordered_dense] url=git@github.com:motis-project/unordered_dense.git - branch=master - commit=77e91016354e6d8cba24a86c5abb807de2534c02 + branch=main + commit=4f380fb1d64f1843ca2c993ed39a8342a8747e5d [fmt] url=git@github.com:motis-project/fmt.git branch=master diff --git a/.pkg.lock b/.pkg.lock index 3ec169d..4d1be67 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,5 +1,5 @@ -1568225318232232742 -cista d14b70b9f45d249d1c9a20bcc13877cc4b771188 +6314464215482173470 +cista fce53787252d749727eddf2bfbb40c679b4306ba zlib fe8e13ffca867612951bc6baf114e5ac8b00f305 boost be5235eb2258d2ec19e32546ab767a62311d9b46 conf aab49490c6b77027938b74265f386144364aa266 @@ -11,6 +11,6 @@ libressl 390253a44ceef00eb620c38606588414326e9f23 net 44674d2f3917e20b7019a0f7254d332522c36fb7 protozero 8c9f3fa97c2cfdceef86d0b61818ae98e9328f29 rapidjson e4a599d2b5dec065b1ea2af5d8dac52baa9df5f5 -unordered_dense 77e91016354e6d8cba24a86c5abb807de2534c02 +unordered_dense 4f380fb1d64f1843ca2c993ed39a8342a8747e5d Catch2 47d56f28a9801911c048d011b375e5631dbb658f utl 3d3b9f2d45fd6e864cfda0b32a924e8d445c8b69 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a70727..78de293 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,7 @@ target_link_libraries(ppr-common boost utl cista + unordered_dense ) target_compile_features(ppr-common PUBLIC cxx_std_20) set_target_properties(ppr-common PROPERTIES CXX_EXTENSIONS OFF) diff --git a/include/ppr/backend/request_parser.h b/include/ppr/backend/request_parser.h index fd5ad78..e44345c 100644 --- a/include/ppr/backend/request_parser.h +++ b/include/ppr/backend/request_parser.h @@ -1,6 +1,11 @@ +#include + #include "rapidjson/document.h" +#include "utl/verify.h" + #include "ppr/common/location.h" +#include "ppr/routing/input_location.h" #include "ppr/profiles/parse_search_profile.h" @@ -19,6 +24,21 @@ inline void get_waypoints(std::vector& waypoints, } } +inline ppr::routing::osm_namespace parse_osm_namespace( + rapidjson::Value const& val) { + using ppr::routing::osm_namespace; + auto const sv = std::string_view{val.GetString(), val.GetStringLength()}; + if (sv == "node") { + return osm_namespace::NODE; + } else if (sv == "way") { + return osm_namespace::WAY; + } else if (sv == "relation") { + return osm_namespace::RELATION; + } else { + throw utl::fail("invalid osm type in request: {}", sv); + } +} + inline void get_location(location& loc, rapidjson::Value const& doc, char const* key) { if (doc.HasMember(key)) { @@ -34,6 +54,35 @@ inline void get_location(location& loc, rapidjson::Value const& doc, } } +inline void get_input_location(ppr::routing::input_location& loc, + rapidjson::Value const& doc, char const* key) { + if (doc.HasMember(key)) { + auto const& val = doc[key]; + if (val.IsObject()) { + auto const& lng = val.FindMember("lng"); + auto const& lat = val.FindMember("lat"); + if (lng != val.MemberEnd() && lat != val.MemberEnd() && + lng->value.IsNumber() && lat->value.IsNumber()) { + loc.location_ = + make_location(lng->value.GetDouble(), lat->value.GetDouble()); + } + + auto const& osm_id = val.FindMember("osm_id"); + auto const& osm_type = val.FindMember("osm_type"); + if (osm_id != val.MemberEnd() && osm_type != val.MemberEnd() && + osm_id->value.IsNumber() && osm_type->value.IsString()) { + loc.osm_element_ = ppr::routing::osm_element{ + osm_id->value.GetInt64(), parse_osm_namespace(osm_type->value)}; + } + + auto const& level = val.FindMember("level"); + if (level != val.MemberEnd() && level->value.IsNumber()) { + loc.level_ = static_cast(level->value.GetDouble() * 10.0); + } + } + } +} + inline void get_profile(ppr::routing::search_profile& profile, rapidjson::Value const& doc, char const* key) { if (doc.HasMember(key)) { diff --git a/include/ppr/backend/requests.h b/include/ppr/backend/requests.h index fcd5ca8..98c8e3a 100644 --- a/include/ppr/backend/requests.h +++ b/include/ppr/backend/requests.h @@ -3,13 +3,14 @@ #include #include "ppr/common/location.h" +#include "ppr/routing/input_location.h" #include "ppr/routing/search_profile.h" namespace ppr::backend { struct route_request { - location start_; - location destination_; + ppr::routing::input_location start_; + ppr::routing::input_location destination_; ppr::routing::search_profile profile_; bool include_infos_{}; bool include_full_path_{}; diff --git a/include/ppr/common/area.h b/include/ppr/common/area.h index 225d75b..6839436 100644 --- a/include/ppr/common/area.h +++ b/include/ppr/common/area.h @@ -1,11 +1,13 @@ #pragma once +#include #include #include "boost/geometry/algorithms/for_each.hpp" #include "boost/geometry/geometries/register/point.hpp" #include "ppr/common/data.h" +#include "ppr/common/edge.h" #include "ppr/common/geometry/polygon.h" #include "ppr/common/geometry/serializable_polygon.h" #include "ppr/common/matrix.h" @@ -86,14 +88,17 @@ struct area { } std::uint32_t id_{0}; + edge_info_idx_t edge_info_{}; polygon_t polygon_; names_idx_t name_{}; std::int64_t osm_id_{0}; bool from_way_{false}; + std::int16_t level_{}; matrix dist_matrix_; matrix next_matrix_; data::vector exit_nodes_; data::vector adjacent_areas_; + location center_{}; }; inline merc get_merc(area::point const& pt) { return to_merc(pt.location_); } diff --git a/include/ppr/common/data.h b/include/ppr/common/data.h index 0978712..cc2d907 100644 --- a/include/ppr/common/data.h +++ b/include/ppr/common/data.h @@ -4,6 +4,6 @@ namespace ppr { -namespace data = cista::offset; +namespace data = cista::offset; // NOLINT(misc-unused-alias-decls) } // namespace ppr diff --git a/include/ppr/common/edge.h b/include/ppr/common/edge.h index 29ad030..6ef21bf 100644 --- a/include/ppr/common/edge.h +++ b/include/ppr/common/edge.h @@ -66,6 +66,7 @@ struct edge_info { wheelchair_type::wheelchair_type wheelchair_ : 2; std::uint8_t step_count_{}; std::int32_t marked_crossing_detour_{}; + std::int16_t level_{}; // stored as level * 10 }; inline edge_info make_edge_info(std::int64_t osm_way_id, edge_type type, @@ -86,6 +87,7 @@ inline edge_info make_edge_info(std::int64_t osm_way_id, edge_type type, tri_state::UNKNOWN, wheelchair_type::UNKNOWN, 0, + 0, 0}; } diff --git a/include/ppr/common/geometry/serializable_polygon.h b/include/ppr/common/geometry/serializable_polygon.h index 0ad59b8..a8c09f8 100644 --- a/include/ppr/common/geometry/serializable_polygon.h +++ b/include/ppr/common/geometry/serializable_polygon.h @@ -55,9 +55,7 @@ inline double distance(Location const& loc, BOOST_GEOMETRY_REGISTER_RING_TEMPLATED(ppr::ring) -namespace boost { -namespace geometry { -namespace traits { +namespace boost::geometry::traits { template struct tag> { @@ -120,6 +118,4 @@ struct closure> { static const closure_selector value = closed; }; -} // namespace traits -} // namespace geometry -} // namespace boost +} // namespace boost::geometry::traits diff --git a/include/ppr/common/mlock.h b/include/ppr/common/mlock.h index 638f568..8fa50f6 100644 --- a/include/ppr/common/mlock.h +++ b/include/ppr/common/mlock.h @@ -16,20 +16,21 @@ namespace ppr { inline bool lock_memory(void* addr, std::size_t len) { HANDLE proc = GetCurrentProcess(); - SIZE_T min_size, max_size; - if (!GetProcessWorkingSetSize(proc, &min_size, &max_size)) { + SIZE_T min_size = 0; + SIZE_T max_size = 0; + if (GetProcessWorkingSetSize(proc, &min_size, &max_size) == FALSE) { return false; } min_size += static_cast(len); max_size += static_cast(len); - if (!SetProcessWorkingSetSize(proc, min_size, max_size)) { + if (SetProcessWorkingSetSize(proc, min_size, max_size) == FALSE) { return false; } - return VirtualLock(addr, len); + return VirtualLock(addr, len) != FALSE; } inline bool unlock_memory(void* addr, std::size_t len) { - return VirtualUnlock(addr, len); + return VirtualUnlock(addr, len) != FALSE; } } // namespace ppr diff --git a/include/ppr/common/routing_graph.h b/include/ppr/common/routing_graph.h index 85debd2..bf72351 100644 --- a/include/ppr/common/routing_graph.h +++ b/include/ppr/common/routing_graph.h @@ -3,8 +3,11 @@ #include #include #include +#include #include +#include "ankerl/unordered_dense.h" + #include "boost/geometry/geometries/geometries.hpp" #include "boost/geometry/geometries/point_xy.hpp" #include "boost/geometry/geometry.hpp" @@ -44,6 +47,11 @@ struct rg_edge { std::uint32_t edge_index_; }; +struct osm_index { + ankerl::unordered_dense::map ways_to_areas_; + ankerl::unordered_dense::map relations_to_areas_; +}; + enum class rtree_options { DEFAULT, PREFETCH, LOCK }; template @@ -94,7 +102,7 @@ struct rtree_data { for (std::size_t i = 0U; i < file_.get_size(); i += 4096) { c += base[i]; } - volatile char cs = c; + volatile char cs = c; // NOLINT (void)cs; } } @@ -119,9 +127,8 @@ struct routing_graph { routing_graph() : data_{cista::raw::make_unique()} {} - routing_graph(cista::wrapped&& data, - std::string const& filename) - : data_{std::move(data)}, filename_{filename} {} + routing_graph(cista::wrapped&& data, std::string filename) + : data_{std::move(data)}, filename_{std::move(filename)} {} void create_in_edges() { for (auto& n : data_->nodes_) { @@ -139,6 +146,7 @@ struct routing_graph { rtree_options rtree_opt) { create_edge_rtree(edge_rtree_file, edge_rtree_size, rtree_opt); create_area_rtree(area_rtree_file, area_rtree_size, rtree_opt); + create_osm_index(); } void prepare_for_routing(std::size_t edge_rtree_size = 1024UL * 1024 * 1024 * @@ -212,6 +220,18 @@ struct routing_graph { } } + void create_osm_index() { + osm_index_ptr_ = std::make_unique(); + osm_index_ = osm_index_ptr_.get(); + for (auto const& a : data_->areas_) { + if (a.from_way_) { + osm_index_->ways_to_areas_[a.osm_id_] = a.id_; + } else { + osm_index_->relations_to_areas_[a.osm_id_] = a.id_; + } + } + } + public: cista::wrapped data_; @@ -219,6 +239,9 @@ struct routing_graph { rtree_data edge_rtree_; rtree_data area_rtree_; + + osm_index* osm_index_{}; + std::unique_ptr osm_index_ptr_; }; } // namespace ppr diff --git a/include/ppr/common/timing.h b/include/ppr/common/timing.h index 32b1634..4df46e7 100644 --- a/include/ppr/common/timing.h +++ b/include/ppr/common/timing.h @@ -27,7 +27,7 @@ inline double ms_since( } inline void print_timing(std::ostream& out, char const* name, double duration) { - boost::io::ios_all_saver all_saver(out); + boost::io::ios_all_saver const all_saver(out); out << std::setfill('.') << std::setw(60) << std::left << name << std::setw(10) << std::right << duration << " ms" << std::endl; } diff --git a/include/ppr/output/geojson/edge_info.h b/include/ppr/output/geojson/edge_info.h index 71add96..dee0f89 100644 --- a/include/ppr/output/geojson/edge_info.h +++ b/include/ppr/output/geojson/edge_info.h @@ -3,6 +3,7 @@ #include "rapidjson/rapidjson.h" #include "ppr/common/routing_graph.h" +#include "ppr/output/json.h" namespace ppr::output::geojson { @@ -205,6 +206,8 @@ void write_edge_info(routing_graph_data const& rg, Writer& writer, writer.String("marked_crossing_detour"); writer.Int(info->marked_crossing_detour_); + + write_level(writer, info->level_); } } // namespace ppr::output::geojson diff --git a/include/ppr/output/geojson/graph.h b/include/ppr/output/geojson/graph.h index cb00bf4..0d8b624 100644 --- a/include/ppr/output/geojson/graph.h +++ b/include/ppr/output/geojson/graph.h @@ -5,8 +5,11 @@ #include "rapidjson/rapidjson.h" #include "ppr/common/routing_graph.h" + #include "ppr/output/geojson/base.h" #include "ppr/output/geojson/edge_info.h" +#include "ppr/output/json.h" + #include "ppr/preprocessing/osm_graph/osm_graph.h" namespace ppr::output::geojson { @@ -97,6 +100,8 @@ void write_area_properties(routing_graph_data const& rg, Writer& writer, writer.String(""); } + write_level(writer, a.level_); + writer.String("from_way"); writer.Bool(a.from_way_); diff --git a/include/ppr/output/json.h b/include/ppr/output/json.h index d4feaa0..2ebf041 100644 --- a/include/ppr/output/json.h +++ b/include/ppr/output/json.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "ppr/common/tri_state.h" namespace ppr::output { @@ -21,4 +23,10 @@ void write_tri_state(Writer& writer, tri_state::tri_state tri) { } } +template +void write_level(Writer& writer, std::int16_t level) { + writer.String("level"); + writer.Double(static_cast(level) / 10.0); +} + } // namespace ppr::output diff --git a/include/ppr/preprocessing/int_graph/int_area.h b/include/ppr/preprocessing/int_graph/int_area.h index 281e05a..0b33957 100644 --- a/include/ppr/preprocessing/int_graph/int_area.h +++ b/include/ppr/preprocessing/int_graph/int_area.h @@ -1,8 +1,11 @@ #pragma once +#include #include #include +#include "boost/geometry/algorithms/centroid.hpp" + #include "ppr/common/names.h" #include "ppr/common/routing_graph.h" #include "ppr/preprocessing/int_graph/int_node.h" @@ -26,9 +29,11 @@ struct int_area_point { struct int_area { explicit int_area(osm_area const& oa) : id_(oa.id_), + edge_info_(oa.edge_info_), name_(oa.name_), osm_id_(oa.osm_id_), from_way_(oa.from_way_), + level_(oa.level_), dist_matrix_(oa.dist_matrix_), next_matrix_(oa.next_matrix_), exit_nodes_(oa.exit_nodes_), @@ -77,22 +82,28 @@ struct int_area { }); a.id_ = id_; + a.edge_info_ = edge_info_; a.name_ = name_; a.osm_id_ = osm_id_; a.from_way_ = from_way_; + a.level_ = level_; a.dist_matrix_ = dist_matrix_; a.next_matrix_ = next_matrix_; a.exit_nodes_ = exit_nodes_; a.adjacent_areas_ = adjacent_areas_; + a.center_ = to_location( + boost::geometry::return_centroid(a.get_outer_polygon())); return a; } std::uint32_t id_; + edge_info_idx_t edge_info_; std::vector outer_; std::vector> inner_; names_idx_t name_; std::int64_t osm_id_; bool from_way_; + std::int16_t level_{}; matrix dist_matrix_; matrix next_matrix_; data::vector exit_nodes_; diff --git a/include/ppr/preprocessing/osm/level.h b/include/ppr/preprocessing/osm/level.h new file mode 100644 index 0000000..7d182f9 --- /dev/null +++ b/include/ppr/preprocessing/osm/level.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#include "osmium/osm/tag.hpp" + +#include "ppr/preprocessing/osm/parse.h" + +namespace ppr::preprocessing::osm { + +inline std::int16_t get_level(osmium::TagList const& tags) { + return static_cast(parse_float(tags["level"]) * 10.0F); +} + +} // namespace ppr::preprocessing::osm diff --git a/include/ppr/preprocessing/osm/parse.h b/include/ppr/preprocessing/osm/parse.h index cf19ce7..85dd4fb 100644 --- a/include/ppr/preprocessing/osm/parse.h +++ b/include/ppr/preprocessing/osm/parse.h @@ -1,21 +1,44 @@ #pragma once -#include +#include +#include +#include +#include namespace ppr::preprocessing::osm { -inline int parse_int(char const* str, int def = 0) { +template +inline T parse_number(char const* str, T const def = 0.0F, + bool const accept_partial_match = true) { if (str == nullptr) { return def; } - try { - if (std::isdigit(static_cast(str[0]))) { - return std::stoi(str); - } + auto const len = std::strlen(str); + auto value = def; + auto const result = std::from_chars(str, str + len, value); + if (result.ec == std::errc{} && + (accept_partial_match || result.ptr == str + len)) { + return value; + } else { return def; - } catch (...) { + } +} + +inline int parse_int(char const* str, int const def = 0, + bool const accept_partial_match = true) { + return parse_number(str, def, accept_partial_match); +} + +inline float parse_float(char const* str, float const def = 0) { + if (str == nullptr) { return def; } + std::stringstream ss; + ss.imbue(std::locale::classic()); + auto val = def; + ss << str; + ss >> val; + return ss ? val : def; } double parse_length(char const* str, double def = 0.0); diff --git a/include/ppr/preprocessing/osm_graph/osm_area.h b/include/ppr/preprocessing/osm_graph/osm_area.h index bbf9a38..5e26f71 100644 --- a/include/ppr/preprocessing/osm_graph/osm_area.h +++ b/include/ppr/preprocessing/osm_graph/osm_area.h @@ -1,12 +1,15 @@ #pragma once +#include #include #include "ankerl/unordered_dense.h" +#include "ppr/common/edge.h" #include "ppr/common/geometry/polygon.h" #include "ppr/common/matrix.h" #include "ppr/common/names.h" + #include "ppr/preprocessing/osm_graph/osm_node.h" namespace ppr::preprocessing { @@ -62,11 +65,13 @@ struct osm_area { } std::uint32_t id_{0}; + edge_info_idx_t edge_info_{}; std::vector outer_; std::vector> inner_; names_idx_t name_{}; std::int64_t osm_id_{0}; bool from_way_{false}; + std::int16_t level_{}; matrix dist_matrix_; matrix next_matrix_; data::vector exit_nodes_; diff --git a/include/ppr/preprocessing/thread_pool.h b/include/ppr/preprocessing/thread_pool.h index 0719877..7bd5ff2 100644 --- a/include/ppr/preprocessing/thread_pool.h +++ b/include/ppr/preprocessing/thread_pool.h @@ -12,8 +12,7 @@ namespace ppr::preprocessing { struct thread_pool { explicit thread_pool( unsigned num_threads = std::thread::hardware_concurrency()) - : ios_(), - work_(std::make_unique(ios_)), + : work_(std::make_unique(ios_)), threads_(num_threads) { for (auto& t : threads_) { t = std::thread([ios = &ios_] { ios->run(); }); diff --git a/include/ppr/routing/additional_edges.h b/include/ppr/routing/additional_edges.h index 4d298b9..57f35df 100644 --- a/include/ppr/routing/additional_edges.h +++ b/include/ppr/routing/additional_edges.h @@ -13,21 +13,24 @@ struct additional_edges { return nodes_.back().get(); } - void connect(node* a, node* b) { + void connect(node* a, node* b, edge_info_idx_t const edge_info) { auto const d = distance(a->location_, b->location_); - edges_.emplace_back(std::make_unique(make_edge(edge_info_, a, b, d))); + edges_.emplace_back(std::make_unique(make_edge(edge_info, a, b, d))); auto const* a_to_b = edges_.back().get(); - edges_.emplace_back(std::make_unique(make_edge(edge_info_, b, a, d))); + edges_.emplace_back(std::make_unique(make_edge(edge_info, b, a, d))); auto const* b_to_a = edges_.back().get(); edge_map_[a].push_back(b_to_a); edge_map_[b].push_back(a_to_b); } + void connect(node* a, node* b) { connect(a, b, default_edge_info_); } + std::vector> nodes_; std::vector> edges_; ankerl::unordered_dense::map> edge_map_; ankerl::unordered_dense::map> area_nodes_; - edge_info_idx_t edge_info_{0}; // edge info created during preprocessing + edge_info_idx_t default_edge_info_{ + 0}; // edge info created during preprocessing }; } // namespace ppr::routing diff --git a/include/ppr/routing/costs.h b/include/ppr/routing/costs.h index 484b2a7..ffb0617 100644 --- a/include/ppr/routing/costs.h +++ b/include/ppr/routing/costs.h @@ -13,7 +13,8 @@ struct edge_costs { bool allowed_{false}; }; -edge_costs get_edge_costs(routing_graph_data const& rg, edge const* e, bool fwd, +edge_costs get_edge_costs(routing_graph_data const& rg, edge const* e, + edge_info const* info, bool fwd, search_profile const& profile); } // namespace ppr::routing diff --git a/include/ppr/routing/directed_edge.h b/include/ppr/routing/directed_edge.h index 30c2a02..c26c497 100644 --- a/include/ppr/routing/directed_edge.h +++ b/include/ppr/routing/directed_edge.h @@ -6,14 +6,6 @@ namespace ppr::routing { struct directed_edge { - directed_edge() = default; - - directed_edge(edge const* edge, edge_costs const& costs, bool fwd) - : edge_(edge), costs_(costs), fwd_(fwd) {} - - directed_edge(edge const* edge, edge_costs&& costs, bool fwd) - : edge_(edge), costs_(costs), fwd_(fwd) {} - node const* from(routing_graph_data const& rg) const { return fwd_ ? edge_->from(rg) : edge_->to(rg); } @@ -38,11 +30,16 @@ struct directed_edge { return fwd_ ? edge_->elevation_down_ : edge_->elevation_up_; } - bool incline_up(routing_graph_data const& rg) const { - return fwd_ == edge_->info(rg)->incline_up_; // NOLINT + bool incline_up() const { + return fwd_ == static_cast(edge_info_->incline_up_); } - edge const* edge_{nullptr}; + std::uint16_t level() const { return edge_info_->level_; } + + bool in_area() const { return edge_info_->area_; } + + edge const* edge_{}; + edge_info const* edge_info_{}; edge_costs costs_; bool fwd_{true}; }; diff --git a/include/ppr/routing/input_location.h b/include/ppr/routing/input_location.h new file mode 100644 index 0000000..cf29a44 --- /dev/null +++ b/include/ppr/routing/input_location.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +#include "ppr/common/location.h" + +namespace ppr::routing { + +enum class osm_namespace : std::uint8_t { NODE, WAY, RELATION }; + +struct osm_element { + std::int64_t id_{}; + osm_namespace type_{osm_namespace::NODE}; +}; + +struct input_location { + inline bool allows_expansion() const { return expanded_max_distance_ != 0; } + + inline bool valid() const { + return (location_ && location_->valid()) || + (osm_element_ && (osm_element_->type_ == osm_namespace::WAY || + osm_element_->type_ == osm_namespace::RELATION)); + } + + // at least one of the following must be set: + std::optional location_; + std::optional osm_element_; + + std::optional level_; // stored as level * 10 + + // max allowed distance to matched point in routing graph in meters: + double initial_max_distance_{200}; + // if expanded != 0 and no results are found, a second pass is + // done with this max distance: + double expanded_max_distance_{300}; +}; + +inline input_location make_input_location(location const& loc) { + input_location il; + il.location_ = loc; + return il; +} + +} // namespace ppr::routing diff --git a/include/ppr/routing/input_pt.h b/include/ppr/routing/input_pt.h index a46d6cf..f150b2e 100644 --- a/include/ppr/routing/input_pt.h +++ b/include/ppr/routing/input_pt.h @@ -1,28 +1,33 @@ #pragma once #include "ppr/common/routing_graph.h" +#include "ppr/routing/input_location.h" +#include "ppr/routing/routing_options.h" namespace ppr::routing { struct input_pt { input_pt() = default; - input_pt(location input, location nearest_pt, edge const* nearest_edge, - data::vector&& from_path, data::vector&& to_path) + input_pt(routing_graph_data const& rg, location input, location nearest_pt, + edge const* nearest_edge, data::vector&& from_path, + data::vector&& to_path) : input_(input), nearest_pt_(nearest_pt), nearest_edge_(nearest_edge), in_area_(nullptr), outside_of_area_(false), from_path_(std::move(from_path)), - to_path_(std::move(to_path)) {} + to_path_(std::move(to_path)), + level_(nearest_edge->info(rg)->level_) {} explicit input_pt(location input, area const* in_area = nullptr) : input_(input), nearest_pt_(input), nearest_edge_(nullptr), in_area_(in_area), - outside_of_area_(false) {} + outside_of_area_(false), + level_(in_area != nullptr ? in_area->level_ : 0) {} explicit operator bool() const { return nearest_edge_ != nullptr || in_area_ != nullptr; @@ -35,10 +40,12 @@ struct input_pt { bool outside_of_area_{false}; data::vector from_path_; data::vector to_path_; + std::int16_t level_{}; }; -std::vector nearest_points(routing_graph const& g, - location const& loc, unsigned max_query, - unsigned max_count, double max_dist); +std::vector resolve_input_location(routing_graph const& g, + input_location const& il, + routing_options const& opt, + bool const expanded); } // namespace ppr::routing diff --git a/include/ppr/routing/label.h b/include/ppr/routing/label.h index 19ef414..6e7a73f 100644 --- a/include/ppr/routing/label.h +++ b/include/ppr/routing/label.h @@ -28,6 +28,13 @@ struct label { return false; } + if (edge_.level() != e.level()) { + // don't allow crossing between areas with different levels + if (edge_.in_area() && e.in_area()) { + return false; + } + } + l.pred_ = this; l.edge_ = e; l.dominated_ = dominated_; diff --git a/include/ppr/routing/labels_to_route.h b/include/ppr/routing/labels_to_route.h index b44ff6e..a41632a 100644 --- a/include/ppr/routing/labels_to_route.h +++ b/include/ppr/routing/labels_to_route.h @@ -37,7 +37,7 @@ route::edge to_route_edge(Label const* label, routing_graph_data const& rg) { re.oneway_street_ = ei->oneway_street_; re.oneway_foot_ = !(ei->allow_fwd_ && ei->allow_bwd_); re.area_ = ei->area_; - re.incline_up_ = de.incline_up(rg); + re.incline_up_ = de.incline_up(); re.handrail_ = ei->handrail_; re.wheelchair_ = ei->wheelchair_; re.step_count_ = ei->step_count_; @@ -49,6 +49,7 @@ route::edge to_route_edge(Label const* label, routing_graph_data const& rg) { re.to_node_osm_id_ = de.to(rg)->osm_id_; assert(re.elevation_up_ >= 0); assert(re.elevation_down_ >= 0); + re.level_ = ei->level_; return re; } diff --git a/include/ppr/routing/pareto_dijkstra.h b/include/ppr/routing/pareto_dijkstra.h index 5d3744d..69f0468 100644 --- a/include/ppr/routing/pareto_dijkstra.h +++ b/include/ppr/routing/pareto_dijkstra.h @@ -18,8 +18,6 @@ #include "ppr/routing/search_profile.h" #include "ppr/routing/statistics.h" -namespace bg = boost::geometry; - namespace ppr::routing { template @@ -55,7 +53,7 @@ struct pareto_dijkstra { } void search() { - if (goals_.empty()) { + if (start_nodes_.empty() || goals_.empty()) { return; } @@ -169,7 +167,9 @@ struct pareto_dijkstra { } directed_edge make_directed_edge(edge const* e, bool fwd) { - return {e, get_edge_costs(rg_, e, reverse_search_ ? !fwd : fwd, profile_), + auto const* ei = e->info(rg_); + return {e, ei, + get_edge_costs(rg_, e, ei, reverse_search_ ? !fwd : fwd, profile_), fwd}; } diff --git a/include/ppr/routing/route.h b/include/ppr/routing/route.h index ddc3560..3b494f2 100644 --- a/include/ppr/routing/route.h +++ b/include/ppr/routing/route.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -29,11 +30,12 @@ struct route { bool incline_up_{false}; tri_state::tri_state handrail_{tri_state::UNKNOWN}; wheelchair_type::wheelchair_type wheelchair_{wheelchair_type::UNKNOWN}; - uint8_t step_count_{0}; - int32_t marked_crossing_detour_{0}; + std::uint8_t step_count_{0}; + std::int32_t marked_crossing_detour_{0}; side_type side_{side_type::CENTER}; elevation_diff_t elevation_up_{0}; elevation_diff_t elevation_down_{0}; + std::int16_t level_{}; std::int64_t from_node_osm_id_{}; std::int64_t to_node_osm_id_{}; }; diff --git a/include/ppr/routing/routing_options.h b/include/ppr/routing/routing_options.h new file mode 100644 index 0000000..524325c --- /dev/null +++ b/include/ppr/routing/routing_options.h @@ -0,0 +1,27 @@ +#pragma once + +namespace ppr::routing { + +struct routing_options { + inline unsigned max_pt_query(bool const expanded) const { + return expanded ? expanded_max_pt_query_ : initial_max_pt_query_; + } + + inline unsigned max_pt_count(bool const expanded) const { + return expanded ? expanded_max_pt_count_ : initial_max_pt_count_; + } + + bool allow_expansion_{true}; + bool allow_osm_id_expansion_{true}; + + bool force_level_match_{false}; + double level_dist_penalty_{50 * 50}; + + unsigned initial_max_pt_query_{10}; + unsigned initial_max_pt_count_{1}; + + unsigned expanded_max_pt_query_{40}; + unsigned expanded_max_pt_count_{20}; +}; + +} // namespace ppr::routing diff --git a/include/ppr/routing/routing_query.h b/include/ppr/routing/routing_query.h new file mode 100644 index 0000000..913b3ea --- /dev/null +++ b/include/ppr/routing/routing_query.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "ppr/routing/input_location.h" +#include "ppr/routing/routing_options.h" +#include "ppr/routing/search_profile.h" + +namespace ppr::routing { + +enum class search_direction { FWD, BWD }; + +struct routing_query { + input_location start_{}; + std::vector destinations_; + search_profile const& profile_; + search_direction dir_{search_direction::FWD}; + routing_options opt_{}; +}; + +} // namespace ppr::routing diff --git a/include/ppr/routing/search.h b/include/ppr/routing/search.h index 5b3a477..06804e1 100644 --- a/include/ppr/routing/search.h +++ b/include/ppr/routing/search.h @@ -6,7 +6,10 @@ #include #include "ppr/common/routing_graph.h" + +#include "ppr/routing/input_location.h" #include "ppr/routing/route.h" +#include "ppr/routing/routing_query.h" #include "ppr/routing/search_profile.h" #include "ppr/routing/statistics.h" @@ -30,12 +33,12 @@ struct search_result { } }; -enum class search_direction { FWD, BWD }; - search_result find_routes(routing_graph const& g, location const& start, std::vector const& destinations, search_profile const& profile, search_direction dir = search_direction::FWD, bool allow_expansion = true); +search_result find_routes_v2(routing_graph const& g, routing_query const& q); + } // namespace ppr::routing diff --git a/src/backend/http_server.cc b/src/backend/http_server.cc index 3db22be..1228e64 100644 --- a/src/backend/http_server.cc +++ b/src/backend/http_server.cc @@ -64,8 +64,8 @@ rapidjson::Document parse_json(std::string const& s) { route_request parse_route_request(web_server::http_req_t const& req) { route_request r{}; auto doc = parse_json(req.body()); - get_location(r.start_, doc, "start"); - get_location(r.destination_, doc, "destination"); + get_input_location(r.start_, doc, "start"); + get_input_location(r.destination_, doc, "destination"); get_profile(r.profile_, doc, "profile"); get_bool(r.include_infos_, doc, "include_infos"); get_bool(r.include_full_path_, doc, "include_full_path"); @@ -119,8 +119,9 @@ struct http_server::impl { http::status::bad_request)); } - auto const result = - find_routes(graph_, r.start_, {r.destination_}, r.profile_); + auto const rq = + ppr::routing::routing_query{r.start_, {r.destination_}, r.profile_}; + auto const result = find_routes_v2(graph_, rq); auto const& stats = result.stats_; auto const t_before_encoding = timing_now(); diff --git a/src/backend/output/route_response.cc b/src/backend/output/route_response.cc index 12e2d43..7e35181 100644 --- a/src/backend/output/route_response.cc +++ b/src/backend/output/route_response.cc @@ -1,5 +1,7 @@ #include "ppr/backend/output/route_response.h" + #include "ppr/output/json.h" + #include "ppr/routing/route_steps.h" #include "rapidjson/prettywriter.h" @@ -150,6 +152,8 @@ void write_edge(Writer& writer, route::edge const& e) { writer.String("marked_crossing_detour"); writer.Int(e.marked_crossing_detour_); + write_level(writer, e.level_); + writer.EndObject(); } diff --git a/src/cmd/backend/main.cc b/src/cmd/backend/main.cc index b2ed1a3..280deee 100644 --- a/src/cmd/backend/main.cc +++ b/src/cmd/backend/main.cc @@ -56,7 +56,7 @@ int main(int argc, char const* argv[]) { std::cout << "Routing graph: " << rg.data_->nodes_.size() << " nodes, " << rg.data_->areas_.size() << " areas" << std::endl; - std::cout << "Preparing r-trees..." << std::endl; + std::cout << "Preparing indices..." << std::endl; auto const t_rtrees_start = timing_now(); auto const rtree_opt = opt.lock_rtrees_ ? rtree_options::LOCK @@ -65,7 +65,7 @@ int main(int argc, char const* argv[]) { rg.prepare_for_routing(opt.edge_rtree_max_size_, opt.area_rtree_max_size_, rtree_opt); auto const t_rtrees_duration = ms_since(t_rtrees_start); - std::cout << "R-trees: " << t_rtrees_duration << "ms" << std::endl; + std::cout << "Indices: " << t_rtrees_duration << "ms" << std::endl; if (opt.verify_graph_) { std::cout << "Verifying routing graph file..." << std::endl; diff --git a/src/cmd/footrouting/main.cc b/src/cmd/footrouting/main.cc index 401fe6a..1c90da5 100644 --- a/src/cmd/footrouting/main.cc +++ b/src/cmd/footrouting/main.cc @@ -61,7 +61,7 @@ int main(int argc, char const* argv[]) { std::cout << "Routing graph: " << rg.data_->nodes_.size() << " nodes, " << rg.data_->areas_.size() << " areas" << std::endl; - std::cout << "Creating r-trees..." << std::endl; + std::cout << "Creating indices..." << std::endl; fs::remove(fs::path("routing-graph.ppr.ert")); fs::remove(fs::path("routing-graph.ppr.art")); rg.prepare_for_routing(); @@ -69,7 +69,7 @@ int main(int argc, char const* argv[]) { auto const d_rtree = ms_between(t_after_build, t_after_rtree); print_timing("Preprocessing", stats.d_total_pp_); - print_timing("R-Tree Generation", d_rtree); + print_timing("Index Generation", d_rtree); mem_usage_printer.stop(); diff --git a/src/preprocessing/build_routing_graph.cc b/src/preprocessing/build_routing_graph.cc index 031adbb..b06f0e6 100644 --- a/src/preprocessing/build_routing_graph.cc +++ b/src/preprocessing/build_routing_graph.cc @@ -91,7 +91,7 @@ struct preprocessor { for_edge_pairs_ccw( sorted_edges, [&](oriented_int_edge& e1) { return is_linked(e1, side_type::LEFT); }, - [&](oriented_int_edge& e1, oriented_int_edge& e2) { + [this](oriented_int_edge& e1, oriented_int_edge& e2) { if (is_street(ig_, e2) && is_angle_around(normalized_angle(e2.angle_ - e1.angle_), 90, 10)) { @@ -121,7 +121,7 @@ struct preprocessor { if (in->street_edges_ == 1) { auto e = std::find_if( begin(sorted_edges), end(sorted_edges), - [&](oriented_int_edge const& oie) { return is_street(ig_, oie); }); + [this](oriented_int_edge const& oie) { return is_street(ig_, oie); }); assert(e != end(sorted_edges)); if (has_sidewalk(*e, side_type::LEFT)) { auto const& mc = first_path_pt(*e, side_type::LEFT); @@ -136,10 +136,10 @@ struct preprocessor { } else { for_edge_pairs_ccw( sorted_edges, - [&](oriented_int_edge& e1) { + [this](oriented_int_edge& e1) { return is_street(ig_, e1) && !is_ignored(e1); }, - [&](oriented_int_edge& e1, oriented_int_edge& e2) { + [this, in](oriented_int_edge& e1, oriented_int_edge& e2) { if (!is_street(ig_, e2) || is_ignored(e2)) { return false; } @@ -173,10 +173,11 @@ struct preprocessor { for_edge_pairs_ccw( sorted_edges, - [&](oriented_int_edge& e1) { + [this](oriented_int_edge& e1) { return !is_street(ig_, e1) && !is_ignored(e1); }, - [&](oriented_int_edge& e1, oriented_int_edge& e2) { + [this, in, &update_streets_required](oriented_int_edge& e1, + oriented_int_edge& e2) { if (!is_street(ig_, e2) || is_ignored(e2)) { return false; } @@ -212,7 +213,7 @@ struct preprocessor { for_edge_pairs_ccw( sorted_edges, - [&](oriented_int_edge& e1) { + [this, in](oriented_int_edge& e1) { auto* n = rg_from(ig_, e1, side_type::LEFT); if (n == nullptr) { n = create_foot_node(in, in->location_); @@ -220,7 +221,7 @@ struct preprocessor { } return true; }, - [&](oriented_int_edge& e1, oriented_int_edge& e2) { + [this, in](oriented_int_edge& e1, oriented_int_edge& e2) { auto* n1 = rg_from(ig_, e1, side_type::LEFT); assert(n1 != nullptr); auto* n2 = rg_from(ig_, e2, side_type::LEFT); @@ -240,8 +241,9 @@ struct preprocessor { void set_missing_street_nodes( std::vector& sorted_edges) const { for_edge_pairs_ccw( - sorted_edges, [&](oriented_int_edge& e1) { return is_street(ig_, e1); }, - [&](oriented_int_edge& e1, oriented_int_edge& e2) { + sorted_edges, + [this](oriented_int_edge& e1) { return is_street(ig_, e1); }, + [this](oriented_int_edge& e1, oriented_int_edge& e2) { if (!is_street(ig_, e2)) { return false; } @@ -279,10 +281,10 @@ struct preprocessor { (void)in; for_edge_pairs_ccw( sorted_edges, - [&](oriented_int_edge& e1) { + [this](oriented_int_edge& e1) { return e1.edge_->info(ig_)->type_ == edge_type::CROSSING; }, - [&](oriented_int_edge& e1, oriented_int_edge& e2) { + [this, in](oriented_int_edge& e1, oriented_int_edge& e2) { if (!is_linked(e2)) { return false; } @@ -375,7 +377,7 @@ struct preprocessor { bool has_crossing(node const* from, node const* to) { return std::any_of( - begin(from->out_edges_), end(from->out_edges_), [&](auto&& e) { + begin(from->out_edges_), end(from->out_edges_), [this, to](auto&& e) { return e->to_ == to && e->info(*edge_infos_)->type_ == edge_type::CROSSING; }); diff --git a/src/preprocessing/osm/way_info.cc b/src/preprocessing/osm/way_info.cc index 52fcd01..380ffdc 100644 --- a/src/preprocessing/osm/way_info.cc +++ b/src/preprocessing/osm/way_info.cc @@ -6,6 +6,7 @@ #include "ppr/preprocessing/osm/crossing.h" #include "ppr/preprocessing/osm/handrail.h" #include "ppr/preprocessing/osm/layer.h" +#include "ppr/preprocessing/osm/level.h" #include "ppr/preprocessing/osm/parse.h" #include "ppr/preprocessing/osm/ramp.h" #include "ppr/preprocessing/osm/surface.h" @@ -203,6 +204,13 @@ way_info get_highway_info(osmium::Way const& way, osmium::TagList const& tags, street = extract_conveying(tags, info); } + info->area_ = tags.has_tag("area", "yes"); + + info->surface_type_ = get_surface_type(tags["surface"]); + info->smoothness_type_ = get_smoothness_type(tags["smoothness"]); + + info->level_ = get_level(tags); + auto const width = get_render_width(type, street); auto const layer = get_layer(tags); @@ -230,6 +238,8 @@ way_info get_railway_info(osmium::Way const& way, osmium::TagList const& tags, make_edge_info(graph.edge_infos_, way.id(), edge_type::STREET, street, crossing_type::NONE); + info->level_ = get_level(tags); + auto const width = 2.0; auto const layer = get_layer(tags); diff --git a/src/preprocessing/osm_graph/areas.cc b/src/preprocessing/osm_graph/areas.cc index 5777977..08106ee 100644 --- a/src/preprocessing/osm_graph/areas.cc +++ b/src/preprocessing/osm_graph/areas.cc @@ -18,6 +18,8 @@ void process_area(osm_graph& graph, osm_graph_statistics& stats, osm_area* area, info_idx = idx; info->name_ = area->name_; info->area_ = true; + info->level_ = area->level_; + area->edge_info_ = info_idx; } auto vg = build_visibility_graph(area); // NOLINT diff --git a/src/preprocessing/osm_graph/extractor.cc b/src/preprocessing/osm_graph/extractor.cc index 33c3597..af34f1e 100644 --- a/src/preprocessing/osm_graph/extractor.cc +++ b/src/preprocessing/osm_graph/extractor.cc @@ -21,6 +21,7 @@ #include "ppr/preprocessing/names.h" #include "ppr/preprocessing/osm/access.h" #include "ppr/preprocessing/osm/crossing.h" +#include "ppr/preprocessing/osm/level.h" #include "ppr/preprocessing/osm/way_info.h" #include "ppr/preprocessing/osm_graph/areas.h" #include "ppr/preprocessing/osm_graph/extractor.h" @@ -150,10 +151,11 @@ struct extract_handler : public osmium::handler::Handler { a->name_ = get_name(area_tags["name"], graph_.names_, graph_.names_map_); a->osm_id_ = orig_id; a->from_way_ = from_way; + a->level_ = get_level(area_tags); for (auto const& node : a->outer_) { auto& areas = node_areas_[node]; for (auto const& other_area : areas) { - if (other_area != a) { + if (other_area != a && other_area->level_ == a->level_) { a->adjacent_areas_.insert(other_area->id_); other_area->adjacent_areas_.insert(a->id_); } @@ -185,7 +187,7 @@ struct extract_handler : public osmium::handler::Handler { std::vector v; v.reserve(nrl.size()); std::transform(nrl.begin(), nrl.end(), std::back_inserter(v), - [&](osmium::NodeRef const& nr) { + [this](osmium::NodeRef const& nr) { return get_node(nr.ref(), nr.location()); }); return v; diff --git a/src/routing/costs.cc b/src/routing/costs.cc index 9f26c8b..d4af34a 100644 --- a/src/routing/costs.cc +++ b/src/routing/costs.cc @@ -64,10 +64,10 @@ int32_t get_max_crossing_detour(search_profile const& profile, } } -edge_costs get_edge_costs(routing_graph_data const& rg, edge const* e, bool fwd, +edge_costs get_edge_costs(routing_graph_data const& rg, edge const* e, + edge_info const* info, bool fwd, search_profile const& profile) { auto const distance = e->distance_; - auto const info = e->info(rg); double duration = distance / profile.walking_speed_; double accessibility = 0; double duration_penalty = 0; diff --git a/src/routing/input_areas.cc b/src/routing/input_areas.cc index 77ae9b8..492efe5 100644 --- a/src/routing/input_areas.cc +++ b/src/routing/input_areas.cc @@ -68,7 +68,7 @@ void create_area_edges(area const* ar, std::vector& nodes, ensure_node(b); if (!any_edge_between(a.node_, b.node_) && !additional_edge_between(a.node_, b.node_)) { - additional.connect(a.node_, b.node_); + additional.connect(a.node_, b.node_, ar->edge_info_); } }); } diff --git a/src/routing/input_pt.cc b/src/routing/input_pt.cc index c56d0e0..8c2f282 100644 --- a/src/routing/input_pt.cc +++ b/src/routing/input_pt.cc @@ -2,6 +2,7 @@ #include #include #include +#include #include "boost/geometry/algorithms/comparable_distance.hpp" #include "boost/geometry/algorithms/for_each.hpp" @@ -9,6 +10,8 @@ #include "boost/iterator/function_output_iterator.hpp" +#include "utl/erase_if.h" + #include "ppr/common/geometry/path_conversion.h" #include "ppr/routing/input_pt.h" @@ -57,7 +60,8 @@ location nearest_pt_on_segment(location const& loc, location const& seg_from, } } -input_pt nearest_pt_on_edge(edge const* e, location const& loc) { +input_pt nearest_pt_on_edge(routing_graph_data const& rg, edge const* e, + location const& loc) { using loc_segment_t = boost::geometry::model::segment; if (e == nullptr) { return {}; @@ -85,12 +89,19 @@ input_pt nearest_pt_on_edge(edge const* e, location const& loc) { std::copy(begin(path) + nearest_segment + 1, end(path), std::back_inserter(to_path)); - return {loc, point, e, std::move(from_path), std::move(to_path)}; + return {rg, loc, point, e, std::move(from_path), std::move(to_path)}; } std::vector> nearest_edges( - routing_graph const& g, location const& loc, unsigned max_query, - unsigned max_count, double max_dist) { + routing_graph const& g, location const& loc, + std::optional const& opt_level, routing_options const& opt, + unsigned max_query, unsigned max_count, double max_dist) { + auto const level = opt_level.value_or(0); + auto const check_level = opt_level.has_value(); + auto const level_penalty = [&](edge const* e) { + return opt.level_dist_penalty_ * (std::abs(level - e->info(g)->level_)); + }; + auto edges = std::vector>{}; edges.reserve(max_query); g.edge_rtree_->query( @@ -98,8 +109,12 @@ std::vector> nearest_edges( boost::make_function_output_iterator([&](auto const& entry) { auto const* e = entry.second.get(g.data_); auto const dist = distance(loc, e->path_); + if (check_level && opt.force_level_match_ && + e->info(g)->level_ != level) { + return; + } if (dist <= max_dist) { - edges.emplace_back(e, dist); + edges.emplace_back(e, check_level ? dist + level_penalty(e) : dist); } })); if (edges.empty()) { @@ -113,13 +128,17 @@ std::vector> nearest_edges( return edges; } -void find_nearest_edges(routing_graph const& g, location const& loc, - std::vector& out_pts, unsigned max_query, +void find_nearest_edges(routing_graph const& g, std::vector& out_pts, + location const& loc, + std::optional const& opt_level, + routing_options const& opt, unsigned max_query, unsigned max_count, double max_dist) { - auto const edges = nearest_edges(g, loc, max_query, max_count, max_dist); + auto const edges = + nearest_edges(g, loc, opt_level, opt, max_query, max_count, max_dist); std::transform(begin(edges), end(edges), std::back_inserter(out_pts), [&](auto const& edge_with_dist) { - return nearest_pt_on_edge(edge_with_dist.first, loc); + return nearest_pt_on_edge(*g.data_, edge_with_dist.first, + loc); }); } @@ -132,13 +151,18 @@ void print_area_info(routing_graph_data const& rg, area const* a) { << a->count_mapped_nodes() << " mapped nodes" << std::endl; } -bool find_containing_areas(routing_graph const& g, location const& loc, - std::vector& out_pts) { +bool find_containing_areas(routing_graph const& g, + std::vector& out_pts, location const& loc, + std::optional const& level, + routing_options const& opt) { + auto const force_level = level && opt.force_level_match_; auto found_areas = false; g.area_rtree_->query( bgi::contains(loc) && bgi::satisfies([&](routing_graph::area_rtree_value_type const& val) { - return bg::within(loc, g.data_->areas_[val.second].polygon_); + auto const& a = g.data_->areas_[val.second]; + return bg::within(loc, a.polygon_) && + (!force_level || a.level_ == *level); }), boost::make_function_output_iterator([&](auto const& entry) { found_areas = true; @@ -178,37 +202,41 @@ void map_to_area_border(area const* a, input_pt& pt) { pt.nearest_pt_ = nearest_pt_on_segment(loc, seg_from, seg_to); } -void find_nearest_areas(routing_graph const& g, location const& loc, - std::vector& out_pts, unsigned max_query, - unsigned max_count, double max_dist, - bool include_containing = false) { +void find_nearest_areas(routing_graph const& g, std::vector& out_pts, + location const& loc, + std::optional const& opt_level, + routing_options const& opt, unsigned max_query, + unsigned max_count, double max_dist) { + auto const level = opt_level.value_or(0); + auto const check_level = opt_level.has_value(); + auto const level_penalty = [&](area const* a) { + return opt.level_dist_penalty_ * (std::abs(level - a->level_)); + }; + auto areas = std::vector>{}; - auto inside_count = 0U; g.area_rtree_->query( bgi::nearest(loc, max_query), boost::make_function_output_iterator([&](auto const& entry) { auto const* a = &g.data_->areas_[entry.second]; - auto const inside = bg::within(loc, a->polygon_); - if (inside && !include_containing) { + + if (check_level && opt.force_level_match_ && a->level_ != level) { return; } - if (inside) { - if (!include_containing) { - return; - } - ++inside_count; - areas.emplace_back(a, -1.0); - } else { - auto const dist = distance(loc, a->polygon_); - if (dist > max_dist) { - return; - } - areas.emplace_back(a, dist); + + if (bg::within(loc, a->polygon_)) { + return; } + + auto const dist = distance(loc, a->polygon_); + if (dist > max_dist) { + return; + } + + areas.emplace_back(a, check_level ? dist + level_penalty(a) : dist); })); std::sort(begin(areas), end(areas), [&](auto const& a, auto const& b) { return a.second < b.second; }); - if (areas.size() > max_count && inside_count <= max_count) { + if (areas.size() > max_count) { areas.resize(max_count); } for (auto const& [a, dist] : areas) { @@ -219,25 +247,93 @@ void find_nearest_areas(routing_graph const& g, location const& loc, } } -std::vector nearest_points(routing_graph const& g, - location const& loc, unsigned max_query, - unsigned max_count, double max_dist) { +inline bool level_found(std::vector const& pts, + std::optional opt_level) { + if (opt_level) { + auto const level = *opt_level; + return std::any_of(begin(pts), end(pts), + [&](auto const& pt) { return pt.level_ == level; }); + } else { + return !pts.empty(); + } +} + +void resolve_input_area(std::vector& out_pts, area const& a, + input_location const& il) { + out_pts.emplace_back(il.location_.value_or(a.center_), &a); +} + +std::vector resolve_input_location(routing_graph const& g, + input_location const& il, + routing_options const& opt, + bool const expanded) { std::vector pts; - find_containing_areas(g, loc, pts); - - auto const area_count = static_cast(pts.size()); - if (area_count < max_count) { - auto const max_pts = max_count - area_count; - find_nearest_areas(g, loc, pts, max_query, max_pts, max_dist); - find_nearest_edges(g, loc, pts, max_query, max_pts, max_dist); - if (pts.size() > max_count) { - std::sort(begin(pts), end(pts), - [&](input_pt const& a, input_pt const& b) { - return bg::comparable_distance(loc, a.nearest_pt_) < - bg::comparable_distance(loc, b.nearest_pt_); - }); - pts.resize(max_count); + if (il.osm_element_) { + auto const& osm = *il.osm_element_; + switch (osm.type_) { + case osm_namespace::WAY: { + if (auto const area_it = g.osm_index_->ways_to_areas_.find(osm.id_); + area_it != end(g.osm_index_->ways_to_areas_)) { + auto const& a = g.data_->areas_.at(area_it->second); + resolve_input_area(pts, a, il); + } + break; + } + case osm_namespace::RELATION: { + if (auto const area_it = + g.osm_index_->relations_to_areas_.find(osm.id_); + area_it != end(g.osm_index_->relations_to_areas_)) { + auto const& a = g.data_->areas_.at(area_it->second); + resolve_input_area(pts, a, il); + } + break; + } + default: break; + } + if (!opt.allow_osm_id_expansion_ || (!pts.empty() && !expanded)) { + return pts; + } + } + + if (il.location_) { + auto const& loc = *il.location_; + + find_containing_areas(g, pts, loc, il.level_, opt); + + auto const area_count = static_cast(pts.size()); + auto const max_count = opt.max_pt_count(expanded); + if (area_count < max_count || !level_found(pts, il.level_)) { + auto const max_query = opt.max_pt_query(expanded); + auto const max_dist = + expanded ? il.expanded_max_distance_ : il.initial_max_distance_; + auto const max_pts = max_count - area_count; + find_nearest_areas(g, pts, loc, il.level_, opt, max_query, max_pts, + max_dist); + find_nearest_edges(g, pts, loc, il.level_, opt, max_query, max_pts, + max_dist); + if (pts.size() > max_count) { + if (il.level_ && !opt.force_level_match_) { + auto const level = *il.level_; + auto const level_penalty = [&](input_pt const& pt) { + return opt.level_dist_penalty_ * (std::abs(level - pt.level_)); + }; + std::sort(begin(pts), end(pts), + [&](input_pt const& a, input_pt const& b) { + return bg::comparable_distance(loc, a.nearest_pt_) + + level_penalty(a) < + bg::comparable_distance(loc, b.nearest_pt_) + + level_penalty(b); + }); + } else { + std::sort(begin(pts), end(pts), + [&](input_pt const& a, input_pt const& b) { + return bg::comparable_distance(loc, a.nearest_pt_) < + bg::comparable_distance(loc, b.nearest_pt_); + }); + } + pts.resize(max_count); + } } } diff --git a/src/routing/search.cc b/src/routing/search.cc index 05d568a..5a46015 100644 --- a/src/routing/search.cc +++ b/src/routing/search.cc @@ -1,6 +1,8 @@ #include #include +#include "utl/to_vec.h" + #include "ppr/common/timing.h" #include "ppr/routing/label.h" #include "ppr/routing/labels_to_route.h" @@ -10,19 +12,23 @@ namespace ppr::routing { -using mapped_pt = std::pair>; +search_result find_routes( + routing_graph_data const& rg, search_result& result, + std::vector const& start, + std::vector> const& destinations, + search_profile const& profile, search_direction dir) { -search_result find_routes(routing_graph_data const& rg, search_result& result, - mapped_pt const& start, - std::vector const& destinations, - search_profile const& profile, search_direction dir) { auto const t_start = timing_now(); pareto_dijkstra