diff --git a/CMakeLists.txt b/CMakeLists.txt index 46354ae..889b339 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,7 +145,7 @@ configure_file( file(GLOB_RECURSE osr-test-files test/*cc) add_executable(osr-test ${osr-test-files}) target_include_directories(osr-test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/generated) -target_link_libraries(osr-test gtest osr) +target_link_libraries(osr-test gtest osr boost-json) # --- MIMALLOC --- if (OSR_MIMALLOC) diff --git a/exe/backend/src/http_server.cc b/exe/backend/src/http_server.cc index 8dbbe83..4eb87cd 100644 --- a/exe/backend/src/http_server.cc +++ b/exe/backend/src/http_server.cc @@ -58,7 +58,7 @@ web_server::string_res_t json_response( 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()) + obj.contains("level") ? level_t{obj.at("level").to_number()} : kNoLevel}; } @@ -132,7 +132,7 @@ struct http_server::impl { {"type", "Feature"}, { "properties", - {{"level", to_float(s.from_level_)}, + {{"level", s.from_level_.to_float()}, {"osm_way_id", s.way_ == way_idx_t::invalid() ? 0U @@ -162,7 +162,7 @@ struct http_server::impl { } }); auto levels_sorted = - utl::to_vec(levels, [](level_t const l) { return to_float(l); }); + utl::to_vec(levels, [](level_t const l) { return l.to_float(); }); utl::sort(levels_sorted, [](auto&& a, auto&& b) { return a > b; }); cb(json_response(req, json::serialize(utl::all(levels_sorted) // @@ -184,10 +184,10 @@ struct http_server::impl { switch (profile) { case search_profile::kFoot: - send_graph_response>(req, cb, gj); + send_graph_response>(req, cb, gj); break; case search_profile::kWheelchair: - send_graph_response>(req, cb, gj); + send_graph_response>(req, cb, gj); break; case search_profile::kBike: send_graph_response(req, cb, gj); break; case search_profile::kCar: send_graph_response(req, cb, gj); break; @@ -229,7 +229,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{query.at("level").to_number()} : kNoLevel; auto const waypoints = query.at("waypoints").as_array(); auto const min = point::from_latlng( diff --git a/include/osr/extract/tags.h b/include/osr/extract/tags.h index 19dea54..59da382 100644 --- a/include/osr/extract/tags.h +++ b/include/osr/extract/tags.h @@ -53,13 +53,16 @@ struct tags { is_platform_ = true; } break; + case cista::hash("layer"): + // not correct but layer seems to be used like level in some places :/ + [[fallthrough]]; case cista::hash("level"): { has_level_ = true; auto s = utl::cstr{t.value()}; while (s) { auto l = 0.0F; utl::parse_arg(s, l); - auto const lvl = to_level(std::clamp(l, kMinLevel, kMaxLevel)); + auto const lvl = level_t{std::clamp(l, kMinLevel, kMaxLevel)}; level_bits_ |= (1U << to_idx(lvl)); if (s) { ++s; diff --git a/include/osr/geojson.h b/include/osr/geojson.h index 8500a29..a9ee766 100644 --- a/include/osr/geojson.h +++ b/include/osr/geojson.h @@ -64,7 +64,7 @@ struct geojson_writer { {"properties", {{"type", is_way(r) ? "way" : "node"}, {"platform_idx", to_idx(i)}, - {"level", to_float(platforms_->get_level(w_, i))}, + {"level", platforms_->get_level(w_, i).to_float()}, {"names", platform_names(*platforms_, i)}}}, {"geometry", geometry}}); } @@ -94,8 +94,8 @@ struct geojson_writer { {"oneway_car", p.is_oneway_car()}, {"oneway_bike", p.is_oneway_bike()}, {"max_speed", p.max_speed_km_per_h()}, - {"from_level", to_float(level_t{p.from_level()})}, - {"to_level", to_float(level_t{p.to_level()})}, + {"from_level", p.from_level().to_float()}, + {"to_level", p.to_level().to_float()}, {"is_elevator", p.is_elevator()}, {"is_steps", p.is_steps()}, {"is_parking", p.is_parking_}}}, @@ -116,8 +116,8 @@ struct geojson_writer { {"oneway_car", p.is_oneway_car()}, {"oneway_bike", p.is_oneway_bike()}, {"max_speed", p.max_speed_km_per_h()}, - {"from_level", to_float(level_t{p.from_level()})}, - {"to_level", to_float(level_t{p.to_level()})}, + {"from_level", p.from_level().to_float()}, + {"to_level", p.to_level().to_float()}, {"is_elevator", p.is_elevator()}, {"is_steps", p.is_steps()}}}, {"geometry", to_line_string(w_.way_polylines_[i])}}); @@ -142,7 +142,7 @@ struct geojson_writer { auto levels = std::vector(); foot::for_each_elevator_level( - *w_.r_, n, [&](auto&& l) { levels.push_back(to_float(level_t{l})); }); + *w_.r_, n, [&](level_t const l) { levels.push_back(l.to_float()); }); auto properties = boost::json::object{ {"osm_node_id", to_idx(w_.node_to_osm_[n])}, diff --git a/include/osr/location.h b/include/osr/location.h index 2959734..dc6b227 100644 --- a/include/osr/location.h +++ b/include/osr/location.h @@ -14,7 +14,7 @@ struct location { CISTA_FRIEND_COMPARABLE(location) friend std::ostream& operator<<(std::ostream& out, location const& l) { - return out << "{ pos=" << l.pos_ << ", lvl=" << to_float(l.lvl_) << " }"; + return out << "{ pos=" << l.pos_ << ", lvl=" << l.lvl_.to_float() << " }"; } geo::latlng pos_; diff --git a/include/osr/platforms.h b/include/osr/platforms.h index dc913b4..b459d13 100644 --- a/include/osr/platforms.h +++ b/include/osr/platforms.h @@ -133,7 +133,7 @@ struct platforms { level_t get_level(ways const& w, platform_idx_t const i) const { if (i == platform_idx_t::invalid()) { - return to_level(0.0); + return level_t{0.0F}; } return std::visit( utl::overloaded{ diff --git a/include/osr/routing/profiles/bike_sharing.h b/include/osr/routing/profiles/bike_sharing.h index b3267f3..d39d68e 100644 --- a/include/osr/routing/profiles/bike_sharing.h +++ b/include/osr/routing/profiles/bike_sharing.h @@ -83,14 +83,14 @@ struct bike_sharing { 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_)}, + {"level", lvl_.to_float()}, {"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_) + << ", level=" << lvl_.to_float() << ", type=" << node_type_to_str(type_) << ")"; } diff --git a/include/osr/routing/profiles/car_parking.h b/include/osr/routing/profiles/car_parking.h index d91b26a..d35722d 100644 --- a/include/osr/routing/profiles/car_parking.h +++ b/include/osr/routing/profiles/car_parking.h @@ -42,7 +42,7 @@ struct car_parking { 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_)}, + {"level", lvl_.to_float()}, {"type", node_type_to_str(type_)}}; if (is_car_node()) { properties.emplace("direction", to_str(dir_)); @@ -52,7 +52,7 @@ struct car_parking { std::ostream& print(std::ostream& out, ways const& w) const { return out << "(node=" << w.node_to_osm_[n_] - << ", level=" << to_float(lvl_) << ", dir=" << to_str(dir_) + << ", level=" << lvl_.to_float() << ", dir=" << to_str(dir_) << ", way=" << w.way_osm_idx_[w.r_->node_ways_[n_][way_]] << ", type=" << node_type_to_str(type_) << ")"; } diff --git a/include/osr/routing/profiles/foot.h b/include/osr/routing/profiles/foot.h index a05bfa7..7074a45 100644 --- a/include/osr/routing/profiles/foot.h +++ b/include/osr/routing/profiles/foot.h @@ -28,8 +28,7 @@ struct foot { 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_) << ")"; + return out << "(node=" << w.node_to_osm_[n_] << ", level=" << lvl_ << ")"; } node_idx_t n_; @@ -107,9 +106,9 @@ struct foot { if (lvl == kNoLevel || (p.from_level() == lvl || p.to_level() == lvl || can_use_elevator(w, n, lvl)) || - (lvl == to_level(0.F) && + (lvl == level_t{0.F} && (p.from_level() == kNoLevel && p.to_level() == kNoLevel))) { - f(node{n, lvl == kNoLevel ? p.from_level() : lvl}); + f(node{n, p.from_level()}); } } @@ -243,7 +242,7 @@ struct foot { return way_prop.from_level(); } else if (way_prop.from_level() == from_level || way_prop.from_level() == kNoLevel || from_level == kNoLevel) { - return from_level; + return way_prop.from_level(); } else { return std::nullopt; } @@ -263,8 +262,9 @@ struct foot { Fn&& f) { auto const p = w.node_properties_[n]; if (p.is_multi_level()) { - utl::for_each_set_bit(get_elevator_multi_levels(w, n), - [&](auto&& l) { f(level_t{l}); }); + utl::for_each_set_bit(get_elevator_multi_levels(w, n), [&](auto&& l) { + f(level_t{static_cast(l)}); + }); } else { f(p.from_level()); f(p.to_level()); diff --git a/include/osr/types.h b/include/osr/types.h index d2ad0b3..0085c4c 100644 --- a/include/osr/types.h +++ b/include/osr/types.h @@ -138,19 +138,26 @@ constexpr direction to_direction(std::string_view s) { } // level +constexpr auto const kMinLevel = -4.0F; +constexpr auto const kMaxLevel = 3.5F; + struct level_t { + static constexpr auto kNoLevel = 0U; + 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)); - } + return (l.v_ == kNoLevel) ? (out << "-") : (out << l.to_float()); } - template - explicit constexpr level_t(X x) : v_{static_cast(x)} {} + explicit constexpr level_t(std::uint8_t const x) : v_{x} {} + + explicit constexpr level_t(float const f) + : v_{static_cast((f - kMinLevel) / 0.25F + 1U)} {} + + constexpr float to_float() const { + return (v_ == kNoLevel) ? 0.0F : (kMinLevel + ((v_ - 1U) / 4.0F)); + } constexpr level_t() = default; @@ -161,20 +168,10 @@ struct level_t { std::uint8_t v_; }; -constexpr auto const kNoLevel = level_t{0U}; -constexpr auto const kMinLevel = -4.0F; -constexpr auto const kMaxLevel = 3.5F; - -constexpr level_t to_level(float const f) { - return level_t{static_cast((f - kMinLevel) / 0.25F + 1U)}; -} - -constexpr float to_float(level_t const l) { - return (l == kNoLevel) ? 0.0F : (kMinLevel + ((to_idx(l) - 1U) / 4.0F)); -} +constexpr auto const kNoLevel = level_t{std::uint8_t{0U}}; constexpr auto const kLevelBits = cista::constexpr_trailing_zeros( - cista::next_power_of_two(to_idx(to_level(kMaxLevel)) + 1U)); + cista::next_power_of_two(to_idx(level_t{kMaxLevel}) + 1U)); using level_bits_t = std::uint32_t; @@ -186,8 +183,8 @@ constexpr std::tuple get_levels( auto from = kNoLevel, to = kNoLevel; utl::for_each_set_bit(levels, [&](auto&& bit) { from == kNoLevel // - ? from = level_t{bit} - : to = level_t{bit}; + ? from = level_t{static_cast(bit)} + : to = level_t{static_cast(bit)}; }); return {from, to == kNoLevel ? from : to, std::popcount(levels) > 2}; } diff --git a/src/route.cc b/src/route.cc index f893238..9ea7769 100644 --- a/src/route.cc +++ b/src/route.cc @@ -133,8 +133,8 @@ double add_path(ways const& w, } } } else { - segment.from_level_ = to_level(0); - segment.to_level_ = to_level(0); + segment.from_level_ = level_t{0.0F}; + segment.to_level_ = level_t{0.0F}; segment.from_ = from.get_node(); segment.to_ = to.get_node(); // polyline has to be filled by the caller, because we don't know diff --git a/test/level_test.cc b/test/level_test.cc index b7807e4..db25aad 100644 --- a/test/level_test.cc +++ b/test/level_test.cc @@ -9,12 +9,15 @@ using namespace osr; TEST(osr, level) { - auto const lvl_0 = to_float(to_level(0.1F)); + auto const lvl_0 = level_t{0.1F}.to_float(); EXPECT_EQ(0.0F, lvl_0); - auto const lvl_4 = to_float(to_level(4.0F)); + auto const lvl_neg4 = level_t{-4.0F}.to_float(); + EXPECT_EQ(-4.0F, lvl_neg4); + + auto const lvl_4 = level_t{4.0F}.to_float(); EXPECT_EQ(4.0F, lvl_4); - auto const lvl_minus_3 = to_float(to_level(-3.0F)); + auto const lvl_minus_3 = level_t{-3.0F}.to_float(); EXPECT_EQ(-3.0F, lvl_minus_3); } \ No newline at end of file diff --git a/test/lvl_wildcard_test.cc b/test/lvl_wildcard_test.cc new file mode 100644 index 0000000..db87d63 --- /dev/null +++ b/test/lvl_wildcard_test.cc @@ -0,0 +1,90 @@ +#ifdef _WIN32 +#include "windows.h" +#endif + +#include "gtest/gtest.h" + +#include + +#include "boost/json.hpp" + +#include "utl/pipes.h" + +#include "fmt/core.h" +#include "fmt/ranges.h" + +#include "osr/extract/extract.h" +#include "osr/geojson.h" +#include "osr/lookup.h" +#include "osr/routing/route.h" +#include "osr/ways.h" + +namespace json = boost::json; +namespace fs = std::filesystem; +using namespace osr; + +TEST(routing, no_lvl_wildcard) { + constexpr auto const kTestFolder = "/tmp/osr_stuttgart"; + + auto ec = std::error_code{}; + fs::remove_all(kTestFolder, ec); + fs::create_directories(kTestFolder, ec); + + extract(false, "test/stuttgart.osm.pbf", kTestFolder); + + auto w = osr::ways{kTestFolder, cista::mmap::protection::READ}; + auto l = osr::lookup{w}; + + auto const p = route(w, l, search_profile::kFoot, // + {{48.7829, 9.18212}, level_t{0.F}}, + {{48.7847, 9.18337}, level_t{1.F}}, // + 3600, direction::kForward, 100); + + ASSERT_TRUE(p.has_value()); + + auto const path = json::serialize(json::object{ + {"type", "FeatureCollection"}, + {"metadata", {{"duration", p->cost_}, {"distance", p->dist_}}}, + {"features", + utl::all(p->segments_) | utl::transform([&](const path::segment& s) { + return json::object{ + {"type", "Feature"}, + { + "properties", + {{"level", s.from_level_.to_float()}, + {"osm_way_id", s.way_ == way_idx_t::invalid() + ? 0U + : to_idx(w.way_osm_idx_[s.way_])}, + {"cost", s.cost_}, + {"distance", s.dist_}}, + }, + {"geometry", to_line_string(s.polyline_)}}; + }) | utl::emplace_back_to()}}); + EXPECT_EQ( + "{\"type\":\"FeatureCollection\",\"metadata\":{\"duration\":385," + "\"distance\":4.2683610950337305E2},\"features\":[{\"type\":\"Feature\"," + "\"properties\":{\"level\":0E0,\"osm_way_id\":0,\"cost\":32,\"distance\":" + "36},\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[9.18212E0,4." + "87829E1],[9.1824098E0,4.87829011E1],[9.1825297E0,4.87829369E1],[9." + "1826092E0,4.87829279E1]]}},{\"type\":\"Feature\",\"properties\":{" + "\"level\":1E0,\"osm_way_id\":1016875485,\"cost\":308,\"distance\":339}," + "\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[9.1826092E0,4." + "87829279E1],[9.1827967E0,4.87828835E1],[9.1830696E0,4.87828214E1],[9." + "1836147E0,4.87829191E1],[9.1844751E0,4.87833829E1],[9.1845214E0,4." + "87834284E1],[9.1845395E0,4.87835258E1],[9.1845748E0,4.87835747E1],[9." + "1846319E0,4.8783605E1],[9.1846862E0,4.87836279E1],[9.1847208E0,4." + "8783654E1],[9.1847473E0,4.8783684E1],[9.1847559E0,4.87837162E1],[9." + "1847516E0,4.8783756E1],[9.1846579E0,4.87840841E1],[9.1846341E0,4." + "8784113E1],[9.1845471E0,4.87841486E1],[9.1844849E0,4.87841546E1],[9." + "1841289E0,4.87841629E1],[9.1840357E0,4.87841863E1],[9.1838475E0,4." + "87842531E1],[9.1835E0,4.8784369E1],[9.1835041E0,4.87843773E1]]}},{" + "\"type\":\"Feature\",\"properties\":{\"level\":1E0,\"osm_way_id\":" + "713810685,\"cost\":4,\"distance\":4},\"geometry\":{\"type\":" + "\"LineString\",\"coordinates\":[[9.1835041E0,4.87843773E1],[9.1835086E0," + "4.87844171E1]]}},{\"type\":\"Feature\",\"properties\":{\"level\":1E0," + "\"osm_way_id\":0,\"cost\":41,\"distance\":47},\"geometry\":{\"type\":" + "\"LineString\",\"coordinates\":[[9.1835086E0,4.87844171E1],[9.1834509E0," + "4.87844364E1],[9.1832201E0,4.87845268E1],[9.183214598396768E0,4." + "8784528970208854E1],[9.18337E0,4.87847E1]]}}]}", + path); +} \ No newline at end of file diff --git a/test/stuttgart.osm.pbf b/test/stuttgart.osm.pbf new file mode 100644 index 0000000..8a2350d Binary files /dev/null and b/test/stuttgart.osm.pbf differ diff --git a/web/index.html b/web/index.html index 6b74647..422c83b 100644 --- a/web/index.html +++ b/web/index.html @@ -2,109 +2,97 @@ Open Street Router - + - + -
- -
-
- - -
- - - -
-
-
+G +
+ + +
+ Test
-
- - - -
-
+