diff --git a/CMakeLists.txt b/CMakeLists.txt index 86971623..832f4813 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -130,6 +130,9 @@ target_link_libraries(osr add_executable(osr-extract exe/extract.cc) target_link_libraries(osr-extract osr) +add_executable(osr-preprocessing exe/preprocessing.cc) +target_link_libraries(osr-preprocessing osr) + add_executable(osr-benchmark exe/benchmark.cc) target_link_libraries(osr-benchmark osr) diff --git a/exe/backend/src/main.cc b/exe/backend/src/main.cc index 98cf3647..290c37b8 100644 --- a/exe/backend/src/main.cc +++ b/exe/backend/src/main.cc @@ -16,6 +16,9 @@ #include "osr/elevation_storage.h" #include "osr/lookup.h" #include "osr/platforms.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" +#include "osr/routing/profile.h" +#include "osr/routing/profiles/car.h" #include "osr/ways.h" namespace fs = std::filesystem; @@ -78,6 +81,14 @@ int main(int argc, char const* argv[]) { } auto const w = ways{opt.data_dir_, cista::mmap::protection::READ}; + if (auto const f_name = + shortcut_storage::get_filename(search_profile::kCar); + fs::exists(fs::path{opt.data_dir_} / f_name)) { + get_shortcut_storage() = + shortcut_storage::read(opt.data_dir_, search_profile::kCar); + } else { + fmt::println("{} not found. Shortcuts won't be available", f_name); + } auto const platforms_check_path = opt.data_dir_ / "node_is_platform.bin"; if (!fs::exists(platforms_check_path)) { diff --git a/exe/benchmark.cc b/exe/benchmark.cc index 735b3cba..c5c869ca 100644 --- a/exe/benchmark.cc +++ b/exe/benchmark.cc @@ -265,11 +265,11 @@ int main(int argc, char const* argv[]) { auto const ends = set_end

(params, b, w, end); auto const start_time = std::chrono::steady_clock::now(); - d.template run( + d.template run( params, w, *w.r_, opt.max_dist_, nullptr, nullptr, - elevations.get()); + elevations.get(), nullptr, node_idx_t::invalid()); auto const middle_time = std::chrono::steady_clock::now(); - b.template run( + b.template run( params, w, *w.r_, opt.max_dist_, nullptr, nullptr, elevations.get()); auto const end_time = std::chrono::steady_clock::now(); diff --git a/exe/preprocessing.cc b/exe/preprocessing.cc new file mode 100644 index 00000000..75a5a696 --- /dev/null +++ b/exe/preprocessing.cc @@ -0,0 +1,71 @@ +#include + +#include "fmt/std.h" + +#include "conf/options_parser.h" + +#include "utl/progress_tracker.h" + +#include "osr/preprocessing/contraction_hierarchy/contraction.h" +#include "osr/preprocessing/contraction_hierarchy/node_order.h" +#include "osr/routing/parameters.h" +#include "osr/routing/profile.h" +#include "osr/routing/profiles/car.h" +#include "osr/types.h" + +using namespace osr; +using namespace boost::program_options; +namespace fs = std::filesystem; + +struct config : public conf::configuration { + config(std::filesystem::path dir, + size_t const seed, + cost_t const detour_limit, + std::string const& method) + : configuration{"Options"}, + dir_{std::move(dir)}, + seed_(seed), + detour_limit_(detour_limit), + ordering_method_(method) { + param(dir_, "folder,f", + "directory of extracted data and to save shortcuts to"); + param(seed_, "seed,s", + "(optional) the seed to for random node ordering (default: 1337)"); + param(detour_limit_, "detour,d", + "(optional) max feasible cost to detour turning restrictions " + "(default: 10 min)"); + param(ordering_method_, "method,m", + "(optional) the method to order the nodes: random, real (orders by " + "OSM metadata) (default: real)"); + } + + std::filesystem::path dir_; + size_t seed_; + cost_t detour_limit_; + std::string ordering_method_; +}; + +int main(int ac, char const** av) { + auto c = config{"", 1337, 10 * 60U, "real"}; + + conf::options_parser parser({&c}); + parser.read_command_line_args(ac, av); + + parser.read_configuration_file(); + + parser.print_unrecognized(std::cout); + parser.print_used(std::cout); + + if (!fs::is_directory(c.dir_)) { + fmt::println("input extract directory {} not found", c.dir_); + return 1; + } + + utl::activate_progress_tracker("osr-preprocessing"); + auto const silencer = utl::global_progress_bars{false}; + + preprocess_ch( + c.dir_, search_profile::kCar, + std::get(get_parameters(search_profile::kCar)), c.seed_, + c.detour_limit_, node_order::string2method(c.ordering_method_)); +} \ No newline at end of file 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/preprocessing/contraction_hierarchy/contraction.h b/include/osr/preprocessing/contraction_hierarchy/contraction.h new file mode 100644 index 00000000..e380e84d --- /dev/null +++ b/include/osr/preprocessing/contraction_hierarchy/contraction.h @@ -0,0 +1,427 @@ +#pragma once + +#include +#include +#include +#include + +#include "fmt/std.h" +#include "utl/enumerate.h" + +#include "osr/preprocessing/contraction_hierarchy/node_order.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" +#include "osr/routing/dijkstra.h" +#include "osr/routing/profile.h" +#include "osr/routing/route.h" +#include "osr/types.h" +#include "osr/ways.h" + +namespace osr { + +template +struct incoming_sc_candidate { + Node via_; + Node in_; + way_idx_t way_; + cost_t way_cost_; +}; + +template +struct outgoing_sc_candidate { + Node out_; + Node via_; + way_idx_t way_; + cost_t way_cost_; + cost_t cost_; + cost_t turning_cost_; +}; + +template +struct found_shortcut { + shortcut_storage::shortcut shortcut_; + shortcut_storage::shortcut_reconstruct shortcut_reconstruct_; + cost_t cost_; +}; + +template +std::vector> find_incoming( + typename P::parameters const& params, + ways::routing const& r, + shortcut_storage const& sc_stor, + node_idx_t const via_idx) { + auto incoming = std::vector>{}; + P::resolve_all(r, via_idx, kNoLevel, [&](typename P::node const u) { + P::template adjacent( + params, r, u, nullptr, nullptr, nullptr, &sc_stor, + sc_stor.order_.node2order_[via_idx], + [&](typename P::node const neighbor, std::uint32_t const cost, + distance_t, way_idx_t const way, std::uint16_t, std::uint16_t, + elevation_storage::elevation, bool, typename P::node const via_out, + cost_t const turning_cost) { + cost_t const way_cost = + cost - P::node_cost(r.node_properties_[neighbor.get_node()]) - + turning_cost; + for (auto& in : incoming) { + if (in.in_ == neighbor && in.way_ == way) { + if (in.way_cost_ <= way_cost) { + return; + } + in.via_ = via_out; + in.in_ = neighbor; + in.way_cost_ = way_cost; + return; + } + } + incoming.push_back(incoming_sc_candidate{ + via_out, neighbor, way, way_cost}); + }); + }); + return incoming; +} + +template +std::vector> find_outgoing( + typename P::parameters const& params, + ways::routing const& r, + shortcut_storage const& sc_stor, + typename P::node const via_node, + cost_t& max_cost, + cost_t& max_turning_cost) { + auto outgoing = std::vector>{}; + P::template adjacent( + params, r, via_node, nullptr, nullptr, nullptr, &sc_stor, + sc_stor.order_.node2order_[via_node.get_node()], + [&](typename P::node const neighbor, std::uint32_t const cost, distance_t, + way_idx_t const way, std::uint16_t, std::uint16_t, + elevation_storage::elevation, bool, typename P::node const via_out, + cost_t const turning_cost) { + cost_t const out_cost = + static_cast(cost) - + P::node_cost(r.node_properties_[neighbor.get_node()]); + outgoing.push_back(outgoing_sc_candidate{ + neighbor, via_out, way, + static_cast( + cost - P::node_cost(r.node_properties_[neighbor.get_node()])), + out_cost, turning_cost}); + if (cost > max_cost) { + max_cost = static_cast(cost); + } + max_turning_cost = std::max(max_turning_cost, turning_cost); + }); + return outgoing; +} + +template +void apply_outgoing_turn_replacement_difference( + typename P::parameters const& params, + ways::routing const& r, + shortcut_storage const& sc_stor, + node_idx_t const via_order, + typename P::node const in_node, + cost_t const cost_limit, + dijkstra

& d) { + P::template adjacent( + params, r, in_node, nullptr, nullptr, nullptr, &sc_stor, via_order, + [&](typename P::node const neighbor, std::uint32_t const cost, distance_t, + way_idx_t const way, std::uint16_t, std::uint16_t, + elevation_storage::elevation, bool, typename P::node const start_node, + cost_t) { + int32_t r_max = 0; + P::template adjacent( + params, r, start_node, nullptr, nullptr, nullptr, nullptr, + node_idx_t::invalid(), + [&](typename P::node, std::uint32_t, distance_t, way_idx_t, + std::uint16_t, std::uint16_t, elevation_storage::elevation, + bool, typename P::node const from_node, + cost_t const turning_cost) { + r_max = std::max( + r_max, static_cast(turning_cost) - + P::template turning_cost( + r, from_node, in_node)); + }); + r_max = std::min(r_max, static_cast(cost_limit)); + cost_t const total = r_max + cost; + if (total >= cost_limit) { + return; + } + d.cost_[neighbor.get_key()].update({neighbor, total}, neighbor, total, + P::node::invalid(), way); + d.pq_.push(typename P::label{neighbor, total}); + }); +} + +template +bool check_incoming_turn_replacement_difference( + typename P::parameters const& params, + ways::routing const& r, + outgoing_sc_candidate const& out, + dijkstra

const& d, + cost_t const sc_cost) { + bool add_sc = true; + P::template adjacent( + params, r, out.out_, nullptr, nullptr, nullptr, nullptr, + node_idx_t::invalid(), + [&](typename P::node, std::uint32_t, distance_t, way_idx_t, std::uint16_t, + std::uint16_t, elevation_storage::elevation, bool, + typename P::node const dest_node, cost_t) { + if (!add_sc) { + return; + } + int32_t r_max = 0; + P::template adjacent( + params, r, dest_node, nullptr, nullptr, nullptr, nullptr, + node_idx_t::invalid(), + [&](typename P::node, std::uint32_t, distance_t, way_idx_t, + std::uint16_t, std::uint16_t, elevation_storage::elevation, + bool, typename P::node const to_node, + cost_t const turning_cost) { + if (!add_sc) { + return; + } + r_max = std::max( + r_max, static_cast(turning_cost) - + P::template turning_cost( + r, out.out_, to_node)); + }); + add_sc = r_max + d.get_cost(dest_node) >= sc_cost; + }); + return add_sc; +} + +template +bool check_loop_avoidance(typename P::parameters const& params, + ways::routing const& r, + incoming_sc_candidate const& in, + outgoing_sc_candidate const& out, + cost_t const sc_cost) { + bool add_sc = false; + P::template adjacent( + params, r, in.in_, nullptr, nullptr, nullptr, nullptr, + node_idx_t::invalid(), + [&](typename P::node, std::uint32_t, distance_t, way_idx_t, std::uint16_t, + std::uint16_t, elevation_storage::elevation, bool, + typename P::node const from_node, cost_t const ct_from) { + if (add_sc) { + return; + } + P::template adjacent( + params, r, out.out_, nullptr, nullptr, nullptr, nullptr, + node_idx_t::invalid(), + [&](typename P::node, std::uint32_t, distance_t, way_idx_t, + std::uint16_t, std::uint16_t, elevation_storage::elevation, + bool, typename P::node const to_node, cost_t const ct_to) { + if (add_sc) { + return; + } + add_sc = ct_from + sc_cost + ct_to < + P::template turning_cost( + r, from_node, to_node); + }); + }); + return add_sc; +} + +template +bool check_path(incoming_sc_candidate const& in, + outgoing_sc_candidate& out, + dijkstra

const& d) { + auto node = out.out_; + auto path = std::vector>{}; + while (true) { + auto const& e = d.cost_.at(node.get_key()); + auto const pred = e.pred(node); + auto const cost = e.cost(node); + auto const way = e.way(node); + path.emplace_back(node, cost, way); + if (!pred.has_value()) { + break; + } + node = *pred; + } + // because the first round of dijkstra was skipped, + // a path does not contain the first node + // we have to add a shortcut iff the found path only consists of the exact + // incoming and outgoing edge and a loop inbetween (optional) + if (path.size() < 2 || path.back() != path_label{ + in.via_, in.way_cost_, in.way_}) { + return false; + } + if (path.size() > 2) { + auto const& l_begin = path.back(); + auto const& l_end = path[1]; + if (l_begin.node_.get_node() != l_end.node_.get_node()) { + return false; + } + out.turning_cost_ = l_end.cost_ - l_begin.cost_; + } else { + out.turning_cost_ = 0; + } + return true; +} + +template +void handle_outgoing_edge( + typename P::parameters const& params, + ways::routing const& r, + cost_t const via_cost, + incoming_sc_candidate const& in, + outgoing_sc_candidate& out, + dijkstra

const& d, + std::vector>& found_SCs) { + auto const sc_cost = d.get_cost(out.out_); + if (sc_cost == kInfeasible || + d.cost_.at(out.out_.get_key()).way(out.out_) != out.way_ || + out.turning_cost_ == 0 && + sc_cost < in.way_cost_ + via_cost + out.cost_ + + P::node_cost(r.node_properties_[out.out_.get_node()]) || + !check_incoming_turn_replacement_difference(params, r, out, d, sc_cost) || + in.in_.get_node() == out.out_.get_node() && + !check_loop_avoidance

(params, r, in, out, sc_cost) || + !check_path(in, out, d)) { + return; + } + found_SCs.push_back(found_shortcut{ + {in.in_, out.out_}, + {in.via_, out.via_, in.way_, out.way_, in.way_cost_, out.way_cost_, + out.turning_cost_}, + static_cast( + sc_cost - P::node_cost(r.node_properties_[out.out_.get_node()]))}); +} + +template +std::vector> handle_incoming_edge( + typename P::parameters const& params, + ways const& w, + ways::routing const& r, + shortcut_storage const& sc_stor, + node_idx_t const via_order, + cost_t const via_cost, + incoming_sc_candidate& in, + cost_t const detour_limit) { + cost_t max_cost = 0; + cost_t max_turning_cost = 0; + auto outgoing = + find_outgoing

(params, r, sc_stor, in.via_, max_cost, max_turning_cost); + if (max_turning_cost == kInfeasible) { + max_turning_cost = detour_limit; + } + + dijkstra

& d = get_dijkstra

(); + cost_t const cost_limit = + in.way_cost_ + via_cost + max_cost + max_turning_cost + 1; + d.reset(cost_limit); + + apply_outgoing_turn_replacement_difference(params, r, sc_stor, via_order, + in.in_, cost_limit, d); + d.template run( + params, w, r, cost_limit, nullptr, nullptr, nullptr, &sc_stor, via_order); + + auto found_SCs = std::vector>{}; + for (auto& out : outgoing) { + handle_outgoing_edge(params, r, via_cost, in, out, d, found_SCs); + } + return found_SCs; +} + +template +void contract_node(typename P::parameters const& params, + ways const& w, + ways::routing const& r, + shortcut_storage& sc_stor, + node_idx_t const via_idx, + cost_t const detour_limit) { + node_properties const via_props = r.node_properties_[via_idx]; + node_idx_t const via_order = sc_stor.order_.node2order_[via_idx]; + cost_t const via_cost = P::node_cost(via_props); + if (via_cost == kInfeasible) { + return; + } + for (auto& in : find_incoming

(params, r, sc_stor, via_idx)) { + auto new_idx = way_idx_t{w.n_ways() + sc_stor.shortcuts_.size()}; + for (auto const& [sc, scr, cost] : handle_incoming_edge

( + params, w, r, sc_stor, via_order, via_cost, in, detour_limit)) { + auto const i = sc.from_.get_node(); + auto const o = sc.to_.get_node(); + sc_stor.neighbors_fwd_[i].push_back({o, new_idx}); + sc_stor.neighbors_bwd_[o].push_back({i, new_idx}); + sc_stor.shortcuts_.emplace_back(sc); + sc_stor.shortcuts_reconstruct_.emplace_back(scr); + sc_stor.costs_.emplace_back(cost); + ++new_idx; + } + } +} + +template +void preprocess_ch(std::filesystem::path const& directory, + search_profile const profile, + typename P::parameters const& params, + auto const seed, + cost_t const detour_limit, + node_order::method const ordering_method) { + static constexpr auto kProgressUpdateInterval = + std::chrono::milliseconds(500); + auto const start = std::chrono::steady_clock::now(); + auto pt = utl::get_active_progress_tracker_or_activate("osr-preprocessing"); + + auto sc_stor = shortcut_storage{}; + auto& ordering = sc_stor.order_; + auto const w = ways{directory, cista::mmap::protection::READ}; + auto const& r = *w.r_; + + auto const num_nodes = w.n_nodes(); + auto num_edges = way_idx_t::value_t{0}; + auto const num_restricted = r.node_is_restricted_.count(); + for (auto way : r.way_nodes_) { + if (way.empty()) continue; + num_edges += way.size() - 1; + } + fmt::println( + "#nodes (#restricted) | #edges:\n" + "{} ({}) | {}\n", + num_nodes, num_restricted, num_edges); + + sc_stor.shortcuts_.reserve(2 * num_edges); + sc_stor.shortcuts_reconstruct_.reserve(2 * num_edges); + sc_stor.costs_.reserve(2 * num_edges); + sc_stor.neighbors_fwd_.resize(num_nodes); + sc_stor.neighbors_bwd_.resize(num_nodes); + + fmt::print("ordering nodes..."); + ordering.initialize(num_nodes); + ordering.shuffle_random(seed); + if (ordering_method == node_order::method::kRealImportance) { + ordering.by_real_world_importance(r); + } + fmt::println(" finished"); + + fmt::println("contracting nodes..."); + // offset the progress because the first ~40 % are contracted almost instantly + auto constexpr kOffsetFactor = 4U; + auto update_progress = [&](size_t const val) { + pt->status(std::to_string(val) + "/" + std::to_string(num_nodes)); + auto const offset = kOffsetFactor * num_nodes / 10; + pt->update(val > offset ? val - offset : 0); + }; + pt->in_high((10 - kOffsetFactor) * num_nodes / 10); + update_progress(0); + auto timer = std::chrono::steady_clock::now(); + for (auto const [order, via_idx] : utl::enumerate(ordering.order2node_)) { + contract_node

(params, w, r, sc_stor, via_idx, detour_limit); + if (std::chrono::steady_clock::now() - timer >= kProgressUpdateInterval) { + timer = std::chrono::steady_clock::now(); + update_progress(order); + } + } + update_progress(num_nodes); + + auto const stop = std::chrono::steady_clock::now(); + fmt::println("\nfinished with {} #shortcuts", sc_stor.shortcuts_.size()); + fmt::println("preprocessing took {:.0%-M:%S} (mm:ss)", stop - start); + + sc_stor.write(directory, profile); +} + +} // namespace osr diff --git a/include/osr/preprocessing/contraction_hierarchy/node_order.h b/include/osr/preprocessing/contraction_hierarchy/node_order.h new file mode 100644 index 00000000..002efffa --- /dev/null +++ b/include/osr/preprocessing/contraction_hierarchy/node_order.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#include "osr/types.h" +#include "osr/ways.h" + +namespace osr { + +struct node_order final { + enum method { kRandom, kRealImportance }; + + static method string2method(std::string const& str) { + if (str == "real") { + return kRealImportance; + } + return kRandom; + } + + cista::offset::vector_map node2order_; + cista::offset::vector_map order2node_; + + void initialize(auto const num_nodes) { + node2order_.reserve(num_nodes); + order2node_.reserve(num_nodes); + for (uint64_t i = 0; i < num_nodes; ++i) { + node2order_.emplace_back(i); + order2node_.emplace_back(i); + } + } + + void shuffle_random(uint_fast64_t const seed = 1337) { + std::ranges::shuffle(order2node_, std::mt19937_64{seed}); + update_node2order(); + } + + void by_real_world_importance(ways::routing const& r) { + std::ranges::sort(order2node_, std::ranges::less{}, [&](auto const n) { + return r.node_properties_[n].importance_; + }); + update_node2order(); + } + + void update_node2order() { + for (node_idx_t i{0}; i < node2order_.size(); ++i) { + node2order_[order2node_[i]] = i; + } + } + + [[nodiscard]] bool constexpr lower_than(node_idx_t const node, + node_idx_t const min_order) const { + return node2order_[node] < min_order; + } +}; + +} // namespace osr diff --git a/include/osr/preprocessing/contraction_hierarchy/shortcut_storage.h b/include/osr/preprocessing/contraction_hierarchy/shortcut_storage.h new file mode 100644 index 00000000..7ff049c5 --- /dev/null +++ b/include/osr/preprocessing/contraction_hierarchy/shortcut_storage.h @@ -0,0 +1,90 @@ +#pragma once + +#include + +#include "osr/preprocessing/contraction_hierarchy/node_order.h" +#include "osr/types.h" + +namespace osr { + +enum class search_profile : std::uint8_t; +std::string_view to_str(search_profile); + +template +struct shortcut_storage final { + struct shortcut final { + Node from_; + Node to_; + }; + struct shortcut_reconstruct final { + Node via_in_; + Node via_out_; + way_idx_t from_way_; + way_idx_t to_way_; + cost_t from_way_cost_; + cost_t to_way_cost_; + cost_t loop_cost_; + }; + struct neighbor final { + node_idx_t idx_; + way_idx_t sc_idx_; + }; + using data_t = cista::paged< + cista::offset::vector, + typename cista::offset::vector::size_type, + uint32_t, + std::max( + std::size_t{2U}, + cista::next_power_of_two( + sizeof(cista::page< + typename cista::offset::vector::size_type, + uint32_t>) / + sizeof(typename cista::offset::vector::value_type))), + (1U << 31U)>; + using idx_t = cista::offset::vector; + + static constexpr std::string_view kFilenamePrefix = "shortcuts_"; + + cista::offset::vector_map shortcuts_; + cista::offset::vector_map + shortcuts_reconstruct_; + cista::offset::vector_map costs_; + cista::paged_vecvec neighbors_fwd_; + cista::paged_vecvec neighbors_bwd_; + node_order order_; + + static constexpr std::string get_filename( + search_profile const profile) noexcept { + return (std::string(kFilenamePrefix) + std::string(to_str(profile)) + + ".bin"); + } + + static cista::wrapped read( + std::filesystem::path const& p, search_profile const profile) { + return cista::read(p / get_filename(profile)); + } + + void write(std::filesystem::path const& p, + search_profile const profile) const { + return cista::write(p / get_filename(profile), *this); + } +}; + +template +struct path_label final { + Node node_; + cost_t cost_; + way_idx_t way_; + bool operator==(path_label const&) const = default; + path_label() = default; + path_label(Node const n, cost_t const c, way_idx_t const w) + : node_{std::move(n)}, cost_{c}, way_{w} {} +}; + +template +cista::wrapped const>& get_shortcut_storage() { + static auto sc_stor = cista::wrapped const>{}; + return sc_stor; +} + +} // namespace osr diff --git a/include/osr/routing/algorithms.h b/include/osr/routing/algorithms.h index acc9a211..ee450862 100644 --- a/include/osr/routing/algorithms.h +++ b/include/osr/routing/algorithms.h @@ -5,7 +5,11 @@ namespace osr { -enum class routing_algorithm : std::uint8_t { kDijkstra, kAStarBi }; +enum class routing_algorithm : std::uint8_t { + kDijkstra, + kAStarBi, + kCHDijkstra +}; routing_algorithm to_algorithm(std::string_view); diff --git a/include/osr/routing/bidirectional.h b/include/osr/routing/bidirectional.h index 0ea2bbff..bb9c96d1 100644 --- a/include/osr/routing/bidirectional.h +++ b/include/osr/routing/bidirectional.h @@ -83,8 +83,8 @@ struct bidirectional { sharing_data const* sharing) { auto const heur = heuristic(params, w, l.n_, dir, sharing); if (l.cost() + heur < d.n_buckets() - 1 && - cost_map[l.get_node().get_key()].update(l, l.get_node(), l.cost(), - node::invalid())) { + cost_map[l.get_node().get_key()].update( + l, l.get_node(), l.cost(), node::invalid(), way_idx_t::invalid())) { auto const total = static_cast(l.cost() + heur); d.push(label{l.get_node(), total}); } @@ -162,7 +162,7 @@ struct bidirectional { return f_cost + b_cost; } - template + template bool run_single(P::parameters const& params, ways const& w, ways::routing const& r, @@ -187,11 +187,13 @@ struct bidirectional { std::cout << "\n"; } - P::template adjacent( - params, r, curr, blocked, sharing, elevations, + P::template adjacent( + params, r, curr, blocked, sharing, elevations, nullptr, + node_idx_t::invalid(), [&](node const neighbor, std::uint32_t const cost, distance_t, way_idx_t const way, std::uint16_t, std::uint16_t, - elevation_storage::elevation const, bool const track) { + elevation_storage::elevation const, bool const track, node, + cost_t) { if constexpr (kDebug) { std::cout << " NEIGHBOR "; neighbor.print(std::cout, w); @@ -207,9 +209,9 @@ struct bidirectional { } return; } - if (heur < max && - costs[neighbor.get_key()].update( - l, neighbor, static_cast(total), curr)) { + if (heur < max && costs[neighbor.get_key()].update( + l, neighbor, static_cast(total), curr, + way_idx_t::invalid())) { auto next = label{neighbor, static_cast(heur)}; next.track(l, r, way, neighbor.get_node(), track); @@ -262,11 +264,13 @@ struct bidirectional { if (!pred.has_value()) { return; } - P::template adjacent( - params, r, curr, blocked, sharing, elevations, + P::template adjacent( + params, r, curr, blocked, sharing, elevations, nullptr, + node_idx_t::invalid(), [&](node const neighbor, std::uint32_t const, distance_t, way_idx_t const, std::uint16_t, std::uint16_t, - elevation_storage::elevation const, bool const) { + elevation_storage::elevation const, bool const, node, + cost_t) { if (neighbor.get_key() != pred->get_key()) { return; } @@ -325,7 +329,7 @@ struct bidirectional { return true; } - template + template bool run(P::parameters const& params, ways const& w, ways::routing const& r, @@ -338,12 +342,12 @@ struct bidirectional { } while (!pq1_.empty() || !pq2_.empty()) { if (!pq1_.empty() && - !run_single( + !run_single( params, w, r, max, blocked, sharing, elevations, pq1_, cost1_)) { break; } if (!pq2_.empty() && - !run_single( + !run_single( params, w, r, max, blocked, sharing, elevations, pq2_, cost2_)) { break; } @@ -365,16 +369,16 @@ struct bidirectional { direction const dir) { if (blocked == nullptr) { return dir == direction::kForward - ? run(params, w, r, max, blocked, - sharing, elevations) - : run(params, w, r, max, blocked, - sharing, elevations); + ? run( + params, w, r, max, blocked, sharing, elevations) + : run( + params, w, r, max, blocked, sharing, elevations); } else { return dir == direction::kForward - ? run(params, w, r, max, blocked, - sharing, elevations) - : run(params, w, r, max, blocked, - sharing, elevations); + ? run( + params, w, r, max, blocked, sharing, elevations) + : run( + params, w, r, max, blocked, sharing, elevations); } } diff --git a/include/osr/routing/ch_dijkstra.h b/include/osr/routing/ch_dijkstra.h new file mode 100644 index 00000000..a2ae6db0 --- /dev/null +++ b/include/osr/routing/ch_dijkstra.h @@ -0,0 +1,268 @@ +#pragma once + +#include "osr/elevation_storage.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" +#include "osr/routing/dial.h" +#include "osr/routing/profile.h" +#include "osr/types.h" +#include "osr/ways.h" + +namespace osr { + +struct sharing_data; + +template +struct ch_dijkstra { + using profile_t = P; + using key = typename P::key; + using label = typename P::label; + using node = typename P::node; + using entry = typename P::entry; + using hash = typename P::hash; + using cost_map = ankerl::unordered_dense::map; + + constexpr static auto kDebug = false; + + struct get_bucket { + cost_t operator()(label const& l) { return l.cost(); } + }; + + void reset(cost_t const max) { + pq_s_.clear(); + pq_t_.clear(); + pq_s_.n_buckets(max + 1U); + pq_t_.n_buckets(max + 1U); + cost_s_.clear(); + cost_t_.clear(); + tentative_cost_ = kInfeasible; + tentative_mp_ = {node::invalid(), node::invalid()}; + settled_s_.clear(); + settled_t_.clear(); + max_reached_s_ = false; + max_reached_t_ = false; + } + + void reset_partially() { + pq_s_.clear(); + pq_t_.clear(); + cost_t_.clear(); + max_reached_t_ = false; + settled_t_.clear(); + } + + bool add(label const l, auto& c_map, auto& pq) { + if (c_map[l.get_node().get_key()].update( + l, l.get_node(), l.cost(), node::invalid(), way_idx_t::invalid())) { + pq.push(l); + return true; + } + return false; + } + + void add_start(ways const& w, label const l) { + if (add(l, cost_s_, pq_s_) && kDebug) { + std::cout << "ADD START "; + l.get_node().print(std::cout, w); + std::cout << "\n"; + } + } + + void add_end(ways const& w, label const l) { + if (add(l, cost_t_, pq_t_) && kDebug) { + std::cout << "ADD END "; + l.get_node().print(std::cout, w); + std::cout << "\n"; + } + } + + template + cost_t get_cost(node const n) { + if constexpr (SearchDir == direction::kForward) { + auto const it = cost_s_.find(n.get_key()); + return it != end(cost_s_) ? it->second.cost(n) : kInfeasible; + } else { + auto const it = cost_t_.find(n.get_key()); + return it != end(cost_t_) ? it->second.cost(n) : kInfeasible; + } + } + + template + void constexpr evaluate_meetpoint(cost_t const cost, + cost_t const other_cost, + node const meetpoint1, + node const meetpoint2, + ways const& w) { + if constexpr (kDebug) { + std::cout << " potential MEETPOINT found by start "; + meetpoint1.print(std::cout, w); + } + if (uint32_t const tentative = + static_cast(cost) + other_cost + + P::template turning_cost(*w.r_, meetpoint1, meetpoint2); + tentative < tentative_cost_) { + tentative_mp_ = {meetpoint1, meetpoint2}; + tentative_cost_ = static_cast(tentative); + + if constexpr (kDebug) { + std::cout << " with cost " << tentative_cost_ << " -> ACCEPTED\n"; + } + } else if constexpr (kDebug) { + std::cout << " -> DOMINATED\n"; + } + } + + template + node run_single(P::parameters const& params, + ways const& w, + ways::routing const& r, + cost_t const max, + sharing_data const* sharing, + elevation_storage const* elevations, + shortcut_storage const& sc_stor, + dial& pq, + cost_map& costs) { + auto l = pq.pop(); + auto const curr = l.get_node(); + auto const it = costs.find(curr.get_key()); + auto const curr_cost = + it != end(costs) ? it->second.cost(curr) : kInfeasible; + if (curr_cost < l.cost()) { + return node::invalid(); + } + if constexpr (kDebug) { + std::cout << "EXTRACT "; + l.get_node().print(std::cout, w); + std::cout << "\n"; + } + + P::template adjacent( + params, r, curr, nullptr, sharing, elevations, &sc_stor, + sc_stor.order_.node2order_[curr.get_node()], + [&](node const neighbor, std::uint32_t const cost, distance_t, + way_idx_t const way, std::uint16_t, std::uint16_t, + elevation_storage::elevation, bool const track, node, cost_t) { + if constexpr (kDebug) { + std::cout << " NEIGHBOR "; + neighbor.print(std::cout, w); + } + + auto const total = l.cost() + cost; + if (total >= max) { + if (SearchDir == direction::kForward) { + max_reached_s_ = true; + } else { + max_reached_t_ = true; + } + return; + } + if (total < max && + costs[neighbor.get_key()].update( + l, neighbor, static_cast(total), curr, way)) { + auto next = label{neighbor, static_cast(total)}; + next.track(l, r, way, neighbor.get_node(), track); + pq.push(std::move(next)); + + if constexpr (kDebug) { + std::cout << " -> PUSH\n"; + } + } else { + if constexpr (kDebug) { + std::cout << " -> DOMINATED\n"; + } + } + }); + return curr; + } + + template + bool run(P::parameters const& params, + ways const& w, + ways::routing const& r, + cost_t const max, + sharing_data const* sharing, + elevation_storage const* elevations, + shortcut_storage const& sc_stor) { + settled_s_.resize(w.r_->node_ways_.size()); + settled_t_.resize(w.r_->node_ways_.size()); + while (!pq_s_.empty() || !pq_t_.empty()) { + auto const min_pq_s = + pq_s_.empty() ? kInfeasible : pq_s_.get_next_bucket(); + auto const min_pq_t = + pq_t_.empty() ? kInfeasible : pq_t_.get_next_bucket(); + bool const use_pq_s = min_pq_s <= min_pq_t; + node curr; + if (use_pq_s) { + curr = run_single( + params, w, r, max, sharing, elevations, sc_stor, pq_s_, cost_s_); + } else { + curr = run_single( + params, w, r, max, sharing, elevations, sc_stor, pq_t_, cost_t_); + } + if (curr == node::invalid()) { + continue; + } + auto& cur_pq = use_pq_s ? pq_s_ : pq_t_; + auto& cur_cost = use_pq_s ? cost_s_ : cost_t_; + auto const& other_cost = use_pq_s ? cost_t_ : cost_s_; + auto& cur_settled = use_pq_s ? settled_s_ : settled_t_; + auto const& other_settled = use_pq_s ? settled_t_ : settled_s_; + + cur_settled[curr.get_node().v_] = true; + if (other_settled[curr.get_node().v_]) { + other_cost.at(curr.get_key()) + .for_each(curr.get_key(), [&](node const node, cost_t const cost) { + if (use_pq_s) { + evaluate_meetpoint( + cur_cost[curr.get_key()].cost(curr), cost, curr, node, w); + } else { + evaluate_meetpoint( + cost, cur_cost[curr.get_key()].cost(curr), node, curr, w); + } + }); + } + + if (tentative_cost_ != kInfeasible && !cur_pq.empty() && + cur_pq.get_next_bucket() > tentative_cost_) { + if (kDebug) { + std::cout << "stopping criterion met " << cur_pq.get_next_bucket() + << " " << tentative_cost_ << std::endl; + } + cur_pq.clear(); + } + } + if (tentative_cost_ != kInfeasible && tentative_cost_ >= max) { + tentative_cost_ = kInfeasible; + tentative_mp_ = {node::invalid(), node::invalid()}; + return false; + } + return !max_reached_s_ || !max_reached_t_; + } + + bool run(P::parameters const& params, + ways const& w, + ways::routing const& r, + cost_t const max, + sharing_data const* sharing, + elevation_storage const* elevations, + shortcut_storage const& sc_stor, + direction const dir) { + return dir == direction::kForward + ? run( + params, w, r, max, sharing, elevations, sc_stor) + : run( + params, w, r, max, sharing, elevations, sc_stor); + } + + dial pq_s_{get_bucket{}}; + dial pq_t_{get_bucket{}}; + cost_map cost_s_; + cost_map cost_t_; + std::vector settled_s_; + std::vector settled_t_; + cost_t tentative_cost_{kInfeasible}; + std::pair tentative_mp_; + bool max_reached_s_ = false; + bool max_reached_t_ = false; +}; + +} // namespace osr diff --git a/include/osr/routing/dijkstra.h b/include/osr/routing/dijkstra.h index fd97d954..7a095e16 100644 --- a/include/osr/routing/dijkstra.h +++ b/include/osr/routing/dijkstra.h @@ -1,6 +1,7 @@ #pragma once #include "osr/elevation_storage.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" #include "osr/routing/additional_edge.h" #include "osr/routing/dial.h" #include "osr/routing/profile.h" @@ -34,8 +35,8 @@ struct dijkstra { } void add_start(ways const& w, label const l) { - if (cost_[l.get_node().get_key()].update(l, l.get_node(), l.cost(), - node::invalid())) { + if (cost_[l.get_node().get_key()].update( + l, l.get_node(), l.cost(), node::invalid(), way_idx_t::invalid())) { if constexpr (kDebug) { std::cout << "START "; l.get_node().print(std::cout, w); @@ -50,14 +51,16 @@ struct dijkstra { return it != end(cost_) ? it->second.cost(n) : kInfeasible; } - template + template bool run(P::parameters const& params, ways const& w, ways::routing const& r, cost_t const max, bitvec const* blocked, sharing_data const* sharing, - elevation_storage const* elevations) { + elevation_storage const* elevations, + shortcut_storage const* const sc_stor, + node_idx_t const min_order) { while (!pq_.empty()) { auto l = pq_.pop(); if (get_cost(l.get_node()) < l.cost()) { @@ -71,11 +74,11 @@ struct dijkstra { } auto const curr = l.get_node(); - P::template adjacent( - params, r, curr, blocked, sharing, elevations, + P::template adjacent( + params, r, curr, blocked, sharing, elevations, sc_stor, min_order, [&](node const neighbor, std::uint32_t const cost, distance_t, way_idx_t const way, std::uint16_t, std::uint16_t, - elevation_storage::elevation, bool const track) { + elevation_storage::elevation, bool const track, node, cost_t) { if constexpr (kDebug) { std::cout << " NEIGHBOR "; neighbor.print(std::cout, w); @@ -88,7 +91,7 @@ struct dijkstra { } if (total < max && cost_[neighbor.get_key()].update( - l, neighbor, static_cast(total), curr)) { + l, neighbor, static_cast(total), curr, way)) { auto next = label{neighbor, static_cast(total)}; next.track(l, r, way, neighbor.get_node(), track); pq_.push(std::move(next)); @@ -116,16 +119,20 @@ struct dijkstra { direction const dir) { if (blocked == nullptr) { return dir == direction::kForward - ? run(params, w, r, max, blocked, - sharing, elevations) - : run(params, w, r, max, blocked, - sharing, elevations); + ? run( + params, w, r, max, blocked, sharing, elevations, nullptr, + node_idx_t::invalid()) + : run( + params, w, r, max, blocked, sharing, elevations, nullptr, + node_idx_t::invalid()); } else { return dir == direction::kForward - ? run(params, w, r, max, blocked, - sharing, elevations) - : run(params, w, r, max, blocked, - sharing, elevations); + ? run( + params, w, r, max, blocked, sharing, elevations, nullptr, + node_idx_t::invalid()) + : run( + params, w, r, max, blocked, sharing, elevations, nullptr, + node_idx_t::invalid()); } } diff --git a/include/osr/routing/profile.h b/include/osr/routing/profile.h index d987fde2..3369dd66 100644 --- a/include/osr/routing/profile.h +++ b/include/osr/routing/profile.h @@ -8,6 +8,7 @@ #include #include "osr/elevation_storage.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" #include "osr/routing/mode.h" #include "osr/routing/path.h" #include "osr/routing/sharing_data.h" @@ -50,18 +51,26 @@ concept IsLabel = { label.track(l, r, w, n, std::declval()) } -> std::same_as; }; -template +template concept IsEntry = requires(Entry const& entry, Node const node, path& p) { { entry.pred(node) } -> std::same_as>; + { entry.way(node) } -> std::same_as; { entry.cost(node) } -> std::same_as; { entry.write(node, p) } -> std::same_as; - } && requires(Entry entry, - Node const node, - Label const& label, - cost_t const cost, - path& p) { - { entry.update(label, node, cost, node) } -> std::same_as; + } && + requires(Entry entry, + Node const node, + Label const& label, + cost_t const cost, + way_idx_t const way, + path& p) { + { entry.update(label, node, cost, node, way) } -> std::same_as; + } && + requires(Entry entry, + NodeKey key, + std::function fn) { + { entry.for_each(key, fn) } -> std::same_as; }; template @@ -74,7 +83,10 @@ concept Profile = IsParameters && IsNode && IsLabel && - IsEntry && + IsEntry && IsHash && requires(typename P::node const node, ways::routing const& r, @@ -112,6 +124,8 @@ concept Profile = bitvec const* blocked, sharing_data const* sharing, elevation_storage const* elevation, + shortcut_storage const* sc_stor, + node_idx_t min_order, std::function f) { + bool const, + typename P::node const, + cost_t)> f) { { - P::template adjacent(params, r, n, blocked, - sharing, elevation, f) + P::template adjacent( + params, r, n, blocked, sharing, elevation, sc_stor, min_order, f) } -> std::same_as; }; diff --git a/include/osr/routing/profiles/bike.h b/include/osr/routing/profiles/bike.h index 7db21d03..fd51880e 100644 --- a/include/osr/routing/profiles/bike.h +++ b/include/osr/routing/profiles/bike.h @@ -1,6 +1,7 @@ #pragma once #include "osr/elevation_storage.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" #include "osr/routing/mode.h" #include "osr/routing/path.h" #include "osr/types.h" @@ -94,6 +95,10 @@ struct bike { : std::optional{node{pred_[idx], pred_dir_[idx]}}; } + constexpr way_idx_t way(node) const noexcept { + return way_idx_t::invalid(); + } + constexpr cost_t cost(node const n) const noexcept { return cost_[get_index(n)]; } @@ -101,7 +106,8 @@ struct bike { constexpr bool update(label const&, node const n, cost_t const c, - node const pred) noexcept { + node const pred, + way_idx_t) noexcept { auto const idx = get_index(n); if (c < cost_[idx]) { cost_[idx] = c; @@ -123,6 +129,9 @@ struct bike { void write(node, path&) const {} + template + constexpr void for_each(key, Fn&&) const {} + std::array pred_; std::array pred_dir_; std::array cost_; @@ -157,13 +166,15 @@ struct bike { return way_cost(params, w.way_properties_[way], way_dir, 0U) != kInfeasible; } - template + template static void adjacent(parameters const& params, ways::routing const& w, node const n, bitvec const* blocked, sharing_data const*, elevation_storage const* elevations, + shortcut_storage const*, + node_idx_t, Fn&& fn) { for (auto const [way, i] : utl::zip_unchecked(w.node_ways_[n.n_], w.node_in_way_idx_[n.n_])) { @@ -171,7 +182,7 @@ struct bike { std::uint16_t const to) { // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) auto const target_node = w.way_nodes_[way][to]; - if constexpr (WithBlocked) { + if constexpr (is_enabled(Config, adj_conf::kBlocked)) { if (blocked->test(target_node)) { return; } @@ -207,7 +218,7 @@ struct bike { auto const cost = way_cost(params, target_way_prop, way_dir, dist) + node_cost(target_node_prop) + elevation_cost; fn(node{target_node, way_dir}, static_cast(cost), dist, - way, from, to, elevation, false); + way, from, to, elevation, false, node::invalid(), 0); }; if (i != 0U) { @@ -248,6 +259,13 @@ struct bike { static constexpr node get_reverse(node const n) { return {n, opposite(n.dir_)}; } + + template + static constexpr cost_t turning_cost(ways::routing const&, + node const, + node const) { + return 0; + } }; } // namespace osr diff --git a/include/osr/routing/profiles/bike_sharing.h b/include/osr/routing/profiles/bike_sharing.h index 778e3ba2..05ae371f 100644 --- a/include/osr/routing/profiles/bike_sharing.h +++ b/include/osr/routing/profiles/bike_sharing.h @@ -10,6 +10,7 @@ #include "utl/helpers/algorithm.h" #include "osr/elevation_storage.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" #include "osr/routing/additional_edge.h" #include "osr/routing/mode.h" #include "osr/routing/path.h" @@ -47,7 +48,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 +60,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, @@ -196,6 +199,10 @@ struct bike_sharing { .lvl_ = pred_lvl_[idx]}}; } + constexpr way_idx_t way(node) const noexcept { + return way_idx_t::invalid(); + } + constexpr cost_t cost(node const n) const noexcept { return cost_[get_index(n)]; } @@ -203,7 +210,8 @@ struct bike_sharing { constexpr bool update(label const, node const n, cost_t const c, - node const pred) noexcept { + node const pred, + way_idx_t) noexcept { auto const idx = get_index(n); if (c < cost_[idx]) { cost_[idx] = c; @@ -221,6 +229,9 @@ struct bike_sharing { void write(node, path&) const {} + template + constexpr void for_each(key, Fn&&) const {} + std::array pred_{}; std::array cost_{}; std::array pred_lvl_{}; @@ -270,13 +281,15 @@ struct bike_sharing { }); } - template + template static void adjacent(parameters const& params, ways::routing const& w, node const n, bitvec const* blocked, sharing_data const* sharing, elevation_storage const* elevations, + shortcut_storage const*, + node_idx_t, Fn&& fn) { assert(sharing != nullptr); @@ -286,21 +299,23 @@ struct bike_sharing { .type_ = nt, .lvl_ = nt == node_type::kBike ? kNoLevel : n.lvl_}, cost, ae.distance_, way_idx_t::invalid(), 0, 1, - elevation_storage::elevation{}, false); + elevation_storage::elevation{}, false, node::invalid(), 0); }; auto const& continue_on_foot = [&](node_type const nt, bool const include_additional_edges, cost_t const switch_penalty = 0) { - footp::template adjacent( - params.foot_, w, to_foot(n), blocked, nullptr, elevations, + footp::template adjacent( + params.foot_, w, to_foot(n), blocked, nullptr, elevations, nullptr, + node_idx_t::invalid(), [&](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, - elevation_storage::elevation const elevation, bool) { + elevation_storage::elevation const elevation, bool, footp::node, + cost_t) { if (is_allowed(sharing->through_allowed_, neighbor.n_)) { fn(to_node(neighbor, nt), cost + switch_penalty, dist, way, from, - to, elevation, false); + to, elevation, false, node::invalid(), 0); } }); if (include_additional_edges) { @@ -320,15 +335,17 @@ struct bike_sharing { auto const& continue_on_bike = [&](bool const include_additional_edges, cost_t const switch_penalty = 0) { - bikep::adjacent( - params.bike_, w, to_bike(n), blocked, nullptr, elevations, + bikep::adjacent( + params.bike_, w, to_bike(n), blocked, nullptr, elevations, nullptr, + node_idx_t::invalid(), [&](bikep::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, - elevation_storage::elevation const elevation, bool) { + elevation_storage::elevation const elevation, bool, bikep::node, + cost_t) { if (is_allowed(sharing->through_allowed_, neighbor.n_)) { fn(to_node(neighbor, kNoLevel), cost + switch_penalty, dist, way, - from, to, elevation, false); + from, to, elevation, false, node::invalid(), 0); } }); if (include_additional_edges) { @@ -452,6 +469,13 @@ struct bike_sharing { } static constexpr node get_reverse(node const n) { return n; } + + template + static constexpr cost_t turning_cost(ways::routing const&, + node const, + node const) { + return 0; + } }; } // namespace osr diff --git a/include/osr/routing/profiles/car.h b/include/osr/routing/profiles/car.h index 05ca4c8c..f98c4047 100644 --- a/include/osr/routing/profiles/car.h +++ b/include/osr/routing/profiles/car.h @@ -7,6 +7,7 @@ #include "utl/helpers/algorithm.h" #include "osr/elevation_storage.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" #include "osr/routing/mode.h" #include "osr/routing/path.h" #include "osr/ways.h" @@ -73,7 +74,10 @@ struct car { static constexpr auto const kMaxWays = way_pos_t{16U}; static constexpr auto const kN = kMaxWays * 2U /* FWD+BWD */; - entry() { utl::fill(cost_, kInfeasible); } + entry() { + utl::fill(cost_, kInfeasible); + utl::fill(way_, way_idx_t::invalid()); + } constexpr std::optional pred(node const n) const noexcept { auto const idx = get_index(n); @@ -83,6 +87,10 @@ struct car { to_dir(pred_dir_[idx])}}; } + constexpr way_idx_t way(node const n) const noexcept { + return way_[get_index(n)]; + } + constexpr cost_t cost(node const n) const noexcept { return cost_[get_index(n)]; } @@ -90,13 +98,14 @@ struct car { 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]) { + node const pred, + way_idx_t const w = way_idx_t{0}) noexcept { + if (auto const idx = get_index(n); c < cost_[idx]) { cost_[idx] = c; pred_[idx] = pred.n_; pred_way_[idx] = pred.way_; pred_dir_[idx] = to_bool(pred.dir_); + way_[idx] = w; return true; } return false; @@ -122,9 +131,17 @@ struct car { return d == direction::kForward ? false : true; } + template + constexpr void for_each(key const k, Fn&& fn) const { + for (size_t i = 0; i < cost_.size(); ++i) { + fn(get_node(k, i), cost_[i]); + } + } + std::array pred_; std::array pred_way_; std::bitset pred_dir_; + std::array way_; std::array cost_; }; @@ -164,14 +181,20 @@ struct car { } } - template + template static void adjacent(parameters const& params, ways::routing const& w, node const n, bitvec const* blocked, sharing_data const*, elevation_storage const*, + shortcut_storage const* const sc_stor, + node_idx_t const min_order, Fn&& fn) { + static_assert( + is_disabled(Config, adj_conf::kBlocked) || + is_disabled(Config, adj_conf::kUseCH), + "Blocking nodes not supported when using contraction hierarchies"); auto way_pos = way_pos_t{0U}; for (auto const [way, i] : utl::zip_unchecked(w.node_ways_[n.n_], w.node_in_way_idx_[n.n_])) { @@ -179,12 +202,18 @@ struct car { std::uint16_t const to) { // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) auto const target_node = w.way_nodes_[way][to]; - if constexpr (WithBlocked) { + if constexpr (is_enabled(Config, adj_conf::kBlocked)) { if (blocked->test(target_node)) { return; } } + if constexpr (is_enabled(Config, adj_conf::kUseCH)) { + if (sc_stor->order_.lower_than(target_node, min_order)) { + return; + } + } + auto const target_node_prop = w.node_properties_[target_node]; if (node_cost(target_node_prop) == kInfeasible) { return; @@ -195,19 +224,23 @@ struct car { return; } - if (w.is_restricted(n.n_, n.way_, way_pos)) { + auto const turning_cost = + car::turning_cost(w, n, node{n.n_, way_pos, way_dir}); + if (is_disabled(Config, adj_conf::kNoOmitRestricted) && + turning_cost == kInfeasible) { return; } - auto const is_u_turn = way_pos == n.way_ && way_dir == opposite(n.dir_); auto const dist = w.way_node_dist_[way][std::min(from, to)]; auto const target = node{target_node, w.get_way_pos(target_node, way, to), way_dir}; - auto const cost = way_cost(params, target_way_prop, way_dir, dist) + - node_cost(target_node_prop) + - (is_u_turn ? kUturnPenalty : 0U); + auto const cost = + way_cost(params, target_way_prop, way_dir, dist) + + node_cost(target_node_prop) + + (is_disabled(Config, adj_conf::kNoOmitRestricted) ? turning_cost + : 0U); fn(target, cost, dist, way, from, to, elevation_storage::elevation{}, - false); + false, node{n.n_, way_pos, way_dir}, turning_cost); }; if (i != 0U) { @@ -219,6 +252,51 @@ struct car { ++way_pos; } + + if constexpr (is_disabled(Config, adj_conf::kUseCH)) { + return; + } + + auto const nb = SearchDir == direction::kForward + ? sc_stor->neighbors_fwd_[n.n_] + : sc_stor->neighbors_bwd_[n.n_]; + if (nb.empty()) { + return; + } + for (auto const [nb_idx, sc_idx] : nb) { + if constexpr (is_enabled(Config, adj_conf::kUseCH)) { + if (sc_stor->order_.lower_than(nb_idx, min_order)) { + continue; + } + } + + auto const target_node_prop = w.node_properties_[nb_idx]; + + auto const vrt_idx = sc_idx - w.way_nodes_.size(); + auto const sc = sc_stor->shortcuts_[vrt_idx]; + bool constexpr is_fwd = SearchDir == direction::kForward; + + auto const pos_to = is_fwd ? sc.from_.way_ : sc.to_.way_; + auto const dir_to = is_fwd ? sc.from_.dir_ : sc.to_.dir_; + + auto const turning_cost = + car::turning_cost(w, n, node{n.n_, pos_to, dir_to}); + if (is_disabled(Config, adj_conf::kNoOmitRestricted) && + turning_cost == kInfeasible) { + continue; + } + + auto const target = is_fwd ? node{nb_idx, sc.to_.way_, sc.to_.dir_} + : node{nb_idx, sc.from_.way_, sc.from_.dir_}; + auto const w_cost = sc_stor->costs_[vrt_idx]; + auto const cost = + w_cost + node_cost(target_node_prop) + + (is_disabled(Config, adj_conf::kNoOmitRestricted) ? turning_cost + : 0U); + fn(target, cost, 0, sc_idx, !is_fwd, is_fwd, + elevation_storage::elevation{}, false, node{n.n_, pos_to, dir_to}, + turning_cost); + } } static bool is_dest_reachable(parameters const& params, @@ -262,6 +340,18 @@ struct car { static constexpr node get_reverse(node const n) { return {n.n_, n.way_, opposite(n.dir_)}; } + + template + static constexpr cost_t turning_cost(ways::routing const& w, + node const from, + node const to) { + if (w.is_restricted(from.n_, from.way_, to.way_)) { + return kInfeasible; + } + auto const is_u_turn = + to.way_ == from.way_ && to.dir_ == opposite(from.dir_); + return is_u_turn ? kUturnPenalty : 0; + } }; } // namespace osr diff --git a/include/osr/routing/profiles/car_parking.h b/include/osr/routing/profiles/car_parking.h index f7431456..b068ff3d 100644 --- a/include/osr/routing/profiles/car_parking.h +++ b/include/osr/routing/profiles/car_parking.h @@ -7,6 +7,7 @@ #include "utl/helpers/algorithm.h" #include "osr/elevation_storage.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" #include "osr/routing/mode.h" #include "osr/routing/path.h" #include "osr/routing/profiles/car.h" @@ -142,6 +143,10 @@ struct car_parking { .way_ = pred_way_[idx]}}; } + constexpr way_idx_t way(node) const noexcept { + return way_idx_t::invalid(); + } + constexpr cost_t cost(node const n) const noexcept { return cost_[get_index(n)]; } @@ -149,7 +154,8 @@ struct car_parking { constexpr bool update(label const, node const n, cost_t const c, - node const pred) noexcept { + node const pred, + way_idx_t) noexcept { auto const idx = get_index(n); if (c < cost_[idx]) { cost_[idx] = c; @@ -188,6 +194,9 @@ struct car_parking { void write(node, path&) const {} + template + constexpr void for_each(key, Fn&&) const {} + std::array pred_; std::array cost_; std::array pred_way_; @@ -243,13 +252,15 @@ struct car_parking { }); } - template + template static void adjacent(parameters const& params, ways::routing const& w, node const n, bitvec const* blocked, sharing_data const*, elevation_storage const* elevations, + shortcut_storage const*, + node_idx_t, Fn&& fn) { static constexpr auto const kFwd = SearchDir == direction::kForward; static constexpr auto const kBwd = SearchDir == direction::kBackward; @@ -261,29 +272,33 @@ struct car_parking { }); if (n.is_foot_node() || (kFwd && n.is_car_node() && is_parking)) { - footp::template adjacent( - params.foot_, w, to_foot(n), blocked, nullptr, elevations, + footp::template adjacent( + params.foot_, w, to_foot(n), blocked, nullptr, elevations, nullptr, + node_idx_t::invalid(), [&](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, - elevation_storage::elevation const elevation, bool) { + elevation_storage::elevation const elevation, bool, footp::node, + cost_t) { fn(to_node(neighbor), cost + (n.is_foot_node() ? 0 : kSwitchPenalty), dist, way, from, - to, elevation, false); + to, elevation, false, node::invalid(), 0); }); } if (n.is_car_node() || (kBwd && n.is_foot_node() && is_parking)) { - car::template adjacent( - params.car_, w, to_car(n), blocked, nullptr, elevations, + car::template adjacent( + params.car_, w, to_car(n), blocked, nullptr, elevations, nullptr, + node_idx_t::invalid(), [&](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, - elevation_storage::elevation const elevation, bool) { + elevation_storage::elevation const elevation, bool, car::node, + cost_t) { auto const way_prop = w.way_properties_[way]; fn(to_node(neighbor, way_prop.from_level()), cost + (n.is_car_node() ? 0 : kSwitchPenalty), dist, way, from, - to, elevation, false); + to, elevation, false, node::invalid(), 0); }); } } @@ -343,6 +358,13 @@ struct car_parking { static constexpr node get_reverse(node n) { return {n.n_, n.type_, n.lvl_, opposite(n.dir_), n.way_}; } + + template + static constexpr cost_t turning_cost(ways::routing const&, + node const, + node const) { + return 0; + } }; } // namespace osr diff --git a/include/osr/routing/profiles/car_sharing.h b/include/osr/routing/profiles/car_sharing.h index 636a4377..500c9221 100644 --- a/include/osr/routing/profiles/car_sharing.h +++ b/include/osr/routing/profiles/car_sharing.h @@ -10,6 +10,7 @@ #include "utl/helpers/algorithm.h" #include "osr/elevation_storage.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" #include "osr/routing/additional_edge.h" #include "osr/routing/mode.h" #include "osr/routing/path.h" @@ -47,7 +48,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 +60,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, @@ -223,6 +226,10 @@ struct car_sharing { .way_ = pred_way_[idx]}}; } + constexpr way_idx_t way(node) const noexcept { + return way_idx_t::invalid(); + } + constexpr cost_t cost(node const n) const noexcept { return cost_[get_index(n)]; } @@ -230,7 +237,8 @@ struct car_sharing { constexpr bool update(label const& l, node const n, cost_t const c, - node const pred) noexcept { + node const pred, + way_idx_t) noexcept { auto const idx = get_index(n); if (c < cost_[idx]) { cost_[idx] = c; @@ -266,6 +274,9 @@ struct car_sharing { tracking_[get_index(n)].write(p); } + template + constexpr void for_each(key, Fn&&) const {} + std::array pred_{}; std::array cost_{}; std::array pred_lvl_{}; @@ -322,13 +333,15 @@ struct car_sharing { }); } - template + template static void adjacent(parameters const& params, ways::routing const& w, node const n, bitvec const* blocked, sharing_data const* sharing, elevation_storage const* elevations, + shortcut_storage const*, + node_idx_t, Fn&& fn) { assert(sharing != nullptr); @@ -338,21 +351,23 @@ struct car_sharing { .type_ = nt, .lvl_ = nt == node_type::kRental ? kNoLevel : n.lvl_}, cost, ae.distance_, way_idx_t::invalid(), 0, 1, - elevation_storage::elevation{}, false); + elevation_storage::elevation{}, false, node::invalid(), 0); }; auto const& continue_on_foot = [&](node_type const nt, bool const include_additional_edges, cost_t const switch_penalty = 0) { - footp::template adjacent( - params.foot_, w, to_foot(n), blocked, nullptr, elevations, + footp::template adjacent( + params.foot_, w, to_foot(n), blocked, nullptr, elevations, nullptr, + node_idx_t::invalid(), [&](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, - elevation_storage::elevation const elevation, bool) { + elevation_storage::elevation const elevation, bool, footp::node, + cost_t) { if (is_allowed(sharing->through_allowed_, neighbor.n_)) { fn(to_node(neighbor, nt), cost + switch_penalty, dist, way, from, - to, elevation, switch_penalty != 0); + to, elevation, switch_penalty != 0, node::invalid(), 0); } }); if (include_additional_edges) { @@ -372,15 +387,17 @@ struct car_sharing { auto const& continue_with_vehicle = [&](bool const include_additional_edges, cost_t const switch_penalty = 0) { - car::adjacent( - params.car_, w, to_rental(n), blocked, nullptr, elevations, + car::adjacent( + params.car_, w, to_rental(n), blocked, nullptr, elevations, nullptr, + node_idx_t::invalid(), [&](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, - elevation_storage::elevation const elevation, bool) { + elevation_storage::elevation const elevation, bool, car::node, + cost_t) { if (is_allowed(sharing->through_allowed_, neighbor.n_)) { fn(to_node(neighbor, kNoLevel), cost + switch_penalty, dist, way, - from, to, elevation, false); + from, to, elevation, false, node::invalid(), 0); } }); if (include_additional_edges) { @@ -519,6 +536,13 @@ struct car_sharing { .dir_ = opposite(n.dir_), .way_ = n.way_}; } + + template + static constexpr cost_t turning_cost(ways::routing const&, + node const, + node const) { + return 0; + } }; } // namespace osr diff --git a/include/osr/routing/profiles/foot.h b/include/osr/routing/profiles/foot.h index 0a51458c..d04db49c 100644 --- a/include/osr/routing/profiles/foot.h +++ b/include/osr/routing/profiles/foot.h @@ -3,6 +3,7 @@ #include "utl/for_each_bit_set.h" #include "osr/elevation_storage.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" #include "osr/routing/mode.h" #include "osr/routing/path.h" #include "osr/routing/tracking.h" @@ -77,11 +78,15 @@ struct foot { ? std::nullopt : std::optional{node{pred_, pred_lvl_}}; } + constexpr way_idx_t way(node) const noexcept { + return way_idx_t::invalid(); + } constexpr cost_t cost(node) const noexcept { return cost_; } constexpr bool update(label const& l, node, cost_t const c, - node const pred) noexcept { + node const pred, + way_idx_t) noexcept { if (c < cost_) { tracking_ = l.tracking_; cost_ = c; @@ -94,6 +99,9 @@ struct foot { void write(node, path& p) const { tracking_.write(p); } + template + constexpr void for_each(key, Fn&&) const {} + node_idx_t pred_{node_idx_t::invalid()}; cost_t cost_{kInfeasible}; level_t pred_lvl_; @@ -153,13 +161,15 @@ struct foot { } } - template + template static void adjacent(parameters const& params, ways::routing const& w, node const n, bitvec const* blocked, sharing_data const*, elevation_storage const*, + shortcut_storage const*, + node_idx_t, Fn&& fn) { for (auto const [way, i] : utl::zip_unchecked(w.node_ways_[n.n_], w.node_in_way_idx_[n.n_])) { @@ -167,7 +177,7 @@ struct foot { std::uint16_t const to) { // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) auto const target_node = w.way_nodes_[way][to]; - if constexpr (WithBlocked) { + if constexpr (is_enabled(Config, adj_conf::kBlocked)) { if (blocked->test(target_node)) { return; } @@ -192,7 +202,7 @@ struct foot { node_cost(target_node_prop); fn(node{target_node, target_lvl}, static_cast(cost), dist, way, from, to, - elevation_storage::elevation{}, false); + elevation_storage::elevation{}, false, node::invalid(), 0); }); } else { auto const target_lvl = get_target_level(w, n.n_, n.lvl_, way); @@ -204,7 +214,8 @@ struct foot { auto const cost = way_cost(params, target_way_prop, way_dir, dist) + node_cost(target_node_prop); fn(node{target_node, *target_lvl}, static_cast(cost), - dist, way, from, to, elevation_storage::elevation{}, false); + dist, way, from, to, elevation_storage::elevation{}, false, + node::invalid(), 0); } }; @@ -347,6 +358,13 @@ struct foot { } static constexpr node get_reverse(node const n) { return n; } + + template + static constexpr cost_t turning_cost(ways::routing const&, + node const, + node const) { + return 0; + } }; } // namespace osr diff --git a/include/osr/routing/route.h b/include/osr/routing/route.h index 4583df94..0aaa9400 100644 --- a/include/osr/routing/route.h +++ b/include/osr/routing/route.h @@ -25,6 +25,9 @@ struct dijkstra; template struct bidirectional; +template +struct ch_dijkstra; + struct sharing_data; template @@ -33,6 +36,9 @@ bidirectional

& get_bidirectional(); template dijkstra

& get_dijkstra(); +template +ch_dijkstra

& get_ch_dijkstra(); + std::vector> route( profile_parameters const&, ways const&, diff --git a/include/osr/types.h b/include/osr/types.h index 8649dbbc..3efdc1f9 100644 --- a/include/osr/types.h +++ b/include/osr/types.h @@ -159,6 +159,8 @@ struct level_t { explicit constexpr level_t(float const f) : v_{static_cast((f - kMinLevel) / 0.25F + 1U)} {} + auto cista_members() { return std::tie(v_); } + constexpr float to_float() const { return (v_ == kNoLevel) ? 0.0F : (kMinLevel + ((v_ - 1U) / 4.0F)); } @@ -245,4 +247,30 @@ constexpr std::uint16_t to_meters_per_second(speed_limit const l) { return static_cast(to_kmh(l) / 3.6); } +// adjacent config +enum class adj_conf { + kNone = 0U, + kBlocked = 1U << 1U, + kUseCH = 1U << 2U, + kNoOmitRestricted = 1U << 3U +}; + +constexpr adj_conf operator|(adj_conf const& a, adj_conf const& b) noexcept { + return adj_conf{static_cast>(a) | + static_cast>(b)}; +} + +constexpr adj_conf operator&(adj_conf const& a, adj_conf const& b) noexcept { + return adj_conf{static_cast>(a) & + static_cast>(b)}; +} + +constexpr bool is_enabled(adj_conf const in, adj_conf const flag) noexcept { + return (in & flag) == flag; +} + +constexpr bool is_disabled(adj_conf const in, adj_conf const flag) noexcept { + return (in & flag) == adj_conf::kNone; +} + } // namespace osr 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/route.cc b/src/route.cc index 4c33bc05..ebb1e1d8 100644 --- a/src/route.cc +++ b/src/route.cc @@ -1,7 +1,10 @@ #include "osr/routing/route.h" +#include #include #include +#include +#include #include #include "boost/thread/tss.hpp" @@ -14,7 +17,9 @@ #include "osr/elevation_storage.h" #include "osr/lookup.h" +#include "osr/preprocessing/contraction_hierarchy/shortcut_storage.h" #include "osr/routing/bidirectional.h" +#include "osr/routing/ch_dijkstra.h" #include "osr/routing/dijkstra.h" #include "osr/routing/profiles/bike.h" #include "osr/routing/profiles/bike_sharing.h" @@ -50,6 +55,15 @@ dijkstra

& get_dijkstra() { return *s.get(); } +template +ch_dijkstra

& get_ch_dijkstra() { + static auto s = boost::thread_specific_ptr>{}; + if (s.get() == nullptr) { + s.reset(new ch_dijkstra

{}); + } + return *s.get(); +} + struct connecting_way { constexpr bool valid() const { return way_ != way_idx_t::invalid(); } @@ -64,11 +78,12 @@ 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("chdijkstra"): return routing_algorithm::kCHDijkstra; } throw utl::fail("unknown routing algorithm: {}", s); } -template +template connecting_way find_connecting_way(typename P::parameters const& params, ways const& w, ways::routing const& r, @@ -79,12 +94,14 @@ connecting_way find_connecting_way(typename P::parameters const& params, typename P::node const to, cost_t const expected_cost) { auto conn = std::optional{}; - P::template adjacent( - params, r, from, blocked, sharing, elevations, + P::template adjacent( + params, r, from, blocked, sharing, elevations, nullptr, + node_idx_t::invalid(), [&](typename P::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, - elevation_storage::elevation const elevation, bool) { + elevation_storage::elevation const elevation, bool, typename P::node, + cost_t) { if (target == to && cost == expected_cost) { auto const is_loop = way != way_idx_t::invalid() && r.is_loop(way) && static_cast(std::abs(a_idx - b_idx)) == @@ -114,22 +131,22 @@ connecting_way find_connecting_way(typename P::parameters const& params, typename P::node const to, cost_t const expected_cost, direction const dir) { - auto const call = [&]() { + auto const call = [&]() { if (dir == direction::kForward) { - return find_connecting_way( + return find_connecting_way( params, w, *w.r_, blocked, sharing, elevations, from, to, expected_cost); } else { - return find_connecting_way( + return find_connecting_way( params, w, *w.r_, blocked, sharing, elevations, from, to, expected_cost); } }; if (blocked == nullptr) { - return call.template operator()(); + return call.template operator()(); } else { - return call.template operator()(); + return call.template operator()(); } } @@ -333,6 +350,232 @@ path reconstruct_bi(typename P::parameters const& params, return p; } +template +auto reconstruct_ch_partially(typename P::parameters const& params, + ways const& w, + lookup const& l, + sharing_data const* sharing, + elevation_storage const* elevations, + shortcut_storage const& sc_stor, + auto const& costs, + location const& loc, + way_candidate const& wc, + typename P::node& mp_node, + auto& path_segments) { + auto retrieve_path = [&](typename P::node& node, + std::list>& path) { + while (true) { + auto const& e = costs.at(node.get_key()); + auto const pred = e.pred(node); + auto const way = e.way(node); + if (!pred.has_value()) { + break; + } + auto const expected_cost = static_cast( + e.cost(node) - costs.at(pred->get_key()).cost(*pred)); + path.emplace_front(node, expected_cost, way); + node = *pred; + } + }; + + auto find_loop = [&](typename P::node const from_node, + typename P::node const to_node, cost_t const max) { + assert(from_node.get_node() == to_node.get_node() && + "loop requires start and end node to have the same node_idx_t"); + auto const order_limit = sc_stor.order_.node2order_[from_node.get_node()]; + auto& normal_d = get_dijkstra

(); + normal_d.reset(max + 1); + auto const start_node = + flip(PathDir) == direction::kForward ? from_node : to_node; + normal_d.pq_.push(typename P::label{start_node, 0}); + auto p = std::list>{}; + normal_d.template run(PathDir), adj_conf::kUseCH>( + params, w, *w.r_, max + 1, nullptr, sharing, elevations, &sc_stor, + order_limit); + auto const dest_node = + flip(PathDir) == direction::kForward ? to_node : from_node; + auto node = dest_node; + normal_d.cost_.at(dest_node.get_key()) + .for_each(dest_node.get_key(), + [&](typename P::node const n, cost_t const c) { + if (static_cast(c) + + P::template turning_cost(PathDir)>( + *w.r_, n, dest_node) == + max) { + node = n; + } + }); + while (true) { + auto const& e = normal_d.cost_.at(node.get_key()); + auto const pred = *e.pred(node); + auto const way = e.way(node); + if (pred == start_node) { + p.emplace_front(node, e.cost(node), way); + break; + } + auto const expected_cost = e.cost(node) - normal_d.get_cost(pred); + p.emplace_front(node, expected_cost, way); + node = pred; + } + return p; + }; + + auto resolve_shortcuts = [&](std::list>& path) { + for (auto it = begin(path), next_it = it; + (next_it = std::next(it)) != end(path);) { + auto& label = *next_it; + int64_t const vrt_idx = static_cast(label.way_.v_) - w.n_ways(); + if (vrt_idx < 0) { + ++it; + continue; + } + auto const& sc = sc_stor.shortcuts_[way_idx_t{vrt_idx}]; + auto const& scr = sc_stor.shortcuts_reconstruct_[way_idx_t{vrt_idx}]; + if (scr.loop_cost_ > 0) { + auto res = find_loop(scr.via_in_, scr.via_out_, scr.loop_cost_); + path.insert(next_it, begin(res), end(res)); + next_it = std::next(it); + } + if constexpr (flip(PathDir) == direction::kForward) { + cost_t const via_cost = + P::template turning_cost(PathDir)>( + *w.r_, (*it).node_, sc.from_) + + scr.from_way_cost_ + + P::node_cost(w.r_->node_properties_[scr.via_in_.get_node()]); + cost_t const to_cost = + (scr.loop_cost_ > 0 + ? 0 + : P::template turning_cost(PathDir)>( + *w.r_, scr.via_in_, scr.via_out_)) + + scr.to_way_cost_ + + P::node_cost(w.r_->node_properties_[label.node_.get_node()]); + path.insert(next_it, {scr.via_in_, via_cost, scr.from_way_}); + label = {sc.to_, to_cost, scr.to_way_}; + } else { + cost_t const via_cost = + P::template turning_cost(PathDir)>( + *w.r_, (*it).node_, sc.to_) + + scr.to_way_cost_ + + P::node_cost(w.r_->node_properties_[scr.via_out_.get_node()]); + cost_t const from_cost = + (scr.loop_cost_ > 0 + ? 0 + : P::template turning_cost(PathDir)>( + *w.r_, scr.via_out_, scr.via_in_)) + + scr.from_way_cost_ + + P::node_cost(w.r_->node_properties_[label.node_.get_node()]); + path.insert(next_it, {scr.via_out_, via_cost, scr.to_way_}); + label = {sc.from_, from_cost, scr.from_way_}; + } + } + }; + + auto add_segments = [&](std::list> const& path, + auto& segments, auto& dist) { + for (auto label = begin(path), next_label = label; + (next_label = std::next(label)) != end(path);) { + dist += add_path

(params, w, *w.r_, nullptr, sharing, elevations, + label->node_, next_label->node_, next_label->cost_, + segments, flip(SearchDir)); + label = next_label; + } + }; + + auto handle_endpoint = [&](typename P::node& node, + std::vector& segments) { + auto const& candidate = + node.get_node() == wc.left_.node_ ? wc.left_ : wc.right_; + segments.front() = { + .polyline_ = l.get_node_candidate_path( + wc, candidate, PathDir == direction::kBackward, loc), + .from_level_ = candidate.lvl_, + .to_level_ = candidate.lvl_, + .from_ = SearchDir == flip(direction::kBackward) + ? node.get_node() + : node_idx_t::invalid(), + .to_ = SearchDir == flip(direction::kForward) + ? node.get_node() + : node_idx_t::invalid(), + + .way_ = way_idx_t::invalid(), + .cost_ = candidate.cost_, + .dist_ = static_cast(candidate.dist_to_node_), + .mode_ = node.get_mode()}; + return candidate.dist_to_node_; + }; + + auto label_path = std::list>{}; + auto dist = 0.0; + + retrieve_path(mp_node, label_path); + label_path.emplace_front(mp_node, kInfeasible, way_idx_t::invalid()); + resolve_shortcuts(label_path); + add_segments(label_path, path_segments, dist); + return dist + handle_endpoint(mp_node, path_segments); +} + +template +path reconstruct_ch(typename P::parameters const& params, + ways const& w, + lookup const& l, + sharing_data const* sharing, + elevation_storage const* elevations, + ch_dijkstra

& d, + shortcut_storage const& sc_stor, + location const& from, + location const& to, + way_candidate const& start, + way_candidate const& dest, + cost_t const cost, + direction const dir) { + auto fwd_node = d.tentative_mp_.first; + auto bwd_node = d.tentative_mp_.second; + auto fwd_segments = std::vector{path::segment{}}; + auto bwd_segments = std::vector{path::segment{}}; + auto fwd_dist = 0.0; + auto bwd_dist = 0.0; + + if (dir == direction::kForward) { + fwd_dist = + reconstruct_ch_partially( + params, w, l, sharing, elevations, sc_stor, d.cost_s_, from, start, + fwd_node, fwd_segments); + bwd_dist = + reconstruct_ch_partially( + params, w, l, sharing, elevations, sc_stor, d.cost_t_, to, dest, + bwd_node, bwd_segments); + } else { + fwd_dist = + reconstruct_ch_partially( + params, w, l, sharing, elevations, sc_stor, d.cost_s_, from, start, + fwd_node, fwd_segments); + bwd_dist = + reconstruct_ch_partially( + params, w, l, sharing, elevations, sc_stor, d.cost_t_, to, dest, + bwd_node, bwd_segments); + } + + bwd_segments.back().cost_ -= + P::node_cost(w.r_->node_properties_[d.tentative_mp_.second.get_node()]); + std::ranges::reverse(dir == direction::kForward ? bwd_segments + : fwd_segments); + // TODO replace with .append_range as soon as Apple Clang version is updated + fwd_segments.insert(end(fwd_segments), begin(bwd_segments), + end(bwd_segments)); + + auto path_elevation = elevation_storage::elevation{}; + for (auto const& segment : fwd_segments) { + path_elevation += segment.elevation_; + } + auto p = path{.cost_ = cost, + .dist_ = fwd_dist + bwd_dist, + .elevation_ = path_elevation, + .segments_ = fwd_segments}; + + d.cost_t_.at(bwd_node.get_key()).write(bwd_node, p); + return p; +} + template path reconstruct(typename P::parameters const& params, ways const& w, @@ -677,6 +920,91 @@ std::optional route_dijkstra(typename P::parameters const& params, return std::nullopt; } +template +std::optional route_ch_dijkstra( + typename P::parameters const& params, + ways const& w, + lookup const& l, + ch_dijkstra

& d, + location const& from, + location const& to, + match_view_t from_match, + match_view_t to_match, + cost_t const max, + direction const dir, + sharing_data const* sharing, + elevation_storage const* elevations, + shortcut_storage const& sc_stor) { + if (auto const direct = try_direct(from, to); direct.has_value()) { + return *direct; + } + + auto const limit_squared_max_matching_distance = + std::pow(geo::distance(from.pos_, to.pos_), 2) / + kMaxMatchingDistanceSquaredRatio; + + d.reset(max); + for (auto const [i, start] : utl::enumerate(from_match)) { + if (d.max_reached_s_ && component_seen(w, from_match, i)) { + continue; + } + + for (auto const* nc : {&start.left_, &start.right_}) { + if (nc->valid() && nc->cost_ < max) { + P::resolve_start_node( + *w.r_, start.way_, nc->node_, from.lvl_, dir, + [&](auto const node) { d.add_start(w, {node, nc->cost_}); }); + } + } + + if (d.pq_s_.empty()) { + continue; + } + for (auto const [j, end] : utl::enumerate(to_match)) { + if (w.r_->way_component_[start.way_] != w.r_->way_component_[end.way_]) { + continue; + } + if (d.max_reached_t_ && component_seen(w, to_match, j)) { + continue; + } + if (std::pow(end.dist_to_way_, 2) > limit_squared_max_matching_distance && + j > kBottomKDefinitelyConsidered) { + break; + } + auto const end_way = end.way_; + for (auto const* nc : {&end.left_, &end.right_}) { + if (nc->valid() && nc->cost_ < max) { + P::resolve_start_node( + *w.r_, end_way, nc->node_, to.lvl_, opposite(dir), + [&](auto const node) { + auto label = typename P::label{node, nc->cost_}; + label.track(label, *w.r_, end_way, node.get_node(), false); + d.add_end(w, label); + }); + } + } + if (d.pq_t_.empty()) { + continue; + } + + auto const should_continue = + d.run(params, w, *w.r_, max, sharing, elevations, sc_stor, dir); + + if (d.tentative_mp_.first.get_node() == node_idx_t::invalid()) { + if (should_continue) { + continue; + } + return std::nullopt; + } + + return reconstruct_ch

(params, w, l, sharing, elevations, d, sc_stor, + from, to, start, end, d.tentative_cost_, dir); + } + d.reset_partially(); + } + return std::nullopt; +} + template std::vector> route( typename P::parameters const& params, @@ -849,6 +1177,37 @@ std::optional route_dijkstra(profile_parameters const& params, }); } +std::optional route_ch_dijkstra(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, + sharing_data const* sharing, + elevation_storage const* elevations) { + return with_profile(profile, [&](P&&) -> std::optional { + auto const& pp = std::get(params); + auto const& wrp_sc_stor = get_shortcut_storage(); + if (!wrp_sc_stor) { + return std::nullopt; + } + auto const from_match = + l.match

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

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

(), from, to, + from_match, to_match, max, dir, sharing, + elevations, *wrp_sc_stor); + }); +} + std::vector> route( profile_parameters const& params, ways const& w, @@ -911,6 +1270,17 @@ std::optional route(profile_parameters const& params, from_match, to_match, max, dir, blocked, sharing, elevations); }); + case routing_algorithm::kCHDijkstra: + return with_profile(profile, [&](P&&) { + auto const& wrp_sc_stor = get_shortcut_storage(); + if (!wrp_sc_stor) { + return std::optional{}; + } + return route_ch_dijkstra(std::get(params), w, l, + get_ch_dijkstra

(), from, to, from_match, + to_match, max, dir, sharing, elevations, + *wrp_sc_stor); + }); } throw utl::fail("not implemented"); } @@ -942,6 +1312,9 @@ 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::kCHDijkstra: + return route_ch_dijkstra(params, w, l, profile, from, to, max, dir, + max_match_distance, sharing, elevations); } throw utl::fail("not implemented"); } 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()); diff --git a/test/dijkstra_contraction_hierarchies_test.cc b/test/dijkstra_contraction_hierarchies_test.cc new file mode 100644 index 00000000..63f057b1 --- /dev/null +++ b/test/dijkstra_contraction_hierarchies_test.cc @@ -0,0 +1,296 @@ +#ifdef _WIN32 +#include "windows.h" +#endif + +#include "gtest/gtest.h" + +#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/preprocessing/contraction_hierarchy/contraction.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" + +namespace fs = std::filesystem; +using namespace osr; + +constexpr auto kUseFixedSeed = true; +constexpr auto kForcePreprocessing = true; +constexpr auto kUseMultithreading = true; +constexpr auto kPrintDebugGeojson = false; +constexpr auto kMaxMatchDistance = 100; +constexpr auto kMaxAllowedPathDifferenceRatio = 0.5; +constexpr auto kMaxDetourCost = 10 * 60U; +constexpr auto kOrderingMethod = node_order::method::kRealImportance; + +void extract_ch(std::string_view const raw_data, + std::string_view const data_dir) { + if (!fs::exists(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); + extract(false, raw_data, data_dir, fs::path{}); + } + } +} + +template +void preprocess(std::string_view const data_dir) { + if (auto const f_name = + shortcut_storage::get_filename(search_profile::kCar); + Force || !fs::exists(fs::path{data_dir} / f_name)) { + fmt::println("{} not found or FORCED\nContracting...", f_name); + auto const seed = + UseFixedSeed + ? 1337 + : std::chrono::steady_clock::now().time_since_epoch().count(); + preprocess_ch(data_dir, search_profile::kCar, car::parameters{}, seed, + kMaxDetourCost, kOrderingMethod); + } else { + fmt::println("{} found\nStarting tests...", f_name); + } + get_shortcut_storage() = + shortcut_storage::read(data_dir, search_profile::kCar); +} + +void run_ch(ways const& w, + lookup const& l, + unsigned const n_samples, + unsigned const max_cost, + direction const dir) { + + auto const from_tos = [&]() { + auto prng = std::mt19937{}; // NOLINT(*-msc51-cpp) + 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 = std::atomic{0U}; + auto n_empty_matches = std::atomic{0U}; + auto reference_times = std::vector{}; + auto 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(car::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) { + // matches.resize(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 = + route(car::parameters{}, w, l, search_profile::kCar, from_loc, to_loc, + from_matches_span, to_matches_span, max_cost, dir, nullptr, + nullptr, nullptr, routing_algorithm::kDijkstra); + auto const reference_time = + std::chrono::steady_clock::now() - reference_start; + + auto const experiment_start = std::chrono::steady_clock::now(); + auto const experiment = + route(car::parameters{}, w, l, search_profile::kCar, from_loc, to_loc, + from_matches_span, to_matches_span, max_cost, dir, nullptr, + nullptr, nullptr, routing_algorithm::kCHDijkstra); + auto const experiment_time = + std::chrono::steady_clock::now() - experiment_start; + + if (reference.has_value() != experiment.has_value() || + (reference && experiment && + (reference->cost_ != experiment->cost_ /*|| + std::abs(reference->dist_ - experiment->dist_) / reference->dist_ > + kMaxAllowedPathDifferenceRatio*/))) { + 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, from_node, 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); + print_result("CH ", experiment, experiment_time); + + } else { + ++n_congruent; + } + + 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); + } + }; + + if (kUseMultithreading) { + utl::parallel_for(from_tos, single_run); + } else { + std::ranges::for_each(from_tos, single_run); + } + + auto const non_empty_congruent = n_congruent - n_empty_matches; + auto const non_empty_samples = n_samples - n_empty_matches; + + EXPECT_EQ(non_empty_samples, non_empty_congruent); + + fmt::println("congruent on non-empty: {}/{} ({:3.1f}%)", non_empty_congruent, + non_empty_samples, + (static_cast(non_empty_congruent) / + static_cast(non_empty_samples)) * + 100); + if (non_empty_congruent == non_empty_samples) { + fmt::println( + "speedup on non-empty: {:.2f}", + static_cast( + std::reduce(begin(reference_times), end(reference_times)).count()) / + static_cast( + std::reduce(begin(experiment_times), end(experiment_times)) + .count())); + } +} + +TEST(dijkstra_ch, monaco_fwd) { + auto const raw_data = "test/monaco.osm.pbf"; + auto const data_dir = "test/monaco"; + constexpr auto num_samples = 10000U; + constexpr auto max_cost = 2 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + extract_ch(raw_data, data_dir); + auto const w = ways{data_dir, cista::mmap::protection::READ}; + auto const l = lookup{w, data_dir, cista::mmap::protection::READ}; + preprocess(data_dir); + + run_ch(w, l, num_samples, max_cost, dir); +} + +TEST(dijkstra_ch, monaco_bwd) { + auto const raw_data = "test/monaco.osm.pbf"; + auto const data_dir = "test/monaco"; + constexpr auto num_samples = 10000U; + constexpr auto max_cost = 2 * 3600U; + auto constexpr dir = direction::kBackward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + extract_ch(raw_data, data_dir); + auto const w = ways{data_dir, cista::mmap::protection::READ}; + auto const l = lookup{w, data_dir, cista::mmap::protection::READ}; + preprocess(data_dir); + + run_ch(w, l, num_samples, max_cost, dir); +} + +TEST(dijkstra_ch, hamburg) { + auto const raw_data = "test/hamburg.osm.pbf"; + auto const data_dir = "test/hamburg"; + constexpr auto num_samples = 5000U; + constexpr auto max_cost = 3 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + extract_ch(raw_data, data_dir); + auto const w = ways{data_dir, cista::mmap::protection::READ}; + auto const l = lookup{w, data_dir, cista::mmap::protection::READ}; + preprocess(data_dir); + + run_ch(w, l, num_samples, max_cost, dir); +} + +TEST(dijkstra_ch, DISABLED_switzerland) { + auto const raw_data = "test/switzerland.osm.pbf"; + auto const data_dir = "test/switzerland"; + constexpr auto num_samples = 1000U; + constexpr auto max_cost = 5 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + extract_ch(raw_data, data_dir); + auto const w = ways{data_dir, cista::mmap::protection::READ}; + auto const l = lookup{w, data_dir, cista::mmap::protection::READ}; + preprocess(data_dir); + + run_ch(w, l, num_samples, max_cost, dir); +} + +TEST(dijkstra_ch, DISABLED_germany) { + auto const raw_data = "test/germany.osm.pbf"; + auto const data_dir = "test/germany"; + constexpr auto num_samples = 50U; + constexpr auto max_cost = 12 * 3600U; + auto constexpr dir = direction::kForward; + + if (!fs::exists(raw_data) && !fs::exists(data_dir)) { + GTEST_SKIP() << raw_data << " not found"; + } + + extract_ch(raw_data, data_dir); + auto const w = ways{data_dir, cista::mmap::protection::READ}; + auto const l = lookup{w, data_dir, cista::mmap::protection::READ}; + preprocess(data_dir); + + run_ch(w, l, num_samples, max_cost, dir); +} \ No newline at end of file diff --git a/web/index.html b/web/index.html index 0c2da3be..b5798b6b 100644 --- a/web/index.html +++ b/web/index.html @@ -140,6 +140,7 @@