From d17d7eb1e52650e11fb90245c660aea8d3d0584b Mon Sep 17 00:00:00 2001 From: Traines Date: Tue, 26 Aug 2025 22:04:59 +0000 Subject: [PATCH 01/17] node importance --- include/osr/geojson.h | 1 + include/osr/routing/profiles/bike_sharing.h | 6 ++- include/osr/routing/profiles/car_sharing.h | 6 ++- include/osr/ways.h | 5 ++- src/extract.cc | 43 ++++++++++++++------- src/ways.cc | 9 ++++- 6 files changed, 50 insertions(+), 20 deletions(-) diff --git a/include/osr/geojson.h b/include/osr/geojson.h index 5435621b..d0ac16eb 100644 --- a/include/osr/geojson.h +++ b/include/osr/geojson.h @@ -179,6 +179,7 @@ struct geojson_writer { {"car", p.is_car_accessible()}, {"bike", p.is_bike_accessible()}, {"foot", p.is_walk_accessible()}, + {"importance", p.importance()}, {"is_restricted", w_.r_->node_is_restricted_[n]}, {"is_entrance", p.is_entrance()}, {"is_elevator", p.is_elevator()}, diff --git a/include/osr/routing/profiles/bike_sharing.h b/include/osr/routing/profiles/bike_sharing.h index f01bec0a..6b7e3e8f 100644 --- a/include/osr/routing/profiles/bike_sharing.h +++ b/include/osr/routing/profiles/bike_sharing.h @@ -47,7 +47,8 @@ struct bike_sharing { .is_sidewalk_separate_ = false, .motor_vehicle_no_ = false, .has_toll_ = false, - .is_big_street_ = false}; + .is_big_street_ = false, + .importance_ = 0}; static constexpr auto const kAdditionalNodeProperties = node_properties{.from_level_ = 0, @@ -58,7 +59,8 @@ struct bike_sharing { .is_entrance_ = false, .is_multi_level_ = false, .is_parking_ = false, - .to_level_ = 0}; + .to_level_ = 0, + .importance_ = 0}; enum class node_type : std::uint8_t { kInitialFoot, diff --git a/include/osr/routing/profiles/car_sharing.h b/include/osr/routing/profiles/car_sharing.h index 5d5fe7b2..da738dfc 100644 --- a/include/osr/routing/profiles/car_sharing.h +++ b/include/osr/routing/profiles/car_sharing.h @@ -47,7 +47,8 @@ struct car_sharing { .is_sidewalk_separate_ = false, .motor_vehicle_no_ = false, .has_toll_ = false, - .is_big_street_ = false}; + .is_big_street_ = false, + .importance_ = 0}; static constexpr auto const kAdditionalNodeProperties = node_properties{.from_level_ = 0, @@ -58,7 +59,8 @@ struct car_sharing { .is_entrance_ = false, .is_multi_level_ = false, .is_parking_ = false, - .to_level_ = 0}; + .to_level_ = 0, + .importance_ = 0}; enum class node_type : std::uint8_t { kInitialFoot, diff --git a/include/osr/ways.h b/include/osr/ways.h index 9a3496ef..e2c940e3 100644 --- a/include/osr/ways.h +++ b/include/osr/ways.h @@ -103,6 +103,7 @@ struct way_properties { bool motor_vehicle_no_ : 1; bool has_toll_ : 1; bool is_big_street_ : 1; + std::uint8_t importance_ : 3; // only used during extract }; static_assert(sizeof(way_properties) == 4); @@ -115,6 +116,7 @@ struct node_properties { constexpr bool is_multi_level() const { return is_multi_level_; } constexpr bool is_entrance() const { return is_entrance_; } constexpr bool is_parking() const { return is_parking_; } + constexpr std::uint8_t importance() const { return importance_; } constexpr level_t from_level() const { return level_t{from_level_}; } constexpr level_t to_level() const { return level_t{to_level_}; } @@ -142,6 +144,7 @@ struct node_properties { bool is_parking_ : 1; std::uint8_t to_level_ : 5; + std::uint8_t importance_ : 3; }; static_assert(sizeof(node_properties) == 3); @@ -152,7 +155,7 @@ struct ways { void add_restriction(std::vector&); void compute_big_street_neighbors(); void connect_ways(); - void build_components(); + void build_components_and_importance(); std::optional find_way(osm_way_idx_t const i) { auto const it = std::lower_bound(begin(way_osm_idx_), end(way_osm_idx_), i); diff --git a/src/extract.cc b/src/extract.cc index 8465d269..e0530d97 100644 --- a/src/extract.cc +++ b/src/extract.cc @@ -60,23 +60,38 @@ bool is_number(std::string_view s) { utl::all_of(s, [](char const c) { return std::isdigit(c); }); } -bool is_big_street(tags const& t) { +std::uint8_t get_importance(tags const& t) { switch (cista::hash(t.highway_)) { - case cista::hash("motorway"): - case cista::hash("motorway_link"): - case cista::hash("trunk"): - case cista::hash("trunk_link"): - case cista::hash("primary"): - case cista::hash("primary_link"): + case cista::hash("pedestrian"): + case cista::hash("busway"): + case cista::hash("footway"): + case cista::hash("cycleway"): + case cista::hash("bridleway"): + case cista::hash("steps"): + case cista::hash("corridor"): + case cista::hash("path"): return 0; + case cista::hash("track"): return 1; + case cista::hash("living_street"): + case cista::hash("service"): return 2; + case cista::hash("residential"): return 3; + case cista::hash("road"): return 4; + case cista::hash("unclassified"): + case cista::hash("tertiary"): + case cista::hash("tertiary_link"): return 5; case cista::hash("secondary"): case cista::hash("secondary_link"): - case cista::hash("tertiary"): - case cista::hash("tertiary_link"): - case cista::hash("unclassified"): return true; - default: return false; + case cista::hash("primary"): + case cista::hash("primary_link"): return 6; + case cista::hash("trunk"): + case cista::hash("trunk_link"): + case cista::hash("motorway"): + case cista::hash("motorway_link"): return 7; + default: return 4; } } +bool is_big_street(std::uint8_t const importance) { return importance > 4; } + speed_limit get_speed_limit(tags const& t) { if (is_number(t.max_speed_) /* TODO: support units (kmh/mph) */) { return get_speed_limit( @@ -140,7 +155,8 @@ way_properties get_way_properties(tags const& t) { p.motor_vehicle_no_ = (t.motor_vehicle_ == "no"sv) || (t.vehicle_ == override::kBlacklist); p.has_toll_ = t.toll_; - p.is_big_street_ = is_big_street(t); + p.importance_ = get_importance(t); + p.is_big_street_ = is_big_street(p.importance_); return p; } @@ -157,6 +173,7 @@ std::pair get_node_properties(tags const& t) { p.is_multi_level_ = is_multi; p.is_parking_ = t.is_parking_; p.to_level_ = to_idx(to); + p.importance_ = 0; return {p, t.level_bits_}; } @@ -581,7 +598,6 @@ void extract(bool const with_platforms, w.sync(); w.connect_ways(); - w.build_components(); auto r = std::vector{}; { @@ -600,6 +616,7 @@ void extract(bool const with_platforms, pt->update(pt->in_high_); } + w.build_components_and_importance(); w.add_restriction(r); utl::sort(w.r_->multi_level_elevators_); diff --git a/src/ways.cc b/src/ways.cc index 6143b0d1..fb49091b 100644 --- a/src/ways.cc +++ b/src/ways.cc @@ -25,7 +25,7 @@ ways::ways(std::filesystem::path p, cista::mmap::protection const mode) mm_vec(mm("way_has_conditional_access_no"))}, way_conditional_access_no_{mm("way_conditional_access_no")} {} -void ways::build_components() { +void ways::build_components_and_importance() { auto q = hash_set{}; auto flood_fill = [&](way_idx_t const way_idx, component_idx_t const c) { assert(q.empty()); @@ -34,6 +34,9 @@ void ways::build_components() { auto const next = *q.begin(); q.erase(q.begin()); for (auto const n : r_->way_nodes_[next]) { + r_->node_properties_[n].importance_ = + std::max(r_->node_properties_[n].importance_, + r_->way_properties_[next].importance_); for (auto const w : r_->node_ways_[n]) { auto& wc = r_->way_component_[w]; if (wc == component_idx_t::invalid()) { @@ -46,7 +49,9 @@ void ways::build_components() { }; auto pt = utl::get_active_progress_tracker_or_activate("osr"); - pt->status("Build components").in_high(n_ways()).out_bounds(75, 90); + pt->status("Build components and importance") + .in_high(n_ways()) + .out_bounds(75, 90); auto next_component_idx = component_idx_t{0U}; r_->way_component_.resize(n_ways(), component_idx_t::invalid()); From 56a3e83c32a99a9fedd088e03e37009cb722a3db Mon Sep 17 00:00:00 2001 From: Traines Date: Tue, 12 May 2026 14:19:19 +0000 Subject: [PATCH 02/17] refactor importance as separate vec --- include/osr/geojson.h | 2 +- include/osr/routing/profiles/bike_sharing.h | 6 ++---- include/osr/routing/profiles/car_sharing.h | 6 ++---- include/osr/ways.h | 9 ++++----- src/extract.cc | 5 ++--- src/ways.cc | 8 +++++--- 6 files changed, 16 insertions(+), 20 deletions(-) diff --git a/include/osr/geojson.h b/include/osr/geojson.h index 473d6d11..59e50854 100644 --- a/include/osr/geojson.h +++ b/include/osr/geojson.h @@ -200,7 +200,7 @@ struct geojson_writer { {"car", p.is_car_accessible()}, {"bike", p.is_bike_accessible()}, {"foot", p.is_walk_accessible()}, - {"importance", p.importance()}, + {"importance", w_.r_->node_importance_[n]}, {"bus", p.is_bus_accessible()}, {"bus_with_penalty", p.is_bus_accessible_with_penalty()}, {"is_restricted", w_.r_->node_is_restricted_[n]}, diff --git a/include/osr/routing/profiles/bike_sharing.h b/include/osr/routing/profiles/bike_sharing.h index 1b21df84..7dc4e329 100644 --- a/include/osr/routing/profiles/bike_sharing.h +++ b/include/osr/routing/profiles/bike_sharing.h @@ -56,8 +56,7 @@ struct bike_sharing { .is_incline_down_ = false, .is_bus_accessible_with_penalty_ = false, .is_ferry_accessible_ = false, - .is_railway_accessible_with_penalty_ = false, - .importance_ = 0}; + .is_railway_accessible_with_penalty_ = false}; static constexpr auto const kAdditionalNodeProperties = node_properties{.from_level_ = 0, @@ -70,8 +69,7 @@ struct bike_sharing { .is_multi_level_ = false, .is_parking_ = false, .to_level_ = 0, - .is_bus_accessible_with_penalty_ = false, - .importance_ = 0}; + .is_bus_accessible_with_penalty_ = false}; enum class node_type : std::uint8_t { kInitialFoot, diff --git a/include/osr/routing/profiles/car_sharing.h b/include/osr/routing/profiles/car_sharing.h index e44001d8..475bdbde 100644 --- a/include/osr/routing/profiles/car_sharing.h +++ b/include/osr/routing/profiles/car_sharing.h @@ -56,8 +56,7 @@ struct car_sharing { .is_incline_down_ = false, .is_bus_accessible_with_penalty_ = false, .is_ferry_accessible_ = false, - .is_railway_accessible_with_penalty_ = false, - .importance_ = 0}; + .is_railway_accessible_with_penalty_ = false}; static constexpr auto const kAdditionalNodeProperties = node_properties{.from_level_ = 0, @@ -70,8 +69,7 @@ struct car_sharing { .is_multi_level_ = false, .is_parking_ = false, .to_level_ = 0, - .is_bus_accessible_with_penalty_ = false, - .importance_ = 0}; + .is_bus_accessible_with_penalty_ = false}; enum class node_type : std::uint8_t { kInitialFoot, diff --git a/include/osr/ways.h b/include/osr/ways.h index d0c0ff3c..2b6de1f8 100644 --- a/include/osr/ways.h +++ b/include/osr/ways.h @@ -145,10 +145,9 @@ struct way_properties { std::uint8_t is_bus_accessible_with_penalty_ : 1; std::uint8_t is_ferry_accessible_ : 1; std::uint8_t is_railway_accessible_with_penalty_ : 1; - std::uint8_t importance_ : 3; // only used during extract }; -static_assert(sizeof(way_properties) == 6); +static_assert(sizeof(way_properties) == 5); struct node_properties { constexpr bool is_car_accessible() const { return is_car_accessible_; } @@ -162,7 +161,6 @@ struct node_properties { constexpr bool is_multi_level() const { return is_multi_level_; } constexpr bool is_entrance() const { return is_entrance_; } constexpr bool is_parking() const { return is_parking_; } - constexpr std::uint8_t importance() const { return importance_; } constexpr level_t from_level() const { return level_t{from_level_}; } constexpr level_t to_level() const { return level_t{to_level_}; } @@ -192,10 +190,9 @@ struct node_properties { std::uint8_t to_level_ : 6; std::uint8_t is_bus_accessible_with_penalty_ : 1; - std::uint8_t importance_ : 3; }; -static_assert(sizeof(node_properties) == 4); +static_assert(sizeof(node_properties) == 3); struct ways { ways(std::filesystem::path, cista::mmap::protection); @@ -373,10 +370,12 @@ struct ways { vecvec node_restrictions_; vec_map node_positions_; + vec_map node_importance_; vec> multi_level_elevators_; vec_map way_component_; + vec_map way_importance_; }; cista::wrapped r_; diff --git a/src/extract.cc b/src/extract.cc index 5cd2578d..a00fcf55 100644 --- a/src/extract.cc +++ b/src/extract.cc @@ -170,8 +170,7 @@ way_properties get_way_properties( p.motor_vehicle_no_ = (t.motor_vehicle_ == "no"sv) || (t.vehicle_ == override::kBlacklist); p.has_toll_ = t.toll_; - p.importance_ = get_importance(t); - p.is_big_street_ = is_big_street(p.importance_); + p.is_big_street_ = is_big_street(get_importance(t)); p.in_route_ = t.is_route_ && t.is_public_transport_route(); p.is_bus_accessible_with_penalty_ = is_accessible_with_penalty(t, obj_type); @@ -197,7 +196,6 @@ std::pair get_node_properties(tags const& t) { p.to_level_ = to_idx(to); p.is_bus_accessible_with_penalty_ = is_accessible_with_penalty(t, osm_obj_type::kNode); - p.importance_ = 0; return {p, t.level_bits_}; } @@ -324,6 +322,7 @@ struct way_handler : public osm::handler::Handler { w_.way_osm_idx_.push_back(osm_way_idx_t{w.positive_id()}); w_.r_->way_properties_.emplace_back(p); + w_.r_->way_importance_.emplace_back(get_importance(t)); w_.way_polylines_.emplace_back(w.nodes() | std::views::transform(get_point)); diff --git a/src/ways.cc b/src/ways.cc index 21a55827..1ee1496b 100644 --- a/src/ways.cc +++ b/src/ways.cc @@ -68,6 +68,7 @@ ways::ways(std::filesystem::path p, cista::mmap::protection const mode) way_conditional_access_no_{mm("way_conditional_access_no")} {} void ways::build_components_and_importance() { + r_->node_importance_.resize(n_nodes()); auto q = hash_set{}; auto flood_fill = [&](way_idx_t const way_idx, component_idx_t const c) { assert(q.empty()); @@ -76,9 +77,9 @@ void ways::build_components_and_importance() { auto const next = *q.begin(); q.erase(q.begin()); for (auto const n : r_->way_nodes_[next]) { - r_->node_properties_[n].importance_ = - std::max(r_->node_properties_[n].importance_, - r_->way_properties_[next].importance_); + r_->node_importance_[n] = + std::max(r_->node_importance_[n], + static_cast(r_->way_importance_[next])); for (auto const w : r_->node_ways_[n]) { auto& wc = r_->way_component_[w]; if (wc == component_idx_t::invalid()) { @@ -107,6 +108,7 @@ void ways::build_components_and_importance() { flood_fill(way_idx, c); pt->increment(); } + r_->way_importance_.clear(); } void ways::add_restriction(std::vector& rs) { From 7a65f0a6f0c6a83d46f08a4bb388a9edbad4f6a5 Mon Sep 17 00:00:00 2001 From: Traines Date: Tue, 12 May 2026 14:21:33 +0000 Subject: [PATCH 03/17] add inertialflowcutter --- .pkg | 4 ++++ CMakeLists.txt | 1 + src/ways.cc | 30 ++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/.pkg b/.pkg index 432bb8af..29bb8938 100644 --- a/.pkg +++ b/.pkg @@ -66,3 +66,7 @@ url=git@github.com:motis-project/net.git branch=master commit=c2599102dcffa4babfdcf5320a6bd6f5c6e9b731 +[inertialflowcutter] + url=git@github.com:motis-project/InertialFlowCutter.git + branch=master + commit=8163095365f4812337290dcbe8bd65df531e0398 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a7c9e36..679a46ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,7 @@ target_link_libraries(osr unordered_dense boost-thread boost-iostreams + inertialflowcutter ) # --- MAIN --- diff --git a/src/ways.cc b/src/ways.cc index 1ee1496b..8a010d75 100644 --- a/src/ways.cc +++ b/src/ways.cc @@ -2,10 +2,16 @@ #include +#include "utl/pairwise.h" #include "utl/parallel_for.h" #include "cista/io.h" +#define USE_INERTIAL_FLOW_CUT +#ifdef USE_INERTIAL_FLOW_CUT +#include "inertialflowcutter/run.h" +#endif + namespace osr { namespace { @@ -109,6 +115,30 @@ void ways::build_components_and_importance() { pt->increment(); } r_->way_importance_.clear(); + +#ifdef USE_INERTIAL_FLOW_CUT + pt->status("Run inertial flow cutter").in_high(n_ways()).out_bounds(90, 91); + + auto v_tail = std::vector{}; + auto v_head = std::vector{}; + + for (auto const nodes : r_->way_nodes_) { + for (auto const [a, b] : utl::pairwise(nodes)) { + v_tail.push_back(a.v_); + v_head.push_back(b.v_); + } + } + run_inertial_flow_cutter( + static_cast(n_nodes()), v_head, v_tail, + [&](int i) { + auto const p = get_node_pos(node_idx_t{i}); + return std::pair{p.lng(), p.lat()}; + }, + [&](int node_idx, int level) { + r_->node_importance_[node_idx_t{node_idx}] = + static_cast(level); + }); +#endif } void ways::add_restriction(std::vector& rs) { From 544a956c650020c23da6830a8f0ceced6eba60b3 Mon Sep 17 00:00:00 2001 From: Traines Date: Tue, 12 May 2026 20:58:29 +0000 Subject: [PATCH 04/17] upd ifc --- .gitignore | 7 ++++++- .pkg | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index ec2b0e3a..f843a234 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,9 @@ /.pkg.mutex /.clang-tidy /.vs -/out \ No newline at end of file +/out +*.pbf +test/monaco +test/hamburg +test/switzerland +test/germany \ No newline at end of file diff --git a/.pkg b/.pkg index 29bb8938..762d987a 100644 --- a/.pkg +++ b/.pkg @@ -69,4 +69,4 @@ [inertialflowcutter] url=git@github.com:motis-project/InertialFlowCutter.git branch=master - commit=8163095365f4812337290dcbe8bd65df531e0398 \ No newline at end of file + commit=9dc89671c2b54fc7557c9eb472c90429726ba717 \ No newline at end of file From 125270fbfc9f80045687ed06f86f1220ec0de7b9 Mon Sep 17 00:00:00 2001 From: Traines Date: Tue, 12 May 2026 21:21:00 +0000 Subject: [PATCH 05/17] fix tbb static build --- .pkg | 2 +- .pkg.lock | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.pkg b/.pkg index 762d987a..242ab53e 100644 --- a/.pkg +++ b/.pkg @@ -69,4 +69,4 @@ [inertialflowcutter] url=git@github.com:motis-project/InertialFlowCutter.git branch=master - commit=9dc89671c2b54fc7557c9eb472c90429726ba717 \ No newline at end of file + commit=90a46297b76f34a99c695c512e2b69196db4e3db \ No newline at end of file diff --git a/.pkg.lock b/.pkg.lock index e216bd06..a7c90253 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,5 +1,7 @@ -11881102796209809478 +3138434207344735686 cista 02fc087619fc913aca2c3fd3e07ec12279b8d6c6 +oneTBB cda5d37ce8303c58ac506259387e75de4ffcf26b +InertialFlowCutter 90a46297b76f34a99c695c512e2b69196db4e3db zlib-ng d68e1d908e789c31c1f2fafe4bd8e09cb91e21c5 boost 77467bf580d98ea06716e2931cbe3b1f28e0cd37 conf f9bf4bd83bf55a2170725707e526cbacc45dcc66 @@ -25,7 +27,6 @@ clipper 2abf72015b85f1cfd38706b21d4f28b8c359c7d5 concurrentqueue ab6b0b5334a005ef6ff1365ba5af87404de39d29 lmdb 39d8127e5697b1323a67e61c3ad8f087384c7429 miniz e857234a23de8e5a2d05d266e518220a495db0ce -oneTBB cda5d37ce8303c58ac506259387e75de4ffcf26b pbf-sdf-fonts 66f0c760272c7bdd0efeaac5aab5520b9194225e sol2 40c7cbc7c5cfed1e8c7f1bbe6fcbe23d7a67fc75 variant 5aa73631dc969087c77433a5cdef246303051f69 From 183f335d1b6e68767a15f784e10089b43dfd7bdc Mon Sep 17 00:00:00 2001 From: Traines Date: Wed, 13 May 2026 09:45:51 +0000 Subject: [PATCH 06/17] build fixes --- .pkg | 2 +- .pkg.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pkg b/.pkg index 242ab53e..57c66d8a 100644 --- a/.pkg +++ b/.pkg @@ -69,4 +69,4 @@ [inertialflowcutter] url=git@github.com:motis-project/InertialFlowCutter.git branch=master - commit=90a46297b76f34a99c695c512e2b69196db4e3db \ No newline at end of file + commit=0846850d372be5302151455f02e94130281f0101 \ No newline at end of file diff --git a/.pkg.lock b/.pkg.lock index a7c90253..c8ff4966 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,7 +1,7 @@ -3138434207344735686 +16587491345925743305 cista 02fc087619fc913aca2c3fd3e07ec12279b8d6c6 oneTBB cda5d37ce8303c58ac506259387e75de4ffcf26b -InertialFlowCutter 90a46297b76f34a99c695c512e2b69196db4e3db +InertialFlowCutter 0846850d372be5302151455f02e94130281f0101 zlib-ng d68e1d908e789c31c1f2fafe4bd8e09cb91e21c5 boost 77467bf580d98ea06716e2931cbe3b1f28e0cd37 conf f9bf4bd83bf55a2170725707e526cbacc45dcc66 From dc1709554d9fb94dc52e2e56e763df2c9fd08755 Mon Sep 17 00:00:00 2001 From: Traines Date: Wed, 13 May 2026 10:07:55 +0000 Subject: [PATCH 07/17] build fixes --- .pkg | 2 +- .pkg.lock | 4 ++-- CMakeLists.txt | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.pkg b/.pkg index 57c66d8a..1402cff2 100644 --- a/.pkg +++ b/.pkg @@ -69,4 +69,4 @@ [inertialflowcutter] url=git@github.com:motis-project/InertialFlowCutter.git branch=master - commit=0846850d372be5302151455f02e94130281f0101 \ No newline at end of file + commit=132fff5c953b23986696f7f3947c3c5ed6a0722c \ No newline at end of file diff --git a/.pkg.lock b/.pkg.lock index c8ff4966..5569fb80 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,7 +1,7 @@ -16587491345925743305 +30591291662462833 cista 02fc087619fc913aca2c3fd3e07ec12279b8d6c6 oneTBB cda5d37ce8303c58ac506259387e75de4ffcf26b -InertialFlowCutter 0846850d372be5302151455f02e94130281f0101 +InertialFlowCutter 132fff5c953b23986696f7f3947c3c5ed6a0722c zlib-ng d68e1d908e789c31c1f2fafe4bd8e09cb91e21c5 boost 77467bf580d98ea06716e2931cbe3b1f28e0cd37 conf f9bf4bd83bf55a2170725707e526cbacc45dcc66 diff --git a/CMakeLists.txt b/CMakeLists.txt index 679a46ae..289c9366 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.10) project(osr LANGUAGES C CXX ASM) +set(BUILD_SHARED_LIBS OFF) +option(TBB_BUILD_SHARED "Build TBB shared library" OFF) +option(TBB_BUILD_STATIC "Build TBB static library" ON) + cmake_policy(SET CMP0079 NEW) if (MSVC) From f44d9b8c7f846690b01c054614be7e6345d20afe Mon Sep 17 00:00:00 2001 From: Traines Date: Wed, 13 May 2026 10:26:15 +0000 Subject: [PATCH 08/17] build fixes --- .pkg | 2 +- .pkg.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pkg b/.pkg index 1402cff2..2fc53745 100644 --- a/.pkg +++ b/.pkg @@ -69,4 +69,4 @@ [inertialflowcutter] url=git@github.com:motis-project/InertialFlowCutter.git branch=master - commit=132fff5c953b23986696f7f3947c3c5ed6a0722c \ No newline at end of file + commit=b130a9abc17934e9ce4013ade85806fcdbc75f92 \ No newline at end of file diff --git a/.pkg.lock b/.pkg.lock index 5569fb80..306f024b 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,7 +1,7 @@ -30591291662462833 +17539992508914132447 cista 02fc087619fc913aca2c3fd3e07ec12279b8d6c6 oneTBB cda5d37ce8303c58ac506259387e75de4ffcf26b -InertialFlowCutter 132fff5c953b23986696f7f3947c3c5ed6a0722c +InertialFlowCutter b130a9abc17934e9ce4013ade85806fcdbc75f92 zlib-ng d68e1d908e789c31c1f2fafe4bd8e09cb91e21c5 boost 77467bf580d98ea06716e2931cbe3b1f28e0cd37 conf f9bf4bd83bf55a2170725707e526cbacc45dcc66 From 931ead62d9b7cb07d8ff20401115ae14bf7b20b2 Mon Sep 17 00:00:00 2001 From: Traines Date: Wed, 13 May 2026 10:59:58 +0000 Subject: [PATCH 09/17] build fixes --- .pkg | 2 +- .pkg.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pkg b/.pkg index 2fc53745..bc322ba8 100644 --- a/.pkg +++ b/.pkg @@ -69,4 +69,4 @@ [inertialflowcutter] url=git@github.com:motis-project/InertialFlowCutter.git branch=master - commit=b130a9abc17934e9ce4013ade85806fcdbc75f92 \ No newline at end of file + commit=6a4835a114a67d8a6320cd750684167796e55e37 \ No newline at end of file diff --git a/.pkg.lock b/.pkg.lock index 306f024b..ce82148f 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,7 +1,7 @@ -17539992508914132447 +3055980142366698842 cista 02fc087619fc913aca2c3fd3e07ec12279b8d6c6 oneTBB cda5d37ce8303c58ac506259387e75de4ffcf26b -InertialFlowCutter b130a9abc17934e9ce4013ade85806fcdbc75f92 +InertialFlowCutter 6a4835a114a67d8a6320cd750684167796e55e37 zlib-ng d68e1d908e789c31c1f2fafe4bd8e09cb91e21c5 boost 77467bf580d98ea06716e2931cbe3b1f28e0cd37 conf f9bf4bd83bf55a2170725707e526cbacc45dcc66 From 9943a452b1e574f802941e8e9978a9349f4f597c Mon Sep 17 00:00:00 2001 From: Traines Date: Wed, 13 May 2026 12:54:26 +0000 Subject: [PATCH 10/17] disable IFC by default, tweaks --- .pkg | 2 +- .pkg.lock | 4 ++-- src/ways.cc | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.pkg b/.pkg index bc322ba8..e1a5553f 100644 --- a/.pkg +++ b/.pkg @@ -69,4 +69,4 @@ [inertialflowcutter] url=git@github.com:motis-project/InertialFlowCutter.git branch=master - commit=6a4835a114a67d8a6320cd750684167796e55e37 \ No newline at end of file + commit=73f7b7a744e68201a08bb9137d826d872c9c9f1c \ No newline at end of file diff --git a/.pkg.lock b/.pkg.lock index ce82148f..e2c74304 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,7 +1,7 @@ -3055980142366698842 +280718750575957457 cista 02fc087619fc913aca2c3fd3e07ec12279b8d6c6 oneTBB cda5d37ce8303c58ac506259387e75de4ffcf26b -InertialFlowCutter 6a4835a114a67d8a6320cd750684167796e55e37 +InertialFlowCutter 73f7b7a744e68201a08bb9137d826d872c9c9f1c zlib-ng d68e1d908e789c31c1f2fafe4bd8e09cb91e21c5 boost 77467bf580d98ea06716e2931cbe3b1f28e0cd37 conf f9bf4bd83bf55a2170725707e526cbacc45dcc66 diff --git a/src/ways.cc b/src/ways.cc index 8a010d75..272ebb43 100644 --- a/src/ways.cc +++ b/src/ways.cc @@ -7,7 +7,8 @@ #include "cista/io.h" -#define USE_INERTIAL_FLOW_CUT +// uncomment the following line to enable IFC +// #define USE_INERTIAL_FLOW_CUT #ifdef USE_INERTIAL_FLOW_CUT #include "inertialflowcutter/run.h" #endif @@ -128,8 +129,9 @@ void ways::build_components_and_importance() { v_head.push_back(b.v_); } } + auto const thread_count = 4; run_inertial_flow_cutter( - static_cast(n_nodes()), v_head, v_tail, + thread_count, static_cast(n_nodes()), v_head, v_tail, [&](int i) { auto const p = get_node_pos(node_idx_t{i}); return std::pair{p.lng(), p.lat()}; From e6a69a65d264208cb17be79e6d50c781572509f1 Mon Sep 17 00:00:00 2001 From: Traines Date: Fri, 15 May 2026 09:35:42 +0000 Subject: [PATCH 11/17] update ifc --- .pkg | 2 +- src/ways.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pkg b/.pkg index e1a5553f..8da25f46 100644 --- a/.pkg +++ b/.pkg @@ -69,4 +69,4 @@ [inertialflowcutter] url=git@github.com:motis-project/InertialFlowCutter.git branch=master - commit=73f7b7a744e68201a08bb9137d826d872c9c9f1c \ No newline at end of file + commit=04bc916d16df4dca862f92a1e441252e5775b3e4 \ No newline at end of file diff --git a/src/ways.cc b/src/ways.cc index 272ebb43..a23e80da 100644 --- a/src/ways.cc +++ b/src/ways.cc @@ -130,7 +130,7 @@ void ways::build_components_and_importance() { } } auto const thread_count = 4; - run_inertial_flow_cutter( + ifc::run_inertial_flow_cutter( thread_count, static_cast(n_nodes()), v_head, v_tail, [&](int i) { auto const p = get_node_pos(node_idx_t{i}); From 7640a496964ac10a60ff29315b0d839b2be5afe4 Mon Sep 17 00:00:00 2001 From: Traines Date: Fri, 15 May 2026 09:41:19 +0000 Subject: [PATCH 12/17] verify build --- src/ways.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ways.cc b/src/ways.cc index a23e80da..9379504c 100644 --- a/src/ways.cc +++ b/src/ways.cc @@ -8,7 +8,7 @@ #include "cista/io.h" // uncomment the following line to enable IFC -// #define USE_INERTIAL_FLOW_CUT +#define USE_INERTIAL_FLOW_CUT #ifdef USE_INERTIAL_FLOW_CUT #include "inertialflowcutter/run.h" #endif From 5f8f6e24e99cf3903578626ec0f8bdc427442d27 Mon Sep 17 00:00:00 2001 From: Traines Date: Fri, 15 May 2026 09:50:10 +0000 Subject: [PATCH 13/17] Revert "verify build" This reverts commit 7640a496964ac10a60ff29315b0d839b2be5afe4. --- src/ways.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ways.cc b/src/ways.cc index 9379504c..a23e80da 100644 --- a/src/ways.cc +++ b/src/ways.cc @@ -8,7 +8,7 @@ #include "cista/io.h" // uncomment the following line to enable IFC -#define USE_INERTIAL_FLOW_CUT +// #define USE_INERTIAL_FLOW_CUT #ifdef USE_INERTIAL_FLOW_CUT #include "inertialflowcutter/run.h" #endif From 5e14f9984a10e960b8cf26c4d602897426a2843c Mon Sep 17 00:00:00 2001 From: Traines Date: Tue, 11 Aug 2026 14:00:32 +0000 Subject: [PATCH 14/17] fix tiles --- .pkg.lock | 4 +- web/style.js | 2441 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 1844 insertions(+), 601 deletions(-) diff --git a/.pkg.lock b/.pkg.lock index e2c74304..db28725d 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,7 +1,7 @@ -280718750575957457 +12201350688430969258 cista 02fc087619fc913aca2c3fd3e07ec12279b8d6c6 oneTBB cda5d37ce8303c58ac506259387e75de4ffcf26b -InertialFlowCutter 73f7b7a744e68201a08bb9137d826d872c9c9f1c +InertialFlowCutter 04bc916d16df4dca862f92a1e441252e5775b3e4 zlib-ng d68e1d908e789c31c1f2fafe4bd8e09cb91e21c5 boost 77467bf580d98ea06716e2931cbe3b1f28e0cd37 conf f9bf4bd83bf55a2170725707e526cbacc45dcc66 diff --git a/web/style.js b/web/style.js index dc35d35f..c89474c3 100644 --- a/web/style.js +++ b/web/style.js @@ -55,687 +55,1763 @@ function createShield(opt) { ]; } -const water = "#99ddff"; -const rail = "#a8a8a8"; -const pedestrian = "#e8e7eb"; - -const sport = "#d0f4be"; -const sport_outline = "#b3e998"; - -// const building = "#ff0000"; -const building = "#ded7d3"; -const building_outline = "#cfc8c4"; - -const style = (map, level) => { - const demSource = new mlcontour.DemSource({ - url: 'https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png', - encoding: 'terrarium', - maxzoom: 14, - // offload contour line computation to a web worker - worker: true - }); +// The production MOTIS tile backend (proxied to via index.html's +// transformRequest for tile requests) also serves glyphs/sprites at its +// root, which this dev page has no local copy of. +const PROD_BASE = 'https://europe.motis-project.de/'; + +const LEVEL_MIN_ZOOM = 16; + +// Shortbread `streets` layer: `kind` is the base highway class, links carry +// `link=true` instead of a `*_link` kind, railways share the layer with +// `rail=true` and aeroways appear as kind runway/taxiway. +const colors = { + light: { + background: '#f8f4f0', + + water: '#99ddff', + waterText: 'hsl(205, 56%, 38%)', + glacier: 'rgb(255, 255, 255)', + damPier: 'rgb(249, 244, 238)', + bridgeArea: 'rgb(244, 239, 233)', + bridgeCasing: 'rgb(217, 217, 217)', + bicycleRoad: 'rgb(239, 249, 255)', + housenumber: 'hsla(33, 48%, 15%, 0.6)', + rail: '#a8a8a8', + railOutline: 'rgb(177, 187, 196)', + railCore: 'rgb(197, 204, 211)', + subwayOutline: 'rgb(166, 184, 199)', + subwayCore: 'rgb(188, 202, 213)', + pedestrian: '#e8e7eb', + ferryRoute: 'rgba(102, 102, 255, 0.5)', + + sport: '#d0f4be', + sportOutline: '#b3e998', + + building: '#ded7d3', + buildingOutline: '#cfc8c4', + + landuseComplex: '#f0e6d1', + landuseCommercial: 'hsla(0, 60%, 87%, 0.23)', + landuseIndustrial: '#e0e2eb', + landuseResidential: '#ece7e4', + landuseRetail: 'hsla(0, 60%, 87%, 0.23)', + landuseConstruction: '#a9a9a9', + landuseAgriculture: 'rgb(240, 231, 209)', + siteHospital: 'rgb(255, 102, 102)', + + landusePark: '#b8ebad', + landuseNatureLight: '#cdedc0', + landuseNatureHeavy: '#90ee90', + landuseCemetery: '#e0e4dd', + landuseBeach: '#fffcd3', + + indoorCorridor: '#fdfcfa', + indoor: '#d4edff', + indoorOutline: '#808080', + indoorText: '#333333', + + publicTransport: 'rgba(218,140,140,0.3)', + + footway: 'rgb(252, 251, 250)', + footwayOutline: 'rgb(206, 202, 199)', + track: '#c0aa77', + livingStreet: 'rgb(248, 245, 244)', + footpath: 'rgb(160, 160, 160)', + cycleway: 'rgb(100, 145, 205)', + unclassified: 'rgb(232, 230, 227)', + steps: '#ff4524', + + elevatorOutline: '#808080', + elevator: '#bcf1ba', + + roadBackResidential: '#ffffff', + roadBackNonResidential: '#ffffff', + + motorway: '#ffb366', + motorwayLink: '#f7e06e', + primarySecondary: '#fffbf8', + linkTertiary: '#ffffff', + residential: '#ffffff', + road: '#ffffff', + + townText: '#333333', + townTextHalo: 'rgba(255, 255, 255, 0.8)', + text: '#333333', + textHalo: 'rgba(255, 255, 255, 0.8)', + citiesText: 'hsl(0, 0%, 20%)', + citiesTextHalo: 'rgba(255, 255, 255, 0.8)', - demSource.setupMaplibre(maplibregl); + shield: 'shield' + }, + dark: { + background: '#292929', + + water: '#1f2830', + waterText: 'hsl(205, 40%, 65%)', + glacier: 'hsl(0, 0%, 10%)', + damPier: 'hsl(33, 48%, 5%)', + bridgeArea: 'rgb(22, 17, 11)', + bridgeCasing: 'rgb(38, 38, 38)', + bicycleRoad: 'hsl(203, 100%, 3%)', + housenumber: 'hsla(33, 48%, 90%, 0.6)', + rail: '#808080', + railOutline: 'hsl(208, 14%, 27%)', + railCore: 'rgb(44, 52, 59)', + subwayOutline: 'hsl(207, 23%, 28%)', + subwayCore: 'rgb(42, 55, 67)', + pedestrian: '#292929', + ferryRoute: 'rgba(58, 77, 139, 0.5)', + + sport: '#272525', + sportOutline: '#272525', + + building: '#1F1F1F', + buildingOutline: '#1A1A1A', + + landuseComplex: '#292929', + landuseCommercial: '#292929', + landuseIndustrial: '#353538', + landuseResidential: '#292929', + landuseRetail: '#292929', + landuseConstruction: 'hsl(0, 0%, 34%)', + landuseAgriculture: 'hsl(43, 51%, 12%)', + siteHospital: 'hsl(0, 100%, 30%)', + + landusePark: '#18221f', + landuseNatureLight: '#1e2322', + landuseNatureHeavy: '#1a2020', + landuseCemetery: '#202423', + landuseBeach: '#4c4b3e', + + indoorCorridor: '#494949', + indoor: '#1a1a1a', + indoorOutline: '#0d0d0d', + indoorText: '#eeeeee', + + publicTransport: 'rgba(89, 45, 45, 0.405)', + + footway: 'rgb(30, 30, 30)', + footwayOutline: 'rgb(62, 62, 62)', + track: '#483d24', + livingStreet: 'rgb(46, 46, 46)', + footpath: 'rgb(80, 80, 80)', + cycleway: 'rgb(110, 150, 205)', + unclassified: 'rgb(50, 50, 50)', + steps: '#70504b', + + elevatorOutline: '#808080', + elevator: '#3b423b', + + roadBackResidential: '#414141', + roadBackNonResidential: '#414141', + + motorway: '#414141', + motorwayLink: '#414141', + primarySecondary: '#414141', + linkTertiary: '#414141', + residential: '#414141', + road: '#414141', + + text: '#9a9a9a', + textHalo: 'rgba(0, 0, 0, 0.8)', + townText: '#bebebe', + townTextHalo: 'rgba(0, 0, 0, 0.8)', + citiesText: '#bebebe', + citiesTextHalo: 'rgba(0, 0, 0, 0.8)', + + shield: 'shield-dark' + } +}; + +const style = (map, level, theme) => { + const c = colors[theme === 'dark' ? 'dark' : 'light']; map.setStyle({ - "version": 8, - "sources": { - "osm": { - "type": "vector", - "tiles": ["/{z}/{x}/{y}.mvt"], - "maxzoom": 20 - }, - "hillshadeSource": { - type: "raster-dem", - encoding: "terrarium", - tiles: [ - "https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png" - ], - tileSize: 256, - minzoom: 0, - maxzoom: 14 - }, - "terrainSource": { - type: "raster-dem", - encoding: "terrarium", - tiles: [ - "https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png" - ], - tileSize: 256, - minzoom: 0, - maxzoom: 14 - }, - "contourSourceFeet": { - "type": 'vector', - "tiles": [ - demSource.contourProtocolUrl({ - // meters to feet - "overzoom": 1, - "thresholds": { - // zoom: [minor, major] - 11: [200, 1000], - 12: [100, 500], - 13: [100, 500], - 14: [50, 200], - 15: [20, 100] - }, - "elevationKey": 'ele', - "levelKey": 'level', - "contourLayer": 'contours' - }) - ], - "maxzoom": 15 + version: 8, + sources: { + osm: { + type: 'vector', + tiles: ['/{z}/{x}/{y}.mvt'], + maxzoom: 20, + attribution: '© OpenStreetMap contributors' } }, - "glyphs": "/glyphs/{fontstack}/{range}.pbf", - "sprite": `${window.location.origin}/sprite`, - "layers": [ - { - "id": "background", - "type": "background", - "paint": {"background-color": "#f8f4f0"} - }, { - "id": "coastline", - "type": "fill", - "source": "osm", - "source-layer": "coastline", - "paint": {"fill-color": water} - }, - { - "id": "landuse_park", - "type": "fill", - "source": "osm", - "source-layer": "landuse", - "filter": ["==", ["get", "landuse"], "park"], - "paint": { - "fill-color": "#b8ebad", - // "fill-outline-color": "rgba(95, 208, 100, 1)" - } - }, - { - "id": "landuse", - "type": "fill", - "source": "osm", - "source-layer": "landuse", - "filter": [ - "!in", - "landuse", - "park", - "public_transport" - ], - "paint": { - "fill-color": ["match", ["get", "landuse"], - "complex", "#f0e6d1", - "commercial", "hsla(0, 60%, 87%, 0.23)", - "industrial", "#e0e2eb", - "residential", "#ece7e4", - "retail", "hsla(0, 60%, 87%, 0.23)", - "construction", "#aaa69d", - - "nature_light", "#ddecd7", - "nature_heavy", "#a3e39c", - "cemetery", "#e0e4dd", - "beach", "#fffcd3", - - "magenta"] - } - }, - { - "id": "water", - "type": "fill", - "source": "osm", - "source-layer": "water", - "paint": {"fill-color": water} - }, - { - "id": "sport", - "type": "fill", - "source": "osm", - "source-layer": "sport", - "paint": { - "fill-color": sport, - "fill-outline-color": sport_outline - } - }, - { - "id": "pedestrian", - "type": "fill", - "source": "osm", - "source-layer": "pedestrian", - "paint": {"fill-color": pedestrian} - }, - { - "id": "waterway", - "type": "line", - "source": "osm", - "source-layer": "waterway", - "paint": {"line-color": water} - }, - { - "id": "building", - "type": "fill", - "source": "osm", - "source-layer": "building", - "paint": { - "fill-color": building, - "fill-outline-color": building_outline, - "fill-opacity": - ["interpolate", ["linear"], ["zoom"], - 14, 0, - 16, 0.8, - ] + glyphs: `${PROD_BASE}glyphs/{fontstack}/{range}.pbf`, + // MOTIS icons (elevator, shields) from the local sprite; the `basics` + // sprite (from VersaTiles) carries fill patterns (basics:pattern-*) + // and markings (basics:marking-*) and isn't shipped with this repo, + // so it's loaded from the production tile backend. + sprite: [ + { id: 'default', url: `${PROD_BASE}/sprite` }, + { id: 'basics', url: `${PROD_BASE}sprites/basics/sprites` } + ], + layers: [ + { + id: 'background', + type: 'background', + paint: { 'background-color': c.background } + }, + { + id: 'coastline', + type: 'fill', + source: 'osm', + 'source-layer': 'coastline', + paint: { 'fill-color': c.water } + }, + { + id: 'landuse_park', + type: 'fill', + source: 'osm', + 'source-layer': 'land', + filter: [ + 'in', + 'kind', + 'park', + 'garden', + 'playground', + 'miniature_golf', + 'golf_course', + 'village_green', + 'recreation_ground', + 'greenhouse_horticulture', + 'allotments' + ], + paint: { + 'fill-color': c.landusePark, + 'fill-opacity': ['interpolate', ['linear'], ['zoom'], 8, 0.6, 10, 1] } }, { - "id": "indoor-corridor", - "type": "fill", - "source": "osm", - "source-layer": "indoor", - "filter": [ - "all", - ["==", "indoor", "corridor"], - ["==", "level", level] + id: 'landuse', + type: 'fill', + source: 'osm', + 'source-layer': 'land', + filter: [ + '!in', + 'kind', + 'park', + 'garden', + 'playground', + 'miniature_golf', + 'golf_course', + 'village_green', + 'recreation_ground', + 'greenhouse_horticulture', + 'allotments', + 'public_transport' ], - "paint": { - "fill-color": "#fdfcfa", - "fill-opacity": 0.8 + paint: { + 'fill-color': [ + 'match', + ['get', 'kind'], + 'commercial', + c.landuseCommercial, + ['industrial', 'railway', 'quarry', 'farmyard', 'landfill', 'garages'], + c.landuseIndustrial, + 'residential', + c.landuseResidential, + 'retail', + c.landuseRetail, + // agriculture group in the VersaTiles Shortbread style + ['brownfield', 'greenfield'], + c.landuseAgriculture, + ['bare_rock', 'scree', 'shingle'], + c.landuseConstruction, + + [ + 'farmland', + 'vineyard', + 'plant_nursery', + 'orchard', + 'meadow', + 'grassland', + 'grass', + 'heath', + 'swamp', + 'bog', + 'string_bog', + 'wet_meadow', + 'marsh' + ], + c.landuseNatureLight, + ['forest', 'scrub'], + c.landuseNatureHeavy, + ['cemetery', 'grave_yard'], + c.landuseCemetery, + ['beach', 'sand'], + c.landuseBeach, + + 'magenta' + ], + // soft fade-in like VersaTiles land layers + 'fill-opacity': ['interpolate', ['linear'], ['zoom'], 8, 0.6, 10, 1] + } + }, + { + id: 'sites', + type: 'fill', + source: 'osm', + 'source-layer': 'sites', + // construction is rendered by its own pattern layer below + filter: ['!=', 'kind', 'construction'], + paint: { + 'fill-color': [ + 'match', + ['get', 'kind'], + ['university', 'school', 'college', 'prison'], + c.landuseComplex, + // like VersaTiles: translucent red + 'hospital', + c.siteHospital, + 'sports_center', + c.sport, + 'danger_area', + c.landuseConstruction, + ['parking', 'bicycle_parking'], + c.pedestrian, + + 'magenta' + ], + 'fill-opacity': ['match', ['get', 'kind'], 'hospital', 0.1, 1] + } + }, + { + // like the VersaTiles Shortbread style: faint thin hatching + id: 'sites-construction', + type: 'fill', + source: 'osm', + 'source-layer': 'sites', + filter: ['==', 'kind', 'construction'], + paint: { + 'fill-color': c.landuseConstruction, + 'fill-pattern': 'basics:pattern-hatched_thin', + 'fill-opacity': 0.1 + } + }, + { + id: 'water', + type: 'fill', + source: 'osm', + 'source-layer': 'water_polygons', + paint: { + // glaciers white like in VersaTiles + 'fill-color': ['match', ['get', 'kind'], 'glacier', c.glacier, c.water] + } + }, + // dams and piers like in VersaTiles + { + id: 'dam-area', + type: 'fill', + source: 'osm', + 'source-layer': 'dam_polygons', + paint: { + 'fill-color': c.damPier, + 'fill-opacity': ['interpolate', ['linear'], ['zoom'], 12, 0, 13, 1] + } + }, + { + id: 'dam-line', + type: 'line', + source: 'osm', + 'source-layer': 'dam_lines', + layout: { 'line-cap': 'round', 'line-join': 'round' }, + paint: { 'line-color': c.water } + }, + { + id: 'pier-area', + type: 'fill', + source: 'osm', + 'source-layer': 'pier_polygons', + paint: { + 'fill-color': c.damPier, + 'fill-opacity': ['interpolate', ['linear'], ['zoom'], 12, 0, 13, 1] + } + }, + { + id: 'pier-line', + type: 'line', + source: 'osm', + 'source-layer': 'pier_lines', + layout: { 'line-cap': 'round', 'line-join': 'round' }, + paint: { 'line-color': c.damPier } + }, + { + id: 'pedestrian', + type: 'fill', + source: 'osm', + 'source-layer': 'street_polygons', + filter: ['in', 'kind', 'pedestrian', 'service'], + paint: { 'fill-color': c.pedestrian } + }, + { + id: 'waterway', + type: 'line', + source: 'osm', + 'source-layer': 'water_lines', + layout: { 'line-cap': 'round', 'line-join': 'round' }, + paint: { + 'line-color': c.water, + // width hierarchy from VersaTiles: river > canal > stream > ditch + 'line-width': [ + 'interpolate', + ['linear'], + ['zoom'], + 9, + ['match', ['get', 'kind'], 'river', 0.5, 'canal', 0.5, 0], + 10, + ['match', ['get', 'kind'], 'river', 3, 'canal', 2, 0], + 14, + ['match', ['get', 'kind'], 'river', 4.6, 'canal', 3.6, 'stream', 1, 0.5], + 15, + ['match', ['get', 'kind'], 'river', 5, 'canal', 4, 'stream', 2, 'ditch', 1, 1], + 17, + ['match', ['get', 'kind'], 'river', 9, 'canal', 8, 'stream', 6, 'ditch', 4, 2], + 18, + ['match', ['get', 'kind'], 'river', 20, 'canal', 17, 'stream', 12, 'ditch', 8, 4], + 20, + ['match', ['get', 'kind'], 'river', 60, 'canal', 50, 'stream', 30, 'ditch', 20, 10] + ] + } + }, + // buildings like in VersaTiles: darker footprint + body shifted up-left + // by 2px for a pseudo-2.5d effect, fading in at z14-15 + { + id: 'building-outline', + type: 'fill', + source: 'osm', + 'source-layer': 'buildings', + paint: { + 'fill-color': c.buildingOutline, + 'fill-opacity': ['interpolate', ['linear'], ['zoom'], 14, 0, 15, 1] + } + }, + { + id: 'building', + type: 'fill', + source: 'osm', + 'source-layer': 'buildings', + paint: { + 'fill-color': c.building, + 'fill-opacity': ['interpolate', ['linear'], ['zoom'], 14, 0, 15, 1], + 'fill-translate': [-2, -2] + } + }, + // bridge decks (man_made=bridge areas) like in VersaTiles + { + id: 'bridge-area', + type: 'fill', + source: 'osm', + 'source-layer': 'bridges', + paint: { + 'fill-color': c.bridgeArea, + 'fill-opacity': 0.8 + } + }, + // casing under major streets on bridges (VersaTiles bridge outlines) + { + id: 'bridge-casing', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + minzoom: 12, + filter: [ + 'all', + ['==', ['get', 'bridge'], true], + ['==', ['get', 'rail'], false], + [ + '!', + [ + 'in', + ['get', 'kind'], + [ + 'literal', + [ + 'footway', + 'track', + 'steps', + 'cycleway', + 'path', + 'unclassified', + 'service', + 'pedestrian' + ] + ] + ] + ], + [ + 'any', + ['!', ['to-boolean', ['get', 'link']]], + ['all', ['==', ['get', 'kind'], 'motorway'], ['>=', ['zoom'], 12]], + ['>=', ['zoom'], 13] + ] + ], + layout: { + 'line-cap': 'butt' + }, + paint: { + 'line-color': c.bridgeCasing, + 'line-width': [ + 'let', + 'base', + [ + 'case', + ['to-boolean', ['get', 'link']], + [ + 'match', + ['get', 'kind'], + 'motorway', + 3.5, + 'trunk', + 3, + ['primary', 'secondary', 'tertiary'], + 1.75, + 0.0 + ], + [ + 'match', + ['get', 'kind'], + 'motorway', + 4, + 'trunk', + 3.5, + ['primary', 'secondary', 'runway', 'taxiway'], + 3, + 'tertiary', + 1.75, + 'residential', + 1.5, + 0.75 + ] + ], + [ + 'interpolate', + ['linear'], + ['zoom'], + 12, + ['+', ['*', ['var', 'base'], 1], 2.5], + 16, + ['+', ['*', ['var', 'base'], 4], 2.5], + 20, + ['+', ['*', ['var', 'base'], 8], 2.5] + ] + ] + } + }, + { + id: 'road_back_residential', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + filter: ['==', 'kind', 'residential'], + layout: { + 'line-cap': 'round' + }, + paint: { + 'line-color': c.roadBackResidential, + 'line-width': ['interpolate', ['linear'], ['zoom'], 5, 0, 9, 0.5, 12, 1, 16, 4, 20, 20], + // dimmed in tunnels + 'line-opacity': [ + 'interpolate', + ['linear'], + ['zoom'], + 12, + ['case', ['==', ['get', 'tunnel'], true], 0.15, 0.4], + 15, + ['case', ['==', ['get', 'tunnel'], true], 0.4, 1] + ] } }, + // minor ways (track / living street / service / unclassified) drawn here, + // below the bigger streets (primary etc.): the big-road bodies paint over + // them at junctions, while unclassified sits on top of track / living + // street / service + // tracks as a thin, grey plain line (no casing), fading in at z13-14 + // so field and forest ways show up early { - "id": "indoor", - "type": "fill", - "source": "osm", - "source-layer": "indoor", - "filter": [ - "all", - ["!in", "indoor", "corridor", "wall", "elevator"], - ["==", "level", level] + id: 'track', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + filter: [ + 'all', + ['==', 'kind', 'track'], + level === 0 ? ['any', ['!has', 'level'], ['==', 'level', level]] : ['==', 'level', level] ], - "paint": { - "fill-color": "#d4edff", - "fill-opacity": 0.8 + layout: { + 'line-cap': 'round' + }, + minzoom: 13, + paint: { + 'line-color': c.track, + 'line-width': [ + 'interpolate', + ['linear'], + ['zoom'], + 13, + 0, + 14, + 0.9, + 16, + 1.5, + 18, + 3, + 19, + 4, + 20, + 6 + ] } }, + // living street: a plain solid body like the old service look, no casing { - "id": "indoor-outline", - "type": "line", - "source": "osm", - "source-layer": "indoor", - "filter": [ - "all", - ["!in", "indoor", "corridor", "wall", "elevator"], - ["==", "level", level] + id: 'living-street', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + filter: [ + 'all', + ['==', 'kind', 'living_street'], + level === 0 ? ['any', ['!has', 'level'], ['==', 'level', level]] : ['==', 'level', level] ], - "minzoom": 18, - "paint": { - "line-color": "#808080", - "line-width": 2 + layout: { + 'line-cap': 'round' + }, + minzoom: 15, + paint: { + 'line-color': c.livingStreet, + 'line-width': ['interpolate', ['linear'], ['zoom'], 15, 0, 16, 4, 18, 6, 19, 10, 20, 20] } }, + // service as a plain solid line (no casing), thinner than living street, + // fading in at z13-14 like tracks and footpaths { - "id": "indoor-names", - "type": "symbol", - "source": "osm", - "source-layer": "indoor", - "minzoom": 18, - "filter": ["any", ["!has", "level"], ["==", "level", level]], - "layout": { - "symbol-placement": "point", - "text-field": ["get", "name"], - "text-font": ["Noto Sans Display Regular"], - "text-size": 12, + id: 'footway', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + filter: [ + 'all', + ['==', 'kind', 'service'], + level === 0 ? ['any', ['!has', 'level'], ['==', 'level', level]] : ['==', 'level', level] + ], + layout: { + 'line-cap': 'round' }, - "paint": { - "text-color": "#333333" + minzoom: 13, + paint: { + 'line-color': c.footway, + 'line-width': [ + 'interpolate', + ['linear'], + ['zoom'], + 13, + 0, + 14, + 1, + 16, + 2.5, + 18, + 4, + 19, + 6, + 20, + 12 + ] } }, + // unclassified as a plain borderless line, slightly grey vs the white + // residential roads { - "id": "landuse-public-transport", - "type": "fill", - "source": "osm", - "source-layer": "landuse", - "filter": [ - "all", - ["==", "landuse", "public_transport"], - ["any", ["!has", "level"], ["==", "level", level]] + id: 'unclassified', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + filter: [ + 'all', + ['==', 'kind', 'unclassified'], + level === 0 ? ['any', ['!has', 'level'], ['==', 'level', level]] : ['==', 'level', level] ], - "paint": { - "fill-color": "rgba(218,140,140,0.3)" + layout: { + 'line-cap': 'round' + }, + minzoom: 14, + paint: { + 'line-color': c.unclassified, + 'line-width': [ + 'interpolate', + ['linear'], + ['zoom'], + 14, + 2, + 15, + 4, + 16, + 6, + 18, + 10, + 19, + 16, + 20, + 24 + ] } }, { - "id": "footway", - "type": "line", - "source": "osm", - "source-layer": "road", - "filter": [ - "all", + id: 'road_back_non_residential', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + // link zoom gating like VersaTiles: motorway_link from z12, other + // links from z13 (the tiles carry them from z9). + // minzoom 6 like the road layer: rail first, motorways one zoom later. + minzoom: 6, + filter: [ + 'all', + ['==', ['get', 'rail'], false], [ - "in", - "highway", - "footway", "track", "cycleway", "path", "unclassified", "service" - ], - level === 0 - ? - ["any", - ["!has", "level"], - ["==", "level", level] + '!', + [ + 'in', + ['get', 'kind'], + [ + 'literal', + [ + 'footway', + 'track', + 'steps', + 'cycleway', + 'path', + 'unclassified', + 'residential', + 'service', + 'living_street' + ] + ] ] - : - ["==", "level", level] + ], + [ + 'any', + ['!', ['to-boolean', ['get', 'link']]], + ['all', ['==', ['get', 'kind'], 'motorway'], ['>=', ['zoom'], 12]], + ['>=', ['zoom'], 13] + ] ], - "layout": { - "line-cap": "round", + layout: { + 'line-cap': 'round' }, - "minzoom": 14, - "paint": { - "line-dasharray": [ 0.75, 1.5 ], - "line-color": "#fff", - "line-opacity": 0.5, - "line-width": [ - "let", - "base", 0.4, - ["interpolate", ["linear"], ["zoom"], - 5, ["+", ["*", ["var", "base"], 0.1], 1], - 9, ["+", ["*", ["var", "base"], 0.4], 1], - 12, ["+", ["*", ["var", "base"], 1], 1], - 16, ["+", ["*", ["var", "base"], 4], 1], - 20, ["+", ["*", ["var", "base"], 8], 1] + paint: { + 'line-color': c.roadBackNonResidential, + // dimmed in tunnels + 'line-opacity': ['case', ['==', ['get', 'tunnel'], true], 0.4, 1], + 'line-width': [ + 'let', + 'base', + [ + 'case', + ['to-boolean', ['get', 'link']], + [ + 'match', + ['get', 'kind'], + 'motorway', + 3.5, + 'trunk', + 3, + ['primary', 'secondary', 'tertiary'], + 1.75, + 0.0 + ], + [ + 'match', + ['get', 'kind'], + 'motorway', + 4, + 'trunk', + 3.5, + ['primary', 'secondary', 'runway', 'taxiway'], + 3, + 'tertiary', + 1.75, + 0.0 + ] + ], + [ + 'interpolate', + ['linear'], + ['zoom'], + 5, + ['+', ['*', ['var', 'base'], 0.1], 1], + 9, + ['+', ['*', ['var', 'base'], 0.4], 1], + 12, + ['+', ['*', ['var', 'base'], 1], 1], + 16, + ['+', ['*', ['var', 'base'], 4], 1], + 20, + ['+', ['*', ['var', 'base'], 8], 1] ] ] } }, { - "id": "steps", - "type": "line", - "source": "osm", - "source-layer": "road", - "minzoom": 18, - "filter": [ - "all", - ["==", "highway", "steps"], - level === 0 - ? - ["any", - ["!has", "from_level"], - ["any", - ["==", "from_level", level], - ["==", "to_level", level] + id: 'road', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + layout: { + 'line-cap': 'round' + }, + // like the old release: only roads with a ref get the full-width + // opaque body here; everything else is carried by the faint back + // layers (thin at overview zooms) and the minor-way layers. + // minzoom 6: rail comes first, motorway/trunk one zoom later (only + // those two kinds exist below z6 in the tiles). + minzoom: 6, + filter: [ + 'all', + ['has', 'ref'], + ['==', ['get', 'rail'], false], + [ + '!', + [ + 'in', + ['get', 'kind'], + [ + 'literal', + [ + 'footway', + 'track', + 'steps', + 'cycleway', + 'path', + 'unclassified', + 'service', + 'pedestrian' + ] ] ] - : - ["any", - ["==", "from_level", level], - ["==", "to_level", level] - ] + ], + [ + 'any', + ['!', ['to-boolean', ['get', 'link']]], + ['all', ['==', ['get', 'kind'], 'motorway'], ['>=', ['zoom'], 12]], + ['>=', ['zoom'], 13] + ], + [ + 'any', + ['==', ['get', 'kind'], 'motorway'], + ['==', ['get', 'kind'], 'trunk'], + ['==', ['get', 'kind'], 'secondary'], + ['>', ['zoom'], 11] + ] ], - "paint": { - "line-dasharray": [ 0.5, 0.5 ], - "line-color": "#ff4524", - "line-opacity": 1, - "line-width": [ - "let", - "base", 0.4, - ["interpolate", ["linear"], ["zoom"], - 5, ["+", ["*", ["var", "base"], 0.1], 1], - 9, ["+", ["*", ["var", "base"], 0.4], 1], - 12, ["+", ["*", ["var", "base"], 1], 1], - 16, ["+", ["*", ["var", "base"], 4], 1], - 20, ["+", ["*", ["var", "base"], 8], 1] + paint: { + // dimmed in tunnels + 'line-opacity': ['case', ['==', ['get', 'tunnel'], true], 0.5, 1], + // like VersaTiles: links get the same color as their parent kind + // (motorway_link = motorway, ...), only the width is reduced + 'line-color': [ + 'match', + ['get', 'kind'], + 'motorway', + c.motorway, + 'trunk', + c.motorwayLink, + ['primary', 'secondary', 'runway', 'taxiway'], + c.primarySecondary, + 'tertiary', + c.linkTertiary, + 'residential', + c.residential, + c.road + ], + 'line-width': [ + 'let', + 'base', + [ + 'case', + ['to-boolean', ['get', 'link']], + [ + 'match', + ['get', 'kind'], + 'motorway', + 3, + 'trunk', + 2.5, + ['primary', 'secondary', 'tertiary'], + 1.75, + 0.75 + ], + [ + 'match', + ['get', 'kind'], + 'motorway', + 3.5, + 'trunk', + 3, + ['primary', 'secondary', 'runway', 'taxiway'], + 2.5, + 'tertiary', + 1.75, + 'residential', + 1.5, + 0.75 + ] + ], + [ + 'interpolate', + ['linear'], + ['zoom'], + 5, + ['*', ['var', 'base'], 0.5], + 9, + ['*', ['var', 'base'], 1], + 12, + ['*', ['var', 'base'], 2], + 16, + ['*', ['var', 'base'], 2.5], + 20, + ['*', ['var', 'base'], 3] ] ] } }, + // streets with designated bicycle infrastructure, tinted like VersaTiles { - "id": "indoor-elevator-outline", - "type": "circle", - "source": "osm", - "source-layer": "indoor", - "minzoom": 18, - "filter": [ - "all", - ["==", "indoor", "elevator"], - ["<=", "from_level", level], - [">=", "to_level", level] + id: 'road-bicycle', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + minzoom: 13, + filter: [ + 'all', + ['==', 'bicycle', 'designated'], + [ + 'in', + 'kind', + 'residential', + 'unclassified', + 'service', + 'track', + 'living_street', + 'pedestrian' + ] ], - "paint": { - 'circle-color': "#808080", - 'circle-radius': 16, + layout: { + 'line-cap': 'round' + }, + paint: { + 'line-color': c.bicycleRoad, + 'line-width': ['interpolate', ['linear'], ['zoom'], 12, 1, 14, 2, 16, 4, 18, 12, 20, 40] } }, + // one-way arrows like in VersaTiles { - "id": "indoor-elevator", - "type": "circle", - "source": "osm", - "source-layer": "indoor", - "minzoom": 18, - "filter": [ - "all", - ["==", "indoor", "elevator"], - ["<=", "from_level", level], - [">=", "to_level", level] + id: 'marking-oneway', + type: 'symbol', + source: 'osm', + 'source-layer': 'streets', + minzoom: 16, + filter: [ + 'all', + ['==', 'oneway', true], + [ + 'in', + 'kind', + 'trunk', + 'primary', + 'secondary', + 'tertiary', + 'unclassified', + 'residential', + 'living_street' + ] ], - "paint": { - 'circle-color': '#bcf1ba', - 'circle-radius': 14, + layout: { + 'symbol-placement': 'line', + 'symbol-spacing': 175, + 'icon-rotate': 90, + 'icon-rotation-alignment': 'map', + 'icon-padding': 5, + 'symbol-avoid-edges': true, + 'icon-image': 'basics:marking-arrow' + }, + paint: { + 'icon-color': c.text, + 'icon-opacity': ['interpolate', ['linear'], ['zoom'], 16, 0, 17, 0.4] } }, { - "id": "indoor-elevator-icon", - "type": "symbol", - "source": "osm", - "source-layer": "indoor", - "minzoom": 18, - "filter": [ - "all", - ["==", "indoor", "elevator"], - ["<=", "from_level", level], - [">=", "to_level", level] + id: 'marking-oneway-reverse', + type: 'symbol', + source: 'osm', + 'source-layer': 'streets', + minzoom: 16, + filter: [ + 'all', + ['==', 'oneway_reverse', true], + [ + 'in', + 'kind', + 'trunk', + 'primary', + 'secondary', + 'tertiary', + 'unclassified', + 'residential', + 'living_street' + ] ], - "layout": { - "icon-image": "elevator", - "icon-size": 0.9 + layout: { + 'symbol-placement': 'line', + 'symbol-spacing': 75, + 'icon-rotate': -90, + 'icon-rotation-alignment': 'map', + 'icon-padding': 5, + 'symbol-avoid-edges': true, + 'icon-image': 'basics:marking-arrow' + }, + paint: { + 'icon-color': c.text, + 'icon-opacity': ['interpolate', ['linear'], ['zoom'], 16, 0, 17, 0.4] } }, { - "id": "road_back_residential", - "type": "line", - "source": "osm", - "source-layer": "road", - "filter": [ "==", "highway", "residential" ], - "layout": { - "line-cap": "round", - }, - "paint": { - "line-color": "#ffffff", - "line-width": ["interpolate", ["linear"], ["zoom"], - 5, 0, - 9, 0.5, - 12, 1, - 16, 4, - 20, 20 - ], - "line-opacity": ["interpolate", ["linear"], ["zoom"], - 12, 0.4, - 15, 1 + id: 'ferry_routes', + type: 'line', + source: 'osm', + 'source-layer': 'ferries', + paint: { + 'line-width': 1.5, + 'line-dasharray': [2, 3], + 'line-color': c.ferryRoute + } + }, + // rail / tram rendering like the VersaTiles Shortbread style: a solid + // "roadbed" outline with a dashed core on top for heavy rail, thin lines + // with crosstie dashes for tram-like kinds, own colors for subways. + { + id: 'rail-outline', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + // wider and much earlier than VersaTiles (minzoom 8, 1px until z15) + // so the main rail network already stands out at country level. + // Fully visible from z5 — one zoom before motorways (their layers + // have minzoom 6). + minzoom: 5, + filter: [ + 'all', + ['==', ['get', 'kind'], 'rail'], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] ] + ], + paint: { + 'line-color': c.railOutline, + 'line-width': ['interpolate', ['linear'], ['zoom'], 5, 1.5, 8, 2, 13, 2.4, 15, 3, 20, 14], + 'line-opacity': ['case', ['==', ['get', 'tunnel'], true], 0.3, 1] } }, { - "id": "road_back_non_residential", - "type": "line", - "source": "osm", - "source-layer": "road", - "filter": [ - "!in", - "highway", - "footway", "track", "steps", "cycleway", "path", "unclassified", "residential", "service" + id: 'lightrail-outline', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + minzoom: 8, + filter: [ + 'all', + ['==', ['get', 'kind'], 'light_rail'], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] ], - "layout": { - "line-cap": "round", - }, - "paint": { - "line-color": "#ffffff", - "line-width": [ - "let", - "base", ["match", ["get", "highway"], - "motorway", 4, - ["trunk", "motorway_link"], 3.5, - ["primary", "secondary", "aeroway", "trunk_link"], 3, - ["primary_link", "secondary_link", "tertiary", "tertiary_link"], 1.75, - 0.0], - ["interpolate", ["linear"], ["zoom"], - 5, ["+", ["*", ["var", "base"], 0.1], 1], - 9, ["+", ["*", ["var", "base"], 0.4], 1], - 12, ["+", ["*", ["var", "base"], 1], 1], - 16, ["+", ["*", ["var", "base"], 4], 1], - 20, ["+", ["*", ["var", "base"], 8], 1] - ] + paint: { + 'line-color': c.railOutline, + 'line-width': ['interpolate', ['linear'], ['zoom'], 8, 1, 13, 1, 15, 1, 20, 14], + 'line-opacity': [ + 'interpolate', + ['linear'], + ['zoom'], + 11, + 0, + 12, + ['case', ['==', ['get', 'tunnel'], true], 0.5, 1] ] } }, { - "id": "road", - "type": "line", - "source": "osm", - "source-layer": "road", - "layout": { - "line-cap": "round", - }, - "filter": ["all", - ["has", "ref"], - ["any", ["==", ["get", "highway"], "motorway"], - ["==", ["get", "highway"], "trunk"], - ["==", ["get", "highway"], "secondary"], - [">", ["zoom"], 11]]], - "paint": { - "line-color": ["match", ["get", "highway"], - "motorway", "#ffb366", - ["trunk", "motorway_link"], "#f7e06e", - ["primary", "secondary", "aeroway", "trunk_link"], "#fffbf8", - ["primary_link", "secondary_link", "tertiary", "tertiary_link"], "#ffffff", - "residential", "#ffffff", - "#ffffff"], - "line-width": [ - "let", - "base", ["match", ["get", "highway"], - "motorway", 3.5, - ["trunk", "motorway_link"], 3, - ["primary", "secondary", "aeroway", "trunk_link"], 2.5, - ["primary_link", "secondary_link", "tertiary", "tertiary_link"], 1.75, - "residential", 1.5, - 0.75], - ["interpolate", ["linear"], ["zoom"], - 5, ["*", ["var", "base"], 0.5], - 9, ["*", ["var", "base"], 1], - 12, ["*", ["var", "base"], 2], - 16, ["*", ["var", "base"], 2.5], - 20, ["*", ["var", "base"], 3] - ] + id: 'rail-service-outline', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + minzoom: 14, + filter: [ + 'all', + ['in', ['get', 'kind'], ['literal', ['rail', 'light_rail']]], + ['has', 'service'], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ], + paint: { + 'line-color': c.railOutline, + 'line-width': ['interpolate', ['linear'], ['zoom'], 14, 0, 15, 1, 16, 1, 20, 14], + 'line-opacity': ['case', ['==', ['get', 'tunnel'], true], 0.3, 1] + } + }, + { + id: 'subway-outline', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + filter: [ + 'all', + ['==', ['get', 'kind'], 'subway'], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] ] + ], + paint: { + 'line-color': c.subwayOutline, + 'line-width': [ + 'interpolate', + ['linear'], + ['zoom'], + 11, + 0, + 12, + 1, + 15, + 3, + 16, + 3, + 18, + 6, + 19, + 8, + 20, + 10 + ], + 'line-opacity': ['interpolate', ['linear'], ['zoom'], 11, 0, 12, 1] } }, { - "id": "rail_old", - "type": "line", - "source": "osm", - "source-layer": "rail", - "filter": ["==", "rail", "old"], - "paint": { - "line-color": rail, + id: 'tram-outline', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + minzoom: 15, + filter: [ + 'all', + ['in', ['get', 'kind'], ['literal', ['tram', 'narrow_gauge', 'monorail', 'funicular']]], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ], + paint: { + 'line-color': c.railOutline, + 'line-width': ['interpolate', ['linear'], ['zoom'], 15, 0, 16, 5, 18, 7, 20, 20], + 'line-dasharray': [0.1, 0.5] } }, { - "id": "rail_detail", - "type": "line", - "source": "osm", - "source-layer": "rail", - "filter": ["==", "rail", "detail"], - "paint": { - "line-color": rail, + id: 'rail', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + minzoom: 14, + filter: [ + 'all', + ['in', ['get', 'kind'], ['literal', ['rail', 'light_rail']]], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ], + paint: { + 'line-color': c.railCore, + 'line-width': ['interpolate', ['linear'], ['zoom'], 14, 0, 15, 1, 20, 10], + 'line-dasharray': [2, 2], + 'line-opacity': [ + 'interpolate', + ['linear'], + ['zoom'], + 14, + 0, + 15, + ['case', ['==', ['get', 'tunnel'], true], 0.3, 1] + ] } }, { - "id": "rail_secondary", - "type": "line", - "source": "osm", - "source-layer": "rail", - "filter": [ - "all", - ["==", "rail", "secondary"], - ["any", - ["!has", "level"], - ["==", "level", level] + id: 'rail-service', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + minzoom: 15, + filter: [ + 'all', + ['in', ['get', 'kind'], ['literal', ['rail', 'light_rail']]], + ['has', 'service'], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] ] ], - "paint": { - "line-color": rail, - "line-width": 1.15 + paint: { + 'line-color': c.railCore, + 'line-width': ['interpolate', ['linear'], ['zoom'], 15, 0, 16, 1, 20, 10], + 'line-dasharray': [2, 2], + 'line-opacity': ['case', ['==', ['get', 'tunnel'], true], 0.3, 1] } }, { - "id": "rail_primary", - "type": "line", - "source": "osm", - "source-layer": "rail", - "filter": [ - "all", - ["==", "rail", "primary"], - ["any", - ["!has", "level"], - ["==", "level", level] + id: 'subway', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + filter: [ + 'all', + ['==', ['get', 'kind'], 'subway'], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] ] ], - "paint": { - "line-color": rail, - "line-width": 1.3 - } - }, - { - "id": "road-ref-shield", - "type": "symbol", - "source": "osm", - "source-layer": "road", - "minzoom": 6, - "filter": ["all", - ["has", "ref"], - ["any", ["==", ["get", "highway"], "motorway"], - ["==", ["get", "highway"], "trunk"], - ["==", ["get", "highway"], "secondary"], - [">", ["zoom"], 11]]], - "layout": { - "symbol-placement": "line", - "text-field": ["get", "ref"], - "text-font": ["Noto Sans Display Regular"], - "text-size": ["case", ["==", ["get", "highway"], "motorway"], 11, 10], - "text-justify": "center", - "text-rotation-alignment": "viewport", - "text-pitch-alignment": "viewport", - "icon-image": "shield", - "icon-text-fit": "both", - "icon-text-fit-padding": [.5, 4, .5, 4], - "icon-rotation-alignment": "viewport", - "icon-pitch-alignment": "viewport", - }, - "paint": { - "text-color": "#333333", - }, + paint: { + 'line-color': c.subwayCore, + 'line-width': [ + 'interpolate', + ['linear'], + ['zoom'], + 11, + 0, + 12, + 1, + 15, + 2, + 16, + 2, + 18, + 5, + 19, + 6, + 20, + 8 + ], + 'line-dasharray': [2, 2], + 'line-opacity': ['interpolate', ['linear'], ['zoom'], 14, 0, 15, 1] + } }, { - "id": "road-name-text", - "type": "symbol", - "source": "osm", - "source-layer": "road", - "minzoom": 14, - "layout": { - "symbol-placement": "line", - "text-field": ["get", "name"], - "text-font": ["Noto Sans Display Regular"], - "text-size": 9, + id: 'tram', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + // like the old release (rail=secondary): visible from z10 at ~1px + minzoom: 10, + filter: [ + 'all', + ['in', ['get', 'kind'], ['literal', ['tram', 'narrow_gauge', 'monorail', 'funicular']]], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ], + paint: { + 'line-color': c.railOutline, + 'line-width': [ + 'interpolate', + ['linear'], + ['zoom'], + 10, + 1.15, + 16, + 1.15, + 17, + 2, + 18, + 3, + 20, + 5 + ] + } + }, + { + id: 'aerialway', + type: 'line', + source: 'osm', + 'source-layer': 'aerialways', + paint: { + 'line-color': c.rail, + 'line-dasharray': [10, 2] + } + }, + // Indoor overlay: the level-filtered station interior (z17+, plus the + // platform areas). Drawn on top of the base map so that tracks and roads + // cannot cut through rooms, platforms, stairs, elevators or their labels. + { + id: 'landuse-public-transport', + type: 'fill', + source: 'osm', + 'source-layer': 'land', + filter: [ + 'all', + ['==', 'kind', 'public_transport'], + ['any', ['!has', 'level'], ['==', 'level', level]] + ], + paint: { + 'fill-color': c.publicTransport + } + }, + { + id: 'indoor-corridor', + type: 'fill', + source: 'osm', + 'source-layer': 'indoor', + filter: ['all', ['==', 'indoor', 'corridor'], ['==', 'level', level]], + paint: { + 'fill-color': c.indoorCorridor, + 'fill-opacity': 0.8 + } + }, + { + id: 'indoor', + type: 'fill', + source: 'osm', + 'source-layer': 'indoor', + filter: ['all', ['!in', 'indoor', 'corridor', 'wall', 'elevator'], ['==', 'level', level]], + paint: { + 'fill-color': c.indoor, + 'fill-opacity': 0.8 + } + }, + { + id: 'indoor-outline', + type: 'line', + source: 'osm', + 'source-layer': 'indoor', + filter: ['all', ['!in', 'indoor', 'corridor', 'wall', 'elevator'], ['==', 'level', level]], + minzoom: 17, + paint: { + 'line-color': c.indoorOutline, + 'line-width': 2 + } + }, + // footpaths (highway=footway/path) and pedestrian streets as thin grey + // dashed lines; cycleways get the same treatment in blue. Staggered like + // the VersaTiles ladder but earlier: pedestrian fades in z12-13, walking + // paths (footway, path) and cycleways follow at z13-14. (pedestrian + // lines are only in the tiles from z13, so they join at z13 at full + // opacity.) + { + id: 'footpath', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + filter: [ + 'all', + ['in', 'kind', 'footway', 'path', 'pedestrian', 'cycleway'], + level === 0 ? ['any', ['!has', 'level'], ['==', 'level', level]] : ['==', 'level', level] + ], + minzoom: 12, + paint: { + 'line-color': ['match', ['get', 'kind'], 'cycleway', c.cycleway, c.footpath], + 'line-dasharray': [2, 1], + 'line-opacity': [ + 'interpolate', + ['linear'], + ['zoom'], + 12, + 0, + 13, + ['match', ['get', 'kind'], ['pedestrian'], 1, 0], + 14, + 1 + ], + 'line-width': ['interpolate', ['linear'], ['zoom'], 12, 0.3, 14, 0.6, 17, 1.8] + } + }, + // steps when zoomed out: a thin grey dashed line like normal pedestrian + // paths, handed off to the ribbon + rung detail below as that fades in + // around z15-16 + { + id: 'steps-line', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + minzoom: 12, + filter: [ + 'all', + ['==', 'kind', 'steps'], + level === 0 + ? [ + 'any', + ['!has', 'from_level'], + ['any', ['==', 'from_level', level], ['==', 'to_level', level]] + ] + : ['any', ['==', 'from_level', level], ['==', 'to_level', level]] + ], + paint: { + 'line-color': c.footpath, + 'line-dasharray': [2, 1], + 'line-opacity': ['interpolate', ['linear'], ['zoom'], 12, 0, 13, 1, 14, 0], + 'line-width': ['interpolate', ['linear'], ['zoom'], 12, 0.3, 13, 0.6, 14, 0.9] + } + }, + // steps like the original OSM style: a footway-colored line with short + // perpendicular tread rungs and no casing / outline + { + id: 'steps', + type: 'line', + source: 'osm', + 'source-layer': 'streets', + minzoom: 14, + filter: [ + 'all', + ['==', 'kind', 'steps'], + level === 0 + ? [ + 'any', + ['!has', 'from_level'], + ['any', ['==', 'from_level', level], ['==', 'to_level', level]] + ] + : ['any', ['==', 'from_level', level], ['==', 'to_level', level]] + ], + layout: { + 'line-cap': 'butt' }, - "paint": { - "text-halo-width": 11, - "text-halo-color": "white", - "text-color": "#333333" + paint: { + 'line-color': c.footpath, + 'line-dasharray': [0.4, 0.3], + 'line-width': ['interpolate', ['linear'], ['zoom'], 14, 2, 16, 4.5, 18, 7.5, 20, 13] } }, { - - "id": "towns", - "type": "symbol", - "source": "osm", - "source-layer": "cities", - "filter": ["!=", ["get", "place"], "city"], - "layout": { - // "symbol-sort-key": ["get", "population"], - "text-field": ["get", "name"], - "text-font": ["Noto Sans Display Regular"], - "text-size": 12 + id: 'indoor-elevator-outline', + type: 'circle', + source: 'osm', + 'source-layer': 'indoor', + minzoom: 17, + filter: [ + 'all', + ['==', 'indoor', 'elevator'], + ['<=', 'from_level', level], + ['>=', 'to_level', level] + ], + paint: { + 'circle-color': c.elevatorOutline, + 'circle-radius': 16 + } + }, + { + id: 'indoor-elevator', + type: 'circle', + source: 'osm', + 'source-layer': 'indoor', + minzoom: 17, + filter: [ + 'all', + ['==', 'indoor', 'elevator'], + ['<=', 'from_level', level], + ['>=', 'to_level', level] + ], + paint: { + 'circle-color': c.elevator, + 'circle-radius': 14 + } + }, + { + id: 'indoor-elevator-icon', + type: 'symbol', + source: 'osm', + 'source-layer': 'indoor', + minzoom: 17, + filter: [ + 'all', + ['==', 'indoor', 'elevator'], + ['<=', 'from_level', level], + ['>=', 'to_level', level] + ], + layout: { + 'icon-image': 'elevator', + 'icon-size': 0.9 + } + }, + { + id: 'indoor-names', + type: 'symbol', + source: 'osm', + 'source-layer': 'indoor', + minzoom: 17, + filter: ['any', ['!has', 'level'], ['==', 'level', level]], + layout: { + 'symbol-placement': 'point', + 'text-field': ['get', 'name'], + 'text-font': ['Noto Sans Regular'], + 'text-size': 12 }, - "paint": { - "text-halo-width": 1, - "text-halo-color": "white", - "text-color": "#333333" - } - }, - { - "id": "cities", - "type": "symbol", - "source": "osm", - "source-layer": "cities", - "filter": ["==", ["get", "place"], "city"], - "layout": { - // "symbol-sort-key": ["get", "population"], - "text-field": ["get", "name"], - "text-font": ["Noto Sans Display Bold"], - "text-size": 18 + paint: { + 'text-color': c.indoorText, + 'text-halo-color': c.textHalo, + 'text-halo-width': 2, + 'text-halo-blur': 1 + } + }, + { + id: 'road-ref-shield', + type: 'symbol', + source: 'osm', + 'source-layer': 'streets', + minzoom: 6, + filter: [ + 'all', + ['has', 'ref'], + [ + 'any', + [ + 'all', + ['in', ['get', 'kind'], ['literal', ['motorway', 'trunk', 'secondary']]], + ['!', ['to-boolean', ['get', 'link']]] + ], + ['>', ['zoom'], 11] + ] + ], + layout: { + 'symbol-placement': 'line', + 'text-field': ['get', 'ref'], + 'text-font': ['Noto Sans Regular'], + 'text-size': ['case', ['==', ['get', 'kind'], 'motorway'], 11, 10], + 'text-justify': 'center', + 'text-rotation-alignment': 'viewport', + 'text-pitch-alignment': 'viewport', + 'icon-image': c.shield, + 'icon-text-fit': 'both', + 'icon-text-fit-padding': [0.5, 4, 0.5, 4], + 'icon-rotation-alignment': 'viewport', + 'icon-pitch-alignment': 'viewport' }, - "paint": { - "text-halo-width": 2, - "text-halo-color": "white", - "text-color": "#111111" + paint: { + 'text-color': c.text } }, - /* { - id: 'hills', - type: 'hillshade', - source: 'hillshadeSource', - layout: {visibility: 'visible'}, - paint: {'hillshade-shadow-color': '#473B24'} + id: 'road-name-text', + type: 'symbol', + source: 'osm', + 'source-layer': 'streets', + minzoom: 14, + filter: ['==', 'rail', false], + layout: { + 'symbol-placement': 'line', + 'text-field': ['get', 'name'], + 'text-font': ['Noto Sans Regular'], + 'text-size': 9 + }, + paint: { + 'text-halo-width': 2, + 'text-halo-blur': 1, + 'text-halo-color': c.textHalo, + 'text-color': c.text + } }, + // house numbers like in VersaTiles: tiny, translucent, z17+ { - id: 'contours', - type: 'line', - source: 'contourSourceFeet', - 'source-layer': 'contours', - "filter": [">=", "ele", 200], + id: 'housenumbers', + type: 'symbol', + source: 'osm', + 'source-layer': 'addresses', + minzoom: 17, + filter: ['!=', 'housenumber', ''], + layout: { + 'symbol-placement': 'point', + 'text-field': ['get', 'housenumber'], + 'text-font': ['Noto Sans Regular'], + 'text-anchor': 'center', + 'text-size': ['interpolate', ['linear'], ['zoom'], 17, 9, 19, 11] + }, paint: { - 'line-opacity': 0.5, - 'line-width': ['match', ['get', 'level'], 1, 1, 0.5] + 'text-color': c.housenumber, + 'text-halo-color': c.textHalo, + 'text-halo-width': 1, + 'text-halo-blur': 1 } }, + // lake and river name labels (the profile attaches names directly to + // the water geometry; Shortbread's *_labels layers are not produced) { - id: 'contour-text', + id: 'water-name', type: 'symbol', - source: 'contourSourceFeet', - 'source-layer': 'contours', + source: 'osm', + 'source-layer': 'water_polygons', + minzoom: 12, + // way_area thresholds per zoom: ≥8000 m² from z12, ≥2000 from z13, + // ≥1000 from z14, ≥500 from z15 (zoom brackets: filters cannot nest + // ['zoom'] inside interpolate) filter: [ 'all', - [">=", "ele", 200], - ['>', 'level', 0] + ['!=', ['get', 'name'], ''], + [ + 'any', + ['all', ['<', ['zoom'], 13], ['>=', ['coalesce', ['get', 'way_area'], 0], 8000]], + [ + 'all', + ['>=', ['zoom'], 13], + ['<', ['zoom'], 14], + ['>=', ['coalesce', ['get', 'way_area'], 0], 2000] + ], + [ + 'all', + ['>=', ['zoom'], 14], + ['<', ['zoom'], 15], + ['>=', ['coalesce', ['get', 'way_area'], 0], 1000] + ], + ['all', ['>=', ['zoom'], 15], ['>=', ['coalesce', ['get', 'way_area'], 0], 500]] + ] ], - paint: { - 'text-halo-color': 'white', - 'text-halo-width': 1 + layout: { + 'symbol-placement': 'point', + 'text-field': ['get', 'name'], + 'text-font': ['Noto Sans Regular'], + 'text-size': ['interpolate', ['linear'], ['zoom'], 11, 10, 16, 14] }, + paint: { + 'text-color': c.waterText, + 'text-halo-color': c.textHalo, + 'text-halo-width': 2, + 'text-halo-blur': 1 + } + }, + { + id: 'waterway-name', + type: 'symbol', + source: 'osm', + 'source-layer': 'water_lines', + minzoom: 13, + filter: ['!=', 'name', ''], layout: { 'symbol-placement': 'line', - 'text-size': 12, - 'text-field': [ - 'concat', - ['number-format', ['get', 'ele'], {}], - 'm' - ], - 'text-font': ['Noto Sans Display Regular'] + 'text-field': ['get', 'name'], + 'text-font': ['Noto Sans Regular'], + 'text-size': 10 + }, + paint: { + 'text-color': c.waterText, + 'text-halo-color': c.textHalo, + 'text-halo-width': 2, + 'text-halo-blur': 1 } }, - */ - ], - /* - terrain: { - source: 'hillshadeSource', - exaggeration: 1 - } - */ + { + id: 'towns', + type: 'symbol', + source: 'osm', + 'source-layer': 'place_labels', + minzoom: 9, + // per-kind min zooms as in the VersaTiles Shortbread style; kinds not + // listed (locality, island, farm, ...) are never rendered there. + filter: [ + '>=', + ['zoom'], + [ + 'match', + ['get', 'kind'], + 'town', + 9, + ['village', 'suburb'], + 11, + ['hamlet', 'quarter'], + 13, + 'neighbourhood', + 14, + 99 + ] + ], + layout: { + 'text-field': ['get', 'name'], + 'text-font': ['Noto Sans Regular'], + 'text-size': 12 + }, + paint: { + 'text-halo-width': 2, + 'text-halo-blur': 1, + 'text-halo-color': c.townTextHalo, + 'text-color': c.townText + } + }, + { + id: 'cities', + type: 'symbol', + source: 'osm', + 'source-layer': 'place_labels', + minzoom: 5, + filter: [ + '>=', + ['zoom'], + ['match', ['get', 'kind'], 'capital', 5, 'state_capital', 6, 'city', 7, 99] + ], + layout: { + 'symbol-sort-key': ['-', ['coalesce', ['get', 'population'], 0]], + 'text-field': ['get', 'name'], + 'text-font': ['Noto Sans Bold'], + 'text-size': ['interpolate', ['linear'], ['zoom'], 6, 12, 9, 16] + }, + paint: { + 'text-halo-width': 2, + 'text-halo-blur': 1, + 'text-halo-color': c.citiesTextHalo, + 'text-color': c.citiesText + } + } + ] }); - if (!map.hasImage("shield")) { + if (!map.hasImage(c.shield)) { map.addImage( - "shield", + c.shield, ...createShield({ - fill: "hsl(0, 0%, 98%)", - stroke: "hsl(0, 0%, 75%)", + fill: theme === 'dark' ? 'hsl(0, 0%, 20%)' : 'hsl(0, 0%, 98%)', + stroke: theme === 'dark' ? 'hsl(0, 0%, 45%)' : 'hsl(0, 0%, 75%)' }) ); } @@ -744,64 +1820,231 @@ const style = (map, level) => { function setStyleLayerFilters(map, level) { const layersWithLevelFilters = [ { - id: "indoor-corridor", - filter: ["all", ["==", "indoor", "corridor"], ["==", "level", level]] + id: 'track', + filter: [ + 'all', + ['==', 'kind', 'track'], + level === 0 ? ['any', ['!has', 'level'], ['==', 'level', level]] : ['==', 'level', level] + ] }, { - id: "indoor", - filter: ["all", ["!in", "indoor", "corridor", "wall", "elevator"], ["==", "level", level]] + id: 'living-street', + filter: [ + 'all', + ['==', 'kind', 'living_street'], + level === 0 ? ['any', ['!has', 'level'], ['==', 'level', level]] : ['==', 'level', level] + ] }, { - id: "indoor-outline", - filter: ["all", ["!in", "indoor", "corridor", "wall", "elevator"], ["==", "level", level]] + id: 'footway', + filter: [ + 'all', + ['==', 'kind', 'service'], + level === 0 ? ['any', ['!has', 'level'], ['==', 'level', level]] : ['==', 'level', level] + ] }, { - id: "indoor-names", - filter: ["any", ["!has", "level"], ["==", "level", level]] + id: 'unclassified', + filter: [ + 'all', + ['==', 'kind', 'unclassified'], + level === 0 ? ['any', ['!has', 'level'], ['==', 'level', level]] : ['==', 'level', level] + ] }, { - id: "landuse-public-transport", - filter: ["all", ["==", "landuse", "public_transport"], ["any", ["!has", "level"], ["==", "level", level]]] + id: 'landuse-public-transport', + filter: ['all', ['==', 'kind', 'public_transport'], ['any', ['!has', 'level'], ['==', 'level', level]]] }, { - id: "footway", + id: 'indoor-corridor', + filter: ['all', ['==', 'indoor', 'corridor'], ['==', 'level', level]] + }, + { + id: 'indoor', + filter: ['all', ['!in', 'indoor', 'corridor', 'wall', 'elevator'], ['==', 'level', level]] + }, + { + id: 'indoor-outline', + filter: ['all', ['!in', 'indoor', 'corridor', 'wall', 'elevator'], ['==', 'level', level]] + }, + { + id: 'footpath', + filter: [ + 'all', + ['in', 'kind', 'footway', 'path', 'pedestrian', 'cycleway'], + level === 0 ? ['any', ['!has', 'level'], ['==', 'level', level]] : ['==', 'level', level] + ] + }, + { + id: 'steps-line', filter: [ - "all", - ["in", "highway", "footway", "track", "cycleway", "path", "unclassified", "service"], + 'all', + ['==', 'kind', 'steps'], level === 0 - ? ["any", ["!has", "level"], ["==", "level", level]] - : ["==", "level", level] + ? ['any', ['!has', 'from_level'], ['any', ['==', 'from_level', level], ['==', 'to_level', level]]] + : ['any', ['==', 'from_level', level], ['==', 'to_level', level]] ] }, { - id: "steps", + id: 'steps', filter: [ - "all", - ["==", "highway", "steps"], + 'all', + ['==', 'kind', 'steps'], level === 0 - ? ["any", ["!has", "from_level"], ["any", ["==", "from_level", level], ["==", "to_level", level]]] - : ["any", ["==", "from_level", level], ["==", "to_level", level]] + ? ['any', ['!has', 'from_level'], ['any', ['==', 'from_level', level], ['==', 'to_level', level]]] + : ['any', ['==', 'from_level', level], ['==', 'to_level', level]] + ] + }, + { + id: 'indoor-elevator-outline', + filter: ['all', ['==', 'indoor', 'elevator'], ['<=', 'from_level', level], ['>=', 'to_level', level]] + }, + { + id: 'indoor-elevator', + filter: ['all', ['==', 'indoor', 'elevator'], ['<=', 'from_level', level], ['>=', 'to_level', level]] + }, + { + id: 'indoor-elevator-icon', + filter: ['all', ['==', 'indoor', 'elevator'], ['<=', 'from_level', level], ['>=', 'to_level', level]] + }, + { + id: 'indoor-names', + filter: ['any', ['!has', 'level'], ['==', 'level', level]] + }, + { + id: 'rail-outline', + filter: [ + 'all', + ['==', ['get', 'kind'], 'rail'], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] ] }, { - id: "indoor-elevator-outline", - filter: ["all", ["==", "indoor", "elevator"], ["<=", "from_level", level], [">=", "to_level", level]] + id: 'lightrail-outline', + filter: [ + 'all', + ['==', ['get', 'kind'], 'light_rail'], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ] }, { - id: "indoor-elevator", - filter: ["all", ["==", "indoor", "elevator"], ["<=", "from_level", level], [">=", "to_level", level]] + id: 'rail-service-outline', + filter: [ + 'all', + ['in', ['get', 'kind'], ['literal', ['rail', 'light_rail']]], + ['has', 'service'], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ] }, { - id: "indoor-elevator-icon", - filter: ["all", ["==", "indoor", "elevator"], ["<=", "from_level", level], [">=", "to_level", level]] + id: 'subway-outline', + filter: [ + 'all', + ['==', ['get', 'kind'], 'subway'], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ] }, { - id: "rail_secondary", - filter: ["all", ["==", "rail", "secondary"], ["any", ["!has", "level"], ["==", "level", level]]] + id: 'tram-outline', + filter: [ + 'all', + ['in', ['get', 'kind'], ['literal', ['tram', 'narrow_gauge', 'monorail', 'funicular']]], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ] }, { - id: "rail_primary", - filter: ["all", ["==", "rail", "primary"], ["any", ["!has", "level"], ["==", "level", level]]] + id: 'rail', + filter: [ + 'all', + ['in', ['get', 'kind'], ['literal', ['rail', 'light_rail']]], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ] + }, + { + id: 'rail-service', + filter: [ + 'all', + ['in', ['get', 'kind'], ['literal', ['rail', 'light_rail']]], + ['has', 'service'], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ] + }, + { + id: 'subway', + filter: [ + 'all', + ['==', ['get', 'kind'], 'subway'], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ] + }, + { + id: 'tram', + filter: [ + 'all', + ['in', ['get', 'kind'], ['literal', ['tram', 'narrow_gauge', 'monorail', 'funicular']]], + ['!', ['has', 'service']], + [ + 'any', + ['<=', ['zoom'], LEVEL_MIN_ZOOM], + ['!', ['has', 'level']], + ['==', ['get', 'level'], 0], + ['==', ['get', 'level'], level] + ] + ] } ]; @@ -810,4 +2053,4 @@ function setStyleLayerFilters(map, level) { }); } -export { style, setStyleLayerFilters } \ No newline at end of file +export { style, setStyleLayerFilters }; From bffbf182c53ddcd515ee86386b16b01274b7242a Mon Sep 17 00:00:00 2001 From: p <149105134+peisha2@users.noreply.github.com> Date: Sun, 27 Sep 2026 22:49:41 +0200 Subject: [PATCH 15/17] Add CCH routing --- include/osr/routing/algorithms.h | 2 +- include/osr/routing/cch.h | 425 +++++++++++++++++++++++ src/route.cc | 266 +++++++++++++- src/ways.cc | 6 +- test/cch_dijkstra_astarbidir_test.cc | 496 +++++++++++++++++++++++++++ web/index.html | 1 + 6 files changed, 1188 insertions(+), 8 deletions(-) create mode 100644 include/osr/routing/cch.h create mode 100644 test/cch_dijkstra_astarbidir_test.cc diff --git a/include/osr/routing/algorithms.h b/include/osr/routing/algorithms.h index acc9a211..05a70417 100644 --- a/include/osr/routing/algorithms.h +++ b/include/osr/routing/algorithms.h @@ -5,7 +5,7 @@ namespace osr { -enum class routing_algorithm : std::uint8_t { kDijkstra, kAStarBi }; +enum class routing_algorithm : std::uint8_t { kDijkstra, kAStarBi, kCCH}; routing_algorithm to_algorithm(std::string_view); diff --git a/include/osr/routing/cch.h b/include/osr/routing/cch.h new file mode 100644 index 00000000..a02f9328 --- /dev/null +++ b/include/osr/routing/cch.h @@ -0,0 +1,425 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "osr/elevation_storage.h" +#include "osr/routing/profile.h" +#include "osr/types.h" +#include "osr/ways.h" + +namespace osr { + +template +struct cch { + + struct shortcut { + /* from*/ + typename P::node to_; + typename P::node inbetween_up_; + typename P::node inbetween_down_; + cost_t weight_up_{kInfeasible}; //up and down for directions + cost_t weight_down_{kInfeasible}; + bool is_shortcut_up_{false}; + bool is_shortcut_down_{false}; + }; + + struct item { + typename P::node n_; + std::uint32_t importance_; + }; + + struct route{ + cost_t cost_{kInfeasible}; + typename P::node intersection_{}; + //pairs upgraph edges (to unpack) + std::vector packed_path_; + }; + + struct pq_entry{ + cost_t cost_; + typename P::node node_; + + bool operator>(pq_entry const& other_entry) const{ + return cost_>other_entry.cost_; + } + }; + + osr::hash_map rank_; + + osr::hash_map> up_graph_; + + std::size_t n_nodes_{}; //topology dataset + + void build_pre_order(ways const& way){ + std::vector nodes; + for (node_idx_t n = node_idx_t{0}; n < way.n_nodes(); ++n){ + auto const importance = way.r_->node_importance_[n]; + P::resolve_all(*way.r_,n,level_t{}, [&](typename P::node const vn){ + nodes.push_back(item{ + vn,importance + }); + }); + } + std::sort(nodes.begin(), nodes.end(), [&](item const& a, item const& b) { + if (a.importance_ != b.importance_) { + return a.importance_ < b.importance_; + } + return a.n_ < b.n_; // guaranteed no tie + }); + + std::uint32_t rank = 0; + for (auto const& x : nodes) { + rank_[x.n_] = rank++; + } + } + + template + void for_each_edge(ways const& way, + typename P::parameters const& params, + Fn&& fn) const { + for(node_idx_t n = node_idx_t{}; n( + params, + *way.r_, + u, + nullptr,//blocked + nullptr,//additional + nullptr,//elevation_storage + [&](typename P::node const v, + cost_t const cost, + distance_t /*dist*/, + way_idx_t /*way*/, + int /*0*/, + int /*0*/, + elevation_storage::elevation /*elevation*/, + bool /*false*/ + ){ + fn(u,v,cost); + } + ); + } + ); + } + } + + std::vector get_ordered_nodes(){ + std::vector ordered_nodes(rank_.size()); + for(auto const& pair:rank_){ + ordered_nodes.at(pair.second) = pair.first; + } + return ordered_nodes; + } + + //tpopolgy + template + void build_topology(ways const& way){ + build_pre_order(way); + n_nodes_ = way.n_nodes(); + + osr::hash_map> adj_temp; + + for_each_edge(way, typename P::parameters{}, + [&](typename P::node const u, typename P::node v, cost_t /*cost*/ ){ + if(u==v){ return;} //self edge + adj_temp[u].push_back(v); + adj_temp[v].push_back(u); + } + ); + + auto const ordered_nodes = get_ordered_nodes(); + + for(typename P::node const v : ordered_nodes) { + if(!up_graph_.contains(v)) { + up_graph_[v] = std::vector{}; + } + } + + for (typename P::node const v : ordered_nodes) { + auto const rank_v = rank_.at(v); + std::vector upward_neighbour; + for(auto neighbour : adj_temp[v]){ + if(rank_.at(neighbour)> rank_v){ + bool duplicate(false); + for(typename P::node const duplicates:upward_neighbour){ + if(duplicates==neighbour){ + duplicate = true; + break; + } + } + if(!duplicate){ + upward_neighbour.push_back(neighbour); + } + } + } + //non-shortcut roads + auto& v_up_edges = up_graph_.at(v); + for(auto const u : upward_neighbour){ + v_up_edges.push_back({u, typename P::node{}, typename P::node{}, kInfeasible, kInfeasible, false,false}); + } + + //shortcuts + for(std::size_t i=0; i + void customise(ways const& way, typename P::parameters const& params){ + for(auto& [node, edges] : up_graph_){ + for(auto& edge : edges){ + edge.weight_up_ = kInfeasible; + edge.weight_down_ = kInfeasible; + edge.is_shortcut_up_ = false; + edge.is_shortcut_down_ = false; + } + } + + //real edges + for_each_edge(way, params,[&](typename P::node const u, typename P::node const v, cost_t cost){ + + typename P::node source = rank_.at(u)rank_.at(v) ? u : v; //higher ranks + + for (auto& edge : up_graph_.at(source)) { //edges to higher rank + if (edge.to_ == target) { + if( u == source){ + edge.weight_up_ = std::min(edge.weight_up_, cost); + }else{ + edge.weight_down_ = std::min(edge.weight_down_, cost); + } + } + } + } + ); + + //shortcuts + auto const ordered_nodes = get_ordered_nodes(); //low->high + + for(typename P::node v : ordered_nodes){ + auto const& upward_edges = up_graph_.at(v); + + for(std::size_t i = 0; i < upward_edges.size(); ++i){ + for(std::size_t j = i+1; j < upward_edges.size(); ++j){ + + + auto const& edge_vu = upward_edges[i];// edge vu u w + auto const& edge_vw = upward_edges[j]; //Edge vw v + + auto u = edge_vu.to_; + auto w = edge_vw.to_; + + typename P::node source = rank_.at(u)rank_.at(w) ? u : w; + + cost_t cost_src_to_target; // u v w + cost_t cost_target_to_src; // w v u + + if( u == source){ //base case; fwd down up + cost_src_to_target = + (edge_vu.weight_down_ == kInfeasible || edge_vw.weight_up_ == kInfeasible) + ? kInfeasible + : edge_vu.weight_down_ + edge_vw.weight_up_; + + cost_target_to_src = + (edge_vw.weight_down_ == kInfeasible || edge_vu.weight_up_ == kInfeasible) + ? kInfeasible + : edge_vw.weight_down_ + edge_vu.weight_up_; + }else{ + cost_target_to_src = + (edge_vu.weight_down_ == kInfeasible || edge_vw.weight_up_ == kInfeasible) + ? kInfeasible + : edge_vu.weight_down_ + edge_vw.weight_up_; + + cost_src_to_target = + (edge_vw.weight_down_ == kInfeasible || edge_vu.weight_up_ == kInfeasible) + ? kInfeasible + : edge_vw.weight_down_ + edge_vu.weight_up_; + } + + for(auto& shortcut : up_graph_.at(source)){ + if( shortcut.to_ == target){ + if(cost_src_to_target != kInfeasible + && cost_src_to_target < shortcut.weight_up_){ + shortcut.weight_up_ = cost_src_to_target; + shortcut.inbetween_up_ = v; + shortcut.is_shortcut_up_ = true; + } + if(cost_target_to_src != kInfeasible + && cost_target_to_src < shortcut.weight_down_){ + shortcut.weight_down_ = cost_target_to_src; + shortcut.inbetween_down_ = v; + shortcut.is_shortcut_down_ = true; + } + } + } + + } + } + } + for(auto& [node, edges] : up_graph_){ + std::vector kept; + for(auto const& edge : edges){ + if(edge.weight_up_ != kInfeasible || edge.weight_down_ != kInfeasible){ + kept.push_back(edge); + } + } + edges = kept; + } + } + + void search(auto& pq, auto& cost_map, auto const& cost_map_bwd, auto& pred_map, route& result, bool use_up) const { + auto current = pq.top(); + pq.pop(); + + auto const self = cost_map.find(current.node_); + if( self != cost_map.end() && current.cost_ > self->second){ //skip if better alt + return; + } + + auto const current_key = cost_map_bwd.find(current.node_); + //check for improvement + if( current_key != cost_map_bwd.end()){ + cost_t total_cost = current.cost_ + current_key->second; + if( total_cost < result.cost_){ + result.cost_ = total_cost; + result.intersection_ = current.node_; + } + } + + auto const current_up_key = up_graph_.find(current.node_); + if( current_up_key != up_graph_.end()){ + for(auto& edge:current_up_key->second){ + auto const edge_weight_dir = use_up ? edge.weight_up_ : edge.weight_down_; + if(edge_weight_dir == kInfeasible){continue;} + auto const new_cost = current.cost_ + edge_weight_dir; + + auto const target = cost_map.find(edge.to_); + if(target == cost_map.end() || target->second > new_cost ){ + cost_map[edge.to_] = new_cost; + pred_map[edge.to_] = current.node_; + pq.push({new_cost, edge.to_}); + } + } + } + } + + route query(std::vector> const& sources, + std::vector> const& targets) const { + route result{}; + + for (auto const& [source, source_cost] : sources) { + for (auto const& [target, target_cost] : targets) { + if (source == target) { + auto const total = clamp_cost(static_cast(source_cost) + target_cost); + if (total < result.cost_) { + result.cost_ = total; + result.intersection_ = source; + } + } + } + } + + std::priority_queue, std::greater> pq_fwd, pq_bwd; //min pq + hash_map fwd_cost, bwd_cost; + hash_map fwd_pred, bwd_pred; + + for (auto const& [source, source_cost] : sources) { + auto const it = fwd_cost.find(source); + if (it == fwd_cost.end() || source_cost < it->second) { + fwd_cost[source] = source_cost; + pq_fwd.push({source_cost, source}); + } + } + + for (auto const& [target, target_cost] : targets) { + auto const it = bwd_cost.find(target); + if (it == bwd_cost.end() || target_cost < it->second) { + bwd_cost[target] = target_cost; + pq_bwd.push({target_cost, target}); + } + } + + while(!pq_fwd.empty() || !pq_bwd.empty()){ + cost_t min_fwd = pq_fwd.empty() ? kInfeasible : pq_fwd.top().cost_; + cost_t min_bwd = pq_bwd.empty() ? kInfeasible : pq_bwd.top().cost_; + + if ((pq_fwd.empty() || min_fwd >= result.cost_) && + (pq_bwd.empty() || min_bwd >= result.cost_)) { + break; + } + if( !pq_fwd.empty()&& min_fwd <= min_bwd){ //continue from short path + search(pq_fwd, fwd_cost, bwd_cost, fwd_pred, result, true); + }else if(!pq_bwd.empty()){ + search(pq_bwd, bwd_cost, fwd_cost, bwd_pred, result, false); + } + + } + + //build packed.. + //1st (start->intersection) from the preds + if(result.cost_ != kInfeasible){ + + //backwards + auto node_to_start = result.intersection_; + while(true){ + result.packed_path_.push_back(node_to_start); + auto const it = fwd_pred.find(node_to_start); + if(it == fwd_pred.end()){ break; } + node_to_start = it->second; + } + std::reverse(result.packed_path_.begin(), result.packed_path_.end()); + + //2nd (intersection -> target) + auto node_to_target = result.intersection_; + while(true){ + auto const it = bwd_pred.find(node_to_target); + if(it == bwd_pred.end()){ break; } + node_to_target = it->second; + result.packed_path_.push_back(node_to_target); + } + //result (start-intersection->target) + } + return result; + } + + + void preprocess(ways const& way, + direction const dir, + typename P::parameters const& params){ + if (dir == direction::kForward) { + build_topology(way); + customise(way, params); + } else { + build_topology(way); + customise(way, params); + } + } +}; +}// namespace osr diff --git a/src/route.cc b/src/route.cc index 9ef50c96..f9f499d0 100644 --- a/src/route.cc +++ b/src/route.cc @@ -1,8 +1,13 @@ #include "osr/routing/route.h" +#include #include #include +#include +#include +#include #include +#include #include "boost/thread/tss.hpp" @@ -15,6 +20,7 @@ #include "osr/elevation_storage.h" #include "osr/lookup.h" #include "osr/routing/bidirectional.h" +#include "osr/routing/cch.h" #include "osr/routing/dijkstra.h" #include "osr/routing/path_reconstruction.h" #include "osr/routing/profiles/bike.h" @@ -33,6 +39,21 @@ namespace osr { constexpr auto const kMaxMatchingDistanceSquaredRatio = 9.0; constexpr auto const kBottomKDefinitelyConsidered = 5; +template +cch

& get_cch(ways const& way, + direction const dir, + typename P::parameters const& params){ + static std::map, cch

> cache; + static std::mutex m; + + auto const lock = std::lock_guard{m}; + auto& entry = cache[std::pair{way.p_, dir}]; + if(entry.n_nodes_ == 0){ + entry.preprocess(way, dir, params); + } + return entry; +} + template bidirectional

& get_bidirectional() { static auto s = boost::thread_specific_ptr>{}; @@ -55,6 +76,7 @@ routing_algorithm to_algorithm(std::string_view s) { switch (cista::hash(s)) { case cista::hash("dijkstra"): return routing_algorithm::kDijkstra; case cista::hash("bidirectional"): return routing_algorithm::kAStarBi; + case cista::hash("cch"): return routing_algorithm::kCCH; } throw utl::fail("unknown routing algorithm: {}", s); } @@ -74,9 +96,6 @@ path reconstruct_bi(typename P::parameters const& params, cost_t const cost, direction const dir) { auto forward_n = b.meet_point_1_; - - // TODO subtract meetpoint node cost? - auto forward_segments = std::vector{}; auto forward_dist = 0.0; @@ -316,7 +335,7 @@ best_candidate(typename P::parameters const& params, break; } auto best_node = P::node::invalid(); - auto best_cost = path{.cost_ = std::numeric_limits::max()}; + auto best_cost = path{.cost_ = std::numeric_limits::max()}; //path{.xyz} auto best = static_cast(nullptr); for (auto const x : {&dest.left_, &dest.right_}) { @@ -519,6 +538,194 @@ std::optional route_dijkstra(typename P::parameters const& params, return std::nullopt; } +template +void unpack_shortcut(typename P::parameters const& params, + ways const& w, + cch

& b, + typename P::node const u, + typename P::node const v, + direction const dir, + double& dist, + std::vector& segments){ + if(u==v){return;} + + typename P::node source = b.rank_.at(u)b.rank_.at(v) ? u : v; + + auto const dir_up = (u == source); + auto const node_it = b.up_graph_.find(source); + + if(node_it != b.up_graph_.end()){ + for(auto& shortcut : node_it->second){ + if(shortcut.to_ == target){ + bool const is_shortcut = dir_up ? shortcut.is_shortcut_up_ : shortcut.is_shortcut_down_; + if(is_shortcut){ + auto const inbetween = dir_up ? shortcut.inbetween_up_ : shortcut.inbetween_down_; + unpack_shortcut

(params, w, b, u, inbetween, dir, dist, segments); + unpack_shortcut

(params, w, b, inbetween, v, dir, dist, segments); + }else{ + //real edge add_path for polyline + auto const weight = dir_up ? shortcut.weight_up_ : shortcut.weight_down_; + dist += add_path

(params, w, *w.r_, nullptr, nullptr, nullptr, u, v, + weight, segments, dir); + } + } + } + } +} +template +std::optional route_cch(typename P::parameters const& params, + ways const& w, + lookup const& l, + cch

& b, + location const& from, + location const& to, + match_view_t from_match, + match_view_t to_match, + cost_t const max, + direction const dir, + bitvec const* blocked, + sharing_data const* sharing, + elevation_storage const* elevations) { + + if(from_match.empty() || to_match.empty()){ + return std::nullopt; + } + + if (auto const direct = try_direct(from, to); direct.has_value()) { + return *direct; + } + + //blcked and sharing don't work with cch. height irrelevant + if(sharing != nullptr){ + return route_dijkstra(params, w, l, get_dijkstra

(), from, to, + from_match, to_match, max, dir, blocked, + sharing, elevations); + } + + auto start_states = std::vector>{}; + auto target_states = std::vector>{}; + auto query_result = typename cch

::route{}; + auto winning_start = static_cast(nullptr); + auto winning_target = static_cast(nullptr); + + for(auto const& start_cand : from_match){ + start_states.clear(); + for(auto const* nc : {&start_cand.left_, &start_cand.right_}){ + if(nc->valid() && nc->cost_ < max){ + P::resolve_start_node(*w.r_, start_cand.way_, nc->node_, from.lvl_, dir, [&](auto const n) { + start_states.emplace_back(n, nc->cost_); + }); + } + } + if(start_states.empty()){ continue; } + + for(auto const& target_cand : to_match){ + //drive between same component only + if(w.r_->way_component_[start_cand.way_] != w.r_->way_component_[target_cand.way_]){ + continue; + } + + target_states.clear(); + for(auto const* nc : {&target_cand.left_, &target_cand.right_}){ + if(nc->valid()){ + //only legal states + P::resolve_all(*w.r_, nc->node_, to.lvl_, [&](auto const n) { + if(!P::is_dest_reachable(params, *w.r_, n, target_cand.way_, + flip(opposite(dir), nc->way_dir_), dir)){ + return; + } + target_states.emplace_back(n, nc->cost_); + }); + } + } + if(target_states.empty()){ continue; } + + auto const a = b.query(start_states, target_states); + //max param + if(a.cost_ != kInfeasible && a.cost_ < max){ + query_result = a; //first hit + winning_start = &start_cand; + winning_target = &target_cand; + break; + } + } + if(query_result.cost_ != kInfeasible){ break; } + } + + if(query_result.cost_ == kInfeasible){ + return std::nullopt; + } + auto segments = std::vector{}; + auto dist = 0.0; + + auto const& start_cand = *winning_start; + auto const& target_cand = *winning_target; + auto const start_node = query_result.packed_path_.front(); + auto const target_node = query_result.packed_path_.back(); + //start + auto const& start_node_cand = + start_node.get_node() == start_cand.left_.node_ ? start_cand.left_ : start_cand.right_; + segments.push_back({ + .polyline_ = l.get_node_candidate_path(start_cand, start_node_cand, dir == direction::kBackward, from), + .from_level_ = start_node_cand.lvl_, + .to_level_ = start_node_cand.lvl_, + .from_ = dir == direction::kBackward ? start_node.get_node() : node_idx_t::invalid(), + .to_ = dir == direction::kForward ? start_node.get_node() : node_idx_t::invalid(), + .cost_ = start_node_cand.cost_, + .dist_ = static_cast(start_node_cand.dist_to_node_), + .elevation_ = {}, + .mode_ = start_node.get_mode() + }); + + //packedpath result = edge + for(std::size_t i = 1; i < query_result.packed_path_.size(); ++i){ + unpack_shortcut

(params, w, b, query_result.packed_path_[i-1], + query_result.packed_path_[i], dir, dist, segments); + } + + auto const& target_node_cand = + target_node.get_node() == target_cand.left_.node_ ? target_cand.left_ : target_cand.right_; + segments.push_back({ + .polyline_ = l.get_node_candidate_path(target_cand, target_node_cand, dir == direction::kForward, to), + .from_level_ = target_node_cand.lvl_, + .to_level_ = target_node_cand.lvl_, + .from_ = dir == direction::kForward ? target_node.get_node() : node_idx_t::invalid(), + .to_ = dir == direction::kBackward ? target_node.get_node() : node_idx_t::invalid(), + .cost_ = target_node_cand.cost_, + .dist_ = static_cast(target_node_cand.dist_to_node_), + .elevation_ ={}, + .mode_ = target_node.get_mode() + }); + + //fwd start->target + if(dir == direction::kBackward){ + std::reverse(begin(segments), end(segments)); + } + + //no elevation + auto p = path{ + .cost_ = query_result.cost_, + .dist_ = start_node_cand.dist_to_node_ + dist + target_node_cand.dist_to_node_, + .segments_ = segments, + }; + + //blocked -> dijkstra + if(blocked != nullptr){ + for(auto const& segment : p.segments_){ + for(auto const n : {segment.from_, segment.to_}){ + if(n != node_idx_t::invalid() && blocked->test(n)){ + return route_dijkstra(params, w, l, get_dijkstra

(), from, to, + from_match, to_match, max, dir, blocked, + sharing, elevations); + } + } + } + } + return p; +} + + template std::vector> route( typename P::parameters const& params, @@ -691,6 +898,33 @@ std::optional route_dijkstra(profile_parameters const& params, }); } +std::optional cch_route(profile_parameters const& params, + ways const& w, + lookup const& l, + search_profile const profile, + location const& from, + location const& to, + cost_t const max, + direction const dir, + double const max_match_distance, + bitvec const* blocked, + sharing_data const* sharing, + elevation_storage const* elevations){ + return with_profile(profile, [&](P&&) -> std::optional { + + + + auto const& pp = std::get(params); + auto const from_match = l.match

(pp, from, false, dir, max_match_distance, blocked); + auto const to_match = l.match

(pp, to, true, dir, max_match_distance, blocked); + + if (from_match.empty() || to_match.empty()) { + return std::nullopt; + } + return route_cch(pp, w, l, get_cch

(w, dir, pp), from, to, from_match, to_match, max, dir, blocked, sharing, elevations); + }); +} + std::vector> route( profile_parameters const& params, ways const& w, @@ -739,6 +973,12 @@ std::optional route(profile_parameters const& params, algo = routing_algorithm::kDijkstra; // TODO } + if (algo == routing_algorithm::kCCH && + profile != search_profile::kCar && + profile != search_profile::kBus) { + algo = routing_algorithm::kDijkstra; // CCH only car/bus + } + switch (algo) { case routing_algorithm::kDijkstra: return with_profile(profile, [&](P&&) { @@ -753,6 +993,13 @@ std::optional route(profile_parameters const& params, from_match, to_match, max, dir, blocked, sharing, elevations); }); + case routing_algorithm::kCCH: + return with_profile(profile, [&](P&&) { + auto const& pp = std::get(params); + return route_cch(pp, w, l, + get_cch

(w, dir, pp), from, to, from_match, to_match, + max, dir, blocked, sharing, elevations); + }); } throw utl::fail("not implemented"); } @@ -776,6 +1023,11 @@ std::optional route(profile_parameters const& params, profile == search_profile::kCarParking) { algo = routing_algorithm::kDijkstra; // TODO } + if (algo == routing_algorithm::kCCH && + profile != search_profile::kCar && + profile != search_profile::kBus) { + algo = routing_algorithm::kDijkstra; // CCH only car/bus + } switch (algo) { case routing_algorithm::kDijkstra: return route_dijkstra(params, w, l, profile, from, to, max, dir, @@ -784,7 +1036,11 @@ std::optional route(profile_parameters const& params, return route_bidirectional(params, w, l, profile, from, to, max, dir, max_match_distance, blocked, sharing, elevations); - } + case routing_algorithm::kCCH: + return cch_route(params, w, l, profile, from, to, max, dir, + max_match_distance, blocked, sharing, + elevations); + } throw utl::fail("not implemented"); } diff --git a/src/ways.cc b/src/ways.cc index a23e80da..6c10a6f0 100644 --- a/src/ways.cc +++ b/src/ways.cc @@ -1,6 +1,7 @@ #include "osr/ways.h" #include +#include #include "utl/pairwise.h" #include "utl/parallel_for.h" @@ -8,7 +9,7 @@ #include "cista/io.h" // uncomment the following line to enable IFC -// #define USE_INERTIAL_FLOW_CUT +#define USE_INERTIAL_FLOW_CUT #ifdef USE_INERTIAL_FLOW_CUT #include "inertialflowcutter/run.h" #endif @@ -129,7 +130,8 @@ void ways::build_components_and_importance() { v_head.push_back(b.v_); } } - auto const thread_count = 4; + auto const thread_count = std::max(static_cast(1), + std::thread::hardware_concurrency()); ifc::run_inertial_flow_cutter( thread_count, static_cast(n_nodes()), v_head, v_tail, [&](int i) { diff --git a/test/cch_dijkstra_astarbidir_test.cc b/test/cch_dijkstra_astarbidir_test.cc new file mode 100644 index 00000000..cd948724 --- /dev/null +++ b/test/cch_dijkstra_astarbidir_test.cc @@ -0,0 +1,496 @@ +#ifdef _WIN32 +#include "windows.h" +#endif + +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cista/mmap.h" + +#include "utl/parallel_for.h" + +#include "fmt/core.h" + +#include "osr/extract/extract.h" +#include "osr/geojson.h" +#include "osr/location.h" +#include "osr/lookup.h" +#include "osr/routing/algorithms.h" +#include "osr/routing/bidirectional.h" +#include "osr/routing/cch.h" +#include "osr/routing/dijkstra.h" +#include "osr/routing/profile.h" +#include "osr/routing/profiles/car.h" +#include "osr/routing/route.h" +#include "osr/types.h" +#include "osr/ways.h" + +//copy of CCH_dijkstra_astarbidir_test.cc with cch + + +namespace fs = std::filesystem; +using namespace osr; + +constexpr auto const kUseMultithreading = false; +constexpr auto const kPrintDebugGeojson = false; +constexpr auto const kMaxMatchDistance = 100; +constexpr auto const kMaxAllowedPathDifferenceRatio = 0.5; + +void cch_load(std::string_view raw_data, std::string_view data_dir) { + if (fs::exists(raw_data)) { + auto const p = fs::path{data_dir}; + auto ec = std::error_code{}; + fs::remove_all(p, ec); + fs::create_directories(p, ec); + osr::extract(false, raw_data, data_dir, fs::path{}); + } +} + +template +void cch_run(ways const& w, + lookup const& l, + unsigned const n_samples, + unsigned const max_cost, + direction const dir, + search_profile const profile) { + + auto const from_tos = [&]() { + auto prng = std::mt19937{}; + auto distr = + std::uniform_int_distribution{0, w.n_nodes() - 1}; + auto from_tos = std::vector>{}; + for (auto i = 0U; i != n_samples; ++i) { + from_tos.emplace_back(distr(prng), distr(prng)); + } + return from_tos; + }(); + + auto n_congruent_astar = std::atomic{0U}; + auto n_congruent_cch = std::atomic{0U}; + auto n_empty_matches = std::atomic{0U}; + + auto reference_times = std::vector{}; + auto experiment_times = std::vector{}; + auto cch_experiment_times = std::vector(); + + auto m = std::mutex{}; + + auto const single_run = [&](std::pair const from_to) { + auto const from_node = from_to.first; + auto const from_loc = location{w.get_node_pos(from_node)}; + auto const to_node = from_to.second; + auto const to_loc = location{w.get_node_pos(to_node)}; + + auto const node_pinned_matches = + [&](location const& loc, node_idx_t const n, bool const reverse) { + auto matches = l.match

(typename P::parameters{}, loc, reverse, dir, + kMaxMatchDistance, nullptr); + std::erase_if(matches, [&](auto const& wc) { + return wc.left_.node_ != n && wc.right_.node_ != n; + }); + if (matches.size() > 1) { + + } + return matches; + }; + auto const from_matches = node_pinned_matches(from_loc, from_node, false); + auto const to_matches = node_pinned_matches(to_loc, to_node, true); + if (from_matches.empty() || to_matches.empty()) { + ++n_empty_matches; + } + + auto const from_matches_span = + std::span{begin(from_matches), end(from_matches)}; + auto const to_matches_span = std::span{begin(to_matches), end(to_matches)}; + + auto const reference_start = std::chrono::steady_clock::now(); + auto const reference = [&]() { + try { + return route(typename P::parameters{}, w, l, profile, from_loc, + to_loc, from_matches_span, to_matches_span, max_cost, dir, + nullptr, nullptr, nullptr, routing_algorithm::kDijkstra); + } catch (std::exception const& ex) { + fmt::println("dijkstra exception: {}", ex.what()); + throw ex; + } + }(); + auto const reference_time = + std::chrono::steady_clock::now() - reference_start; + + auto const experiment_start = std::chrono::steady_clock::now(); + auto const experiment = [&]() { + try { + return route(typename P::parameters{}, w, l, profile, from_loc, + to_loc, from_matches_span, to_matches_span, max_cost, dir, + nullptr, nullptr, nullptr, routing_algorithm::kAStarBi); + } catch (std::exception const& ex) { + fmt::println("a* bidir exception: {}", ex.what()); + throw ex; + } + }(); + auto const experiment_time = + std::chrono::steady_clock::now() - experiment_start; + + /* + cch + */ + + auto const cch_experiment_start = std::chrono::steady_clock::now(); + auto const cch_experiment = [&]() { + try { + return route(typename P::parameters{}, w, l, profile, from_loc, + to_loc, from_matches_span, to_matches_span, max_cost, dir, + nullptr, nullptr, nullptr, routing_algorithm::kCCH); + } catch (std::exception const& ex) { + fmt::println("CCH exception: {}", ex.what()); + throw ex; + } + }(); + auto const cch_experiment_time = + std::chrono::steady_clock::now() - cch_experiment_start; + + auto astar_pass = + (reference.has_value() == experiment.has_value() && (!reference || !experiment || (reference->cost_ == experiment->cost_ ))); + + auto cch_pass = + (reference.has_value() == cch_experiment.has_value() && (!reference || !cch_experiment || (reference->cost_ == cch_experiment->cost_))); + + if(astar_pass){ ++n_congruent_astar; } + if(cch_pass){ ++n_congruent_cch; } + + if(!astar_pass || !cch_pass){ + auto const print_result = [&](std::string_view name, auto const& p, + auto const& t) { + fmt::println( + "{:10}: {:11} --> {:11} | {} | time: " + "{}:{:0>3}:{:0>3} s", + name, w.node_to_osm_[from_node], w.node_to_osm_[to_node], + p ? fmt::format("cost: {:5} | dist: {:>10.2f}", p->cost_, p->dist_) + : "no result", + std::chrono::duration_cast(t).count(), + std::chrono::duration_cast(t).count() % + 1000, + std::chrono::duration_cast(t).count() % + 1000); + if (p.has_value() && kPrintDebugGeojson) { + fmt::println("{}\n", to_featurecollection(w, p)); + } + + }; + print_result("dijkstra", reference, reference_time); + if( !astar_pass){print_result("a* bidir", experiment, experiment_time);} + if( !cch_pass){ print_result("cch", cch_experiment, cch_experiment_time);} + } + + if (!from_matches.empty() && !to_matches.empty()) { + auto const guard = std::lock_guard{m}; + reference_times.emplace_back(reference_time); + experiment_times.emplace_back(experiment_time); + cch_experiment_times.emplace_back(cch_experiment_time); + } + }; + + if constexpr (kUseMultithreading) { + for (auto i = std::size_t{0}; + i != from_tos.size() && cch_experiment_times.empty(); ++i) { + single_run(from_tos[i]); + } + n_congruent_astar = 0U; + n_congruent_cch = 0U; + n_empty_matches = 0U; + reference_times.clear(); + experiment_times.clear(); + cch_experiment_times.clear(); + utl::parallel_for(from_tos, single_run); + } else { + std::for_each(begin(from_tos), end(from_tos), single_run); + } + auto const non_empty_samples = n_samples - n_empty_matches; + auto const non_empty_astar = n_congruent_astar - n_empty_matches; + auto const non_empty_cch = n_congruent_cch - n_empty_matches; + + auto const percent = [&](unsigned const congruent) { + return (static_cast(congruent) / + static_cast(non_empty_samples)) * + 100; + }; + + // no a* validation + EXPECT_EQ(non_empty_samples, non_empty_cch); + fmt::println("cch congruent on non-empty: {}/{} ({:3.1f}%)", non_empty_cch, + non_empty_samples, percent(non_empty_cch)); + fmt::println("a* bidir congruent on non-empty: {}/{} ({:3.1f}%)", + non_empty_astar, non_empty_samples, percent(non_empty_astar)); + + if (non_empty_cch == non_empty_samples) { + auto const dijkstra_total_time = static_cast( + std::reduce(begin(reference_times), end(reference_times)).count() + ); + auto const astar_total_time = static_cast( + std::reduce(begin(experiment_times), end(experiment_times)).count() + ); + auto const cch_total_time = static_cast( + std::reduce(begin(cch_experiment_times), end(cch_experiment_times)).count() + ); + + fmt::println( "speedup a* vs dijkstra on non-empty: {:.2f}", dijkstra_total_time / astar_total_time); + fmt::println( "speedup cch vs dijkstra on non-epty: {:.2f}", dijkstra_total_time / cch_total_time); + fmt::println( "speedup cch vs a* on non-empty: {:.2f}", astar_total_time / cch_total_time); + + if constexpr (!kUseMultithreading) { + auto const warmup_hit = *std::max_element(begin(cch_experiment_times), end(cch_experiment_times)); + auto const cch_total_time_warm = cch_total_time - static_cast(warmup_hit.count()); + fmt::println( "speedup WARM cch vs dijkstra on non-epty: {:.2f}", dijkstra_total_time / cch_total_time_warm); + fmt::println( "speedup WARM cch vs a* on non-empty: {:.2f}", astar_total_time / cch_total_time_warm); + } + } +} + +TEST(CCH_dijkstra_astarbidir, monaco_fwd) { + auto const raw_data = "test/monaco.osm.pbf"; + auto const data_dir = "test/monaco"; + auto const num_samples = 1000U; + auto const max_cost = 2 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kCar); +} + +TEST(CCH_dijkstra_astarbidir, monaco_bus_fwd) { + auto const raw_data = "test/monaco.osm.pbf"; + auto const data_dir = "test/monaco"; + auto const num_samples = 1000U; + auto const max_cost = 2 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kBus); +} + +TEST(CCH_dijkstra_astarbidir, monaco_bwd) { + auto const raw_data = "test/monaco.osm.pbf"; + auto const data_dir = "test/monaco"; + auto const num_samples = 1000U; + auto const max_cost = 2 * 3600U; + auto constexpr dir = direction::kBackward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kCar); +} + +TEST(CCH_dijkstra_astarbidir, monaco_bus_bwd) { + auto const raw_data = "test/monaco.osm.pbf"; + auto const data_dir = "test/monaco"; + auto const num_samples = 1000U; + auto const max_cost = 2 * 3600U; + auto constexpr dir = direction::kBackward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kBus); +} + +TEST(CCH_dijkstra_astarbidir, hamburg) { + auto const raw_data = "test/hamburg.osm.pbf"; + auto const data_dir = "test/hamburg"; + auto const num_samples = 5000U; + auto const max_cost = 3 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kCar); +} + +TEST(CCH_dijkstra_astarbidir, hamburg_bus_fwd) { + auto const raw_data = "test/hamburg.osm.pbf"; + auto const data_dir = "test/hamburg"; + auto const num_samples = 5000U; + auto const max_cost = 3 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kBus); +} + +TEST(CCH_dijkstra_astarbidir, DISABLED_hamburg_bwd) { + auto const raw_data = "test/hamburg.osm.pbf"; + auto const data_dir = "test/hamburg"; + auto const num_samples = 5000U; + auto const max_cost = 3 * 3600U; + auto constexpr dir = direction::kBackward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kCar); +} + +TEST(CCH_dijkstra_astarbidir, DISABLED_hamburg_bus_bwd) { + auto const raw_data = "test/hamburg.osm.pbf"; + auto const data_dir = "test/hamburg"; + auto const num_samples = 5000U; + auto const max_cost = 3 * 3600U; + auto constexpr dir = direction::kBackward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kBus); +} + +TEST(CCH_dijkstra_astarbidir, DISABLED_switzerland) { + auto const raw_data = "test/switzerland.osm.pbf"; + auto const data_dir = "test/switzerland"; + auto const num_samples = 1000U; + auto const max_cost = 5 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kCar); +} + +TEST(CCH_dijkstra_astarbidir, DISABLED_switzerland_bus) { + auto const raw_data = "test/switzerland.osm.pbf"; + auto const data_dir = "test/switzerland"; + auto const num_samples = 1000U; + auto const max_cost = 5 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kBus); +} + +TEST(CCH_dijkstra_astarbidir, DISABLED_switzerland_bwd) { + auto const raw_data = "test/switzerland.osm.pbf"; + auto const data_dir = "test/switzerland"; + auto const num_samples = 1000U; + auto const max_cost = 5 * 3600U; + auto constexpr dir = direction::kBackward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kCar); +} + +TEST(CCH_dijkstra_astarbidir, DISABLED_switzerland_bwd_bus) { + auto const raw_data = "test/switzerland.osm.pbf"; + auto const data_dir = "test/switzerland"; + auto const num_samples = 1000U; + auto const max_cost = 5 * 3600U; + auto constexpr dir = direction::kBackward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kBus); +} + +TEST(CCH_dijkstra_astarbidir, DISABLED_germany) { + auto const raw_data = "test/germany.osm.pbf"; + auto const data_dir = "test/germany"; + constexpr auto const num_samples = 50U; + constexpr auto const max_cost = 12 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + cch_load(raw_data, data_dir); + auto const w = osr::ways{data_dir, cista::mmap::protection::READ}; + auto const l = osr::lookup{w, data_dir, cista::mmap::protection::READ}; + + cch_run(w, l, num_samples, max_cost, dir, search_profile::kCar); +} \ No newline at end of file diff --git a/web/index.html b/web/index.html index 134b6c5f..5e9ca0f5 100644 --- a/web/index.html +++ b/web/index.html @@ -140,6 +140,7 @@ From 0fc24fa34949e40e88525508ed1e5c4553985616 Mon Sep 17 00:00:00 2001 From: p <149105134+peisha2@users.noreply.github.com> Date: Mon, 28 Sep 2026 17:58:46 +0200 Subject: [PATCH 16/17] clang21 formatting, cast std::thread::hardware_concurrency() to int --- include/osr/routing/algorithms.h | 2 +- include/osr/routing/cch.h | 735 ++++++++++++++------------- src/route.cc | 378 +++++++------- src/ways.cc | 4 +- test/cch_dijkstra_astarbidir_test.cc | 129 ++--- 5 files changed, 641 insertions(+), 607 deletions(-) diff --git a/include/osr/routing/algorithms.h b/include/osr/routing/algorithms.h index 05a70417..89cc41b6 100644 --- a/include/osr/routing/algorithms.h +++ b/include/osr/routing/algorithms.h @@ -5,7 +5,7 @@ namespace osr { -enum class routing_algorithm : std::uint8_t { kDijkstra, kAStarBi, kCCH}; +enum class routing_algorithm : std::uint8_t { kDijkstra, kAStarBi, kCCH }; routing_algorithm to_algorithm(std::string_view); diff --git a/include/osr/routing/cch.h b/include/osr/routing/cch.h index a02f9328..1d7f753f 100644 --- a/include/osr/routing/cch.h +++ b/include/osr/routing/cch.h @@ -18,408 +18,415 @@ namespace osr { template struct cch { - struct shortcut { - /* from*/ - typename P::node to_; - typename P::node inbetween_up_; - typename P::node inbetween_down_; - cost_t weight_up_{kInfeasible}; //up and down for directions - cost_t weight_down_{kInfeasible}; - bool is_shortcut_up_{false}; - bool is_shortcut_down_{false}; - }; - - struct item { - typename P::node n_; - std::uint32_t importance_; - }; - - struct route{ - cost_t cost_{kInfeasible}; - typename P::node intersection_{}; - //pairs upgraph edges (to unpack) - std::vector packed_path_; - }; - - struct pq_entry{ - cost_t cost_; - typename P::node node_; - - bool operator>(pq_entry const& other_entry) const{ - return cost_>other_entry.cost_; - } - }; + struct shortcut { + /* from*/ + typename P::node to_; + typename P::node inbetween_up_; + typename P::node inbetween_down_; + cost_t weight_up_{kInfeasible}; // up and down for directions + cost_t weight_down_{kInfeasible}; + bool is_shortcut_up_{false}; + bool is_shortcut_down_{false}; + }; + + struct item { + typename P::node n_; + std::uint32_t importance_; + }; + + struct route { + cost_t cost_{kInfeasible}; + typename P::node intersection_{}; + // pairs upgraph edges (to unpack) + std::vector packed_path_; + }; + + struct pq_entry { + cost_t cost_; + typename P::node node_; + + bool operator>(pq_entry const& other_entry) const { + return cost_ > other_entry.cost_; + } + }; - osr::hash_map rank_; + osr::hash_map rank_; - osr::hash_map> up_graph_; + osr::hash_map> up_graph_; - std::size_t n_nodes_{}; //topology dataset + std::size_t n_nodes_{}; // topology dataset - void build_pre_order(ways const& way){ - std::vector nodes; - for (node_idx_t n = node_idx_t{0}; n < way.n_nodes(); ++n){ - auto const importance = way.r_->node_importance_[n]; - P::resolve_all(*way.r_,n,level_t{}, [&](typename P::node const vn){ - nodes.push_back(item{ - vn,importance - }); - }); - } - std::sort(nodes.begin(), nodes.end(), [&](item const& a, item const& b) { - if (a.importance_ != b.importance_) { - return a.importance_ < b.importance_; - } - return a.n_ < b.n_; // guaranteed no tie - }); - - std::uint32_t rank = 0; - for (auto const& x : nodes) { - rank_[x.n_] = rank++; - } + void build_pre_order(ways const& way) { + std::vector nodes; + for (node_idx_t n = node_idx_t{0}; n < way.n_nodes(); ++n) { + auto const importance = way.r_->node_importance_[n]; + P::resolve_all(*way.r_, n, level_t{}, [&](typename P::node const vn) { + nodes.push_back(item{vn, importance}); + }); } - - template - void for_each_edge(ways const& way, - typename P::parameters const& params, - Fn&& fn) const { - for(node_idx_t n = node_idx_t{}; n( - params, - *way.r_, - u, - nullptr,//blocked - nullptr,//additional - nullptr,//elevation_storage - [&](typename P::node const v, - cost_t const cost, - distance_t /*dist*/, - way_idx_t /*way*/, - int /*0*/, - int /*0*/, - elevation_storage::elevation /*elevation*/, - bool /*false*/ - ){ - fn(u,v,cost); - } - ); - } - ); - } + std::sort(nodes.begin(), nodes.end(), [&](item const& a, item const& b) { + if (a.importance_ != b.importance_) { + return a.importance_ < b.importance_; + } + return a.n_ < b.n_; // guaranteed no tie + }); + + std::uint32_t rank = 0; + for (auto const& x : nodes) { + rank_[x.n_] = rank++; } + } + + template + void for_each_edge(ways const& way, + typename P::parameters const& params, + Fn&& fn) const { + for (node_idx_t n = node_idx_t{}; n < way.n_nodes(); ++n) { + P::resolve_all(*way.r_, n, level_t{}, [&](typename P::node const u) { + P::template adjacent( + params, *way.r_, u, + nullptr, // blocked + nullptr, // additional + nullptr, // elevation_storage + [&](typename P::node const v, cost_t const cost, + distance_t /*dist*/, way_idx_t /*way*/, int /*0*/, int /*0*/, + elevation_storage::elevation /*elevation*/, bool /*false*/ + ) { fn(u, v, cost); }); + }); + } + } - std::vector get_ordered_nodes(){ - std::vector ordered_nodes(rank_.size()); - for(auto const& pair:rank_){ - ordered_nodes.at(pair.second) = pair.first; - } - return ordered_nodes; + std::vector get_ordered_nodes() { + std::vector ordered_nodes(rank_.size()); + for (auto const& pair : rank_) { + ordered_nodes.at(pair.second) = pair.first; } + return ordered_nodes; + } - //tpopolgy - template - void build_topology(ways const& way){ - build_pre_order(way); - n_nodes_ = way.n_nodes(); + // tpopolgy + template + void build_topology(ways const& way) { + build_pre_order(way); + n_nodes_ = way.n_nodes(); - osr::hash_map> adj_temp; + osr::hash_map> adj_temp; - for_each_edge(way, typename P::parameters{}, - [&](typename P::node const u, typename P::node v, cost_t /*cost*/ ){ - if(u==v){ return;} //self edge - adj_temp[u].push_back(v); - adj_temp[v].push_back(u); - } - ); + for_each_edge( + way, typename P::parameters{}, + [&](typename P::node const u, typename P::node v, cost_t /*cost*/) { + if (u == v) { + return; + } // self edge + adj_temp[u].push_back(v); + adj_temp[v].push_back(u); + }); - auto const ordered_nodes = get_ordered_nodes(); + auto const ordered_nodes = get_ordered_nodes(); - for(typename P::node const v : ordered_nodes) { - if(!up_graph_.contains(v)) { - up_graph_[v] = std::vector{}; - } - } + for (typename P::node const v : ordered_nodes) { + if (!up_graph_.contains(v)) { + up_graph_[v] = std::vector{}; + } + } - for (typename P::node const v : ordered_nodes) { - auto const rank_v = rank_.at(v); - std::vector upward_neighbour; - for(auto neighbour : adj_temp[v]){ - if(rank_.at(neighbour)> rank_v){ - bool duplicate(false); - for(typename P::node const duplicates:upward_neighbour){ - if(duplicates==neighbour){ - duplicate = true; - break; - } - } - if(!duplicate){ - upward_neighbour.push_back(neighbour); - } - } - } - //non-shortcut roads - auto& v_up_edges = up_graph_.at(v); - for(auto const u : upward_neighbour){ - v_up_edges.push_back({u, typename P::node{}, typename P::node{}, kInfeasible, kInfeasible, false,false}); + for (typename P::node const v : ordered_nodes) { + auto const rank_v = rank_.at(v); + std::vector upward_neighbour; + for (auto neighbour : adj_temp[v]) { + if (rank_.at(neighbour) > rank_v) { + bool duplicate(false); + for (typename P::node const duplicates : upward_neighbour) { + if (duplicates == neighbour) { + duplicate = true; + break; } - - //shortcuts - for(std::size_t i=0; i + void customise(ways const& way, typename P::parameters const& params) { + for (auto& [node, edges] : up_graph_) { + for (auto& edge : edges) { + edge.weight_up_ = kInfeasible; + edge.weight_down_ = kInfeasible; + edge.is_shortcut_up_ = false; + edge.is_shortcut_down_ = false; + } } - //phase2 - template - void customise(ways const& way, typename P::parameters const& params){ - for(auto& [node, edges] : up_graph_){ - for(auto& edge : edges){ - edge.weight_up_ = kInfeasible; - edge.weight_down_ = kInfeasible; - edge.is_shortcut_up_ = false; - edge.is_shortcut_down_ = false; + // real edges + for_each_edge( + way, params, + [&](typename P::node const u, typename P::node const v, cost_t cost) { + typename P::node source = rank_.at(u) < rank_.at(v) ? u : v; + typename P::node target = + rank_.at(u) > rank_.at(v) ? u : v; // higher ranks + + for (auto& edge : up_graph_.at(source)) { // edges to higher rank + if (edge.to_ == target) { + if (u == source) { + edge.weight_up_ = std::min(edge.weight_up_, cost); + } else { + edge.weight_down_ = std::min(edge.weight_down_, cost); + } } - } - - //real edges - for_each_edge(way, params,[&](typename P::node const u, typename P::node const v, cost_t cost){ - - typename P::node source = rank_.at(u)rank_.at(v) ? u : v; //higher ranks + } + }); - for (auto& edge : up_graph_.at(source)) { //edges to higher rank - if (edge.to_ == target) { - if( u == source){ - edge.weight_up_ = std::min(edge.weight_up_, cost); - }else{ - edge.weight_down_ = std::min(edge.weight_down_, cost); - } - } + // shortcuts + auto const ordered_nodes = get_ordered_nodes(); // low->high + + for (typename P::node v : ordered_nodes) { + auto const& upward_edges = up_graph_.at(v); + + for (std::size_t i = 0; i < upward_edges.size(); ++i) { + for (std::size_t j = i + 1; j < upward_edges.size(); ++j) { + + auto const& edge_vu = upward_edges[i]; // edge vu u w + auto const& edge_vw = upward_edges[j]; // Edge vw v + + auto u = edge_vu.to_; + auto w = edge_vw.to_; + + typename P::node source = rank_.at(u) < rank_.at(w) ? u : w; + typename P::node target = rank_.at(u) > rank_.at(w) ? u : w; + + cost_t cost_src_to_target; // u v w + cost_t cost_target_to_src; // w v u + + if (u == source) { // base case; fwd down up + cost_src_to_target = + (edge_vu.weight_down_ == kInfeasible || + edge_vw.weight_up_ == kInfeasible) + ? kInfeasible + : edge_vu.weight_down_ + edge_vw.weight_up_; + + cost_target_to_src = + (edge_vw.weight_down_ == kInfeasible || + edge_vu.weight_up_ == kInfeasible) + ? kInfeasible + : edge_vw.weight_down_ + edge_vu.weight_up_; + } else { + cost_target_to_src = + (edge_vu.weight_down_ == kInfeasible || + edge_vw.weight_up_ == kInfeasible) + ? kInfeasible + : edge_vu.weight_down_ + edge_vw.weight_up_; + + cost_src_to_target = + (edge_vw.weight_down_ == kInfeasible || + edge_vu.weight_up_ == kInfeasible) + ? kInfeasible + : edge_vw.weight_down_ + edge_vu.weight_up_; + } + + for (auto& shortcut : up_graph_.at(source)) { + if (shortcut.to_ == target) { + if (cost_src_to_target != kInfeasible && + cost_src_to_target < shortcut.weight_up_) { + shortcut.weight_up_ = cost_src_to_target; + shortcut.inbetween_up_ = v; + shortcut.is_shortcut_up_ = true; + } + if (cost_target_to_src != kInfeasible && + cost_target_to_src < shortcut.weight_down_) { + shortcut.weight_down_ = cost_target_to_src; + shortcut.inbetween_down_ = v; + shortcut.is_shortcut_down_ = true; + } } + } } - ); - - //shortcuts - auto const ordered_nodes = get_ordered_nodes(); //low->high - - for(typename P::node v : ordered_nodes){ - auto const& upward_edges = up_graph_.at(v); - - for(std::size_t i = 0; i < upward_edges.size(); ++i){ - for(std::size_t j = i+1; j < upward_edges.size(); ++j){ - - - auto const& edge_vu = upward_edges[i];// edge vu u w - auto const& edge_vw = upward_edges[j]; //Edge vw v - - auto u = edge_vu.to_; - auto w = edge_vw.to_; - - typename P::node source = rank_.at(u)rank_.at(w) ? u : w; - - cost_t cost_src_to_target; // u v w - cost_t cost_target_to_src; // w v u - - if( u == source){ //base case; fwd down up - cost_src_to_target = - (edge_vu.weight_down_ == kInfeasible || edge_vw.weight_up_ == kInfeasible) - ? kInfeasible - : edge_vu.weight_down_ + edge_vw.weight_up_; - - cost_target_to_src = - (edge_vw.weight_down_ == kInfeasible || edge_vu.weight_up_ == kInfeasible) - ? kInfeasible - : edge_vw.weight_down_ + edge_vu.weight_up_; - }else{ - cost_target_to_src = - (edge_vu.weight_down_ == kInfeasible || edge_vw.weight_up_ == kInfeasible) - ? kInfeasible - : edge_vu.weight_down_ + edge_vw.weight_up_; - - cost_src_to_target = - (edge_vw.weight_down_ == kInfeasible || edge_vu.weight_up_ == kInfeasible) - ? kInfeasible - : edge_vw.weight_down_ + edge_vu.weight_up_; - } - - for(auto& shortcut : up_graph_.at(source)){ - if( shortcut.to_ == target){ - if(cost_src_to_target != kInfeasible - && cost_src_to_target < shortcut.weight_up_){ - shortcut.weight_up_ = cost_src_to_target; - shortcut.inbetween_up_ = v; - shortcut.is_shortcut_up_ = true; - } - if(cost_target_to_src != kInfeasible - && cost_target_to_src < shortcut.weight_down_){ - shortcut.weight_down_ = cost_target_to_src; - shortcut.inbetween_down_ = v; - shortcut.is_shortcut_down_ = true; - } - } - } - - } - } - } - for(auto& [node, edges] : up_graph_){ - std::vector kept; - for(auto const& edge : edges){ - if(edge.weight_up_ != kInfeasible || edge.weight_down_ != kInfeasible){ - kept.push_back(edge); - } - } - edges = kept; + } + } + for (auto& [node, edges] : up_graph_) { + std::vector kept; + for (auto const& edge : edges) { + if (edge.weight_up_ != kInfeasible || + edge.weight_down_ != kInfeasible) { + kept.push_back(edge); } + } + edges = kept; + } + } + + void search(auto& pq, + auto& cost_map, + auto const& cost_map_bwd, + auto& pred_map, + route& result, + bool use_up) const { + auto current = pq.top(); + pq.pop(); + + auto const self = cost_map.find(current.node_); + if (self != cost_map.end() && + current.cost_ > self->second) { // skip if better alt + return; } - void search(auto& pq, auto& cost_map, auto const& cost_map_bwd, auto& pred_map, route& result, bool use_up) const { - auto current = pq.top(); - pq.pop(); - - auto const self = cost_map.find(current.node_); - if( self != cost_map.end() && current.cost_ > self->second){ //skip if better alt - return; - } + auto const current_key = cost_map_bwd.find(current.node_); + // check for improvement + if (current_key != cost_map_bwd.end()) { + cost_t total_cost = current.cost_ + current_key->second; + if (total_cost < result.cost_) { + result.cost_ = total_cost; + result.intersection_ = current.node_; + } + } - auto const current_key = cost_map_bwd.find(current.node_); - //check for improvement - if( current_key != cost_map_bwd.end()){ - cost_t total_cost = current.cost_ + current_key->second; - if( total_cost < result.cost_){ - result.cost_ = total_cost; - result.intersection_ = current.node_; - } + auto const current_up_key = up_graph_.find(current.node_); + if (current_up_key != up_graph_.end()) { + for (auto& edge : current_up_key->second) { + auto const edge_weight_dir = + use_up ? edge.weight_up_ : edge.weight_down_; + if (edge_weight_dir == kInfeasible) { + continue; } + auto const new_cost = current.cost_ + edge_weight_dir; - auto const current_up_key = up_graph_.find(current.node_); - if( current_up_key != up_graph_.end()){ - for(auto& edge:current_up_key->second){ - auto const edge_weight_dir = use_up ? edge.weight_up_ : edge.weight_down_; - if(edge_weight_dir == kInfeasible){continue;} - auto const new_cost = current.cost_ + edge_weight_dir; - - auto const target = cost_map.find(edge.to_); - if(target == cost_map.end() || target->second > new_cost ){ - cost_map[edge.to_] = new_cost; - pred_map[edge.to_] = current.node_; - pq.push({new_cost, edge.to_}); - } - } + auto const target = cost_map.find(edge.to_); + if (target == cost_map.end() || target->second > new_cost) { + cost_map[edge.to_] = new_cost; + pred_map[edge.to_] = current.node_; + pq.push({new_cost, edge.to_}); } + } } - - route query(std::vector> const& sources, - std::vector> const& targets) const { - route result{}; - - for (auto const& [source, source_cost] : sources) { - for (auto const& [target, target_cost] : targets) { - if (source == target) { - auto const total = clamp_cost(static_cast(source_cost) + target_cost); - if (total < result.cost_) { - result.cost_ = total; - result.intersection_ = source; - } - } - } - } - - std::priority_queue, std::greater> pq_fwd, pq_bwd; //min pq - hash_map fwd_cost, bwd_cost; - hash_map fwd_pred, bwd_pred; - - for (auto const& [source, source_cost] : sources) { - auto const it = fwd_cost.find(source); - if (it == fwd_cost.end() || source_cost < it->second) { - fwd_cost[source] = source_cost; - pq_fwd.push({source_cost, source}); - } + } + + route query( + std::vector> const& sources, + std::vector> const& targets) const { + route result{}; + + for (auto const& [source, source_cost] : sources) { + for (auto const& [target, target_cost] : targets) { + if (source == target) { + auto const total = + clamp_cost(static_cast(source_cost) + target_cost); + if (total < result.cost_) { + result.cost_ = total; + result.intersection_ = source; + } } + } + } - for (auto const& [target, target_cost] : targets) { - auto const it = bwd_cost.find(target); - if (it == bwd_cost.end() || target_cost < it->second) { - bwd_cost[target] = target_cost; - pq_bwd.push({target_cost, target}); - } - } + std::priority_queue, std::greater> + pq_fwd, pq_bwd; // min pq + hash_map fwd_cost, bwd_cost; + hash_map fwd_pred, bwd_pred; + + for (auto const& [source, source_cost] : sources) { + auto const it = fwd_cost.find(source); + if (it == fwd_cost.end() || source_cost < it->second) { + fwd_cost[source] = source_cost; + pq_fwd.push({source_cost, source}); + } + } - while(!pq_fwd.empty() || !pq_bwd.empty()){ - cost_t min_fwd = pq_fwd.empty() ? kInfeasible : pq_fwd.top().cost_; - cost_t min_bwd = pq_bwd.empty() ? kInfeasible : pq_bwd.top().cost_; + for (auto const& [target, target_cost] : targets) { + auto const it = bwd_cost.find(target); + if (it == bwd_cost.end() || target_cost < it->second) { + bwd_cost[target] = target_cost; + pq_bwd.push({target_cost, target}); + } + } - if ((pq_fwd.empty() || min_fwd >= result.cost_) && - (pq_bwd.empty() || min_bwd >= result.cost_)) { - break; - } - if( !pq_fwd.empty()&& min_fwd <= min_bwd){ //continue from short path - search(pq_fwd, fwd_cost, bwd_cost, fwd_pred, result, true); - }else if(!pq_bwd.empty()){ - search(pq_bwd, bwd_cost, fwd_cost, bwd_pred, result, false); - } + while (!pq_fwd.empty() || !pq_bwd.empty()) { + cost_t min_fwd = pq_fwd.empty() ? kInfeasible : pq_fwd.top().cost_; + cost_t min_bwd = pq_bwd.empty() ? kInfeasible : pq_bwd.top().cost_; + + if ((pq_fwd.empty() || min_fwd >= result.cost_) && + (pq_bwd.empty() || min_bwd >= result.cost_)) { + break; + } + if (!pq_fwd.empty() && min_fwd <= min_bwd) { // continue from short path + search(pq_fwd, fwd_cost, bwd_cost, fwd_pred, result, true); + } else if (!pq_bwd.empty()) { + search(pq_bwd, bwd_cost, fwd_cost, bwd_pred, result, false); + } + } + // build packed.. + // 1st (start->intersection) from the preds + if (result.cost_ != kInfeasible) { + + // backwards + auto node_to_start = result.intersection_; + while (true) { + result.packed_path_.push_back(node_to_start); + auto const it = fwd_pred.find(node_to_start); + if (it == fwd_pred.end()) { + break; } - - //build packed.. - //1st (start->intersection) from the preds - if(result.cost_ != kInfeasible){ - - //backwards - auto node_to_start = result.intersection_; - while(true){ - result.packed_path_.push_back(node_to_start); - auto const it = fwd_pred.find(node_to_start); - if(it == fwd_pred.end()){ break; } - node_to_start = it->second; - } - std::reverse(result.packed_path_.begin(), result.packed_path_.end()); - - //2nd (intersection -> target) - auto node_to_target = result.intersection_; - while(true){ - auto const it = bwd_pred.find(node_to_target); - if(it == bwd_pred.end()){ break; } - node_to_target = it->second; - result.packed_path_.push_back(node_to_target); - } - //result (start-intersection->target) + node_to_start = it->second; + } + std::reverse(result.packed_path_.begin(), result.packed_path_.end()); + + // 2nd (intersection -> target) + auto node_to_target = result.intersection_; + while (true) { + auto const it = bwd_pred.find(node_to_target); + if (it == bwd_pred.end()) { + break; } - return result; + node_to_target = it->second; + result.packed_path_.push_back(node_to_target); + } + // result (start-intersection->target) } - - - void preprocess(ways const& way, - direction const dir, - typename P::parameters const& params){ - if (dir == direction::kForward) { - build_topology(way); - customise(way, params); - } else { - build_topology(way); - customise(way, params); - } + return result; + } + + void preprocess(ways const& way, + direction const dir, + typename P::parameters const& params) { + if (dir == direction::kForward) { + build_topology(way); + customise(way, params); + } else { + build_topology(way); + customise(way, params); } + } }; -}// namespace osr +} // namespace osr diff --git a/src/route.cc b/src/route.cc index f9f499d0..abb8ab53 100644 --- a/src/route.cc +++ b/src/route.cc @@ -42,13 +42,13 @@ constexpr auto const kBottomKDefinitelyConsidered = 5; template cch

& get_cch(ways const& way, direction const dir, - typename P::parameters const& params){ + typename P::parameters const& params) { static std::map, cch

> cache; static std::mutex m; auto const lock = std::lock_guard{m}; auto& entry = cache[std::pair{way.p_, dir}]; - if(entry.n_nodes_ == 0){ + if (entry.n_nodes_ == 0) { entry.preprocess(way, dir, params); } return entry; @@ -335,7 +335,8 @@ best_candidate(typename P::parameters const& params, break; } auto best_node = P::node::invalid(); - auto best_cost = path{.cost_ = std::numeric_limits::max()}; //path{.xyz} + auto best_cost = + path{.cost_ = std::numeric_limits::max()}; // path{.xyz} auto best = static_cast(nullptr); for (auto const x : {&dest.left_, &dest.right_}) { @@ -546,26 +547,31 @@ void unpack_shortcut(typename P::parameters const& params, typename P::node const v, direction const dir, double& dist, - std::vector& segments){ - if(u==v){return;} + std::vector& segments) { + if (u == v) { + return; + } - typename P::node source = b.rank_.at(u)b.rank_.at(v) ? u : v; + typename P::node source = b.rank_.at(u) < b.rank_.at(v) ? u : v; + typename P::node target = b.rank_.at(u) > b.rank_.at(v) ? u : v; auto const dir_up = (u == source); auto const node_it = b.up_graph_.find(source); - if(node_it != b.up_graph_.end()){ - for(auto& shortcut : node_it->second){ - if(shortcut.to_ == target){ - bool const is_shortcut = dir_up ? shortcut.is_shortcut_up_ : shortcut.is_shortcut_down_; - if(is_shortcut){ - auto const inbetween = dir_up ? shortcut.inbetween_up_ : shortcut.inbetween_down_; + if (node_it != b.up_graph_.end()) { + for (auto& shortcut : node_it->second) { + if (shortcut.to_ == target) { + bool const is_shortcut = + dir_up ? shortcut.is_shortcut_up_ : shortcut.is_shortcut_down_; + if (is_shortcut) { + auto const inbetween = + dir_up ? shortcut.inbetween_up_ : shortcut.inbetween_down_; unpack_shortcut

(params, w, b, u, inbetween, dir, dist, segments); unpack_shortcut

(params, w, b, inbetween, v, dir, dist, segments); - }else{ - //real edge add_path for polyline - auto const weight = dir_up ? shortcut.weight_up_ : shortcut.weight_down_; + } else { + // real edge add_path for polyline + auto const weight = + dir_up ? shortcut.weight_up_ : shortcut.weight_down_; dist += add_path

(params, w, *w.r_, nullptr, nullptr, nullptr, u, v, weight, segments, dir); } @@ -588,143 +594,155 @@ std::optional route_cch(typename P::parameters const& params, sharing_data const* sharing, elevation_storage const* elevations) { - if(from_match.empty() || to_match.empty()){ - return std::nullopt; - } - - if (auto const direct = try_direct(from, to); direct.has_value()) { - return *direct; - } - - //blcked and sharing don't work with cch. height irrelevant - if(sharing != nullptr){ - return route_dijkstra(params, w, l, get_dijkstra

(), from, to, - from_match, to_match, max, dir, blocked, - sharing, elevations); - } - - auto start_states = std::vector>{}; - auto target_states = std::vector>{}; - auto query_result = typename cch

::route{}; - auto winning_start = static_cast(nullptr); - auto winning_target = static_cast(nullptr); - - for(auto const& start_cand : from_match){ - start_states.clear(); - for(auto const* nc : {&start_cand.left_, &start_cand.right_}){ - if(nc->valid() && nc->cost_ < max){ - P::resolve_start_node(*w.r_, start_cand.way_, nc->node_, from.lvl_, dir, [&](auto const n) { - start_states.emplace_back(n, nc->cost_); - }); - } - } - if(start_states.empty()){ continue; } - - for(auto const& target_cand : to_match){ - //drive between same component only - if(w.r_->way_component_[start_cand.way_] != w.r_->way_component_[target_cand.way_]){ - continue; - } - - target_states.clear(); - for(auto const* nc : {&target_cand.left_, &target_cand.right_}){ - if(nc->valid()){ - //only legal states - P::resolve_all(*w.r_, nc->node_, to.lvl_, [&](auto const n) { - if(!P::is_dest_reachable(params, *w.r_, n, target_cand.way_, - flip(opposite(dir), nc->way_dir_), dir)){ - return; - } - target_states.emplace_back(n, nc->cost_); - }); - } - } - if(target_states.empty()){ continue; } - - auto const a = b.query(start_states, target_states); - //max param - if(a.cost_ != kInfeasible && a.cost_ < max){ - query_result = a; //first hit - winning_start = &start_cand; - winning_target = &target_cand; - break; - } - } - if(query_result.cost_ != kInfeasible){ break; } - } - - if(query_result.cost_ == kInfeasible){ - return std::nullopt; - } - auto segments = std::vector{}; - auto dist = 0.0; - - auto const& start_cand = *winning_start; - auto const& target_cand = *winning_target; - auto const start_node = query_result.packed_path_.front(); - auto const target_node = query_result.packed_path_.back(); - //start - auto const& start_node_cand = - start_node.get_node() == start_cand.left_.node_ ? start_cand.left_ : start_cand.right_; - segments.push_back({ - .polyline_ = l.get_node_candidate_path(start_cand, start_node_cand, dir == direction::kBackward, from), - .from_level_ = start_node_cand.lvl_, - .to_level_ = start_node_cand.lvl_, - .from_ = dir == direction::kBackward ? start_node.get_node() : node_idx_t::invalid(), - .to_ = dir == direction::kForward ? start_node.get_node() : node_idx_t::invalid(), - .cost_ = start_node_cand.cost_, - .dist_ = static_cast(start_node_cand.dist_to_node_), - .elevation_ = {}, - .mode_ = start_node.get_mode() - }); - - //packedpath result = edge - for(std::size_t i = 1; i < query_result.packed_path_.size(); ++i){ - unpack_shortcut

(params, w, b, query_result.packed_path_[i-1], - query_result.packed_path_[i], dir, dist, segments); - } - - auto const& target_node_cand = - target_node.get_node() == target_cand.left_.node_ ? target_cand.left_ : target_cand.right_; - segments.push_back({ - .polyline_ = l.get_node_candidate_path(target_cand, target_node_cand, dir == direction::kForward, to), - .from_level_ = target_node_cand.lvl_, - .to_level_ = target_node_cand.lvl_, - .from_ = dir == direction::kForward ? target_node.get_node() : node_idx_t::invalid(), - .to_ = dir == direction::kBackward ? target_node.get_node() : node_idx_t::invalid(), - .cost_ = target_node_cand.cost_, - .dist_ = static_cast(target_node_cand.dist_to_node_), - .elevation_ ={}, - .mode_ = target_node.get_mode() - }); - - //fwd start->target - if(dir == direction::kBackward){ - std::reverse(begin(segments), end(segments)); - } - - //no elevation - auto p = path{ - .cost_ = query_result.cost_, - .dist_ = start_node_cand.dist_to_node_ + dist + target_node_cand.dist_to_node_, - .segments_ = segments, - }; - - //blocked -> dijkstra - if(blocked != nullptr){ - for(auto const& segment : p.segments_){ - for(auto const n : {segment.from_, segment.to_}){ - if(n != node_idx_t::invalid() && blocked->test(n)){ - return route_dijkstra(params, w, l, get_dijkstra

(), from, to, - from_match, to_match, max, dir, blocked, - sharing, elevations); - } - } - } - } - return p; -} + if (from_match.empty() || to_match.empty()) { + return std::nullopt; + } + + if (auto const direct = try_direct(from, to); direct.has_value()) { + return *direct; + } + + // blcked and sharing don't work with cch. height irrelevant + if (sharing != nullptr) { + return route_dijkstra(params, w, l, get_dijkstra

(), from, to, from_match, + to_match, max, dir, blocked, sharing, elevations); + } + + auto start_states = std::vector>{}; + auto target_states = std::vector>{}; + auto query_result = typename cch

::route{}; + auto winning_start = static_cast(nullptr); + auto winning_target = static_cast(nullptr); + + for (auto const& start_cand : from_match) { + start_states.clear(); + for (auto const* nc : {&start_cand.left_, &start_cand.right_}) { + if (nc->valid() && nc->cost_ < max) { + P::resolve_start_node( + *w.r_, start_cand.way_, nc->node_, from.lvl_, dir, + [&](auto const n) { start_states.emplace_back(n, nc->cost_); }); + } + } + if (start_states.empty()) { + continue; + } + + for (auto const& target_cand : to_match) { + // drive between same component only + if (w.r_->way_component_[start_cand.way_] != + w.r_->way_component_[target_cand.way_]) { + continue; + } + + target_states.clear(); + for (auto const* nc : {&target_cand.left_, &target_cand.right_}) { + if (nc->valid()) { + // only legal states + P::resolve_all(*w.r_, nc->node_, to.lvl_, [&](auto const n) { + if (!P::is_dest_reachable(params, *w.r_, n, target_cand.way_, + flip(opposite(dir), nc->way_dir_), dir)) { + return; + } + target_states.emplace_back(n, nc->cost_); + }); + } + } + if (target_states.empty()) { + continue; + } + + auto const a = b.query(start_states, target_states); + // max param + if (a.cost_ != kInfeasible && a.cost_ < max) { + query_result = a; // first hit + winning_start = &start_cand; + winning_target = &target_cand; + break; + } + } + if (query_result.cost_ != kInfeasible) { + break; + } + } + + if (query_result.cost_ == kInfeasible) { + return std::nullopt; + } + auto segments = std::vector{}; + auto dist = 0.0; + + auto const& start_cand = *winning_start; + auto const& target_cand = *winning_target; + auto const start_node = query_result.packed_path_.front(); + auto const target_node = query_result.packed_path_.back(); + // start + auto const& start_node_cand = start_node.get_node() == start_cand.left_.node_ + ? start_cand.left_ + : start_cand.right_; + segments.push_back( + {.polyline_ = l.get_node_candidate_path( + start_cand, start_node_cand, dir == direction::kBackward, from), + .from_level_ = start_node_cand.lvl_, + .to_level_ = start_node_cand.lvl_, + .from_ = dir == direction::kBackward ? start_node.get_node() + : node_idx_t::invalid(), + .to_ = dir == direction::kForward ? start_node.get_node() + : node_idx_t::invalid(), + .cost_ = start_node_cand.cost_, + .dist_ = static_cast(start_node_cand.dist_to_node_), + .elevation_ = {}, + .mode_ = start_node.get_mode()}); + + // packedpath result = edge + for (std::size_t i = 1; i < query_result.packed_path_.size(); ++i) { + unpack_shortcut

(params, w, b, query_result.packed_path_[i - 1], + query_result.packed_path_[i], dir, dist, segments); + } + + auto const& target_node_cand = + target_node.get_node() == target_cand.left_.node_ ? target_cand.left_ + : target_cand.right_; + segments.push_back( + {.polyline_ = l.get_node_candidate_path(target_cand, target_node_cand, + dir == direction::kForward, to), + .from_level_ = target_node_cand.lvl_, + .to_level_ = target_node_cand.lvl_, + .from_ = dir == direction::kForward ? target_node.get_node() + : node_idx_t::invalid(), + .to_ = dir == direction::kBackward ? target_node.get_node() + : node_idx_t::invalid(), + .cost_ = target_node_cand.cost_, + .dist_ = static_cast(target_node_cand.dist_to_node_), + .elevation_ = {}, + .mode_ = target_node.get_mode()}); + // fwd start->target + if (dir == direction::kBackward) { + std::reverse(begin(segments), end(segments)); + } + + // no elevation + auto p = path{ + .cost_ = query_result.cost_, + .dist_ = + start_node_cand.dist_to_node_ + dist + target_node_cand.dist_to_node_, + .segments_ = segments, + }; + + // blocked -> dijkstra + if (blocked != nullptr) { + for (auto const& segment : p.segments_) { + for (auto const n : {segment.from_, segment.to_}) { + if (n != node_idx_t::invalid() && blocked->test(n)) { + return route_dijkstra(params, w, l, get_dijkstra

(), from, to, + from_match, to_match, max, dir, blocked, + sharing, elevations); + } + } + } + } + return p; +} template std::vector> route( @@ -899,29 +917,29 @@ std::optional route_dijkstra(profile_parameters const& params, } std::optional cch_route(profile_parameters const& params, - ways const& w, - lookup const& l, - search_profile const profile, - location const& from, - location const& to, - cost_t const max, - direction const dir, - double const max_match_distance, - bitvec const* blocked, - sharing_data const* sharing, - elevation_storage const* elevations){ - return with_profile(profile, [&](P&&) -> std::optional { - - - + ways const& w, + lookup const& l, + search_profile const profile, + location const& from, + location const& to, + cost_t const max, + direction const dir, + double const max_match_distance, + bitvec const* blocked, + sharing_data const* sharing, + elevation_storage const* elevations) { + return with_profile(profile, [&](P&&) -> std::optional { auto const& pp = std::get(params); - auto const from_match = l.match

(pp, from, false, dir, max_match_distance, blocked); - auto const to_match = l.match

(pp, to, true, dir, max_match_distance, blocked); + auto const from_match = + l.match

(pp, from, false, dir, max_match_distance, blocked); + auto const to_match = + l.match

(pp, to, true, dir, max_match_distance, blocked); if (from_match.empty() || to_match.empty()) { return std::nullopt; } - return route_cch(pp, w, l, get_cch

(w, dir, pp), from, to, from_match, to_match, max, dir, blocked, sharing, elevations); + return route_cch(pp, w, l, get_cch

(w, dir, pp), from, to, from_match, + to_match, max, dir, blocked, sharing, elevations); }); } @@ -973,8 +991,7 @@ std::optional route(profile_parameters const& params, algo = routing_algorithm::kDijkstra; // TODO } - if (algo == routing_algorithm::kCCH && - profile != search_profile::kCar && + if (algo == routing_algorithm::kCCH && profile != search_profile::kCar && profile != search_profile::kBus) { algo = routing_algorithm::kDijkstra; // CCH only car/bus } @@ -993,12 +1010,11 @@ std::optional route(profile_parameters const& params, from_match, to_match, max, dir, blocked, sharing, elevations); }); - case routing_algorithm::kCCH: + case routing_algorithm::kCCH: return with_profile(profile, [&](P&&) { auto const& pp = std::get(params); - return route_cch(pp, w, l, - get_cch

(w, dir, pp), from, to, from_match, to_match, - max, dir, blocked, sharing, elevations); + return route_cch(pp, w, l, get_cch

(w, dir, pp), from, to, from_match, + to_match, max, dir, blocked, sharing, elevations); }); } throw utl::fail("not implemented"); @@ -1023,8 +1039,7 @@ std::optional route(profile_parameters const& params, profile == search_profile::kCarParking) { algo = routing_algorithm::kDijkstra; // TODO } - if (algo == routing_algorithm::kCCH && - profile != search_profile::kCar && + if (algo == routing_algorithm::kCCH && profile != search_profile::kCar && profile != search_profile::kBus) { algo = routing_algorithm::kDijkstra; // CCH only car/bus } @@ -1037,10 +1052,9 @@ std::optional route(profile_parameters const& params, max_match_distance, blocked, sharing, elevations); case routing_algorithm::kCCH: - return cch_route(params, w, l, profile, from, to, max, dir, - max_match_distance, blocked, sharing, - elevations); - } + return cch_route(params, w, l, profile, from, to, max, dir, + max_match_distance, blocked, sharing, elevations); + } throw utl::fail("not implemented"); } diff --git a/src/ways.cc b/src/ways.cc index 6c10a6f0..963fb845 100644 --- a/src/ways.cc +++ b/src/ways.cc @@ -130,8 +130,8 @@ void ways::build_components_and_importance() { v_head.push_back(b.v_); } } - auto const thread_count = std::max(static_cast(1), - std::thread::hardware_concurrency()); + auto const thread_count = + std::max(1, static_cast(std::thread::hardware_concurrency())); ifc::run_inertial_flow_cutter( thread_count, static_cast(n_nodes()), v_head, v_tail, [&](int i) { diff --git a/test/cch_dijkstra_astarbidir_test.cc b/test/cch_dijkstra_astarbidir_test.cc index cd948724..804eb741 100644 --- a/test/cch_dijkstra_astarbidir_test.cc +++ b/test/cch_dijkstra_astarbidir_test.cc @@ -40,8 +40,7 @@ #include "osr/types.h" #include "osr/ways.h" -//copy of CCH_dijkstra_astarbidir_test.cc with cch - +// copy of CCH_dijkstra_astarbidir_test.cc with cch namespace fs = std::filesystem; using namespace osr; @@ -63,11 +62,11 @@ void cch_load(std::string_view raw_data, std::string_view data_dir) { template void cch_run(ways const& w, - lookup const& l, - unsigned const n_samples, - unsigned const max_cost, - direction const dir, - search_profile const profile) { + lookup const& l, + unsigned const n_samples, + unsigned const max_cost, + direction const dir, + search_profile const profile) { auto const from_tos = [&]() { auto prng = std::mt19937{}; @@ -86,7 +85,8 @@ void cch_run(ways const& w, auto reference_times = std::vector{}; auto experiment_times = std::vector{}; - auto cch_experiment_times = std::vector(); + auto cch_experiment_times = + std::vector(); auto m = std::mutex{}; @@ -104,7 +104,6 @@ void cch_run(ways const& w, return wc.left_.node_ != n && wc.right_.node_ != n; }); if (matches.size() > 1) { - } return matches; }; @@ -121,9 +120,9 @@ void cch_run(ways const& w, auto const reference_start = std::chrono::steady_clock::now(); auto const reference = [&]() { try { - return route(typename P::parameters{}, w, l, profile, from_loc, - to_loc, from_matches_span, to_matches_span, max_cost, dir, - nullptr, nullptr, nullptr, routing_algorithm::kDijkstra); + return route(typename P::parameters{}, w, l, profile, from_loc, to_loc, + from_matches_span, to_matches_span, max_cost, dir, nullptr, + nullptr, nullptr, routing_algorithm::kDijkstra); } catch (std::exception const& ex) { fmt::println("dijkstra exception: {}", ex.what()); throw ex; @@ -135,9 +134,9 @@ void cch_run(ways const& w, auto const experiment_start = std::chrono::steady_clock::now(); auto const experiment = [&]() { try { - return route(typename P::parameters{}, w, l, profile, from_loc, - to_loc, from_matches_span, to_matches_span, max_cost, dir, - nullptr, nullptr, nullptr, routing_algorithm::kAStarBi); + return route(typename P::parameters{}, w, l, profile, from_loc, to_loc, + from_matches_span, to_matches_span, max_cost, dir, nullptr, + nullptr, nullptr, routing_algorithm::kAStarBi); } catch (std::exception const& ex) { fmt::println("a* bidir exception: {}", ex.what()); throw ex; @@ -145,7 +144,7 @@ void cch_run(ways const& w, }(); auto const experiment_time = std::chrono::steady_clock::now() - experiment_start; - + /* cch */ @@ -153,9 +152,9 @@ void cch_run(ways const& w, auto const cch_experiment_start = std::chrono::steady_clock::now(); auto const cch_experiment = [&]() { try { - return route(typename P::parameters{}, w, l, profile, from_loc, - to_loc, from_matches_span, to_matches_span, max_cost, dir, - nullptr, nullptr, nullptr, routing_algorithm::kCCH); + return route(typename P::parameters{}, w, l, profile, from_loc, to_loc, + from_matches_span, to_matches_span, max_cost, dir, nullptr, + nullptr, nullptr, routing_algorithm::kCCH); } catch (std::exception const& ex) { fmt::println("CCH exception: {}", ex.what()); throw ex; @@ -164,37 +163,46 @@ void cch_run(ways const& w, auto const cch_experiment_time = std::chrono::steady_clock::now() - cch_experiment_start; - auto astar_pass = - (reference.has_value() == experiment.has_value() && (!reference || !experiment || (reference->cost_ == experiment->cost_ ))); - - auto cch_pass = - (reference.has_value() == cch_experiment.has_value() && (!reference || !cch_experiment || (reference->cost_ == cch_experiment->cost_))); + auto astar_pass = (reference.has_value() == experiment.has_value() && + (!reference || !experiment || + (reference->cost_ == experiment->cost_))); - if(astar_pass){ ++n_congruent_astar; } - if(cch_pass){ ++n_congruent_cch; } + auto cch_pass = (reference.has_value() == cch_experiment.has_value() && + (!reference || !cch_experiment || + (reference->cost_ == cch_experiment->cost_))); + + if (astar_pass) { + ++n_congruent_astar; + } + if (cch_pass) { + ++n_congruent_cch; + } - if(!astar_pass || !cch_pass){ + if (!astar_pass || !cch_pass) { auto const print_result = [&](std::string_view name, auto const& p, auto const& t) { - fmt::println( - "{:10}: {:11} --> {:11} | {} | time: " - "{}:{:0>3}:{:0>3} s", - name, w.node_to_osm_[from_node], w.node_to_osm_[to_node], - p ? fmt::format("cost: {:5} | dist: {:>10.2f}", p->cost_, p->dist_) - : "no result", - std::chrono::duration_cast(t).count(), - std::chrono::duration_cast(t).count() % - 1000, - std::chrono::duration_cast(t).count() % - 1000); - if (p.has_value() && kPrintDebugGeojson) { - fmt::println("{}\n", to_featurecollection(w, p)); - } - + fmt::println( + "{:10}: {:11} --> {:11} | {} | time: " + "{}:{:0>3}:{:0>3} s", + name, w.node_to_osm_[from_node], w.node_to_osm_[to_node], + p ? fmt::format("cost: {:5} | dist: {:>10.2f}", p->cost_, p->dist_) + : "no result", + std::chrono::duration_cast(t).count(), + std::chrono::duration_cast(t).count() % + 1000, + std::chrono::duration_cast(t).count() % + 1000); + if (p.has_value() && kPrintDebugGeojson) { + fmt::println("{}\n", to_featurecollection(w, p)); + } }; print_result("dijkstra", reference, reference_time); - if( !astar_pass){print_result("a* bidir", experiment, experiment_time);} - if( !cch_pass){ print_result("cch", cch_experiment, cch_experiment_time);} + if (!astar_pass) { + print_result("a* bidir", experiment, experiment_time); + } + if (!cch_pass) { + print_result("cch", cch_experiment, cch_experiment_time); + } } if (!from_matches.empty() && !to_matches.empty()) { @@ -239,24 +247,29 @@ void cch_run(ways const& w, if (non_empty_cch == non_empty_samples) { auto const dijkstra_total_time = static_cast( - std::reduce(begin(reference_times), end(reference_times)).count() - ); + std::reduce(begin(reference_times), end(reference_times)).count()); auto const astar_total_time = static_cast( - std::reduce(begin(experiment_times), end(experiment_times)).count() - ); + std::reduce(begin(experiment_times), end(experiment_times)).count()); auto const cch_total_time = static_cast( - std::reduce(begin(cch_experiment_times), end(cch_experiment_times)).count() - ); + std::reduce(begin(cch_experiment_times), end(cch_experiment_times)) + .count()); - fmt::println( "speedup a* vs dijkstra on non-empty: {:.2f}", dijkstra_total_time / astar_total_time); - fmt::println( "speedup cch vs dijkstra on non-epty: {:.2f}", dijkstra_total_time / cch_total_time); - fmt::println( "speedup cch vs a* on non-empty: {:.2f}", astar_total_time / cch_total_time); + fmt::println("speedup a* vs dijkstra on non-empty: {:.2f}", + dijkstra_total_time / astar_total_time); + fmt::println("speedup cch vs dijkstra on non-epty: {:.2f}", + dijkstra_total_time / cch_total_time); + fmt::println("speedup cch vs a* on non-empty: {:.2f}", + astar_total_time / cch_total_time); if constexpr (!kUseMultithreading) { - auto const warmup_hit = *std::max_element(begin(cch_experiment_times), end(cch_experiment_times)); - auto const cch_total_time_warm = cch_total_time - static_cast(warmup_hit.count()); - fmt::println( "speedup WARM cch vs dijkstra on non-epty: {:.2f}", dijkstra_total_time / cch_total_time_warm); - fmt::println( "speedup WARM cch vs a* on non-empty: {:.2f}", astar_total_time / cch_total_time_warm); + auto const warmup_hit = *std::max_element(begin(cch_experiment_times), + end(cch_experiment_times)); + auto const cch_total_time_warm = + cch_total_time - static_cast(warmup_hit.count()); + fmt::println("speedup WARM cch vs dijkstra on non-epty: {:.2f}", + dijkstra_total_time / cch_total_time_warm); + fmt::println("speedup WARM cch vs a* on non-empty: {:.2f}", + astar_total_time / cch_total_time_warm); } } } @@ -444,7 +457,7 @@ TEST(CCH_dijkstra_astarbidir, DISABLED_switzerland_bus) { TEST(CCH_dijkstra_astarbidir, DISABLED_switzerland_bwd) { auto const raw_data = "test/switzerland.osm.pbf"; auto const data_dir = "test/switzerland"; - auto const num_samples = 1000U; + auto const num_samples = 1000U; auto const max_cost = 5 * 3600U; auto constexpr dir = direction::kBackward; From fc5521cf3555a51893bc82ab9ae97902057351f3 Mon Sep 17 00:00:00 2001 From: p <149105134+peisha2@users.noreply.github.com> Date: Wed, 30 Sep 2026 17:17:47 +0200 Subject: [PATCH 17/17] fix Valgrind CI (skip IFC) --- src/ways.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ways.cc b/src/ways.cc index 963fb845..591e827d 100644 --- a/src/ways.cc +++ b/src/ways.cc @@ -14,6 +14,10 @@ #include "inertialflowcutter/run.h" #endif +#if __has_include() +#include +#endif + namespace osr { namespace { @@ -119,6 +123,12 @@ void ways::build_components_and_importance() { r_->way_importance_.clear(); #ifdef USE_INERTIAL_FLOW_CUT +#ifdef RUNNING_ON_VALGRIND // skip IFC on Valgrind + if (RUNNING_ON_VALGRIND) { + return; + } +#endif + pt->status("Run inertial flow cutter").in_high(n_ways()).out_bounds(90, 91); auto v_tail = std::vector{};