From 9ff28a7aac3660f842ceb597a83e24b62c3b522b Mon Sep 17 00:00:00 2001 From: Pablo Hoch Date: Fri, 25 Oct 2024 14:32:32 +0200 Subject: [PATCH] add bike_sharing profile --- exe/backend/src/http_server.cc | 6 +- include/osr/routing/additional_edge.h | 14 + include/osr/routing/dijkstra.h | 19 +- include/osr/routing/mode.h | 17 + include/osr/routing/profile.h | 5 +- include/osr/routing/profiles/bike.h | 8 +- include/osr/routing/profiles/bike_sharing.h | 389 ++++++++++++++++++++ include/osr/routing/profiles/car.h | 8 +- include/osr/routing/profiles/car_parking.h | 14 +- include/osr/routing/profiles/foot.h | 8 +- include/osr/routing/route.h | 27 +- include/osr/routing/sharing_data.h | 17 + src/lookup.cc | 6 +- src/route.cc | 141 ++++--- src/routing/mode.cc | 19 + src/routing/profile.cc | 4 +- 16 files changed, 625 insertions(+), 77 deletions(-) create mode 100644 include/osr/routing/additional_edge.h create mode 100644 include/osr/routing/mode.h create mode 100644 include/osr/routing/profiles/bike_sharing.h create mode 100644 include/osr/routing/sharing_data.h create mode 100644 src/routing/mode.cc diff --git a/exe/backend/src/http_server.cc b/exe/backend/src/http_server.cc index f1b35ca..a24b60e 100644 --- a/exe/backend/src/http_server.cc +++ b/exe/backend/src/http_server.cc @@ -23,6 +23,7 @@ #include "osr/routing/profiles/car.h" #include "osr/routing/profiles/car_parking.h" #include "osr/routing/profiles/foot.h" +#include "osr/routing/profiles/bike_sharing.h" #include "osr/routing/route.h" using namespace net; @@ -196,6 +197,9 @@ struct http_server::impl { case search_profile::kCarParkingWheelchair: send_graph_response>(req, cb, gj); break; + case search_profile::kBikeSharing: + send_graph_response(req, cb, gj); + break; default: throw utl::fail("not implemented"); } } @@ -369,4 +373,4 @@ void http_server::listen(std::string const& host, std::string const& port) { void http_server::stop() { impl_->stop(); } -} // namespace osr::backend \ No newline at end of file +} // namespace osr::backend diff --git a/include/osr/routing/additional_edge.h b/include/osr/routing/additional_edge.h new file mode 100644 index 0000000..1228e3e --- /dev/null +++ b/include/osr/routing/additional_edge.h @@ -0,0 +1,14 @@ +#pragma once + +#include "osr/types.h" + +namespace osr { + +struct additional_edge { + friend bool operator==(additional_edge, additional_edge) = default; + + node_idx_t node_{}; + distance_t distance_{}; +}; + +} // namespace osr diff --git a/include/osr/routing/dijkstra.h b/include/osr/routing/dijkstra.h index 5013799..6d69e33 100644 --- a/include/osr/routing/dijkstra.h +++ b/include/osr/routing/dijkstra.h @@ -1,11 +1,14 @@ #pragma once +#include "osr/routing/additional_edge.h" #include "osr/routing/dial.h" #include "osr/types.h" #include "osr/ways.h" namespace osr { +struct sharing_data; + template struct dijkstra { using profile_t = Profile; @@ -40,7 +43,8 @@ struct dijkstra { template void run(ways::routing const& r, cost_t const max, - bitvec const* blocked) { + bitvec const* blocked, + sharing_data const* sharing) { while (!pq_.empty()) { auto l = pq_.pop(); if (get_cost(l.get_node()) < l.cost()) { @@ -49,7 +53,7 @@ struct dijkstra { auto const curr = l.get_node(); Profile::template adjacent( - r, curr, blocked, + r, curr, blocked, sharing, [&](node const neighbor, std::uint32_t const cost, distance_t, way_idx_t const way, std::uint16_t, std::uint16_t) { auto const total = l.cost() + cost; @@ -67,15 +71,16 @@ struct dijkstra { void run(ways::routing const& r, cost_t const max, bitvec const* blocked, + sharing_data const* sharing, direction const dir) { if (blocked == nullptr) { dir == direction::kForward - ? run(r, max, blocked) - : run(r, max, blocked); + ? run(r, max, blocked, sharing) + : run(r, max, blocked, sharing); } else { dir == direction::kForward - ? run(r, max, blocked) - : run(r, max, blocked); + ? run(r, max, blocked, sharing) + : run(r, max, blocked, sharing); } } @@ -83,4 +88,4 @@ struct dijkstra { ankerl::unordered_dense::map cost_; }; -} // namespace osr \ No newline at end of file +} // namespace osr diff --git a/include/osr/routing/mode.h b/include/osr/routing/mode.h new file mode 100644 index 0000000..13f45a1 --- /dev/null +++ b/include/osr/routing/mode.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +namespace osr { + +enum class mode : std::uint8_t { + kFoot, + kWheelchair, + kBike, + kCar, +}; + +std::string_view to_str(mode); + +} // namespace osr diff --git a/include/osr/routing/profile.h b/include/osr/routing/profile.h index bab44aa..98329ed 100644 --- a/include/osr/routing/profile.h +++ b/include/osr/routing/profile.h @@ -11,11 +11,12 @@ enum class search_profile : std::uint8_t { kBike, kCar, kCarParking, - kCarParkingWheelchair + kCarParkingWheelchair, + kBikeSharing, }; search_profile to_profile(std::string_view); std::string_view to_str(search_profile); -} // namespace osr \ No newline at end of file +} // namespace osr diff --git a/include/osr/routing/profiles/bike.h b/include/osr/routing/profiles/bike.h index 170608d..2dbc583 100644 --- a/include/osr/routing/profiles/bike.h +++ b/include/osr/routing/profiles/bike.h @@ -1,10 +1,13 @@ #pragma once +#include "osr/routing/mode.h" #include "osr/routing/route.h" #include "osr/ways.h" namespace osr { +struct sharing_data; + struct bike { static constexpr auto const kMaxMatchDistance = 100U; static constexpr auto const kOffroadPenalty = 1U; @@ -20,6 +23,8 @@ struct bike { constexpr node get_key() const noexcept { return *this; } + static constexpr mode get_mode() noexcept { return mode::kBike; } + std::ostream& print(std::ostream& out, ways const& w) const { return out << w.node_to_osm_[n_]; } @@ -101,6 +106,7 @@ struct bike { static void adjacent(ways::routing const& w, node const n, bitvec const* blocked, + sharing_data const*, Fn&& fn) { for (auto const [way, i] : utl::zip_unchecked(w.node_ways_[n.n_], w.node_in_way_idx_[n.n_])) { @@ -154,4 +160,4 @@ struct bike { } }; -} // namespace osr \ No newline at end of file +} // namespace osr diff --git a/include/osr/routing/profiles/bike_sharing.h b/include/osr/routing/profiles/bike_sharing.h new file mode 100644 index 0000000..9fc9a4a --- /dev/null +++ b/include/osr/routing/profiles/bike_sharing.h @@ -0,0 +1,389 @@ +#pragma once + +#include +#include +#include +#include + +#include "boost/json.hpp" + +#include "utl/helpers/algorithm.h" + +#include "osr/routing/additional_edge.h" +#include "osr/routing/mode.h" +#include "osr/routing/profiles/bike.h" +#include "osr/routing/profiles/foot.h" +#include "osr/routing/route.h" +#include "osr/routing/sharing_data.h" +#include "osr/ways.h" + +namespace osr { + +struct bike_sharing { + using footp = foot; + + // initial foot -> bike + static constexpr auto const kStartSwitchPenalty = cost_t{30U}; + // bike -> trailing foot + static constexpr auto const kEndSwitchPenalty = cost_t{30U}; + + static constexpr auto const kAdditionalWayProperties = + way_properties{.is_foot_accessible_ = true, + .is_bike_accessible_ = true, + .is_steps_ = false}; + + static constexpr auto const kAdditionalNodeProperties = + node_properties{.is_foot_accessible_ = true, + .is_bike_accessible_ = true, + .is_elevator_ = false}; + + enum class node_type : std::uint8_t { + kInitialFoot, + kBike, + kTrailingFoot, + kInvalid, + }; + + static constexpr std::string_view node_type_to_str(node_type const type) { + switch (type) { + case node_type::kInitialFoot: return "initial_foot"; + case node_type::kBike: return "bike"; + case node_type::kTrailingFoot: return "trailing_foot"; + case node_type::kInvalid: return "invalid"; + } + std::unreachable(); + } + + struct key { + friend bool operator==(key, key) = default; + + node_idx_t n_{node_idx_t::invalid()}; + level_t lvl_{}; + }; + + struct node { + friend bool operator==(node, node) = default; + + boost::json::object geojson_properties(ways const& w) const { + auto properties = + boost::json::object{{"osm_node_id", to_idx(w.node_to_osm_[n_])}, + {"level", to_float(lvl_)}, + {"type", node_type_to_str(type_)}}; + return properties; + } + + std::ostream& print(std::ostream& out, ways const& w) const { + return out << "(node=" << w.node_to_osm_[n_] + << ", level=" << to_float(lvl_) + << ", type=" << node_type_to_str(type_) << ")"; + } + + static constexpr node invalid() noexcept { return {}; } + constexpr node_idx_t get_node() const noexcept { return n_; } + constexpr key get_key() const noexcept { return {n_, lvl_}; } + + constexpr mode get_mode() const noexcept { + return is_bike_node() ? mode::kBike : mode::kFoot; + } + + constexpr bool is_initial_foot_node() const noexcept { + return type_ == node_type::kInitialFoot; + } + + constexpr bool is_bike_node() const noexcept { + return type_ == node_type::kBike; + } + + constexpr bool is_trailing_foot_node() const noexcept { + return type_ == node_type::kTrailingFoot; + } + + constexpr bool is_invalid_node() const noexcept { + return type_ == node_type::kInvalid; + } + + constexpr bool is_additional_node( + sharing_data const* sharing) const noexcept { + return to_idx(n_) >= sharing->additional_node_offset_; + } + + node_idx_t n_{node_idx_t::invalid()}; + node_type type_{node_type::kInvalid}; + level_t lvl_{}; + }; + + struct label { + constexpr label(node const n, cost_t const c) + : n_{n.n_}, type_{n.type_}, lvl_{n.lvl_}, cost_{c} {} + + constexpr node get_node() const noexcept { + return {.n_ = n_, .type_ = type_, .lvl_ = lvl_}; + } + + constexpr cost_t cost() const noexcept { return cost_; } + + void track(label const&, ways::routing const&, way_idx_t, node_idx_t) {} + + node_idx_t n_; + node_type type_; + level_t lvl_; + cost_t cost_; + }; + + struct entry { + static constexpr auto const kN = + static_cast>(node_type::kInvalid); + + entry() { + utl::fill(pred_, node_idx_t::invalid()); + utl::fill(cost_, kInfeasible); + utl::fill(pred_lvl_, level_t::invalid()); + utl::fill(pred_type_, node_type::kInvalid); + } + + constexpr std::optional pred(node const n) const noexcept { + auto const idx = get_index(n); + return pred_[idx] == node_idx_t::invalid() + ? std::nullopt + : std::optional{node{.n_ = pred_[idx], + .type_ = pred_type_[idx], + .lvl_ = pred_lvl_[idx]}}; + } + + constexpr cost_t cost(node const n) const noexcept { + return cost_[get_index(n)]; + } + + constexpr bool update(label const, + node const n, + cost_t const c, + node const pred) noexcept { + auto const idx = get_index(n); + if (c < cost_[idx]) { + cost_[idx] = c; + pred_[idx] = pred.n_; + pred_lvl_[idx] = pred.lvl_; + pred_type_[idx] = pred.type_; + return true; + } + return false; + } + + static constexpr std::size_t get_index(node const n) { + return static_cast(n.type_); + } + + void write(node, path&) const {} + + std::array pred_{}; + std::array cost_{}; + std::array pred_lvl_{}; + std::array pred_type_{}; + }; + + struct hash { + using is_avalanching = void; + auto operator()(key const k) const noexcept -> std::uint64_t { + using namespace ankerl::unordered_dense::detail; + return wyhash::mix( + wyhash::hash(static_cast(to_idx(k.lvl_))), + wyhash::hash(static_cast(to_idx(k.n_)))); + } + }; + + static footp::node to_foot(node const n) { + return {.n_ = n.n_, .lvl_ = n.lvl_}; + } + + static bike::node to_bike(node const n) { return {.n_ = n.n_}; } + + static node to_node(footp::node const n, node_type const type) { + return {.n_ = n.n_, .type_ = type, .lvl_ = n.lvl_}; + } + + static node to_node(bike::node const n, level_t const lvl) { + return {.n_ = n.n_, .type_ = node_type::kBike, .lvl_ = lvl}; + } + + template + static void resolve_start_node(ways::routing const& w, + way_idx_t const way, + node_idx_t const n, + level_t lvl, + direction search_dir, + Fn&& f) { + footp::resolve_start_node(w, way, n, lvl, search_dir, + [&](footp::node const fn) { + f(to_node(fn, search_dir == direction::kForward + ? node_type::kInitialFoot + : node_type::kTrailingFoot)); + }); + } + + template + static void resolve_all(ways::routing const& w, + node_idx_t const n, + level_t const lvl, + Fn&& f) { + footp::resolve_all(w, n, lvl, [&](footp::node const neighbor) { + f(to_node(neighbor, node_type::kInitialFoot)); + f(to_node(neighbor, node_type::kTrailingFoot)); + }); + } + + template + static void adjacent(ways::routing const& w, + node const n, + bitvec const* blocked, + sharing_data const* sharing, + Fn&& fn) { + assert(sharing != nullptr); + + auto const& handle_additional_edge = + [&](additional_edge const& ae, node_type const nt, cost_t const cost) { + fn(node{.n_ = ae.node_, .type_ = nt, .lvl_ = n.lvl_}, cost, + ae.distance_, way_idx_t::invalid(), 0, 1); + }; + + auto const& continue_on_foot = [&](node_type const nt, + bool const include_additional_edges, + cost_t const switch_penalty = 0) { + footp::template adjacent( + w, to_foot(n), blocked, nullptr, + [&](footp::node const neighbor, std::uint32_t const cost, + distance_t const dist, way_idx_t const way, + std::uint16_t const from, std::uint16_t const to) { + fn(to_node(neighbor, nt), cost + switch_penalty, dist, way, from, + to); + }); + if (include_additional_edges) { + // walk to station or free-floating bike + if (auto const it = sharing->additional_edges_.find(n.n_); + it != end(sharing->additional_edges_)) { + for (auto const& ae : it->second) { + handle_additional_edge( + ae, nt, + footp::way_cost(kAdditionalWayProperties, direction::kForward, + ae.distance_) + + switch_penalty); + } + } + } + }; + + auto const& continue_on_bike = [&](bool const include_additional_edges, + cost_t const switch_penalty = 0) { + bike::adjacent( + w, to_bike(n), blocked, nullptr, + [&](bike::node const neighbor, std::uint32_t const cost, + distance_t const dist, way_idx_t const way, + std::uint16_t const from, std::uint16_t const to) { + fn(to_node(neighbor, n.lvl_), cost + switch_penalty, dist, way, + from, to); + }); + if (include_additional_edges) { + // drive to station + if (auto const it = sharing->additional_edges_.find(n.n_); + it != end(sharing->additional_edges_)) { + for (auto const& ae : it->second) { + handle_additional_edge( + ae, node_type::kBike, + bike::way_cost(kAdditionalWayProperties, direction::kForward, + ae.distance_) + + switch_penalty); + } + } + } + }; + + if (SearchDir == direction::kForward) { + + if (n.is_additional_node(sharing)) { + // additional node - station or free-floating bike + // switch mode and use additional edge + if (auto const it = sharing->additional_edges_.find(n.n_); + it != end(sharing->additional_edges_)) { + for (auto const& ae : it->second) { + if (n.is_initial_foot_node() && + sharing->start_allowed_.test(n.n_)) { + handle_additional_edge( + ae, node_type::kBike, + bike::way_cost(kAdditionalWayProperties, direction::kForward, + ae.distance_) + + kStartSwitchPenalty); + } else if (n.is_bike_node() && sharing->end_allowed_.test(n.n_)) { + handle_additional_edge( + ae, node_type::kTrailingFoot, + footp::way_cost(kAdditionalWayProperties, direction::kForward, + ae.distance_) + + kEndSwitchPenalty); + } + } + } + } else { + if (n.is_initial_foot_node() || n.is_trailing_foot_node()) { + continue_on_foot(n.type_, n.is_initial_foot_node()); + } else if (n.is_bike_node()) { + continue_on_bike(true); + if (sharing->end_allowed_.test(n.n_)) { + // switch to foot + continue_on_foot(node_type::kTrailingFoot, false, + kEndSwitchPenalty); + } + } + } + + } else /* backward */ { + + if (n.is_additional_node(sharing)) { + // additional node - station or free-floating bike + // switch mode and use additional edge + if (auto const it = sharing->additional_edges_.find(n.n_); + it != end(sharing->additional_edges_)) { + for (auto const& ae : it->second) { + if (n.is_trailing_foot_node() && sharing->end_allowed_.test(n.n_)) { + handle_additional_edge( + ae, node_type::kBike, + bike::way_cost(kAdditionalWayProperties, direction::kForward, + ae.distance_) + + kEndSwitchPenalty); + } else if (n.is_bike_node() && sharing->start_allowed_.test(n.n_)) { + handle_additional_edge( + ae, node_type::kInitialFoot, + footp::way_cost(kAdditionalWayProperties, direction::kForward, + ae.distance_) + + kStartSwitchPenalty); + } + } + } + } else { + if (n.is_initial_foot_node() || n.is_trailing_foot_node()) { + continue_on_foot(n.type_, n.is_trailing_foot_node()); + if (n.is_trailing_foot_node() && sharing->end_allowed_.test(n.n_)) { + // switch to bike + continue_on_bike(false, kEndSwitchPenalty); + } + } else if (n.is_bike_node()) { + continue_on_bike(true); + } + } + } + } + + static bool is_dest_reachable(ways::routing const& w, + node const n, + way_idx_t const way, + direction const way_dir, + direction const search_dir) { + return !n.is_bike_node() && + footp::is_dest_reachable(w, to_foot(n), way, way_dir, search_dir); + } + + static constexpr cost_t way_cost(way_properties const& e, + direction const dir, + std::uint16_t const dist) { + return footp::way_cost(e, dir, dist); + } +}; + +} // namespace osr diff --git a/include/osr/routing/profiles/car.h b/include/osr/routing/profiles/car.h index bf178ea..eb547a1 100644 --- a/include/osr/routing/profiles/car.h +++ b/include/osr/routing/profiles/car.h @@ -7,10 +7,13 @@ #include "utl/helpers/algorithm.h" #include "osr/routing/route.h" +#include "osr/routing/mode.h" #include "osr/ways.h" namespace osr { +struct sharing_data; + struct car { static constexpr auto const kMaxMatchDistance = 200U; static constexpr auto const kUturnPenalty = cost_t{120U}; @@ -33,6 +36,8 @@ struct car { constexpr node_idx_t get_key() const noexcept { return n_; } + static constexpr mode get_mode() noexcept { return mode::kCar; } + std::ostream& print(std::ostream& out, ways const& w) const { return out << "(node=" << w.node_to_osm_[n_] << ", dir=" << to_str(dir_) << ", way=" << w.way_osm_idx_[w.r_->node_ways_[n_][way_]] @@ -158,6 +163,7 @@ struct car { static void adjacent(ways::routing const& w, node const n, bitvec const* blocked, + sharing_data const*, Fn&& fn) { auto way_pos = way_pos_t{0U}; for (auto const [way, i] : @@ -241,4 +247,4 @@ struct car { } }; -} // namespace osr \ No newline at end of file +} // namespace osr diff --git a/include/osr/routing/profiles/car_parking.h b/include/osr/routing/profiles/car_parking.h index b7a0386..db2ba0a 100644 --- a/include/osr/routing/profiles/car_parking.h +++ b/include/osr/routing/profiles/car_parking.h @@ -6,6 +6,7 @@ #include "utl/helpers/algorithm.h" +#include "osr/routing/mode.h" #include "osr/routing/profiles/car.h" #include "osr/routing/profiles/foot.h" #include "osr/routing/route.h" @@ -13,6 +14,8 @@ namespace osr { +struct sharing_data; + template struct car_parking { using footp = foot; @@ -58,6 +61,10 @@ struct car_parking { constexpr node_idx_t get_node() const noexcept { return n_; } constexpr node_idx_t get_key() const noexcept { return n_; } + constexpr mode get_mode() const noexcept { + return is_car_node() ? mode::kCar : mode::kFoot; + } + constexpr bool is_car_node() const noexcept { return type_ == node_type::kCar; } @@ -225,6 +232,7 @@ struct car_parking { static void adjacent(ways::routing const& w, node const n, bitvec const* blocked, + sharing_data const*, Fn&& fn) { static constexpr auto const kFwd = SearchDir == direction::kForward; static constexpr auto const kBwd = SearchDir == direction::kBackward; @@ -233,7 +241,7 @@ struct car_parking { if (n.is_foot_node() || (kFwd && n.is_car_node() && is_parking)) { footp::template adjacent( - w, to_foot(n), blocked, + w, to_foot(n), blocked, nullptr, [&](footp::node const neighbor, std::uint32_t const cost, distance_t const dist, way_idx_t const way, std::uint16_t const from, std::uint16_t const to) { @@ -245,7 +253,7 @@ struct car_parking { if (n.is_car_node() || (kBwd && n.is_foot_node() && is_parking)) { car::template adjacent( - w, to_car(n), blocked, + w, to_car(n), blocked, nullptr, [&](car::node const neighbor, std::uint32_t const cost, distance_t const dist, way_idx_t const way, std::uint16_t const from, std::uint16_t const to) { @@ -304,4 +312,4 @@ struct car_parking { } }; -} // namespace osr \ No newline at end of file +} // namespace osr diff --git a/include/osr/routing/profiles/foot.h b/include/osr/routing/profiles/foot.h index d43c57a..a0c051b 100644 --- a/include/osr/routing/profiles/foot.h +++ b/include/osr/routing/profiles/foot.h @@ -2,11 +2,14 @@ #include "utl/for_each_bit_set.h" +#include "osr/routing/mode.h" #include "osr/routing/tracking.h" #include "osr/ways.h" namespace osr { +struct sharing_data; + template struct foot { static constexpr auto const kMaxMatchDistance = 100U; @@ -22,6 +25,8 @@ struct foot { constexpr node get_key() const noexcept { return *this; } + static constexpr mode get_mode() noexcept { return mode::kFoot; } + std::ostream& print(std::ostream& out, ways const& w) const { return out << "(node=" << w.node_to_osm_[n_] << ", level=" << to_float(lvl_) << ")"; @@ -135,6 +140,7 @@ struct foot { static void adjacent(ways::routing const& w, node const n, bitvec const* blocked, + sharing_data const*, Fn&& fn) { for (auto const [way, i] : utl::zip_unchecked(w.node_ways_[n.n_], w.node_in_way_idx_[n.n_])) { @@ -309,4 +315,4 @@ struct foot { } }; -} // namespace osr \ No newline at end of file +} // namespace osr diff --git a/include/osr/routing/route.h b/include/osr/routing/route.h index e59cb76..38282fc 100644 --- a/include/osr/routing/route.h +++ b/include/osr/routing/route.h @@ -7,6 +7,7 @@ #include "osr/location.h" #include "osr/lookup.h" +#include "osr/routing/mode.h" #include "osr/routing/profile.h" #include "osr/types.h" @@ -17,15 +18,19 @@ struct ways; template struct dijkstra; +struct sharing_data; + struct path { struct segment { geo::polyline polyline_; - level_t from_level_; - level_t to_level_; - node_idx_t from_, to_; - way_idx_t way_; + level_t from_level_{level_t::invalid()}; + level_t to_level_{level_t::invalid()}; + node_idx_t from_{node_idx_t::invalid()}; + node_idx_t to_{node_idx_t::invalid()}; + way_idx_t way_{way_idx_t::invalid()}; cost_t cost_{kInfeasible}; distance_t dist_{0}; + mode mode_{mode::kFoot}; }; cost_t cost_{kInfeasible}; @@ -47,7 +52,8 @@ std::vector> route( direction, double max_match_distance, bitvec const* blocked = nullptr, - std::function const& = [](path const&) { + sharing_data const* sharing = nullptr, + std::function const& do_reconstruct = [](path const&) { return false; }); @@ -59,7 +65,8 @@ std::optional route(ways const&, cost_t max, direction, double max_match_distance, - bitvec const* blocked = nullptr); + bitvec const* blocked = nullptr, + sharing_data const* sharing = nullptr); std::optional route(ways const&, search_profile, @@ -69,7 +76,8 @@ std::optional route(ways const&, match_view_t to_match, cost_t const max, direction, - bitvec const* blocked = nullptr); + bitvec const* blocked = nullptr, + sharing_data const* sharing = nullptr); std::vector> route( ways const&, @@ -81,8 +89,9 @@ std::vector> route( cost_t const max, direction const, bitvec const* blocked = nullptr, - std::function const& = [](path const&) { + sharing_data const* sharing = nullptr, + std::function const& do_reconstruct = [](path const&) { return false; }); -} // namespace osr \ No newline at end of file +} // namespace osr diff --git a/include/osr/routing/sharing_data.h b/include/osr/routing/sharing_data.h new file mode 100644 index 0000000..4a4a386 --- /dev/null +++ b/include/osr/routing/sharing_data.h @@ -0,0 +1,17 @@ +#pragma once + +#include "osr/routing/additional_edge.h" +#include "osr/types.h" + +namespace osr { + +struct sharing_data { + bitvec const& start_allowed_; + bitvec const& end_allowed_; + bitvec const& through_allowed_; + + node_idx_t::value_t additional_node_offset_{}; + hash_map> const& additional_edges_; +}; + +} // namespace osr diff --git a/src/lookup.cc b/src/lookup.cc index c0f1696..3b5dd6f 100644 --- a/src/lookup.cc +++ b/src/lookup.cc @@ -1,6 +1,7 @@ #include "osr/lookup.h" #include "osr/routing/profiles/bike.h" +#include "osr/routing/profiles/bike_sharing.h" #include "osr/routing/profiles/car.h" #include "osr/routing/profiles/car_parking.h" #include "osr/routing/profiles/foot.h" @@ -41,6 +42,9 @@ match_t lookup::match(location const& query, case search_profile::kCarParkingWheelchair: return match>(query, reverse, search_dir, max_match_distance, blocked); + case search_profile::kBikeSharing: + return match(query, reverse, search_dir, max_match_distance, + blocked); } throw utl::fail("{} is not a valid profile", static_cast(p)); } @@ -70,4 +74,4 @@ void lookup::insert(way_idx_t const way) { reinterpret_cast(static_cast(to_idx(way)))); } -} // namespace osr \ No newline at end of file +} // namespace osr diff --git a/src/route.cc b/src/route.cc index f876863..97370cd 100644 --- a/src/route.cc +++ b/src/route.cc @@ -8,49 +8,61 @@ #include "osr/routing/dijkstra.h" #include "osr/routing/profiles/bike.h" +#include "osr/routing/profiles/bike_sharing.h" #include "osr/routing/profiles/car.h" #include "osr/routing/profiles/car_parking.h" #include "osr/routing/profiles/foot.h" +#include "osr/routing/sharing_data.h" #include "osr/util/infinite.h" #include "osr/util/reverse.h" namespace osr { struct connecting_way { - way_idx_t way_; - std::uint16_t from_, to_; - bool is_loop_; - std::uint16_t distance_; + constexpr bool valid() const { return way_ != way_idx_t::invalid(); } + + way_idx_t way_{way_idx_t::invalid()}; + std::uint16_t from_{}, to_{}; + bool is_loop_{}; + std::uint16_t distance_{}; }; template connecting_way find_connecting_way(ways const& w, ways::routing const& r, bitvec const* blocked, + sharing_data const* sharing, typename Profile::node const from, typename Profile::node const to, cost_t const expected_cost) { auto conn = std::optional{}; Profile::template adjacent( - r, from, blocked, + r, from, blocked, sharing, [&](typename Profile::node const target, std::uint32_t const cost, distance_t const dist, way_idx_t const way, std::uint16_t const a_idx, std::uint16_t const b_idx) { if (target == to && cost == expected_cost) { - auto const is_loop = r.is_loop(way) && + auto const is_loop = way != way_idx_t::invalid() && r.is_loop(way) && static_cast(std::abs(a_idx - b_idx)) == r.way_nodes_[way].size() - 2U; conn = {way, a_idx, b_idx, is_loop, dist}; } }); - utl::verify(conn.has_value(), "no connecting way node/{} -> node/{} found", - w.node_to_osm_[from.get_node()], w.node_to_osm_[to.get_node()]); + utl::verify( + conn.has_value(), "no connecting way node/{} -> node/{} found", + (sharing == nullptr || from.get_node() < sharing->additional_node_offset_) + ? to_idx(w.node_to_osm_[from.get_node()]) + : 0, + (sharing == nullptr || to.get_node() < sharing->additional_node_offset_) + ? to_idx(w.node_to_osm_[to.get_node()]) + : 0); return *conn; } template connecting_way find_connecting_way(ways const& w, bitvec const* blocked, + sharing_data const* sharing, typename Profile::node const from, typename Profile::node const to, cost_t const expected_cost, @@ -58,10 +70,10 @@ connecting_way find_connecting_way(ways const& w, auto const call = [&]() { if (dir == direction::kForward) { return find_connecting_way( - w, *w.r_, blocked, from, to, expected_cost); + w, *w.r_, blocked, sharing, from, to, expected_cost); } else { return find_connecting_way( - w, *w.r_, blocked, from, to, expected_cost); + w, *w.r_, blocked, sharing, from, to, expected_cost); } }; @@ -76,50 +88,66 @@ template double add_path(ways const& w, ways::routing const& r, bitvec const* blocked, + sharing_data const* sharing, typename Profile::node const from, typename Profile::node const to, cost_t const expected_cost, std::vector& path, direction const dir) { auto const& [way, from_idx, to_idx, is_loop, distance] = - find_connecting_way(w, blocked, from, to, expected_cost, dir); + find_connecting_way(w, blocked, sharing, from, to, expected_cost, + dir); auto j = 0U; auto active = false; auto& segment = path.emplace_back(); segment.way_ = way; segment.dist_ = distance; segment.cost_ = expected_cost; - segment.from_level_ = r.way_properties_[way].from_level(); - segment.to_level_ = r.way_properties_[way].to_level(); - segment.from_ = r.way_nodes_[way][from_idx]; - segment.to_ = r.way_nodes_[way][to_idx]; - - for (auto const [osm_idx, coord] : - infinite(reverse(utl::zip(w.way_osm_nodes_[way], w.way_polylines_[way]), - (from_idx > to_idx) ^ is_loop), - is_loop)) { - utl::verify(j++ != 2 * w.way_polylines_[way].size() + 1U, "infinite loop"); - if (!active && w.node_to_osm_[r.way_nodes_[way][from_idx]] == osm_idx) { - active = true; - } - if (active) { - if (w.node_to_osm_[r.way_nodes_[way][from_idx]] == osm_idx) { - // Again "from" node, then it's shorter to start from here. - segment.polyline_.clear(); + segment.mode_ = to.get_mode(); + + if (way != way_idx_t::invalid()) { + segment.from_level_ = r.way_properties_[way].from_level(); + segment.to_level_ = r.way_properties_[way].to_level(); + segment.from_ = r.way_nodes_[way][from_idx]; + segment.to_ = r.way_nodes_[way][to_idx]; + + for (auto const [osm_idx, coord] : infinite( + reverse(utl::zip(w.way_osm_nodes_[way], w.way_polylines_[way]), + (from_idx > to_idx) ^ is_loop), + is_loop)) { + utl::verify(j++ != 2 * w.way_polylines_[way].size() + 1U, + "infinite loop"); + if (!active && w.node_to_osm_[r.way_nodes_[way][from_idx]] == osm_idx) { + active = true; } + if (active) { + if (w.node_to_osm_[r.way_nodes_[way][from_idx]] == osm_idx) { + // Again "from" node, then it's shorter to start from here. + segment.polyline_.clear(); + } - segment.polyline_.emplace_back(coord); - if (w.node_to_osm_[r.way_nodes_[way][to_idx]] == osm_idx) { - break; + segment.polyline_.emplace_back(coord); + if (w.node_to_osm_[r.way_nodes_[way][to_idx]] == osm_idx) { + break; + } } } + } else { + segment.from_level_ = to_level(0); + segment.to_level_ = to_level(0); + segment.from_ = from.get_node(); + segment.to_ = to.get_node(); + // polyline has to be filled by the caller, because we don't know + // the positions of the additional nodes here } + return distance; } template path reconstruct(ways const& w, bitvec const* blocked, + sharing_data const* sharing, dijkstra const& d, way_candidate const& start, node_candidate const& dest, @@ -140,8 +168,8 @@ path reconstruct(ways const& w, if (pred.has_value()) { auto const expected_cost = static_cast(e.cost(n) - d.get_cost(*pred)); - dist += add_path(w, *w.r_, blocked, *pred, n, expected_cost, - segments, dir); + dist += add_path(w, *w.r_, blocked, sharing, *pred, n, + expected_cost, segments, dir); } else { break; } @@ -255,7 +283,8 @@ std::optional route(ways const& w, match_view_t to_match, cost_t const max, direction const dir, - bitvec const* blocked) { + bitvec const* blocked, + sharing_data const* sharing) { if (auto const direct = try_direct(from, to); direct.has_value()) { return *direct; } @@ -265,9 +294,10 @@ std::optional route(ways const& w, for (auto const& start : from_match) { for (auto const* nc : {&start.left_, &start.right_}) { if (nc->valid() && nc->cost_ < max) { - Profile::resolve_start_node( - *w.r_, start.way_, nc->node_, from.lvl_, dir, - [&](auto const node) { d.add_start({node, nc->cost_}); }); + Profile::resolve_start_node(*w.r_, start.way_, nc->node_, from.lvl_, + dir, [&](auto const node) { + d.add_start({node, nc->cost_}); + }); } } @@ -275,13 +305,13 @@ std::optional route(ways const& w, continue; } - d.run(*w.r_, max, blocked, dir); + d.run(*w.r_, max, blocked, sharing, dir); auto const c = best_candidate(w, d, to.lvl_, to_match, max, dir); if (c.has_value()) { auto const [nc, wc, node, p] = *c; - return reconstruct(w, blocked, d, start, *nc, node, p.cost_, - dir); + return reconstruct(w, blocked, sharing, d, start, *nc, node, + p.cost_, dir); } } @@ -299,6 +329,7 @@ std::vector> route( cost_t const max, direction const dir, bitvec const* blocked, + sharing_data const* sharing, std::function const& do_reconstruct) { auto result = std::vector>{}; result.resize(to_match.size()); @@ -320,7 +351,7 @@ std::vector> route( } } - d.run(*w.r_, max, blocked, dir); + d.run(*w.r_, max, blocked, sharing, dir); auto found = 0U; for (auto const [m, t, r] : utl::zip(to_match, to, result)) { @@ -334,8 +365,8 @@ std::vector> route( auto [nc, wc, n, p] = *c; d.cost_.at(n.get_key()).write(n, p); if (do_reconstruct(p)) { - p = reconstruct(w, blocked, d, start, *nc, n, p.cost_, - dir); + p = reconstruct(w, blocked, sharing, d, start, *nc, n, + p.cost_, dir); p.uses_elevator_ = true; } r = std::make_optional(p); @@ -362,6 +393,7 @@ std::vector> route( direction const dir, double const max_match_distance, bitvec const* blocked, + sharing_data const* sharing, std::function const& do_reconstruct) { auto const r = [&]( dijkstra& d) -> std::vector> { @@ -374,7 +406,7 @@ std::vector> route( return l.match(x, true, dir, max_match_distance, blocked); }); return route(w, d, from, to, from_match, to_match, max, dir, blocked, - do_reconstruct); + sharing, do_reconstruct); }; switch (profile) { @@ -388,6 +420,7 @@ std::vector> route( return r(get_dijkstra>()); case search_profile::kCarParkingWheelchair: return r(get_dijkstra>()); + case search_profile::kBikeSharing: return r(get_dijkstra()); } throw utl::fail("not implemented"); @@ -401,7 +434,8 @@ std::optional route(ways const& w, cost_t const max, direction const dir, double const max_match_distance, - bitvec const* blocked) { + bitvec const* blocked, + sharing_data const* sharing) { auto const r = [&](dijkstra& d) -> std::optional { auto const from_match = @@ -413,7 +447,8 @@ std::optional route(ways const& w, return std::nullopt; } - return route(w, d, from, to, from_match, to_match, max, dir, blocked); + return route(w, d, from, to, from_match, to_match, max, dir, blocked, + sharing); }; switch (profile) { @@ -427,6 +462,7 @@ std::optional route(ways const& w, return r(get_dijkstra>()); case search_profile::kCarParkingWheelchair: return r(get_dijkstra>()); + case search_profile::kBikeSharing: return r(get_dijkstra()); } throw utl::fail("not implemented"); @@ -442,6 +478,7 @@ std::vector> route( cost_t const max, direction const dir, bitvec const* blocked, + sharing_data const* sharing, std::function const& do_reconstruct) { if (from_match.empty()) { return std::vector>(to.size()); @@ -450,7 +487,7 @@ std::vector> route( auto const r = [&]( dijkstra& d) -> std::vector> { return route(w, d, from, to, from_match, to_match, max, dir, blocked, - do_reconstruct); + sharing, do_reconstruct); }; switch (profile) { @@ -464,6 +501,7 @@ std::vector> route( return r(get_dijkstra>()); case search_profile::kCarParkingWheelchair: return r(get_dijkstra>()); + case search_profile::kBikeSharing: return r(get_dijkstra()); } throw utl::fail("not implemented"); @@ -477,14 +515,16 @@ std::optional route(ways const& w, match_view_t to_match, cost_t const max, direction const dir, - bitvec const* blocked) { + bitvec const* blocked, + sharing_data const* sharing) { if (from_match.empty() || to_match.empty()) { return std::nullopt; } auto const r = [&](dijkstra& d) -> std::optional { - return route(w, d, from, to, from_match, to_match, max, dir, blocked); + return route(w, d, from, to, from_match, to_match, max, dir, blocked, + sharing); }; switch (profile) { @@ -498,6 +538,7 @@ std::optional route(ways const& w, return r(get_dijkstra>()); case search_profile::kCarParkingWheelchair: return r(get_dijkstra>()); + case search_profile::kBikeSharing: return r(get_dijkstra()); } throw utl::fail("not implemented"); @@ -518,4 +559,4 @@ get_dijkstra>(); template dijkstra>& get_dijkstra>(); -} // namespace osr \ No newline at end of file +} // namespace osr diff --git a/src/routing/mode.cc b/src/routing/mode.cc new file mode 100644 index 0000000..5636a8c --- /dev/null +++ b/src/routing/mode.cc @@ -0,0 +1,19 @@ +#include "osr/routing/mode.h" + +#include "utl/verify.h" + +#include "cista/hash.h" + +namespace osr { + +std::string_view to_str(mode const m) { + switch (m) { + case mode::kFoot: return "foot"; + case mode::kWheelchair: return "wheelchair"; + case mode::kCar: return "car"; + case mode::kBike: return "bike"; + } + throw utl::fail("{} is not a valid mode", static_cast(m)); +} + +} // namespace osr diff --git a/src/routing/profile.cc b/src/routing/profile.cc index e1d3028..7e8d192 100644 --- a/src/routing/profile.cc +++ b/src/routing/profile.cc @@ -15,6 +15,7 @@ search_profile to_profile(std::string_view s) { case cista::hash("car_parking"): return search_profile::kCarParking; case cista::hash("car_parking_wheelchair"): return search_profile::kCarParkingWheelchair; + case cista::hash("bike_sharing"): return search_profile::kBikeSharing; } throw utl::fail("{} is not a valid profile", s); } @@ -27,8 +28,9 @@ std::string_view to_str(search_profile const p) { case search_profile::kBike: return "bike"; case search_profile::kCarParking: return "car_parking"; case search_profile::kCarParkingWheelchair: return "car_parking_wheelchair"; + case search_profile::kBikeSharing: return "bike_sharing"; } throw utl::fail("{} is not a valid profile", static_cast(p)); } -} // namespace osr \ No newline at end of file +} // namespace osr