From 03de6b4553e6d76b44a26b477dcd9343333f4f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20G=C3=BCndling?= Date: Fri, 1 Nov 2024 18:29:12 +0100 Subject: [PATCH] no level = wildcard (can transition to any level) (#23) * custom level struct * wip --- exe/backend/src/http_server.cc | 6 +-- include/osr/geojson.h | 17 ++++---- include/osr/lookup.h | 2 +- include/osr/routing/profiles/bike_sharing.h | 2 +- include/osr/routing/profiles/car_parking.h | 17 ++++---- include/osr/routing/profiles/foot.h | 30 +++++++------- include/osr/routing/route.h | 4 +- include/osr/types.h | 45 ++++++++++++++++----- 8 files changed, 74 insertions(+), 49 deletions(-) diff --git a/exe/backend/src/http_server.cc b/exe/backend/src/http_server.cc index 9591654..8dbbe83 100644 --- a/exe/backend/src/http_server.cc +++ b/exe/backend/src/http_server.cc @@ -59,7 +59,7 @@ location parse_location(json::value const& v) { auto const& obj = v.as_object(); return {obj.at("lat").as_double(), obj.at("lng").as_double(), obj.contains("level") ? to_level(obj.at("level").to_number()) - : level_t::invalid()}; + : kNoLevel}; } json::value to_json(std::vector const& polyline) { @@ -230,7 +230,7 @@ struct http_server::impl { auto const query = boost::json::parse(req.body()).as_object(); auto const level = query.contains("level") ? to_level(query.at("level").to_number()) - : level_t::invalid(); + : kNoLevel; auto const waypoints = query.at("waypoints").as_array(); auto const min = point::from_latlng( {waypoints[1].as_double(), waypoints[0].as_double()}); @@ -239,7 +239,7 @@ struct http_server::impl { auto gj = geojson_writer{.w_ = w_, .platforms_ = pl_}; pl_->find(min, max, [&](platform_idx_t const i) { - if (level == level_t::invalid() || pl_->get_level(w_, i) == level) { + if (level == kNoLevel || pl_->get_level(w_, i) == level) { gj.write_platform(i); } }); diff --git a/include/osr/geojson.h b/include/osr/geojson.h index 5a8c587..8500a29 100644 --- a/include/osr/geojson.h +++ b/include/osr/geojson.h @@ -131,15 +131,14 @@ struct geojson_writer { auto const p = w_.r_->node_properties_[n]; auto ss = std::stringstream{}; - Dijkstra::profile_t::resolve_all(*w_.r_, n, level_t::invalid(), - [&](auto const x) { - auto const cost = s->get_cost(x); - if (cost != kInfeasible) { - ss << "{"; - x.print(ss, w_); - ss << ", " << cost << "}\n"; - } - }); + Dijkstra::profile_t::resolve_all(*w_.r_, n, kNoLevel, [&](auto const x) { + auto const cost = s->get_cost(x); + if (cost != kInfeasible) { + ss << "{"; + x.print(ss, w_); + ss << ", " << cost << "}\n"; + } + }); auto levels = std::vector(); foot::for_each_elevator_level( diff --git a/include/osr/lookup.h b/include/osr/lookup.h index 7244c0b..2787487 100644 --- a/include/osr/lookup.h +++ b/include/osr/lookup.h @@ -44,7 +44,7 @@ void till_the_end(T const& start, struct node_candidate { bool valid() const { return node_ != node_idx_t::invalid(); } - level_t lvl_{level_t::invalid()}; + level_t lvl_{kNoLevel}; direction way_dir_{direction::kForward}; node_idx_t node_{node_idx_t::invalid()}; double dist_to_node_{0.0}; diff --git a/include/osr/routing/profiles/bike_sharing.h b/include/osr/routing/profiles/bike_sharing.h index 4e6aaf0..b3267f3 100644 --- a/include/osr/routing/profiles/bike_sharing.h +++ b/include/osr/routing/profiles/bike_sharing.h @@ -153,7 +153,7 @@ struct bike_sharing { entry() { utl::fill(pred_, node_idx_t::invalid()); utl::fill(cost_, kInfeasible); - utl::fill(pred_lvl_, level_t::invalid()); + utl::fill(pred_lvl_, kNoLevel); utl::fill(pred_type_, node_type::kInvalid); } diff --git a/include/osr/routing/profiles/car_parking.h b/include/osr/routing/profiles/car_parking.h index db2ba0a..d91b26a 100644 --- a/include/osr/routing/profiles/car_parking.h +++ b/include/osr/routing/profiles/car_parking.h @@ -223,7 +223,7 @@ struct car_parking { w, n, lvl, [&](footp::node const neighbor) { f(to_node(neighbor)); }); car::resolve_all(w, n, lvl, [&](car::node const neighbor) { auto const p = w.way_properties_[w.node_ways_[n][neighbor.way_]]; - auto const node_level = lvl == level_t::invalid() ? p.from_level() : lvl; + auto const node_level = lvl == kNoLevel ? p.from_level() : lvl; f(to_node(neighbor, node_level)); }); } @@ -277,14 +277,13 @@ struct car_parking { Fn&& f) { auto const way_properties = w.way_properties_[way]; search_dir == direction::kForward - ? car::resolve_start_node(w, way, n, lvl, search_dir, - [&](car::node const cn) { - auto const node_level = - lvl == level_t::invalid() - ? way_properties.from_level() - : lvl; - f(to_node(cn, node_level)); - }) + ? car::resolve_start_node( + w, way, n, lvl, search_dir, + [&](car::node const cn) { + auto const node_level = + lvl == kNoLevel ? way_properties.from_level() : lvl; + f(to_node(cn, node_level)); + }) : footp::resolve_start_node( w, way, n, lvl, search_dir, [&](footp::node const fn) { f(to_node(fn)); }); diff --git a/include/osr/routing/profiles/foot.h b/include/osr/routing/profiles/foot.h index a0c051b..a05bfa7 100644 --- a/include/osr/routing/profiles/foot.h +++ b/include/osr/routing/profiles/foot.h @@ -19,7 +19,7 @@ struct foot { friend bool operator==(node, node) = default; static constexpr node invalid() noexcept { - return {.n_ = node_idx_t::invalid(), .lvl_{level_t::invalid()}}; + return {.n_ = node_idx_t::invalid(), .lvl_{kNoLevel}}; } constexpr node_idx_t get_node() const noexcept { return n_; } @@ -104,10 +104,12 @@ struct foot { direction, Fn&& f) { auto const p = w.way_properties_[way]; - if (lvl == level_t::invalid() || + if (lvl == kNoLevel || (p.from_level() == lvl || p.to_level() == lvl || - can_use_elevator(w, n, lvl))) { - f(node{n, lvl == level_t::invalid() ? p.from_level() : lvl}); + can_use_elevator(w, n, lvl)) || + (lvl == to_level(0.F) && + (p.from_level() == kNoLevel && p.to_level() == kNoLevel))) { + f(node{n, lvl == kNoLevel ? p.from_level() : lvl}); } } @@ -121,7 +123,7 @@ struct foot { for (auto i = way_pos_t{0U}; i != ways.size(); ++i) { // TODO what's with stairs? need to resolve to from_level or to_level? auto const p = w.way_properties_[w.node_ways_[n][i]]; - if (lvl == level_t::invalid()) { + if (lvl == kNoLevel) { if (levels.emplace(p.from_level()).second) { f(node{n, p.from_level()}); } @@ -129,7 +131,7 @@ struct foot { f(node{n, p.to_level()}); } } else if ((p.from_level() == lvl || p.to_level() == lvl || - can_use_elevator(w, n, lvl)) && + p.from_level() == kNoLevel || can_use_elevator(w, n, lvl)) && levels.emplace(lvl).second) { f(node{n, lvl}); } @@ -239,7 +241,8 @@ struct foot { } else if (can_use_elevator(w, from_node, way_prop.from_level(), from_level)) { return way_prop.from_level(); - } else if (way_prop.from_level() == from_level) { + } else if (way_prop.from_level() == from_level || + way_prop.from_level() == kNoLevel || from_level == kNoLevel) { return from_level; } else { return std::nullopt; @@ -249,7 +252,7 @@ struct foot { static bool can_use_elevator(ways::routing const& w, way_idx_t const way, level_t const a, - level_t const b = level_t::invalid()) { + level_t const b = kNoLevel) { return w.way_properties_[way].is_elevator() && can_use_elevator(w, w.way_nodes_[way][0], a, b); } @@ -271,7 +274,7 @@ struct foot { static bool can_use_elevator(ways::routing const& w, node_idx_t const n, level_t const a, - level_t const b = level_t::invalid()) { + level_t const b = kNoLevel) { auto const p = w.node_properties_[n]; if (!p.is_elevator()) { return false; @@ -279,12 +282,11 @@ struct foot { if (p.is_multi_level()) { auto const levels = get_elevator_multi_levels(w, n); - return utl::has_bit_set(levels, to_idx(a)) && - (b == level_t::invalid() || utl::has_bit_set(levels, to_idx(b))); + return (a == kNoLevel || utl::has_bit_set(levels, to_idx(a))) && + (b == kNoLevel || utl::has_bit_set(levels, to_idx(b))); } else { - return (a == p.from_level() || a == p.to_level()) && - (b == level_t::invalid() || b == p.from_level() || - b == p.to_level()); + return (a == kNoLevel || a == p.from_level() || a == p.to_level()) && + (b == kNoLevel || b == p.from_level() || b == p.to_level()); } } diff --git a/include/osr/routing/route.h b/include/osr/routing/route.h index 38282fc..c475ae7 100644 --- a/include/osr/routing/route.h +++ b/include/osr/routing/route.h @@ -23,8 +23,8 @@ struct sharing_data; struct path { struct segment { geo::polyline polyline_; - level_t from_level_{level_t::invalid()}; - level_t to_level_{level_t::invalid()}; + level_t from_level_{kNoLevel}; + level_t to_level_{kNoLevel}; node_idx_t from_{node_idx_t::invalid()}; node_idx_t to_{node_idx_t::invalid()}; way_idx_t way_{way_idx_t::invalid()}; diff --git a/include/osr/types.h b/include/osr/types.h index d691d3f..d2ad0b3 100644 --- a/include/osr/types.h +++ b/include/osr/types.h @@ -103,6 +103,10 @@ enum class direction : std::uint8_t { kBackward, }; +inline std::ostream& operator<<(std::ostream& out, direction const d) { + return out << (d == direction::kBackward ? "bwd" : "fwd"); +} + constexpr direction opposite(direction const dir) { return dir == direction::kForward ? direction::kBackward : direction::kForward; @@ -134,37 +138,58 @@ constexpr direction to_direction(std::string_view s) { } // level -using level_t = cista::strong; +struct level_t { + friend constexpr std::uint8_t to_idx(level_t l) { return l.v_; } + + friend std::ostream& operator<<(std::ostream& out, level_t const l) { + if (to_idx(l) == 0) { + return out << "-"; + } else { + return out << (-4.0 + ((to_idx(l) - 1U) / 4.0F)); + } + } + + template + explicit constexpr level_t(X x) : v_{static_cast(x)} {} + + constexpr level_t() = default; + + auto operator<=>(level_t const&) const = default; + + constexpr cista::hash_t hash() const { return v_; } + + std::uint8_t v_; +}; +constexpr auto const kNoLevel = level_t{0U}; constexpr auto const kMinLevel = -4.0F; -constexpr auto const kMaxLevel = 3.75F; +constexpr auto const kMaxLevel = 3.5F; constexpr level_t to_level(float const f) { - return level_t{static_cast((f - kMinLevel) / 0.25F)}; + return level_t{static_cast((f - kMinLevel) / 0.25F + 1U)}; } constexpr float to_float(level_t const l) { - return l == level_t::invalid() ? 0.0F : (kMinLevel + (to_idx(l) / 4.0F)); + return (l == kNoLevel) ? 0.0F : (kMinLevel + ((to_idx(l) - 1U) / 4.0F)); } constexpr auto const kLevelBits = cista::constexpr_trailing_zeros( - cista::next_power_of_two(to_idx(to_level(kMaxLevel)))); + cista::next_power_of_two(to_idx(to_level(kMaxLevel)) + 1U)); using level_bits_t = std::uint32_t; constexpr std::tuple get_levels( bool const has_level, level_bits_t const levels) noexcept { if (!has_level) { - return {level_t{to_level(0.F)}, level_t{to_level(0.F)}, false}; + return {level_t{kNoLevel}, level_t{kNoLevel}, false}; } - auto from = level_t::invalid(), to = level_t::invalid(); + auto from = kNoLevel, to = kNoLevel; utl::for_each_set_bit(levels, [&](auto&& bit) { - from == level_t::invalid() // + from == kNoLevel // ? from = level_t{bit} : to = level_t{bit}; }); - return {from, to == level_t::invalid() ? from : to, - std::popcount(levels) > 2}; + return {from, to == kNoLevel ? from : to, std::popcount(levels) > 2}; } static_assert(kLevelBits == 5U);