From 381bce06f64bde09e2264cc9d9b58d6921ef5087 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Sat, 20 Jan 2024 15:04:15 +0100 Subject: [PATCH 001/198] Add time zone info to `/route` and `sources_to_targets` responses (#4491) Co-authored-by: Nils --- CHANGELOG.md | 1 + proto/common.proto | 2 + proto/matrix.proto | 2 + src/baldr/datetime.cc | 21 ++-- src/thor/costmatrix.cc | 10 +- src/thor/route_action.cc | 74 ++++++----- src/thor/timedistancematrix.cc | 24 ++-- src/tyr/matrix_serializer.cc | 11 ++ src/tyr/route_serializer_valhalla.cc | 16 ++- test/gurka/test_matrix.cc | 177 +++++++++++++++++++++++++++ test/gurka/test_route.cc | 156 +++++++++++++++++++++++ valhalla/baldr/datetime.h | 19 ++- valhalla/thor/matrix_common.h | 2 + valhalla/thor/timedistancematrix.h | 2 +- 14 files changed, 461 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fd91ee3e1..3c38cff127 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,6 +72,7 @@ * CHANGED: Adujustment of walk speed when walking on slight downhill [#4302](https://github.com/valhalla/valhalla/pull/4302) * CHANGED: Do not reclassify ferry connections when no hierarchies are to be generated [#4487](https://github.com/valhalla/valhalla/pull/4487) * ADDED: Added a config option to sort nodes spatially during graph building [#4455](https://github.com/valhalla/valhalla/pull/4455) + * ADDED: Timezone info in route and matrix responses [#4491](https://github.com/valhalla/valhalla/pull/4491) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/proto/common.proto b/proto/common.proto index 9b228b797d..a99d74d317 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -159,6 +159,8 @@ message Location { // This information will be ignored if provided in the request. Instead it will be filled in as the request is handled Correlation correlation = 90; + string time_zone_offset = 91; + string time_zone_name = 92; } message TransitEgressInfo { diff --git a/proto/matrix.proto b/proto/matrix.proto index cbf1ba75f6..e89b74b6d0 100644 --- a/proto/matrix.proto +++ b/proto/matrix.proto @@ -16,4 +16,6 @@ message Matrix { repeated string date_times = 6; Algorithm algorithm = 7; repeated string shapes = 8; + repeated string time_zone_offsets = 9; + repeated string time_zone_names = 10; } diff --git a/src/baldr/datetime.cc b/src/baldr/datetime.cc index d14e865e09..f29d9cce06 100644 --- a/src/baldr/datetime.cc +++ b/src/baldr/datetime.cc @@ -16,6 +16,7 @@ namespace { // NOTE, the below timezone maps are indexed for compatibility reasons. We put the value // (index) +const valhalla::baldr::DateTime::dt_info_t INVALID_DT = {"", "", ""}; // into the tiles, so it needs to be stable. We have 2 timezone maps here: // - tz_name_to_id: // - before 2023c: currently and past official timezone names @@ -1192,18 +1193,24 @@ uint32_t second_of_week(uint32_t epoch_time, const date::time_zone* time_zone) { return day * midgard::kSecondsPerDay + since_midnight.count(); } -std::string +dt_info_t offset_date(const std::string& in_dt, const uint32_t in_tz, const uint32_t out_tz, float offset) { if (in_dt.empty()) { - return ""; - } else if (!offset) { - return in_dt; - } - // get the input UTC time, add the offset and translate to the out timezone + return INVALID_DT; + } // get the input UTC time, add the offset and translate to the out timezone + auto iepoch = DateTime::seconds_since_epoch(in_dt, DateTime::get_tz_db().from_index(in_tz)); + auto oepoch = static_cast(static_cast(iepoch) + static_cast(offset + .5f)); - return DateTime::seconds_to_date(oepoch, DateTime::get_tz_db().from_index(out_tz), false); + auto tz = DateTime::get_tz_db().from_index(out_tz); + auto dt = DateTime::seconds_to_date(oepoch, tz, true); + + // dt can be empty if time zones are invalid + if (dt.empty()) { + return INVALID_DT; + }; + return {dt.substr(0, 16), dt.substr(16), tz->name()}; } } // namespace DateTime } // namespace baldr diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index ffc573eee7..f3b5a634d6 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -278,7 +278,7 @@ void CostMatrix::SourceToTarget(Api& request, float time = connection.cost.secs; if (time < kMaxCost) { - auto date_time = + auto dt_info = DateTime::offset_date(source_location_list[source_idx].date_time(), time_infos[source_idx].timezone_index, graphreader.GetTimezoneFromEdge(edgelabel_[MATRIX_REV][target_idx] @@ -287,7 +287,13 @@ void CostMatrix::SourceToTarget(Api& request, tile), time); auto* pbf_date_time = matrix.mutable_date_times()->Add(); - *pbf_date_time = date_time; + *pbf_date_time = dt_info.date_time; + + auto* pbf_time_zone_offset = matrix.mutable_time_zone_offsets()->Add(); + *pbf_time_zone_offset = dt_info.time_zone_offset; + + auto* pbf_time_zone_name = matrix.mutable_time_zone_names()->Add(); + *pbf_time_zone_name = dt_info.time_zone_name; } matrix.mutable_from_indices()->Set(count, source_idx); matrix.mutable_to_indices()->Set(count, target_idx); diff --git a/src/thor/route_action.cc b/src/thor/route_action.cc index d990716435..8de8c66301 100644 --- a/src/thor/route_action.cc +++ b/src/thor/route_action.cc @@ -381,26 +381,28 @@ void thor_worker_t::path_arrive_by(Api& api, const std::string& costing) { if (temp_paths.empty()) return false; for (auto& temp_path : temp_paths) { - // back propagate time information - if (!destination->date_time().empty() && - options.date_time_type() != valhalla::Options::invariant) { - auto origin_dt = - DateTime::offset_date(destination->date_time(), - reader->GetTimezoneFromEdge(temp_path.back().edgeid, tile), - reader->GetTimezoneFromEdge(temp_path.front().edgeid, tile), - -temp_path.back().elapsed_cost.secs); - origin->set_date_time(origin_dt); + auto out_tz = reader->GetTimezoneFromEdge(temp_path.back().edgeid, tile); + auto in_tz = reader->GetTimezoneFromEdge(temp_path.front().edgeid, tile); + + // we add the timezone info if destination is the last location + // and add waiting_secs again from the final destination's datetime, so we output the departing + // time at intermediate locations, not the arrival time + if ((destination->correlation().original_index() == (options.locations().size() - 1) && + (in_tz || out_tz))) { + auto destination_dt = DateTime::offset_date(destination->date_time(), out_tz, out_tz, + destination->waiting_secs()); + destination->set_date_time(destination_dt.date_time); + destination->set_time_zone_offset(destination_dt.time_zone_offset); + destination->set_time_zone_name(destination_dt.time_zone_name); } - // add waiting_secs again from the final destination's datetime, so we output the departing time - // at intermediate locations, not the arrival time - if (destination->waiting_secs() && !destination->date_time().empty()) { - auto dest_dt = - DateTime::offset_date(destination->date_time(), - reader->GetTimezoneFromEdge(temp_path.back().edgeid, tile), - reader->GetTimezoneFromEdge(temp_path.back().edgeid, tile), - destination->waiting_secs()); - destination->set_date_time(dest_dt); + // back propagate time information + if (!destination->date_time().empty()) { + auto origin_dt = DateTime::offset_date(destination->date_time(), out_tz, in_tz, + -temp_path.back().elapsed_cost.secs); + origin->set_date_time(origin_dt.date_time); + origin->set_time_zone_offset(origin_dt.time_zone_offset); + origin->set_time_zone_name(origin_dt.time_zone_name); } first_edge = temp_path.front().edgeid; @@ -465,13 +467,12 @@ void thor_worker_t::path_arrive_by(Api& api, const std::string& costing) { // advance the time for the next destination (i.e. algo origin) by the waiting_secs // of this origin (i.e. algo destination) // TODO(nils): why do we do this twice? above we also do it for a destination.. - if (origin->waiting_secs()) { + if (origin->waiting_secs() && !origin->date_time().empty()) { auto origin_dt = - DateTime::offset_date(origin->date_time(), - reader->GetTimezoneFromEdge(temp_path.front().edgeid, tile), - reader->GetTimezoneFromEdge(temp_path.front().edgeid, tile), - -origin->waiting_secs()); - origin->set_date_time(origin_dt); + DateTime::offset_date(origin->date_time(), in_tz, in_tz, -origin->waiting_secs()); + origin->set_date_time(origin_dt.date_time); + origin->set_time_zone_offset(origin_dt.time_zone_offset); + origin->set_time_zone_name(origin_dt.time_zone_name); } path.clear(); edge_trimming.clear(); @@ -564,14 +565,25 @@ void thor_worker_t::path_depart_at(Api& api, const std::string& costing) { return false; for (auto& temp_path : temp_paths) { + + auto in_tz = reader->GetTimezoneFromEdge(temp_path.front().edgeid, tile); + auto out_tz = reader->GetTimezoneFromEdge(temp_path.back().edgeid, tile); + if ((origin->correlation().original_index() == 0) && (in_tz || out_tz)) { + auto origin_dt = DateTime::offset_date(origin->date_time(), in_tz, in_tz, 0); + + origin->set_date_time(origin_dt.date_time); + origin->set_time_zone_offset(origin_dt.time_zone_offset); + origin->set_time_zone_name(origin_dt.time_zone_name); + } // forward propagate time information - if (!origin->date_time().empty() && options.date_time_type() != valhalla::Options::invariant) { - auto destination_dt = - DateTime::offset_date(origin->date_time(), - reader->GetTimezoneFromEdge(temp_path.front().edgeid, tile), - reader->GetTimezoneFromEdge(temp_path.back().edgeid, tile), - temp_path.back().elapsed_cost.secs + destination->waiting_secs()); - destination->set_date_time(destination_dt); + if (!origin->date_time().empty() && (in_tz || out_tz)) { + float offset = (options.date_time_type() != valhalla::Options::invariant) + ? (temp_path.back().elapsed_cost.secs + destination->waiting_secs()) + : 0.0f; + auto destination_dt = DateTime::offset_date(origin->date_time(), in_tz, out_tz, offset); + destination->set_date_time(destination_dt.date_time); + destination->set_time_zone_offset(destination_dt.time_zone_offset); + destination->set_time_zone_name(destination_dt.time_zone_name); } last_edge = temp_path.back().edgeid; diff --git a/src/thor/timedistancematrix.cc b/src/thor/timedistancematrix.cc index de4328c547..68a22a0131 100644 --- a/src/thor/timedistancematrix.cc +++ b/src/thor/timedistancematrix.cc @@ -207,7 +207,7 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, size_t num_elements = origins.size() * destinations.size(); auto time_infos = SetTime(origins, graphreader); // thanks to protobuf not handling strings well, we have to collect those - std::vector out_date_times(num_elements); + std::vector out_tz_infos(num_elements); // Initialize destinations once for all origins InitDestinations(graphreader, destinations); @@ -243,7 +243,7 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, if (predindex == kInvalidLabel) { // Can not expand any further... FormTimeDistanceMatrix(request, graphreader, FORWARD, origin_index, origin.date_time(), - time_info.timezone_index, dest_edge_ids, out_date_times); + time_info.timezone_index, dest_edge_ids, out_tz_infos); break; } @@ -272,7 +272,7 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, if (UpdateDestinations(origin, destinations, destedge->second, edge, tile, pred, time_info, matrix_locations)) { FormTimeDistanceMatrix(request, graphreader, FORWARD, origin_index, origin.date_time(), - time_info.timezone_index, dest_edge_ids, out_date_times); + time_info.timezone_index, dest_edge_ids, out_tz_infos); break; } } @@ -280,7 +280,7 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, // Terminate when we are beyond the cost threshold if (pred.cost().cost > current_cost_threshold_) { FormTimeDistanceMatrix(request, graphreader, FORWARD, origin_index, origin.date_time(), - time_info.timezone_index, dest_edge_ids, out_date_times); + time_info.timezone_index, dest_edge_ids, out_tz_infos); break; } @@ -293,9 +293,15 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, } // amend the date_time strings - for (auto& date_time : out_date_times) { + for (auto& date_time : out_tz_infos) { auto* pbf_dt = request.mutable_matrix()->mutable_date_times()->Add(); - *pbf_dt = date_time; + *pbf_dt = date_time.date_time; + + auto* pbf_tz_offset = request.mutable_matrix()->mutable_time_zone_offsets()->Add(); + *pbf_tz_offset = date_time.time_zone_offset; + + auto* pbf_tz_names = request.mutable_matrix()->mutable_time_zone_names()->Add(); + *pbf_tz_names = date_time.time_zone_name; } } @@ -573,7 +579,7 @@ void TimeDistanceMatrix::FormTimeDistanceMatrix(Api& request, const std::string& origin_dt, const uint64_t& origin_tz, std::unordered_map& edge_ids, - std::vector& out_date_times) { + std::vector& out_tz_infos) { // when it's forward, origin_index will be the source_index // when it's reverse, origin_index will be the target_index valhalla::Matrix& matrix = *request.mutable_matrix(); @@ -590,10 +596,10 @@ void TimeDistanceMatrix::FormTimeDistanceMatrix(Api& request, // this logic doesn't work with string repeated fields, gotta collect them // and process them later - auto date_time = + auto dt_info = DateTime::offset_date(origin_dt, origin_tz, reader.GetTimezoneFromEdge(edge_ids[i], tile), static_cast(time)); - out_date_times[pbf_idx] = date_time; + out_tz_infos[pbf_idx] = dt_info; } } diff --git a/src/tyr/matrix_serializer.cc b/src/tyr/matrix_serializer.cc index ba22981516..9151744adb 100644 --- a/src/tyr/matrix_serializer.cc +++ b/src/tyr/matrix_serializer.cc @@ -137,6 +137,8 @@ json::ArrayPtr serialize_row(const valhalla::Matrix& matrix, json::MapPtr map; const auto time = matrix.times()[i]; const auto& date_time = matrix.date_times()[i]; + const auto& time_zone_offset = matrix.time_zone_offsets()[i]; + const auto& time_zone_name = matrix.time_zone_names()[i]; if (time != kMaxCost) { map = json::map({{"from_index", static_cast(source_index)}, {"to_index", static_cast(target_index + (i - start_td))}, @@ -145,6 +147,15 @@ json::ArrayPtr serialize_row(const valhalla::Matrix& matrix, if (!date_time.empty()) { map->emplace("date_time", date_time); } + + if (!time_zone_offset.empty()) { + map->emplace("time_zone_offset", time_zone_offset); + } + + if (!time_zone_name.empty()) { + map->emplace("time_zone_name", time_zone_name); + } + if (matrix.shapes().size() && shape_format != no_shape) { switch (shape_format) { case geojson: diff --git a/src/tyr/route_serializer_valhalla.cc b/src/tyr/route_serializer_valhalla.cc index d6560c7868..7455cc16c1 100644 --- a/src/tyr/route_serializer_valhalla.cc +++ b/src/tyr/route_serializer_valhalla.cc @@ -193,6 +193,14 @@ void locations(const valhalla::Api& api, int route_index, rapidjson::writer_wrap writer("date_time", location->date_time()); } + if (!location->time_zone_offset().empty()) { + writer("time_zone_offset", location->time_zone_offset()); + } + + if (!location->time_zone_name().empty()) { + writer("time_zone_name", location->time_zone_name()); + } + if (location->waiting_secs()) { writer("waiting", static_cast(location->waiting_secs())); } @@ -497,10 +505,10 @@ void legs(const valhalla::Api& api, int route_index, rapidjson::writer_wrapper_t // man->emplace("hasGate", maneuver.); // man->emplace("hasFerry", maneuver.); - //“portionsTollNote” : “”, - //“portionsUnpavedNote” : “”, - //“gateAccessRequiredNote” : “”, - //“checkFerryInfoNote” : “” + // “portionsTollNote” : “”, + // “portionsUnpavedNote” : “”, + // “gateAccessRequiredNote” : “”, + // “checkFerryInfoNote” : “” writer.end_object(); // maneuver } diff --git a/test/gurka/test_matrix.cc b/test/gurka/test_matrix.cc index b2df6fab00..c2bb1d4971 100644 --- a/test/gurka/test_matrix.cc +++ b/test/gurka/test_matrix.cc @@ -538,3 +538,180 @@ TEST(StandAlone, CostMatrixShapes) { res.erase(); } + +class DateTimeTest : public ::testing::Test { +protected: + // check both with and without time zones present + static gurka::map map; + static gurka::map map_tz; + + static void SetUpTestSuite() { + constexpr double gridsize = 1500; + + // ~ are approximate time zone crossings + const std::string ascii_map = R"( + A----------B + | | + C D + | | + ~ ~ + | | + | | + E F + | | + G----------H + )"; + + const gurka::ways ways = {{"AC", {{"highway", "residential"}}}, + {"CE", {{"highway", "residential"}}}, + {"EG", {{"highway", "residential"}}}, + {"GH", {{"highway", "residential"}}}, + {"HF", {{"highway", "residential"}}}, + {"FD", {{"highway", "residential"}}}, + {"DB", {{"highway", "residential"}}}, + {"BA", {{"highway", "residential"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {-8.5755, 42.1079}); + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/time_zone_matrix_no_tz"); + map_tz = gurka::buildtiles(layout, ways, {}, {}, "test/data/time_zone_matrix", + {{"mjolnir.timezone", "test/data/tz.sqlite"}, + {"service_limits.max_timedep_distance_matrix", "50000"}}); + } +}; +gurka::map DateTimeTest::map = {}; +gurka::map DateTimeTest::map_tz = {}; + +TEST_F(DateTimeTest, DepartAtCostMatrix) { + rapidjson::Document res_doc; + std::string res; + { + auto api = gurka::do_action(valhalla::Options::sources_to_targets, map_tz, {"A", "G"}, {"A", "G"}, + "auto", + {{"/prioritize_bidirectional", "1"}, + {"/date_time/type", "1"}, + {"/date_time/value", "2020-10-30T09:00"}}, + nullptr, &res); + + res_doc.Parse(res.c_str()); + + // sanity check + EXPECT_EQ(api.matrix().algorithm(), Matrix::CostMatrix); + + EXPECT_TRUE( + res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember("date_time")); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[0] + .GetArray()[0] + .GetObject()["time_zone_offset"], + "+01:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[0] + .GetArray()[1] + .GetObject()["time_zone_offset"], + "+00:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[1] + .GetArray()[0] + .GetObject()["time_zone_offset"], + "+01:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[1] + .GetArray()[1] + .GetObject()["time_zone_offset"], + "+00:00"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject()["time_zone_name"], + "Europe/Madrid"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[0].GetArray()[1].GetObject()["time_zone_name"], + "Europe/Lisbon"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[1].GetArray()[0].GetObject()["time_zone_name"], + "Europe/Madrid"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[1].GetArray()[1].GetObject()["time_zone_name"], + "Europe/Lisbon"); + } +} + +TEST_F(DateTimeTest, DepartAtTimeDistanceMatrix) { + rapidjson::Document res_doc; + std::string res; + { + auto api = gurka::do_action(valhalla::Options::sources_to_targets, map_tz, {"A", "G"}, {"A", "G"}, + "bicycle", + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-30T09:00"}}, + nullptr, &res); + + res_doc.Parse(res.c_str()); + + // sanity check + EXPECT_EQ(api.matrix().algorithm(), Matrix::TimeDistanceMatrix); + + EXPECT_TRUE( + res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember("date_time")); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[0] + .GetArray()[0] + .GetObject()["time_zone_offset"], + "+01:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[0] + .GetArray()[1] + .GetObject()["time_zone_offset"], + "+00:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[1] + .GetArray()[0] + .GetObject()["time_zone_offset"], + "+01:00"); + + EXPECT_EQ(res_doc["sources_to_targets"] + .GetArray()[1] + .GetArray()[1] + .GetObject()["time_zone_offset"], + "+00:00"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject()["time_zone_name"], + "Europe/Madrid"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[0].GetArray()[1].GetObject()["time_zone_name"], + "Europe/Lisbon"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[1].GetArray()[0].GetObject()["time_zone_name"], + "Europe/Madrid"); + + EXPECT_EQ(res_doc["sources_to_targets"].GetArray()[1].GetArray()[1].GetObject()["time_zone_name"], + "Europe/Lisbon"); + } +} + +TEST_F(DateTimeTest, NoTimeZone) { + rapidjson::Document res_doc; + std::string res; + { + auto api = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"A", "G"}, {"A", "G"}, "auto", + {{"/prioritize_bidirectional", "true"}, + {"/date_time/type", "1"}, + {"/date_time/value", "2020-10-30T09:00"}}, + nullptr, &res); + res_doc.Parse(res.c_str()); + + EXPECT_FALSE( + res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember("date_time")); + + EXPECT_FALSE(res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember( + "time_zone_offset")); + + EXPECT_FALSE(res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember( + "time_zone_name")); + } +} \ No newline at end of file diff --git a/test/gurka/test_route.cc b/test/gurka/test_route.cc index 5fd68b4fee..a7ab49095a 100644 --- a/test/gurka/test_route.cc +++ b/test/gurka/test_route.cc @@ -997,3 +997,159 @@ TEST(AlgorithmTestDest, TestAlgoMultiOriginDestination) { check("8", "8", {"CD"}); } + +class DateTimeTest : public ::testing::Test { +protected: + // check both with and without time zones present + static gurka::map map; + static gurka::map map_tz; + + static void SetUpTestSuite() { + constexpr double gridsize = 1500; + + // ~ are approximate time zone crossings + const std::string ascii_map = R"( + A----------B + | | + C D + | | + ~ ~ + | | + | | + E F + | | + G----------H + )"; + + const gurka::ways ways = {{"AC", {{"highway", "residential"}}}, + {"CE", {{"highway", "residential"}}}, + {"EG", {{"highway", "residential"}}}, + {"GH", {{"highway", "residential"}}}, + {"HF", {{"highway", "residential"}}}, + {"FD", {{"highway", "residential"}}}, + {"DB", {{"highway", "residential"}}}, + {"BA", {{"highway", "residential"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {-8.5755, 42.1079}); + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/time_zone_route_no_tz"); + map_tz = gurka::buildtiles(layout, ways, {}, {}, "test/data/time_zone_route", + {{"mjolnir.timezone", VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}); + } +}; +gurka::map DateTimeTest::map = {}; +gurka::map DateTimeTest::map_tz = {}; + +TEST_F(DateTimeTest, DepartAt) { + { + // one time zone crossing + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "G"}, "auto", + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T08:23"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+00:00"); + } + { + // no time zone crossing + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "B"}, "auto", + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T09:21"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+01:00"); + } + { + // two time zone crossings + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "G", "H", "B"}, "auto", + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T08:23"); + EXPECT_EQ(api.options().locations(2).date_time(), "2020-10-30T08:44"); + EXPECT_EQ(api.options().locations(3).date_time(), "2020-10-30T10:07"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+00:00"); + EXPECT_EQ(api.options().locations(2).time_zone_offset(), "+00:00"); + EXPECT_EQ(api.options().locations(3).time_zone_offset(), "+01:00"); + } +} + +TEST_F(DateTimeTest, DepartAtNoTz) { + { + // one time zone crossing + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto", + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(1).date_time(), ""); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), ""); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), ""); + } +} + +TEST_F(DateTimeTest, ArriveBy) { + { + // one time zone crossing + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "G"}, "auto", + {{"/date_time/type", "2"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T09:37"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+00:00"); + } + { + // no time zone crossing + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "B"}, "auto", + {{"/date_time/type", "2"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T08:39"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+01:00"); + } + { + // two time zone crossings + auto api = gurka::do_action(valhalla::Options::route, map_tz, {"A", "G", "H", "B"}, "auto", + {{"/date_time/type", "2"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), "2020-10-30T07:53"); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T07:16"); + EXPECT_EQ(api.options().locations(2).date_time(), "2020-10-30T07:37"); + EXPECT_EQ(api.options().locations(3).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), "+01:00"); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), "+00:00"); + EXPECT_EQ(api.options().locations(2).time_zone_offset(), "+00:00"); + EXPECT_EQ(api.options().locations(3).time_zone_offset(), "+01:00"); + } +} + +TEST_F(DateTimeTest, ArriveByNoTz) { + { + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto", + {{"/date_time/type", "2"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), ""); + EXPECT_EQ(api.options().locations(1).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), ""); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), ""); + } + { + // multiple locations to check we do not propagate date time + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G", "F", "D"}, "auto", + {{"/date_time/type", "2"}, {"/date_time/value", "2020-10-30T09:00"}}); + EXPECT_EQ(api.options().locations(0).date_time(), ""); + EXPECT_EQ(api.options().locations(1).date_time(), ""); + EXPECT_EQ(api.options().locations(2).date_time(), ""); + EXPECT_EQ(api.options().locations(3).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(0).time_zone_offset(), ""); + EXPECT_EQ(api.options().locations(1).time_zone_offset(), ""); + EXPECT_EQ(api.options().locations(2).time_zone_offset(), ""); + EXPECT_EQ(api.options().locations(3).time_zone_offset(), ""); + } +} + +TEST_F(DateTimeTest, Invariant) { + { + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G", "D", "B"}, "auto", + {{"/date_time/type", "3"}, {"/date_time/value", "2020-10-30T09:00"}}); + for (int i = 0; i < 4; ++i) { + EXPECT_EQ(api.options().locations(i).date_time(), "2020-10-30T09:00"); + EXPECT_EQ(api.options().locations(i).time_zone_offset(), ""); + } + } +} diff --git a/valhalla/baldr/datetime.h b/valhalla/baldr/datetime.h index f9f6b8af9d..2ce2ce712f 100644 --- a/valhalla/baldr/datetime.h +++ b/valhalla/baldr/datetime.h @@ -49,6 +49,20 @@ struct tz_db_t { std::unordered_map zones; }; +struct dt_info_t { + dt_info_t() { + } + dt_info_t(const std::string& dt, const std::string& tzo, const std::string& tzn) + : + + date_time(dt), time_zone_offset(tzo), time_zone_name(tzn) { + } + + std::string date_time; + std::string time_zone_offset; + std::string time_zone_name; +}; + /** * Get the timezone database singleton * @return timezone database @@ -216,9 +230,10 @@ uint32_t second_of_week(uint32_t epoch_time, const date::time_zone* time_zone); * @param in_tz the start timezone * @param out_tz the end timezone * @param offset the offset in seconds from the input date time string - * @return out_dt the time at the out_edge in local time after the offset is applied to the in_dt + * @return out_dt a struct containing the time, UTC offset and timezone name at the out_edge in + * local time after the offset is applied to the in_dt */ -std::string +dt_info_t offset_date(const std::string& in_dt, const uint32_t in_tz, const uint32_t out_tz, float offset); /** diff --git a/valhalla/thor/matrix_common.h b/valhalla/thor/matrix_common.h index 42c69bf4f3..92ff454ef5 100644 --- a/valhalla/thor/matrix_common.h +++ b/valhalla/thor/matrix_common.h @@ -95,6 +95,8 @@ inline void reserve_pbf_arrays(valhalla::Matrix& matrix, size_t size) { matrix.mutable_times()->Resize(size, 0U); matrix.mutable_date_times()->Reserve(size); matrix.mutable_shapes()->Reserve(size); + matrix.mutable_time_zone_offsets()->Reserve(size); + matrix.mutable_time_zone_names()->Reserve(size); } } // namespace thor diff --git a/valhalla/thor/timedistancematrix.h b/valhalla/thor/timedistancematrix.h index 6be01b7f7e..cf4fac1162 100644 --- a/valhalla/thor/timedistancematrix.h +++ b/valhalla/thor/timedistancematrix.h @@ -276,7 +276,7 @@ class TimeDistanceMatrix { const std::string& origin_dt, const uint64_t& origin_tz, std::unordered_map& edge_ids, - std::vector& out_date_times); + std::vector& out_tz_infos); }; } // namespace thor From 2b2353ebfdf7b0df33ecebb1fa3a4cd590ea0427 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:31:08 +0100 Subject: [PATCH 002/198] clamp truck speed to 90 kph (#4493) Co-authored-by: Nils --- CHANGELOG.md | 1 + docs/docs/api/turn-by-turn/api-reference.md | 2 +- src/mjolnir/shortcutbuilder.cc | 9 +- src/sif/truckcost.cc | 6 +- test/gurka/test_ferry_connections.cc | 13 ++- test/gurka/test_shortcut.cc | 55 ++++++++++- test/gurka/test_truck_restrictions.cc | 102 ++++++++++++++++++++ valhalla/baldr/graphconstants.h | 2 + valhalla/baldr/graphtile.h | 2 +- 9 files changed, 177 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c38cff127..a58cc72445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ * FIXED: -j wasn't taken into account anymore [#4483](https://github.com/valhalla/valhalla/pull/4483) * FIXED: time distance matrix was always using time zone of last settled edge id [#4494](https://github.com/valhalla/valhalla/pull/4494) * FIXED: log to stderr in valhalla_export_edges [#4498](https://github.com/valhalla/valhalla/pull/4498) + * FIXED: set capped speed for truck at 90 KPH [#4493](https://github.com/valhalla/valhalla/pull/4493) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 735dc9be50..c2055973e2 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -117,7 +117,7 @@ These options are available for `auto`, `bus`, and `truck` costing methods. | `country_crossing_penalty` | A penalty applied for a country crossing. This penalty can be used to create paths that avoid spanning country boundaries. The default penalty is 0. | | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | | `disable_hierarchy_pruning` | Disable hierarchies to calculate the actual optimal route. The default is `false`. **Note:** This could be quite a performance drainer so there is a upper limit of distance. If the upper limit is exceeded, this option will always be `false`. | -| `top_speed` | Top speed the vehicle can go. Also used to avoid roads with higher speeds than this value. `top_speed` must be between 10 and 252 KPH. The default value is 140 KPH. | +| `top_speed` | Top speed the vehicle can go. Also used to avoid roads with higher speeds than this value. `top_speed` must be between 10 and 252 KPH. The default value is 90 KPH for `truck` and 140 KPH for `auto` and `bus`. | | `fixed_speed` | Fixed speed the vehicle can go. Used to override the calculated speed. Can be useful if speed of vehicle is known. `fixed_speed` must be between 1 and 252 KPH. The default value is 0 KPH which disables fixed speed and falls back to the standard calculated speed based on the road attribution. | | `ignore_closures` | If set to `true`, ignores all closures, marked due to live traffic closures, during routing. **Note:** This option cannot be set if `location.search_filter.exclude_closures` is also specified in the request and will return an error if it is | | `closure_factor` | A factor that penalizes the cost when traversing a closed edge (eg: if `search_filter.exclude_closures` is `false` for origin and/or destination location and the route starts/ends on closed edges). Its value can range from `1.0` - don't penalize closed edges, to `10.0` - apply high cost penalty to closed edges. Default value is `9.0`. **Note:** This factor is applicable only for motorized modes of transport, i.e `auto`, `motorcycle`, `motor_scooter`, `bus`, `truck` & `taxi`. | diff --git a/src/mjolnir/shortcutbuilder.cc b/src/mjolnir/shortcutbuilder.cc index cb8f0a0985..48921a2409 100644 --- a/src/mjolnir/shortcutbuilder.cc +++ b/src/mjolnir/shortcutbuilder.cc @@ -295,9 +295,11 @@ uint32_t ConnectEdges(GraphReader& reader, total_duration += edge_duration; // Add edge and turn duration for truck - // Currently use the same as for cars. TODO: implement for trucks total_truck_duration += turn_duration; - auto const truck_speed = tile->GetSpeed(directededge, kNoFlowMask, kInvalidSecondsOfWeek, true); + auto const truck_speed = + std::min(tile->GetSpeed(directededge, kNoFlowMask, kInvalidSecondsOfWeek, true), + directededge->truck_speed() ? directededge->truck_speed() : kMaxAssumedTruckSpeed); + assert(truck_speed != 0); auto const edge_duration_truck = directededge->length() / (truck_speed * kKPHtoMetersPerSec); total_truck_duration += edge_duration_truck; @@ -385,7 +387,8 @@ uint32_t AddShortcutEdges(GraphReader& reader, assert(speed != 0); float total_duration = length / (speed * kKPHtoMetersPerSec); uint32_t const truck_speed = - tile->GetSpeed(directededge, kNoFlowMask, kInvalidSecondsOfWeek, true); + std::min(tile->GetSpeed(directededge, kNoFlowMask, kInvalidSecondsOfWeek, true), + directededge->truck_speed() ? directededge->truck_speed() : kMaxAssumedTruckSpeed); assert(truck_speed != 0); float total_truck_duration = directededge->length() / (truck_speed * kKPHtoMetersPerSec); diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index c4f344c090..1fffd14172 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -97,6 +97,7 @@ constexpr ranged_default_t kTruckLengthRange{0, kDefaultTruckLength, 50.0 constexpr ranged_default_t kUseTollsRange{0, kDefaultUseTolls, 1.0f}; constexpr ranged_default_t kAxleCountRange{2, kDefaultAxleCount, 20}; constexpr ranged_default_t kUseHighwaysRange{0, kDefaultUseHighways, 1.0f}; +constexpr ranged_default_t kTopSpeedRange{10, kMaxAssumedTruckSpeed, kMaxSpeedKph}; BaseCostingOptionsConfig GetBaseCostOptsConfig() { BaseCostingOptionsConfig cfg{}; @@ -484,7 +485,9 @@ Cost TruckCost::EdgeCost(const baldr::DirectedEdge* edge, &flow_sources, time_info.seconds_from_now) : fixed_speed_; - auto final_speed = std::min(edge_speed, top_speed_); + auto final_speed = std::min(std::min(edge_speed, edge->truck_speed() ? edge->truck_speed() + : kMaxAssumedTruckSpeed), + top_speed_); float sec = edge->length() * speedfactor_[final_speed]; @@ -710,6 +713,7 @@ void ParseTruckCostOptions(const rapidjson::Document& doc, JSON_PBF_RANGED_DEFAULT(co, kUseHighwaysRange, json, "/use_highways", use_highways); co->set_axle_count( kAxleCountRange(rapidjson::get(json, "/axle_count", co->axle_count()))); + JSON_PBF_RANGED_DEFAULT(co, kTopSpeedRange, json, "/top_speed", top_speed); } cost_ptr_t CreateTruckCost(const Costing& costing_options) { diff --git a/test/gurka/test_ferry_connections.cc b/test/gurka/test_ferry_connections.cc index a8e9b95c35..1c99312d40 100644 --- a/test/gurka/test_ferry_connections.cc +++ b/test/gurka/test_ferry_connections.cc @@ -119,12 +119,15 @@ TEST(Standalone, ShortFerry) { } TEST(Standalone, TruckFerryDuration) { - // corresponds to 33.3 m/sec (120 km/h) in the below map - uint32_t ferry_secs = 9; + // corresponds to 21.4 m/sec (~77 km/h) in the below map + uint32_t ferry_secs = 14; const std::string ascii_map = R"( A--B--C--D | | + | | + | | + | | E--------F )"; @@ -151,8 +154,8 @@ TEST(Standalone, TruckFerryDuration) { // verify we took the ferry edge and the duration tag was respected auto ferry_edge = fastest.trip().routes(0).legs(0).node(1).edge(); ASSERT_EQ(ferry_edge.use(), valhalla::TripLeg_Use::TripLeg_Use_kFerryUse); - ASSERT_NEAR(ferry_edge.speed(), ferry_edge.length_km() / (ferry_secs * kHourPerSec), 0.1); - ASSERT_NEAR(fastest.directions().routes(0).legs(0).summary().time(), 45, 0.001); + ASSERT_NEAR(ferry_edge.speed(), ferry_edge.length_km() / (ferry_secs * kHourPerSec), 0.2); + ASSERT_NEAR(fastest.directions().routes(0).legs(0).summary().time(), 50, 0.1); // pass a higher ferry cost and make sure it's added to the time valhalla::Api higher_ferry_cost = @@ -160,7 +163,7 @@ TEST(Standalone, TruckFerryDuration) { {{"/costing_options/truck/use_ferry", "1"}, {"/costing_options/truck/ferry_cost", "10"}}); - ASSERT_NEAR(higher_ferry_cost.directions().routes(0).legs(0).summary().time(), 55, 0.001); + ASSERT_NEAR(higher_ferry_cost.directions().routes(0).legs(0).summary().time(), 60, 0.1); } TEST_F(FerryTest, DoNotReclassifyFerryConnection) { diff --git a/test/gurka/test_shortcut.cc b/test/gurka/test_shortcut.cc index cb6bfd0923..398a814d8e 100644 --- a/test/gurka/test_shortcut.cc +++ b/test/gurka/test_shortcut.cc @@ -165,7 +165,8 @@ TEST(Shortcuts, ShortcutSpeed) { } TEST(Shortcuts, TruckSpeedNotSet) { - // When truck speed is not set normal speed is used to calculate shortcut truck speed. + // When truck speed is not set normal speed is used to calculate shortcut truck speed, which + // is clamped at 90 KPH. // As a result it should be equal to normal shortcut speed. const std::string ascii_map = R"(A-----B\ \C @@ -205,7 +206,8 @@ TEST(Shortcuts, TruckSpeedNotSet) { if (!edge->is_shortcut() || !(edge->forwardaccess() & baldr::kAutoAccess)) continue; - EXPECT_EQ(edge->speed(), edge->truck_speed()); + // truck speed should be smaller than the edge speed + EXPECT_GT(edge->speed(), edge->truck_speed()); found_shortcut = true; } } @@ -221,12 +223,12 @@ TEST(Shortcuts, TruckSpeedPartiallySet) { {"AB", {{"highway", "motorway"}, {"maxspeed", "100"}, - {"maxspeed:hgv", "90"}, + {"maxspeed:hgv", "100"}, {"name", "High street"}}}, {"BC", {{"highway", "motorway"}, {"maxspeed", "100"}, - {"maxspeed:hgv", "90"}, + {"maxspeed:hgv", "100"}, {"name", "High street"}}}, {"CD", {{"highway", "motorway"}, {"maxspeed", "100"}, {"name", "High street"}}}, {"DE", {{"highway", "motorway"}, {"maxspeed", "100"}, {"name", "High street"}}}, @@ -257,6 +259,51 @@ TEST(Shortcuts, TruckSpeedPartiallySet) { EXPECT_TRUE(found_shortcut) << "No shortcuts found. Check the map."; } +TEST(Shortcuts, TruckSpeedPartiallySetLow) { + // When truck speed is not set normal speed is used to calculate shortcut truck speed. + // As a result, when truck speed is set only for some of constituent edges, resulting speed is + // range from truck speed to normal speed + const std::string ascii_map = R"(A---B---C---D---E)"; + const gurka::ways ways = { + {"AB", + {{"highway", "motorway"}, + {"maxspeed", "100"}, + {"maxspeed:hgv", "60"}, + {"name", "High street"}}}, + {"BC", + {{"highway", "motorway"}, + {"maxspeed", "100"}, + {"maxspeed:hgv", "60"}, + {"name", "High street"}}}, + {"CD", {{"highway", "motorway"}, {"maxspeed", "100"}, {"name", "High street"}}}, + {"DE", {{"highway", "motorway"}, {"maxspeed", "100"}, {"name", "High street"}}}, + }; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 10); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_shortcut"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + bool found_shortcut = false; + auto const tileset = reader.GetTileSet(0); + for (const auto tileid : tileset) { + if (reader.OverCommitted()) + reader.Trim(); + + // for each edge in the tile + auto tile = reader.GetGraphTile(tileid); + for (size_t j = 0; j < tile->header()->directededgecount(); ++j) { + // skip it if its not a shortcut or the shortcut is one we will never traverse + const auto* edge = tile->directededge(j); + if (!edge->is_shortcut() || !(edge->forwardaccess() & baldr::kAutoAccess)) + continue; + + EXPECT_LT(60, edge->truck_speed()); + EXPECT_GT(85, edge->truck_speed()); + found_shortcut = true; + } + } + EXPECT_TRUE(found_shortcut) << "No shortcuts found. Check the map."; +} + TEST(Shortcuts, ShortcutsInBins) { const std::string ascii_map = R"(A---B---C)"; const gurka::ways ways = {{"AB", {{"highway", "motorway"}, {"name", "High Street"}}}, diff --git a/test/gurka/test_truck_restrictions.cc b/test/gurka/test_truck_restrictions.cc index 6ff2a1fd67..7368bb8dbf 100644 --- a/test/gurka/test_truck_restrictions.cc +++ b/test/gurka/test_truck_restrictions.cc @@ -1,8 +1,13 @@ #include "gurka.h" +#include "test.h" #include using namespace valhalla; +inline float getDuration(const valhalla::Api& route) { + return route.directions().routes(0).legs(0).summary().time(); +} + class TruckRestrictionTest : public ::testing::TestWithParam> { protected: static gurka::map map; @@ -63,3 +68,100 @@ INSTANTIATE_TEST_SUITE_P(TruckRestrictions, std::pair{"hazmat", "true"}, std::pair{"axle_load", "11"}, std::pair{"axle_count", "10"})); + +TEST(TruckSpeed, MaxTruckSpeed) { + constexpr double gridsize = 500; + + const std::string ascii_map = R"( + A----------B + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/truckspeed"); + + map.config.put("mjolnir.traffic_extract", "test/data/truckspeed/traffic.tar"); + + test::build_live_traffic_data(map.config); + + valhalla::Api default_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", {}); + + // should be clamped to kMaxAssumedTruckSpeed + valhalla::Api clamped_top_speed_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/costing_options/truck/top_speed", "100"}, + {"/date_time/type", "0"}, + {"/date_time/value", "current"}}); + + // just below kMaxAssumedTruckSpeed + valhalla::Api low_top_speed_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/costing_options/truck/top_speed", "70"}, + {"/date_time/type", "0"}, + {"/date_time/value", "current"}}); + + test::customize_live_traffic_data(map.config, [&](baldr::GraphReader& reader, + baldr::TrafficTile& tile, u_int32_t index, + valhalla::baldr::TrafficSpeed* traffic_speed) { + baldr::GraphId tile_id(tile.header->tile_id); + auto AB = gurka::findEdge(reader, map.nodes, "AB", "B", tile_id); + + if (std::get<1>(AB) != nullptr && std::get<0>(AB).id() == index) { + traffic_speed->overall_encoded_speed = 140 >> 1; + traffic_speed->breakpoint1 = 255; + traffic_speed->encoded_speed1 = 140 >> 1; + } else { + traffic_speed->overall_encoded_speed = UNKNOWN_TRAFFIC_SPEED_RAW - 1; + } + }); + + valhalla::Api modified_traffic_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/date_time/type", "0"}, + {"/date_time/value", "current"}, + {"/costing_options/truck/speed_types/0", "current"}}); + + test::customize_live_traffic_data(map.config, [&](baldr::GraphReader& reader, + baldr::TrafficTile& tile, u_int32_t index, + valhalla::baldr::TrafficSpeed* traffic_speed) { + baldr::GraphId tile_id(tile.header->tile_id); + auto AB = gurka::findEdge(reader, map.nodes, "AB", "B", tile_id); + + if (std::get<1>(AB) != nullptr && std::get<0>(AB).id() == index) { + traffic_speed->overall_encoded_speed = 50 >> 1; + traffic_speed->breakpoint1 = 255; + traffic_speed->encoded_speed1 = 50 >> 1; + } else { + traffic_speed->overall_encoded_speed = UNKNOWN_TRAFFIC_SPEED_RAW - 1; + } + }); + + valhalla::Api modified_traffic_low_speed_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/date_time/type", "0"}, + {"/date_time/value", "current"}, + {"/costing_options/truck/speed_types/0", "current"}}); + + gurka::assert::raw::expect_path(default_route, {"AB"}); + + auto default_time = getDuration(default_route); + auto clamped_top_speed_time = getDuration(clamped_top_speed_route); + auto low_top_speed_time = getDuration(low_top_speed_route); + auto traffic_time = getDuration(modified_traffic_route); + auto traffic_low_speed_time = getDuration(modified_traffic_low_speed_route); + + // default and clamped durations should be the same in this case + ASSERT_EQ(default_time, clamped_top_speed_time); + + // expect a trip to take longer when a low top speed is set + ASSERT_LT(default_time, low_top_speed_time); + + // expect duration to be equal to default if traffic speed is higher than kMaxAssumedTruckCost + // and no truck specific speed tag is set on the way + ASSERT_EQ(default_time, traffic_time); + + // expect lower traffic speeds (< kMaxAssumedTruckSpeed ) to lead to a lower duration + ASSERT_LT(traffic_time, traffic_low_speed_time); +} diff --git a/valhalla/baldr/graphconstants.h b/valhalla/baldr/graphconstants.h index 6a35a106c2..298da5d42c 100644 --- a/valhalla/baldr/graphconstants.h +++ b/valhalla/baldr/graphconstants.h @@ -99,6 +99,8 @@ constexpr uint8_t kMaxTrafficSpeed = 252; // ~157 MPH // clamped to this maximum value. constexpr uint32_t kMaxSpeedKph = std::max(kMaxTrafficSpeed, kMaxAssumedSpeed); +constexpr uint32_t kMaxAssumedTruckSpeed = 90; // ~ 56 MPH + // Minimum speed. This is a stop gap for dubious traffic data. While its possible // to measure a probe going this slow via stop and go traffic over a long enough // stretch, its unlikely to be good signal below this value diff --git a/valhalla/baldr/graphtile.h b/valhalla/baldr/graphtile.h index de329f8348..7c30c19ad5 100644 --- a/valhalla/baldr/graphtile.h +++ b/valhalla/baldr/graphtile.h @@ -620,7 +620,7 @@ class GraphTile { /** * Convenience method for use with costing to get the speed for an edge given the directed * edge and a time (seconds since start of the week). If the current speed of the edge - * is 0 then the current speed is ignore and other speed sources are used to prevent + * is 0 then the current speed is ignored and other speed sources are used to prevent * issues with costing * * @param de Directed edge information. From 84f2259b8b1d7358a2f5ba8fffed81d0fb5605c5 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:35:58 +0100 Subject: [PATCH 003/198] try to assume gha bot identity (#4517) --- .github/workflows/check_tz_releases.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check_tz_releases.yml b/.github/workflows/check_tz_releases.yml index 2ecfa018ff..f7535ec39b 100644 --- a/.github/workflows/check_tz_releases.yml +++ b/.github/workflows/check_tz_releases.yml @@ -20,6 +20,9 @@ jobs: run: | set -e + git config user.name 'github-actions[bot]' + git config user.email 'github-actions[bot]@users.noreply.github.com' + latest_tag=$(git -C third_party/tz ls-remote --tags --refs --quiet origin | tail --lines 1 | awk '{print $2}' | cut --delimiter='/' --fields=3 ) latest_tag_commit=$(git -C third_party/tz rev-list -n 1 $latest_tag) current_commit=$(git -C ./third_party/tz rev-parse HEAD) @@ -56,12 +59,11 @@ jobs: set -e # commit and push git commit -m "bumped tz to ${latest_tag}" - git push origin + git push origin "${new_branch}" # open new PR body=$(echo "See [here](https://github.com/eggert/tz/blob/main/NEWS) for the latest changes made to tz.\ - Created by workflow run [#${WORKFLOW_RUN_ID}](https://github.com/valhalla/valhalla/actions/runs/${WORKFLOW_RUN_ID}/job/${WORKFLOW_JOB_ID})." | xargs) - + Created by workflow run [#${WORKFLOW_RUN_ID}](https://github.com/valhalla/valhalla/actions/runs/${WORKFLOW_RUN_ID})." | xargs) gh pr create --base master --head $new_branch --title "Bump tz to ${latest_tag}" --body "${body}" exit 1 From 03fbd62747de1bf6ccc07b95d279291599c79a9a Mon Sep 17 00:00:00 2001 From: Mahdi Hasnat Siyam <44502454+mahdihasnat@users.noreply.github.com> Date: Wed, 24 Jan 2024 02:38:50 +0600 Subject: [PATCH 004/198] CI: Run build_and_publish job only if repository owner is valhalla (#4522) --- .github/workflows/docker-build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 1e1183f6ec..1d308515b6 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -12,6 +12,7 @@ on: jobs: build_and_publish: runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'valhalla' }} steps: - name: Check out the repo uses: actions/checkout@v3 From 8074e8e20dff55f4c7d753156afb9af22610397c Mon Sep 17 00:00:00 2001 From: Nils Date: Wed, 24 Jan 2024 15:01:10 +0100 Subject: [PATCH 005/198] our cmake find modules are not robust to pkgconfig installations (#4253) --- .circleci/config.yml | 9 ++-- CHANGELOG.md | 1 + CMakeLists.txt | 14 +++--- cmake/FindGEOS.cmake | 89 ------------------------------------- cmake/FindSpatiaLite.cmake | 88 ------------------------------------ conanfile.txt | 6 +-- src/mjolnir/CMakeLists.txt | 4 +- src/mjolnir/adminbuilder.cc | 11 ++++- test/CMakeLists.txt | 2 +- test/util_midgard.cc | 3 +- 10 files changed, 28 insertions(+), 199 deletions(-) delete mode 100644 cmake/FindGEOS.cmake delete mode 100644 cmake/FindSpatiaLite.cmake diff --git a/.circleci/config.yml b/.circleci/config.yml index 86dbcf0e77..2503e26720 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2.1 executors: macos: macos: - xcode: 13.4.1 + xcode: 15.1.0 environment: HOMEBREW_NO_AUTO_UPDATE: 1 CXXFLAGS: -DGEOS_INLINE @@ -11,7 +11,7 @@ executors: commands: mac_deps: steps: - - run: brew install protobuf cmake ccache libtool libspatialite pkg-config luajit curl wget czmq lz4 spatialite-tools unzip + - run: brew install autoconf automake protobuf cmake ccache libtool libspatialite pkg-config luajit curl wget czmq lz4 spatialite-tools unzip - run: sudo python3 -m pip install requests shapely "conan<2.0.0" - run: git clone https://github.com/kevinkreiser/prime_server --recurse-submodules && cd prime_server && ./autogen.sh && ./configure && make -j8 && make install linux_deps: @@ -150,10 +150,7 @@ jobs: keys: - ccache-release-macos-{{ .Branch }}-{{ checksum "conanfile.txt" }} - ccache-release-macos-{{ checksum "conanfile.txt" }} - - run: | - mkdir -p build - cd build - cmake .. + - run: cmake -B build -DENABLE_SINGLE_FILES_WERROR=OFF - run: make -C build -j8 - run: make -C build utrecht_tiles - run: make -C build -j8 tests diff --git a/CHANGELOG.md b/CHANGELOG.md index a58cc72445..1e08a01fd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ * CHANGED: Do not reclassify ferry connections when no hierarchies are to be generated [#4487](https://github.com/valhalla/valhalla/pull/4487) * ADDED: Added a config option to sort nodes spatially during graph building [#4455](https://github.com/valhalla/valhalla/pull/4455) * ADDED: Timezone info in route and matrix responses [#4491](https://github.com/valhalla/valhalla/pull/4491) + * CHANGED: use pkg-config to find spatialite & geos and remove our cmake modules; upgraded conan's boost to 1.83.0 in the process [#4253](https://github.com/valhalla/valhalla/pull/4253) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/CMakeLists.txt b/CMakeLists.txt index 8da2bad542..6dd8065944 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,6 +160,8 @@ add_definitions(-DBOOST_NO_CXX11_SCOPED_ENUMS) add_definitions(-DBOOST_ALLOW_DEPRECATED_HEADERS) add_definitions(-DBOOST_BIND_GLOBAL_PLACEHOLDERS) +find_package(PkgConfig REQUIRED) + if(NOT TARGET CURL::CURL) add_library(CURL::CURL INTERFACE IMPORTED) if(ENABLE_HTTP OR ENABLE_DATA_TOOLS) @@ -169,8 +171,9 @@ if(NOT TARGET CURL::CURL) set_target_properties(CURL::CURL PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIR}" INTERFACE_COMPILE_DEFINITIONS CURL_STATICLIB) + message(STATUS "Found cURL: ${CURL_LIBRARIES}") if(NOT MSVC) - set_property(TARGET CURL::CURL APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${CURL_LIBRARIES}") + target_link_libraries(CURL::CURL INTERFACE ${CURL_LIBRARIES}) else() link_libraries(${CURL_LIBRARIES}) endif() @@ -192,7 +195,7 @@ if(NOT Protobuf_FOUND) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF) endif() -message(STATUS "Using pbf headers from ${PROTOBUF_INCLUDE_DIR}") +message(STATUS "Using pbf headers from ${PROTOBUF_INCLUDE_DIRS}") message(STATUS "Using pbf libs from ${PROTOBUF_LIBRARY}") message(STATUS "Using pbf release libs from ${PROTOBUF_LIBRARY_RELEASE}") message(STATUS "Using pbf debug libs from ${PROTOBUF_LIBRARY_DEBUG}") @@ -229,7 +232,7 @@ endif() if(ENABLE_DATA_TOOLS) add_compile_definitions(DATA_TOOLS) find_package(SQLite3 REQUIRED) - find_package(SpatiaLite REQUIRED) + pkg_check_modules(SpatiaLite REQUIRED IMPORTED_TARGET spatialite) find_package(LuaJIT) add_library(Lua::Lua INTERFACE IMPORTED) set_target_properties(Lua::Lua PROPERTIES @@ -320,8 +323,9 @@ if(ENABLE_DATA_TOOLS) endforeach() # Target-specific dependencies - find_package(GEOS) - target_link_libraries(valhalla_build_admins GEOS::GEOS) + # find_package(GEOS) + pkg_check_modules(GEOS REQUIRED IMPORTED_TARGET geos) + target_link_libraries(valhalla_build_admins PkgConfig::GEOS) target_sources(valhalla_build_statistics PUBLIC ${VALHALLA_SOURCE_DIR}/src/mjolnir/statistics.cc diff --git a/cmake/FindGEOS.cmake b/cmake/FindGEOS.cmake deleted file mode 100644 index 90ced818c2..0000000000 --- a/cmake/FindGEOS.cmake +++ /dev/null @@ -1,89 +0,0 @@ -#--- -# File: FindGEOS.cmake -# -# Find the native GEOS(Geometry Engine - Open Source) includes and libraries. -# -# This module defines: -# -# GEOS_INCLUDE_DIR, where to find geos.h, etc. -# GEOS_LIBRARY, libraries to link against to use GEOS. Currently there are -# two looked for, geos and geos_c libraries. -# GEOS_FOUND, True if found, false if one of the above are not found. -# -# For ossim, typically geos will be system installed which should be found; -# or found in the ossim 3rd party dependencies directory from a geos build -# and install. If the latter it will rely on CMAKE_INCLUDE_PATH and -# CMAKE_LIBRARY_PATH having the path to the party dependencies directory. -# -# NOTE: -# This script is specialized for ossim, e.g. looking in /usr/local/ossim. -# -# $Id$ -#--- - -#--- -# Find include path: -# Note: Ubuntu 14.04+ did not have geos.h (not included in any ossim src). -# Instead looking for Geometry.h -#--- - -find_path( GEOS_INCLUDE_DIR geos/geom/Geometry.h - PATHS - ${CMAKE_INSTALL_PREFIX}/include - ${GEOS_DIR}/include - /usr/include - /usr/local/include - /usr/local/ossim/include ) - -# Find GEOS library: -find_library( GEOS_LIB NAMES geos - PATHS - ${CMAKE_INSTALL_PREFIX}/lib - ${GEOS_DIR}/lib - /usr/lib64 - /usr/lib - /usr/local/lib - /usr/local/ossim/lib ) - -# Find GEOS C library: -find_library( GEOS_C_LIB NAMES geos_c - PATHS - ${CMAKE_INSTALL_PREFIX}/lib - ${GEOS_DIR}/lib - /usr/lib64 - /usr/lib - /usr/local/lib - /usr/local/ossim/lib ) - -# Set the GEOS_LIBRARY: -if( GEOS_LIB AND GEOS_C_LIB ) - set( GEOS_LIBRARY ${GEOS_LIB} ${GEOS_C_LIB} CACHE STRING INTERNAL ) -endif(GEOS_LIB AND GEOS_C_LIB ) - -#--- -# This function sets GEOS_FOUND if variables are valid. -#--- -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( GEOS DEFAULT_MSG - GEOS_LIBRARY - GEOS_INCLUDE_DIR ) - -add_library(GEOS::GEOS INTERFACE IMPORTED) -if( GEOS_FOUND ) - if( NOT GEOS_FIND_QUIETLY ) - message( STATUS "Found GEOS..." ) - endif( NOT GEOS_FIND_QUIETLY ) - - set_target_properties(GEOS::GEOS PROPERTIES - INTERFACE_LINK_LIBRARIES "${GEOS_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${GEOS_INCLUDE_DIR}") -else( GEOS_FOUND ) - if( NOT GEOS_FIND_QUIETLY ) - message( WARNING "Could not find GEOS" ) - endif( NOT GEOS_FIND_QUIETLY ) -endif( GEOS_FOUND ) - -if( NOT GEOS_FIND_QUIETLY ) - message( STATUS "GEOS_INCLUDE_DIR=${GEOS_INCLUDE_DIR}" ) - message( STATUS "GEOS_LIBRARY=${GEOS_LIBRARY}" ) -endif( NOT GEOS_FIND_QUIETLY ) diff --git a/cmake/FindSpatiaLite.cmake b/cmake/FindSpatiaLite.cmake deleted file mode 100644 index 7ad4ed3399..0000000000 --- a/cmake/FindSpatiaLite.cmake +++ /dev/null @@ -1,88 +0,0 @@ -# Find SpatiaLite -# ~~~~~~~~~~~~~~~ -# Copyright (c) 2009, Sandro Furieri -# Redistribution and use is allowed according to the terms of the BSD license. -# For details see the accompanying COPYING-CMAKE-SCRIPTS file. -# -# CMake module to search for SpatiaLite library -# -# If it's found it sets SPATIALITE_FOUND to TRUE -# and following variables are set: -# SPATIALITE_INCLUDE_DIR -# SPATIALITE_LIBRARY - -# This macro checks if the symbol exists -include(CheckLibraryExists) - - -# FIND_PATH and FIND_LIBRARY normally search standard locations -# before the specified paths. To search non-standard paths first, -# FIND_* is invoked first with specified paths and NO_DEFAULT_PATH -# and then again with no specified paths to search the default -# locations. When an earlier FIND_* succeeds, subsequent FIND_*s -# searching for the same item do nothing. - -# try to use sqlite framework on mac -# want clean framework path, not unix compatibility path -IF (APPLE) - IF (CMAKE_FIND_FRAMEWORK MATCHES "FIRST" - OR CMAKE_FRAMEWORK_PATH MATCHES "ONLY" - OR NOT CMAKE_FIND_FRAMEWORK) - SET (CMAKE_FIND_FRAMEWORK_save ${CMAKE_FIND_FRAMEWORK} CACHE STRING "" FORCE) - SET (CMAKE_FIND_FRAMEWORK "ONLY" CACHE STRING "" FORCE) - FIND_PATH(SPATIALITE_INCLUDE_DIR SQLite3/spatialite.h) - # if no SpatiaLite header, we don't want SQLite find below to succeed - IF (SPATIALITE_INCLUDE_DIR) - FIND_LIBRARY(SPATIALITE_LIBRARY SQLite3) - # FIND_PATH doesn't add "Headers" for a framework - SET (SPATIALITE_INCLUDE_DIR ${SPATIALITE_LIBRARY}/Headers CACHE PATH "Path to a file." FORCE) - ENDIF (SPATIALITE_INCLUDE_DIR) - SET (CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK_save} CACHE STRING "" FORCE) - ENDIF () -ENDIF (APPLE) - -FIND_PATH(SPATIALITE_INCLUDE_DIR spatialite.h - /usr/include - "$ENV{INCLUDE}" - "$ENV{LIB_DIR}/include" - "$ENV{LIB_DIR}/include/spatialite" - ) - -FIND_LIBRARY(SPATIALITE_LIBRARY NAMES spatialite_i spatialite PATHS - /usr/lib - $ENV{LIB} - $ENV{LIB_DIR}/lib - ) - -# Handle the QUIETLY and REQUIRED arguments and set SQLITE3_FOUND to TRUE -# if all listed variables are TRUE -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SpatiaLite - DEFAULT_MSG - SPATIALITE_LIBRARY - SPATIALITE_INCLUDE_DIR) - -mark_as_advanced(SPATIALITE_LIBRARY SPATIALITE_INCLUDE_DIR SPATIALITE_LIBRARY) - -IF (SPATIALITE_FOUND) - set(SPATIALITE_LIBRARIES ${SPATIALITE_LIBRARY}) - - IF(APPLE) - # no extra LDFLAGS used in link test, may fail in OS X SDK - SET(CMAKE_REQUIRED_LIBRARIES "-F/Library/Frameworks" ${CMAKE_REQUIRED_LIBRARIES}) - ENDIF(APPLE) - - check_library_exists("${SPATIALITE_LIBRARY}" gaiaStatisticsInvalidate "" SPATIALITE_VERSION_GE_4_2_0) - IF (NOT SPATIALITE_VERSION_GE_4_2_0) - MESSAGE(FATAL_ERROR "Found SpatiaLite, but version is too old. Requires at least version 4.2.0") - ENDIF (NOT SPATIALITE_VERSION_GE_4_2_0) - - if(NOT TARGET SpatiaLite::SpatiaLite) - add_library(SpatiaLite::SpatiaLite INTERFACE IMPORTED) - - set_target_properties(SpatiaLite::SpatiaLite - PROPERTIES - INTERFACE_LINK_LIBRARIES "${SPATIALITE_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${SPATIALITE_INCLUDE_DIR}") - endif() -ENDIF (SPATIALITE_FOUND) diff --git a/conanfile.txt b/conanfile.txt index 61aafb8abc..9dfa3cb050 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -1,5 +1,5 @@ [requires] -boost/1.71.0 +boost/1.83.0 [generators] cmake_find_package @@ -7,16 +7,13 @@ json [options] boost:header_only=True -boost:without_atomic=True boost:without_chrono=True -boost:without_container=True boost:without_context=True boost:without_contract=True boost:without_coroutine=True boost:without_date_time=True boost:without_exception=True boost:without_fiber=True -boost:without_filesystem=True boost:without_graph=True boost:without_graph_parallel=True boost:without_iostreams=True @@ -30,7 +27,6 @@ boost:without_random=True boost:without_regex=True boost:without_serialization=True boost:without_stacktrace=True -boost:without_system=True boost:without_locale=True boost:without_test=True boost:without_thread=True diff --git a/src/mjolnir/CMakeLists.txt b/src/mjolnir/CMakeLists.txt index 0c46435375..71907fb56e 100644 --- a/src/mjolnir/CMakeLists.txt +++ b/src/mjolnir/CMakeLists.txt @@ -77,7 +77,7 @@ valhalla_module(NAME mjolnir ${VALHALLA_SOURCE_DIR}/valhalla ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> - ${PROTOBUF_INCLUDE_DIR} + ${PROTOBUF_INCLUDE_DIRS} PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/valhalla @@ -88,7 +88,7 @@ valhalla_module(NAME mjolnir DEPENDS valhalla::proto valhalla::baldr - SpatiaLite::SpatiaLite + PkgConfig::SpatiaLite SQLite3::SQLite3 Boost::boost Lua::Lua diff --git a/src/mjolnir/adminbuilder.cc b/src/mjolnir/adminbuilder.cc index 15c4e7552e..d602785776 100644 --- a/src/mjolnir/adminbuilder.cc +++ b/src/mjolnir/adminbuilder.cc @@ -142,8 +142,14 @@ void buffer_polygon(const polygon_t& polygon, multipolygon_t& multipolygon) { auto* outer_ring = geos_helper_t::from_striped_container(polygon.outer()); std::vector inner_rings; inner_rings.reserve(polygon.inners().size()); - for (const auto& inner : polygon.inners()) + + // annoying circleci apple clang bug, not reproducible on any other machine.. + // https://github.com/valhalla/valhalla/pull/4500/files#r1445039739 + auto unused_size = std::to_string(polygon.inners().size()); + + for (const auto& inner : polygon.inners()) { inner_rings.push_back(geos_helper_t::from_striped_container(inner)); + } auto* geos_poly = GEOSGeom_createPolygon(outer_ring, &inner_rings.front(), inner_rings.size()); auto* buffered = GEOSBuffer(geos_poly, 0, 8); GEOSNormalize(buffered); @@ -163,7 +169,8 @@ void buffer_polygon(const polygon_t& polygon, multipolygon_t& multipolygon) { break; } default: - throw std::runtime_error("Unusable geometry type after buffering"); + throw std::runtime_error("Unusable geometry type after buffering with inners size " + + unused_size); } GEOSGeom_destroy(geos_poly); GEOSGeom_destroy(buffered); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ff8a1309b7..0f2776ab32 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,7 +29,7 @@ target_include_directories(valhalla_test PUBLIC target_link_libraries(valhalla_test valhalla gtest gtest_main gmock pthread) if(ENABLE_DATA_TOOLS) - target_link_libraries(valhalla_test GEOS::GEOS) + target_link_libraries(valhalla_test PkgConfig::GEOS) endif() diff --git a/test/util_midgard.cc b/test/util_midgard.cc index 3f7b23d61b..6f66bec9e4 100644 --- a/test/util_midgard.cc +++ b/test/util_midgard.cc @@ -416,7 +416,8 @@ TEST(UtilMidgard, TestTrimPolylineWithFloatGeoPoint) { // Worst case is they may quantized at 1.69m intervals (for an epsilon change). // https://stackoverflow.com/a/28420164 // The length comparisons below do better than that, but not a lot. - constexpr double MAX_FLOAT_PRECISION = 0.05; // Should be good for 5cm at this lon/lat + constexpr double MAX_FLOAT_PRECISION = 0.07; // Should be good for 5cm at this lon/lat, + // also account for some float point inaccuracies auto clip = trim_polyline(line.begin(), line.end(), 0.f, 1.f); EXPECT_DOUBLE_EQ(length(clip.begin(), clip.end()), length(line.begin(), line.end())) From 4fb198d327629192c4bb535a9160a31e45861973 Mon Sep 17 00:00:00 2001 From: Nils Date: Wed, 24 Jan 2024 16:21:53 +0100 Subject: [PATCH 006/198] migrate CI to M1 (#4500) --- .circleci/config.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2503e26720..8ea18b4c87 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,7 +13,7 @@ commands: steps: - run: brew install autoconf automake protobuf cmake ccache libtool libspatialite pkg-config luajit curl wget czmq lz4 spatialite-tools unzip - run: sudo python3 -m pip install requests shapely "conan<2.0.0" - - run: git clone https://github.com/kevinkreiser/prime_server --recurse-submodules && cd prime_server && ./autogen.sh && ./configure && make -j8 && make install + - run: git clone https://github.com/kevinkreiser/prime_server --recurse-submodules && cd prime_server && ./autogen.sh && ./configure && make -j8 && sudo make install linux_deps: steps: - run: ./scripts/install-linux-deps.sh @@ -140,10 +140,9 @@ jobs: build-osx: executor: macos - resource_class: macos.x86.medium.gen2 + resource_class: macos.m1.medium.gen1 steps: - checkout - - check_ci_lint - mac_deps - run: git submodule sync && git submodule update --init - restore_cache: From 90ae30e00936c4857a6595a103acc522b1b3380f Mon Sep 17 00:00:00 2001 From: Attila Kesmarki <157377688+akesmarki@users.noreply.github.com> Date: Wed, 24 Jan 2024 21:28:34 +0100 Subject: [PATCH 007/198] Bugfix of issue: Config singleton multiple instantiation issue #4521 (#4523) Co-authored-by: akesmarki --- CHANGELOG.md | 1 + src/CMakeLists.txt | 2 ++ src/config.cc | 47 +++++++++++++++++++++++++++++++++++++++++++ valhalla/config.h | 50 +--------------------------------------------- 4 files changed, 51 insertions(+), 49 deletions(-) create mode 100644 src/config.cc diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e08a01fd5..52e4121b87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ * FIXED: time distance matrix was always using time zone of last settled edge id [#4494](https://github.com/valhalla/valhalla/pull/4494) * FIXED: log to stderr in valhalla_export_edges [#4498](https://github.com/valhalla/valhalla/pull/4498) * FIXED: set capped speed for truck at 90 KPH [#4493](https://github.com/valhalla/valhalla/pull/4493) + * FIXED: Config singleton multiple instantiation issue [#4521](https://github.com/valhalla/valhalla/pull/4521) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc31ee4117..b3b7086e61 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -184,11 +184,13 @@ endif() set(valhalla_hdrs ${VALHALLA_SOURCE_DIR}/valhalla/valhalla.h ${VALHALLA_SOURCE_DIR}/valhalla/worker.h + ${VALHALLA_SOURCE_DIR}/valhalla/config.h ${VALHALLA_SOURCE_DIR}/valhalla/filesystem.h ${VALHALLA_SOURCE_DIR}/valhalla/proto_conversions.h ) set(valhalla_src + config.cc worker.cc filesystem.cc proto_conversions.cc diff --git a/src/config.cc b/src/config.cc new file mode 100644 index 0000000000..36c2ce6d9b --- /dev/null +++ b/src/config.cc @@ -0,0 +1,47 @@ +#include "config.h" + +namespace { +struct config_singleton_t { +protected: + boost::property_tree::ptree config_; + + config_singleton_t() = delete; + + config_singleton_t(const std::string& config_file_or_inline) { + if (config_file_or_inline.empty()) { + throw std::runtime_error("Config singleton was not initialized before usage"); + } + + try { + if (std::filesystem::is_regular_file(config_file_or_inline)) { + rapidjson::read_json(config_file_or_inline, config_); + } else { + auto inline_config = std::stringstream(config_file_or_inline); + rapidjson::read_json(inline_config, config_); + } + } catch (const std::filesystem::filesystem_error& e) { + if (e.code() == std::errc::filename_too_long) { + auto inline_config = std::stringstream(config_file_or_inline); + rapidjson::read_json(inline_config, config_); + } else { + throw e; + } + } + } + +public: + config_singleton_t(config_singleton_t const&) = delete; + void operator=(const config_singleton_t&) = delete; + friend const boost::property_tree::ptree& + valhalla::config(const std::string& config_file_or_inline); +}; +} // namespace + +namespace valhalla { + +const boost::property_tree::ptree& config(const std::string& config_file_or_inline) { + static config_singleton_t instance(config_file_or_inline); + return instance.config_; +} + +}; // namespace valhalla diff --git a/valhalla/config.h b/valhalla/config.h index bfcd277fda..6536a203e5 100644 --- a/valhalla/config.h +++ b/valhalla/config.h @@ -39,55 +39,7 @@ #define PACKAGE_VERSION VALHALLA_VERSION namespace valhalla { -const boost::property_tree::ptree& config(const std::string&); -class ConfigUninitializedException : std::exception { -public: - const char* what() const throw() { - return "Config singleton was not initialized before usage"; - } -}; -} // namespace valhalla - -namespace { -struct config_singleton_t { -protected: - boost::property_tree::ptree config_; - - config_singleton_t() = delete; - config_singleton_t(const std::string& config_file_or_inline) { - if (config_file_or_inline.empty()) { - throw valhalla::ConfigUninitializedException(); - } +const boost::property_tree::ptree& config(const std::string& config_file_or_inline = ""); - try { - if (std::filesystem::is_regular_file(config_file_or_inline)) { - rapidjson::read_json(config_file_or_inline, config_); - } else { - auto inline_config = std::stringstream(config_file_or_inline); - rapidjson::read_json(inline_config, config_); - } - } catch (const std::filesystem::filesystem_error& e) { - if (e.code() == std::errc::filename_too_long) { - auto inline_config = std::stringstream(config_file_or_inline); - rapidjson::read_json(inline_config, config_); - } else { - throw e; - } - } - } - -public: - config_singleton_t(config_singleton_t const&) = delete; - void operator=(const config_singleton_t&) = delete; - friend const boost::property_tree::ptree& - valhalla::config(const std::string& config_file_or_inline); -}; -} // namespace - -namespace valhalla { -inline const boost::property_tree::ptree& config(const std::string& config_file_or_inline = "") { - static config_singleton_t instance(config_file_or_inline); - return instance.config_; -} } // namespace valhalla From 5fadcb69e12db23faf4826368aeac527f5c2db31 Mon Sep 17 00:00:00 2001 From: Greg Knisely Date: Thu, 25 Jan 2024 10:00:07 -0500 Subject: [PATCH 008/198] added aggregation logic to filter stage of tile building (#4512) --- CHANGELOG.md | 1 + README.md | 2 +- src/mjolnir/graphfilter.cc | 570 +++++++++++++++++++++++++++++++- test/gurka/test_graphfilter.cc | 582 +++++++++++++++++++++++++++++++++ valhalla/baldr/nodeinfo.h | 1 + 5 files changed, 1147 insertions(+), 9 deletions(-) create mode 100644 test/gurka/test_graphfilter.cc diff --git a/CHANGELOG.md b/CHANGELOG.md index 52e4121b87..065a6a92de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ * ADDED: Added a config option to sort nodes spatially during graph building [#4455](https://github.com/valhalla/valhalla/pull/4455) * ADDED: Timezone info in route and matrix responses [#4491](https://github.com/valhalla/valhalla/pull/4491) * CHANGED: use pkg-config to find spatialite & geos and remove our cmake modules; upgraded conan's boost to 1.83.0 in the process [#4253](https://github.com/valhalla/valhalla/pull/4253) + * ADDED: Added aggregation logic to filter stage of tile building [#4512](https://github.com/valhalla/valhalla/pull/4512) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/README.md b/README.md index 9c68fb50b9..1b9c575398 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Valhalla is an open source routing engine and accompanying libraries for use wit ## License -Valhalla, and all of the projects under the Valhalla organization, use the [MIT License](COPYING). Avatar/logo by [Jordan](https://www.instagram.com/jaykaydraws/). +Valhalla, and all of the projects under the Valhalla organization, use the [MIT License](COPYING). Avatar/logo by [Jordan](https://www.jaykaydraws.com/portfolio). OpenStreetMap data in the `./test/data` is licensed under [ODbL](https://opendatacommons.org/licenses/odbl/) and [copyrighted](https://www.openstreetmap.org/copyright) by OSM contributors. Additional information on licenses and other requirements concerning the data sources most frequently used by Valhalla can be found in [the docs](https://valhalla.github.io/valhalla/mjolnir/data_sources/). diff --git a/src/mjolnir/graphfilter.cc b/src/mjolnir/graphfilter.cc index 68d4b5fa30..d85da4e358 100644 --- a/src/mjolnir/graphfilter.cc +++ b/src/mjolnir/graphfilter.cc @@ -1,5 +1,6 @@ #include "mjolnir/graphfilter.h" #include "mjolnir/graphtilebuilder.h" +#include "mjolnir/util.h" #include #include @@ -18,6 +19,7 @@ #include "midgard/sequence.h" using namespace valhalla::baldr; +using namespace valhalla::midgard; using namespace valhalla::mjolnir; namespace { @@ -27,10 +29,184 @@ uint32_t n_original_nodes = 0; uint32_t n_filtered_edges = 0; uint32_t n_filtered_nodes = 0; uint32_t can_aggregate = 0; +uint32_t aggregated = 0; // Group wheelchair and pedestrian access together constexpr uint32_t kAllPedestrianAccess = (kPedestrianAccess | kWheelchairAccess); +bool CanAggregate(const DirectedEdge* de) { + if (de->start_restriction() || de->part_of_complex_restriction() || de->end_restriction() || + de->restrictions() || de->traffic_signal() || de->access_restriction()) { + return false; + } + return true; +} + +// ExpandFromNode and ExpandFromNodeInner is reused code from restriction builder with some slight +// modifications. We are using recursion for graph traversal. We have to make sure we don't loop back +// to ourselves, walk in the correct direction, have not already visited a node, etc. Once we meet our +// criteria or not we stop. +bool ExpandFromNode(GraphReader& reader, + std::list& shape, + GraphId& en, + const GraphId& from_node, + std::unordered_set& isos, + bool forward, + std::unordered_set& visited_nodes, + uint64_t& way_id, + const graph_tile_ptr& prev_tile, + GraphId prev_node, + GraphId current_node); + +/* + * Expand from the current node + * @param reader Graph reader. + * @param shape shape that we need to update + * @param en current end node that we started at + * @param from_node node that we started from + * @param isos country ISOs. Used to see if we cross into a new country + * @param forward traverse in the forward or backward direction + * @param visited_nodes nodes that we already visited. don't visit again + * @param way_id only interested in edges with this way_id + * @param prev_tile previous tile + * @param prev_node previous node + * @param current_node current node + * @param node_info current node's info + * + */ +bool ExpandFromNodeInner(GraphReader& reader, + std::list& shape, + GraphId& en, + const GraphId& from_node, + std::unordered_set& isos, + bool forward, + std::unordered_set& visited_nodes, + uint64_t& way_id, + const graph_tile_ptr& prev_tile, + GraphId prev_node, + GraphId current_node, + const NodeInfo* node_info) { + + for (size_t j = 0; j < node_info->edge_count(); ++j) { + GraphId edge_id(prev_tile->id().tileid(), prev_tile->id().level(), node_info->edge_index() + j); + const DirectedEdge* de = prev_tile->directededge(edge_id); + const auto& edge_info = prev_tile->edgeinfo(de); + + auto tile = prev_tile; + if (tile->id() != de->endnode().Tile_Base()) { + tile = reader.GetGraphTile(de->endnode()); + } + + const NodeInfo* en_info = tile->node(de->endnode().id()); + + // check the direction, if we looped back, or are we done + if ((de->endnode() != prev_node) && (de->forward() == forward) && + (de->endnode() != from_node || (de->endnode() == from_node && visited_nodes.size() > 1))) { + if (edge_info.wayid() == way_id && + (en_info->mode_change() || (node_info->mode_change() && !en_info->mode_change()))) { + + // If this edge has special attributes, then we can't aggregate + if (!CanAggregate(de)) { + way_id = 0; + return false; + } + + if (isos.size() >= 1) { + isos.insert(tile->admin(en_info->admin_index())->country_iso()); + } else { + std::list edgeshape = + valhalla::midgard::decode7>(edge_info.encoded_shape()); + if (!de->forward()) { + std::reverse(edgeshape.begin(), edgeshape.end()); + } + + // Append shape. Skip first point since it + // should equal the last of the prior edge. + edgeshape.pop_front(); + shape.splice(shape.end(), edgeshape); + } + + // found a node that does not have aggregation marked (using mode_change flag) + // we are done. + if (node_info->mode_change() && !en_info->mode_change()) { + en = de->endnode(); + aggregated++; + return true; + } + aggregated++; + + bool found; + if (visited_nodes.find(de->endnode()) == visited_nodes.end()) { + visited_nodes.insert(de->endnode()); + + // expand with the same way_id + found = ExpandFromNode(reader, shape, en, from_node, isos, forward, visited_nodes, way_id, + tile, current_node, de->endnode()); + if (found) { + return true; + } + + visited_nodes.erase(de->endnode()); + } + } + } + } + return false; +} + +/* + * Expand from the next node which is now our new current node + * @param reader Graph reader. + * @param shape shape that we need to update + * @param en current end node that we started at + * @param from_node node that we started from + * @param isos country ISOs. Used to see if we cross into a new country + * @param forward traverse in the forward or backward direction + * @param visited_nodes nodes that we already visited. don't visit again + * @param way_id only interested in edges with this way_id + * @param prev_tile previous tile + * @param prev_node previous node + * @param current_node current node + * + */ +bool ExpandFromNode(GraphReader& reader, + std::list& shape, + GraphId& en, + const GraphId& from_node, + std::unordered_set& isos, + bool forward, + std::unordered_set& visited_nodes, + uint64_t& way_id, + const graph_tile_ptr& prev_tile, + GraphId prev_node, + GraphId current_node) { + + auto tile = prev_tile; + if (tile->id() != current_node.Tile_Base()) { + tile = reader.GetGraphTile(current_node); + } + + auto* node_info = tile->node(current_node); + // expand from the current node + return ExpandFromNodeInner(reader, shape, en, from_node, isos, forward, visited_nodes, way_id, tile, + prev_node, current_node, node_info); +} + +bool Aggregate(GraphId& start_node, + GraphReader& reader, + std::list& shape, + GraphId& en, + const GraphId& from_node, + uint64_t& way_id, + std::unordered_set& isos, + bool forward) { + + graph_tile_ptr tile = reader.GetGraphTile(start_node); + std::unordered_set visited_nodes{start_node}; + return ExpandFromNode(reader, shape, en, from_node, isos, forward, visited_nodes, way_id, tile, + GraphId(), start_node); +} + /** * Filter edges to optionally remove edges by access. * @param reader Graph reader. @@ -75,7 +251,8 @@ void FilterTiles(GraphReader& reader, GraphId nodeid(tile_id.tileid(), tile_id.level(), 0); for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { bool diff_names = false; - + bool diff_tile = false; + bool edge_filtered = false; // Count of edges added for this node uint32_t edge_count = 0; @@ -86,12 +263,15 @@ void FilterTiles(GraphReader& reader, std::vector wayid; std::vector endnode; const NodeInfo* nodeinfo = tile->node(nodeid); + std::string begin_node_iso = tile->admin(nodeinfo->admin_index())->country_iso(); + GraphId edgeid(nodeid.tileid(), nodeid.level(), nodeinfo->edge_index()); for (uint32_t j = 0; j < nodeinfo->edge_count(); ++j, ++edgeid) { // Check if the directed edge should be included const DirectedEdge* directededge = tile->directededge(edgeid); if (!include_edge(directededge)) { ++n_filtered_edges; + edge_filtered = true; continue; } @@ -147,7 +327,7 @@ void FilterTiles(GraphReader& reader, // highway hierarchy can cross base tiles! Use a hash based on the // encoded shape plus way Id. bool added; - auto edgeinfo = tile->edgeinfo(directededge); + const auto& edgeinfo = tile->edgeinfo(directededge); std::string encoded_shape = edgeinfo.encoded_shape(); uint32_t w = hasher(encoded_shape + std::to_string(edgeinfo.wayid())); uint32_t edge_info_offset = @@ -160,6 +340,10 @@ void FilterTiles(GraphReader& reader, wayid.push_back(edgeinfo.wayid()); endnode.push_back(directededge->endnode()); + if (directededge->endnode().tile_value() != tile->header()->graphid().tile_value()) { + diff_tile = true; + } + // Add directed edge tilebuilder.directededges().emplace_back(std::move(newedge)); ++edge_count; @@ -191,9 +375,26 @@ void FilterTiles(GraphReader& reader, old_to_new[nodeid] = new_node; // Check if edges at this node can be aggregated. Only 2 edges, same way Id (so that - // edge attributes should match), don't end at same node (no loops). - if (edge_count == 2 && wayid[0] == wayid[1] && endnode[0] != endnode[1] && !diff_names) { - ++can_aggregate; + // edge attributes should match), don't end at same node (no loops), no traffic signal, + // no signs exist at the node(named_intersection), does not have different + // names, and end node of edges are not in a different tile. + if (edge_filtered && edge_count == 2 && wayid[0] == wayid[1] && endnode[0] != endnode[1] && + !nodeinfo->traffic_signal() && !nodeinfo->named_intersection() && !diff_names && + !diff_tile) { + + // one more check on intersection and node type. similar to shortcuts + bool aggregate = + (nodeinfo->intersection() != IntersectionType::kFork && + nodeinfo->type() != NodeType::kGate && nodeinfo->type() != NodeType::kTollBooth && + nodeinfo->type() != NodeType::kTollGantry && nodeinfo->type() != NodeType::kBollard && + nodeinfo->type() != NodeType::kSumpBuster && + nodeinfo->type() != NodeType::kBorderControl); + + if (aggregate) { + // temporarily used to check aggregating edges from this node + node.set_mode_change(true); + ++can_aggregate; + } } } else { ++n_filtered_nodes; @@ -219,7 +420,353 @@ void FilterTiles(GraphReader& reader, std::to_string(n_original_nodes)); LOG_INFO("Filtered " + std::to_string(n_filtered_edges) + " directededges out of " + std::to_string(n_original_edges)); - LOG_INFO("Can aggregate: " + std::to_string(can_aggregate)); + LOG_INFO("Nodes to aggregate: " + std::to_string(can_aggregate)); +} + +void GetAggregatedData(GraphReader& reader, + std::list& shape, + GraphId& en, + const GraphId& from_node, + const graph_tile_ptr& tile, + const DirectedEdge* directededge) { + + // Get the tile at the end node. Skip if node is in another tile. + // mode_change is not set for end nodes that are in diff tiles + if (directededge->endnode().tile_value() == tile->header()->graphid().tile_value()) { + + // original edge data. + const auto& edgeinfo = tile->edgeinfo(directededge); + const NodeInfo* en_info = tile->node(directededge->endnode().id()); + + // do we need to aggregate? + if (en_info->mode_change()) { + std::unordered_set isos; + bool isForward = directededge->forward(); + auto id = directededge->endnode(); + if (!isForward) { + std::reverse(shape.begin(), shape.end()); + } + // walk in the correct direction. + uint64_t wayid = edgeinfo.wayid(); + if (Aggregate(id, reader, shape, en, from_node, wayid, isos, isForward)) { + aggregated++; // count the current edge + // flip the shape back for storing in edgeinfo + if (!isForward) { + std::reverse(shape.begin(), shape.end()); + } + } + } + } +} + +// If we cross into another country we can't aggregate the edges +// as the access can be different in each country. Also, bollards +// and or gates could exist at the node blocking access. +// +// Also, we need to handle islands that we created by tossing the +// pedestrian edges. For example:// +// https://www.openstreetmap.org/way/993706522 +// https://www.openstreetmap.org/way/975845893 +// As of 01/15/2024 there are only ~180 of these. + +void ValidateData(GraphReader& reader, + std::list& shape, + GraphId& en, + std::unordered_set& processed_nodes, + std::unordered_set& no_agg_ways, + const GraphId& from_node, + const graph_tile_ptr& tile, + const DirectedEdge* directededge) { + + // Get the tile at the end node. Skip if node is in another tile. + // mode_change is not set for end nodes that are in diff tiles + if (directededge->endnode().tile_value() == tile->header()->graphid().tile_value()) { + + // original edge data. + const auto& edgeinfo = tile->edgeinfo(directededge); + const NodeInfo* en_info = tile->node(directededge->endnode().id()); + const NodeInfo* sn_info = tile->node(from_node); + + if (en_info->mode_change()) { + + // If this edge has special attributes, then we can't aggregate + if (!CanAggregate(directededge)) { + processed_nodes.insert(directededge->endnode()); + no_agg_ways.insert(edgeinfo.wayid()); + return; + } + + std::unordered_set isos; + bool isForward = directededge->forward(); + auto id = directededge->endnode(); + + isos.insert(tile->admin(sn_info->admin_index())->country_iso()); // start node + isos.insert(tile->admin(en_info->admin_index())->country_iso()); // end node + + if (isos.size() > 1) { // already in diff country + processed_nodes.insert(directededge->endnode()); + return; + } + + // walk in the correct direction. + uint64_t wayid = edgeinfo.wayid(); + if (!Aggregate(id, reader, shape, en, from_node, wayid, isos, isForward)) { + // LOG_WARN("ValidateData - failed to validate node. Will not aggregate."); + // for debugging only + // std::cout << "End node: " << directededge->endnode().value << " WayId: " << + // edgeinfo.wayid() + // << std::endl; + + if (wayid == 0) { // This edge has special attributes, we can't aggregate + no_agg_ways.insert(edgeinfo.wayid()); + } + + processed_nodes.insert(directededge->endnode()); // turn off so that we don't fail + } else if (isos.size() > 1) { // in diff country + processed_nodes.insert(directededge->endnode()); + } + } + } +} + +void AggregateTiles(GraphReader& reader, std::unordered_map& old_to_new) { + + LOG_INFO("Validating edges for aggregation"); + // Iterate through all tiles in the local level + auto local_tiles = reader.GetTileSet(TileHierarchy::levels().back().level); + for (const auto& tile_id : local_tiles) { + // Get the graph tile. Read from this tile to create the new tile. + graph_tile_ptr tile = reader.GetGraphTile(tile_id); + assert(tile); + + std::unordered_set processed_nodes; + std::unordered_set no_agg_ways; + processed_nodes.reserve(tile->header()->nodecount()); + no_agg_ways.reserve(tile->header()->directededgecount()); + + GraphId nodeid = GraphId(tile_id.tileid(), tile_id.level(), 0); + for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + const NodeInfo* nodeinfo = tile->node(i); + uint32_t idx = nodeinfo->edge_index(); + for (uint32_t j = 0; j < nodeinfo->edge_count(); j++, idx++) { + const DirectedEdge* directededge = tile->directededge(idx); + if (processed_nodes.find(nodeid) == processed_nodes.end()) { + GraphId en = directededge->endnode(); + std::list shape; + // check if we can aggregate the edges at this node. + ValidateData(reader, shape, en, processed_nodes, no_agg_ways, nodeid, tile, directededge); + } + } + } + + // Now loop again double checking the ways. + nodeid = GraphId(tile_id.tileid(), tile_id.level(), 0); + for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + const NodeInfo* nodeinfo = tile->node(i); + uint32_t idx = nodeinfo->edge_index(); + for (uint32_t j = 0; j < nodeinfo->edge_count(); j++, idx++) { + const DirectedEdge* directededge = tile->directededge(idx); + if (no_agg_ways.find(tile->edgeinfo(directededge).wayid()) != no_agg_ways.end()) { + processed_nodes.insert(directededge->endnode()); + } + } + } + + // Create a new tile builder + GraphTileBuilder tilebuilder(reader.tile_dir(), tile_id, false); + std::vector nodes; + + // Copy edges (they do not change) + std::vector directededges; + size_t n = tile->header()->directededgecount(); + directededges.reserve(n); + const DirectedEdge* orig_edges = tile->directededge(0); + std::copy(orig_edges, orig_edges + n, std::back_inserter(directededges)); + + nodeid = GraphId(tile_id.tileid(), tile_id.level(), 0); + for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + NodeInfo nodeinfo = tilebuilder.node(i); + bool found = (processed_nodes.find(nodeid) != processed_nodes.end()); + + // We can not aggregate at this node. Turn off the mode change(aggregation) bit + if (found) { + nodeinfo.set_mode_change(false); + } + // Add the node to the local list + nodes.emplace_back(std::move(nodeinfo)); + } + tilebuilder.Update(nodes, directededges); + + if (reader.OverCommitted()) { + reader.Trim(); + } + } + + LOG_INFO("Aggregating edges"); + reader.Clear(); + // Iterate through all tiles in the local level + local_tiles = reader.GetTileSet(TileHierarchy::levels().back().level); + // Iterate through all tiles in the local level + for (const auto& tile_id : local_tiles) { + // Create a new tilebuilder - should copy header information + GraphTileBuilder tilebuilder(reader.tile_dir(), tile_id, false); + + // Get the graph tile. Read from this tile to create the new tile. + graph_tile_ptr tile = reader.GetGraphTile(tile_id); + assert(tile); + + std::hash hasher; + GraphId nodeid(tile_id.tileid(), tile_id.level(), 0); + for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + bool diff_names = false; + + // Count of edges added for this node + uint32_t edge_count = 0; + + // Current edge index for first edge from this node + uint32_t edge_index = tilebuilder.directededges().size(); + + // Iterate through directed edges outbound from this node + std::vector wayid; + std::vector endnode; + const NodeInfo* nodeinfo = tile->node(nodeid); + + // Nodes marked with mode_change = true are tossed. + if (nodeinfo->mode_change()) { + continue; + } + + GraphId edgeid(nodeid.tileid(), nodeid.level(), nodeinfo->edge_index()); + + for (uint32_t j = 0; j < nodeinfo->edge_count(); ++j, ++edgeid) { + // Check if the directed edge should be included + const DirectedEdge* directededge = tile->directededge(edgeid); + + // Copy the directed edge information + DirectedEdge newedge = *directededge; + + // Set opposing edge indexes to 0 (gets set in graph validator). + newedge.set_opp_index(0); + + // Get signs from the base directed edge + if (directededge->sign()) { + std::vector signs = tile->GetSigns(edgeid.id()); + if (signs.size() == 0) { + LOG_ERROR("Base edge should have signs, but none found"); + } + tilebuilder.AddSigns(tilebuilder.directededges().size(), signs); + } + + // Get turn lanes from the base directed edge + if (directededge->turnlanes()) { + uint32_t offset = tile->turnlanes_offset(edgeid.id()); + tilebuilder.AddTurnLanes(tilebuilder.directededges().size(), tile->GetName(offset)); + } + + // Get access restrictions from the base directed edge. Add these to + // the list of access restrictions in the new tile. Update the + // edge index in the restriction to be the current directed edge Id + if (directededge->access_restriction()) { + auto restrictions = tile->GetAccessRestrictions(edgeid.id(), kAllAccess); + for (const auto& res : restrictions) { + tilebuilder.AddAccessRestriction(AccessRestriction(tilebuilder.directededges().size(), + res.type(), res.modes(), res.value())); + } + } + + // Copy lane connectivity + if (directededge->laneconnectivity()) { + auto laneconnectivity = tile->GetLaneConnectivity(edgeid.id()); + if (laneconnectivity.size() == 0) { + LOG_ERROR("Base edge should have lane connectivity, but none found"); + } + for (auto& lc : laneconnectivity) { + lc.set_to(tilebuilder.directededges().size()); + } + tilebuilder.AddLaneConnectivity(laneconnectivity); + } + + // Names can be different in the forward and backward direction + diff_names = tilebuilder.OpposingEdgeInfoDiffers(tile, directededge); + + bool added; + const auto& edgeinfo = tile->edgeinfo(directededge); + std::string encoded_shape = edgeinfo.encoded_shape(); + std::list shape = valhalla::midgard::decode7>(encoded_shape); + + GraphId en = directededge->endnode(); + // Do the actual aggregation. + GetAggregatedData(reader, shape, en, nodeid, tile, directededge); + + newedge.set_endnode(en); + encoded_shape = encode7(shape); + uint32_t w = hasher(encoded_shape + std::to_string(edgeinfo.wayid())); + uint32_t edge_info_offset = + tilebuilder.AddEdgeInfo(w, nodeid, en, edgeinfo.wayid(), edgeinfo.mean_elevation(), + edgeinfo.bike_network(), edgeinfo.speed_limit(), encoded_shape, + edgeinfo.GetNames(), edgeinfo.GetTaggedValues(), + edgeinfo.GetLinguisticTaggedValues(), edgeinfo.GetTypes(), added, + diff_names); + + // length + auto length = valhalla::midgard::length(shape); + + // Compute a curvature metric [0-15]. TODO - use resampled polyline? + uint32_t curvature = compute_curvature(shape); + + newedge.set_length(length); + newedge.set_curvature(curvature); + + newedge.set_edgeinfo_offset(edge_info_offset); + // Add directed edge + tilebuilder.directededges().emplace_back(std::move(newedge)); + ++edge_count; + } + + // Add the node to the tilebuilder unless no edges remain + if (edge_count > 0) { + // Add a node builder to the tile. Update the edge count and edgeindex + GraphId new_node(nodeid.tileid(), nodeid.level(), tilebuilder.nodes().size()); + tilebuilder.nodes().push_back(*nodeinfo); + NodeInfo& node = tilebuilder.nodes().back(); + node.set_edge_count(edge_count); + node.set_edge_index(edge_index); + const auto& admin = tile->admininfo(nodeinfo->admin_index()); + node.set_admin_index(tilebuilder.AddAdmin(admin.country_text(), admin.state_text(), + admin.country_iso(), admin.state_iso())); + + // Get named signs from the base node + if (nodeinfo->named_intersection()) { + std::vector signs = tile->GetSigns(nodeid.id(), true); + if (signs.size() == 0) { + LOG_ERROR("Base node should have signs, but none found"); + } + node.set_named_intersection(true); + tilebuilder.AddSigns(tilebuilder.nodes().size() - 1, signs); + } + // Associate the old node to the new node. + old_to_new[nodeid] = new_node; + } + } + + // Store the updated tile data (or remove tile if all edges are filtered) + if (tilebuilder.nodes().size() > 0) { + tilebuilder.StoreTileData(); + } else { + // Remove the tile - all nodes and edges were filtered + std::string file_location = + reader.tile_dir() + filesystem::path::preferred_separator + GraphTile::FileSuffix(tile_id); + remove(file_location.c_str()); + LOG_INFO("Remove file: " + file_location + " all edges were filtered"); + } + + if (reader.OverCommitted()) { + reader.Trim(); + } + } + + LOG_INFO("Aggregated " + std::to_string(aggregated) + " directededges out of " + + std::to_string(n_original_edges)); } /** @@ -258,6 +805,8 @@ void UpdateEndNodes(GraphReader& reader, std::unordered_map& o auto iter = old_to_new.find(edge->endnode()); if (iter == old_to_new.end()) { LOG_ERROR("UpdateEndNodes - failed to find associated node"); + std::cout << std::to_string(edge->endnode().value) << " " + << std::to_string(tile->edgeinfo(edge).wayid()) << std::endl; } else { end_node = iter->second; } @@ -315,8 +864,13 @@ void GraphFilter::Filter(const boost::property_tree::ptree& pt) { // Filter edges (and nodes) by access FilterTiles(reader, old_to_new, include_driving, include_bicycle, include_pedestrian); - // TODO - aggregate / combine edges across false nodes (only 2 directed edges) - // where way Ids are equal + // Update end nodes. Clear the GraphReader cache first. + reader.Clear(); + UpdateEndNodes(reader, old_to_new); + + reader.Clear(); + old_to_new.clear(); + AggregateTiles(reader, old_to_new); // Update end nodes. Clear the GraphReader cache first. reader.Clear(); diff --git a/test/gurka/test_graphfilter.cc b/test/gurka/test_graphfilter.cc new file mode 100644 index 0000000000..75c541c241 --- /dev/null +++ b/test/gurka/test_graphfilter.cc @@ -0,0 +1,582 @@ +#include "baldr/graphreader.h" +#include "mjolnir/util.h" + +#include "gurka.h" +#include "test/test.h" + +using namespace valhalla; +using namespace valhalla::baldr; +using namespace valhalla::gurka; + +const std::string work_dir = {VALHALLA_BUILD_DIR "test/data/gurka_graphfilter"}; + +TEST(Standalone, SimpleFilter) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + + M P R W Y + | | | | | + | | | | | + A----B----C----D----E----F----G----H----I----J----K----L + | | | | | | + | | Q----S----T | | + N O | V X + | + U + )"; + + const gurka::ways ways = { + {"ABCDEFGHIJKL", {{"highway", "residential"}, {"osm_id", "100"}, {"name", "East Fort Avenue"}}}, + {"BM", {{"highway", "residential"}, {"osm_id", "101"}, {"name", "Hull Street"}}}, + {"CN", + {{"highway", "residential"}, + {"osm_id", "102"}, + {"name", "Latrobe Park Terrace"}, + {"oneway", "yes"}}}, + {"DO", {{"highway", "footway"}, {"osm_id", "103"}, {"foot", "yes"}}}, + {"EP", + {{"highway", "residential"}, + {"osm_id", "104"}, + {"name", "Cooksie Street"}, + {"oneway", "yes"}}}, + {"FQ", {{"highway", "footway"}, {"osm_id", "105"}, {"foot", "yes"}}}, + {"QST", {{"highway", "footway"}, {"osm_id", "106"}, {"foot", "yes"}}}, + {"SU", {{"highway", "footway"}, {"osm_id", "107"}, {"foot", "yes"}}}, + {"GR", {{"highway", "residential"}, {"osm_id", "108"}, {"name", "Towson Street"}}}, + {"HT", {{"highway", "footway"}, {"osm_id", "109"}, {"foot", "yes"}}}, + {"IV", {{"highway", "footway"}, {"osm_id", "110"}, {"foot", "yes"}}}, + {"JW", {{"highway", "residential"}, {"osm_id", "111"}, {"name", "Richardson Street"}}}, + {"YKX", {{"highway", "residential"}, {"osm_id", "112"}, {"name", "Andre Street"}}}, + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-76.59223, 39.26825}); + auto map = + gurka::buildtiles(layout, ways, {}, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + + // all footways will be deleted. check for DO + { + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + GraphId DO_edge_id; + const DirectedEdge* DO_edge = nullptr; + GraphId OD_edge_id; + const DirectedEdge* OD_edge = nullptr; + std::tie(DO_edge_id, DO_edge, OD_edge_id, OD_edge) = + findEdge(graph_reader, map.nodes, "DO", "O", baldr::GraphId{}); + EXPECT_EQ(DO_edge, nullptr); + EXPECT_EQ(OD_edge, nullptr); + } + + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "L"}, "auto"); + gurka::assert::raw::expect_path(result, {"East Fort Avenue", "East Fort Avenue", "East Fort Avenue", + "East Fort Avenue", "East Fort Avenue", "East Fort Avenue", + "East Fort Avenue"}); + + // reconvert again, but include pedestrian edges + map = gurka::buildtiles(layout, ways, {}, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "true"}}); + + // all footways will not be deleted + { + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + GraphId DO_edge_id; + const DirectedEdge* DO_edge = nullptr; + GraphId OD_edge_id; + const DirectedEdge* OD_edge = nullptr; + std::tie(DO_edge_id, DO_edge, OD_edge_id, OD_edge) = + findEdge(graph_reader, map.nodes, "DO", "O", baldr::GraphId{}); + EXPECT_NE(DO_edge, nullptr); + EXPECT_NE(OD_edge, nullptr); + } + + // more edges will exist + result = gurka::do_action(valhalla::Options::route, map, {"A", "L"}, "auto"); + gurka::assert::raw::expect_path(result, {"East Fort Avenue", "East Fort Avenue", "East Fort Avenue", + "East Fort Avenue", "East Fort Avenue", "East Fort Avenue", + "East Fort Avenue", "East Fort Avenue", "East Fort Avenue", + "East Fort Avenue", "East Fort Avenue"}); +} + +TEST(Standalone, SimpleFilter2) { + + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + + J----K----L----M----------------------N + | | | | | + | H I | | + | | | | | + A----B----C----D----E------------F----G + | | \ / | + | | \ / | + O P Q------R S + )"; + + const gurka::ways ways = { + {"JKLMN", + {{"highway", "secondary"}, + {"osm_id", "100"}, + {"bicycle", "no"}, + {"foot", "no"}, + {"name", "Vondellaan"}, + {"oneway", "yes"}}}, + {"ABCDEFG", {{"highway", "cycleway"}, {"osm_id", "101"}, {"oneway", "yes"}}}, + {"JAO", + {{"highway", "residential"}, + {"osm_id", "102"}, + {"bicycle", "no"}, + {"foot", "no"}, + {"name", "Croesestraat"}, + {"oneway", "yes"}, + {"oneway:bicycle", "no"}}}, + {"BHK", {{"highway", "cycleway"}, {"osm_id", "103"}, {"oneway", "yes"}}}, + {"CIL", {{"highway", "footway"}, {"osm_id", "104"}}}, + {"MDP", {{"highway", "service"}, {"osm_id", "105"}, {"access", "private"}}}, + {"EQRF", + {{"highway", "service"}, {"osm_id", "106"}, {"access", "delivery"}, {"oneway", "yes"}}}, + {"NG", {{"highway", "unclassified"}, {"osm_id", "107"}, {"name", "Oosterkade"}}}, + {"GS", {{"highway", "cycleway"}, {"osm_id", "108"}}}, + }; + + const gurka::nodes nodes = { + {"H", {{"highway", "traffic_signals"}}}, + {"I", {{"highway", "traffic_signals"}, {"traffic_signals:direction", "backward"}}}, + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {5.11668, 52.07826}); + auto map = + gurka::buildtiles(layout, ways, nodes, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + + // CIL should be deleted + { + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + GraphId CL_edge_id; + const DirectedEdge* CL_edge = nullptr; + GraphId LC_edge_id; + const DirectedEdge* LC_edge = nullptr; + std::tie(CL_edge_id, CL_edge, LC_edge_id, LC_edge) = + findEdge(graph_reader, map.nodes, "CIL", "L", baldr::GraphId{}); + EXPECT_EQ(CL_edge, nullptr); + EXPECT_EQ(LC_edge, nullptr); + } + + auto result = gurka::do_action(valhalla::Options::route, map, {"J", "N"}, "auto"); + gurka::assert::raw::expect_path(result, {"Vondellaan", "Vondellaan", "Vondellaan"}); + + // reconvert again, but include pedestrian edges + map = gurka::buildtiles(layout, ways, nodes, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "true"}}); + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // CIL should not be deleted + { + GraphId CL_edge_id; + const DirectedEdge* CL_edge = nullptr; + GraphId LC_edge_id; + const DirectedEdge* LC_edge = nullptr; + std::tie(CL_edge_id, CL_edge, LC_edge_id, LC_edge) = + findEdge(graph_reader, map.nodes, "CIL", "L", baldr::GraphId{}); + EXPECT_NE(CL_edge, nullptr); + EXPECT_NE(LC_edge, nullptr); + } + + // should have an extra edge since CIL was not deleted + result = gurka::do_action(valhalla::Options::route, map, {"J", "N"}, "auto"); + gurka::assert::raw::expect_path(result, {"Vondellaan", "Vondellaan", "Vondellaan", "Vondellaan"}); +} + +TEST(Standalone, FilterTestComplexRestrictionsSignals) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + L + | + | + M--N + | | + | | + A------------------B--C----D-----------E + | | | + F------------------G--H----I-----------J + | + | + K + )"; + + const gurka::ways ways = { + {"ED", + {{"highway", "primary"}, + {"osm_id", "100"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"DCB", + {{"highway", "primary"}, + {"osm_id", "101"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"BA", + {{"highway", "primary"}, + {"osm_id", "102"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"AF", {{"highway", "tertiary"}, {"osm_id", "103"}}}, + {"FG", + {{"highway", "primary"}, + {"osm_id", "104"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"GHI", + {{"highway", "primary"}, + {"osm_id", "105"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"IJ", + {{"highway", "primary"}, + {"osm_id", "106"}, + {"oneway", "yes"}, + {"name", "East Houston Street"}}}, + {"LMB", {{"highway", "tertiary"}, {"osm_id", "107"}, {"name", "Avenue B"}}}, + {"BG", {{"highway", "tertiary"}, {"osm_id", "108"}, {"name", "Avenue B"}}}, + {"GK", {{"highway", "tertiary"}, {"osm_id", "109"}, {"name", "Avenue B"}}}, + {"MN", {{"highway", "footway"}, {"osm_id", "110"}, {"foot", "yes"}}}, + {"NCH", {{"highway", "footway"}, {"osm_id", "111"}, {"foot", "yes"}}}, + }; + + const gurka::relations relations = { + {{ + {gurka::way_member, "DCB", "from"}, + {gurka::way_member, "BG", "via"}, + {gurka::way_member, "GHI", "to"}, + }, + { + {"type", "restriction"}, + {"restriction", "no_u_turn"}, + }}, + }; + + const gurka::nodes nodes = { + {"M", {{"highway", "traffic_signals"}}}, + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-73.98311, 40.72122}); + auto map = + gurka::buildtiles(layout, ways, nodes, relations, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // even though we filitered pedestrian edges, make sure the restriction is not deleted. + auto result = gurka::do_action(valhalla::Options::route, map, {"E", "I"}, "auto"); + gurka::assert::raw::expect_path(result, {"East Houston Street", "East Houston Street", + "East Houston Street", "East Houston Street", "AF", + "East Houston Street", "East Houston Street", + "East Houston Street"}); + + // even though we filitered pedestrian edges, make sure we don't split at the traffic signal + result = gurka::do_action(valhalla::Options::route, map, {"L", "K"}, "auto"); + gurka::assert::raw::expect_path(result, {"Avenue B", "Avenue B", "Avenue B", "Avenue B"}); +} + +TEST(Standalone, FilterTestSimpleRestrictions) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + + + H----------D----------I + / \ + / \ + J-------E-----C-------K + / \ + F B + | | + | | + | | + G A + )"; + + const gurka::ways ways = { + {"ABCD", + {{"highway", "primary"}, {"osm_id", "100"}, {"oneway", "yes"}, {"name", "Washington Avenue"}}}, + {"DEFG", + {{"highway", "primary"}, {"osm_id", "101"}, {"oneway", "yes"}, {"name", "Washington Avenue"}}}, + {"HD", {{"highway", "tertiary"}, {"osm_id", "102"}, {"name", "State Street"}}}, + {"DI", {{"highway", "tertiary"}, {"osm_id", "103"}, {"name", "T Street"}}}, + {"JECK", {{"highway", "footway"}, {"osm_id", "104"}, {"foot", "yes"}}}, + }; + + const gurka::relations relations = { + {{ + {gurka::way_member, "ABCD", "from"}, + {gurka::node_member, "D", "via"}, + {gurka::way_member, "DEFG", "to"}, + }, + { + {"type", "restriction"}, + {"restriction", "no_u_turn"}, + }}, + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-73.94913, 42.81490}); + auto map = + gurka::buildtiles(layout, ways, {}, relations, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // even though we filitered pedestrian edges, make sure the simple restriction is not deleted. We + // make a uturn at the H node + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto"); + gurka::assert::raw::expect_path(result, {"Washington Avenue", "Washington Avenue", "State Street", + "State Street", "Washington Avenue"}); + + // JECK should be deleted + { + GraphId JK_edge_id; + const DirectedEdge* JK_edge = nullptr; + GraphId KJ_edge_id; + const DirectedEdge* KJ_edge = nullptr; + std::tie(JK_edge_id, JK_edge, KJ_edge_id, KJ_edge) = + findEdge(graph_reader, map.nodes, "JECK", "K", baldr::GraphId{}); + EXPECT_EQ(JK_edge, nullptr); + EXPECT_EQ(KJ_edge, nullptr); + } +} + +TEST(Standalone, FilterTestNodeTypeSignals) { + constexpr double gridsize_metres = 10; + + const std::string ascii_map = R"( + + F + | + G--H + | | + | | + A----------B--C-------D + | + | + E + + )"; + + const gurka::ways ways = { + {"ABCD", {{"highway", "secondary"}, {"osm_id", "100"}}}, + {"FGBE", {{"highway", "secondary"}, {"osm_id", "101"}}}, + {"GHC", {{"highway", "footway"}, {"osm_id", "102"}, {"foot", "yes"}}}, + }; + + const gurka::nodes nodes = { + {"C", {{"highway", "traffic_signals"}}}, + {"G", {{"barrier", "gate"}}}, + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-73.94913, 42.81490}); + auto map = + gurka::buildtiles(layout, ways, nodes, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // even though we filitered pedestrian edges there should be 3 edges as ABCD has a signal set on the + // edge + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "auto"); + gurka::assert::raw::expect_path(result, {"ABCD", "ABCD", "ABCD"}); + + // even though we filitered pedestrian edges there should be 3 edges as G is a gate + result = gurka::do_action(valhalla::Options::route, map, {"F", "E"}, "auto"); + gurka::assert::raw::expect_path(result, {"FGBE", "FGBE", "FGBE"}); + + // GHC should be deleted + { + GraphId GC_edge_id; + const DirectedEdge* GC_edge = nullptr; + GraphId CG_edge_id; + const DirectedEdge* CG_edge = nullptr; + std::tie(GC_edge_id, GC_edge, CG_edge_id, CG_edge) = + findEdge(graph_reader, map.nodes, "GHC", "C", baldr::GraphId{}); + EXPECT_EQ(GC_edge, nullptr); + EXPECT_EQ(CG_edge, nullptr); + } +} + +TEST(Standalone, FilterTestMultipleEdges) { + constexpr double gridsize_metres = 50; + + const std::string ascii_map = R"( + + A---B---C---D---E---F---G---H---I + | | | | | | | + | | | | | | | + J K L M N O P + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "secondary"}, {"osm_id", "100"}}}, + {"BC", {{"highway", "secondary"}, {"osm_id", "101"}}}, + {"CD", {{"highway", "secondary"}, {"osm_id", "102"}}}, + {"DE", {{"highway", "secondary"}, {"osm_id", "103"}}}, + {"EF", {{"highway", "secondary"}, {"osm_id", "104"}}}, + // note these next 2 have same way id + {"FG", {{"highway", "secondary"}, {"osm_id", "105"}}}, + {"GH", {{"highway", "secondary"}, {"osm_id", "105"}}}, + {"HI", {{"highway", "secondary"}, {"osm_id", "106"}}}, + {"BJ", {{"highway", "residential"}, {"osm_id", "107"}}}, + {"CK", {{"highway", "residential"}, {"osm_id", "108"}}}, + {"DL", {{"highway", "footway"}, {"osm_id", "109"}, {"foot", "yes"}}}, + {"EM", {{"highway", "residential"}, {"osm_id", "110"}}}, + {"FN", {{"highway", "residential"}, {"osm_id", "111"}}}, + {"GO", {{"highway", "footway"}, {"osm_id", "112"}, {"foot", "yes"}}}, + {"HP", {{"highway", "residential"}, {"osm_id", "113"}}}, + + }; + + auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-73.94913, 42.81490}); + auto map = + gurka::buildtiles(layout, ways, {}, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "false"}}); + + // there should be 7 edges from A to I as even though DJ is a footway, CD and DE have different way + // ids FG and GH will be aggregated as they have the same way id and are separated by a footway. + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "I"}, "auto"); + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EF", "FG", "HI"}); + + { + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // DL should be deleted + GraphId DL_edge_id; + const DirectedEdge* DL_edge = nullptr; + GraphId LD_edge_id; + const DirectedEdge* LD_edge = nullptr; + std::tie(DL_edge_id, DL_edge, LD_edge_id, LD_edge) = + findEdge(graph_reader, map.nodes, "DL", "L", baldr::GraphId{}); + EXPECT_EQ(DL_edge, nullptr); + EXPECT_EQ(LD_edge, nullptr); + + // GO should be deleted + GraphId GO_edge_id; + const DirectedEdge* GO_edge = nullptr; + GraphId OG_edge_id; + const DirectedEdge* OG_edge = nullptr; + std::tie(GO_edge_id, GO_edge, OG_edge_id, OG_edge) = + findEdge(graph_reader, map.nodes, "GO", "O", baldr::GraphId{}); + EXPECT_EQ(GO_edge, nullptr); + EXPECT_EQ(OG_edge, nullptr); + + // FG should be deleted as it was aggregated + GraphId FG_edge_id; + const DirectedEdge* FG_edge = nullptr; + GraphId GF_edge_id; + const DirectedEdge* GF_edge = nullptr; + std::tie(FG_edge_id, FG_edge, GF_edge_id, GF_edge) = + findEdge(graph_reader, map.nodes, "FG", "G", baldr::GraphId{}); + EXPECT_EQ(FG_edge, nullptr); + EXPECT_EQ(GF_edge, nullptr); + + // GH should be deleted as it was aggregated + GraphId GH_edge_id; + const DirectedEdge* GH_edge = nullptr; + GraphId HG_edge_id; + const DirectedEdge* HG_edge = nullptr; + std::tie(GH_edge_id, GH_edge, HG_edge_id, HG_edge) = + findEdge(graph_reader, map.nodes, "GH", "H", baldr::GraphId{}); + EXPECT_EQ(GH_edge, nullptr); + EXPECT_EQ(HG_edge, nullptr); + + // FH is the new aggregated edge. Make sure is equal to wayid 105 + GraphId FH_edge_id; + const DirectedEdge* FH_edge = nullptr; + GraphId HF_edge_id; + const DirectedEdge* HF_edge = nullptr; + std::tie(FH_edge_id, FH_edge, HF_edge_id, HF_edge) = + findEdge(graph_reader, map.nodes, "FG", "H", baldr::GraphId{}, 105); + EXPECT_NE(FH_edge, nullptr); + EXPECT_NE(HF_edge, nullptr); + + auto FH = gurka::findEdgeByNodes(graph_reader, layout, "F", "H"); + auto tile = graph_reader.GetGraphTile(std::get<1>(FH)->endnode()); + EXPECT_EQ(tile->edgeinfo(std::get<1>(FH)).wayid(), 105); + } + + // save and check later + auto shape = result.trip().routes(0).legs().begin()->shape(); + + // Now run again with include_pedestrian = true. all footways should be back. + map = gurka::buildtiles(layout, ways, {}, {}, work_dir, + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}, + {"mjolnir.include_pedestrian", "true"}}); + + { + GraphReader graph_reader = GraphReader(map.config.get_child("mjolnir")); + + // there should be 8 edges from A to I + result = gurka::do_action(valhalla::Options::route, map, {"A", "I"}, "auto"); + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EF", "FG", "GH", "HI"}); + // DL should not be deleted + + GraphId DL_edge_id; + const DirectedEdge* DL_edge = nullptr; + GraphId LD_edge_id; + const DirectedEdge* LD_edge = nullptr; + std::tie(DL_edge_id, DL_edge, LD_edge_id, LD_edge) = + findEdge(graph_reader, map.nodes, "DL", "L", baldr::GraphId{}); + EXPECT_NE(DL_edge, nullptr); + EXPECT_NE(LD_edge, nullptr); + + // GO should not be deleted + GraphId GO_edge_id; + const DirectedEdge* GO_edge = nullptr; + GraphId OG_edge_id; + const DirectedEdge* OG_edge = nullptr; + std::tie(GO_edge_id, GO_edge, OG_edge_id, OG_edge) = + findEdge(graph_reader, map.nodes, "GO", "O", baldr::GraphId{}); + EXPECT_NE(GO_edge, nullptr); + EXPECT_NE(OG_edge, nullptr); + + // FG should not be deleted + GraphId FG_edge_id; + const DirectedEdge* FG_edge = nullptr; + GraphId GF_edge_id; + const DirectedEdge* GF_edge = nullptr; + std::tie(FG_edge_id, FG_edge, GF_edge_id, GF_edge) = + findEdge(graph_reader, map.nodes, "FG", "G", baldr::GraphId{}); + EXPECT_NE(FG_edge, nullptr); + EXPECT_NE(GF_edge, nullptr); + + auto tile = graph_reader.GetGraphTile(FG_edge->endnode()); + EXPECT_EQ(tile->edgeinfo(FG_edge).wayid(), 105); + + tile = graph_reader.GetGraphTile(GF_edge->endnode()); + EXPECT_EQ(tile->edgeinfo(GF_edge).wayid(), 105); + + // GH should not be deleted + GraphId GH_edge_id; + const DirectedEdge* GH_edge = nullptr; + GraphId HG_edge_id; + const DirectedEdge* HG_edge = nullptr; + std::tie(GH_edge_id, GH_edge, HG_edge_id, HG_edge) = + findEdge(graph_reader, map.nodes, "GH", "H", baldr::GraphId{}); + EXPECT_NE(GH_edge, nullptr); + EXPECT_NE(HG_edge, nullptr); + + tile = graph_reader.GetGraphTile(GH_edge->endnode()); + EXPECT_EQ(tile->edgeinfo(GH_edge).wayid(), 105); + + tile = graph_reader.GetGraphTile(HG_edge->endnode()); + EXPECT_EQ(tile->edgeinfo(HG_edge).wayid(), 105); + + // FH should not exist + ASSERT_THROW(gurka::findEdgeByNodes(graph_reader, layout, "F", "H"), std::runtime_error); + } + // the shape between including the pedestrian edges or not, should match + EXPECT_EQ(shape, result.trip().routes(0).legs().begin()->shape()); +} diff --git a/valhalla/baldr/nodeinfo.h b/valhalla/baldr/nodeinfo.h index e98c47f0f8..56934ab9b7 100644 --- a/valhalla/baldr/nodeinfo.h +++ b/valhalla/baldr/nodeinfo.h @@ -512,6 +512,7 @@ class NodeInfo { uint64_t density_ : 4; // Relative road density uint64_t traffic_signal_ : 1; // Traffic signal uint64_t mode_change_ : 1; // Mode change allowed? + // Also used for aggregation of edges at filter stage uint64_t named_ : 1; // Is this a named intersection? uint64_t transition_index_ : 21; // Index into the node transitions to the first transition From 57e5e14f630920ebd02234c2e041fbf8779787eb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:46:35 +0100 Subject: [PATCH 009/198] Bump tz to 2023d (#4519) Co-authored-by: github-actions[bot] --- CHANGELOG.md | 1 + scripts/valhalla_build_timezones | 2 +- third_party/tz | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 065a6a92de..3458ef019c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,6 +77,7 @@ * ADDED: Timezone info in route and matrix responses [#4491](https://github.com/valhalla/valhalla/pull/4491) * CHANGED: use pkg-config to find spatialite & geos and remove our cmake modules; upgraded conan's boost to 1.83.0 in the process [#4253](https://github.com/valhalla/valhalla/pull/4253) * ADDED: Added aggregation logic to filter stage of tile building [#4512](https://github.com/valhalla/valhalla/pull/4512) + * UPDATED: tz to 2023d [#4519](https://github.com/valhalla/valhalla/pull/4519) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/scripts/valhalla_build_timezones b/scripts/valhalla_build_timezones index 9d273a79f4..98fbddeee4 100755 --- a/scripts/valhalla_build_timezones +++ b/scripts/valhalla_build_timezones @@ -21,7 +21,7 @@ fi rm -rf dist rm -f ./timezones-with-oceans.shapefile.zip -url="https://github.com/evansiroky/timezone-boundary-builder/releases/download/2023b/timezones-with-oceans.shapefile.zip" +url="https://github.com/evansiroky/timezone-boundary-builder/releases/download/2023d/timezones-with-oceans.shapefile.zip" echo "downloading timezone polygon file." 1>&2 curl -L -s -o ./timezones-with-oceans.shapefile.zip ${url} || error_exit "curl failed for ${url}" diff --git a/third_party/tz b/third_party/tz index ddb8cf09d5..cc48c2dfa2 160000 --- a/third_party/tz +++ b/third_party/tz @@ -1 +1 @@ -Subproject commit ddb8cf09d54ecc486ed6665edcdd642abccd4eef +Subproject commit cc48c2dfa2f3c21d25ab108bba978b0307ecf0e8 From 0de56b564fd28a27f8d046f72c862d64f943be6f Mon Sep 17 00:00:00 2001 From: David Nesbitt Date: Thu, 25 Jan 2024 14:41:10 -0500 Subject: [PATCH 010/198] Fix crash using costmatrix (and potentially timedistancebssmatrix) (#4530) --- CHANGELOG.md | 1 + src/thor/costmatrix.cc | 9 +++++++++ src/thor/timedistancebssmatrix.cc | 9 +++++++++ 3 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3458ef019c..a792eb4462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,7 @@ * CHANGED: use pkg-config to find spatialite & geos and remove our cmake modules; upgraded conan's boost to 1.83.0 in the process [#4253](https://github.com/valhalla/valhalla/pull/4253) * ADDED: Added aggregation logic to filter stage of tile building [#4512](https://github.com/valhalla/valhalla/pull/4512) * UPDATED: tz to 2023d [#4519](https://github.com/valhalla/valhalla/pull/4519) + * FIXED: Fix segfault in costmatrix (date_time and time zone always added). [#4530](https://github.com/valhalla/valhalla/pull/4530) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index f3b5a634d6..3c74ca16d4 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -294,6 +294,15 @@ void CostMatrix::SourceToTarget(Api& request, auto* pbf_time_zone_name = matrix.mutable_time_zone_names()->Add(); *pbf_time_zone_name = dt_info.time_zone_name; + } else { + // Add empty strings to make sure pbf arrays are populated (serializer + // requires this) + auto* pbf_date_time = matrix.mutable_date_times()->Add(); + *pbf_date_time = ""; + auto* pbf_time_zone_offset = matrix.mutable_time_zone_offsets()->Add(); + *pbf_time_zone_offset = ""; + auto* pbf_time_zone_name = matrix.mutable_time_zone_names()->Add(); + *pbf_time_zone_name = ""; } matrix.mutable_from_indices()->Set(count, source_idx); matrix.mutable_to_indices()->Set(count, target_idx); diff --git a/src/thor/timedistancebssmatrix.cc b/src/thor/timedistancebssmatrix.cc index 63c28dc3ec..2fb5ffffa2 100644 --- a/src/thor/timedistancebssmatrix.cc +++ b/src/thor/timedistancebssmatrix.cc @@ -531,6 +531,15 @@ void TimeDistanceBSSMatrix::FormTimeDistanceMatrix(Api& request, matrix.mutable_to_indices()->Set(pbf_idx, forward ? i : origin_index); matrix.mutable_distances()->Set(pbf_idx, dest.distance); matrix.mutable_times()->Set(pbf_idx, time); + + // TODO - support date_time and time zones as in timedistancematrix. + // For now, add empty strings (serializer requires this) to prevent crashing + auto* pbf_dt = matrix.mutable_date_times()->Add(); + *pbf_dt = ""; + auto* pbf_tz_offset = matrix.mutable_time_zone_offsets()->Add(); + *pbf_tz_offset = ""; + auto* pbf_tz_names = matrix.mutable_time_zone_names()->Add(); + *pbf_tz_names = ""; } } From 69a6bfc70aae90a7398315e94c10f1b098c785f0 Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 29 Jan 2024 14:43:31 +0100 Subject: [PATCH 011/198] clean up cmake (#4516) --- .azure-pipelines.yml | 39 +- .circleci/config.yml | 32 +- .github/workflows/typos.toml | 2 - .vcpkg_deps.txt | 8 - CHANGELOG.md | 1 + CMakeLists.txt | 234 ++--- Dockerfile | 4 - cmake/FindLuaJIT.cmake | 62 -- cmake/PkgConfig.cmake | 3 - ...2Header.cmake => ValhallaBin2Header.cmake} | 4 +- cmake/ValhallaPkgConfig.cmake | 51 + ...s.cmake => ValhallaSanitizerOptions.cmake} | 12 +- cmake/ValhallaSourceGroups.cmake | 25 + cmake/ValhallaVersion.cmake | 19 + cmake/ValhallaWarnings.cmake | 18 + cmake/conan.cmake | 909 ------------------ cmake/uninstall.cmake.in | 65 ++ conanfile.txt | 35 - docs/docs/building.md | 19 +- libvalhalla.pc.in | 10 +- proto/CMakeLists.txt | 2 +- scripts/install-linux-deps.sh | 9 +- src/CMakeLists.txt | 97 +- src/baldr/CMakeLists.txt | 15 +- src/baldr/curler.cc | 2 +- src/bindings/python/CMakeLists.txt | 4 +- src/loki/CMakeLists.txt | 7 +- src/loki/status_action.cc | 2 +- src/loki/worker.cc | 2 +- src/meili/CMakeLists.txt | 8 +- src/midgard/CMakeLists.txt | 7 +- src/mjolnir/CMakeLists.txt | 22 +- src/mjolnir/ingest_transit.cc | 1 - src/odin/CMakeLists.txt | 12 +- src/odin/worker.cc | 4 +- src/sif/CMakeLists.txt | 8 +- src/skadi/CMakeLists.txt | 6 +- src/thor/CMakeLists.txt | 13 +- src/thor/status_action.cc | 2 +- src/thor/worker.cc | 4 +- src/tyr/CMakeLists.txt | 8 +- src/valhalla_service.cc | 6 +- src/worker.cc | 4 +- test/CMakeLists.txt | 5 +- test/gurka/CMakeLists.txt | 2 +- test/statsd.cc | 2 +- valhalla/baldr/graphreader.h | 1 - valhalla/baldr/graphtile.h | 1 - valhalla/loki/worker.h | 4 +- valhalla/odin/worker.h | 4 +- valhalla/thor/worker.h | 4 +- valhalla/worker.h | 8 +- 52 files changed, 451 insertions(+), 1377 deletions(-) delete mode 100644 .vcpkg_deps.txt delete mode 100644 cmake/FindLuaJIT.cmake delete mode 100644 cmake/PkgConfig.cmake rename cmake/{Binary2Header.cmake => ValhallaBin2Header.cmake} (98%) create mode 100644 cmake/ValhallaPkgConfig.cmake rename cmake/{SanitizerOptions.cmake => ValhallaSanitizerOptions.cmake} (78%) create mode 100644 cmake/ValhallaSourceGroups.cmake create mode 100644 cmake/ValhallaVersion.cmake create mode 100644 cmake/ValhallaWarnings.cmake delete mode 100644 cmake/conan.cmake create mode 100644 cmake/uninstall.cmake.in delete mode 100644 conanfile.txt diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 3689df5ef0..7beddebcca 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -11,11 +11,25 @@ trigger: branches: include: - master + paths: + include: + - '*' + exclude: + - README.md + - CHANGELOG.md + - bench/ + - docs/ + - run_route_scripts/ + - test/ + - .circleci/ + - .github/ pr: branches: include: - master paths: + include: + - '*' exclude: - README.md - CHANGELOG.md @@ -38,12 +52,8 @@ jobs: variables: BUILD_CFG: 'Release' BUILD_DIR: '$(Agent.WorkFolder)\build' - VCPKG_DIR: '$(Build.SourcesDirectory)\vcpkg' - VCPKG_ROOT: '$(Build.SourcesDirectory)\vcpkg' - VCPKG_INSTALLATION_ROOT: '$(Build.SourcesDirectory)\vcpkg' TRIPLET: 'x64' VCPKG_REF: 'f330a32' - CONAN_HOME: '$(Build.SourcesDirectory)/conan' steps: - script: | @@ -54,8 +64,8 @@ jobs: - task: Cache@2 displayName: "Cache vcpkg's packages" inputs: - key: .\vcpkg.json | "$(VCPKG_REF)" | ".v2" - path: "$(VCPKG_DIR)" + key: .\vcpkg.json | "$(VCPKG_REF)" + path: '$(BUILD_DIR)/vcpkg_installed' cacheHitVar: CACHE_RESTORED # TODO: cache build never worked, look into it @@ -70,20 +80,13 @@ jobs: # cacheHitVar: BUILD_CACHE_RESTORED - script: | - git clone https://github.com/microsoft/vcpkg %VCPKG_DIR% - git -C %VCPKG_DIR% checkout %VCPKG_REF% - echo.set(VCPKG_BUILD_TYPE release)>> %VCPKG_DIR%\triplets\%TRIPLET%-windows.cmake - call %VCPKG_DIR%\bootstrap-vcpkg.bat - %VCPKG_DIR%\vcpkg.exe version + git -C %VCPKG_INSTALLATION_ROOT% checkout %VCPKG_REF% + echo.set(VCPKG_BUILD_TYPE release)>> %VCPKG_INSTALLATION_ROOT%\triplets\%TRIPLET%-windows.cmake + call %VCPKG_INSTALLATION_ROOT%\bootstrap-vcpkg.bat + %VCPKG_INSTALLATION_ROOT%\vcpkg.exe version displayName: 'Install vcpkg' condition: ne(variables.CACHE_RESTORED, 'true') - - script: | - %VCPKG_DIR%\vcpkg.exe install - %VCPKG_DIR%\vcpkg.exe list - displayName: 'Install vcpkg packages' - condition: ne(variables.CACHE_RESTORED, 'true') - - script: | move /Y third_party\OSM-binary\src\fileformat.proto third_party\OSM-binary\src\fileformat.proto.orig move /Y third_party\OSM-binary\src\osmformat.proto third_party\OSM-binary\src\osmformat.proto.orig @@ -97,7 +100,7 @@ jobs: - script: | cmake --version - cmake -G "Visual Studio 17 2022" -A %TRIPLET% -H$(Build.SourcesDirectory) -B%BUILD_DIR% -DCMAKE_BUILD_TYPE=%BUILD_CFG% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_DIR%\scripts\buildsystems\vcpkg.cmake -DVCPKG_APPLOCAL_DEPS=ON -DENABLE_DATA_TOOLS=ON -DENABLE_TOOLS=ON -DENABLE_PYTHON_BINDINGS=ON -DENABLE_TESTS=OFF -DENABLE_CCACHE=OFF -DENABLE_HTTP=OFF -DENABLE_SERVICES=OFF -DENABLE_BENCHMARKS=OFF + cmake -G "Visual Studio 17 2022" -A %TRIPLET% -H$(Build.SourcesDirectory) -B%BUILD_DIR% -DCMAKE_BUILD_TYPE=%BUILD_CFG% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_INSTALLATION_ROOT%\scripts\buildsystems\vcpkg.cmake -DVCPKG_APPLOCAL_DEPS=ON -DENABLE_DATA_TOOLS=ON -DENABLE_TOOLS=ON -DENABLE_PYTHON_BINDINGS=ON -DENABLE_TESTS=OFF -DENABLE_CCACHE=OFF -DENABLE_HTTP=OFF -DENABLE_SERVICES=OFF -DENABLE_BENCHMARKS=OFF cmake --build %BUILD_DIR% --config %BUILD_CFG% -- /clp:ErrorsOnly /p:BuildInParallel=true /m:4 displayName: 'Build Valhalla' diff --git a/.circleci/config.yml b/.circleci/config.yml index 8ea18b4c87..1e53d1a3d7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,8 +11,8 @@ executors: commands: mac_deps: steps: - - run: brew install autoconf automake protobuf cmake ccache libtool libspatialite pkg-config luajit curl wget czmq lz4 spatialite-tools unzip - - run: sudo python3 -m pip install requests shapely "conan<2.0.0" + - run: brew install autoconf automake protobuf cmake ccache libtool sqlite3 libspatialite luajit curl wget czmq lz4 spatialite-tools unzip boost + - run: sudo python3 -m pip install requests shapely - run: git clone https://github.com/kevinkreiser/prime_server --recurse-submodules && cd prime_server && ./autogen.sh && ./configure && make -j8 && sudo make install linux_deps: steps: @@ -39,8 +39,8 @@ jobs: - run: git submodule sync && git submodule update --init - restore_cache: keys: - - ccache-debug-linux-x86_64-v3-{{ .Branch }}-{{ checksum "conanfile.txt" }} - - ccache-debug-linux-x86_64-v3-{{ checksum "conanfile.txt" }} + - ccache-debug-linux-x86_64-v3-{{ .Branch }} + - ccache-debug-linux-x86_64-v3 - run: | mkdir build # NOTE: -Werror disabled in CI, as we currently have >4k warnings. @@ -57,10 +57,9 @@ jobs: # Note: we save the cache here before doing linting so that if linting fails, we can rebuild quickly # for follow-up fixes - save_cache: - key: ccache-debug-linux-x86_64-v3-{{ .Branch }}-{{ checksum "conanfile.txt" }}-{{ epoch }} + key: ccache-debug-linux-x86_64-v3-{{ .Branch }}-{{ epoch }} paths: - ~/.ccache - - ~/.conan - run: scripts/clang-tidy-only-diff.sh 4 - run: sudo make -C build install - run: make -C build package @@ -81,8 +80,8 @@ jobs: - run: git submodule sync && git submodule update --init - restore_cache: keys: - - ccache-release-linux-x86_64-v3-{{ .Branch }}-{{ checksum "conanfile.txt" }} - - ccache-release-linux-x86_64-v3-{{ checksum "conanfile.txt" }} + - ccache-release-linux-x86_64-v3-{{ .Branch }} + - ccache-release-linux-x86_64-v3 - run: | mkdir build cd build @@ -97,10 +96,9 @@ jobs: - run: make -C build -j8 benchmarks - run: make -C build -j8 run-benchmarks - save_cache: - key: ccache-release-linux-x86_64-v3-{{ .Branch }}-{{ checksum "conanfile.txt" }}-{{ epoch }} + key: ccache-release-linux-x86_64-v3-{{ .Branch }}-{{ epoch }} paths: - ~/.ccache - - ~/.conan - run: sudo make -C build install - run: make -C build package @@ -116,8 +114,8 @@ jobs: - run: git submodule sync && git submodule update --init - restore_cache: keys: - - ccache-release-linux-arm_64-v3-{{ .Branch }}-{{ checksum "conanfile.txt" }} - - ccache-release-linux-arm_64-v3-{{ checksum "conanfile.txt" }} + - ccache-release-linux-arm_64-v3-{{ .Branch }} + - ccache-release-linux-arm_64-v3 - run: | mkdir build cd build @@ -131,10 +129,9 @@ jobs: - run: make -C build -j8 benchmarks - run: make -C build -j8 run-benchmarks - save_cache: - key: ccache-release-linux-arm_64-v3-{{ .Branch }}-{{ checksum "conanfile.txt" }}-{{ epoch }} + key: ccache-release-linux-arm_64-v3-{{ .Branch }}-{{ epoch }} paths: - ~/.ccache - - ~/.conan - run: sudo make -C build install - run: make -C build package @@ -147,8 +144,8 @@ jobs: - run: git submodule sync && git submodule update --init - restore_cache: keys: - - ccache-release-macos-{{ .Branch }}-{{ checksum "conanfile.txt" }} - - ccache-release-macos-{{ checksum "conanfile.txt" }} + - ccache-release-macos-{{ .Branch }} + - ccache-release-macos - run: cmake -B build -DENABLE_SINGLE_FILES_WERROR=OFF - run: make -C build -j8 - run: make -C build utrecht_tiles @@ -157,10 +154,9 @@ jobs: - run: make -C build -j8 benchmarks - run: make -C build run-benchmarks - save_cache: - key: ccache-release-macos-{{ .Branch }}-{{ checksum "conanfile.txt" }}-{{ epoch }} + key: ccache-release-macos-{{ epoch }} paths: - ~/.ccache - - ~/.conan workflows: version: 2 diff --git a/.github/workflows/typos.toml b/.github/workflows/typos.toml index fc896cf6dc..1c1f41b804 100644 --- a/.github/workflows/typos.toml +++ b/.github/workflows/typos.toml @@ -48,8 +48,6 @@ extend-exclude = [ "test_requests/", "url_de_benchmark_routes.txt", - "cmake/conan.cmake", # fixing 'setings' for some reason breaks conan - "test/gurka/", # contains a bunch of two-letter names to refer to edges "src/baldr/admin.cc", # contains a bunch of two-letter country-codes diff --git a/.vcpkg_deps.txt b/.vcpkg_deps.txt deleted file mode 100644 index 7178909c6d..0000000000 --- a/.vcpkg_deps.txt +++ /dev/null @@ -1,8 +0,0 @@ -curl -protobuf -zlib -sqlite3 -proj4 -luajit -libspatialite -geos \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a792eb4462..091192c74a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,7 @@ * ADDED: Added aggregation logic to filter stage of tile building [#4512](https://github.com/valhalla/valhalla/pull/4512) * UPDATED: tz to 2023d [#4519](https://github.com/valhalla/valhalla/pull/4519) * FIXED: Fix segfault in costmatrix (date_time and time zone always added). [#4530](https://github.com/valhalla/valhalla/pull/4530) + * CHANGED: libvalhalla.pc generation to have finer controls; install third_party public headers; overhaul lots of CMake; remove conan support [#4516](https://github.com/valhalla/valhalla/pull/4516) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dd8065944..957030efb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,6 @@ set(LOGGING_LEVEL "" CACHE STRING "Logging level, default is INFO") set_property(CACHE LOGGING_LEVEL PROPERTY STRINGS "NONE;ALL;ERROR;WARN;INFO;DEBUG;TRACE") set_property(GLOBAL PROPERTY USE_FOLDERS ON) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(THREADS_PREFER_PTHREAD_FLAG ON) @@ -52,13 +50,10 @@ include(CheckCXXCompilerFlag) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set(COLOR_FLAG "-fdiagnostics-color=auto") check_cxx_compiler_flag("-fdiagnostics-color=auto" HAS_COLOR_FLAG) - if(NOT HAS_COLOR_FLAG) - set(COLOR_FLAG "") + if(HAS_COLOR_FLAG) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COLOR_FLAG}") endif() - # using GCC - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COLOR_FLAG}") endif() # Explicitly set the build type to Release if no other type is specified @@ -85,18 +80,6 @@ else() message(FATAL_ERROR "Unrecognized build type. Use one of Debug, Release, RelWithDebInfo, MinRelSize, None") endif() -function(create_source_groups prefix) - foreach(file ${ARGN}) - get_filename_component(file "${file}" ABSOLUTE) - string(FIND "${file}" "${PROJECT_BINARY_DIR}/" pos) - if(pos EQUAL 0) - source_group(TREE "${PROJECT_BINARY_DIR}/" PREFIX "Generated Files" FILES "${file}") - else() - source_group(TREE "${PROJECT_SOURCE_DIR}/" PREFIX "${prefix}" FILES "${file}") - endif() - endforeach() -endfunction() - if(ENABLE_CCACHE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")) find_program(CCACHE_FOUND ccache) if(CCACHE_FOUND) @@ -107,106 +90,50 @@ if(ENABLE_CCACHE AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILE endif() endif() -include(cmake/SanitizerOptions.cmake) - -## Coverage report targets -if(ENABLE_COVERAGE) - find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat) - if(NOT GENHTML_PATH) - message(FATAL_ERROR "no genhtml installed") - endif() - - set(FASTCOV_PATH ${VALHALLA_SOURCE_DIR}/third_party/fastcov/fastcov.py) - add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/coverage.info - COMMAND ${FASTCOV_PATH} -d . --exclude /usr/ third_party/ ${CMAKE_CURRENT_BINARY_DIR}/ --lcov -o coverage.info - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS check) - - add_custom_target(coverage - COMMAND ${GENHTML_PATH} --prefix ${CMAKE_CURRENT_BINARY_DIR} --output-directory coverage --title "Test Coverage" --legend --show-details coverage.info - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/coverage.info) - set_target_properties(coverage PROPERTIES FOLDER "Tests") -endif() +include(ValhallaSanitizerOptions) +include(ValhallaSourceGroups) -## Dependencies +# We use pkg-config for (almost) all dependencies: +# - CMake Find* modules are versioned with CMake, not the packages, see protobuf > 21.12 +# - it's more trivial to use custom installations via PKG_CONFIG_PATH env var +find_package(PkgConfig REQUIRED) find_package(Threads REQUIRED) -find_package(ZLIB REQUIRED) - -# try to find an installed boost or install locally with conan -set(boost_VERSION "1.71") -find_package(Boost ${boost_VERSION} QUIET) -if (NOT Boost_FOUND) - # bail if there's no conan installed - message(STATUS "No compatible boost version detected, using conan...") - find_program(conan_FOUND conan) - if (NOT conan_FOUND) - message(FATAL_ERROR "conan needs to be installed for boost, see https://docs.conan.io/en/latest/installation.html.") +pkg_check_modules(ZLIB REQUIRED IMPORTED_TARGET zlib) + +# cURL +set(curl_targets "") +if (ENABLE_HTTP OR ENABLE_DATA_TOOLS) + pkg_check_modules(CURL REQUIRED IMPORTED_TARGET libcurl) + if (CURL_FOUND) + set(curl_targets PkgConfig::CURL) + target_compile_definitions(PkgConfig::CURL INTERFACE ENABLE_HTTP) + else() + message(FATAL_ERROR "ENABLE_HTTP=ON, but cURL not found...") endif() - - # build dir needs to be added, that's where conan writes some info - list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_BINARY_DIR}) - list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_BINARY_DIR}) - include(${VALHALLA_SOURCE_DIR}/cmake/conan.cmake) - - conan_cmake_autodetect(settings) - conan_cmake_install(PATH_OR_REFERENCE ${VALHALLA_SOURCE_DIR}/conanfile.txt - REMOTE conancenter - SETTINGS ${settings} - OUTPUT_QUIET) endif() -find_package(Boost ${boost_VERSION} REQUIRED) + +# Boost has no .pc file.. +find_package(Boost 1.71 REQUIRED) add_definitions(-DBOOST_NO_CXX11_SCOPED_ENUMS) add_definitions(-DBOOST_ALLOW_DEPRECATED_HEADERS) add_definitions(-DBOOST_BIND_GLOBAL_PLACEHOLDERS) -find_package(PkgConfig REQUIRED) - -if(NOT TARGET CURL::CURL) - add_library(CURL::CURL INTERFACE IMPORTED) - if(ENABLE_HTTP OR ENABLE_DATA_TOOLS) - if(NOT CURL_FOUND) - find_package(CURL REQUIRED) - endif() - set_target_properties(CURL::CURL PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${CURL_INCLUDE_DIR}" - INTERFACE_COMPILE_DEFINITIONS CURL_STATICLIB) - message(STATUS "Found cURL: ${CURL_LIBRARIES}") - if(NOT MSVC) - target_link_libraries(CURL::CURL INTERFACE ${CURL_LIBRARIES}) - else() - link_libraries(${CURL_LIBRARIES}) - endif() - endif() -else() - message(STATUS "Using curl from the outside") -endif() - -if(NOT Protobuf_FOUND) - # prefer CONFIG mode over MODULE mode, there were a few CMake/protobuf fuckups - # NOTE: this is only supported for cmake >= 3.15, but shouldn't be a problem in real life - set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) - - # newer protobuf versions require a compat bool - set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "") - find_package(Protobuf REQUIRED) - - # and turn it off again - set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF) -endif() - -message(STATUS "Using pbf headers from ${PROTOBUF_INCLUDE_DIRS}") -message(STATUS "Using pbf libs from ${PROTOBUF_LIBRARY}") -message(STATUS "Using pbf release libs from ${PROTOBUF_LIBRARY_RELEASE}") -message(STATUS "Using pbf debug libs from ${PROTOBUF_LIBRARY_DEBUG}") +# Protobuf is non-trivial to include via pkg-config, pkg_check_modules has no way to check +# for protoc location in a platform agnostic manner +# prefer CONFIG mode over MODULE mode, there were a few CMake/protobuf messups +# NOTE: this is only supported for cmake >= 3.15, but shouldn't be a problem in real life +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) +# newer protobuf versions require a compat bool +set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "") +find_package(Protobuf REQUIRED) +# and turn it off again +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF) +message(STATUS "Using protoc from ${Protobuf_PROTOC_EXECUTABLE}") +message(STATUS "Using pbf headers from ${Protobuf_INCLUDE_DIRS}") +message(STATUS "Using pbf libs from ${PROTOBUF_LIBRARIES}") if(TARGET protobuf::libprotobuf-lite) message(STATUS "Using pbf-lite") endif() - -# Allow linking against full or lite version of Protobuf library -# TODO: After switching to CMake 3.12+, replace with -# $=0.6.3) - # workaround for https://gitlab.kitware.com/cmake/cmake/issues/15804 - find_library(libprime_server_LIBRARY - NAME ${libprime_server_LIBRARIES} - HINTS ${libprime_server_LIBRARY_DIRS}) - set_target_properties(libprime_server PROPERTIES - INTERFACE_LINK_LIBRARIES "${libprime_server_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${libprime_server_INCLUDE_DIRS}" - INTERFACE_COMPILE_DEFINITIONS HAVE_HTTP) + pkg_check_modules(libprime_server IMPORTED_TARGET libprime_server>=0.6.3) + if (libprime_server_FOUND) + target_compile_definitions(PkgConfig::libprime_server INTERFACE ENABLE_SERVICES) + set(libprime_server_targets PkgConfig::libprime_server) + else() + set(ENABLE_SERVICES OFF) + message(FATAL_ERROR "ENABLE_SERVICES=ON but prime_server not found...") + endif() endif() ## Mjolnir and associated executables if(ENABLE_DATA_TOOLS) add_compile_definitions(DATA_TOOLS) + # keep sqlite3 with cmake find_package(), as OSX needs our own FindSqlite3.cmake + # otherwise the system sqlite3 is found which doesn't allow for spatialite find_package(SQLite3 REQUIRED) pkg_check_modules(SpatiaLite REQUIRED IMPORTED_TARGET spatialite) - find_package(LuaJIT) - add_library(Lua::Lua INTERFACE IMPORTED) - set_target_properties(Lua::Lua PROPERTIES - INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}") + pkg_check_modules(LuaJIT REQUIRED IMPORTED_TARGET luajit) endif() if (ENABLE_THREAD_SAFE_TILE_REF_COUNT) @@ -249,7 +175,7 @@ add_subdirectory(src) ## Python bindings if(ENABLE_PYTHON_BINDINGS) - # favors Py3, falls back to Py2 + # python is fine for cmake as well find_package(Python COMPONENTS Development Interpreter) if (Python_FOUND) add_subdirectory(src/bindings/python) @@ -258,22 +184,11 @@ if(ENABLE_PYTHON_BINDINGS) COMPONENT python) else() set(ENABLE_PYTHON_BINDINGS OFF) - message(WARNING "Skipping Python bindings... Install a development Python version including headers to compile the bindings.") + message(WARNING "Python development version not found, skipping Python bindings...") endif() endif() ## Executable targets -function(get_source_path PATH NAME) - if(EXISTS ${VALHALLA_SOURCE_DIR}/src/${NAME}.cc) - set(${PATH} ${VALHALLA_SOURCE_DIR}/src/${NAME}.cc PARENT_SCOPE) - elseif(EXISTS ${VALHALLA_SOURCE_DIR}/src/meili/${NAME}.cc) - set(${PATH} ${VALHALLA_SOURCE_DIR}/src/meili/${NAME}.cc PARENT_SCOPE) - elseif(EXISTS ${VALHALLA_SOURCE_DIR}/src/mjolnir/${NAME}.cc) - set(${PATH} ${VALHALLA_SOURCE_DIR}/src/mjolnir/${NAME}.cc PARENT_SCOPE) - else() - message(FATAL_ERROR "no source path for ${NAME}") - endif() -endfunction() ## Valhalla programs set(valhalla_programs valhalla_run_map_match valhalla_benchmark_loki valhalla_benchmark_skadi @@ -296,10 +211,7 @@ if(ENABLE_TOOLS) set_target_properties(${program} PROPERTIES FOLDER "Tools") create_source_groups("Source Files" ${path}) target_link_libraries(${program} valhalla) - target_include_directories(${program} PUBLIC ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) - if(MSVC) - target_link_libraries(${program} ${CURL_LIBRARIES}) - endif() + target_include_directories(${program} PRIVATE ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) install(TARGETS ${program} DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime) endforeach() endif() @@ -310,20 +222,16 @@ if(ENABLE_DATA_TOOLS) add_executable(${program} ${path}) create_source_groups("Source Files" ${path}) set_target_properties(${program} PROPERTIES FOLDER "Data Tools") - target_include_directories(${program} PUBLIC ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) + target_include_directories(${program} PRIVATE ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) target_link_libraries(${program} valhalla) - if (LUAJIT_FOUND AND APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") + if (LuaJIT_FOUND AND APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") # Using LuaJIT on macOS on Intel processors requires a couple of extra linker flags target_link_options(${program} PUBLIC -pagezero_size 10000 -image_base 100000000) endif() - if(MSVC) - target_link_libraries(${program} ${CURL_LIBRARIES}) - endif() install(TARGETS ${program} DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime) endforeach() # Target-specific dependencies - # find_package(GEOS) pkg_check_modules(GEOS REQUIRED IMPORTED_TARGET geos) target_link_libraries(valhalla_build_admins PkgConfig::GEOS) target_sources(valhalla_build_statistics @@ -338,7 +246,6 @@ if(ENABLE_SERVICES) create_source_groups("Source Files" src/${program}.cc) set_target_properties(${program} PROPERTIES FOLDER "Services") target_link_libraries(${program} valhalla) - target_include_directories(${program} PUBLIC ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) install(TARGETS ${program} DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime) endforeach() endif() @@ -364,6 +271,16 @@ install(FILES COPYING CHANGELOG.md DESTINATION "${CMAKE_INSTALL_DOCDIR}" COMPONENT runtime) +# install third_party +install(DIRECTORY ${CMAKE_SOURCE_DIR}/third_party/rapidjson/include/ + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla/third_party") +install(DIRECTORY ${CMAKE_SOURCE_DIR}/third_party/date/include/ + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla/third_party") +if (WIN32) + install(DIRECTORY ${CMAKE_SOURCE_DIR}/third_party/dirent/include/ + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla/third_party") +endif() + if(ENABLE_TESTS) add_subdirectory(test) endif() @@ -373,6 +290,26 @@ if(ENABLE_BENCHMARKS AND ENABLE_DATA_TOOLS AND NOT WIN32) add_subdirectory(bench) endif() +## Coverage report targets +if(ENABLE_COVERAGE) + find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat) + if(NOT GENHTML_PATH) + message(FATAL_ERROR "no genhtml installed") + endif() + + set(FASTCOV_PATH ${VALHALLA_SOURCE_DIR}/third_party/fastcov/fastcov.py) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/coverage.info + COMMAND ${FASTCOV_PATH} -d . --exclude /usr/ third_party/ ${CMAKE_CURRENT_BINARY_DIR}/ --lcov -o coverage.info + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS check) + + add_custom_target(coverage + COMMAND ${GENHTML_PATH} --prefix ${CMAKE_CURRENT_BINARY_DIR} --output-directory coverage --title "Test Coverage" --legend --show-details coverage.info + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/coverage.info) + set_target_properties(coverage PROPERTIES FOLDER "Tests") +endif() + ## Packaging via CPack include(CPackComponent) @@ -429,3 +366,6 @@ endif() set(CPACK_PROJECT_CONFIG_FILE ${VALHALLA_SOURCE_DIR}/cmake/CPackConfig.cmake) set(CPACK_DEBIAN_PACKAGE_DEBUG OFF) include(CPack) + +configure_file(cmake/uninstall.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/valhalla_uninstall.cmake @ONLY) +add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/valhalla_uninstall.cmake) diff --git a/Dockerfile b/Dockerfile index b973b3beab..52d940f2e3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,10 +30,6 @@ RUN ls -la RUN git submodule sync && git submodule update --init --recursive RUN rm -rf build && mkdir build -# upgrade Conan again, to avoid using an outdated version: -# https://github.com/valhalla/valhalla/issues/3685#issuecomment-1198604174 -RUN sudo PIP_BREAK_SYSTEM_PACKAGES=1 pip install --upgrade "conan<2.0.0" - # configure the build with symbols turned on so that crashes can be triaged WORKDIR /usr/local/src/valhalla/build # switch back to -DCMAKE_BUILD_TYPE=RelWithDebInfo and uncomment the block below if you want debug symbols diff --git a/cmake/FindLuaJIT.cmake b/cmake/FindLuaJIT.cmake deleted file mode 100644 index 2a37871cd1..0000000000 --- a/cmake/FindLuaJIT.cmake +++ /dev/null @@ -1,62 +0,0 @@ -# Locate LuaJIT library -# This module defines -# LUAJIT_FOUND, if false, do not try to link to Lua -# LUA_LIBRARIES -# LUA_INCLUDE_DIR, where to find lua.h -# LUAJIT_VERSION_STRING, the version of Lua found (since CMake 2.8.8) - -## Copied from default CMake FindLua51.cmake - -find_path(LUA_INCLUDE_DIR luajit.h - HINTS - ENV LUA_DIR - PATH_SUFFIXES include/luajit-2.0 include/luajit-2.1 include/luajit include - PATHS - ~/Library/Frameworks - /Library/Frameworks - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) - -find_library(LUA_LIBRARY - NAMES luajit-5.1 lua51 - HINTS - ENV LUA_DIR - PATH_SUFFIXES lib - PATHS - ~/Library/Frameworks - /Library/Frameworks - /sw - /opt/local - /opt/csw - /opt -) - -if(LUA_LIBRARY) - # include the math library for Unix - if(UNIX AND NOT APPLE) - find_library(LUA_MATH_LIBRARY m) - set( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") - # For Windows and Mac, don't need to explicitly include the math library - else() - set( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") - endif() -endif() - -if(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/luajit.h") - file(STRINGS "${LUA_INCLUDE_DIR}/luajit.h" luajit_version_str REGEX "^#define[ \t]+LUAJIT_VERSION[ \t]+\"LuaJIT .+\"") - - string(REGEX REPLACE "^#define[ \t]+LUAJIT_VERSION[ \t]+\"LuaJIT ([^\"]+)\".*" "\\1" LUAJIT_VERSION_STRING "${luajit_version_str}") - unset(luajit_version_str) -endif() - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if -# all listed variables are TRUE -FIND_PACKAGE_HANDLE_STANDARD_ARGS(LuaJIT - REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR - VERSION_VAR LUAJIT_VERSION_STRING) - -mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY) diff --git a/cmake/PkgConfig.cmake b/cmake/PkgConfig.cmake deleted file mode 100644 index 0bf007b5af..0000000000 --- a/cmake/PkgConfig.cmake +++ /dev/null @@ -1,3 +0,0 @@ -string(REGEX REPLACE " [^ ]*valhalla[^ ]+" "" deplibs "${deplibs}") -string(REGEX REPLACE " [^ ]*lib([^/ ]+).so" " -l\\1" deplibs "${deplibs}") -configure_file(${INPUT} ${OUTPUT} @ONLY) diff --git a/cmake/Binary2Header.cmake b/cmake/ValhallaBin2Header.cmake similarity index 98% rename from cmake/Binary2Header.cmake rename to cmake/ValhallaBin2Header.cmake index 1e59e96568..c380208b6e 100644 --- a/cmake/Binary2Header.cmake +++ b/cmake/ValhallaBin2Header.cmake @@ -127,7 +127,7 @@ function(BIN2H) endif() endfunction() -if(CMAKE_SCRIPT_MODE_FILE AND "${CMAKE_SCRIPT_MODE_FILE}" MATCHES "Binary2Header.cmake$") +if(CMAKE_SCRIPT_MODE_FILE AND "${CMAKE_SCRIPT_MODE_FILE}" MATCHES "ValhallaBin2Header.cmake$") # Parse command line argmuents set(ARG_NUM 1) set(conversion_type "HEADER") @@ -136,7 +136,7 @@ if(CMAKE_SCRIPT_MODE_FILE AND "${CMAKE_SCRIPT_MODE_FILE}" MATCHES "Binary2Header set(CURRENT_ARG ${CMAKE_ARGV${ARG_NUM}}) if(${CURRENT_ARG} MATCHES "^--usage$") message("Usage: - cmake -P cmake/Binary2Header.cmake [options] infile outfile + cmake -P cmake/ValhallaBin2Header.cmake [options] infile outfile Options: --header convert to a header file --locales convert locales json files to a header file diff --git a/cmake/ValhallaPkgConfig.cmake b/cmake/ValhallaPkgConfig.cmake new file mode 100644 index 0000000000..3649a24dd3 --- /dev/null +++ b/cmake/ValhallaPkgConfig.cmake @@ -0,0 +1,51 @@ +function(set_variable_from_rel_or_absolute_path var root rel_or_abs_path) + if(IS_ABSOLUTE "${rel_or_abs_path}") + set(${var} "${rel_or_abs_path}" PARENT_SCOPE) + else() + set(${var} "${root}/${rel_or_abs_path}" PARENT_SCOPE) + endif() +endfunction() + +# configure a pkg-config file libvalhalla.pc +function(configure_valhalla_pc) + set(prefix ${CMAKE_INSTALL_PREFIX}) + set(exec_prefix ${prefix}) + set_variable_from_rel_or_absolute_path("libdir" "$\{prefix\}" "${CMAKE_INSTALL_LIBDIR}") + set_variable_from_rel_or_absolute_path("includedir" "$\{prefix\}" "${CMAKE_INSTALL_INCLUDEDIR}") + # Build strings of dependencies + set(LIBS "") + set(REQUIRES "zlib") + set(LIBS_PRIVATE "${CMAKE_THREAD_LIBS_INIT}") + set(CFLAGS "-I$\{includedir\}/valhalla/third_party") + + if(TARGET protobuf::libprotobuf-lite) + list(APPEND REQUIRES protobuf-lite) + else() + list(APPEND REQUIRES protobuf) + endif() + + if(ENABLE_DATA_TOOLS) + list(APPEND REQUIRES spatialite sqlite3 luajit geos) + endif() + if(ENABLE_HTTP OR ENABLE_PYTHON_BINDINGS) + list(APPEND REQUIRES libcurl) + endif() + if(ENABLE_SERVICES) + list(APPEND REQUIRES libprime_server) + endif() + if(WIN32 AND NOT MINGW) + list(APPEND LIBS_PRIVATE -lole32 -lshell32) + else() + if(NOT "-lm" IN_LIST LIBS_PRIVATE) + list(APPEND LIBS_PRIVATE -lm) + endif() + endif() + list(JOIN LIBS " " LIBS) + list(JOIN REQUIRES " " REQUIRES) + list(JOIN LIBS_PRIVATE " " LIBS_PRIVATE) + + configure_file( + ${CMAKE_SOURCE_DIR}/libvalhalla.pc.in + ${CMAKE_BINARY_DIR}/libvalhalla.pc + @ONLY) +endfunction() diff --git a/cmake/SanitizerOptions.cmake b/cmake/ValhallaSanitizerOptions.cmake similarity index 78% rename from cmake/SanitizerOptions.cmake rename to cmake/ValhallaSanitizerOptions.cmake index 229fc9f4b3..a0d9052762 100644 --- a/cmake/SanitizerOptions.cmake +++ b/cmake/ValhallaSanitizerOptions.cmake @@ -27,10 +27,10 @@ if(ENABLE_UNDEFINED_SANITIZER) append_flags_if_supported(SANITIZER_SHARED_LINKER_FLAGS_LIST -fsanitize=undefined -fno-sanitize=vptr) endif() -string(REPLACE ";" " " SANITIZER_FLAGS_STR "${SANITIZER_FLAGS_LIST}") -string(REPLACE ";" " " SANITIZER_EXE_LINKER_FLAGS_STR "${SANITIZER_EXE_LINKER_FLAGS_LIST}") -string(REPLACE ";" " " SANITIZER_SHARED_LINKER_FLAGS_STR "${SANITIZER_SHARED_LINKER_FLAGS_LIST}") +list(JOIN SANITIZER_FLAGS_LIST " " SANITIZER_FLAGS_LIST) +list(JOIN SANITIZER_EXE_LINKER_FLAGS_LIST " " SANITIZER_EXE_LINKER_FLAGS_LIST) +list(JOIN SANITIZER_SHARED_LINKER_FLAGS_LIST " " SANITIZER_SHARED_LINKER_FLAGS_LIST) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS_STR}") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_EXE_LINKER_FLAGS_STR}") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${SANITIZER_SHARED_LINKER_FLAGS_STR}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${SANITIZER_FLAGS_LIST}") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_EXE_LINKER_FLAGS_LIST}") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${SANITIZER_SHARED_LINKER_FLAGS_LIST}") diff --git a/cmake/ValhallaSourceGroups.cmake b/cmake/ValhallaSourceGroups.cmake new file mode 100644 index 0000000000..c688b15406 --- /dev/null +++ b/cmake/ValhallaSourceGroups.cmake @@ -0,0 +1,25 @@ +# create source groups for IDEs such as Visual Studio & XCode + +function(create_source_groups prefix) + foreach(file ${ARGN}) + get_filename_component(file "${file}" ABSOLUTE) + string(FIND "${file}" "${PROJECT_BINARY_DIR}/" pos) + if(pos EQUAL 0) + source_group(TREE "${PROJECT_BINARY_DIR}/" PREFIX "Generated Files" FILES "${file}") + else() + source_group(TREE "${PROJECT_SOURCE_DIR}/" PREFIX "${prefix}" FILES "${file}") + endif() + endforeach() +endfunction() + +function(get_source_path PATH NAME) + if(EXISTS ${VALHALLA_SOURCE_DIR}/src/${NAME}.cc) + set(${PATH} ${VALHALLA_SOURCE_DIR}/src/${NAME}.cc PARENT_SCOPE) + elseif(EXISTS ${VALHALLA_SOURCE_DIR}/src/meili/${NAME}.cc) + set(${PATH} ${VALHALLA_SOURCE_DIR}/src/meili/${NAME}.cc PARENT_SCOPE) + elseif(EXISTS ${VALHALLA_SOURCE_DIR}/src/mjolnir/${NAME}.cc) + set(${PATH} ${VALHALLA_SOURCE_DIR}/src/mjolnir/${NAME}.cc PARENT_SCOPE) + else() + message(FATAL_ERROR "no source path for ${NAME}") + endif() +endfunction() diff --git a/cmake/ValhallaVersion.cmake b/cmake/ValhallaVersion.cmake new file mode 100644 index 0000000000..b1ddb56087 --- /dev/null +++ b/cmake/ValhallaVersion.cmake @@ -0,0 +1,19 @@ +## Get Valhalla version +file(STRINGS "${VALHALLA_SOURCE_DIR}/valhalla/valhalla.h" version_lines REGEX "VALHALLA_VERSION_(MAJOR|MINOR|PATCH)") +foreach(line ${version_lines}) + if("${line}" MATCHES "(VALHALLA_VERSION_(MAJOR|MINOR|PATCH))[\t ]+([0-9]+)") + set(${CMAKE_MATCH_1} ${CMAKE_MATCH_3}) + set(${CMAKE_MATCH_1} ${CMAKE_MATCH_3} PARENT_SCOPE) + endif() +endforeach() +if(DEFINED VALHALLA_VERSION_MAJOR) + set(VERSION "${VALHALLA_VERSION_MAJOR}") + if(DEFINED VALHALLA_VERSION_MINOR) + set(VERSION "${VERSION}.${VALHALLA_VERSION_MINOR}") + if(DEFINED VALHALLA_VERSION_PATCH) + set(VERSION "${VERSION}.${VALHALLA_VERSION_PATCH}") + endif() + endif() +else() + message(FATAL_ERROR "No Valhalla major version") +endif() diff --git a/cmake/ValhallaWarnings.cmake b/cmake/ValhallaWarnings.cmake new file mode 100644 index 0000000000..30c2577e81 --- /dev/null +++ b/cmake/ValhallaWarnings.cmake @@ -0,0 +1,18 @@ +## Declare C++ build configuration variables as part of HandleLibcxxFlags. +# +# - LIBCXX_COMPILE_FLAGS: flags used to compile libc++ +# - LIBCXX_LINK_FLAGS: flags used to link libc++ +# - LIBCXX_LIBRARIES: libraries to link libc++ to +set(LIBCXX_COMPILE_FLAGS "") +set(LIBCXX_LINK_FLAGS "") +set(LIBCXX_LIBRARIES "") + +# Include build macros for updating configuration variables +include(HandleLibcxxFlags) + +function (cxx_add_warning_flags target) + target_add_compile_flags_if_supported(${target} PRIVATE -Wall -Wextra) + if (ENABLE_WERROR) + target_add_compile_flags_if_supported(${target} PRIVATE -Werror) + endif() +endfunction() diff --git a/cmake/conan.cmake b/cmake/conan.cmake deleted file mode 100644 index 8e2a15c094..0000000000 --- a/cmake/conan.cmake +++ /dev/null @@ -1,909 +0,0 @@ -# The MIT License (MIT) - -# Copyright (c) 2018 JFrog - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - - - -# This file comes from: https://github.com/conan-io/cmake-conan. Please refer -# to this repository for issues and documentation. - -# Its purpose is to wrap and launch Conan C/C++ Package Manager when cmake is called. -# It will take CMake current settings (os, compiler, compiler version, architecture) -# and translate them to conan settings for installing and retrieving dependencies. - -# It is intended to facilitate developers building projects that have conan dependencies, -# but it is only necessary on the end-user side. It is not necessary to create conan -# packages, in fact it shouldn't be use for that. Check the project documentation. - -# version: 0.17.0 - -include(CMakeParseArguments) - -function(_get_msvc_ide_version result) - set(${result} "" PARENT_SCOPE) - if(NOT MSVC_VERSION VERSION_LESS 1400 AND MSVC_VERSION VERSION_LESS 1500) - set(${result} 8 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1500 AND MSVC_VERSION VERSION_LESS 1600) - set(${result} 9 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1600 AND MSVC_VERSION VERSION_LESS 1700) - set(${result} 10 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1700 AND MSVC_VERSION VERSION_LESS 1800) - set(${result} 11 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1800 AND MSVC_VERSION VERSION_LESS 1900) - set(${result} 12 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1900 AND MSVC_VERSION VERSION_LESS 1910) - set(${result} 14 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1910 AND MSVC_VERSION VERSION_LESS 1920) - set(${result} 15 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1920 AND MSVC_VERSION VERSION_LESS 1930) - set(${result} 16 PARENT_SCOPE) - elseif(NOT MSVC_VERSION VERSION_LESS 1930 AND MSVC_VERSION VERSION_LESS 1940) - set(${result} 17 PARENT_SCOPE) - else() - message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]") - endif() -endfunction() - -macro(_conan_detect_build_type) - conan_parse_arguments(${ARGV}) - - if(ARGUMENTS_BUILD_TYPE) - set(_CONAN_SETTING_BUILD_TYPE ${ARGUMENTS_BUILD_TYPE}) - elseif(CMAKE_BUILD_TYPE) - set(_CONAN_SETTING_BUILD_TYPE ${CMAKE_BUILD_TYPE}) - else() - message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)") - endif() - - string(TOUPPER ${_CONAN_SETTING_BUILD_TYPE} _CONAN_SETTING_BUILD_TYPE_UPPER) - if (_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "DEBUG") - set(_CONAN_SETTING_BUILD_TYPE "Debug") - elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELEASE") - set(_CONAN_SETTING_BUILD_TYPE "Release") - elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "RELWITHDEBINFO") - set(_CONAN_SETTING_BUILD_TYPE "RelWithDebInfo") - elseif(_CONAN_SETTING_BUILD_TYPE_UPPER STREQUAL "MINSIZEREL") - set(_CONAN_SETTING_BUILD_TYPE "MinSizeRel") - endif() -endmacro() - -macro(_conan_check_system_name) - #handle -s os setting - if(CMAKE_SYSTEM_NAME AND NOT CMAKE_SYSTEM_NAME STREQUAL "Generic") - #use default conan os setting if CMAKE_SYSTEM_NAME is not defined - set(CONAN_SYSTEM_NAME ${CMAKE_SYSTEM_NAME}) - if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") - set(CONAN_SYSTEM_NAME Macos) - endif() - if(${CMAKE_SYSTEM_NAME} STREQUAL "QNX") - set(CONAN_SYSTEM_NAME Neutrino) - endif() - set(CONAN_SUPPORTED_PLATFORMS Windows Linux Macos Android iOS FreeBSD WindowsStore WindowsCE watchOS tvOS FreeBSD SunOS AIX Arduino Emscripten Neutrino) - list (FIND CONAN_SUPPORTED_PLATFORMS "${CONAN_SYSTEM_NAME}" _index) - if (${_index} GREATER -1) - #check if the cmake system is a conan supported one - set(_CONAN_SETTING_OS ${CONAN_SYSTEM_NAME}) - else() - message(FATAL_ERROR "cmake system ${CONAN_SYSTEM_NAME} is not supported by conan. Use one of ${CONAN_SUPPORTED_PLATFORMS}") - endif() - endif() -endmacro() - -macro(_conan_check_language) - get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES) - if (";${_languages};" MATCHES ";CXX;") - set(LANGUAGE CXX) - set(USING_CXX 1) - elseif (";${_languages};" MATCHES ";C;") - set(LANGUAGE C) - set(USING_CXX 0) - else () - message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unabled to detect compiler version.") - endif() -endmacro() - -macro(_conan_detect_compiler) - - conan_parse_arguments(${ARGV}) - - if(ARGUMENTS_ARCH) - set(_CONAN_SETTING_ARCH ${ARGUMENTS_ARCH}) - endif() - - if(USING_CXX) - set(_CONAN_SETTING_COMPILER_CPPSTD ${CMAKE_CXX_STANDARD}) - endif() - - if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU) - # using GCC - # TODO: Handle other params - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - set(COMPILER_VERSION ${MAJOR}.${MINOR}) - if(${MAJOR} GREATER 4) - set(COMPILER_VERSION ${MAJOR}) - endif() - set(_CONAN_SETTING_COMPILER gcc) - set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Intel) - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - set(COMPILER_VERSION ${MAJOR}.${MINOR}) - set(_CONAN_SETTING_COMPILER intel) - set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION}) - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL AppleClang) - # using AppleClang - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - set(_CONAN_SETTING_COMPILER apple-clang) - set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR}) - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang) - string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION}) - list(GET VERSION_LIST 0 MAJOR) - list(GET VERSION_LIST 1 MINOR) - set(_CONAN_SETTING_COMPILER clang) - set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR}) - if(APPLE) - cmake_policy(GET CMP0025 APPLE_CLANG_POLICY) - if(NOT APPLE_CLANG_POLICY STREQUAL NEW) - message(STATUS "Conan: APPLE and Clang detected. Assuming apple-clang compiler. Set CMP0025 to avoid it") - set(_CONAN_SETTING_COMPILER apple-clang) - endif() - endif() - if(${_CONAN_SETTING_COMPILER} STREQUAL clang AND ${MAJOR} GREATER 7) - set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}) - endif() - if (USING_CXX) - conan_cmake_detect_unix_libcxx(_LIBCXX) - set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX}) - endif () - elseif(${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL MSVC) - set(_VISUAL "Visual Studio") - _get_msvc_ide_version(_VISUAL_VERSION) - if("${_VISUAL_VERSION}" STREQUAL "") - message(FATAL_ERROR "Conan: Visual Studio not recognized") - else() - set(_CONAN_SETTING_COMPILER ${_VISUAL}) - set(_CONAN_SETTING_COMPILER_VERSION ${_VISUAL_VERSION}) - endif() - - if(NOT _CONAN_SETTING_ARCH) - if (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "64") - set(_CONAN_SETTING_ARCH x86_64) - elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "^ARM") - message(STATUS "Conan: Using default ARM architecture from MSVC") - set(_CONAN_SETTING_ARCH armv6) - elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "86") - set(_CONAN_SETTING_ARCH x86) - else () - message(FATAL_ERROR "Conan: Unknown MSVC architecture [${MSVC_${LANGUAGE}_ARCHITECTURE_ID}]") - endif() - endif() - - conan_cmake_detect_vs_runtime(_vs_runtime ${ARGV}) - message(STATUS "Conan: Detected VS runtime: ${_vs_runtime}") - set(_CONAN_SETTING_COMPILER_RUNTIME ${_vs_runtime}) - - if (CMAKE_GENERATOR_TOOLSET) - set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) - elseif(CMAKE_VS_PLATFORM_TOOLSET AND (CMAKE_GENERATOR STREQUAL "Ninja")) - set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET}) - endif() - else() - message(FATAL_ERROR "Conan: compiler setup not recognized") - endif() - -endmacro() - -function(conan_cmake_settings result) - #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER}) - #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER_ID}) - #message(STATUS "VERSION " ${CMAKE_CXX_COMPILER_VERSION}) - #message(STATUS "FLAGS " ${CMAKE_LANG_FLAGS}) - #message(STATUS "LIB ARCH " ${CMAKE_CXX_LIBRARY_ARCHITECTURE}) - #message(STATUS "BUILD TYPE " ${CMAKE_BUILD_TYPE}) - #message(STATUS "GENERATOR " ${CMAKE_GENERATOR}) - #message(STATUS "GENERATOR WIN64 " ${CMAKE_CL_64}) - - message(STATUS "Conan: Automatic detection of conan settings from cmake") - - conan_parse_arguments(${ARGV}) - - _conan_detect_build_type(${ARGV}) - - _conan_check_system_name() - - _conan_check_language() - - _conan_detect_compiler(${ARGV}) - - # If profile is defined it is used - if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND ARGUMENTS_DEBUG_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_DEBUG_PROFILE}) - elseif(CMAKE_BUILD_TYPE STREQUAL "Release" AND ARGUMENTS_RELEASE_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_RELEASE_PROFILE}) - elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND ARGUMENTS_RELWITHDEBINFO_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_RELWITHDEBINFO_PROFILE}) - elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" AND ARGUMENTS_MINSIZEREL_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_MINSIZEREL_PROFILE}) - elseif(ARGUMENTS_PROFILE) - set(_APPLIED_PROFILES ${ARGUMENTS_PROFILE}) - endif() - - foreach(ARG ${_APPLIED_PROFILES}) - set(_SETTINGS ${_SETTINGS} -pr=${ARG}) - endforeach() - foreach(ARG ${ARGUMENTS_PROFILE_BUILD}) - conan_check(VERSION 1.24.0 REQUIRED DETECT_QUIET) - set(_SETTINGS ${_SETTINGS} -pr:b=${ARG}) - endforeach() - - if(NOT _SETTINGS OR ARGUMENTS_PROFILE_AUTO STREQUAL "ALL") - set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version - compiler.runtime compiler.libcxx compiler.toolset) - endif() - - # remove any manually specified settings from the autodetected settings - foreach(ARG ${ARGUMENTS_SETTINGS}) - string(REGEX MATCH "[^=]*" MANUAL_SETTING "${ARG}") - message(STATUS "Conan: ${MANUAL_SETTING} was added as an argument. Not using the autodetected one.") - list(REMOVE_ITEM ARGUMENTS_PROFILE_AUTO "${MANUAL_SETTING}") - endforeach() - - # Automatic from CMake - foreach(ARG ${ARGUMENTS_PROFILE_AUTO}) - string(TOUPPER ${ARG} _arg_name) - string(REPLACE "." "_" _arg_name ${_arg_name}) - if(_CONAN_SETTING_${_arg_name}) - set(_SETTINGS ${_SETTINGS} -s ${ARG}=${_CONAN_SETTING_${_arg_name}}) - endif() - endforeach() - - foreach(ARG ${ARGUMENTS_SETTINGS}) - set(_SETTINGS ${_SETTINGS} -s ${ARG}) - endforeach() - - message(STATUS "Conan: Settings= ${_SETTINGS}") - - set(${result} ${_SETTINGS} PARENT_SCOPE) -endfunction() - - -function(conan_cmake_detect_unix_libcxx result) - # Take into account any -stdlib in compile options - get_directory_property(compile_options DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_OPTIONS) - string(GENEX_STRIP "${compile_options}" compile_options) - - # Take into account any _GLIBCXX_USE_CXX11_ABI in compile definitions - get_directory_property(defines DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS) - string(GENEX_STRIP "${defines}" defines) - - foreach(define ${defines}) - if(define MATCHES "_GLIBCXX_USE_CXX11_ABI") - if(define MATCHES "^-D") - set(compile_options ${compile_options} "${define}") - else() - set(compile_options ${compile_options} "-D${define}") - endif() - endif() - endforeach() - - # add additional compiler options ala cmRulePlaceholderExpander::ExpandRuleVariable - set(EXPAND_CXX_COMPILER ${CMAKE_CXX_COMPILER}) - if(CMAKE_CXX_COMPILER_ARG1) - # CMake splits CXX="foo bar baz" into CMAKE_CXX_COMPILER="foo", CMAKE_CXX_COMPILER_ARG1="bar baz" - # without this, ccache, winegcc, or other wrappers might lose all their arguments - separate_arguments(SPLIT_CXX_COMPILER_ARG1 NATIVE_COMMAND ${CMAKE_CXX_COMPILER_ARG1}) - list(APPEND EXPAND_CXX_COMPILER ${SPLIT_CXX_COMPILER_ARG1}) - endif() - - if(CMAKE_CXX_COMPILE_OPTIONS_TARGET AND CMAKE_CXX_COMPILER_TARGET) - # without --target= we may be calling the wrong underlying GCC - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_TARGET}${CMAKE_CXX_COMPILER_TARGET}") - endif() - - if(CMAKE_CXX_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN AND CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN) - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN}${CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN}") - endif() - - if(CMAKE_CXX_COMPILE_OPTIONS_SYSROOT) - # without --sysroot= we may find the wrong #include - if(CMAKE_SYSROOT_COMPILE) - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT_COMPILE}") - elseif(CMAKE_SYSROOT) - list(APPEND EXPAND_CXX_COMPILER "${CMAKE_CXX_COMPILE_OPTIONS_SYSROOT}${CMAKE_SYSROOT}") - endif() - endif() - - separate_arguments(SPLIT_CXX_FLAGS NATIVE_COMMAND ${CMAKE_CXX_FLAGS}) - - if(CMAKE_OSX_SYSROOT) - set(xcode_sysroot_option "--sysroot=${CMAKE_OSX_SYSROOT}") - endif() - - execute_process( - COMMAND ${CMAKE_COMMAND} -E echo "#include " - COMMAND ${EXPAND_CXX_COMPILER} ${SPLIT_CXX_FLAGS} -x c++ ${xcode_sysroot_option} ${compile_options} -E -dM - - OUTPUT_VARIABLE string_defines - ) - - if(string_defines MATCHES "#define __GLIBCXX__") - # Allow -D_GLIBCXX_USE_CXX11_ABI=ON/OFF as argument to cmake - if(DEFINED _GLIBCXX_USE_CXX11_ABI) - if(_GLIBCXX_USE_CXX11_ABI) - set(${result} libstdc++11 PARENT_SCOPE) - return() - else() - set(${result} libstdc++ PARENT_SCOPE) - return() - endif() - endif() - - if(string_defines MATCHES "#define _GLIBCXX_USE_CXX11_ABI 1\n") - set(${result} libstdc++11 PARENT_SCOPE) - else() - # Either the compiler is missing the define because it is old, and so - # it can't use the new abi, or the compiler was configured to use the - # old abi by the user or distro (e.g. devtoolset on RHEL/CentOS) - set(${result} libstdc++ PARENT_SCOPE) - endif() - else() - set(${result} libc++ PARENT_SCOPE) - endif() -endfunction() - -function(conan_cmake_detect_vs_runtime result) - - conan_parse_arguments(${ARGV}) - if(ARGUMENTS_BUILD_TYPE) - set(build_type "${ARGUMENTS_BUILD_TYPE}") - elseif(CMAKE_BUILD_TYPE) - set(build_type "${CMAKE_BUILD_TYPE}") - else() - message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)") - endif() - - if(build_type) - string(TOUPPER "${build_type}" build_type) - endif() - set(variables CMAKE_CXX_FLAGS_${build_type} CMAKE_C_FLAGS_${build_type} CMAKE_CXX_FLAGS CMAKE_C_FLAGS) - foreach(variable ${variables}) - if(NOT "${${variable}}" STREQUAL "") - string(REPLACE " " ";" flags "${${variable}}") - foreach (flag ${flags}) - if("${flag}" STREQUAL "/MD" OR "${flag}" STREQUAL "/MDd" OR "${flag}" STREQUAL "/MT" OR "${flag}" STREQUAL "/MTd") - string(SUBSTRING "${flag}" 1 -1 runtime) - set(${result} "${runtime}" PARENT_SCOPE) - return() - endif() - endforeach() - endif() - endforeach() - if("${build_type}" STREQUAL "DEBUG") - set(${result} "MDd" PARENT_SCOPE) - else() - set(${result} "MD" PARENT_SCOPE) - endif() -endfunction() - -function(_collect_settings result) - set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version - compiler.runtime compiler.libcxx compiler.toolset - compiler.cppstd) - foreach(ARG ${ARGUMENTS_PROFILE_AUTO}) - string(TOUPPER ${ARG} _arg_name) - string(REPLACE "." "_" _arg_name ${_arg_name}) - if(_CONAN_SETTING_${_arg_name}) - set(detected_setings ${detected_setings} ${ARG}=${_CONAN_SETTING_${_arg_name}}) - endif() - endforeach() - set(${result} ${detected_setings} PARENT_SCOPE) -endfunction() - -function(conan_cmake_autodetect detected_settings) - _conan_detect_build_type(${ARGV}) - _conan_check_system_name() - _conan_check_language() - _conan_detect_compiler(${ARGV}) - _collect_settings(collected_settings) - set(${detected_settings} ${collected_settings} PARENT_SCOPE) -endfunction() - -macro(conan_parse_arguments) - set(options BASIC_SETUP CMAKE_TARGETS UPDATE KEEP_RPATHS NO_LOAD NO_OUTPUT_DIRS OUTPUT_QUIET NO_IMPORTS SKIP_STD) - set(oneValueArgs CONANFILE ARCH BUILD_TYPE INSTALL_FOLDER CONAN_COMMAND) - set(multiValueArgs DEBUG_PROFILE RELEASE_PROFILE RELWITHDEBINFO_PROFILE MINSIZEREL_PROFILE - PROFILE REQUIRES OPTIONS IMPORTS SETTINGS BUILD ENV GENERATORS PROFILE_AUTO - INSTALL_ARGS CONFIGURATION_TYPES PROFILE_BUILD BUILD_REQUIRES) - cmake_parse_arguments(ARGUMENTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) -endmacro() - -function(old_conan_cmake_install) - # Calls "conan install" - # Argument BUILD is equivalent to --build={missing, PkgName,...} or - # --build when argument is 'BUILD all' (which builds all packages from source) - # Argument CONAN_COMMAND, to specify the conan path, e.g. in case of running from source - # cmake does not identify conan as command, even if it is +x and it is in the path - conan_parse_arguments(${ARGV}) - - if(CONAN_CMAKE_MULTI) - set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake_multi) - else() - set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake) - endif() - - set(CONAN_BUILD_POLICY "") - foreach(ARG ${ARGUMENTS_BUILD}) - if(${ARG} STREQUAL "all") - set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build) - break() - else() - set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build=${ARG}) - endif() - endforeach() - if(ARGUMENTS_CONAN_COMMAND) - set(CONAN_CMD ${ARGUMENTS_CONAN_COMMAND}) - else() - conan_check(REQUIRED) - endif() - set(CONAN_OPTIONS "") - if(ARGUMENTS_CONANFILE) - if(IS_ABSOLUTE ${ARGUMENTS_CONANFILE}) - set(CONANFILE ${ARGUMENTS_CONANFILE}) - else() - set(CONANFILE ${CMAKE_CURRENT_SOURCE_DIR}/${ARGUMENTS_CONANFILE}) - endif() - else() - set(CONANFILE ".") - endif() - foreach(ARG ${ARGUMENTS_OPTIONS}) - set(CONAN_OPTIONS ${CONAN_OPTIONS} -o=${ARG}) - endforeach() - if(ARGUMENTS_UPDATE) - set(CONAN_INSTALL_UPDATE --update) - endif() - if(ARGUMENTS_NO_IMPORTS) - set(CONAN_INSTALL_NO_IMPORTS --no-imports) - endif() - set(CONAN_INSTALL_FOLDER "") - if(ARGUMENTS_INSTALL_FOLDER) - set(CONAN_INSTALL_FOLDER -if=${ARGUMENTS_INSTALL_FOLDER}) - endif() - foreach(ARG ${ARGUMENTS_GENERATORS}) - set(CONAN_GENERATORS ${CONAN_GENERATORS} -g=${ARG}) - endforeach() - foreach(ARG ${ARGUMENTS_ENV}) - set(CONAN_ENV_VARS ${CONAN_ENV_VARS} -e=${ARG}) - endforeach() - set(conan_args install ${CONANFILE} ${settings} ${CONAN_ENV_VARS} ${CONAN_GENERATORS} ${CONAN_BUILD_POLICY} ${CONAN_INSTALL_UPDATE} ${CONAN_INSTALL_NO_IMPORTS} ${CONAN_OPTIONS} ${CONAN_INSTALL_FOLDER} ${ARGUMENTS_INSTALL_ARGS}) - - string (REPLACE ";" " " _conan_args "${conan_args}") - message(STATUS "Conan executing: ${CONAN_CMD} ${_conan_args}") - - if(ARGUMENTS_OUTPUT_QUIET) - execute_process(COMMAND ${CONAN_CMD} ${conan_args} - RESULT_VARIABLE return_code - OUTPUT_VARIABLE conan_output - ERROR_VARIABLE conan_output - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - else() - execute_process(COMMAND ${CONAN_CMD} ${conan_args} - RESULT_VARIABLE return_code - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - endif() - - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan install failed='${return_code}'") - endif() - -endfunction() - -function(conan_cmake_install) - if(DEFINED CONAN_COMMAND) - set(CONAN_CMD ${CONAN_COMMAND}) - else() - conan_check(REQUIRED) - endif() - - set(installOptions UPDATE NO_IMPORTS OUTPUT_QUIET ERROR_QUIET) - set(installOneValueArgs PATH_OR_REFERENCE REFERENCE REMOTE LOCKFILE LOCKFILE_OUT LOCKFILE_NODE_ID INSTALL_FOLDER) - set(installMultiValueArgs GENERATOR BUILD ENV ENV_HOST ENV_BUILD OPTIONS_HOST OPTIONS OPTIONS_BUILD PROFILE - PROFILE_HOST PROFILE_BUILD SETTINGS SETTINGS_HOST SETTINGS_BUILD) - cmake_parse_arguments(ARGS "${installOptions}" "${installOneValueArgs}" "${installMultiValueArgs}" ${ARGN}) - foreach(arg ${installOptions}) - if(ARGS_${arg}) - set(${arg} ${${arg}} ${ARGS_${arg}}) - endif() - endforeach() - foreach(arg ${installOneValueArgs}) - if(DEFINED ARGS_${arg}) - if("${arg}" STREQUAL "REMOTE") - set(flag "--remote") - elseif("${arg}" STREQUAL "LOCKFILE") - set(flag "--lockfile") - elseif("${arg}" STREQUAL "LOCKFILE_OUT") - set(flag "--lockfile-out") - elseif("${arg}" STREQUAL "LOCKFILE_NODE_ID") - set(flag "--lockfile-node-id") - elseif("${arg}" STREQUAL "INSTALL_FOLDER") - set(flag "--install-folder") - endif() - set(${arg} ${${arg}} ${flag} ${ARGS_${arg}}) - endif() - endforeach() - foreach(arg ${installMultiValueArgs}) - if(DEFINED ARGS_${arg}) - if("${arg}" STREQUAL "GENERATOR") - set(flag "--generator") - elseif("${arg}" STREQUAL "BUILD") - set(flag "--build") - elseif("${arg}" STREQUAL "ENV") - set(flag "--env") - elseif("${arg}" STREQUAL "ENV_HOST") - set(flag "--env:host") - elseif("${arg}" STREQUAL "ENV_BUILD") - set(flag "--env:build") - elseif("${arg}" STREQUAL "OPTIONS") - set(flag "--options") - elseif("${arg}" STREQUAL "OPTIONS_HOST") - set(flag "--options:host") - elseif("${arg}" STREQUAL "OPTIONS_BUILD") - set(flag "--options:build") - elseif("${arg}" STREQUAL "PROFILE") - set(flag "--profile") - elseif("${arg}" STREQUAL "PROFILE_HOST") - set(flag "--profile:host") - elseif("${arg}" STREQUAL "PROFILE_BUILD") - set(flag "--profile:build") - elseif("${arg}" STREQUAL "SETTINGS") - set(flag "--settings") - elseif("${arg}" STREQUAL "SETTINGS_HOST") - set(flag "--settings:host") - elseif("${arg}" STREQUAL "SETTINGS_BUILD") - set(flag "--settings:build") - endif() - list(LENGTH ARGS_${arg} numargs) - foreach(item ${ARGS_${arg}}) - if(${item} STREQUAL "all" AND ${arg} STREQUAL "BUILD") - set(${arg} "--build") - break() - endif() - set(${arg} ${${arg}} ${flag} ${item}) - endforeach() - endif() - endforeach() - if(DEFINED UPDATE) - set(UPDATE --update) - endif() - if(DEFINED NO_IMPORTS) - set(NO_IMPORTS --no-imports) - endif() - set(install_args install ${PATH_OR_REFERENCE} ${REFERENCE} ${UPDATE} ${NO_IMPORTS} ${REMOTE} ${LOCKFILE} ${LOCKFILE_OUT} ${LOCKFILE_NODE_ID} ${INSTALL_FOLDER} - ${GENERATOR} ${BUILD} ${ENV} ${ENV_HOST} ${ENV_BUILD} ${OPTIONS} ${OPTIONS_HOST} ${OPTIONS_BUILD} - ${PROFILE} ${PROFILE_HOST} ${PROFILE_BUILD} ${SETTINGS} ${SETTINGS_HOST} ${SETTINGS_BUILD}) - - string(REPLACE ";" " " _install_args "${install_args}") - message(STATUS "Conan executing: ${CONAN_CMD} ${_install_args}") - - if(ARGS_OUTPUT_QUIET) - set(OUTPUT_OPT OUTPUT_QUIET) - endif() - if(ARGS_ERROR_QUIET) - set(ERROR_OPT ERROR_QUIET) - endif() - - execute_process(COMMAND ${CONAN_CMD} ${install_args} - RESULT_VARIABLE return_code - ${OUTPUT_OPT} - ${ERROR_OPT} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - - if(NOT "${return_code}" STREQUAL "0") - if (ARGS_ERROR_QUIET) - message(WARNING "Conan install failed='${return_code}'") - else() - message(FATAL_ERROR "Conan install failed='${return_code}'") - endif() - endif() - -endfunction() - -function(conan_cmake_setup_conanfile) - conan_parse_arguments(${ARGV}) - if(ARGUMENTS_CONANFILE) - get_filename_component(_CONANFILE_NAME ${ARGUMENTS_CONANFILE} NAME) - # configure_file will make sure cmake re-runs when conanfile is updated - configure_file(${ARGUMENTS_CONANFILE} ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk COPYONLY) - file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${_CONANFILE_NAME}.junk) - else() - conan_cmake_generate_conanfile(ON ${ARGV}) - endif() -endfunction() - -function(conan_cmake_configure) - conan_cmake_generate_conanfile(OFF ${ARGV}) -endfunction() - -# Generate, writing in disk a conanfile.txt with the requires, options, and imports -# specified as arguments -# This will be considered as temporary file, generated in CMAKE_CURRENT_BINARY_DIR) -function(conan_cmake_generate_conanfile DEFAULT_GENERATOR) - - conan_parse_arguments(${ARGV}) - - set(_FN "${CMAKE_CURRENT_BINARY_DIR}/conanfile.txt") - file(WRITE ${_FN} "") - - if(DEFINED ARGUMENTS_REQUIRES) - file(APPEND ${_FN} "[requires]\n") - foreach(REQUIRE ${ARGUMENTS_REQUIRES}) - file(APPEND ${_FN} ${REQUIRE} "\n") - endforeach() - endif() - - if (DEFAULT_GENERATOR OR DEFINED ARGUMENTS_GENERATORS) - file(APPEND ${_FN} "[generators]\n") - if (DEFAULT_GENERATOR) - file(APPEND ${_FN} "cmake\n") - endif() - if (DEFINED ARGUMENTS_GENERATORS) - foreach(GENERATOR ${ARGUMENTS_GENERATORS}) - file(APPEND ${_FN} ${GENERATOR} "\n") - endforeach() - endif() - endif() - - if(DEFINED ARGUMENTS_BUILD_REQUIRES) - file(APPEND ${_FN} "[build_requires]\n") - foreach(BUILD_REQUIRE ${ARGUMENTS_BUILD_REQUIRES}) - file(APPEND ${_FN} ${BUILD_REQUIRE} "\n") - endforeach() - endif() - - if(DEFINED ARGUMENTS_IMPORTS) - file(APPEND ${_FN} "[imports]\n") - foreach(IMPORTS ${ARGUMENTS_IMPORTS}) - file(APPEND ${_FN} ${IMPORTS} "\n") - endforeach() - endif() - - if(DEFINED ARGUMENTS_OPTIONS) - file(APPEND ${_FN} "[options]\n") - foreach(OPTION ${ARGUMENTS_OPTIONS}) - file(APPEND ${_FN} ${OPTION} "\n") - endforeach() - endif() - -endfunction() - - -macro(conan_load_buildinfo) - if(CONAN_CMAKE_MULTI) - set(_CONANBUILDINFO conanbuildinfo_multi.cmake) - else() - set(_CONANBUILDINFO conanbuildinfo.cmake) - endif() - if(ARGUMENTS_INSTALL_FOLDER) - set(_CONANBUILDINFOFOLDER ${ARGUMENTS_INSTALL_FOLDER}) - else() - set(_CONANBUILDINFOFOLDER ${CMAKE_CURRENT_BINARY_DIR}) - endif() - # Checks for the existence of conanbuildinfo.cmake, and loads it - # important that it is macro, so variables defined at parent scope - if(EXISTS "${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}") - message(STATUS "Conan: Loading ${_CONANBUILDINFO}") - include(${_CONANBUILDINFOFOLDER}/${_CONANBUILDINFO}) - else() - message(FATAL_ERROR "${_CONANBUILDINFO} doesn't exist in ${CMAKE_CURRENT_BINARY_DIR}") - endif() -endmacro() - - -macro(conan_cmake_run) - conan_parse_arguments(${ARGV}) - - if(ARGUMENTS_CONFIGURATION_TYPES AND NOT CMAKE_CONFIGURATION_TYPES) - message(WARNING "CONFIGURATION_TYPES should only be specified for multi-configuration generators") - elseif(ARGUMENTS_CONFIGURATION_TYPES AND ARGUMENTS_BUILD_TYPE) - message(WARNING "CONFIGURATION_TYPES and BUILD_TYPE arguments should not be defined at the same time.") - endif() - - if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE AND NOT CONAN_EXPORTED - AND NOT ARGUMENTS_BUILD_TYPE) - set(CONAN_CMAKE_MULTI ON) - if (NOT ARGUMENTS_CONFIGURATION_TYPES) - set(ARGUMENTS_CONFIGURATION_TYPES "Release;Debug") - endif() - message(STATUS "Conan: Using cmake-multi generator") - else() - set(CONAN_CMAKE_MULTI OFF) - endif() - - if(NOT CONAN_EXPORTED) - conan_cmake_setup_conanfile(${ARGV}) - if(CONAN_CMAKE_MULTI) - foreach(CMAKE_BUILD_TYPE ${ARGUMENTS_CONFIGURATION_TYPES}) - set(ENV{CONAN_IMPORT_PATH} ${CMAKE_BUILD_TYPE}) - conan_cmake_settings(settings ${ARGV}) - old_conan_cmake_install(SETTINGS ${settings} ${ARGV}) - endforeach() - set(CMAKE_BUILD_TYPE) - else() - conan_cmake_settings(settings ${ARGV}) - old_conan_cmake_install(SETTINGS ${settings} ${ARGV}) - endif() - endif() - - if (NOT ARGUMENTS_NO_LOAD) - conan_load_buildinfo() - endif() - - if(ARGUMENTS_BASIC_SETUP) - foreach(_option CMAKE_TARGETS KEEP_RPATHS NO_OUTPUT_DIRS SKIP_STD) - if(ARGUMENTS_${_option}) - if(${_option} STREQUAL "CMAKE_TARGETS") - list(APPEND _setup_options "TARGETS") - else() - list(APPEND _setup_options ${_option}) - endif() - endif() - endforeach() - conan_basic_setup(${_setup_options}) - endif() -endmacro() - -macro(conan_check) - # Checks conan availability in PATH - # Arguments REQUIRED, DETECT_QUIET and VERSION are optional - # Example usage: - # conan_check(VERSION 1.0.0 REQUIRED) - set(options REQUIRED DETECT_QUIET) - set(oneValueArgs VERSION) - cmake_parse_arguments(CONAN "${options}" "${oneValueArgs}" "" ${ARGN}) - if(NOT CONAN_DETECT_QUIET) - message(STATUS "Conan: checking conan executable") - endif() - - find_program(CONAN_CMD conan) - if(NOT CONAN_CMD AND CONAN_REQUIRED) - message(FATAL_ERROR "Conan executable not found! Please install conan.") - endif() - if(NOT CONAN_DETECT_QUIET) - message(STATUS "Conan: Found program ${CONAN_CMD}") - endif() - execute_process(COMMAND ${CONAN_CMD} --version - RESULT_VARIABLE return_code - OUTPUT_VARIABLE CONAN_VERSION_OUTPUT - ERROR_VARIABLE CONAN_VERSION_OUTPUT) - - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan --version failed='${return_code}'") - endif() - - if(NOT CONAN_DETECT_QUIET) - string(STRIP "${CONAN_VERSION_OUTPUT}" _CONAN_VERSION_OUTPUT) - message(STATUS "Conan: Version found ${_CONAN_VERSION_OUTPUT}") - endif() - - if(DEFINED CONAN_VERSION) - string(REGEX MATCH ".*Conan version ([0-9]+\\.[0-9]+\\.[0-9]+)" FOO - "${CONAN_VERSION_OUTPUT}") - if(${CMAKE_MATCH_1} VERSION_LESS ${CONAN_VERSION}) - message(FATAL_ERROR "Conan outdated. Installed: ${CMAKE_MATCH_1}, \ - required: ${CONAN_VERSION}. Consider updating via 'pip \ - install conan==${CONAN_VERSION}'.") - endif() - endif() -endmacro() - -function(conan_add_remote) - # Adds a remote - # Arguments URL and NAME are required, INDEX, COMMAND and VERIFY_SSL are optional - # Example usage: - # conan_add_remote(NAME bincrafters INDEX 1 - # URL https://api.bintray.com/conan/bincrafters/public-conan - # VERIFY_SSL True) - set(oneValueArgs URL NAME INDEX COMMAND VERIFY_SSL) - cmake_parse_arguments(CONAN "" "${oneValueArgs}" "" ${ARGN}) - - if(DEFINED CONAN_INDEX) - set(CONAN_INDEX_ARG "-i ${CONAN_INDEX}") - endif() - if(DEFINED CONAN_COMMAND) - set(CONAN_CMD ${CONAN_COMMAND}) - else() - conan_check(REQUIRED DETECT_QUIET) - endif() - set(CONAN_VERIFY_SSL_ARG "True") - if(DEFINED CONAN_VERIFY_SSL) - set(CONAN_VERIFY_SSL_ARG ${CONAN_VERIFY_SSL}) - endif() - message(STATUS "Conan: Adding ${CONAN_NAME} remote repository (${CONAN_URL}) verify ssl (${CONAN_VERIFY_SSL_ARG})") - execute_process(COMMAND ${CONAN_CMD} remote add ${CONAN_NAME} ${CONAN_INDEX_ARG} -f ${CONAN_URL} ${CONAN_VERIFY_SSL_ARG} - RESULT_VARIABLE return_code) - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan remote failed='${return_code}'") - endif() -endfunction() - -macro(conan_config_install) - # install a full configuration from a local or remote zip file - # Argument ITEM is required, arguments TYPE, SOURCE, TARGET and VERIFY_SSL are optional - # Example usage: - # conan_config_install(ITEM https://github.com/conan-io/cmake-conan.git - # TYPE git SOURCE source-folder TARGET target-folder VERIFY_SSL false) - set(oneValueArgs ITEM TYPE SOURCE TARGET VERIFY_SSL) - set(multiValueArgs ARGS) - cmake_parse_arguments(CONAN "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - find_program(CONAN_CMD conan) - if(NOT CONAN_CMD AND CONAN_REQUIRED) - message(FATAL_ERROR "Conan executable not found!") - endif() - - if(DEFINED CONAN_VERIFY_SSL) - set(CONAN_VERIFY_SSL_ARG "--verify-ssl=${CONAN_VERIFY_SSL}") - endif() - - if(DEFINED CONAN_TYPE) - set(CONAN_TYPE_ARG "--type=${CONAN_TYPE}") - endif() - - if(DEFINED CONAN_ARGS) - set(CONAN_ARGS_ARGS "--args=\"${CONAN_ARGS}\"") - endif() - - if(DEFINED CONAN_SOURCE) - set(CONAN_SOURCE_ARGS "--source-folder=${CONAN_SOURCE}") - endif() - - if(DEFINED CONAN_TARGET) - set(CONAN_TARGET_ARGS "--target-folder=${CONAN_TARGET}") - endif() - - set (CONAN_CONFIG_INSTALL_ARGS ${CONAN_VERIFY_SSL_ARG} - ${CONAN_TYPE_ARG} - ${CONAN_ARGS_ARGS} - ${CONAN_SOURCE_ARGS} - ${CONAN_TARGET_ARGS}) - - message(STATUS "Conan: Installing config from ${CONAN_ITEM}") - execute_process(COMMAND ${CONAN_CMD} config install ${CONAN_ITEM} ${CONAN_CONFIG_INSTALL_ARGS} - RESULT_VARIABLE return_code) - if(NOT "${return_code}" STREQUAL "0") - message(FATAL_ERROR "Conan config failed='${return_code}'") - endif() -endmacro() diff --git a/cmake/uninstall.cmake.in b/cmake/uninstall.cmake.in new file mode 100644 index 0000000000..d7a10953b6 --- /dev/null +++ b/cmake/uninstall.cmake.in @@ -0,0 +1,65 @@ +# from https://github.com/OSGeo/PROJ/blob/5920e2998318189970c0fe1e2db60f643515b9ea/cmake/uninstall.cmake.in + +# install_manifest.txt is created in the top build tree, not the project one +if (NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_BINARY_DIR@/install_manifest.txt\"") +endif() + +set(uninstall_file_list "@CMAKE_BINARY_DIR@/install_manifest.txt") +if(EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest_extra.txt") + list(APPEND uninstall_file_list "@CMAKE_CURRENT_BINARY_DIR@/install_manifest_extra.txt") +endif() + +set(dir_list) +foreach (manifest_file IN ITEMS ${uninstall_file_list}) + file(READ "${manifest_file}" files) + string(REGEX REPLACE "\n$" "" files "${files}") + string(REGEX REPLACE "\n" ";" files "${files}") + list(REVERSE files) + foreach (file ${files}) + message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + if (IS_DIRECTORY "$ENV{DESTDIR}${file}") + list(APPEND dir_list "${file}") + elseif (EXISTS "$ENV{DESTDIR}${file}") + get_filename_component(dir "${file}" DIRECTORY) + list(APPEND dir_list "${dir}") + execute_process( + COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(NOT ${rm_retval} EQUAL 0) + message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + endif (NOT ${rm_retval} EQUAL 0) + else () + message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + endif () + endforeach(file) +endforeach() + +while(NOT "${dir_list}" STREQUAL "") + list(REMOVE_DUPLICATES dir_list) + set(new_dir_list) + foreach (file IN ITEMS ${dir_list}) + if (IS_DIRECTORY "$ENV{DESTDIR}${file}" AND "${file}" MATCHES "@CMAKE_INSTALL_PREFIX@[/\\].+") + file(GLOB file_list "$ENV{DESTDIR}${file}/*") + list(LENGTH file_list file_list_len) + # Only remove empty directories + if(file_list_len EQUAL 0) + message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + execute_process( + COMMAND @CMAKE_COMMAND@ -E remove_directory "$ENV{DESTDIR}${file}" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(${rm_retval} EQUAL 0) + get_filename_component(upper_dir "${file}" DIRECTORY) + list(APPEND new_dir_list "${upper_dir}") + else() + message(STATUS "Problem when removing directory \"$ENV{DESTDIR}${file}\"") + endif() + endif() + endif() + endforeach() + set(dir_list "${new_dir_list}") +endwhile() diff --git a/conanfile.txt b/conanfile.txt deleted file mode 100644 index 9dfa3cb050..0000000000 --- a/conanfile.txt +++ /dev/null @@ -1,35 +0,0 @@ -[requires] -boost/1.83.0 - -[generators] -cmake_find_package -json - -[options] -boost:header_only=True -boost:without_chrono=True -boost:without_context=True -boost:without_contract=True -boost:without_coroutine=True -boost:without_date_time=True -boost:without_exception=True -boost:without_fiber=True -boost:without_graph=True -boost:without_graph_parallel=True -boost:without_iostreams=True -boost:without_locale=True -boost:without_log=True -boost:without_math=True -boost:without_mpi=True -boost:without_program_options=True -boost:without_python=True -boost:without_random=True -boost:without_regex=True -boost:without_serialization=True -boost:without_stacktrace=True -boost:without_locale=True -boost:without_test=True -boost:without_thread=True -boost:without_timer=True -boost:without_type_erasure=True -boost:without_wave=True diff --git a/docs/docs/building.md b/docs/docs/building.md index c8ba1df26b..fc39f2db5a 100644 --- a/docs/docs/building.md +++ b/docs/docs/building.md @@ -117,8 +117,10 @@ It's recommended to work with the following toolset: 1. Install the dependencies with `vcpkg`: ``` git -C C:\path\to\vcpkg checkout f330a32 +# only build release versions for vcpkg packages +echo.set(VCPKG_BUILD_TYPE release)>> path\to\vcpkg\triplets\x64-windows.cmake cd C:\path\to\valhalla -C:\path\to\vcpkg.exe install +C:\path\to\vcpkg.exe install --triple x64-windows ``` 2. Let CMake configure the build with the required modules enabled. The final command for `x64` could look like ``` @@ -131,6 +133,18 @@ cmake -B build -S C:\path\to\valhalla --config Release -- /clp:ErrorsOnly /p:Bui The artifacts will be built to `./build/Release`. +#### Troubleshooting + +- if the build fails on something with `date_time`, chances are you don't have [`make`](https://gnuwin32.sourceforge.net/packages/make.htm) and/or [`awk`](https://gnuwin32.sourceforge.net/packages/gawk.htm) installed, which is needed to properly configure `third_party/tz`. Even so, it might still fail because the used MS shell can't handle `mv` properly. In that case simply mv `third_party/tz/leapseconds.out` to `third_party/tz/leapseconds` and start the build again + +### Include Valhalla as a project dependency + +When importing `libvalhalla` as a dependency in a project, it's important to know that we're using both CMake and `pkg-config` to resolve our own dependencies. Check the root `CMakeLists.txt` for details. This is important in case you'd like to bring your own dependencies, such as cURL or protobuf. It's always safe to use `PKG_CONFIG_PATH` environment variable to point CMake to custom installations, however, for dependencies we resolve with `find_package` you'll need to check CMake's built-in `Find*` modules on how to provide the proper paths. + +To resolve `libvalhalla`'s linker/library paths/options, we recommend to use `pkg-config` or `pkg_check_modules` (in CMake). + +Currently, `rapidjson`, `date` & `dirent` (Win only) headers are vendored in `third_party`. Consuming applications are encouraged to use `pkg-config` to resolve Valhalla and its dependencies which will automatically install those headers to `/path/to/include/valhalla/third_pary/{rapidjson, date, dirent.h}` and can be `#include`d appropriately. + ## Running Valhalla server on Unix The following script should be enough to make some routing data and start a server using it. (Note - if you would like to run an elevation lookup service with Valhalla follow the instructions [here](./elevation.md)). @@ -144,8 +158,9 @@ mkdir -p valhalla_tiles valhalla_build_config --mjolnir-tile-dir ${PWD}/valhalla_tiles --mjolnir-tile-extract ${PWD}/valhalla_tiles.tar --mjolnir-timezone ${PWD}/valhalla_tiles/timezones.sqlite --mjolnir-admin ${PWD}/valhalla_tiles/admins.sqlite > valhalla.json # build timezones.sqlite to support time-dependent routing valhalla_build_timezones > valhalla_tiles/timezones.sqlite +# build admins.sqlite to support admin-related properties such as access restrictions, driving side, ISO codes etc +valhalla_build_admins -c valhalla.json switzerland-latest.osm.pbf liechtenstein-latest.osm.pbf # build routing tiles -# TODO: run valhalla_build_admins? valhalla_build_tiles -c valhalla.json switzerland-latest.osm.pbf liechtenstein-latest.osm.pbf # tar it up for running the server # either run this to build a tile index for faster graph loading times diff --git a/libvalhalla.pc.in b/libvalhalla.pc.in index 45391b8310..886b7fdf93 100644 --- a/libvalhalla.pc.in +++ b/libvalhalla.pc.in @@ -1,10 +1,12 @@ prefix=@prefix@ exec_prefix=@exec_prefix@ -libdir=${exec_prefix}/@libdir@ -includedir=${prefix}/@includedir@ +libdir=@libdir@ +includedir=@includedir@ Name: libvalhalla Description: valhalla c++ library Version: @VERSION@ -Libs: -L${libdir} -lvalhalla @deplibs@ -Cflags: -I${includedir} +Libs: -L${libdir} -lvalhalla +Libs.private: @LIBS_PRIVATE@ +Requires: @REQUIRES@ +Cflags: -I${includedir} @CFLAGS@ diff --git a/proto/CMakeLists.txt b/proto/CMakeLists.txt index 9772d4446d..b5e6d67bc4 100644 --- a/proto/CMakeLists.txt +++ b/proto/CMakeLists.txt @@ -20,7 +20,7 @@ if(ENABLE_DATA_TOOLS) ${VALHALLA_SOURCE_DIR}/third_party/OSM-binary/src/osmformat.proto) endif() -# TODO: this seems to be deprecated, see https://github.com/valhalla/valhalla/pull/4207 + protobuf_generate_cpp(protobuf_srcs protobuf_hdrs ${protobuf_descriptors}) valhalla_module(NAME proto diff --git a/scripts/install-linux-deps.sh b/scripts/install-linux-deps.sh index 722312265b..4b3e3bae80 100755 --- a/scripts/install-linux-deps.sh +++ b/scripts/install-linux-deps.sh @@ -19,6 +19,7 @@ env DEBIAN_FRONTEND=noninteractive sudo apt install --yes --quiet \ git \ jq \ lcov \ + libboost-all-dev \ libcurl4-openssl-dev \ libczmq-dev \ libgeos++-dev \ @@ -37,7 +38,7 @@ env DEBIAN_FRONTEND=noninteractive sudo apt install --yes --quiet \ make \ osmium-tool \ parallel \ - pkg-config \ + pkgconf \ protobuf-compiler \ python3-all-dev \ python3-shapely \ @@ -57,8 +58,8 @@ sudo make install popd && rm -rf $primeserver_dir # for boost and scripts deps -if [[ $(python3 -c 'import sys; print(int(sys.base_prefix != sys.prefix or hasattr(sys, "real_p refix")))') -eq 1 ]]; then - python3 -m pip install --upgrade "conan<2.0.0" requests shapely +if [[ $(python3 -c 'import sys; print(int(sys.base_prefix != sys.prefix or hasattr(sys, "real_prefix")))') -eq 1 ]]; then + python3 -m pip install --upgrade requests shapely else - sudo PIP_BREAK_SYSTEM_PACKAGES=1 python3 -m pip install --upgrade "conan<2.0.0" requests shapely + sudo PIP_BREAK_SYSTEM_PACKAGES=1 python3 -m pip install --upgrade requests shapely fi diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b3b7086e61..05cf644091 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,47 +1,14 @@ include(GNUInstallDirs) +include(ValhallaVersion) +include(ValhallaWarnings) -list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") - -## Set relative to the current source and binary paths +## Set relative binary path set(VALHALLA_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) -if(NOT VALHALLA_SOURCE_DIR) - set(VALHALLA_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) -endif() -## Get Valhalla version -file(STRINGS "${VALHALLA_SOURCE_DIR}/valhalla/valhalla.h" version_lines REGEX "VALHALLA_VERSION_(MAJOR|MINOR|PATCH)") -foreach(line ${version_lines}) - if("${line}" MATCHES "(VALHALLA_VERSION_(MAJOR|MINOR|PATCH))[\t ]+([0-9]+)") - set(${CMAKE_MATCH_1} ${CMAKE_MATCH_3}) - set(${CMAKE_MATCH_1} ${CMAKE_MATCH_3} PARENT_SCOPE) - endif() -endforeach() -if(DEFINED VALHALLA_VERSION_MAJOR) - set(VERSION "${VALHALLA_VERSION_MAJOR}") - if(DEFINED VALHALLA_VERSION_MINOR) - set(VERSION "${VERSION}.${VALHALLA_VERSION_MINOR}") - if(DEFINED VALHALLA_VERSION_PATCH) - set(VERSION "${VERSION}.${VALHALLA_VERSION_PATCH}") - endif() - endif() -else() - message(FATAL_ERROR "No Valhalla major version") +if (ENABLE_COMPILER_WARNINGS) + message(STATUS "Enabling compiler warning flags") endif() -## Declare C++ build configuration variables as part of HandleLibcxxFlags. -# -# - LIBCXX_COMPILE_FLAGS: flags used to compile libc++ -# - LIBCXX_LINK_FLAGS: flags used to link libc++ -# - LIBCXX_LIBRARIES: libraries to link libc++ to -set(LIBCXX_COMPILE_FLAGS "") -set(LIBCXX_LINK_FLAGS "") -set(LIBCXX_LIBRARIES "") - -# Include build macros for updating configuration variables -include(HandleLibcxxFlags) - -## Define libvalhalla sub-modules as OBJECT libraries - # TODO: Remove workaround after switching to CMake 3.11 # with fixed https://gitlab.kitware.com/cmake/cmake/issues/14778 # and merged https://gitlab.kitware.com/cmake/cmake/merge_requests/1524 @@ -53,20 +20,7 @@ set(libvalhalla_include_directories "" CACHE INTERNAL "") set(libvalhalla_link_objects "" CACHE INTERNAL "") set(libvalhalla_link_libraries "" CACHE INTERNAL "") -# Setup warning flags. Note that these are only added to libvalhalla_compile_options if -# ENABLE_COMPILER_WARNINGS=ON. Note that this needs to be done before the definition of -# valhalla_module, otherwise LIBCXX_COMPILE_FLAGS will not available for downstream targets. - -if (ENABLE_COMPILER_WARNINGS) - message(STATUS "Enabling compiler warning flags") -endif() - -function (cxx_add_warning_flags target) - target_add_compile_flags_if_supported(${target} PRIVATE -Wall -Wextra) - if (ENABLE_WERROR) - target_add_compile_flags_if_supported(${target} PRIVATE -Werror) - endif() -endfunction() +## Define libvalhalla sub-modules as OBJECT libraries function(valhalla_module) set(oneValueArgs NAME) @@ -82,6 +36,7 @@ function(valhalla_module) endif() list(APPEND MODULE_SOURCES ${MODULE_SOURCES_WITH_WARNINGS}) + # add object or static library set(library valhalla-${MODULE_NAME}) if (ENABLE_STATIC_LIBRARY_MODULES) add_library(${library} STATIC ${MODULE_SOURCES} ${MODULE_HEADERS}) @@ -161,10 +116,6 @@ function(valhalla_module) endif() endfunction() -add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing ${CMAKE_BINARY_DIR}/third_party/robin-hood-hashing) -SET(CPP_STATSD_STANDALONE OFF CACHE BOOL "ignore test targets" FORCE) -add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/cpp-statsd-client ${CMAKE_BINARY_DIR}/third_party/cpp-statsd-client) - add_subdirectory(../proto ${CMAKE_CURRENT_BINARY_DIR}/valhalla/proto) add_subdirectory(baldr) add_subdirectory(loki) @@ -187,6 +138,7 @@ set(valhalla_hdrs ${VALHALLA_SOURCE_DIR}/valhalla/config.h ${VALHALLA_SOURCE_DIR}/valhalla/filesystem.h ${VALHALLA_SOURCE_DIR}/valhalla/proto_conversions.h + ${VALHALLA_SOURCE_DIR}/valhalla/config.h ) set(valhalla_src @@ -194,7 +146,6 @@ set(valhalla_src worker.cc filesystem.cc proto_conversions.cc - ${VALHALLA_SOURCE_DIR}/valhalla/config.h ${valhalla_hdrs} ${libvalhalla_link_objects}) @@ -245,15 +196,26 @@ target_include_directories(valhalla target_link_libraries(valhalla PUBLIC ${libvalhalla_link_libraries} + $<$:ws2_32> PRIVATE $<$:gcov> - Threads::Threads - cpp-statsd-client) + Threads::Threads) set_target_properties(valhalla PROPERTIES VERSION "${VERSION}" SOVERSION "${VALHALLA_VERSION_MAJOR}") +# pkg-config installation +if(PKG_CONFIG_FOUND) + include(ValhallaPkgConfig) + + configure_valhalla_pc() + + install(FILES + ${CMAKE_BINARY_DIR}/libvalhalla.pc + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif() + install(TARGETS valhalla LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shared NAMELINK_SKIP @@ -284,20 +246,3 @@ install(FILES ${VALHALLA_SOURCE_DIR}/COPYING ${VALHALLA_SOURCE_DIR}/CHANGELOG.md install(FILES ${valhalla_hdrs} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla" COMPONENT development) - -if(PKG_CONFIG_FOUND) - ## Configure libvalhalla.pc file with valhalla target linking options via PkgConfig linker - set(CMAKE_PkgConfig_LINK_EXECUTABLE " -DINPUT=${VALHALLA_SOURCE_DIR}/libvalhalla.pc.in -DOUTPUT= -DVERSION=${VERSION} -Dprefix=${CMAKE_INSTALL_PREFIX} -Dexec_prefix=${CMAKE_INSTALL_PREFIX} -Dlibdir=${CMAKE_INSTALL_LIBDIR} -Dincludedir=${CMAKE_INSTALL_INCLUDEDIR} -Ddeplibs=\" \" -P ${CMAKE_SOURCE_DIR}/cmake/PkgConfig.cmake") - - add_executable(libvalhalla.pc EXCLUDE_FROM_ALL ../libvalhalla.pc.in) - target_link_libraries(libvalhalla.pc valhalla) - set_target_properties(libvalhalla.pc PROPERTIES LINKER_LANGUAGE PkgConfig) - set_target_properties(libvalhalla.pc PROPERTIES FOLDER "Library") - install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} --build . --target libvalhalla.pc OUTPUT_QUIET ERROR_VARIABLE _err RESULT_VARIABLE _res) - if(NOT \${_res} EQUAL 0) - message(FATAL_ERROR \"Configuring libvalhalla.pc failed: \${_err}\") - endif()") - install(FILES ${VALHALLA_BINARY_DIR}/libvalhalla.pc - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" - COMPONENT development) -endif() diff --git a/src/baldr/CMakeLists.txt b/src/baldr/CMakeLists.txt index bbd88dc6b8..0bf8d6e310 100644 --- a/src/baldr/CMakeLists.txt +++ b/src/baldr/CMakeLists.txt @@ -11,7 +11,7 @@ DEPENDS ${VALHALLA_SOURCE_DIR}/third_party/tz/leapseconds.awk) foreach(tz_db_file ${tz_db_files}) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/date_time_${tz_db_file}.h - COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P ${VALHALLA_SOURCE_DIR}/cmake/Binary2Header.cmake + COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P ${VALHALLA_SOURCE_DIR}/cmake/ValhallaBin2Header.cmake ${VALHALLA_SOURCE_DIR}/third_party/tz/${tz_db_file} ${CMAKE_CURRENT_BINARY_DIR}/date_time_${tz_db_file}.h --variable-name date_time_${tz_db_file} --skip 1 --raw @@ -22,7 +22,7 @@ endforeach() # Process the windowsZones.xml file add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/date_time_windows_zones.h - COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P ${VALHALLA_SOURCE_DIR}/cmake/Binary2Header.cmake + COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P ${VALHALLA_SOURCE_DIR}/cmake/ValhallaBin2Header.cmake ${VALHALLA_SOURCE_DIR}/date_time/windowsZones.xml ${CMAKE_CURRENT_BINARY_DIR}/date_time_windows_zones.h --variable-name date_time_windows_zones_xml --skip 1 --raw @@ -35,13 +35,14 @@ file(GLOB headers ${VALHALLA_SOURCE_DIR}/valhalla/baldr/*.h) set(includes ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_CURRENT_BINARY_DIR}/src/baldr ) # treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(system_includes + ${VALHALLA_SOURCE_DIR}/third_party/date/include + $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> + ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include) if(APPLE) list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) endif() @@ -131,5 +132,5 @@ valhalla_module(NAME baldr valhalla::proto ${valhalla_protobuf_targets} Boost::boost - CURL::CURL - ZLIB::ZLIB) + ${curl_targets} + PkgConfig::ZLIB) diff --git a/src/baldr/curler.cc b/src/baldr/curler.cc index c1358609da..6b4dae4847 100644 --- a/src/baldr/curler.cc +++ b/src/baldr/curler.cc @@ -6,7 +6,7 @@ #include #include -#ifdef CURL_STATICLIB +#ifdef ENABLE_HTTP #if defined(_MSC_VER) && !defined(NOGDI) #define NOGDI // prevents winsock2.h drag in wingdi.h diff --git a/src/bindings/python/CMakeLists.txt b/src/bindings/python/CMakeLists.txt index e4ce5f31fd..b08e87df8a 100644 --- a/src/bindings/python/CMakeLists.txt +++ b/src/bindings/python/CMakeLists.txt @@ -13,8 +13,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/actor.py ${CMAKE_CURRENT_BINARY_DIR}/ message(STATUS "Installing python modules to ${Python_SITEARCH}") install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/valhalla DESTINATION "${Python_SITEARCH}" -COMPONENT python_valhalla +COMPONENT runtime FILES_MATCHING PATTERN "*.py") install(TARGETS python_valhalla DESTINATION "${Python_SITEARCH}/valhalla" -COMPONENT python_valhalla) +COMPONENT runtime) diff --git a/src/loki/CMakeLists.txt b/src/loki/CMakeLists.txt index 21eeb6a648..2ffd7535ef 100644 --- a/src/loki/CMakeLists.txt +++ b/src/loki/CMakeLists.txt @@ -22,7 +22,9 @@ set(sources_with_warnings worker.cc) # treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(system_includes + ${VALHALLA_SOURCE_DIR}/third_party/date/include + $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include>) if(APPLE) list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) endif() @@ -35,7 +37,6 @@ valhalla_module(NAME loki PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_BINARY_DIR} @@ -48,4 +49,4 @@ valhalla_module(NAME loki valhalla::proto ${valhalla_protobuf_targets} Boost::boost - libprime_server) + ${libprime_server_targets}) diff --git a/src/loki/status_action.cc b/src/loki/status_action.cc index 511f44fe7d..f58061cd61 100644 --- a/src/loki/status_action.cc +++ b/src/loki/status_action.cc @@ -33,7 +33,7 @@ time_t get_tileset_last_modified(const std::shared_ptr& reader) { namespace valhalla { namespace loki { void loki_worker_t::status(Api& request) const { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES // if we are in the process of shutting down we signal that here // should react by draining traffic (though they are likely doing this as they are usually the ones // who sent us the request to shutdown) diff --git a/src/loki/worker.cc b/src/loki/worker.cc index ed8a6df808..0a927f21e8 100644 --- a/src/loki/worker.cc +++ b/src/loki/worker.cc @@ -315,7 +315,7 @@ void loki_worker_t::check_hierarchy_distance(Api& request) { } } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES prime_server::worker_t::result_t loki_worker_t::work(const std::list& job, void* request_info, diff --git a/src/meili/CMakeLists.txt b/src/meili/CMakeLists.txt index 35bd4c1c91..734050f7a3 100644 --- a/src/meili/CMakeLists.txt +++ b/src/meili/CMakeLists.txt @@ -15,7 +15,9 @@ set(sources_with_warnings viterbi_search.cc) # treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(system_includes + ${VALHALLA_SOURCE_DIR}/third_party/date/include + $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include>) if(APPLE) list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) endif() @@ -28,13 +30,13 @@ valhalla_module(NAME meili PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_BINARY_DIR} SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} + PRIVATE + ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include DEPENDS valhalla::sif ${valhalla_protobuf_targets} diff --git a/src/midgard/CMakeLists.txt b/src/midgard/CMakeLists.txt index b92484c50b..5f3a0faf4a 100644 --- a/src/midgard/CMakeLists.txt +++ b/src/midgard/CMakeLists.txt @@ -20,7 +20,10 @@ valhalla_module(NAME midgard PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla + SYSTEM_INCLUDE_DIRECTORIES + PUBLIC $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> + PRIVATE + ${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing/src/include DEPENDS - Boost::boost - robin_hood::robin_hood) + Boost::boost) diff --git a/src/mjolnir/CMakeLists.txt b/src/mjolnir/CMakeLists.txt index 71907fb56e..68d353eaaf 100644 --- a/src/mjolnir/CMakeLists.txt +++ b/src/mjolnir/CMakeLists.txt @@ -1,6 +1,6 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/admin_lua_proc.h - COMMAND ${CMAKE_COMMAND} -P ${VALHALLA_SOURCE_DIR}/cmake/Binary2Header.cmake + COMMAND ${CMAKE_COMMAND} -P ${VALHALLA_SOURCE_DIR}/cmake/ValhallaBin2Header.cmake ${VALHALLA_SOURCE_DIR}/lua/admin.lua ${CMAKE_CURRENT_BINARY_DIR}/admin_lua_proc.h --variable-name lua_admin_lua @@ -10,7 +10,7 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/admin_lua_proc.h VERBATIM) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/graph_lua_proc.h - COMMAND ${CMAKE_COMMAND} -P ${VALHALLA_SOURCE_DIR}/cmake/Binary2Header.cmake + COMMAND ${CMAKE_COMMAND} -P ${VALHALLA_SOURCE_DIR}/cmake/ValhallaBin2Header.cmake ${VALHALLA_SOURCE_DIR}/lua/graph.lua ${CMAKE_CURRENT_BINARY_DIR}/graph_lua_proc.h --variable-name lua_graph_lua @@ -63,7 +63,11 @@ set(sources validatetransit.cc) # treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(system_includes + ${VALHALLA_SOURCE_DIR}/third_party/date/include + ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include + $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> + ${PROTOBUF_INCLUDE_DIR}) if(APPLE) list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) endif() @@ -75,23 +79,21 @@ valhalla_module(NAME mjolnir PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> - ${PROTOBUF_INCLUDE_DIRS} PRIVATE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/valhalla - ${VALHALLA_SOURCE_DIR}/third_party/just_gtfs/include SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} + PRIVATE + ${VALHALLA_SOURCE_DIR}/third_party/just_gtfs/include + ${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing/src/include DEPENDS valhalla::proto valhalla::baldr PkgConfig::SpatiaLite SQLite3::SQLite3 Boost::boost - Lua::Lua + PkgConfig::LuaJIT Threads::Threads - ZLIB::ZLIB - robin_hood::robin_hood) + PkgConfig::ZLIB) diff --git a/src/mjolnir/ingest_transit.cc b/src/mjolnir/ingest_transit.cc index 8ab80128aa..db6000f307 100644 --- a/src/mjolnir/ingest_transit.cc +++ b/src/mjolnir/ingest_transit.cc @@ -18,7 +18,6 @@ #include #include #include -#include #include "baldr/graphconstants.h" #include "baldr/graphid.h" diff --git a/src/odin/CMakeLists.txt b/src/odin/CMakeLists.txt index 6bc04b75de..7d7a02aaa5 100644 --- a/src/odin/CMakeLists.txt +++ b/src/odin/CMakeLists.txt @@ -1,7 +1,7 @@ file(GLOB locale_jsons ${VALHALLA_SOURCE_DIR}/locales *.json) add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/locales.h - COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P cmake/Binary2Header.cmake ${VALHALLA_SOURCE_DIR}/locales/ ${CMAKE_CURRENT_BINARY_DIR}/locales.h --locales + COMMAND ${CMAKE_COMMAND} -DMSVC=${MSVC} -P ${VALHALLA_SOURCE_DIR}/cmake/ValhallaBin2Header.cmake ${VALHALLA_SOURCE_DIR}/locales/ ${CMAKE_CURRENT_BINARY_DIR}/locales.h --locales WORKING_DIRECTORY ${VALHALLA_SOURCE_DIR} COMMENT "Compiling locales/*.json to locales.h" DEPENDS ${locale_jsons} @@ -29,7 +29,9 @@ set(sources_with_warnings util.cc) # treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(system_includes + ${VALHALLA_SOURCE_DIR}/third_party/date/include + $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include>) if(APPLE) list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) endif() @@ -42,15 +44,15 @@ valhalla_module(NAME odin PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_CURRENT_BINARY_DIR} SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} + PRIVATE + ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include DEPENDS valhalla::proto ${valhalla_protobuf_targets} Boost::boost - libprime_server) + ${libprime_server_targets}) diff --git a/src/odin/worker.cc b/src/odin/worker.cc index f1f1f54a39..87197e600e 100644 --- a/src/odin/worker.cc +++ b/src/odin/worker.cc @@ -50,7 +50,7 @@ std::string odin_worker_t::narrate(Api& request) const { } void odin_worker_t::status(Api&) const { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES // if we are in the process of shutting down we signal that here // should react by draining traffic (though they are likely doing this as they are usually the ones // who sent us the request to shutdown) @@ -60,7 +60,7 @@ void odin_worker_t::status(Api&) const { #endif } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES prime_server::worker_t::result_t odin_worker_t::work(const std::list& job, void* request_info, diff --git a/src/sif/CMakeLists.txt b/src/sif/CMakeLists.txt index 0f5f0342c6..b4664534c9 100644 --- a/src/sif/CMakeLists.txt +++ b/src/sif/CMakeLists.txt @@ -14,7 +14,10 @@ set(sources recost.cc) # treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(system_includes + ${VALHALLA_SOURCE_DIR}/third_party/date/include + $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> + ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include) if(APPLE) list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) endif() @@ -26,9 +29,6 @@ valhalla_module(NAME sif PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> - PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} diff --git a/src/skadi/CMakeLists.txt b/src/skadi/CMakeLists.txt index 6e3a96c2ce..0439e79d86 100644 --- a/src/skadi/CMakeLists.txt +++ b/src/skadi/CMakeLists.txt @@ -14,10 +14,12 @@ valhalla_module(NAME skadi PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib + SYSTEM_INCLUDE_DIRECTORIES + PUBLIC + $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> DEPENDS valhalla::baldr Boost::boost - ZLIB::ZLIB) + PkgConfig::ZLIB) diff --git a/src/thor/CMakeLists.txt b/src/thor/CMakeLists.txt index c706e8a2a2..cb94882952 100644 --- a/src/thor/CMakeLists.txt +++ b/src/thor/CMakeLists.txt @@ -30,7 +30,9 @@ set(sources_with_warnings triplegbuilder_utils.h) # treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(system_includes + ${VALHALLA_SOURCE_DIR}/third_party/date/include + $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include>) if(APPLE) list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) endif() @@ -43,18 +45,19 @@ valhalla_module(NAME thor PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_BINARY_DIR} SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} + PRIVATE + ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include + ${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing/src/include + DEPENDS valhalla::proto valhalla::sif valhalla::meili ${valhalla_protobuf_targets} Boost::boost - libprime_server - robin_hood::robin_hood) + ${libprime_server_targets}) diff --git a/src/thor/status_action.cc b/src/thor/status_action.cc index 8602b9cfdb..a96b30eb2a 100644 --- a/src/thor/status_action.cc +++ b/src/thor/status_action.cc @@ -3,7 +3,7 @@ namespace valhalla { namespace thor { void thor_worker_t::status(Api&) const { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES // if we are in the process of shutting down we signal that here // should react by draining traffic (though they are likely doing this as they are usually the ones // who sent us the request to shutdown) diff --git a/src/thor/worker.cc b/src/thor/worker.cc index 92d0a92573..968a6df382 100644 --- a/src/thor/worker.cc +++ b/src/thor/worker.cc @@ -48,7 +48,7 @@ const std::unordered_map kMaxDistances = { // a scale factor to apply to the score so that we bias towards closer results more constexpr float kDistanceScale = 10.f; -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES std::string serialize_to_pbf(Api& request) { std::string buf; if (!request.SerializeToString(&buf)) { @@ -111,7 +111,7 @@ thor_worker_t::thor_worker_t(const boost::property_tree::ptree& config, thor_worker_t::~thor_worker_t() { } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES prime_server::worker_t::result_t thor_worker_t::work(const std::list& job, void* request_info, diff --git a/src/tyr/CMakeLists.txt b/src/tyr/CMakeLists.txt index d8929e3ac4..5a0624785f 100644 --- a/src/tyr/CMakeLists.txt +++ b/src/tyr/CMakeLists.txt @@ -16,7 +16,9 @@ set(sources_with_warnings trace_serializer.cc) # treat date library as system -set(system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(system_includes + ${VALHALLA_SOURCE_DIR}/third_party/date/include + $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include>) if(APPLE) list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) endif() @@ -32,13 +34,13 @@ valhalla_module(NAME tyr PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include ${CMAKE_BINARY_DIR} SYSTEM_INCLUDE_DIRECTORIES PUBLIC ${system_includes} + PRIVATE + ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include DEPENDS valhalla::loki valhalla::thor diff --git a/src/valhalla_service.cc b/src/valhalla_service.cc index 3edbc9dcb0..95c753259d 100644 --- a/src/valhalla_service.cc +++ b/src/valhalla_service.cc @@ -11,7 +11,7 @@ #include #include -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES #include #include using namespace prime_server; @@ -26,7 +26,7 @@ using namespace prime_server; #include "tyr/actor.h" int main(int argc, char** argv) { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES if (argc < 2 || argc > 4) { LOG_ERROR("Usage: " + std::string(argv[0]) + " config/file.json [concurrency]"); LOG_ERROR("Usage: " + std::string(argv[0]) + " config/file.json action json_request"); @@ -137,7 +137,7 @@ int main(int argc, char** argv) { return 0; } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES // gracefully shutdown when asked via SIGTERM prime_server::quiesce(config.get("httpd.service.drain_seconds", 28), config.get("httpd.service.shutting_seconds", 1)); diff --git a/src/worker.cc b/src/worker.cc index d4edc293f1..a3e1939b54 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -22,7 +22,7 @@ #include using namespace valhalla; -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES using namespace prime_server; #endif @@ -1291,7 +1291,7 @@ void ParseApi(const std::string& request, Options::Action action, valhalla::Api& from_json(document, action, api); } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES void ParseApi(const http_request_t& request, valhalla::Api& api) { // block all but get and post if (request.method != method_t::POST && request.method != method_t::GET) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0f2776ab32..f77fcfd7d0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,6 +25,7 @@ target_include_directories(valhalla_test PUBLIC ${VALHALLA_SOURCE_DIR}/third_party/protozero/include ${VALHALLA_SOURCE_DIR}/third_party/just_gtfs/include ${VALHALLA_SOURCE_DIR}/third_party/libosmium/include + ${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing/src/include ${VALHALLA_SOURCE_DIR}/third_party/microtar/src) target_link_libraries(valhalla_test valhalla gtest gtest_main gmock pthread) @@ -150,7 +151,7 @@ foreach(test ${tyr_tests}) endforeach() ## Test-specific data, properties and dependencies -set_target_properties(logging PROPERTIES COMPILE_DEFINITIONS LOGGING_LEVEL_ALL) +target_compile_definitions(logging PRIVATE LOGGING_LEVEL_ALL) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/test/data/tz.sqlite DEPENDS ${VALHALLA_SOURCE_DIR}/scripts/valhalla_build_timezones @@ -376,7 +377,7 @@ if(ENABLE_DATA_TOOLS) add_dependencies(run-alternates utrecht_tiles) add_dependencies(run-tar_index utrecht_tiles) add_dependencies(run-graphbuilder build_timezones) -if(ENABLE_HTTP) + if(ENABLE_HTTP) add_dependencies(run-http_tiles utrecht_tiles) endif() endif() diff --git a/test/gurka/CMakeLists.txt b/test/gurka/CMakeLists.txt index d9276dd3f4..f896e0e189 100644 --- a/test/gurka/CMakeLists.txt +++ b/test/gurka/CMakeLists.txt @@ -60,7 +60,7 @@ if(ENABLE_DATA_TOOLS) VALHALLA_BUILD_DIR="${VALHALLA_BUILD_DIR}/") create_source_groups("Source Files" ${FILENAME}) target_link_libraries(${TESTNAME} valhalla_test) - if (LUAJIT_FOUND AND APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") + if (LuaJIT_FOUND AND APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") # Using LuaJIT on macOS on Intel processors requires a couple of extra linker flags target_link_options(${TESTNAME} PUBLIC -pagezero_size 10000 -image_base 100000000) endif() diff --git a/test/statsd.cc b/test/statsd.cc index ed9a0109c4..0e8d1fb113 100644 --- a/test/statsd.cc +++ b/test/statsd.cc @@ -54,7 +54,7 @@ class test_worker_t : public service_worker_t { std::string service_name() const override { return "test"; } -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES virtual prime_server::worker_t::result_t work(const std::list&, void*, const std::function&) { throw std::runtime_error("We arent testing the work method directly"); diff --git a/valhalla/baldr/graphreader.h b/valhalla/baldr/graphreader.h index 8b0afdaa20..d67e9d7578 100644 --- a/valhalla/baldr/graphreader.h +++ b/valhalla/baldr/graphreader.h @@ -10,7 +10,6 @@ #include -#include #include #include #include diff --git a/valhalla/baldr/graphtile.h b/valhalla/baldr/graphtile.h index 7c30c19ad5..9b979ad028 100644 --- a/valhalla/baldr/graphtile.h +++ b/valhalla/baldr/graphtile.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include diff --git a/valhalla/loki/worker.h b/valhalla/loki/worker.h index c1b24f2e99..78927cfcf5 100644 --- a/valhalla/loki/worker.h +++ b/valhalla/loki/worker.h @@ -21,7 +21,7 @@ namespace valhalla { namespace loki { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES void run_service(const boost::property_tree::ptree& config); #endif @@ -29,7 +29,7 @@ class loki_worker_t : public service_worker_t { public: loki_worker_t(const boost::property_tree::ptree& config, const std::shared_ptr& graph_reader = {}); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES virtual prime_server::worker_t::result_t work(const std::list& job, void* request_info, const std::function& interrupt) override; diff --git a/valhalla/odin/worker.h b/valhalla/odin/worker.h index 32ad226092..7a326a9b5a 100644 --- a/valhalla/odin/worker.h +++ b/valhalla/odin/worker.h @@ -8,7 +8,7 @@ namespace valhalla { namespace odin { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES void run_service(const boost::property_tree::ptree& config); #endif @@ -16,7 +16,7 @@ class odin_worker_t : public service_worker_t { public: odin_worker_t(const boost::property_tree::ptree& config); virtual ~odin_worker_t(); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES virtual prime_server::worker_t::result_t work(const std::list& job, void* request_info, const std::function& interrupt) override; diff --git a/valhalla/thor/worker.h b/valhalla/thor/worker.h index 9a3e22fa90..b9fb120981 100644 --- a/valhalla/thor/worker.h +++ b/valhalla/thor/worker.h @@ -35,7 +35,7 @@ namespace valhalla { namespace thor { -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES void run_service(const boost::property_tree::ptree& config); #endif @@ -45,7 +45,7 @@ class thor_worker_t : public service_worker_t { thor_worker_t(const boost::property_tree::ptree& config, const std::shared_ptr& graph_reader = {}); virtual ~thor_worker_t(); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES virtual prime_server::worker_t::result_t work(const std::list& job, void* request_info, const std::function& interrupt) override; diff --git a/valhalla/worker.h b/valhalla/worker.h index 3332e4983b..b21e32ea41 100644 --- a/valhalla/worker.h +++ b/valhalla/worker.h @@ -8,7 +8,7 @@ #include #include -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES #include #include #endif @@ -59,7 +59,7 @@ struct valhalla_exception_t : public std::runtime_error { * already filled out, it will be validated and the json will be ignored */ void ParseApi(const std::string& json_request, Options::Action action, Api& api); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES /** * Take the json OR pbf request and parse/validate it. If you pass a protobuf mime type in the request * it is assumed that the body of the request is protobuf bytes and any json will be ignored. Likewise @@ -78,7 +78,7 @@ std::string serialize_error(const valhalla_exception_t& exception, Api& options) // function to add warnings to proto info object void add_warning(valhalla::Api& api, unsigned code); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES prime_server::worker_t::result_t serialize_error(const valhalla_exception_t& exception, prime_server::http_request_info_t& request_info, Api& options); @@ -102,7 +102,7 @@ class service_worker_t { virtual ~service_worker_t(); -#ifdef HAVE_HTTP +#ifdef ENABLE_SERVICES /** * The main work function that stages in the prime_server will call when responding to requests * From fd90ec416bafcaea609c753b360b41669628f34f Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 29 Jan 2024 15:45:24 +0100 Subject: [PATCH 012/198] GHA for Windows (#4533) --- .azure-pipelines.yml | 112 ------------------- .github/workflows/{ci.yml => syntax.yml} | 2 +- .github/workflows/win.yml | 133 +++++++++++++++++++++++ include/.keep | 0 vcpkg.json | 1 + 5 files changed, 135 insertions(+), 113 deletions(-) delete mode 100644 .azure-pipelines.yml rename .github/workflows/{ci.yml => syntax.yml} (98%) create mode 100644 .github/workflows/win.yml delete mode 100644 include/.keep diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml deleted file mode 100644 index 7beddebcca..0000000000 --- a/.azure-pipelines.yml +++ /dev/null @@ -1,112 +0,0 @@ -# Azure Pipelines for Valhalla -# -# TODO: Shallow clone blocked by -# https://developercommunity.visualstudio.com/content/problem/294872/yaml-build-ignores-fetchdepth.html -# TODO: Once CMake 3.13 is available replace -H with -S option -# - -# No wildcards allowed for file patterns... -# skip draft PR's, skip all commits except for PRs to master -trigger: - branches: - include: - - master - paths: - include: - - '*' - exclude: - - README.md - - CHANGELOG.md - - bench/ - - docs/ - - run_route_scripts/ - - test/ - - .circleci/ - - .github/ -pr: - branches: - include: - - master - paths: - include: - - '*' - exclude: - - README.md - - CHANGELOG.md - - bench/ - - docs/ - - run_route_scripts/ - - test/ - - .circleci/ - - .github/ - drafts: false - -jobs: - - job: VS2022 - displayName: 'Windows 2022 | VS2022' - timeoutInMinutes: 120 - - pool: - vmImage: 'windows-2022' - - variables: - BUILD_CFG: 'Release' - BUILD_DIR: '$(Agent.WorkFolder)\build' - TRIPLET: 'x64' - VCPKG_REF: 'f330a32' - - steps: - - script: | - git submodule update --init --recursive - echo $(Build.SourceBranch) - displayName: 'Pull submodules' - - - task: Cache@2 - displayName: "Cache vcpkg's packages" - inputs: - key: .\vcpkg.json | "$(VCPKG_REF)" - path: '$(BUILD_DIR)/vcpkg_installed' - cacheHitVar: CACHE_RESTORED - - # TODO: cache build never worked, look into it - # - task: Cache@2 - # displayName: "Cache build" - # inputs: - # key: '"msvc-v16.10.0" | build | "$(Build.SourceBranch)"' - # path: "$(BUILD_DIR)" - # restoreKeys: | - # "msvc-v16.10.0" | build | "$(Build.SourceBranch)" - # "msvc-v16.10.0" | build - # cacheHitVar: BUILD_CACHE_RESTORED - - - script: | - git -C %VCPKG_INSTALLATION_ROOT% checkout %VCPKG_REF% - echo.set(VCPKG_BUILD_TYPE release)>> %VCPKG_INSTALLATION_ROOT%\triplets\%TRIPLET%-windows.cmake - call %VCPKG_INSTALLATION_ROOT%\bootstrap-vcpkg.bat - %VCPKG_INSTALLATION_ROOT%\vcpkg.exe version - displayName: 'Install vcpkg' - condition: ne(variables.CACHE_RESTORED, 'true') - - - script: | - move /Y third_party\OSM-binary\src\fileformat.proto third_party\OSM-binary\src\fileformat.proto.orig - move /Y third_party\OSM-binary\src\osmformat.proto third_party\OSM-binary\src\osmformat.proto.orig - echo syntax = "proto2"; > third_party\OSM-binary\src\fileformat.proto - type third_party\OSM-binary\src\fileformat.proto.orig >> third_party\OSM-binary\src\fileformat.proto - echo syntax = "proto2"; > third_party\OSM-binary\src\osmformat.proto - type third_party\OSM-binary\src\osmformat.proto.orig >> third_party\OSM-binary\src\osmformat.proto - del /Q third_party\OSM-binary\src\fileformat.proto.orig - del /Q third_party\OSM-binary\src\osmformat.proto.orig - displayName: 'Patch .proto files of OSMPBF with syntax=proto2' - - - script: | - cmake --version - cmake -G "Visual Studio 17 2022" -A %TRIPLET% -H$(Build.SourcesDirectory) -B%BUILD_DIR% -DCMAKE_BUILD_TYPE=%BUILD_CFG% -DCMAKE_TOOLCHAIN_FILE=%VCPKG_INSTALLATION_ROOT%\scripts\buildsystems\vcpkg.cmake -DVCPKG_APPLOCAL_DEPS=ON -DENABLE_DATA_TOOLS=ON -DENABLE_TOOLS=ON -DENABLE_PYTHON_BINDINGS=ON -DENABLE_TESTS=OFF -DENABLE_CCACHE=OFF -DENABLE_HTTP=OFF -DENABLE_SERVICES=OFF -DENABLE_BENCHMARKS=OFF - cmake --build %BUILD_DIR% --config %BUILD_CFG% -- /clp:ErrorsOnly /p:BuildInParallel=true /m:4 - displayName: 'Build Valhalla' - - - script: | - SET PATH=%PATH%;%BUILD_DIR%\vcpkg_installed\%TRIPLET%-windows\bin - %BUILD_DIR%\%BUILD_CFG%\valhalla_build_tiles.exe -c .\test\win\valhalla.json .\test\data\utrecht_netherlands.osm.pbf - %BUILD_DIR%\%BUILD_CFG%\valhalla_run_route.exe --config .\test\win\valhalla.json -j "{\"locations\": [{\"lat\": 52.10205, \"lon\": 5.114651}, {\"lat\": 52.093113, \"lon\": 5.100918}], \"costing\": \"auto\"}" - %BUILD_DIR%\%BUILD_CFG%\valhalla_run_isochrone.exe --config .\test\win\valhalla.json -j "{\"locations\": [{\"lat\": 52.10205, \"lon\": 5.114651}], \"costing\": \"auto\", \"contours\":[{\"time\":15,\"color\":\"ff0000\"}]}" - displayName: 'Test some executables' diff --git a/.github/workflows/ci.yml b/.github/workflows/syntax.yml similarity index 98% rename from .github/workflows/ci.yml rename to .github/workflows/syntax.yml index ef7e2a2c16..50a9caaccd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/syntax.yml @@ -1,4 +1,4 @@ -name: GitHub CI +name: syntax on: [pull_request] jobs: diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml new file mode 100644 index 0000000000..6ff379ddc2 --- /dev/null +++ b/.github/workflows/win.yml @@ -0,0 +1,133 @@ +name: Windows CI +on: + push: + paths-ignore: + - '*.md' + - .circleci/ + - bench/ + - docs/ + - run_route_scripts/ + - test/ + - test_requests/ + branches: + - master + pull_request: + paths-ignore: + - '*.md' + - .circleci/ + - bench/ + - docs/ + - run_route_scripts/ + - test/ + - test_requests/ + branches: + - master + workflow_dispatch: + inputs: + debug_enabled: + type: boolean + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: false + +defaults: + run: + shell: bash + +env: + BUILD_TYPE: Release + MSVC_VERSION: '2022' + VCPKG_VERSION: '2024.01.12' + VCPKG_INSTALL_OPTIONS: --x-abi-tools-use-exact-versions + VCPKG_DISABLE_COMPILER_TRACKING: ON + +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + # we add a custom triplet to avoid cache misses as much as possible + # https://github.com/microsoft/vcpkg/issues/26346#issuecomment-1319244766 + - name: Configure vckpg + run: | + echo "VCPKG_ROOT=${{ github.workspace }}/vcpkg" >> $GITHUB_ENV + echo "VCPKG_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" >> $GITHUB_ENV + echo "VCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/vcpkg/custom-triplets" >> $GITHUB_ENV + echo "VCPKG_DEFAULT_TRIPLET=custom-x64-windows" >> $GITHUB_ENV + echo "VCPKG_DEFAULT_BINARY_CACHE=${{ github.workspace }}/vcpkg/archives" >> $GITHUB_ENV + + - name: Cache vcpkg + id: cache-vcpkg + uses: actions/cache@v4 + with: + path: ${{ env.VCPKG_ROOT }} + key: vcpkg=${{ env.VCPKG_VERSION }}-msvc=${{ env.MSVC_VERSION }}-json=${{ hashFiles('vcpkg.json') }} + save-always: 'true' + + - name: Install GNU make & awk + run: choco install gawk make + + - if: ${{ steps.cache-vcpkg.outputs.cache-hit != 'true' }} + name: Bootstrap vcpkg and install packages (if cache miss) + run: | + git clone --depth=1 --branch "$VCPKG_VERSION" https://github.com/microsoft/vcpkg.git + cd vcpkg + mkdir archives + mkdir "$VCPKG_OVERLAY_TRIPLETS" + TRIPLET_FILE="$VCPKG_OVERLAY_TRIPLETS/$VCPKG_DEFAULT_TRIPLET.cmake" + cp triplets/x64-windows.cmake "$TRIPLET_FILE" + echo "set(VCPKG_BUILD_TYPE release)" >> "$TRIPLET_FILE" + echo "set(VCPKG_DISABLE_COMPILER_TRACKING $VCPKG_DISABLE_COMPILER_TRACKING)" >> "$TRIPLET_FILE" + cmd.exe /c bootstrap-vcpkg.bat + vcpkg.exe \ + --vcpkg-root="$VCPKG_ROOT" \ + --overlay-triplets="$VCPKG_OVERLAY_TRIPLETS" \ + --triplet="$VCPKG_DEFAULT_TRIPLET" \ + $VCPKG_INSTALL_OPTIONS \ + install + + - name: Setup Developer Command Prompt for VS + uses: ilammy/msvc-dev-cmd@v1 + + - name: Configure CMake + run: | + cmake --version + cmake -B build \ + -G "Visual Studio 17 2022" \ + -A x64 \ + -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ + -DCMAKE_TOOLCHAIN_FILE="$VCPKG_TOOLCHAIN_FILE" \ + -DVCPKG_OVERLAY_TRIPLETS="$VCPKG_OVERLAY_TRIPLETS" \ + -DVCPKG_TARGET_TRIPLET="$VCPKG_DEFAULT_TRIPLET" \ + -DVCPKG_INSTALL_OPTIONS="$VCPKG_INSTALL_OPTIONS" \ + -DENABLE_DATA_TOOLS=ON \ + -DENABLE_TOOLS=ON \ + -DENABLE_PYTHON_BINDINGS=ON \ + -DENABLE_TESTS=OFF \ + -DENABLE_CCACHE=OFF \ + -DENABLE_HTTP=OFF \ + -DENABLE_SERVICES=OFF \ + -DENABLE_BENCHMARKS=OFF + + - name: Build Valhalla + shell: powershell + run: | + cmake --build build --config Release -- /clp:ErrorsOnly /p:BuildInParallel=true /m:4 + + - name: Test Executable + run: | + set PATH=$PATH:${{ github.workspace }}/build/vcpkg_installed/$BUILD_TYPE/bin + ./build/$BUILD_TYPE/valhalla_build_tiles.exe -c ./test/win/valhalla.json ./test/data/utrecht_netherlands.osm.pbf + ./build/$BUILD_TYPE/valhalla_run_isochrone.exe --config ./test/win/valhalla.json -j "{\"locations\": [{\"lat\": 52.10205, \"lon\": 5.114651}], \"costing\": \"auto\", \"contours\":[{\"time\":15,\"color\":\"ff0000\"}]}" + + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + # only run this if manually invoked or a previous job failed + if: ${{ (github.event_name == 'workflow_dispatch' && inputs.debug_enabled) || failure() }} + with: + detached: true + timeout-minutes: 15 + diff --git a/include/.keep b/include/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/vcpkg.json b/vcpkg.json index c4f286fbb5..fb7445b755 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -13,6 +13,7 @@ "curl", "geos", "libspatialite", + "pkgconf", "spatialite-tools", "luajit", "protobuf", From 154c47fa72d41d0bd8970125653aa1311ac6c9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lajos=20Kov=C3=A1cs?= <116808284+lakovacs2@users.noreply.github.com> Date: Mon, 29 Jan 2024 17:40:58 +0100 Subject: [PATCH 013/198] Bugfix: Prevent GetShortcut to run into an infinite loop. (#4532) --- CHANGELOG.md | 1 + src/baldr/graphreader.cc | 11 ++++++++++- test/gurka/test_shortcut.cc | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 091192c74a..cc159d22ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ * FIXED: log to stderr in valhalla_export_edges [#4498](https://github.com/valhalla/valhalla/pull/4498) * FIXED: set capped speed for truck at 90 KPH [#4493](https://github.com/valhalla/valhalla/pull/4493) * FIXED: Config singleton multiple instantiation issue [#4521](https://github.com/valhalla/valhalla/pull/4521) + * FIXED: Prevent GetShortcut to run into an infinite loop [#4532](https://github.com/valhalla/valhalla/pull/4532) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/baldr/graphreader.cc b/src/baldr/graphreader.cc index 63a2b76e19..16724d4cc7 100644 --- a/src/baldr/graphreader.cc +++ b/src/baldr/graphreader.cc @@ -786,10 +786,19 @@ GraphId GraphReader::GetShortcut(const GraphId& id) { GraphId edgeid = id; const NodeInfo* node = nullptr; const DirectedEdge* cont_de = nullptr; + const DirectedEdge* first_de = GetOpposingEdge(id); while (true) { // Get the continuing directed edge. Initial case is to use the opposing // directed edge. - cont_de = (node == nullptr) ? GetOpposingEdge(id) : continuing_edge(tile, edgeid, node); + if (node) { + cont_de = continuing_edge(tile, edgeid, node); + if (cont_de == first_de) { + LOG_DEBUG("GraphReader::GetShortcut edges are in a loop and found no shortcut among them"); + break; + } + } else { + cont_de = first_de; + } if (cont_de == nullptr) { LOG_DEBUG("GraphReader::GetShortcut found no clear continuing edge"); break; diff --git a/test/gurka/test_shortcut.cc b/test/gurka/test_shortcut.cc index 398a814d8e..e32ae18859 100644 --- a/test/gurka/test_shortcut.cc +++ b/test/gurka/test_shortcut.cc @@ -42,6 +42,31 @@ TEST(Shortcuts, CreateValid) { EXPECT_EQ(opp_shortcut_edged->speed(), 90); } +TEST(Shortcuts, LoopWithoutShortcut) { + constexpr double gridsize = 50; + + const std::string ascii_map = R"( + A--B + | | + | | + C--D + )"; + const gurka::ways ways = {{"AB", {{"highway", "primary"}}}, + {"BD", {{"highway", "primary"}}}, + {"DC", {{"highway", "primary"}}}, + {"CA", {{"highway", "primary"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_shortcut"); + + baldr::GraphReader graph_reader(map.config.get_child("mjolnir")); + + auto loopEdge = std::get<0>(gurka::findEdgeByNodes(graph_reader, layout, "A", "B")); + auto shortcut = graph_reader.GetShortcut(loopEdge); + + EXPECT_FALSE(shortcut.Is_Valid()) << "Shortcuts found. Check the map."; +} + // Here no shortcuts are created. There could be one from A to C with speed 80 but in the opposite // direction speeds differ which blocks CA creation. TEST(Shortcuts, CreateInvalid) { From 4d3d8965bc5d79c1109251ea1c1d12114e63af19 Mon Sep 17 00:00:00 2001 From: Nils Date: Wed, 31 Jan 2024 14:45:15 +0100 Subject: [PATCH 014/198] Refactor matrix_action (#4535) --- CHANGELOG.md | 1 + bench/thor/costmatrix.cc | 2 +- src/thor/costmatrix.cc | 17 +-- src/thor/expansion_action.cc | 5 +- src/thor/matrix_action.cc | 102 +++++++------- src/thor/optimized_route_action.cc | 5 +- src/thor/timedistancebssmatrix.cc | 36 ++--- src/thor/timedistancematrix.cc | 42 ++---- src/thor/worker.cc | 6 +- src/tyr/matrix_serializer.cc | 2 +- src/tyr/serializers.cc | 2 +- src/tyr/trace_serializer.cc | 1 - src/valhalla_run_matrix.cc | 4 +- test/CMakeLists.txt | 1 - test/gurka/test_matrix.cc | 2 +- test/matrix.cc | 17 ++- test/matrix_bss.cc | 2 +- valhalla/thor/costmatrix.h | 52 +------ valhalla/thor/matrix_common.h | 105 -------------- valhalla/thor/matrixalgorithm.h | 192 ++++++++++++++++++++++++++ valhalla/thor/pathalgorithm.h | 42 +++--- valhalla/thor/timedistancebssmatrix.h | 30 ++-- valhalla/thor/timedistancematrix.h | 34 ++--- valhalla/thor/worker.h | 1 + valhalla/tyr/serializers.h | 4 +- 25 files changed, 347 insertions(+), 360 deletions(-) delete mode 100644 valhalla/thor/matrix_common.h create mode 100644 valhalla/thor/matrixalgorithm.h diff --git a/CHANGELOG.md b/CHANGELOG.md index cc159d22ad..a800e1238e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,6 +81,7 @@ * UPDATED: tz to 2023d [#4519](https://github.com/valhalla/valhalla/pull/4519) * FIXED: Fix segfault in costmatrix (date_time and time zone always added). [#4530](https://github.com/valhalla/valhalla/pull/4530) * CHANGED: libvalhalla.pc generation to have finer controls; install third_party public headers; overhaul lots of CMake; remove conan support [#4516](https://github.com/valhalla/valhalla/pull/4516) + * CHANGED: refactored matrix code to include a base class for all matrix algorithms to prepare for second passes on matrix [#4535](https://github.com/valhalla/valhalla/pull/4535) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/bench/thor/costmatrix.cc b/bench/thor/costmatrix.cc index 494c769132..e8380c7334 100644 --- a/bench/thor/costmatrix.cc +++ b/bench/thor/costmatrix.cc @@ -102,7 +102,7 @@ static void BM_UtrechtCostMatrix(benchmark::State& state) { thor::CostMatrix matrix; for (auto _ : state) { matrix.SourceToTarget(request, reader, costs, mode, 100000.); - matrix.clear(); + matrix.Clear(); request.clear_matrix(); } state.counters["Routes"] = benchmark::Counter(size, benchmark::Counter::kIsIterationInvariantRate); diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 3c74ca16d4..6a569af0fd 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -49,16 +49,15 @@ class CostMatrix::ReachedMap : public robin_hood::unordered_map("max_reserved_labels_count_bidir_dijkstras", + : MatrixAlgorithm(config), + max_reserved_labels_count_(config.get("max_reserved_labels_count_bidir_dijkstras", kInitialEdgeLabelCountBidirDijkstra)), - clear_reserved_memory_(config.get("clear_reserved_memory", false)), max_reserved_locations_count_( config.get("max_reserved_locations_costmatrix", kMaxLocationReservation)), check_reverse_connections_(config.get("costmatrix_check_reverse_connection", false)), access_mode_(kAutoAccess), mode_(travel_mode_t::kDrive), locs_count_{0, 0}, locs_remaining_{0, 0}, - current_cost_threshold_(0), - has_time_(false), targets_{new ReachedMap}, sources_{new ReachedMap} { + current_cost_threshold_(0), targets_{new ReachedMap}, sources_{new ReachedMap} { } CostMatrix::~CostMatrix() { @@ -86,7 +85,7 @@ float CostMatrix::GetCostThreshold(const float max_matrix_distance) { // Clear the temporary information generated during time + distance matrix // construction. -void CostMatrix::clear() { +void CostMatrix::Clear() { // Clear the target edge markings targets_->clear(); if (check_reverse_connections_) @@ -132,19 +131,17 @@ void CostMatrix::SourceToTarget(Api& request, baldr::GraphReader& graphreader, const sif::mode_costing_t& mode_costing, const sif::travel_mode_t mode, - const float max_matrix_distance, - const bool has_time, - const bool invariant, - const ShapeFormat& shape_format) { + const float max_matrix_distance) { LOG_INFO("matrix::CostMatrix"); request.mutable_matrix()->set_algorithm(Matrix::CostMatrix); + bool invariant = request.options().date_time_type() == Options::invariant; + auto shape_format = request.options().shape_format(); // Set the mode and costing mode_ = mode; costing_ = mode_costing[static_cast(mode_)]; access_mode_ = costing_->access_mode(); - has_time_ = has_time; auto& source_location_list = *request.mutable_options()->mutable_sources(); auto& target_location_list = *request.mutable_options()->mutable_targets(); diff --git a/src/thor/expansion_action.cc b/src/thor/expansion_action.cc index 046cb51008..476e4cc760 100644 --- a/src/thor/expansion_action.cc +++ b/src/thor/expansion_action.cc @@ -130,7 +130,10 @@ std::string thor_worker_t::expansion(Api& request) { }) { alg->set_track_expansion(track_expansion); } - costmatrix_.set_track_expansion(track_expansion); + for (auto* alg : std::vector{&costmatrix_, &time_distance_matrix_, + &time_distance_bss_matrix_}) { + alg->set_track_expansion(track_expansion); + } isochrone_gen.SetInnerExpansionCallback(track_expansion); try { diff --git a/src/thor/matrix_action.cc b/src/thor/matrix_action.cc index c645f62353..7d39f6bb55 100644 --- a/src/thor/matrix_action.cc +++ b/src/thor/matrix_action.cc @@ -21,43 +21,9 @@ constexpr uint32_t kCostMatrixThreshold = 5; namespace valhalla { namespace thor { -std::string thor_worker_t::matrix(Api& request) { - // time this whole method and save that statistic - auto _ = measure_scope_time(request); - - auto& options = *request.mutable_options(); - adjust_scores(options); - auto costing = parse_costing(request); - - // TODO: do this for others as well - costmatrix_.set_interrupt(interrupt); - - // lambdas to do the real work - auto costmatrix = [&](const bool has_time) { - return costmatrix_.SourceToTarget(request, *reader, mode_costing, mode, - max_matrix_distance.find(costing)->second, has_time, - options.date_time_type() == Options::invariant, - options.shape_format()); - }; - auto timedistancematrix = [&]() { - if (options.shape_format() != no_shape) - add_warning(request, 207); - return time_distance_matrix_.SourceToTarget(request, *reader, mode_costing, mode, - max_matrix_distance.find(costing)->second, - options.matrix_locations(), - options.date_time_type() == Options::invariant); - }; +MatrixAlgorithm* thor_worker_t::get_matrix_algorithm(Api& request, const bool has_time) { - if (costing == "bikeshare") { - if (options.shape_format() != no_shape) - add_warning(request, 207); - time_distance_bss_matrix_.SourceToTarget(request, *reader, mode_costing, mode, - max_matrix_distance.find(costing)->second, - options.matrix_locations()); - return tyr::serializeMatrix(request); - } - - Matrix::Algorithm matrix_algo = Matrix::CostMatrix; + Matrix::Algorithm config_algo = Matrix::CostMatrix; switch (source_to_target_algorithm) { case SELECT_OPTIMAL: // TODO - Do further performance testing to pick the best algorithm for the job @@ -66,13 +32,13 @@ std::string thor_worker_t::matrix(Api& request) { case travel_mode_t::kBicycle: // Use CostMatrix if number of sources and number of targets // exceeds some threshold - if (static_cast(options.sources().size()) <= kCostMatrixThreshold || - static_cast(options.targets().size()) <= kCostMatrixThreshold) { - matrix_algo = Matrix::TimeDistanceMatrix; + if (static_cast(request.options().sources().size()) <= kCostMatrixThreshold || + static_cast(request.options().targets().size()) <= kCostMatrixThreshold) { + config_algo = Matrix::TimeDistanceMatrix; } break; case travel_mode_t::kPublicTransit: - matrix_algo = Matrix::TimeDistanceMatrix; + config_algo = Matrix::TimeDistanceMatrix; break; default: break; @@ -81,32 +47,60 @@ std::string thor_worker_t::matrix(Api& request) { case COST_MATRIX: break; case TIME_DISTANCE_MATRIX: - matrix_algo = Matrix::TimeDistanceMatrix; + config_algo = Matrix::TimeDistanceMatrix; break; } // similar to routing: prefer the exact unidirectional algo if not requested otherwise // don't use matrix_type, we only need it to set the right warnings for what will be used - bool has_time = - check_matrix_time(request, options.prioritize_bidirectional() ? Matrix::CostMatrix - : Matrix::TimeDistanceMatrix); - if (has_time && !options.prioritize_bidirectional() && source_to_target_algorithm != COST_MATRIX) { - timedistancematrix(); - } else if (has_time && options.prioritize_bidirectional() && + if (has_time && !request.options().prioritize_bidirectional() && + source_to_target_algorithm != COST_MATRIX) { + return &time_distance_matrix_; + } else if (has_time && request.options().prioritize_bidirectional() && source_to_target_algorithm != TIME_DISTANCE_MATRIX) { - costmatrix(has_time); - } else if (matrix_algo == Matrix::CostMatrix) { - // if this happens, the server config only allows for timedist matrix - if (has_time && !options.prioritize_bidirectional()) { + return &costmatrix_; + } else if (config_algo == Matrix::CostMatrix) { + if (has_time && !request.options().prioritize_bidirectional()) { add_warning(request, 301); } - costmatrix(has_time); + return &costmatrix_; } else { - if (has_time && options.prioritize_bidirectional()) { + // if this happens, the server config only allows for timedist matrix + if (has_time && request.options().prioritize_bidirectional()) { add_warning(request, 300); } - timedistancematrix(); + return &time_distance_matrix_; + } +} + +std::string thor_worker_t::matrix(Api& request) { + // time this whole method and save that statistic + auto _ = measure_scope_time(request); + + auto& options = *request.mutable_options(); + adjust_scores(options); + auto costing = parse_costing(request); + + bool has_time = + check_matrix_time(request, options.prioritize_bidirectional() ? Matrix::CostMatrix + : Matrix::TimeDistanceMatrix); + + // allow all algos to be cancelled + for (auto* alg : std::vector{ + &costmatrix_, + &time_distance_matrix_, + &time_distance_bss_matrix_, + }) { + alg->set_interrupt(interrupt); + alg->set_has_time(has_time); } + + auto* algo = + costing == "bikeshare" ? &time_distance_bss_matrix_ : get_matrix_algorithm(request, has_time); + + algo->SourceToTarget(request, *reader, mode_costing, mode, + max_matrix_distance.find(costing)->second); + return tyr::serializeMatrix(request); } } // namespace thor diff --git a/src/thor/optimized_route_action.cc b/src/thor/optimized_route_action.cc index 0da72727ca..f80b080d57 100644 --- a/src/thor/optimized_route_action.cc +++ b/src/thor/optimized_route_action.cc @@ -28,10 +28,9 @@ void thor_worker_t::optimized_route(Api& request) { // Use CostMatrix to find costs from each location to every other location CostMatrix costmatrix; + costmatrix.set_has_time(check_matrix_time(request, Matrix::CostMatrix)); costmatrix.SourceToTarget(request, *reader, mode_costing, mode, - max_matrix_distance.find(costing)->second, - check_matrix_time(request, Matrix::CostMatrix), - options.date_time_type() == Options::invariant); + max_matrix_distance.find(costing)->second); // Return an error if any locations are totally unreachable const auto& correlated = diff --git a/src/thor/timedistancebssmatrix.cc b/src/thor/timedistancebssmatrix.cc index 2fb5ffffa2..26da685662 100644 --- a/src/thor/timedistancebssmatrix.cc +++ b/src/thor/timedistancebssmatrix.cc @@ -7,22 +7,6 @@ using namespace valhalla::baldr; using namespace valhalla::sif; namespace { -static bool IsTrivial(const uint64_t& edgeid, - const valhalla::Location& origin, - const valhalla::Location& destination) { - for (const auto& destination_edge : destination.correlation().edges()) { - if (destination_edge.graph_id() == edgeid) { - for (const auto& origin_edge : origin.correlation().edges()) { - if (origin_edge.graph_id() == edgeid && - origin_edge.percent_along() <= destination_edge.percent_along()) { - return true; - } - } - } - } - return false; -} - static travel_mode_t get_other_travel_mode(const travel_mode_t current_mode) { static const auto bss_modes = std::vector{travel_mode_t::kPedestrian, travel_mode_t::kBicycle}; @@ -35,7 +19,7 @@ namespace thor { // Constructor with cost threshold. TimeDistanceBSSMatrix::TimeDistanceBSSMatrix(const boost::property_tree::ptree& config) - : settled_count_(0), current_cost_threshold_(0), + : MatrixAlgorithm(config), settled_count_(0), current_cost_threshold_(0), max_reserved_labels_count_(config.get("max_reserved_labels_count_dijkstras", kInitialEdgeLabelCountDijkstras)), clear_reserved_memory_(config.get("clear_reserved_memory", false)) { @@ -180,10 +164,10 @@ void TimeDistanceBSSMatrix::Expand(GraphReader& graphreader, template void TimeDistanceBSSMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations) { - // Run a series of one to many calls and concatenate the results. + const float max_matrix_distance) { + uint32_t matrix_locations = request.options().matrix_locations(); + // Run a series of one to many calls and concatenate the results. auto& origins = FORWARD ? *request.mutable_options()->mutable_sources() : *request.mutable_options()->mutable_targets(); auto& destinations = FORWARD ? *request.mutable_options()->mutable_targets() @@ -210,6 +194,7 @@ void TimeDistanceBSSMatrix::ComputeMatrix(Api& request, SetOrigin(graphreader, origin); SetDestinationEdges(); + uint32_t n = 0; // Find shortest path graph_tile_ptr tile; while (true) { @@ -259,6 +244,11 @@ void TimeDistanceBSSMatrix::ComputeMatrix(Api& request, // Expand forward from the end node of the predecessor edge. Expand(graphreader, pred.endnode(), pred, predindex, false, false, pred.mode()); + + // Allow this process to be aborted + if (interrupt_ && (n++ % kInterruptIterationsInterval) == 0) { + (*interrupt_)(); + } } reset(); } @@ -267,13 +257,11 @@ void TimeDistanceBSSMatrix::ComputeMatrix(Api& request, template void TimeDistanceBSSMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations); + const float max_matrix_distance); template void TimeDistanceBSSMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations); + const float max_matrix_distance); // Add edges at the origin to the adjacency list template diff --git a/src/thor/timedistancematrix.cc b/src/thor/timedistancematrix.cc index 68a22a0131..1d2e99b481 100644 --- a/src/thor/timedistancematrix.cc +++ b/src/thor/timedistancematrix.cc @@ -8,33 +8,14 @@ using namespace valhalla::baldr; using namespace valhalla::sif; -namespace { -static bool IsTrivial(const uint64_t& edgeid, - const valhalla::Location& origin, - const valhalla::Location& destination) { - for (const auto& destination_edge : destination.correlation().edges()) { - if (destination_edge.graph_id() == edgeid) { - for (const auto& origin_edge : origin.correlation().edges()) { - if (origin_edge.graph_id() == edgeid && - origin_edge.percent_along() <= destination_edge.percent_along()) { - return true; - } - } - } - } - return false; -} -} // namespace - namespace valhalla { namespace thor { // Constructor with cost threshold. TimeDistanceMatrix::TimeDistanceMatrix(const boost::property_tree::ptree& config) - : settled_count_(0), current_cost_threshold_(0), + : MatrixAlgorithm(config), settled_count_(0), current_cost_threshold_(0), max_reserved_labels_count_(config.get("max_reserved_labels_count_dijkstras", kInitialEdgeLabelCountDijkstras)), - clear_reserved_memory_(config.get("clear_reserved_memory", false)), mode_(travel_mode_t::kDrive) { } @@ -194,9 +175,10 @@ void TimeDistanceMatrix::Expand(GraphReader& graphreader, template void TimeDistanceMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations, - const bool invariant) { + const float max_matrix_distance) { + bool invariant = request.options().date_time_type() == Options::invariant; + uint32_t matrix_locations = request.options().matrix_locations(); + uint32_t bucketsize = costing_->UnitSize(); auto& origins = FORWARD ? *request.mutable_options()->mutable_sources() @@ -230,6 +212,7 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, SetOrigin(graphreader, origin, time_info); SetDestinationEdges(); + uint32_t n = 0; // Collect edge_ids used for settling a location to determine its time zone std::unordered_map dest_edge_ids; dest_edge_ids.reserve(destinations.size()); @@ -287,6 +270,11 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, // Expand forward from the end node of the predecessor edge. Expand(graphreader, pred.endnode(), pred, predindex, false, time_info, invariant); + + // Allow this process to be aborted + if (interrupt_ && (n++ % kInterruptIterationsInterval) == 0) { + (*interrupt_)(); + } } reset(); @@ -308,15 +296,11 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, template void TimeDistanceMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations, - const bool invariant); + const float max_matrix_distance); template void TimeDistanceMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations, - const bool invariant); + const float max_matrix_distance); // Add edges at the origin to the adjacency list template diff --git a/src/thor/worker.cc b/src/thor/worker.cc index 968a6df382..851baa881c 100644 --- a/src/thor/worker.cc +++ b/src/thor/worker.cc @@ -313,9 +313,9 @@ void thor_worker_t::cleanup() { multi_modal_astar.Clear(); bss_astar.Clear(); trace.clear(); - costmatrix_.clear(); - time_distance_matrix_.clear(); - time_distance_bss_matrix_.clear(); + costmatrix_.Clear(); + time_distance_matrix_.Clear(); + time_distance_bss_matrix_.Clear(); isochrone_gen.Clear(); centroid_gen.Clear(); matcher_factory.ClearFullCache(); diff --git a/src/tyr/matrix_serializer.cc b/src/tyr/matrix_serializer.cc index 9151744adb..91edc92350 100644 --- a/src/tyr/matrix_serializer.cc +++ b/src/tyr/matrix_serializer.cc @@ -2,7 +2,7 @@ #include "baldr/json.h" #include "proto_conversions.h" -#include "thor/matrix_common.h" +#include "thor/matrixalgorithm.h" #include "tyr/serializers.h" using namespace valhalla; diff --git a/src/tyr/serializers.cc b/src/tyr/serializers.cc index e98e5d589a..ef417fd739 100644 --- a/src/tyr/serializers.cc +++ b/src/tyr/serializers.cc @@ -251,7 +251,7 @@ std::string serializePbf(Api& request) { } // Generate leg shape in geojson format. -baldr::json::MapPtr geojson_shape(const std::vector shape) { +baldr::json::MapPtr geojson_shape(const std::vector shape) { auto geojson = baldr::json::map({}); auto coords = baldr::json::array({}); coords->reserve(shape.size()); diff --git a/src/tyr/trace_serializer.cc b/src/tyr/trace_serializer.cc index 9dd352edbc..662ce9d3e2 100644 --- a/src/tyr/trace_serializer.cc +++ b/src/tyr/trace_serializer.cc @@ -10,7 +10,6 @@ using namespace valhalla; using namespace valhalla::midgard; using namespace valhalla::baldr; using namespace valhalla::odin; -using namespace valhalla::thor; namespace { diff --git a/src/valhalla_run_matrix.cc b/src/valhalla_run_matrix.cc index ea22baefc6..f609f8a7bb 100644 --- a/src/valhalla_run_matrix.cc +++ b/src/valhalla_run_matrix.cc @@ -218,7 +218,7 @@ int main(int argc, char* argv[]) { for (uint32_t n = 0; n < iterations; n++) { request.clear_matrix(); matrix.SourceToTarget(request, reader, mode_costing, mode, max_distance); - matrix.clear(); + matrix.Clear(); } t1 = std::chrono::high_resolution_clock::now(); ms = std::chrono::duration_cast(t1 - t0).count(); @@ -231,7 +231,7 @@ int main(int argc, char* argv[]) { for (uint32_t n = 0; n < iterations; n++) { request.clear_matrix(); tdm.SourceToTarget(request, reader, mode_costing, mode, max_distance); - tdm.clear(); + tdm.Clear(); } t1 = std::chrono::high_resolution_clock::now(); ms = std::chrono::duration_cast(t1 - t0).count(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f77fcfd7d0..a8cc10457d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -85,7 +85,6 @@ set(tests_with_warnings logging maneuversbuilder mapmatch - matrix narrativebuilder node_search polyline2 diff --git a/test/gurka/test_matrix.cc b/test/gurka/test_matrix.cc index c2bb1d4971..0b38912af4 100644 --- a/test/gurka/test_matrix.cc +++ b/test/gurka/test_matrix.cc @@ -1,7 +1,7 @@ #include "gurka.h" #include "test.h" #include -#include +#include #include diff --git a/test/matrix.cc b/test/matrix.cc index af4651189e..82ee5b2eb2 100644 --- a/test/matrix.cc +++ b/test/matrix.cc @@ -220,7 +220,7 @@ TEST(Matrix, test_matrix) { CostMatrix cost_matrix; cost_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0); auto matrix = request.matrix(); - for (uint32_t i = 0; i < matrix.times().size(); ++i) { + for (int i = 0; i < matrix.times().size(); ++i) { EXPECT_NEAR(matrix.distances()[i], matrix_answers[i][1], kThreshold) << "result " + std::to_string(i) + "'s distance is not close enough" + " to expected value for CostMatrix"; @@ -237,7 +237,7 @@ TEST(Matrix, test_matrix) { matrix = request.matrix(); uint32_t found = 0; - for (uint32_t i = 0; i < matrix.times().size(); ++i) { + for (int i = 0; i < matrix.times().size(); ++i) { if (matrix.distances()[i] < kMaxCost) { ++found; } @@ -251,7 +251,7 @@ TEST(Matrix, test_matrix) { matrix = request.matrix(); found = 0; - for (uint32_t i = 0; i < matrix.times().size(); ++i) { + for (int i = 0; i < matrix.times().size(); ++i) { if (matrix.distances()[i] < kMaxCost) { ++found; } @@ -263,7 +263,7 @@ TEST(Matrix, test_matrix) { timedist_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0); matrix = request.matrix(); - for (uint32_t i = 0; i < matrix.times().size(); ++i) { + for (int i = 0; i < matrix.times().size(); ++i) { EXPECT_NEAR(matrix.distances()[i], matrix_answers[i][1], kThreshold) << "result " + std::to_string(i) + "'s distance is not equal" + " to expected value for TDMatrix"; @@ -314,7 +314,7 @@ TEST(Matrix, test_timedistancematrix_forward) { {2311, 2111}, {701, 641}, {0, 0}, {2821, 2626}}; // clang-format on - for (uint32_t i = 0; i < matrix.times().size(); ++i) { + for (int i = 0; i < matrix.times().size(); ++i) { EXPECT_NEAR(matrix.distances()[i], expected_results[i][1], kThreshold) << "result " + std::to_string(i) + "'s distance is not equal" + " to expected value for TDMatrix"; @@ -366,7 +366,7 @@ TEST(Matrix, test_timedistancematrix_reverse) { {5562, 5177}, {3952, 3707}, {4367, 4107}}; // clang-format on - for (uint32_t i = 0; i < matrix.times().size(); ++i) { + for (int i = 0; i < matrix.times().size(); ++i) { EXPECT_NEAR(matrix.distances()[i], expected_results[i][1], kThreshold) << "result " + std::to_string(i) + "'s distance is not equal" + " to expected value for TDMatrix"; @@ -433,11 +433,10 @@ TEST(Matrix, partial_matrix) { CreateSimpleCost(request.options().costings().find(request.options().costing_type())->second); TimeDistanceMatrix timedist_matrix; - timedist_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0, - request.options().matrix_locations()); + timedist_matrix.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, 400000.0); auto& matrix = request.matrix(); uint32_t found = 0; - for (uint32_t i = 0; i < matrix.times().size(); ++i) { + for (int i = 0; i < matrix.times().size(); ++i) { if (matrix.distances()[i] > 0) { ++found; } diff --git a/test/matrix_bss.cc b/test/matrix_bss.cc index 174b5531cd..fdb974eb80 100644 --- a/test/matrix_bss.cc +++ b/test/matrix_bss.cc @@ -14,7 +14,7 @@ #include "sif/costfactory.h" #include "sif/dynamiccost.h" -#include "thor/matrix_common.h" +#include "thor/matrixalgorithm.h" #include "thor/timedistancebssmatrix.h" using namespace valhalla; diff --git a/valhalla/thor/costmatrix.h b/valhalla/thor/costmatrix.h index 934a088236..90340bad6e 100644 --- a/valhalla/thor/costmatrix.h +++ b/valhalla/thor/costmatrix.h @@ -15,9 +15,7 @@ #include #include #include -#include -// this is only for EdgeMetadata. one day we should move to a global interface -#include +#include #include namespace valhalla { @@ -81,7 +79,7 @@ struct BestCandidate { * Shortest Paths". * https://i11www.iti.uni-karlsruhe.de/_media/teaching/theses/files/da-sknopp-06.pdf */ -class CostMatrix { +class CostMatrix : public MatrixAlgorithm { public: /** * Default constructor. Most internal values are set when a query is made so @@ -99,55 +97,21 @@ class CostMatrix { * @param mode_costing List of target/destination locations. * @param mode Graph reader for accessing routing graph. * @param max_matrix_distance Maximum arc-length distance for current mode. - * @param has_time whether time-dependence was requested - * @param invariant whether invariant time-dependence was requested - * @param shape_format which shape_format, if any */ void SourceToTarget(Api& request, baldr::GraphReader& graphreader, const sif::mode_costing_t& mode_costing, const sif::travel_mode_t mode, - const float max_matrix_distance, - const bool has_time = false, - const bool invariant = false, - const ShapeFormat& shape_format = no_shape); + const float max_matrix_distance) override; /** * Clear the temporary information generated during time+distance * matrix construction. */ - void clear(); - - /** - * Sets the functor which will track the Dijkstra expansion. - * - * @param expansion_callback the functor to call back when the Dijkstra makes progress - * on a given edge - */ - using expansion_callback_t = std::function; - void set_track_expansion(const expansion_callback_t& expansion_callback) { - expansion_callback_ = expansion_callback; - } - - /** - * Set a callback that will throw when the path computation should be aborted - * @param interrupt_callback the function to periodically call to see if - * we should abort - */ - void set_interrupt(const std::function* interrupt_callback) { - interrupt_ = interrupt_callback; - } + void Clear() override; protected: uint32_t max_reserved_labels_count_; - bool clear_reserved_memory_; uint32_t max_reserved_locations_count_; bool check_reverse_connections_; @@ -186,14 +150,6 @@ class CostMatrix { // when doing timezone differencing a timezone cache speeds up the computation baldr::DateTime::tz_sys_info_cache_t tz_cache_; - // for tracking the expansion of the Dijkstra - expansion_callback_t expansion_callback_; - - // whether time was specified - bool has_time_; - - const std::function* interrupt_ = nullptr; - /** * Get the cost threshold based on the current mode and the max arc-length distance * for that mode. diff --git a/valhalla/thor/matrix_common.h b/valhalla/thor/matrix_common.h deleted file mode 100644 index 92ff454ef5..0000000000 --- a/valhalla/thor/matrix_common.h +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef VALHALLA_THOR_MATRIX_COMMON_H_ -#define VALHALLA_THOR_MATRIX_COMMON_H_ - -#include -#include -#include -#include -#include -#include - -#include "proto/matrix.pb.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace valhalla { -namespace thor { - -// Default for time distance matrix is to find all locations -constexpr uint32_t kAllLocations = std::numeric_limits::max(); -constexpr float kMaxCost = 99999999.9999f; - -// Structure to hold information about each destination. -struct Destination { - // per-origin information - bool settled; // Has the best time/distance to this destination - // been found? - sif::Cost best_cost; // Current best cost to this destination - // Set of still available correlated edges; - std::unordered_set dest_edges_available; - - // global information which only needs to be set once or is reset for every origin in the algorithm - uint32_t distance; // Path distance for the best cost path - float threshold; // Threshold above current best cost where no longer - // need to search for this destination. - // partial distance of correlated edges - std::unordered_map dest_edges_percent_along; - - // Constructor - set best_cost to an absurdly high value so any new cost - // will be lower. - Destination() : settled(false), best_cost{kMaxCost, kMaxCost}, distance(0), threshold(0.0f) { - } - - // clears the per-origin information - void reset() { - settled = false; - best_cost = {kMaxCost, kMaxCost}; - dest_edges_available.clear(); - } -}; - -// return true if any location had a valid time set -// return false if it doesn't make sense computationally and add warnings accordingly -inline bool check_matrix_time(Api& request, const Matrix::Algorithm algo) { - const auto& options = request.options(); - bool less_sources = options.sources().size() <= options.targets().size(); - - for (const auto& source : options.sources()) { - if (!source.date_time().empty()) { - if (!less_sources && algo == Matrix::TimeDistanceMatrix) { - add_warning(request, 201); - return false; - } - return true; - } - } - for (const auto& target : options.targets()) { - if (!target.date_time().empty()) { - if (less_sources && algo == Matrix::TimeDistanceMatrix) { - add_warning(request, 202); - return false; - } else if (algo == Matrix::CostMatrix) { - add_warning(request, 206); - return false; - } - return true; - } - } - - return false; -} - -// resizes all PBF sequences except for date_times -inline void reserve_pbf_arrays(valhalla::Matrix& matrix, size_t size) { - matrix.mutable_from_indices()->Resize(size, 0U); - matrix.mutable_to_indices()->Resize(size, 0U); - matrix.mutable_distances()->Resize(size, 0U); - matrix.mutable_times()->Resize(size, 0U); - matrix.mutable_date_times()->Reserve(size); - matrix.mutable_shapes()->Reserve(size); - matrix.mutable_time_zone_offsets()->Reserve(size); - matrix.mutable_time_zone_names()->Reserve(size); -} - -} // namespace thor -} // namespace valhalla - -#endif // VALHALLA_THOR_MATRIX_COMMON_H_ diff --git a/valhalla/thor/matrixalgorithm.h b/valhalla/thor/matrixalgorithm.h new file mode 100644 index 0000000000..a006b3c73b --- /dev/null +++ b/valhalla/thor/matrixalgorithm.h @@ -0,0 +1,192 @@ +#ifndef __VALHALLA_THOR_MATRIXALGORITHM_H__ +#define __VALHALLA_THOR_MATRIXALGORITHM_H__ + +#include + +#include + +#include +#include +#include +// TODO(nils): should abstract more so we don't pull this in +#include +#include + +namespace valhalla { +namespace thor { + +// Default for time distance matrix is to find all locations +constexpr uint32_t kAllLocations = std::numeric_limits::max(); +constexpr float kMaxCost = 99999999.9999f; + +/** + * Pure virtual class defining the interface for MatrixAlgorithm + */ +class MatrixAlgorithm { +public: + /** + * Constructor + */ + MatrixAlgorithm(const boost::property_tree::ptree& config) + : interrupt_(nullptr), has_time_(false), expansion_callback_(), + clear_reserved_memory_(config.get("clear_reserved_memory", false)) { + } + + MatrixAlgorithm(const MatrixAlgorithm&) = delete; + MatrixAlgorithm& operator=(const MatrixAlgorithm&) = delete; + + /** + * Destructor + */ + virtual ~MatrixAlgorithm() { + } + + /** + * Forms a time distance matrix from the set of source locations + * to the set of target locations. + * @param request the full request + * @param graphreader List of source/origin locations. + * @param mode_costing List of target/destination locations. + * @param mode Graph reader for accessing routing graph. + * @param max_matrix_distance Maximum arc-length distance for current mode. + * @param has_time whether time-dependence was requested + * @param invariant whether invariant time-dependence was requested + * @param shape_format which shape_format, if any + */ + virtual void SourceToTarget(Api& request, + baldr::GraphReader& graphreader, + const sif::mode_costing_t& mode_costing, + const sif::travel_mode_t mode, + const float max_matrix_distance) = 0; + + /** + * Clear the temporary information. + */ + virtual void Clear() = 0; + + /** + * Returns the name of the algorithm + * @return the name of the algorithm + */ + void set_has_time(const bool has_time) { + has_time_ = has_time; + }; + + /** + * Set a callback that will throw when the path computation should be aborted + * @param interrupt_callback the function to periodically call to see if + * we should abort + */ + void set_interrupt(const std::function* interrupt_callback) { + interrupt_ = interrupt_callback; + } + + /** + * Sets the functor which will track the algorithms expansion. + * + * @param expansion_callback the functor to call back when the algorithm makes progress + * on a given edge + */ + using expansion_callback_t = std::function; + void set_track_expansion(const expansion_callback_t& expansion_callback) { + expansion_callback_ = expansion_callback; + } + +protected: + const std::function* interrupt_; + + // whether time was specified + bool has_time_; + + // for tracking the expansion of the algorithm visually + expansion_callback_t expansion_callback_; + + uint32_t max_reserved_labels_count_; + + // if `true` clean reserved memory for edge labels + bool clear_reserved_memory_; + + // resizes all PBF sequences except for repeated string where it reserves instead + inline static void reserve_pbf_arrays(valhalla::Matrix& matrix, size_t size) { + matrix.mutable_from_indices()->Resize(size, 0U); + matrix.mutable_to_indices()->Resize(size, 0U); + matrix.mutable_distances()->Resize(size, 0U); + matrix.mutable_times()->Resize(size, 0U); + matrix.mutable_date_times()->Reserve(size); + matrix.mutable_shapes()->Reserve(size); + matrix.mutable_time_zone_offsets()->Reserve(size); + matrix.mutable_time_zone_names()->Reserve(size); + } +}; + +// Structure to hold information about each destination. +struct Destination { + // per-origin information + bool settled; // Has the best time/distance to this destination + // been found? + sif::Cost best_cost; // Current best cost to this destination + // Set of still available correlated edges; + std::unordered_set dest_edges_available; + + // global information which only needs to be set once or is reset for every origin in the algorithm + uint32_t distance; // Path distance for the best cost path + float threshold; // Threshold above current best cost where no longer + // need to search for this destination. + // partial distance of correlated edges + std::unordered_map dest_edges_percent_along; + + // Constructor - set best_cost to an absurdly high value so any new cost + // will be lower. + Destination() : settled(false), best_cost{kMaxCost, kMaxCost}, distance(0), threshold(0.0f) { + } + + // clears the per-origin information + void reset() { + settled = false; + best_cost = {kMaxCost, kMaxCost}; + dest_edges_available.clear(); + } +}; + +// return true if any location had a valid time set +// return false if it doesn't make sense computationally and add warnings accordingly +inline bool check_matrix_time(Api& request, const Matrix::Algorithm algo) { + const auto& options = request.options(); + bool less_sources = options.sources().size() <= options.targets().size(); + + for (const auto& source : options.sources()) { + if (!source.date_time().empty()) { + if (!less_sources && algo == Matrix::TimeDistanceMatrix) { + add_warning(request, 201); + return false; + } + return true; + } + } + for (const auto& target : options.targets()) { + if (!target.date_time().empty()) { + if (less_sources && algo == Matrix::TimeDistanceMatrix) { + add_warning(request, 202); + return false; + } else if (algo == Matrix::CostMatrix) { + add_warning(request, 206); + return false; + } + return true; + } + } + + return false; +} + +} // namespace thor +} // namespace valhalla + +#endif // __VALHALLA_THOR_MATRIXALGORITHM_H__ diff --git a/valhalla/thor/pathalgorithm.h b/valhalla/thor/pathalgorithm.h index a2ada437bb..0c18bcf914 100644 --- a/valhalla/thor/pathalgorithm.h +++ b/valhalla/thor/pathalgorithm.h @@ -155,32 +155,32 @@ class PathAlgorithm { // if `true` clean reserved memory for edge labels bool clear_reserved_memory_; +}; - /** - * Check for path completion along the same edge. Edge ID in question - * is along both an origin and destination and origin shows up at the - * beginning of the edge while the destination shows up at the end of - * the edge. - * @param edgeid Edge id. - * @param origin Origin path location information. - * @param destination Destination path location information. - */ - virtual bool IsTrivial(const baldr::GraphId& edgeid, - const valhalla::Location& origin, - const valhalla::Location& destination) const { - for (const auto& destination_edge : destination.correlation().edges()) { - if (destination_edge.graph_id() == edgeid) { - for (const auto& origin_edge : origin.correlation().edges()) { - if (origin_edge.graph_id() == edgeid && - origin_edge.percent_along() <= destination_edge.percent_along()) { - return true; - } +/** + * Check for path completion along the same edge. Edge ID in question + * is along both an origin and destination and origin shows up at the + * beginning of the edge while the destination shows up at the end of + * the edge. + * @param edgeid Edge id. + * @param origin Origin path location information. + * @param destination Destination path location information. + */ +inline bool IsTrivial(const baldr::GraphId& edgeid, + const valhalla::Location& origin, + const valhalla::Location& destination) { + for (const auto& destination_edge : destination.correlation().edges()) { + if (destination_edge.graph_id() == edgeid) { + for (const auto& origin_edge : origin.correlation().edges()) { + if (origin_edge.graph_id() == edgeid && + origin_edge.percent_along() <= destination_edge.percent_along()) { + return true; } } } - return false; } -}; + return false; +} // Container for the data we iterate over in Expand* function struct EdgeMetadata { diff --git a/valhalla/thor/timedistancebssmatrix.h b/valhalla/thor/timedistancebssmatrix.h index 6e128cac35..de83b37b09 100644 --- a/valhalla/thor/timedistancebssmatrix.h +++ b/valhalla/thor/timedistancebssmatrix.h @@ -16,14 +16,13 @@ #include #include #include -#include -#include +#include namespace valhalla { namespace thor { // Class to compute time + distance matrices among locations. -class TimeDistanceBSSMatrix { +class TimeDistanceBSSMatrix : public MatrixAlgorithm { public: /** * Default constructor. Most internal values are set when a query is made so @@ -34,28 +33,26 @@ class TimeDistanceBSSMatrix { /** * Forms a time distance matrix from the set of source locations * to the set of target locations. - * @param source_location_list List of source/origin locations. - * @param target_location_list List of target/destination locations. + * @param request the full request * @param graphreader Graph reader for accessing routing graph. * @param mode_costing Costing methods. * @param mode Travel mode to use. Actually It doesn't make sense in matrix_bss, * because the travel mode must be pedestrian and bicycle * @param max_matrix_distance Maximum arc-length distance for current mode. - * @param matrix_locations Number of matrix locations to satisfy a one to many or many to - * one request. This allows partial results: e.g. find time/distance - * to the closest 20 out of 50 locations). * @return time/distance from origin index to all other locations */ inline void SourceToTarget(Api& request, baldr::GraphReader& graphreader, const sif::mode_costing_t& mode_costing, const sif::travel_mode_t /*mode*/, - const float max_matrix_distance, - const uint32_t matrix_locations = kAllLocations) { + const float max_matrix_distance) override { LOG_INFO("matrix::TimeDistanceBSSMatrix"); request.mutable_matrix()->set_algorithm(Matrix::TimeDistanceMatrix); + if (request.options().shape_format() != no_shape) + add_warning(request, 207); + // Set the costings pedestrian_costing_ = mode_costing[static_cast(sif::travel_mode_t::kPedestrian)]; bicycle_costing_ = mode_costing[static_cast(sif::travel_mode_t::kBicycle)]; @@ -63,11 +60,9 @@ class TimeDistanceBSSMatrix { const bool forward_search = request.options().sources().size() <= request.options().targets().size(); if (forward_search) { - return ComputeMatrix(request, graphreader, max_matrix_distance, - matrix_locations); + return ComputeMatrix(request, graphreader, max_matrix_distance); } else { - return ComputeMatrix(request, graphreader, max_matrix_distance, - matrix_locations); + return ComputeMatrix(request, graphreader, max_matrix_distance); } }; @@ -75,7 +70,7 @@ class TimeDistanceBSSMatrix { * Clear the temporary information generated during time+distance * matrix construction. */ - inline void clear() { + inline void Clear() override { auto reservation = clear_reserved_memory_ ? 0 : max_reserved_labels_count_; if (edgelabels_.size() > reservation) { edgelabels_.resize(max_reserved_labels_count_); @@ -151,10 +146,7 @@ class TimeDistanceBSSMatrix { */ template - void ComputeMatrix(Api& request, - baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations = kAllLocations); + void ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); /** * Expand from the node along the forward search path. Immediately expands diff --git a/valhalla/thor/timedistancematrix.h b/valhalla/thor/timedistancematrix.h index cf4fac1162..c10ec98676 100644 --- a/valhalla/thor/timedistancematrix.h +++ b/valhalla/thor/timedistancematrix.h @@ -14,14 +14,13 @@ #include #include #include -#include -#include +#include namespace valhalla { namespace thor { // Class to compute time + distance matrices among locations. -class TimeDistanceMatrix { +class TimeDistanceMatrix : public MatrixAlgorithm { public: /** * Default constructor. Most internal values are set when a query is made so @@ -32,17 +31,11 @@ class TimeDistanceMatrix { /** * Forms a time distance matrix from the set of source locations * to the set of target locations. - * @param source_location_list List of source/origin locations. - * @param target_location_list List of target/destination locations. + * @param request the full request * @param graphreader Graph reader for accessing routing graph. * @param mode_costing Costing methods. * @param mode Travel mode to use. * @param max_matrix_distance Maximum arc-length distance for current mode. - * @param matrix_locations Number of matrix locations to satisfy a one to many or many to - * one request. This allows partial results: e.g. find time/distance - * to the closest 20 out of 50 locations). - * @param has_time Whether the request had valid date_time. - * @param invariant Whether invariant time was requested. * * @return time/distance from all sources to all targets */ @@ -50,13 +43,14 @@ class TimeDistanceMatrix { baldr::GraphReader& graphreader, const sif::mode_costing_t& mode_costing, const sif::travel_mode_t mode, - const float max_matrix_distance, - const uint32_t matrix_locations = kAllLocations, - const bool invariant = false) { + const float max_matrix_distance) override { LOG_INFO("matrix::TimeDistanceMatrix"); request.mutable_matrix()->set_algorithm(Matrix::TimeDistanceMatrix); + if (request.options().shape_format() != no_shape) + add_warning(request, 207); + // Set the mode and costing mode_ = mode; costing_ = mode_costing[static_cast(mode_)]; @@ -64,11 +58,9 @@ class TimeDistanceMatrix { const bool forward_search = request.options().sources().size() <= request.options().targets().size(); if (forward_search) { - return ComputeMatrix(request, graphreader, max_matrix_distance, - matrix_locations, invariant); + return ComputeMatrix(request, graphreader, max_matrix_distance); } else { - return ComputeMatrix(request, graphreader, max_matrix_distance, - matrix_locations, invariant); + return ComputeMatrix(request, graphreader, max_matrix_distance); } }; @@ -76,7 +68,7 @@ class TimeDistanceMatrix { * Clear the temporary information generated during time+distance * matrix construction. */ - inline void clear() { + inline void Clear() override { auto reservation = clear_reserved_memory_ ? 0 : max_reserved_labels_count_; if (edgelabels_.size() > reservation) { edgelabels_.resize(reservation); @@ -147,11 +139,7 @@ class TimeDistanceMatrix { */ template - void ComputeMatrix(Api& request, - baldr::GraphReader& graphreader, - const float max_matrix_distance, - const uint32_t matrix_locations = kAllLocations, - const bool invariant = false); + void ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); /** * Expand from the node along the forward search path. Immediately expands diff --git a/valhalla/thor/worker.h b/valhalla/thor/worker.h index b9fb120981..0548629afa 100644 --- a/valhalla/thor/worker.h +++ b/valhalla/thor/worker.h @@ -77,6 +77,7 @@ class thor_worker_t : public service_worker_t { const Location& origin, const Location& destination, const Options& options); + thor::MatrixAlgorithm* get_matrix_algorithm(Api& request, const bool has_time); void route_match(Api& request); /** * Returns the results of the map match where the first float is the normalized diff --git a/valhalla/tyr/serializers.h b/valhalla/tyr/serializers.h index 5404c7f6a0..8158570fcf 100644 --- a/valhalla/tyr/serializers.h +++ b/valhalla/tyr/serializers.h @@ -15,8 +15,8 @@ #include #include #include +#include #include -#include #include namespace valhalla { @@ -129,7 +129,7 @@ baldr::json::ArrayPtr serializeWarnings(const valhalla::Api& api); * @param shape The points making up the line. * @returns The GeoJSON geometry of the LineString */ -baldr::json::MapPtr geojson_shape(const std::vector shape); +baldr::json::MapPtr geojson_shape(const std::vector shape); // Elevation serialization support From c9c241873bab2155c814d5d04ab740f492427b06 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 1 Feb 2024 11:26:43 +0100 Subject: [PATCH 015/198] try resetting the cache in win ci --- .github/workflows/win.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index 6ff379ddc2..ff326b073b 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -64,7 +64,7 @@ jobs: uses: actions/cache@v4 with: path: ${{ env.VCPKG_ROOT }} - key: vcpkg=${{ env.VCPKG_VERSION }}-msvc=${{ env.MSVC_VERSION }}-json=${{ hashFiles('vcpkg.json') }} + key: vcpkg=${{ env.VCPKG_VERSION }}-msvc=${{ env.MSVC_VERSION }}-json=${{ hashFiles('vcpkg.json') }}-v2 save-always: 'true' - name: Install GNU make & awk @@ -106,9 +106,9 @@ jobs: -DENABLE_DATA_TOOLS=ON \ -DENABLE_TOOLS=ON \ -DENABLE_PYTHON_BINDINGS=ON \ + -DENABLE_HTTP=ON \ -DENABLE_TESTS=OFF \ -DENABLE_CCACHE=OFF \ - -DENABLE_HTTP=OFF \ -DENABLE_SERVICES=OFF \ -DENABLE_BENCHMARKS=OFF From 3a92152fd2ba7ba36063acd40550f4f1d4e69c07 Mon Sep 17 00:00:00 2001 From: Nils Date: Fri, 2 Feb 2024 19:10:47 +0100 Subject: [PATCH 016/198] second pass for CostMatrix (#4536) --- CHANGELOG.md | 1 + lua/graph.lua | 1 + proto/matrix.proto | 2 + scripts/valhalla_build_config | 2 + src/proto_conversions.cc | 39 ++++++----- src/thor/bidirectional_astar.cc | 2 +- src/thor/costmatrix.cc | 97 ++++++++++++++++----------- src/thor/matrix_action.cc | 56 ++++++++++++++-- src/thor/route_action.cc | 1 + src/thor/timedistancebssmatrix.cc | 9 ++- src/thor/timedistancematrix.cc | 43 +++++------- src/thor/worker.cc | 2 + src/tyr/matrix_serializer.cc | 17 +++-- src/worker.cc | 14 ++-- test/gurka/test_matrix.cc | 63 ++++++++++++++++- valhalla/proto_conversions.h | 2 +- valhalla/thor/costmatrix.h | 14 +++- valhalla/thor/matrixalgorithm.h | 74 ++++++++++++++++---- valhalla/thor/timedistancebssmatrix.h | 14 +++- valhalla/thor/timedistancematrix.h | 17 +++-- valhalla/thor/worker.h | 4 +- valhalla/worker.h | 10 ++- 22 files changed, 344 insertions(+), 140 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a800e1238e..75f2a36b6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ * FIXED: Fix segfault in costmatrix (date_time and time zone always added). [#4530](https://github.com/valhalla/valhalla/pull/4530) * CHANGED: libvalhalla.pc generation to have finer controls; install third_party public headers; overhaul lots of CMake; remove conan support [#4516](https://github.com/valhalla/valhalla/pull/4516) * CHANGED: refactored matrix code to include a base class for all matrix algorithms to prepare for second passes on matrix [#4535](https://github.com/valhalla/valhalla/pull/4535) + * ADDED: matrix second pass for connections not found in the first pass, analogous to /route [#4536](https://github.com/valhalla/valhalla/pull/4536) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/lua/graph.lua b/lua/graph.lua index 70261199e9..b27d4af43b 100644 --- a/lua/graph.lua +++ b/lua/graph.lua @@ -1658,6 +1658,7 @@ function filter_tags_generic(kv) kv["link_type"] = kv["link_type"] end + --- TODO(nils): "private" also has directionality which we don't parse and handle yet kv["private"] = private[kv["access"]] or private[kv["motor_vehicle"]] or private[kv["motorcar"]] or "false" kv["private_hgv"] = private[kv["hgv"]] or kv["private"] or "false" kv["no_thru_traffic"] = no_thru_traffic[kv["access"]] or "false" diff --git a/proto/matrix.proto b/proto/matrix.proto index e89b74b6d0..54c47c0a1c 100644 --- a/proto/matrix.proto +++ b/proto/matrix.proto @@ -7,6 +7,7 @@ message Matrix { enum Algorithm { TimeDistanceMatrix = 0; CostMatrix = 1; + TimeDistanceBSSMatrix = 2; } repeated uint32 distances = 2; @@ -18,4 +19,5 @@ message Matrix { repeated string shapes = 8; repeated string time_zone_offsets = 9; repeated string time_zone_names = 10; + repeated bool second_pass = 11; } diff --git a/scripts/valhalla_build_config b/scripts/valhalla_build_config index fb14f04734..862409da38 100755 --- a/scripts/valhalla_build_config +++ b/scripts/valhalla_build_config @@ -119,6 +119,7 @@ config = { 'max_reserved_labels_count_dijkstras': 4000000, 'max_reserved_labels_count_bidir_dijkstras': 2000000, 'costmatrix_check_reverse_connection': False, + 'costmatrix_second_pass': False, 'max_reserved_locations_costmatrix': 25, 'clear_reserved_memory': False, 'extended_search': False, @@ -370,6 +371,7 @@ help_text = { }, 'source_to_target_algorithm': 'Which matrix algorithm should be used, one of "timedistancematrix" or "costmatrix". If blank, the optimal will be selected.', 'costmatrix_check_reverse_connection': 'Whether to check for expansion connections on the reverse tree, which has an adverse effect on performance', + 'costmatrix_second_pass': "Whether to allow a second pass for unfound CostMatrix connections, where we turn off destination-only, relax hierarchies and expand into 'semi-islands'b", 'service': {'proxy': 'IPC linux domain socket file location'}, 'max_reserved_labels_count_astar': 'Maximum capacity allowed to keep reserved for unidirectional A*.', 'max_reserved_labels_count_bidir_astar': 'Maximum capacity allowed to keep reserved for bidirectional A*.', diff --git a/src/proto_conversions.cc b/src/proto_conversions.cc index 16843e33ba..bcab5517f3 100644 --- a/src/proto_conversions.cc +++ b/src/proto_conversions.cc @@ -3,9 +3,17 @@ using namespace valhalla; +const std::string empty_str; + namespace valhalla { -std::string MatrixAlgoToString(const valhalla::Matrix::Algorithm algo) { - return algo == valhalla::Matrix::CostMatrix ? "costmatrix" : "timedistancematrix"; +const std::string& MatrixAlgoToString(const valhalla::Matrix::Algorithm algo) { + static const std::unordered_map algos{ + {valhalla::Matrix::CostMatrix, "costmatrix"}, + {valhalla::Matrix::TimeDistanceMatrix, "timedistancematrix"}, + {valhalla::Matrix::TimeDistanceBSSMatrix, "timedistancbssematrix"}, + }; + auto i = algos.find(algo); + return i == algos.cend() ? empty_str : i->second; }; std::string incidentTypeToString(const valhalla::IncidentsTile::Metadata::Type& incident_type) { @@ -88,7 +96,6 @@ const char* incidentImpactToString(const valhalla::IncidentsTile::Metadata::Impa } const std::string& GuidanceViewTypeToString(const valhalla::DirectionsLeg_GuidanceView_Type type) { - static const std::string empty; static const std::unordered_map types{{DirectionsLeg_GuidanceView_Type_kJunction, "jct"}, {DirectionsLeg_GuidanceView_Type_kSapa, "sapa"}, @@ -100,7 +107,7 @@ const std::string& GuidanceViewTypeToString(const valhalla::DirectionsLeg_Guidan {DirectionsLeg_GuidanceView_Type_kDirectionboard, "directionboard"}, {DirectionsLeg_GuidanceView_Type_kSignboard, "signboard"}}; auto i = types.find(type); - return i == types.cend() ? empty : i->second; + return i == types.cend() ? empty_str : i->second; } bool Options_Action_Enum_Parse(const std::string& action, Options::Action* a) { @@ -138,7 +145,6 @@ bool Options_ExpansionAction_Enum_Parse(const std::string& action, Options::Acti } const std::string& Options_Action_Enum_Name(const Options::Action action) { - static const std::string empty; static const std::unordered_map actions{ {Options::route, "route"}, {Options::locate, "locate"}, @@ -154,7 +160,7 @@ const std::string& Options_Action_Enum_Name(const Options::Action action) { {Options::status, "status"}, }; auto i = actions.find(action); - return i == actions.cend() ? empty : i->second; + return i == actions.cend() ? empty_str : i->second; } bool Location_Type_Enum_Parse(const std::string& type, Location::Type* t) { @@ -171,7 +177,6 @@ bool Location_Type_Enum_Parse(const std::string& type, Location::Type* t) { return true; } const std::string& Location_Type_Enum_Name(const Location::Type type) { - static const std::string empty; static const std::unordered_map types{ {Location::kBreak, "break"}, {Location::kThrough, "through"}, @@ -179,18 +184,17 @@ const std::string& Location_Type_Enum_Name(const Location::Type type) { {Location::kVia, "via"}, }; auto i = types.find(type); - return i == types.cend() ? empty : i->second; + return i == types.cend() ? empty_str : i->second; } const std::string& Location_SideOfStreet_Enum_Name(const Location::SideOfStreet side) { - static const std::string empty; static const std::unordered_map sides{ {Location::kLeft, "left"}, {Location::kRight, "right"}, {Location::kNone, "none"}, }; auto i = sides.find(side); - return i == sides.cend() ? empty : i->second; + return i == sides.cend() ? empty_str : i->second; } bool Costing_Enum_Parse(const std::string& costing, Costing::Type* c) { @@ -219,7 +223,6 @@ bool Costing_Enum_Parse(const std::string& costing, Costing::Type* c) { } const std::string& Costing_Enum_Name(const Costing::Type costing) { - static const std::string empty; static const std::unordered_map costings{ {Costing::auto_, "auto"}, // auto_shorter is deprecated @@ -237,7 +240,7 @@ const std::string& Costing_Enum_Name(const Costing::Type costing) { {Costing::bikeshare, "bikeshare"}, }; auto i = costings.find(costing); - return i == costings.cend() ? empty : i->second; + return i == costings.cend() ? empty_str : i->second; } bool ShapeMatch_Enum_Parse(const std::string& match, ShapeMatch* s) { @@ -254,14 +257,13 @@ bool ShapeMatch_Enum_Parse(const std::string& match, ShapeMatch* s) { } const std::string& ShapeMatch_Enum_Name(const ShapeMatch match) { - static const std::string empty; static const std::unordered_map matches{ {ShapeMatch::edge_walk, "edge_walk"}, {ShapeMatch::map_snap, "map_snap"}, {ShapeMatch::walk_or_snap, "walk_or_snap"}, }; auto i = matches.find(match); - return i == matches.cend() ? empty : i->second; + return i == matches.cend() ? empty_str : i->second; } bool Options_Format_Enum_Parse(const std::string& format, Options::Format* f) { @@ -279,7 +281,6 @@ bool Options_Format_Enum_Parse(const std::string& format, Options::Format* f) { } const std::string& Options_Format_Enum_Name(const Options::Format match) { - static const std::string empty; static const std::unordered_map formats{ {Options::json, "json"}, {Options::gpx, "gpx"}, @@ -287,17 +288,16 @@ const std::string& Options_Format_Enum_Name(const Options::Format match) { {Options::pbf, "pbf"}, }; auto i = formats.find(match); - return i == formats.cend() ? empty : i->second; + return i == formats.cend() ? empty_str : i->second; } const std::string& Options_Units_Enum_Name(const Options::Units unit) { - static const std::string empty; static const std::unordered_map units{ {Options::kilometers, "kilometers"}, {Options::miles, "miles"}, }; auto i = units.find(unit); - return i == units.cend() ? empty : i->second; + return i == units.cend() ? empty_str : i->second; } bool FilterAction_Enum_Parse(const std::string& action, FilterAction* a) { @@ -313,13 +313,12 @@ bool FilterAction_Enum_Parse(const std::string& action, FilterAction* a) { } const std::string& FilterAction_Enum_Name(const FilterAction action) { - static const std::string empty; static const std::unordered_map actions{ {FilterAction::exclude, "exclude"}, {FilterAction::include, "include"}, }; auto i = actions.find(action); - return i == actions.cend() ? empty : i->second; + return i == actions.cend() ? empty_str : i->second; } bool DirectionsType_Enum_Parse(const std::string& dtype, DirectionsType* t) { diff --git a/src/thor/bidirectional_astar.cc b/src/thor/bidirectional_astar.cc index 9bb173807d..61de6281f9 100644 --- a/src/thor/bidirectional_astar.cc +++ b/src/thor/bidirectional_astar.cc @@ -90,7 +90,7 @@ void BidirectionalAStar::Clear() { // Set the ferry flag to false has_ferry_ = false; // Set not thru pruning to true - not_thru_pruning_ = true; + set_not_thru_pruning(true); // reset origin & destination pruning states pruning_disabled_at_origin_ = false; pruning_disabled_at_destination_ = false; diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 6a569af0fd..2386aa1229 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -127,13 +127,11 @@ void CostMatrix::Clear() { // Form a time distance matrix from the set of source locations // to the set of target locations. -void CostMatrix::SourceToTarget(Api& request, +bool CostMatrix::SourceToTarget(Api& request, baldr::GraphReader& graphreader, const sif::mode_costing_t& mode_costing, const sif::travel_mode_t mode, const float max_matrix_distance) { - - LOG_INFO("matrix::CostMatrix"); request.mutable_matrix()->set_algorithm(Matrix::CostMatrix); bool invariant = request.options().date_time_type() == Options::invariant; auto shape_format = request.options().shape_format(); @@ -153,7 +151,7 @@ void CostMatrix::SourceToTarget(Api& request, // Initialize best connections and status. Any locations that are the // same get set to 0 time, distance and are not added to the remaining // location set. - Initialize(source_location_list, target_location_list); + Initialize(source_location_list, target_location_list, request.matrix()); // Set the source and target locations // TODO: for now we only allow depart_at/current date_time @@ -259,21 +257,28 @@ void CostMatrix::SourceToTarget(Api& request, } } + // resize/reserve all properties of Matrix on first pass only + valhalla::Matrix& matrix = *request.mutable_matrix(); + reserve_pbf_arrays(matrix, best_connection_.size(), costing_->pass()); + // Form the matrix PBF output graph_tile_ptr tile; - uint32_t count = 0; - valhalla::Matrix& matrix = *request.mutable_matrix(); - reserve_pbf_arrays(matrix, best_connection_.size()); - for (auto& connection : best_connection_) { - uint32_t target_idx = count % target_location_list.size(); - uint32_t source_idx = count / target_location_list.size(); + bool connection_failed = false; + for (uint32_t connection_idx = 0; connection_idx < best_connection_.size(); connection_idx++) { + auto best_connection = best_connection_[connection_idx]; + // if this is the second pass we don't have to process previously found ones again + if (costing_->pass() > 0 && !(matrix.second_pass(connection_idx))) { + continue; + } + uint32_t target_idx = connection_idx % target_location_list.size(); + uint32_t source_idx = connection_idx / target_location_list.size(); // first recost and form the path, if desired (either time and/or geometry requested) - const auto shape = RecostFormPath(graphreader, connection, source_location_list[source_idx], + const auto shape = RecostFormPath(graphreader, best_connection, source_location_list[source_idx], target_location_list[target_idx], source_idx, target_idx, time_infos[source_idx], invariant, shape_format); - float time = connection.cost.secs; + float time = best_connection.cost.secs; if (time < kMaxCost) { auto dt_info = DateTime::offset_date(source_location_list[source_idx].date_time(), @@ -283,32 +288,22 @@ void CostMatrix::SourceToTarget(Api& request, .edgeid(), tile), time); - auto* pbf_date_time = matrix.mutable_date_times()->Add(); - *pbf_date_time = dt_info.date_time; - - auto* pbf_time_zone_offset = matrix.mutable_time_zone_offsets()->Add(); - *pbf_time_zone_offset = dt_info.time_zone_offset; - - auto* pbf_time_zone_name = matrix.mutable_time_zone_names()->Add(); - *pbf_time_zone_name = dt_info.time_zone_name; + *matrix.mutable_date_times(connection_idx) = dt_info.date_time; + *matrix.mutable_time_zone_offsets(connection_idx) = dt_info.time_zone_offset; + *matrix.mutable_time_zone_names(connection_idx) = dt_info.time_zone_name; } else { - // Add empty strings to make sure pbf arrays are populated (serializer - // requires this) - auto* pbf_date_time = matrix.mutable_date_times()->Add(); - *pbf_date_time = ""; - auto* pbf_time_zone_offset = matrix.mutable_time_zone_offsets()->Add(); - *pbf_time_zone_offset = ""; - auto* pbf_time_zone_name = matrix.mutable_time_zone_names()->Add(); - *pbf_time_zone_name = ""; + // let's try a second pass for this connection + matrix.mutable_second_pass()->Set(connection_idx, true); + connection_failed = true; } - matrix.mutable_from_indices()->Set(count, source_idx); - matrix.mutable_to_indices()->Set(count, target_idx); - matrix.mutable_distances()->Set(count, connection.distance); - matrix.mutable_times()->Set(count, time); - auto* pbf_shape = matrix.mutable_shapes()->Add(); - *pbf_shape = shape; - count++; + matrix.mutable_from_indices()->Set(connection_idx, source_idx); + matrix.mutable_to_indices()->Set(connection_idx, target_idx); + matrix.mutable_distances()->Set(connection_idx, best_connection.distance); + matrix.mutable_times()->Set(connection_idx, time); + *matrix.mutable_shapes(connection_idx) = shape; } + + return !connection_failed; } // Initialize all time distance to "not found". Any locations that @@ -316,7 +311,8 @@ void CostMatrix::SourceToTarget(Api& request, // remaining locations set. void CostMatrix::Initialize( const google::protobuf::RepeatedPtrField& source_locations, - const google::protobuf::RepeatedPtrField& target_locations) { + const google::protobuf::RepeatedPtrField& target_locations, + const valhalla::Matrix& matrix) { locs_count_[MATRIX_FORW] = source_locations.size(); locs_count_[MATRIX_REV] = target_locations.size(); @@ -354,10 +350,18 @@ void CostMatrix::Initialize( best_connection_.reserve(locs_count_[MATRIX_FORW] * locs_count_[MATRIX_REV]); for (uint32_t i = 0; i < locs_count_[MATRIX_FORW]; i++) { for (uint32_t j = 0; j < locs_count_[MATRIX_REV]; j++) { + const auto connection_idx = i * static_cast(target_locations.size()) + j; if (equals(source_locations.Get(i).ll(), target_locations.Get(j).ll())) { best_connection_.emplace_back(empty, empty, trivial_cost, 0.0f); best_connection_.back().found = true; + } else if (costing_->pass() > 0 && !matrix.second_pass(connection_idx)) { + // we've found this connection in a previous pass, we only need the time & distance + best_connection_.emplace_back(empty, empty, Cost{0.0f, matrix.times(connection_idx)}, + matrix.distances(connection_idx)); + best_connection_.back().found = true; } else { + // in a second pass this block makes sure that if e.g. A -> B is found, but B -> A isn't, + // we still expand both A & B to get the bidirectional benefit best_connection_.emplace_back(empty, empty, max_cost, static_cast(kMaxCost)); locs_status_[MATRIX_FORW][i].unfound_connections.insert(j); locs_status_[MATRIX_REV][j].unfound_connections.insert(i); @@ -367,15 +371,21 @@ void CostMatrix::Initialize( // Set the remaining number of sources and targets locs_remaining_[MATRIX_FORW] = 0; - for (const auto& s : locs_status_[MATRIX_FORW]) { + for (auto& s : locs_status_[MATRIX_FORW]) { if (!s.unfound_connections.empty()) { locs_remaining_[MATRIX_FORW]++; + } else { + // don't look at sources which don't have unfound connections, important for second pass + s.threshold = 0; } } locs_remaining_[MATRIX_REV] = 0; - for (const auto& t : locs_status_[MATRIX_REV]) { + for (auto& t : locs_status_[MATRIX_REV]) { if (!t.unfound_connections.empty()) { locs_remaining_[MATRIX_REV]++; + } else { + // don't look at targets which don't have unfound connections, important for second pass + t.threshold = 0; } } } @@ -499,12 +509,17 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, return false; } + // not_thru_pruning_ is only set to false on the 2nd pass in matrix_action. + // TODO(nils): one of these cases where I think reverse tree should look at the opposing edge, + // not the expanding one, same for quite some attributes below (and same in bidir a*) + bool thru = not_thru_pruning_ ? (pred.not_thru_pruning() || !meta.edge->not_thru()) : false; + // Add edge label, add to the adjacency list and set edge status uint32_t idx = edgelabels.size(); *meta.edge_status = {EdgeSet::kTemporary, idx}; if (FORWARD) { edgelabels.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, mode_, tc, - pred_dist, (pred.not_thru_pruning() || !meta.edge->not_thru()), + pred_dist, thru, (pred.closure_pruning() || !costing_->IsClosed(meta.edge, tile)), static_cast(flow_sources & kDefaultFlowMask), costing_->TurnType(pred.opp_local_idx(), nodeinfo, meta.edge), @@ -513,7 +528,7 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, (costing_->is_hgv() && meta.edge->destonly_hgv())); } else { edgelabels.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, mode_, tc, - pred_dist, (pred.not_thru_pruning() || !meta.edge->not_thru()), + pred_dist, thru, (pred.closure_pruning() || !costing_->IsClosed(meta.edge, tile)), static_cast(flow_sources & kDefaultFlowMask), costing_->TurnType(meta.edge->localedgeidx(), nodeinfo, opp_edge, @@ -535,7 +550,7 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, pred_dist, newcost.cost); } - return true; + return !(pred.not_thru_pruning() && meta.edge->not_thru()); } template diff --git a/src/thor/matrix_action.cc b/src/thor/matrix_action.cc index 7d39f6bb55..48838592a9 100644 --- a/src/thor/matrix_action.cc +++ b/src/thor/matrix_action.cc @@ -15,13 +15,29 @@ using namespace valhalla::sif; using namespace valhalla::thor; namespace { -constexpr uint32_t kCostMatrixThreshold = 5; +const std::string get_unfound_indices(const google::protobuf::RepeatedField& result) { + std::string indices; + for (int i = 0; i != result.size(); ++i) { + if (result[i]) { + indices += std::to_string(i) + ","; + } + } + indices.pop_back(); + + return indices; } +constexpr uint32_t kCostMatrixThreshold = 5; +} // namespace + namespace valhalla { namespace thor { -MatrixAlgorithm* thor_worker_t::get_matrix_algorithm(Api& request, const bool has_time) { +MatrixAlgorithm* +thor_worker_t::get_matrix_algorithm(Api& request, const bool has_time, const std::string& costing) { + if (costing == "bikeshare") { + return &time_distance_bss_matrix_; + } Matrix::Algorithm config_algo = Matrix::CostMatrix; switch (source_to_target_algorithm) { @@ -95,11 +111,39 @@ std::string thor_worker_t::matrix(Api& request) { alg->set_has_time(has_time); } - auto* algo = - costing == "bikeshare" ? &time_distance_bss_matrix_ : get_matrix_algorithm(request, has_time); + auto* algo = get_matrix_algorithm(request, has_time, costing); + LOG_INFO("matrix::" + std::string(algo->name())); + + // TODO(nils): TDMatrix doesn't care about either destonly or no_thru + if (algo->name() != "costmatrix") { + algo->SourceToTarget(request, *reader, mode_costing, mode, + max_matrix_distance.find(costing)->second); + return tyr::serializeMatrix(request); + } + + // for costmatrix try a second pass if the first didn't work out + valhalla::sif::cost_ptr_t cost = mode_costing[static_cast(mode)]; + cost->set_allow_destination_only(false); + cost->set_pass(0); + + if (!algo->SourceToTarget(request, *reader, mode_costing, mode, + max_matrix_distance.find(costing)->second) && + cost->AllowMultiPass() && costmatrix_allow_second_pass) { + // NOTE: we only look for unfound connections in a second pass; but + // if A -> B wasn't found and B -> A was, we still expand both for bidirectional efficiency + // TODO(nils): probably add filtered edges here too? + algo->Clear(); + cost->set_pass(1); + cost->RelaxHierarchyLimits(true); + cost->set_allow_destination_only(true); + cost->set_allow_conditional_destination(true); + algo->set_not_thru_pruning(false); + algo->SourceToTarget(request, *reader, mode_costing, mode, + max_matrix_distance.find(costing)->second); - algo->SourceToTarget(request, *reader, mode_costing, mode, - max_matrix_distance.find(costing)->second); + // add a warning that we needed to open destonly etc + add_warning(request, 400, get_unfound_indices(request.matrix().second_pass())); + }; return tyr::serializeMatrix(request); } diff --git a/src/thor/route_action.cc b/src/thor/route_action.cc index 8de8c66301..7362d28ffc 100644 --- a/src/thor/route_action.cc +++ b/src/thor/route_action.cc @@ -312,6 +312,7 @@ std::vector> thor_worker_t::get_path(PathAlgorithm* // Check if we should run a second pass pedestrian route with different A* // (to look for better routes where a ferry is taken) + // TODO(nils): how would a second pass find a better route, if it changes nothing ferry-related? bool ped_second_pass = false; if (!paths.empty() && (costing == "pedestrian" && path_algorithm->has_ferry())) { // DO NOT run a second pass on long routes due to performance issues diff --git a/src/thor/timedistancebssmatrix.cc b/src/thor/timedistancebssmatrix.cc index 26da685662..2c43c7382f 100644 --- a/src/thor/timedistancebssmatrix.cc +++ b/src/thor/timedistancebssmatrix.cc @@ -162,7 +162,7 @@ void TimeDistanceBSSMatrix::Expand(GraphReader& graphreader, // Calculate time and distance from one origin location to many destination // locations. template -void TimeDistanceBSSMatrix::ComputeMatrix(Api& request, +bool TimeDistanceBSSMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance) { uint32_t matrix_locations = request.options().matrix_locations(); @@ -252,13 +252,16 @@ void TimeDistanceBSSMatrix::ComputeMatrix(Api& request, } reset(); } + + // TODO(nils): not sure a second pass would make for BSS + return true; } -template void +template bool TimeDistanceBSSMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); -template void +template bool TimeDistanceBSSMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); diff --git a/src/thor/timedistancematrix.cc b/src/thor/timedistancematrix.cc index 1d2e99b481..a3762e8ae7 100644 --- a/src/thor/timedistancematrix.cc +++ b/src/thor/timedistancematrix.cc @@ -173,7 +173,7 @@ void TimeDistanceMatrix::Expand(GraphReader& graphreader, } template -void TimeDistanceMatrix::ComputeMatrix(Api& request, +bool TimeDistanceMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance) { bool invariant = request.options().date_time_type() == Options::invariant; @@ -188,13 +188,11 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, size_t num_elements = origins.size() * destinations.size(); auto time_infos = SetTime(origins, graphreader); - // thanks to protobuf not handling strings well, we have to collect those - std::vector out_tz_infos(num_elements); // Initialize destinations once for all origins InitDestinations(graphreader, destinations); // reserve the PBF vectors - reserve_pbf_arrays(*request.mutable_matrix(), num_elements); + reserve_pbf_arrays(*request.mutable_matrix(), num_elements, costing_->pass()); for (int origin_index = 0; origin_index < origins.size(); ++origin_index) { // reserve some space for the next dijkstras (will be cleared at the end of the loop) @@ -226,7 +224,7 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, if (predindex == kInvalidLabel) { // Can not expand any further... FormTimeDistanceMatrix(request, graphreader, FORWARD, origin_index, origin.date_time(), - time_info.timezone_index, dest_edge_ids, out_tz_infos); + time_info.timezone_index, dest_edge_ids); break; } @@ -255,7 +253,7 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, if (UpdateDestinations(origin, destinations, destedge->second, edge, tile, pred, time_info, matrix_locations)) { FormTimeDistanceMatrix(request, graphreader, FORWARD, origin_index, origin.date_time(), - time_info.timezone_index, dest_edge_ids, out_tz_infos); + time_info.timezone_index, dest_edge_ids); break; } } @@ -263,7 +261,7 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, // Terminate when we are beyond the cost threshold if (pred.cost().cost > current_cost_threshold_) { FormTimeDistanceMatrix(request, graphreader, FORWARD, origin_index, origin.date_time(), - time_info.timezone_index, dest_edge_ids, out_tz_infos); + time_info.timezone_index, dest_edge_ids); break; } @@ -280,24 +278,15 @@ void TimeDistanceMatrix::ComputeMatrix(Api& request, reset(); } - // amend the date_time strings - for (auto& date_time : out_tz_infos) { - auto* pbf_dt = request.mutable_matrix()->mutable_date_times()->Add(); - *pbf_dt = date_time.date_time; - - auto* pbf_tz_offset = request.mutable_matrix()->mutable_time_zone_offsets()->Add(); - *pbf_tz_offset = date_time.time_zone_offset; - - auto* pbf_tz_names = request.mutable_matrix()->mutable_time_zone_names()->Add(); - *pbf_tz_names = date_time.time_zone_name; - } + // TODO(nils): implement second pass here too + return true; } -template void +template bool TimeDistanceMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); -template void +template bool TimeDistanceMatrix::ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); @@ -562,28 +551,26 @@ void TimeDistanceMatrix::FormTimeDistanceMatrix(Api& request, const uint32_t origin_index, const std::string& origin_dt, const uint64_t& origin_tz, - std::unordered_map& edge_ids, - std::vector& out_tz_infos) { + std::unordered_map& edge_ids) { // when it's forward, origin_index will be the source_index // when it's reverse, origin_index will be the target_index valhalla::Matrix& matrix = *request.mutable_matrix(); graph_tile_ptr tile; for (uint32_t i = 0; i < destinations_.size(); i++) { auto& dest = destinations_[i]; - float time = dest.best_cost.secs + .5f; auto pbf_idx = forward ? (origin_index * request.options().targets().size()) + i : (i * request.options().targets().size()) + origin_index; matrix.mutable_from_indices()->Set(pbf_idx, forward ? origin_index : i); matrix.mutable_to_indices()->Set(pbf_idx, forward ? i : origin_index); matrix.mutable_distances()->Set(pbf_idx, dest.distance); - matrix.mutable_times()->Set(pbf_idx, time); + matrix.mutable_times()->Set(pbf_idx, dest.best_cost.secs); - // this logic doesn't work with string repeated fields, gotta collect them - // and process them later auto dt_info = DateTime::offset_date(origin_dt, origin_tz, reader.GetTimezoneFromEdge(edge_ids[i], tile), - static_cast(time)); - out_tz_infos[pbf_idx] = dt_info; + static_cast(dest.best_cost.secs)); + *matrix.mutable_date_times(pbf_idx) = dt_info.date_time; + *matrix.mutable_time_zone_names(pbf_idx) = dt_info.time_zone_name; + *matrix.mutable_time_zone_offsets(pbf_idx) = dt_info.time_zone_offset; } } diff --git a/src/thor/worker.cc b/src/thor/worker.cc index 851baa881c..afe1e8373e 100644 --- a/src/thor/worker.cc +++ b/src/thor/worker.cc @@ -101,6 +101,8 @@ thor_worker_t::thor_worker_t(const boost::property_tree::ptree& config, source_to_target_algorithm = SELECT_OPTIMAL; } + costmatrix_allow_second_pass = config.get("thor.costmatrix_allow_second_pass", false); + max_timedep_distance = config.get("service_limits.max_timedep_distance", kDefaultMaxTimeDependentDistance); diff --git a/src/tyr/matrix_serializer.cc b/src/tyr/matrix_serializer.cc index 91edc92350..14a7348c10 100644 --- a/src/tyr/matrix_serializer.cc +++ b/src/tyr/matrix_serializer.cc @@ -157,13 +157,16 @@ json::ArrayPtr serialize_row(const valhalla::Matrix& matrix, } if (matrix.shapes().size() && shape_format != no_shape) { - switch (shape_format) { - case geojson: - map->emplace("shape", - tyr::geojson_shape(decode>(matrix.shapes()[i]))); - break; - default: - map->emplace("shape", matrix.shapes()[i]); + // TODO(nils): tdmatrices don't have "shape" support yet + if (!matrix.shapes()[i].empty()) { + switch (shape_format) { + case geojson: + map->emplace("shape", + tyr::geojson_shape(decode>(matrix.shapes()[i]))); + break; + default: + map->emplace("shape", matrix.shapes()[i]); + } } } } else { diff --git a/src/worker.cc b/src/worker.cc index a3e1939b54..4c1ee7820c 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -160,6 +160,8 @@ const std::unordered_map warning_codes = { // 3xx is used when costing options were specified but we had to change them internally for some reason {300, R"(Many:Many CostMatrix was requested, but server only allows 1:Many TimeDistanceMatrix)"}, {301, R"(1:Many TimeDistanceMatrix was requested, but server only allows Many:Many CostMatrix)"}, + // 4xx is used when we do sneaky important things the user should be aware of + {400, R"(CostMatrix turned off destination-only on a second pass for connections: )"} }; // clang-format on @@ -1221,12 +1223,12 @@ valhalla_exception_t::valhalla_exception_t(unsigned code, const std::string& ext } // function to add warnings to proto info object -void add_warning(valhalla::Api& api, unsigned code) { - auto message = warning_codes.find(code); - if (message != warning_codes.end()) { - auto* warning = api.mutable_info()->mutable_warnings()->Add(); - warning->set_description(message->second); - warning->set_code(message->first); +void add_warning(valhalla::Api& api, unsigned code, const std::string& extra) { + auto warning = warning_codes.find(code); + if (warning != warning_codes.end()) { + auto* warning_pbf = api.mutable_info()->mutable_warnings()->Add(); + warning_pbf->set_description(warning->second + extra); + warning_pbf->set_code(warning->first); } } diff --git a/test/gurka/test_matrix.cc b/test/gurka/test_matrix.cc index 0b38912af4..786994bf37 100644 --- a/test/gurka/test_matrix.cc +++ b/test/gurka/test_matrix.cc @@ -48,7 +48,7 @@ void check_matrix(const rapidjson::Document& result, } } const std::string algo = result["algorithm"].GetString(); - const std::string exp_algo = MatrixAlgoToString(matrix_type); + const std::string& exp_algo = MatrixAlgoToString(matrix_type); EXPECT_EQ(algo, exp_algo); } } // namespace @@ -714,4 +714,63 @@ TEST_F(DateTimeTest, NoTimeZone) { EXPECT_FALSE(res_doc["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject().HasMember( "time_zone_name")); } -} \ No newline at end of file +} + +TEST(StandAlone, MatrixSecondPass) { + // from no-thru to no-thru should trigger a second pass + // JL has a forward destination-only, + // so K -> I also triggers second pass (see oneway at HK), but I -> K doesn't (no oneway) + const std::string ascii_map = R"( + A---B I---J + | | | | + | E---F---G---H | + | | ↓ | + C---D K---L + )"; + + gurka::ways ways; + for (const auto& node_pair : + {"AB", "BE", "AC", "CD", "DE", "EF", "FG", "GH", "HI", "IJ", "JL", "HK", "KL"}) { + ways[node_pair] = {{"highway", "residential"}}; + } + ways["JL"].emplace("motor_vehicle", "destination"); + ways["HK"].emplace("oneway", "true"); + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50); + const auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/matrix_second_pass", + {{"thor.costmatrix_allow_second_pass", "1"}}); + baldr::GraphReader graph_reader(map.config.get_child("mjolnir")); + + // Make sure the relevant edges are actually built as no-thru + auto FE_edge = std::get<1>(gurka::findEdgeByNodes(graph_reader, layout, "F", "E")); + auto GH_edge = std::get<1>(gurka::findEdgeByNodes(graph_reader, layout, "G", "H")); + auto JL_edge = std::get<1>(gurka::findEdgeByNodes(graph_reader, layout, "J", "L")); + EXPECT_TRUE(FE_edge->not_thru()); + EXPECT_TRUE(GH_edge->not_thru()); + EXPECT_TRUE(JL_edge->destonly()); + + // Simple single route from no-thru to no-thru + { + auto api = gurka::do_action(valhalla::Options::sources_to_targets, map, {"A"}, {"J"}, "auto"); + EXPECT_GT(api.matrix().times(0), 0.f); + EXPECT_TRUE(api.matrix().second_pass(0)); + EXPECT_TRUE(api.info().warnings(0).description().find('0') != std::string::npos); + } + + // I -> K (idx 1) should pass on the first try + // K -> I (idx 2) should need a second pass + { + auto api = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"I", "K"}, {"I", "K"}, "auto"); + EXPECT_GT(api.matrix().times(1), 0.f); + EXPECT_FALSE(api.matrix().second_pass(1)); + EXPECT_GT(api.matrix().times(2), 0.f); + EXPECT_TRUE(api.matrix().second_pass(2)); + EXPECT_GT(api.matrix().times(2), api.matrix().times(1)); + + // I -> I & K -> K shouldn't be processed a second time either + EXPECT_FALSE(api.matrix().second_pass(0)); + EXPECT_FALSE(api.matrix().second_pass(3)); + EXPECT_TRUE(api.info().warnings(0).description().find('2') != std::string::npos); + } +} diff --git a/valhalla/proto_conversions.h b/valhalla/proto_conversions.h index 46a014f5fd..ab68e0b523 100644 --- a/valhalla/proto_conversions.h +++ b/valhalla/proto_conversions.h @@ -517,7 +517,7 @@ inline TripLeg_Use GetTripLegUse(const baldr::Use use) { } // matrix algo to string -std::string MatrixAlgoToString(const valhalla::Matrix::Algorithm algo); +const std::string& MatrixAlgoToString(const valhalla::Matrix::Algorithm algo); // Get the string representing the incident-type std::string incidentTypeToString(const valhalla::IncidentsTile::Metadata::Type& incident_type); // Get the string representing the incident-Impact diff --git a/valhalla/thor/costmatrix.h b/valhalla/thor/costmatrix.h index 90340bad6e..40697f4c59 100644 --- a/valhalla/thor/costmatrix.h +++ b/valhalla/thor/costmatrix.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -98,7 +99,7 @@ class CostMatrix : public MatrixAlgorithm { * @param mode Graph reader for accessing routing graph. * @param max_matrix_distance Maximum arc-length distance for current mode. */ - void SourceToTarget(Api& request, + bool SourceToTarget(Api& request, baldr::GraphReader& graphreader, const sif::mode_costing_t& mode_costing, const sif::travel_mode_t mode, @@ -110,6 +111,14 @@ class CostMatrix : public MatrixAlgorithm { */ void Clear() override; + /** + * Get the algorithm's name + * @return the name of the algorithm + */ + inline const std::string& name() override { + return MatrixAlgoToString(Matrix::CostMatrix); + } + protected: uint32_t max_reserved_labels_count_; uint32_t max_reserved_locations_count_; @@ -164,7 +173,8 @@ class CostMatrix : public MatrixAlgorithm { * @param target_location_list List of target/destination locations. */ void Initialize(const google::protobuf::RepeatedPtrField& source_location_list, - const google::protobuf::RepeatedPtrField& target_location_list); + const google::protobuf::RepeatedPtrField& target_location_list, + const valhalla::Matrix& matrix); /** * Iterate the forward search from the source/origin location. diff --git a/valhalla/thor/matrixalgorithm.h b/valhalla/thor/matrixalgorithm.h index a006b3c73b..a697dcb7cf 100644 --- a/valhalla/thor/matrixalgorithm.h +++ b/valhalla/thor/matrixalgorithm.h @@ -28,7 +28,7 @@ class MatrixAlgorithm { * Constructor */ MatrixAlgorithm(const boost::property_tree::ptree& config) - : interrupt_(nullptr), has_time_(false), expansion_callback_(), + : interrupt_(nullptr), has_time_(false), not_thru_pruning_(true), expansion_callback_(), clear_reserved_memory_(config.get("clear_reserved_memory", false)) { } @@ -52,8 +52,9 @@ class MatrixAlgorithm { * @param has_time whether time-dependence was requested * @param invariant whether invariant time-dependence was requested * @param shape_format which shape_format, if any + * @returns Whether there were unfound connections */ - virtual void SourceToTarget(Api& request, + virtual bool SourceToTarget(Api& request, baldr::GraphReader& graphreader, const sif::mode_costing_t& mode_costing, const sif::travel_mode_t mode, @@ -64,6 +65,12 @@ class MatrixAlgorithm { */ virtual void Clear() = 0; + /** + * Get the algorithm's name + * @return the name of the algorithm + */ + virtual const std::string& name() = 0; + /** * Returns the name of the algorithm * @return the name of the algorithm @@ -72,6 +79,32 @@ class MatrixAlgorithm { has_time_ = has_time; }; + /** + * + * There is a rare case where we may encounter only_restrictions with edges being + * marked as not_thru. Basically the only way to get in this area is via one edge + * and all other edges are restricted, but this one edge is also marked as not_thru. + * Therefore, on the first pass the expansion stops as we cannot take the restricted + * turns and we cannot go into the not_thru region. On the 2nd pass, we now ignore + * not_thru flags and allow entry into the not_thru region due to the fact that + * not_thru_pruning_ is false. See the gurka test not_thru_pruning_. + * + * Set the not_thru_pruning_ + * @param pruning set the not_thru_pruning_ to pruning value. + * only set on the second pass + */ + void set_not_thru_pruning(const bool pruning) { + not_thru_pruning_ = pruning; + } + + /** + * Get the not thru pruning + * @return Returns not_thru_pruning_ + */ + bool not_thru_pruning() { + return not_thru_pruning_; + } + /** * Set a callback that will throw when the path computation should be aborted * @param interrupt_callback the function to periodically call to see if @@ -105,6 +138,9 @@ class MatrixAlgorithm { // whether time was specified bool has_time_; + // Indicates whether to allow access into a not-thru region. + bool not_thru_pruning_; + // for tracking the expansion of the algorithm visually expansion_callback_t expansion_callback_; @@ -113,16 +149,30 @@ class MatrixAlgorithm { // if `true` clean reserved memory for edge labels bool clear_reserved_memory_; - // resizes all PBF sequences except for repeated string where it reserves instead - inline static void reserve_pbf_arrays(valhalla::Matrix& matrix, size_t size) { - matrix.mutable_from_indices()->Resize(size, 0U); - matrix.mutable_to_indices()->Resize(size, 0U); - matrix.mutable_distances()->Resize(size, 0U); - matrix.mutable_times()->Resize(size, 0U); - matrix.mutable_date_times()->Reserve(size); - matrix.mutable_shapes()->Reserve(size); - matrix.mutable_time_zone_offsets()->Reserve(size); - matrix.mutable_time_zone_names()->Reserve(size); + // on first pass, resizes all PBF sequences and defaults to 0 or "" + inline static void reserve_pbf_arrays(valhalla::Matrix& matrix, size_t size, uint32_t pass = 0) { + if (pass == 0) { + matrix.mutable_from_indices()->Resize(size, 0U); + matrix.mutable_to_indices()->Resize(size, 0U); + matrix.mutable_distances()->Resize(size, 0U); + matrix.mutable_times()->Resize(size, 0U); + matrix.mutable_second_pass()->Resize(size, false); + // repeated strings don't support Resize() + matrix.mutable_date_times()->Reserve(size); + matrix.mutable_time_zone_offsets()->Reserve(size); + matrix.mutable_time_zone_names()->Reserve(size); + matrix.mutable_shapes()->Reserve(size); + for (size_t i = 0; i < size; i++) { + auto* date_time = matrix.mutable_date_times()->Add(); + *date_time = ""; + auto* time_zone_offset = matrix.mutable_time_zone_offsets()->Add(); + *time_zone_offset = ""; + auto* time_zone_name = matrix.mutable_time_zone_names()->Add(); + *time_zone_name = ""; + auto* shape = matrix.mutable_shapes()->Add(); + *shape = ""; + } + } } }; diff --git a/valhalla/thor/timedistancebssmatrix.h b/valhalla/thor/timedistancebssmatrix.h index de83b37b09..5287d6deb5 100644 --- a/valhalla/thor/timedistancebssmatrix.h +++ b/valhalla/thor/timedistancebssmatrix.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -41,13 +42,12 @@ class TimeDistanceBSSMatrix : public MatrixAlgorithm { * @param max_matrix_distance Maximum arc-length distance for current mode. * @return time/distance from origin index to all other locations */ - inline void SourceToTarget(Api& request, + inline bool SourceToTarget(Api& request, baldr::GraphReader& graphreader, const sif::mode_costing_t& mode_costing, const sif::travel_mode_t /*mode*/, const float max_matrix_distance) override { - LOG_INFO("matrix::TimeDistanceBSSMatrix"); request.mutable_matrix()->set_algorithm(Matrix::TimeDistanceMatrix); if (request.options().shape_format() != no_shape) @@ -81,6 +81,14 @@ class TimeDistanceBSSMatrix : public MatrixAlgorithm { dest_edges_.clear(); }; + /** + * Get the algorithm's name + * @return the name of the algorithm + */ + inline const std::string& name() override { + return MatrixAlgoToString(Matrix::TimeDistanceBSSMatrix); + } + protected: // Number of destinations that have been found and settled (least cost path // computed). @@ -146,7 +154,7 @@ class TimeDistanceBSSMatrix : public MatrixAlgorithm { */ template - void ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); + bool ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); /** * Expand from the node along the forward search path. Immediately expands diff --git a/valhalla/thor/timedistancematrix.h b/valhalla/thor/timedistancematrix.h index c10ec98676..254336c7f5 100644 --- a/valhalla/thor/timedistancematrix.h +++ b/valhalla/thor/timedistancematrix.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -39,13 +40,12 @@ class TimeDistanceMatrix : public MatrixAlgorithm { * * @return time/distance from all sources to all targets */ - inline void SourceToTarget(Api& request, + inline bool SourceToTarget(Api& request, baldr::GraphReader& graphreader, const sif::mode_costing_t& mode_costing, const sif::travel_mode_t mode, const float max_matrix_distance) override { - LOG_INFO("matrix::TimeDistanceMatrix"); request.mutable_matrix()->set_algorithm(Matrix::TimeDistanceMatrix); if (request.options().shape_format() != no_shape) @@ -79,6 +79,14 @@ class TimeDistanceMatrix : public MatrixAlgorithm { dest_edges_.clear(); }; + /** + * Get the algorithm's name + * @return the name of the algorithm + */ + inline const std::string& name() override { + return MatrixAlgoToString(Matrix::TimeDistanceMatrix); + } + protected: // Number of destinations that have been found and settled (least cost path // computed). @@ -139,7 +147,7 @@ class TimeDistanceMatrix : public MatrixAlgorithm { */ template - void ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); + bool ComputeMatrix(Api& request, baldr::GraphReader& graphreader, const float max_matrix_distance); /** * Expand from the node along the forward search path. Immediately expands @@ -263,8 +271,7 @@ class TimeDistanceMatrix : public MatrixAlgorithm { const uint32_t origin_index, const std::string& origin_dt, const uint64_t& origin_tz, - std::unordered_map& edge_ids, - std::vector& out_tz_infos); + std::unordered_map& edge_ids); }; } // namespace thor diff --git a/valhalla/thor/worker.h b/valhalla/thor/worker.h index 0548629afa..7334cdcac2 100644 --- a/valhalla/thor/worker.h +++ b/valhalla/thor/worker.h @@ -77,7 +77,8 @@ class thor_worker_t : public service_worker_t { const Location& origin, const Location& destination, const Options& options); - thor::MatrixAlgorithm* get_matrix_algorithm(Api& request, const bool has_time); + thor::MatrixAlgorithm* + get_matrix_algorithm(Api& request, const bool has_time, const std::string& costing); void route_match(Api& request); /** * Returns the results of the map match where the first float is the normalized @@ -129,6 +130,7 @@ class thor_worker_t : public service_worker_t { float max_timedep_distance; std::unordered_map max_matrix_distance; SOURCE_TO_TARGET_ALGORITHM source_to_target_algorithm; + bool costmatrix_allow_second_pass; std::shared_ptr reader; meili::MapMatcherFactory matcher_factory; baldr::AttributesController controller; diff --git a/valhalla/worker.h b/valhalla/worker.h index b21e32ea41..6f131d039c 100644 --- a/valhalla/worker.h +++ b/valhalla/worker.h @@ -75,8 +75,14 @@ void ParseApi(const prime_server::http_request_t& http_request, Api& api); std::string serialize_error(const valhalla_exception_t& exception, Api& options); -// function to add warnings to proto info object -void add_warning(valhalla::Api& api, unsigned code); +/** + * Adds a warning to the request PBF object. + * + * @param api the full request + * @param code the warning code + * @param extra an optional string to append to the hard-coded warning message + */ +void add_warning(valhalla::Api& api, unsigned code, const std::string& extra = ""); #ifdef ENABLE_SERVICES prime_server::worker_t::result_t serialize_error(const valhalla_exception_t& exception, From db0b6a1e794e2a68227f05ab47f6443fdaa69a17 Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Sun, 4 Feb 2024 14:10:16 -0600 Subject: [PATCH 017/198] Remove warnings from #4493 (#4555) --- test/gurka/CMakeLists.txt | 2 -- test/gurka/test_ferry_connections.cc | 3 ++- test/gurka/test_shortcut.cc | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/test/gurka/CMakeLists.txt b/test/gurka/CMakeLists.txt index f896e0e189..1c6bd54e49 100644 --- a/test/gurka/CMakeLists.txt +++ b/test/gurka/CMakeLists.txt @@ -15,7 +15,6 @@ if(ENABLE_DATA_TOOLS) test_admin_sidewalk_crossing_override.cc test_admin_uk_override.cc test_elevation.cc - test_ferry_connections.cc test_gtfs.cc test_guidance_views.cc test_guidance_views_signposts.cc @@ -33,7 +32,6 @@ if(ENABLE_DATA_TOOLS) test_precision.cc test_recost.cc test_simple_restrictions.cc - test_shortcut.cc test_time_tracking.cc test_traffic.cc test_traffic_smoothing.cc diff --git a/test/gurka/test_ferry_connections.cc b/test/gurka/test_ferry_connections.cc index 1c99312d40..a804cd81b7 100644 --- a/test/gurka/test_ferry_connections.cc +++ b/test/gurka/test_ferry_connections.cc @@ -297,7 +297,8 @@ TEST(Standalone, ReclassifyFerryUntagDestOnly) { EXPECT_TRUE(std::get<1>(upclassed)->classification() == valhalla::baldr::RoadClass::kPrimary); // we expect to take the shorter route on the left and the detour on the right - for (const std::string& mode : {"auto", "motorcycle", "taxi", "bus", "hov", "truck"}) { + std::vector modes = {"auto", "motorcycle", "taxi", "bus", "hov", "truck"}; + for (const auto& mode : modes) { auto res = gurka::do_action(valhalla::Options::route, map, {"A", "M"}, mode); gurka::assert::raw::expect_path(res, {"AB", "BC", "CD", "DE", "EF", "FKLG", "GH", "HM"}, mode + " failed."); diff --git a/test/gurka/test_shortcut.cc b/test/gurka/test_shortcut.cc index e32ae18859..ad47fd1d70 100644 --- a/test/gurka/test_shortcut.cc +++ b/test/gurka/test_shortcut.cc @@ -168,7 +168,7 @@ TEST(Shortcuts, ShortcutSpeed) { ASSERT_EQ(shortcut_infos.size(), 2); - for (auto const shortcut_info : shortcut_infos) { + for (auto const& shortcut_info : shortcut_infos) { auto const shortcutid = std::get<0>(shortcut_info); auto const shortcut_speed = std::get<1>(shortcut_info); auto const shortcut_truck_speed = std::get<2>(shortcut_info); From 453d3ef433d4cbd496004c0eb65bb0a4a1c85828 Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Sun, 4 Feb 2024 16:12:25 -0600 Subject: [PATCH 018/198] Remove warnings from pr4536 (#4556) --- src/thor/CMakeLists.txt | 10 +++++----- src/thor/route_action.cc | 7 ++++--- src/thor/timedistancebssmatrix.cc | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/thor/CMakeLists.txt b/src/thor/CMakeLists.txt index cb94882952..8492764663 100644 --- a/src/thor/CMakeLists.txt +++ b/src/thor/CMakeLists.txt @@ -2,16 +2,18 @@ file(GLOB headers ${VALHALLA_SOURCE_DIR}/valhalla/thor/*.h) set(sources astar_bss.cc - alternates.cc + alternates.cc bidirectional_astar.cc costmatrix.cc dijkstras.cc matrix_action.cc multimodal.cc + route_action.cc + timedistancebssmatrix.cc timedistancematrix.cc - triplegbuilder.cc + triplegbuilder.cc unidirectional_astar.cc - worker.cc) + worker.cc) set(sources_with_warnings centroid.cc @@ -21,10 +23,8 @@ set(sources_with_warnings map_matcher.cc optimized_route_action.cc optimizer.cc - route_action.cc route_matcher.cc status_action.cc - timedistancebssmatrix.cc trace_attributes_action.cc trace_route_action.cc triplegbuilder_utils.h) diff --git a/src/thor/route_action.cc b/src/thor/route_action.cc index 7362d28ffc..0ed8fc0095 100644 --- a/src/thor/route_action.cc +++ b/src/thor/route_action.cc @@ -108,8 +108,8 @@ inline bool is_break_point(const valhalla::Location& l) { } inline bool is_highly_reachable(const valhalla::Location& loc, const valhalla::PathEdge& edge) { - return edge.inbound_reach() >= loc.minimum_reachability() && - edge.outbound_reach() >= loc.minimum_reachability(); + return static_cast(edge.inbound_reach()) >= loc.minimum_reachability() && + static_cast(edge.outbound_reach()) >= loc.minimum_reachability(); } template inline void remove_path_edges(valhalla::Location& loc, Predicate pred) { @@ -388,7 +388,8 @@ void thor_worker_t::path_arrive_by(Api& api, const std::string& costing) { // we add the timezone info if destination is the last location // and add waiting_secs again from the final destination's datetime, so we output the departing // time at intermediate locations, not the arrival time - if ((destination->correlation().original_index() == (options.locations().size() - 1) && + if ((destination->correlation().original_index() == + static_cast((options.locations().size() - 1)) && (in_tz || out_tz))) { auto destination_dt = DateTime::offset_date(destination->date_time(), out_tz, out_tz, destination->waiting_secs()); diff --git a/src/thor/timedistancebssmatrix.cc b/src/thor/timedistancebssmatrix.cc index 2c43c7382f..cb993af199 100644 --- a/src/thor/timedistancebssmatrix.cc +++ b/src/thor/timedistancebssmatrix.cc @@ -182,7 +182,7 @@ bool TimeDistanceBSSMatrix::ComputeMatrix(Api& request, // reserve the PBF vectors reserve_pbf_arrays(*request.mutable_matrix(), origins.size() * destinations.size()); - for (size_t origin_index = 0; origin_index < origins.size(); ++origin_index) { + for (int origin_index = 0; origin_index < origins.size(); ++origin_index) { edgelabels_.reserve(max_reserved_labels_count_); const auto& origin = origins.Get(origin_index); From 82d8215db2b14f680e90f5e67bc1962b591be836 Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Mon, 5 Feb 2024 08:18:04 -0600 Subject: [PATCH 019/198] Nn matrix geometry try2 (#4489) Co-authored-by: Nils --- src/tyr/CMakeLists.txt | 3 +- src/tyr/route_serializer.cc | 2 +- src/tyr/route_serializer_osrm.cc | 65 +++--------- src/tyr/route_serializer_osrm.h | 44 ++++++++ src/tyr/route_summary_cache.cc | 154 ++++++++++++++++++++++++++++ src/tyr/route_summary_cache.h | 147 ++------------------------ valhalla/tyr/serializer_constants.h | 4 + 7 files changed, 232 insertions(+), 187 deletions(-) create mode 100644 src/tyr/route_serializer_osrm.h create mode 100644 src/tyr/route_summary_cache.cc diff --git a/src/tyr/CMakeLists.txt b/src/tyr/CMakeLists.txt index 5a0624785f..3a277f3de3 100644 --- a/src/tyr/CMakeLists.txt +++ b/src/tyr/CMakeLists.txt @@ -5,13 +5,14 @@ set(sources height_serializer.cc isochrone_serializer.cc matrix_serializer.cc + route_serializer_osrm.cc + route_summary_cache.cc serializers.cc transit_available_serializer.cc) set(sources_with_warnings locate_serializer.cc route_serializer.cc - route_serializer_osrm.cc route_serializer_valhalla.cc trace_serializer.cc) diff --git a/src/tyr/route_serializer.cc b/src/tyr/route_serializer.cc index 76df5efe63..370e20be22 100644 --- a/src/tyr/route_serializer.cc +++ b/src/tyr/route_serializer.cc @@ -5,7 +5,7 @@ #include "midgard/encoded.h" #include "midgard/util.h" -#include "route_serializer_osrm.cc" +#include "route_serializer_osrm.h" #include "route_serializer_valhalla.cc" #include "tyr/serializers.h" diff --git a/src/tyr/route_serializer_osrm.cc b/src/tyr/route_serializer_osrm.cc index c2d2c5946f..2ad579d061 100644 --- a/src/tyr/route_serializer_osrm.cc +++ b/src/tyr/route_serializer_osrm.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -11,6 +12,7 @@ #include "midgard/util.h" #include "odin/enhancedtrippath.h" #include "odin/util.h" +#include "route_serializer_osrm.h" #include "route_summary_cache.h" #include "tyr/serializer_constants.h" #include "tyr/serializers.h" @@ -169,34 +171,6 @@ std::unordered_map> speed_limit {"WS", {kSpeedLimitSignVienna, kSpeedLimitUnitsMph}}, }; -namespace osrm_serializers { -/* -OSRM output is described in: http://project-osrm.org/docs/v5.5.1/api/ -{ - "code":"Ok" - "waypoints": [{ }, { }...], - "routes": [ - { - "geometry":"....." - "distance":xxx.y - "duration":yyy.z - "legs":[ - { - "steps":[ - "intersections":[ - ] - "geometry":" " - "maneuver":{ - } - ] - } - ] - }, - ... - ] -} -*/ - std::string destinations(const valhalla::TripSign& sign); // Add OSRM route summary information: distance, duration @@ -382,11 +356,8 @@ json::MapPtr serialize_annotations(const valhalla::TripLeg& trip_leg) { // the optimized sequence. json::ArrayPtr waypoints(google::protobuf::RepeatedPtrField& locs) { // Create a vector of indexes. - uint32_t i = 0; - std::vector indexes; - for (const auto& loc : locs) { - indexes.push_back(i++); - } + std::vector indexes(locs.size()); + std::iota(indexes.begin(), indexes.end(), 0); // Sort the the vector by the location's original index std::sort(indexes.begin(), indexes.end(), [&locs](const uint32_t a, const uint32_t b) -> bool { @@ -475,7 +446,6 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, count = 0; auto intersections = json::array({}); uint32_t n = arrive_maneuver ? maneuver.end_path_index() + 1 : maneuver.end_path_index(); - EnhancedTripLeg_Node* prev_node = nullptr; for (uint32_t i = maneuver.begin_path_index(); i < n; i++) { auto intersection = json::map({}); @@ -533,7 +503,7 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, // Add rest_stop when passing by a rest_area or service_area if (i > 0 && !arrive_maneuver) { auto rest_stop = json::map({}); - for (uint32_t m = 0; m < node->intersecting_edge_size(); m++) { + for (int m = 0; m < node->intersecting_edge_size(); m++) { auto intersecting_edge = node->GetIntersectingEdge(m); bool routeable = intersecting_edge->IsTraversableOutbound(curr_edge->travel_mode()); @@ -574,7 +544,7 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, if (!arrive_maneuver) { edges.emplace_back(curr_edge->begin_heading(), true, false, true); if (i > 0) { - for (uint32_t m = 0; m < node->intersecting_edge_size(); m++) { + for (int m = 0; m < node->intersecting_edge_size(); m++) { auto intersecting_edge = node->GetIntersectingEdge(m); bool routable = intersecting_edge->IsTraversableOutbound(curr_edge->travel_mode()); edges.emplace_back(intersecting_edge->begin_heading(), routable, false, false); @@ -623,9 +593,9 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, // Add tunnel_name for tunnels if (!arrive_maneuver) { if (curr_edge->tunnel() && !curr_edge->tagged_value().empty()) { - for (uint32_t t = 0; t < curr_edge->tagged_value().size(); ++t) { - if (curr_edge->tagged_value().Get(t).type() == TaggedValue_Type_kTunnel) { - intersection->emplace("tunnel_name", curr_edge->tagged_value().Get(t).value()); + for (const auto& e : curr_edge->tagged_value()) { + if (e.type() == TaggedValue_Type_kTunnel) { + intersection->emplace("tunnel_name", e.value()); } } } @@ -1570,7 +1540,7 @@ json::ArrayPtr serialize_legs(const google::protobuf::RepeatedPtrFieldemplace_back(std::move(step)); } // end maneuver loop - // ######################################################################### + // ######################################################################### // Add distance, duration, weight, and summary // Get a summary based on longest maneuvers. @@ -1856,7 +1826,7 @@ summarize_route_legs(const google::protobuf::RepeatedPtrField& // Find the simplest summary for every leg of every route. Important note: // each route should have the same number of legs. Hence, we only need to make // unique the same leg (leg_idx) between all routes. - for (size_t route_i = 0; route_i < routes.size(); route_i++) { + for (int route_i = 0; route_i < routes.size(); route_i++) { size_t num_legs_i = routes.Get(route_i).legs_size(); std::vector leg_summaries; @@ -1872,7 +1842,7 @@ summarize_route_legs(const google::protobuf::RepeatedPtrField& // Compare every jth route/leg summary vs the current ith route/leg summary. // We desire to compute num_named_segs_needed, which is the number of named // segments needed to uniquely identify the ith's summary. - for (size_t route_j = 0; route_j < routes.size(); route_j++) { + for (int route_j = 0; route_j < routes.size(); route_j++) { // avoid self if (route_i == route_j) @@ -1917,6 +1887,9 @@ summarize_route_legs(const google::protobuf::RepeatedPtrField& return all_summaries; } +} // namespace + +namespace osrm_serializers { // Serialize route response in OSRM compatible format. // Inputs are: // directions options @@ -2005,6 +1978,7 @@ std::string serialize(valhalla::Api& api) { using namespace osrm_serializers; +namespace { /// Assert equality of two json documents // // TODO Improve the diffed view of mismatching documents @@ -2304,9 +2278,4 @@ int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } - -#else - -} // namespace - #endif diff --git a/src/tyr/route_serializer_osrm.h b/src/tyr/route_serializer_osrm.h new file mode 100644 index 0000000000..6187e38861 --- /dev/null +++ b/src/tyr/route_serializer_osrm.h @@ -0,0 +1,44 @@ +#ifndef VALHALLA_TYR_ROUTE_SERIALIZER_OSRM_ +#define VALHALLA_TYR_ROUTE_SERIALIZER_OSRM_ +#pragma once + +#include "proto_conversions.h" + +namespace osrm_serializers { +/* +OSRM output is described in: http://project-osrm.org/docs/v5.5.1/api/ +{ + "code":"Ok" + "waypoints": [{ }, { }...], + "routes": [ + { + "geometry":"....." + "distance":xxx.y + "duration":yyy.z + "legs":[ + { + "steps":[ + "intersections":[ + ] + "geometry":" " + "maneuver":{ + } + ] + } + ] + }, + ... + ] +} +*/ + +// Serialize route response in OSRM compatible format. +// Inputs are: +// directions options +// TripLeg protocol buffer +// DirectionsLeg protocol buffer +std::string serialize(valhalla::Api& api); + +} // namespace osrm_serializers + +#endif // VALHALLA_TYR_ROUTE_SERIALIZER_OSRM_ diff --git a/src/tyr/route_summary_cache.cc b/src/tyr/route_summary_cache.cc new file mode 100644 index 0000000000..1e0a4c03f0 --- /dev/null +++ b/src/tyr/route_summary_cache.cc @@ -0,0 +1,154 @@ +#include "proto/directions.pb.h" +#include "proto/options.pb.h" +#include "proto/trip.pb.h" +#include "proto_conversions.h" + +#include "./route_summary_cache.h" + +namespace valhalla { +namespace tyr { + +NamedSegment& NamedSegment::operator=(const NamedSegment& ns) { + name = ns.name; + index = ns.index; + distance = ns.distance; + return *this; +} + +NamedSegment& NamedSegment::operator=(NamedSegment&& ns) noexcept { + name = std::move(ns.name); + index = ns.index; + distance = ns.distance; + return *this; +} + +route_summary_cache::route_summary_cache( + const google::protobuf::RepeatedPtrField& routes) { + // A route/leg/maneuver may go on/off the same named road many times. + // Combine the distances for same named roads - store in a NamedSegment. + route_leg_segs_by_dist.reserve(routes.size()); + for (const auto& route : routes) { + std::vector> leg_segs_by_dist; + leg_segs_by_dist.reserve(route.legs_size()); + for (int j = 0; j < route.legs_size(); j++) { + const DirectionsLeg& leg = route.legs(j); + std::unordered_map> maneuver_summary_map; + maneuver_summary_map.reserve(leg.maneuver_size()); + uint32_t maneuver_index = 0; + for (const auto& maneuver : leg.maneuver()) { + if (maneuver.street_name_size() > 0) { + const std::string& name = maneuver.street_name(0).value(); + auto maneuver_summary = maneuver_summary_map.find(name); + if (maneuver_summary == maneuver_summary_map.end()) { + maneuver_summary_map[name] = std::make_pair(maneuver_index, maneuver.length()); + } else { + maneuver_summary->second.second += maneuver.length(); + } + } + ++maneuver_index; + } + + // Create a list of named segments (maneuver name, maneuver index, distance) + // sorted by distance. + std::vector segs_by_dist; + segs_by_dist.reserve(maneuver_summary_map.size()); + for (const auto& map_item : maneuver_summary_map) { + segs_by_dist.emplace_back(map_item.first, map_item.second.first, map_item.second.second); + } + + // Sort list by descending maneuver distance + std::sort(segs_by_dist.begin(), segs_by_dist.end(), + [](const NamedSegment& a, const NamedSegment& b) { return b.distance < a.distance; }); + + leg_segs_by_dist.emplace_back(std::move(segs_by_dist)); + } + + route_leg_segs_by_dist.emplace_back(std::move(leg_segs_by_dist)); + } + + // fill the cache with empty results so we can safely index it with the same + // dimensionality as the route_leg_segs_by_dist object. + cache.reserve(route_leg_segs_by_dist.size()); + for (size_t i = 0; i < route_leg_segs_by_dist.size(); i++) { + cache.emplace_back(); + cache[i].reserve(route_leg_segs_by_dist[i].size()); + for (size_t j = 0; j < route_leg_segs_by_dist[i].size(); j++) { + cache[i].emplace_back(); + cache[i][j].reserve(route_leg_segs_by_dist[i][j].size()); + for (size_t k = 0; k < route_leg_segs_by_dist[i][j].size(); k++) { + cache[i][j].emplace_back(); + } + } + } +} + +size_t route_summary_cache::num_named_segments_for_route_leg(size_t route_idx, size_t leg_idx) { + if (route_idx >= route_leg_segs_by_dist.size()) { + return 0; + } + + if (leg_idx >= route_leg_segs_by_dist[route_idx].size()) { + return 0; + } + + return route_leg_segs_by_dist[route_idx][leg_idx].size(); +} + +// Return the n-part named-segment summary for the given route/leg. +// The summary returned is guaranteed to be comprised of as few named +// segments as possible, while also being unique among all route/same-leg +// summaries. +std::string +route_summary_cache::get_n_segment_summary(size_t route_idx, size_t leg_idx, size_t num_named_segs) { + if (route_idx >= cache.size()) { + return ""; + } + + if (leg_idx >= cache[route_idx].size()) { + return ""; + } + + if (num_named_segs == 0) { + return ""; + } + + // num_named_segs is the number of named segments you'd like the summary to + // be comprised of. The smallest requestable value is 1 - so we store all + // summaries offset by n = num_named_segs - 1. + size_t n = num_named_segs - 1; + if (n >= cache[route_idx][leg_idx].size()) { + return ""; + } + + // empty summary means cache miss + if (cache[route_idx][leg_idx][n].empty()) { + // go figure out the summary + std::vector segs_by_maneuver_index; + segs_by_maneuver_index.reserve(num_named_segs); + for (size_t i = 0; i < num_named_segs; i++) { + segs_by_maneuver_index.emplace_back(&route_leg_segs_by_dist[route_idx][leg_idx][i]); + } + + // sort by maneuver index + std::sort(segs_by_maneuver_index.begin(), segs_by_maneuver_index.end(), + [](const NamedSegment* a, const NamedSegment* b) { return a->index < b->index; }); + + std::string summary; + for (size_t i = 0; i < num_named_segs; i++) { + summary += segs_by_maneuver_index[i]->name; + if (i != num_named_segs - 1) + summary += ", "; + } + + misses++; + + cache[route_idx][leg_idx][n] = std::move(summary); + } else { + hits++; + } + + return cache[route_idx][leg_idx][n]; +} + +} // namespace tyr +} // namespace valhalla diff --git a/src/tyr/route_summary_cache.h b/src/tyr/route_summary_cache.h index 7ae7d85cd9..c3bd42b4a7 100644 --- a/src/tyr/route_summary_cache.h +++ b/src/tyr/route_summary_cache.h @@ -1,3 +1,5 @@ +#ifndef VALHALLA_TYR_ROUTE_SUMMARY_CACHE_H_ +#define VALHALLA_TYR_ROUTE_SUMMARY_CACHE_H_ #pragma once #include "proto/directions.pb.h" @@ -22,19 +24,9 @@ struct NamedSegment { NamedSegment(NamedSegment&& ns) : name(std::move(ns.name)), index(ns.index), distance(ns.distance) { } - NamedSegment& operator=(const NamedSegment& ns) { - name = ns.name; - index = ns.index; - distance = ns.distance; - return *this; - } + NamedSegment& operator=(const NamedSegment& ns); - NamedSegment& operator=(NamedSegment&& ns) { - name = std::move(ns.name); - index = ns.index; - distance = ns.distance; - return *this; - } + NamedSegment& operator=(NamedSegment&& ns) noexcept; }; //============================================================================= @@ -90,135 +82,16 @@ class route_summary_cache { int misses = 0; public: - route_summary_cache(const google::protobuf::RepeatedPtrField& routes) { - // A route/leg/maneuver may go on/off the same named road many times. - // Combine the distances for same named roads - store in a NamedSegment. - route_leg_segs_by_dist.reserve(routes.size()); - for (size_t i = 0; i < routes.size(); i++) { - const DirectionsRoute& route = routes.Get(i); - std::vector> leg_segs_by_dist; - leg_segs_by_dist.reserve(route.legs_size()); - for (size_t j = 0; j < route.legs_size(); j++) { - const DirectionsLeg& leg = route.legs(j); - std::unordered_map> maneuver_summary_map; - maneuver_summary_map.reserve(leg.maneuver_size()); - uint32_t maneuver_index = 0; - for (const auto& maneuver : leg.maneuver()) { - if (maneuver.street_name_size() > 0) { - const std::string& name = maneuver.street_name(0).value(); - auto maneuver_summary = maneuver_summary_map.find(name); - if (maneuver_summary == maneuver_summary_map.end()) { - maneuver_summary_map[name] = std::make_pair(maneuver_index, maneuver.length()); - } else { - maneuver_summary->second.second += maneuver.length(); - } - } - ++maneuver_index; - } - - // Create a list of named segments (maneuver name, maneuver index, distance) - // sorted by distance. - std::vector segs_by_dist; - segs_by_dist.reserve(maneuver_summary_map.size()); - for (const auto& map_item : maneuver_summary_map) { - segs_by_dist.emplace_back(map_item.first, map_item.second.first, map_item.second.second); - } - - // Sort list by descending maneuver distance - std::sort(segs_by_dist.begin(), segs_by_dist.end(), - [](const NamedSegment& a, const NamedSegment& b) { - return b.distance < a.distance; - }); - - leg_segs_by_dist.emplace_back(std::move(segs_by_dist)); - } - - route_leg_segs_by_dist.emplace_back(std::move(leg_segs_by_dist)); - } - - // fill the cache with empty results so we can safely index it with the same - // dimensionality as the route_leg_segs_by_dist object. - cache.reserve(route_leg_segs_by_dist.size()); - for (size_t i = 0; i < route_leg_segs_by_dist.size(); i++) { - cache.emplace_back(); - cache[i].reserve(route_leg_segs_by_dist[i].size()); - for (size_t j = 0; j < route_leg_segs_by_dist[i].size(); j++) { - cache[i].emplace_back(); - cache[i][j].reserve(route_leg_segs_by_dist[i][j].size()); - for (size_t k = 0; k < route_leg_segs_by_dist[i][j].size(); k++) { - cache[i][j].emplace_back(); - } - } - } - } - - size_t num_named_segments_for_route_leg(size_t route_idx, size_t leg_idx) { - if (route_idx >= route_leg_segs_by_dist.size()) { - return 0; - } - - if (leg_idx >= route_leg_segs_by_dist[route_idx].size()) { - return 0; - } - - return route_leg_segs_by_dist[route_idx][leg_idx].size(); - } + route_summary_cache(const google::protobuf::RepeatedPtrField& routes); + size_t num_named_segments_for_route_leg(size_t route_idx, size_t leg_idx); // Return the n-part named-segment summary for the given route/leg. // The summary returned is guaranteed to be comprised of as few named // segments as possible, while also being unique among all route/same-leg // summaries. - std::string get_n_segment_summary(size_t route_idx, size_t leg_idx, size_t num_named_segs) { - if (route_idx >= cache.size()) { - return ""; - } - - if (leg_idx >= cache[route_idx].size()) { - return ""; - } - - if (num_named_segs == 0) { - return ""; - } - - // num_named_segs is the number of named segments you'd like the summary to - // be comprised of. The smallest requestable value is 1 - so we store all - // summaries offset by n = num_named_segs - 1. - size_t n = num_named_segs - 1; - if (n >= cache[route_idx][leg_idx].size()) { - return ""; - } - - // empty summary means cache miss - if (cache[route_idx][leg_idx][n].empty()) { - // go figure out the summary - std::vector segs_by_maneuver_index; - segs_by_maneuver_index.reserve(num_named_segs); - for (size_t i = 0; i < num_named_segs; i++) { - segs_by_maneuver_index.emplace_back(&route_leg_segs_by_dist[route_idx][leg_idx][i]); - } - - // sort by maneuver index - std::sort(segs_by_maneuver_index.begin(), segs_by_maneuver_index.end(), - [](const NamedSegment* a, const NamedSegment* b) { return a->index < b->index; }); - - std::string summary; - for (size_t i = 0; i < num_named_segs; i++) { - summary += segs_by_maneuver_index[i]->name; - if (i != num_named_segs - 1) - summary += ", "; - } - - misses++; - - cache[route_idx][leg_idx][n] = std::move(summary); - } else { - hits++; - } - - return cache[route_idx][leg_idx][n]; - } + std::string get_n_segment_summary(size_t route_idx, size_t leg_idx, size_t num_named_segs); }; - } // namespace tyr -} // namespace valhalla \ No newline at end of file +} // namespace valhalla + +#endif // VALHALLA_TYR_ROUTE_SUMMARY_CACHE_H diff --git a/valhalla/tyr/serializer_constants.h b/valhalla/tyr/serializer_constants.h index e8c882f186..080164f786 100644 --- a/valhalla/tyr/serializer_constants.h +++ b/valhalla/tyr/serializer_constants.h @@ -1,3 +1,6 @@ +#ifndef VALHALLA_TYR_ROUTE_SERIALIZER_CONSTANTS_ +#define VALHALLA_TYR_ROUTE_SERIALIZER_CONSTANTS_ +#pragma once #include namespace valhalla { @@ -18,3 +21,4 @@ const std::string kModifierSharpRight = "sharp right"; } // namespace osrmconstants } // namespace tyr } // namespace valhalla +#endif // VALHALLA_TYR_ROUTE_SERIALIZER_CONSTANTS_ From 5ce3aa7d4e780e86aae43979726d092e69b463db Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 5 Feb 2024 15:35:12 +0100 Subject: [PATCH 020/198] bump cxxopts to v3 (major version upgrade) (#4541) --- .dockerignore | 1 + .gitignore | 1 + CHANGELOG.md | 1 + src/argparse_utils.h | 6 ++++-- src/mjolnir/valhalla_add_elevation.cc | 2 +- src/mjolnir/valhalla_add_landmarks.cc | 2 +- src/mjolnir/valhalla_add_predicted_traffic.cc | 2 +- src/mjolnir/valhalla_assign_speeds.cc | 2 +- src/mjolnir/valhalla_benchmark_admins.cc | 2 +- src/mjolnir/valhalla_build_admins.cc | 4 ++-- src/mjolnir/valhalla_build_connectivity.cc | 2 +- src/mjolnir/valhalla_build_landmarks.cc | 4 ++-- src/mjolnir/valhalla_build_statistics.cc | 2 +- src/mjolnir/valhalla_build_tiles.cc | 10 +++++----- src/mjolnir/valhalla_convert_transit.cc | 2 +- src/mjolnir/valhalla_ingest_transit.cc | 2 +- src/mjolnir/valhalla_query_transit.cc | 4 ++-- src/mjolnir/valhalla_validate_transit.cc | 2 +- src/mjolnir/valhalla_ways_to_edges.cc | 2 +- src/valhalla_benchmark_adjacency_list.cc | 2 +- src/valhalla_benchmark_loki.cc | 4 ++-- src/valhalla_expand_bounding_box.cc | 2 +- src/valhalla_export_edges.cc | 2 +- src/valhalla_path_comparison.cc | 4 ++-- src/valhalla_run_isochrone.cc | 5 +++-- src/valhalla_run_matrix.cc | 5 +++-- src/valhalla_run_route.cc | 4 ++-- third_party/cxxopts | 2 +- 28 files changed, 45 insertions(+), 38 deletions(-) diff --git a/.dockerignore b/.dockerignore index 11fc8893d6..b6ab5b052f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -59,6 +59,7 @@ CMakeLists.txt.user /CMakeSettings.json .idea /.tidytmp +vcpkg*/ # documentation site/ diff --git a/.gitignore b/.gitignore index efbe467dc0..036d2a39c6 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,7 @@ CMakeLists.txt.user /CMakeSettings.json .idea /.tidytmp +vcpkg*/ # python .venv diff --git a/CHANGELOG.md b/CHANGELOG.md index 75f2a36b6c..a558d0a130 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -83,6 +83,7 @@ * CHANGED: libvalhalla.pc generation to have finer controls; install third_party public headers; overhaul lots of CMake; remove conan support [#4516](https://github.com/valhalla/valhalla/pull/4516) * CHANGED: refactored matrix code to include a base class for all matrix algorithms to prepare for second passes on matrix [#4535](https://github.com/valhalla/valhalla/pull/4535) * ADDED: matrix second pass for connections not found in the first pass, analogous to /route [#4536](https://github.com/valhalla/valhalla/pull/4536) + * UPDATED: cxxopts to 3.1.1 [#4541](https://github.com/valhalla/valhalla/pull/4541) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/argparse_utils.h b/src/argparse_utils.h index e87bb8da6d..18f4f952bf 100644 --- a/src/argparse_utils.h +++ b/src/argparse_utils.h @@ -1,3 +1,5 @@ +#include + #include #include @@ -21,7 +23,7 @@ * @param use_threads Whether this program multi-threads * * @returns true if the program should continue, false if we should EXIT_SUCCESS - * @throws cxxopts::OptionException Thrown if there's no valid configuration + * @throws cxxopts::exceptions::exception Thrown if there's no valid configuration */ bool parse_common_args(const std::string& program, const cxxopts::Options& opts, @@ -46,7 +48,7 @@ bool parse_common_args(const std::string& program, filesystem::is_regular_file(result["config"].as())) { conf = valhalla::config(result["config"].as()); } else { - throw cxxopts::OptionException("Configuration is required\n\n" + opts.help() + "\n\n"); + throw cxxopts::exceptions::exception("Configuration is required\n\n" + opts.help() + "\n\n"); } // configure logging diff --git a/src/mjolnir/valhalla_add_elevation.cc b/src/mjolnir/valhalla_add_elevation.cc index 42fb5b40dd..ba8083229b 100644 --- a/src/mjolnir/valhalla_add_elevation.cc +++ b/src/mjolnir/valhalla_add_elevation.cc @@ -101,7 +101,7 @@ int main(int argc, char** argv) { std::cerr << "All tile files are invalid\n\n" << options.help() << "\n\n"; return EXIT_FAILURE; } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_add_landmarks.cc b/src/mjolnir/valhalla_add_landmarks.cc index c5d45c6a8c..4cec1bbed4 100644 --- a/src/mjolnir/valhalla_add_landmarks.cc +++ b/src/mjolnir/valhalla_add_landmarks.cc @@ -32,7 +32,7 @@ int main(int argc, char** argv) { auto result = options.parse(argc, argv); if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) return EXIT_SUCCESS; - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_add_predicted_traffic.cc b/src/mjolnir/valhalla_add_predicted_traffic.cc index e5ae6df8aa..0df2e1b106 100644 --- a/src/mjolnir/valhalla_add_predicted_traffic.cc +++ b/src/mjolnir/valhalla_add_predicted_traffic.cc @@ -271,7 +271,7 @@ int main(int argc, char** argv) { return false; } traffic_tile_dir = filesystem::path(result["traffic-tile-dir"].as()); - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_assign_speeds.cc b/src/mjolnir/valhalla_assign_speeds.cc index e224e051d2..9dcfc28af1 100644 --- a/src/mjolnir/valhalla_assign_speeds.cc +++ b/src/mjolnir/valhalla_assign_speeds.cc @@ -99,7 +99,7 @@ int main(int argc, char** argv) { auto result = options.parse(argc, argv); if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) return EXIT_SUCCESS; - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_benchmark_admins.cc b/src/mjolnir/valhalla_benchmark_admins.cc index fb206697c2..7f98e9af85 100644 --- a/src/mjolnir/valhalla_benchmark_admins.cc +++ b/src/mjolnir/valhalla_benchmark_admins.cc @@ -224,7 +224,7 @@ int main(int argc, char** argv) { std::cout << "valhalla_benchmark_admins " << VALHALLA_VERSION << "\n"; return EXIT_SUCCESS; } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_build_admins.cc b/src/mjolnir/valhalla_build_admins.cc index 35a61fd2d0..4376339370 100644 --- a/src/mjolnir/valhalla_build_admins.cc +++ b/src/mjolnir/valhalla_build_admins.cc @@ -41,9 +41,9 @@ int main(int argc, char** argv) { // input files are positional if (!result.count("input_files")) { - throw cxxopts::OptionException("Input file is required\n\n" + options.help()); + throw cxxopts::exceptions::exception("Input file is required\n\n" + options.help()); } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_build_connectivity.cc b/src/mjolnir/valhalla_build_connectivity.cc index 91693619cf..5d9effce0a 100644 --- a/src/mjolnir/valhalla_build_connectivity.cc +++ b/src/mjolnir/valhalla_build_connectivity.cc @@ -66,7 +66,7 @@ int main(int argc, char** argv) { auto result = options.parse(argc, argv); if (!parse_common_args(program, options, result, config, "mjolnir.logging")) return EXIT_SUCCESS; - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_build_landmarks.cc b/src/mjolnir/valhalla_build_landmarks.cc index 5a553127a7..5665146189 100644 --- a/src/mjolnir/valhalla_build_landmarks.cc +++ b/src/mjolnir/valhalla_build_landmarks.cc @@ -36,9 +36,9 @@ int main(int argc, char** argv) { // input files are positional if (!result.count("input_files")) { - throw cxxopts::OptionException("Input file is required\n\n" + options.help()); + throw cxxopts::exceptions::exception("Input file is required\n\n" + options.help()); } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_build_statistics.cc b/src/mjolnir/valhalla_build_statistics.cc index 858bcf16ec..ec34f9e88f 100644 --- a/src/mjolnir/valhalla_build_statistics.cc +++ b/src/mjolnir/valhalla_build_statistics.cc @@ -592,7 +592,7 @@ int main(int argc, char** argv) { auto result = options.parse(argc, argv); if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) return EXIT_SUCCESS; - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_build_tiles.cc b/src/mjolnir/valhalla_build_tiles.cc index 235d4b017b..0ef39323e5 100644 --- a/src/mjolnir/valhalla_build_tiles.cc +++ b/src/mjolnir/valhalla_build_tiles.cc @@ -65,14 +65,14 @@ int main(int argc, char** argv) { start_stage = string_to_buildstage(result["start"].as()); if (start_stage == BuildStage::kInvalid) { list_stages(); - throw cxxopts::OptionException("Invalid start stage, see above"); + throw cxxopts::exceptions::exception("Invalid start stage, see above"); } } if (result.count("end")) { end_stage = string_to_buildstage(result["end"].as()); if (end_stage == BuildStage::kInvalid) { list_stages(); - throw cxxopts::OptionException("Invalid end stage, see above"); + throw cxxopts::exceptions::exception("Invalid end stage, see above"); } } LOG_INFO("Start stage = " + to_string(start_stage) + " End stage = " + to_string(end_stage)); @@ -80,15 +80,15 @@ int main(int argc, char** argv) { // Make sure start stage < end stage if (static_cast(start_stage) > static_cast(end_stage)) { list_stages(); - throw cxxopts::OptionException( + throw cxxopts::exceptions::exception( "Starting build stage is after ending build stage in pipeline, see above"); } if (!result.count("input_files") && start_stage <= BuildStage::kParseNodes && end_stage >= BuildStage::kParseWays) { - throw cxxopts::OptionException("Input file is required\n\n" + options.help() + "\n\n"); + throw cxxopts::exceptions::exception("Input file is required\n\n" + options.help() + "\n\n"); } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_convert_transit.cc b/src/mjolnir/valhalla_convert_transit.cc index be54c5165e..f9011cbb69 100644 --- a/src/mjolnir/valhalla_convert_transit.cc +++ b/src/mjolnir/valhalla_convert_transit.cc @@ -50,7 +50,7 @@ int main(int argc, char** argv) { onestoptests = valhalla::mjolnir::ParseTestFile(testfile); std::sort(onestoptests.begin(), onestoptests.end()); } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_ingest_transit.cc b/src/mjolnir/valhalla_ingest_transit.cc index 54d2a56161..bc6ee033e2 100644 --- a/src/mjolnir/valhalla_ingest_transit.cc +++ b/src/mjolnir/valhalla_ingest_transit.cc @@ -28,7 +28,7 @@ int main(int argc, char** argv) { auto result = options.parse(argc, argv); if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) return EXIT_SUCCESS; - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_query_transit.cc b/src/mjolnir/valhalla_query_transit.cc index 7695d23a67..5d35b7f06b 100644 --- a/src/mjolnir/valhalla_query_transit.cc +++ b/src/mjolnir/valhalla_query_transit.cc @@ -397,10 +397,10 @@ int main(int argc, char* argv[]) { for (const auto& arg : std::vector{"o_onestop_id", "o_lat", "o_lng"}) { if (result.count(arg) == 0) { const std::string msg = "The <" + arg + "> argument was not provided, but is mandatory\n\n"; - throw cxxopts::OptionException(msg + options.help()); + throw cxxopts::exceptions::exception(msg + options.help()); } } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_validate_transit.cc b/src/mjolnir/valhalla_validate_transit.cc index fa6d0bfb8b..3503ce44b2 100644 --- a/src/mjolnir/valhalla_validate_transit.cc +++ b/src/mjolnir/valhalla_validate_transit.cc @@ -44,7 +44,7 @@ int main(int argc, char** argv) { auto result = options.parse(argc, argv); if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) return EXIT_SUCCESS; - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/mjolnir/valhalla_ways_to_edges.cc b/src/mjolnir/valhalla_ways_to_edges.cc index ef26411738..28e5cbd7be 100644 --- a/src/mjolnir/valhalla_ways_to_edges.cc +++ b/src/mjolnir/valhalla_ways_to_edges.cc @@ -57,7 +57,7 @@ int main(int argc, char** argv) { if (!parse_common_args(program, options, result, config, "mjolnir.logging")) return EXIT_SUCCESS; - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/valhalla_benchmark_adjacency_list.cc b/src/valhalla_benchmark_adjacency_list.cc index 220dcbd538..b96ef8c317 100644 --- a/src/valhalla_benchmark_adjacency_list.cc +++ b/src/valhalla_benchmark_adjacency_list.cc @@ -123,7 +123,7 @@ int main(int argc, char* argv[]) { auto result = options.parse(argc, argv); if (!parse_common_args(program, options, result, config, "mjolnir.logging")) return EXIT_SUCCESS; - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/valhalla_benchmark_loki.cc b/src/valhalla_benchmark_loki.cc index 519e2b1b98..8f3b988707 100644 --- a/src/valhalla_benchmark_loki.cc +++ b/src/valhalla_benchmark_loki.cc @@ -159,9 +159,9 @@ int main(int argc, char** argv) { return EXIT_SUCCESS; if (!result.count("input_files")) { - throw cxxopts::OptionException("Input file is required\n\n" + options.help()); + throw cxxopts::exceptions::exception("Input file is required\n\n" + options.help()); } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/valhalla_expand_bounding_box.cc b/src/valhalla_expand_bounding_box.cc index d7ca69abcc..56d5b7e521 100644 --- a/src/valhalla_expand_bounding_box.cc +++ b/src/valhalla_expand_bounding_box.cc @@ -43,7 +43,7 @@ int main(int argc, char** argv) { std::cerr << options.help() << std::endl; return EXIT_FAILURE; } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/valhalla_export_edges.cc b/src/valhalla_export_edges.cc index a53ec2309f..07fda5360a 100644 --- a/src/valhalla_export_edges.cc +++ b/src/valhalla_export_edges.cc @@ -169,7 +169,7 @@ int main(int argc, char* argv[]) { auto result = options.parse(argc, argv); if (!parse_common_args(program, options, result, config, "")) return EXIT_SUCCESS; - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/valhalla_path_comparison.cc b/src/valhalla_path_comparison.cc index 246a83828d..53e37ad941 100644 --- a/src/valhalla_path_comparison.cc +++ b/src/valhalla_path_comparison.cc @@ -195,11 +195,11 @@ int main(int argc, char* argv[]) { } else if (result.count("shape")) { shape = result["shape"].as(); } else { - throw cxxopts::OptionException( + throw cxxopts::exceptions::exception( "The json parameter or shape parameter was not supplied but is required.\n\n" + options.help()); } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/valhalla_run_isochrone.cc b/src/valhalla_run_isochrone.cc index 1f199041c4..fa3175840e 100644 --- a/src/valhalla_run_isochrone.cc +++ b/src/valhalla_run_isochrone.cc @@ -59,14 +59,15 @@ int main(int argc, char* argv[]) { return EXIT_SUCCESS; if (!result.count("json")) { - throw cxxopts::OptionException("A JSON format request must be present.\n\n" + options.help()); + throw cxxopts::exceptions::exception("A JSON format request must be present.\n\n" + + options.help()); } json_str = result["json"].as(); if (result.count("file")) { filename = result["file"].as(); } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/valhalla_run_matrix.cc b/src/valhalla_run_matrix.cc index f609f8a7bb..f41f502763 100644 --- a/src/valhalla_run_matrix.cc +++ b/src/valhalla_run_matrix.cc @@ -125,14 +125,15 @@ int main(int argc, char* argv[]) { return EXIT_SUCCESS; if (!result.count("json")) { - throw cxxopts::OptionException("A JSON format request must be present.\n\n" + options.help()); + throw cxxopts::exceptions::exception("A JSON format request must be present.\n\n" + + options.help()); } json_str = result["json"].as(); iterations = result["multi-run"].as(); log_details = result["log-details"].as(); optimize = result["optimize"].as(); - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/src/valhalla_run_route.cc b/src/valhalla_run_route.cc index 3a7f74d250..19193fae29 100644 --- a/src/valhalla_run_route.cc +++ b/src/valhalla_run_route.cc @@ -515,9 +515,9 @@ int main(int argc, char* argv[]) { } else if (result.count("json")) { json_str = result["json"].as(); } else { - throw cxxopts::OptionException("Either json or json-file args must be set."); + throw cxxopts::exceptions::exception("Either json or json-file args must be set."); } - } catch (cxxopts::OptionException& e) { + } catch (cxxopts::exceptions::exception& e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; } catch (std::exception& e) { diff --git a/third_party/cxxopts b/third_party/cxxopts index 302302b308..eb787304d6 160000 --- a/third_party/cxxopts +++ b/third_party/cxxopts @@ -1 +1 @@ -Subproject commit 302302b30839505703d37fb82f536c53cf9172fa +Subproject commit eb787304d67ec22f7c3a184ee8b4c481d04357fd From 21783fc291c70b0c6783cc7c158c6e7d876c09f1 Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 5 Feb 2024 16:21:09 +0100 Subject: [PATCH 021/198] Make vendored libraries optional (#4544) --- .github/workflows/win.yml | 19 +++++----- .gitmodules | 3 -- CHANGELOG.md | 1 + CMakeLists.txt | 60 +++++++++++++++++++++++++++--- README.md | 2 +- docs/docs/building.md | 55 +++++++++++++-------------- src/CMakeLists.txt | 6 +-- src/baldr/CMakeLists.txt | 10 ++--- src/bindings/python/CMakeLists.txt | 11 +++++- src/loki/CMakeLists.txt | 10 ++--- src/meili/CMakeLists.txt | 10 ++--- src/midgard/CMakeLists.txt | 4 +- src/mjolnir/CMakeLists.txt | 9 ++--- src/odin/CMakeLists.txt | 10 ++--- src/sif/CMakeLists.txt | 12 ++---- src/skadi/CMakeLists.txt | 11 ++---- src/thor/CMakeLists.txt | 12 ++---- src/tyr/CMakeLists.txt | 10 ++--- test/CMakeLists.txt | 9 +---- third_party/lz4 | 1 - vcpkg.json | 10 +++++ 21 files changed, 148 insertions(+), 127 deletions(-) delete mode 160000 third_party/lz4 diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index ff326b073b..7f3d785e18 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -37,7 +37,7 @@ defaults: env: BUILD_TYPE: Release MSVC_VERSION: '2022' - VCPKG_VERSION: '2024.01.12' + VCPKG_VERSION: '8040303' VCPKG_INSTALL_OPTIONS: --x-abi-tools-use-exact-versions VCPKG_DISABLE_COMPILER_TRACKING: ON @@ -61,11 +61,11 @@ jobs: - name: Cache vcpkg id: cache-vcpkg - uses: actions/cache@v4 + uses: actions/cache@v3 with: path: ${{ env.VCPKG_ROOT }} - key: vcpkg=${{ env.VCPKG_VERSION }}-msvc=${{ env.MSVC_VERSION }}-json=${{ hashFiles('vcpkg.json') }}-v2 - save-always: 'true' + key: vcpkg=${{ env.VCPKG_VERSION }}-msvc=${{ env.MSVC_VERSION }}-json=${{ hashFiles('vcpkg.json') }}-v3 + enableCrossOsArchive: true - name: Install GNU make & awk run: choco install gawk make @@ -73,8 +73,9 @@ jobs: - if: ${{ steps.cache-vcpkg.outputs.cache-hit != 'true' }} name: Bootstrap vcpkg and install packages (if cache miss) run: | - git clone --depth=1 --branch "$VCPKG_VERSION" https://github.com/microsoft/vcpkg.git + git clone https://github.com/microsoft/vcpkg.git cd vcpkg + git checkout $VCPKG_VERSION mkdir archives mkdir "$VCPKG_OVERLAY_TRIPLETS" TRIPLET_FILE="$VCPKG_OVERLAY_TRIPLETS/$VCPKG_DEFAULT_TRIPLET.cmake" @@ -82,12 +83,11 @@ jobs: echo "set(VCPKG_BUILD_TYPE release)" >> "$TRIPLET_FILE" echo "set(VCPKG_DISABLE_COMPILER_TRACKING $VCPKG_DISABLE_COMPILER_TRACKING)" >> "$TRIPLET_FILE" cmd.exe /c bootstrap-vcpkg.bat - vcpkg.exe \ + vcpkg.exe install \ --vcpkg-root="$VCPKG_ROOT" \ --overlay-triplets="$VCPKG_OVERLAY_TRIPLETS" \ --triplet="$VCPKG_DEFAULT_TRIPLET" \ - $VCPKG_INSTALL_OPTIONS \ - install + $VCPKG_INSTALL_OPTIONS - name: Setup Developer Command Prompt for VS uses: ilammy/msvc-dev-cmd@v1 @@ -110,7 +110,8 @@ jobs: -DENABLE_TESTS=OFF \ -DENABLE_CCACHE=OFF \ -DENABLE_SERVICES=OFF \ - -DENABLE_BENCHMARKS=OFF + -DENABLE_BENCHMARKS=OFF \ + -DPREFER_EXTERNAL=ON \ - name: Build Valhalla shell: powershell diff --git a/.gitmodules b/.gitmodules index 0e922563e9..2524d3e61a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,9 +37,6 @@ [submodule "third_party/cpp-statsd-client"] path = third_party/cpp-statsd-client url = https://github.com/vthiery/cpp-statsd-client -[submodule "third_party/lz4"] - path = third_party/lz4 - url = https://github.com/lz4/lz4 [submodule "third_party/cxxopts"] path = third_party/cxxopts url = https://github.com/jarro2783/cxxopts.git diff --git a/CHANGELOG.md b/CHANGELOG.md index a558d0a130..a1a386b516 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,7 @@ * CHANGED: refactored matrix code to include a base class for all matrix algorithms to prepare for second passes on matrix [#4535](https://github.com/valhalla/valhalla/pull/4535) * ADDED: matrix second pass for connections not found in the first pass, analogous to /route [#4536](https://github.com/valhalla/valhalla/pull/4536) * UPDATED: cxxopts to 3.1.1 [#4541](https://github.com/valhalla/valhalla/pull/4541) + * CHANGED: make use of vendored libraries optional (other than libraries which are not commonly in package managers or only used for testing) [#4544](https://github.com/valhalla/valhalla/pull/4544) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/CMakeLists.txt b/CMakeLists.txt index 957030efb3..50905cebb3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ option(ENABLE_WERROR "Convert compiler warnings to errors. Requires ENABLE_COMPI option(ENABLE_BENCHMARKS "Enable microbenchmarking" ON) option(ENABLE_THREAD_SAFE_TILE_REF_COUNT "If ON uses shared_ptr as tile reference(i.e. it is thread safe)" OFF) option(ENABLE_SINGLE_FILES_WERROR "Convert compiler warnings to errors for single files" ON) +option(PREFER_EXTERNAL_DEPS "Whether to use internally vendored headers or find the equivalent external package" OFF) # useful to workaround issues likes this https://stackoverflow.com/questions/24078873/cmake-generated-xcode-project-wont-compile option(ENABLE_STATIC_LIBRARY_MODULES "If ON builds Valhalla modules as STATIC library targets" OFF) @@ -99,6 +100,7 @@ include(ValhallaSourceGroups) find_package(PkgConfig REQUIRED) find_package(Threads REQUIRED) pkg_check_modules(ZLIB REQUIRED IMPORTED_TARGET zlib) +pkg_check_modules(LZ4 REQUIRED IMPORTED_TARGET liblz4) # cURL set(curl_targets "") @@ -118,9 +120,55 @@ add_definitions(-DBOOST_NO_CXX11_SCOPED_ENUMS) add_definitions(-DBOOST_ALLOW_DEPRECATED_HEADERS) add_definitions(-DBOOST_BIND_GLOBAL_PLACEHOLDERS) +# resolve vendored libraries +set(date_include_dir ${VALHALLA_SOURCE_DIR}/third_party/date/include) +set(rapidjson_include_dir ${CMAKE_SOURCE_DIR}/third_party/rapidjson/include) +set(robinhoodhashing_include_dir ${CMAKE_SOURCE_DIR}/third_party/robin-hood-hashing/src/include) +set(cxxopts_include_dir ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) +set(dirent_include_dir ${CMAKE_SOURCE_DIR}/third_party/dirent/include) +if (PREFER_EXTERNAL_DEPS) + # date + find_package(date QUIET) + if (date_FOUND) + get_target_property(date_include_dir date::date INTERFACE_INCLUDE_DIRECTORIES) + else() + message(WARNING "No date found in system libraries, using vendored date...") + endif() + # rapidjson + find_package(RapidJSON QUIET) + if (RapidJSON_FOUND) + get_target_property(rapidjson_include_dir rapidjson INTERFACE_INCLUDE_DIRECTORIES) + else() + message(WARNING "No RapidJSON found in system libraries, using vendored RapidJSON...") + endif() + # robin-hood-hashing + find_package(robin_hood QUIET) + if (robin_hood_FOUND) + get_target_property(robinhoodhashing_include_dir robin_hood::robin_hood INTERFACE_INCLUDE_DIRECTORIES) + else() + message(WARNING "No robin_hood found in system libraries, using vendored robin_hood...") + endif() + # cxxopts + if (ENABLE_DATA_TOOLS OR ENABLE_TOOLS) + find_package(cxxopts QUIET) + if (cxxopts_FOUND) + get_target_property(cxxopts_include_dir cxxopts::cxxopts INTERFACE_INCLUDE_DIRECTORIES) + else() + message(WARNING "No cxxopts found in system libraries, using vendored cxxopts...") + endif() + endif() + # dirent + if (WIN32) + find_path(dirent_include_dir dirent.h REQUIRED) + if (dirent_include_dir-NOTFOUND) + message(WARNING "No dirent.h found in system headers, using vendored dirent.h...") + endif() + endif() +endif() + # Protobuf is non-trivial to include via pkg-config, pkg_check_modules has no way to check # for protoc location in a platform agnostic manner -# prefer CONFIG mode over MODULE mode, there were a few CMake/protobuf messups +# prefer CONFIG mode over MODULE mode, which versions configuration on the package, not CMake # NOTE: this is only supported for cmake >= 3.15, but shouldn't be a problem in real life set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) # newer protobuf versions require a compat bool @@ -211,7 +259,7 @@ if(ENABLE_TOOLS) set_target_properties(${program} PROPERTIES FOLDER "Tools") create_source_groups("Source Files" ${path}) target_link_libraries(${program} valhalla) - target_include_directories(${program} PRIVATE ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) + target_include_directories(${program} PRIVATE ${cxxopts_include_dir}) install(TARGETS ${program} DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime) endforeach() endif() @@ -222,7 +270,7 @@ if(ENABLE_DATA_TOOLS) add_executable(${program} ${path}) create_source_groups("Source Files" ${path}) set_target_properties(${program} PROPERTIES FOLDER "Data Tools") - target_include_directories(${program} PRIVATE ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) + target_include_directories(${program} PRIVATE ${cxxopts_include_dir}) target_link_libraries(${program} valhalla) if (LuaJIT_FOUND AND APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "x86_64") # Using LuaJIT on macOS on Intel processors requires a couple of extra linker flags @@ -272,12 +320,12 @@ install(FILES COPYING CHANGELOG.md COMPONENT runtime) # install third_party -install(DIRECTORY ${CMAKE_SOURCE_DIR}/third_party/rapidjson/include/ +install(DIRECTORY ${rapidjson_include_dir}/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla/third_party") -install(DIRECTORY ${CMAKE_SOURCE_DIR}/third_party/date/include/ +install(DIRECTORY ${date_include_dir}/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla/third_party") if (WIN32) - install(DIRECTORY ${CMAKE_SOURCE_DIR}/third_party/dirent/include/ + install(DIRECTORY ${dirent_include_dir}/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/valhalla/third_party") endif() diff --git a/README.md b/README.md index 1b9c575398..940970b05b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Valhalla is an open source routing engine and accompanying libraries for use wit | Linux/MacOs | Windows | Code Coverage | | ----------- | ------- | ------------- | -| [![Circle CI](https://circleci.com/gh/valhalla/valhalla/tree/master.svg?style=svg)](https://circleci.com/gh/valhalla/valhalla/tree/master) | [![Build Status](https://dev.azure.com/valhalla1/valhalla/_apis/build/status/valhalla.valhalla?branchName=master)](https://dev.azure.com/valhalla1/valhalla/_build/latest?definitionId=1&branchName=master) | [![codecov](https://codecov.io/gh/valhalla/valhalla/branch/master/graph/badge.svg)](https://codecov.io/gh/valhalla/valhalla) | +| [![Circle CI](https://circleci.com/gh/valhalla/valhalla/tree/master.svg?style=svg)](https://circleci.com/gh/valhalla/valhalla/tree/master) | [![Windows CI](https://github.com/valhalla/valhalla/actions/workflows/win.yml/badge.svg)](https://github.com/valhalla/valhalla/actions/workflows/win.yml) | [![codecov](https://codecov.io/gh/valhalla/valhalla/branch/master/graph/badge.svg)](https://codecov.io/gh/valhalla/valhalla) | ## License diff --git a/docs/docs/building.md b/docs/docs/building.md index fc39f2db5a..06bd1c0800 100644 --- a/docs/docs/building.md +++ b/docs/docs/building.md @@ -26,11 +26,31 @@ Important build options include: | `-DENABLE_SANITIZERS` (`ON` / `OFF`) | Build with all the integrated sanitizers (defaults to off).| | `-DENABLE_ADDRESS_SANITIZER` (`ON` / `OFF`) | Build with address sanitizer (defaults to off).| | `-DENABLE_UNDEFINED_SANITIZER` (`ON` / `OFF`) | Build with undefined behavior sanitizer (defaults to off).| +| `-DPREFER_SYSTEM_DEPS` (`ON` / `OFF`) | Whether to use internally vendored headers or find the equivalent external package (defaults to off).| -If you're building on Apple Silicon and use the Rosetta terminal (see below), you might need to additionally specify the appropriate options: +### Building with `vcpkg` - any platform -``` -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES="x86_64" +Instead of installing the dependencies system-wide, you can also opt to use [`vcpkg`](https://github.com/microsoft/vcpkg). + +The following commands should work on all platforms: + +```bash +git clone --recurse-submodules https://github.com/valhalla/valhalla +cd valhalla +git clone https://github.com/microsoft/vcpkg && git -C vcpkg checkout +./vcpkg/bootstrap-vcpkg.sh +# windows: cmd.exe /c bootstrap-vcpkg.bat +# only build Release versions of dependencies, not Debug +echo "VCPKG_BUILD_TYPE release" >> vcpkg/triplets/x64-linux.cmake +# windows: echo.set(VCPKG_BUILD_TYPE release)>> .\vcpkg\triplets\x64-windows.cmake +# osx: echo "VCPKG_BUILD_TYPE release" >> vcpkg/triplets/arm64-osx.cmake +./vcpkg/vcpkg install + +# if you want to ENABLE_SERVICES=ON, install https://github.com/kevinkreiser/prime_server#build-and-install (no Windows) +cmake -B build -DCMAKE_TOOLCHAIN_FILE=$PWD/vcpkg/scripts/buildsystems/vcpkg.cmake -DENABLE_SERVICE=OFF +cmake --build build -- -j$(nproc) +# windows: cmake --build build --config Release -- /clp:ErrorsOnly /p:BuildInParallel=true /m:4 +# osx: cmake --build build -- -j$(sysctl -n hw.physicalcpu) ``` ### Building from Source - Linux @@ -52,34 +72,9 @@ sudo make -C build install ### Building from Source - macOS -#### Configuring Rosetta for ARM64 MacBook - -Check your architecture typing `arch` in the terminal. In case the result is `arm64` set up Rosetta terminal to emulate x86_64 behavior. Otherwise, skip this step. - -1. Go to `Finder > Application > Utilities`. -2. Select `Terminal` and right-click on it, then choose `Duplicate`. -3. Rename the duplicated app `Rosetta Terminal`. -4. Now select `Rosetta Terminal` application, right-click and choose `Get Info` . -5. Check the box for `Open using Rosetta`, then close the `Get Info` window. -6. Make sure you get `i386` after typing `arch` command in `Rosetta Terminal`. -7. Now it fully supports Homebrew and other x86_64 command line applications. - -Install [Homebrew](http://brew.sh) in the `Rosetta Terminal` app and update the aliases. - -``` -echo "alias ibrew='arch -x86_64 /usr/local/bin/brew'" >> ~/.zshrc -echo "alias mbrew='arch -arm64e /opt/homebrew/bin/brew'" >> ~/.zshrc -``` - -You will use them to specify the platform when installing a library. Note: use `ibrew` in `Rosetta Terminal` to install all dependencies for `valhalla` and `prime_server` projects. - -**_NOTE:_** If when installing packages below you get message `attempting to link with file built for macOS-arm64`, you can remove already installed packages for arm64 i.e. `mbrew uninstall ...`. Also, if there are problems with individual packages, you can install them from sources e.g. [geos](https://github.com/libgeos/geos) or [sqlite](https://www.sqlite.org/download.html). - -**_NOTE:_** It is possible to build Valhalla natively for Apple Silicon, but some dependencies(e.g. LuaJIT) don't have stable versions supporting Apple Silicon and have to be built and installed manually from source. - -#### Installing dependencies +Both arm64 and x64 should build cleanly with the below commands. -To install valhalla on macOS, you need to install its dependencies with [Homebrew](http://brew.sh): +To install Valhalla on macOS, you need to install its dependencies with [Homebrew](http://brew.sh): ```bash # install dependencies (automake & czmq are required by prime_server) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 05cf644091..c8e9a7940c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -186,10 +186,10 @@ target_include_directories(valhalla ${VALHALLA_SOURCE_DIR}/valhalla # TODO: this path must be removed and changed to #include in headers ${VALHALLA_BINARY_DIR} ${VALHALLA_BINARY_DIR}/valhalla # TODO: this path must be removed and changed to #include in headers - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include - ${VALHALLA_SOURCE_DIR}/third_party/date/include + ${rapidjson_include_dir} + ${date_include_dir} ${VALHALLA_SOURCE_DIR}/third_party/cpp-statsd-client/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> + $<$:${dirent_include_dir}> PRIVATE ${libvalhalla_include_directories}) diff --git a/src/baldr/CMakeLists.txt b/src/baldr/CMakeLists.txt index 0bf8d6e310..893011d014 100644 --- a/src/baldr/CMakeLists.txt +++ b/src/baldr/CMakeLists.txt @@ -38,14 +38,10 @@ set(includes ${CMAKE_CURRENT_BINARY_DIR}/src/baldr ) -# treat date library as system set(system_includes - ${VALHALLA_SOURCE_DIR}/third_party/date/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() + ${date_include_dir} + $<$:${dirent_include_dir}> + ${rapidjson_include_dir}) set(sources accessrestriction.cc diff --git a/src/bindings/python/CMakeLists.txt b/src/bindings/python/CMakeLists.txt index b08e87df8a..7bd677bca1 100644 --- a/src/bindings/python/CMakeLists.txt +++ b/src/bindings/python/CMakeLists.txt @@ -1,4 +1,13 @@ -add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/pybind11 ${CMAKE_BINARY_DIR}/third_party/pybind11) +if (PREFER_EXTERNAL_DEPS) + find_package(pybind11 QUIET) + if (NOT pybind11_FOUND) + message(WARNING "No pybind11 found in system libraries, using vendored pybind11...") + endif() +endif() + +if (NOT TARGET pybind11::pybind11_headers) + add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/pybind11 ${CMAKE_BINARY_DIR}/third_party/pybind11) +endif() pybind11_add_module(python_valhalla python.cc) target_link_libraries(python_valhalla PUBLIC valhalla) diff --git a/src/loki/CMakeLists.txt b/src/loki/CMakeLists.txt index 2ffd7535ef..24ba810bcb 100644 --- a/src/loki/CMakeLists.txt +++ b/src/loki/CMakeLists.txt @@ -21,13 +21,9 @@ set(sources_with_warnings trace_route_action.cc worker.cc) -# treat date library as system set(system_includes - ${VALHALLA_SOURCE_DIR}/third_party/date/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include>) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() + ${date_include_dir} + $<$:${dirent_include_dir}>) valhalla_module(NAME loki SOURCES ${sources} @@ -38,7 +34,7 @@ valhalla_module(NAME loki ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include + ${rapidjson_include_dir} ${CMAKE_BINARY_DIR} SYSTEM_INCLUDE_DIRECTORIES PUBLIC diff --git a/src/meili/CMakeLists.txt b/src/meili/CMakeLists.txt index 734050f7a3..3ebf3bcb46 100644 --- a/src/meili/CMakeLists.txt +++ b/src/meili/CMakeLists.txt @@ -14,13 +14,9 @@ set(sources_with_warnings transition_cost_model.cc viterbi_search.cc) -# treat date library as system set(system_includes - ${VALHALLA_SOURCE_DIR}/third_party/date/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include>) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() + ${date_include_dir} + $<$:${dirent_include_dir}>) valhalla_module(NAME meili SOURCES ${sources} @@ -36,7 +32,7 @@ valhalla_module(NAME meili PUBLIC ${system_includes} PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include + ${rapidjson_include_dir} DEPENDS valhalla::sif ${valhalla_protobuf_targets} diff --git a/src/midgard/CMakeLists.txt b/src/midgard/CMakeLists.txt index 5f3a0faf4a..ba018a0a86 100644 --- a/src/midgard/CMakeLists.txt +++ b/src/midgard/CMakeLists.txt @@ -22,8 +22,8 @@ valhalla_module(NAME midgard ${VALHALLA_SOURCE_DIR}/valhalla SYSTEM_INCLUDE_DIRECTORIES PUBLIC - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> + $<$:${dirent_include_dir}> PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing/src/include + ${robinhoodhashing_include_dir} DEPENDS Boost::boost) diff --git a/src/mjolnir/CMakeLists.txt b/src/mjolnir/CMakeLists.txt index 68d353eaaf..a3e00f5f1f 100644 --- a/src/mjolnir/CMakeLists.txt +++ b/src/mjolnir/CMakeLists.txt @@ -62,11 +62,10 @@ set(sources util.cc validatetransit.cc) -# treat date library as system set(system_includes - ${VALHALLA_SOURCE_DIR}/third_party/date/include - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> + ${date_include_dir} + ${rapidjson_include_dir} + $<$:${dirent_include_dir}> ${PROTOBUF_INCLUDE_DIR}) if(APPLE) list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) @@ -87,7 +86,7 @@ valhalla_module(NAME mjolnir ${system_includes} PRIVATE ${VALHALLA_SOURCE_DIR}/third_party/just_gtfs/include - ${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing/src/include + ${robinhoodhashing_include_dir} DEPENDS valhalla::proto valhalla::baldr diff --git a/src/odin/CMakeLists.txt b/src/odin/CMakeLists.txt index 7d7a02aaa5..af779b3e7c 100644 --- a/src/odin/CMakeLists.txt +++ b/src/odin/CMakeLists.txt @@ -28,13 +28,9 @@ set(sources_with_warnings narrativebuilder.cc util.cc) -# treat date library as system set(system_includes - ${VALHALLA_SOURCE_DIR}/third_party/date/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include>) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() + ${date_include_dir} + $<$:${dirent_include_dir}>) valhalla_module(NAME odin SOURCES ${sources} @@ -50,7 +46,7 @@ valhalla_module(NAME odin PUBLIC ${system_includes} PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include + ${rapidjson_include_dir} DEPENDS valhalla::proto ${valhalla_protobuf_targets} diff --git a/src/sif/CMakeLists.txt b/src/sif/CMakeLists.txt index b4664534c9..ec82dc56bc 100644 --- a/src/sif/CMakeLists.txt +++ b/src/sif/CMakeLists.txt @@ -13,15 +13,11 @@ set(sources dynamiccost.cc recost.cc) -# treat date library as system set(system_includes - ${VALHALLA_SOURCE_DIR}/third_party/date/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() - + ${date_include_dir} + $<$:${dirent_include_dir}> + ${rapidjson_include_dir}) + valhalla_module(NAME sif SOURCES ${sources} HEADERS ${headers} diff --git a/src/skadi/CMakeLists.txt b/src/skadi/CMakeLists.txt index 0439e79d86..5b5cbe53a4 100644 --- a/src/skadi/CMakeLists.txt +++ b/src/skadi/CMakeLists.txt @@ -4,22 +4,17 @@ valhalla_module(NAME skadi SOURCES sample.cc util.cc - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4hc.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4frame.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/xxhash.c HEADERS ${headers} INCLUDE_DIRECTORIES PUBLIC ${VALHALLA_SOURCE_DIR} ${VALHALLA_SOURCE_DIR}/valhalla - PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib SYSTEM_INCLUDE_DIRECTORIES PUBLIC - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include> + $<$:${dirent_include_dir}> DEPENDS valhalla::baldr Boost::boost - PkgConfig::ZLIB) + PkgConfig::ZLIB + PkgConfig::LZ4) diff --git a/src/thor/CMakeLists.txt b/src/thor/CMakeLists.txt index 8492764663..adcd0af750 100644 --- a/src/thor/CMakeLists.txt +++ b/src/thor/CMakeLists.txt @@ -29,13 +29,9 @@ set(sources_with_warnings trace_route_action.cc triplegbuilder_utils.h) -# treat date library as system set(system_includes - ${VALHALLA_SOURCE_DIR}/third_party/date/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include>) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() + ${date_include_dir} + $<$:${dirent_include_dir}>) valhalla_module(NAME thor SOURCES ${sources} @@ -51,8 +47,8 @@ valhalla_module(NAME thor PUBLIC ${system_includes} PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include - ${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing/src/include + ${rapidjson_include_dir} + ${robinhoodhashing_include_dir} DEPENDS valhalla::proto diff --git a/src/tyr/CMakeLists.txt b/src/tyr/CMakeLists.txt index 3a277f3de3..197e14b53b 100644 --- a/src/tyr/CMakeLists.txt +++ b/src/tyr/CMakeLists.txt @@ -16,13 +16,9 @@ set(sources_with_warnings route_serializer_valhalla.cc trace_serializer.cc) -# treat date library as system set(system_includes - ${VALHALLA_SOURCE_DIR}/third_party/date/include - $<$:${VALHALLA_SOURCE_DIR}/third_party/dirent/include>) -if(APPLE) - list(APPEND system_includes ${VALHALLA_SOURCE_DIR}/third_party/date/include/date) -endif() + ${date_include_dir} + $<$:${dirent_include_dir}>) valhalla_module(NAME tyr SOURCES @@ -41,7 +37,7 @@ valhalla_module(NAME tyr PUBLIC ${system_includes} PRIVATE - ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include + ${rapidjson_include_dir} DEPENDS valhalla::loki valhalla::thor diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a8cc10457d..3415db7c5d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -13,21 +13,16 @@ add_library(valhalla_test ${TEST_SRCS} ${VALHALLA_SOURCE_DIR}/third_party/microtar/src/microtar.h ${VALHALLA_SOURCE_DIR}/third_party/microtar/src/microtar.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4hc.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/lz4frame.c - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib/xxhash.c ) target_include_directories(valhalla_test PUBLIC ${VALHALLA_SOURCE_DIR}/test ${VALHALLA_SOURCE_DIR}/test/gurka - ${VALHALLA_SOURCE_DIR}/third_party/lz4/lib ${VALHALLA_SOURCE_DIR}/third_party/protozero/include ${VALHALLA_SOURCE_DIR}/third_party/just_gtfs/include ${VALHALLA_SOURCE_DIR}/third_party/libosmium/include - ${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing/src/include + ${robinhoodhashing_include_dir} ${VALHALLA_SOURCE_DIR}/third_party/microtar/src) -target_link_libraries(valhalla_test valhalla gtest gtest_main gmock pthread) +target_link_libraries(valhalla_test valhalla gtest gtest_main gmock pthread PkgConfig::LZ4) if(ENABLE_DATA_TOOLS) target_link_libraries(valhalla_test PkgConfig::GEOS) diff --git a/third_party/lz4 b/third_party/lz4 deleted file mode 160000 index 5ff8396801..0000000000 --- a/third_party/lz4 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5ff839680134437dbf4678f3d0c7b371d84f4964 diff --git a/vcpkg.json b/vcpkg.json index fb7445b755..cafb7d935f 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -11,11 +11,21 @@ "boost-range", "boost-tokenizer", "curl", + "cxxopts", + "date", + { + "name": "dirent", + "platform": "windows" + }, "geos", "libspatialite", "pkgconf", + "pybind11", + "rapidjson", + "robin-hood-hashing", "spatialite-tools", "luajit", + "lz4", "protobuf", "sqlite3", "zlib" From e406c10bbcc01c57a991ec8f3b9beace0113fbe4 Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 5 Feb 2024 17:53:29 +0100 Subject: [PATCH 022/198] move osx ci to github actions (#4554) --- .circleci/config.yml | 57 ++++--------------------- .github/workflows/osx.yml | 88 +++++++++++++++++++++++++++++++++++++++ src/valhalla_run_route.cc | 2 +- 3 files changed, 97 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/osx.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 1e53d1a3d7..baebdbd38c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,19 +1,6 @@ version: 2.1 -executors: - macos: - macos: - xcode: 15.1.0 - environment: - HOMEBREW_NO_AUTO_UPDATE: 1 - CXXFLAGS: -DGEOS_INLINE - commands: - mac_deps: - steps: - - run: brew install autoconf automake protobuf cmake ccache libtool sqlite3 libspatialite luajit curl wget czmq lz4 spatialite-tools unzip boost - - run: sudo python3 -m pip install requests shapely - - run: git clone https://github.com/kevinkreiser/prime_server --recurse-submodules && cd prime_server && ./autogen.sh && ./configure && make -j8 && sudo make install linux_deps: steps: - run: ./scripts/install-linux-deps.sh @@ -40,7 +27,8 @@ jobs: - restore_cache: keys: - ccache-debug-linux-x86_64-v3-{{ .Branch }} - - ccache-debug-linux-x86_64-v3 + - ccache-debug-linux-x86_64-v3-master + - ccache-debug-linux-x86_64-v3- - run: | mkdir build # NOTE: -Werror disabled in CI, as we currently have >4k warnings. @@ -53,13 +41,12 @@ jobs: - run: make -C build utrecht_tiles - run: make -C build -j8 tests - run: make -C build -j8 benchmarks - - run: make -C build -j8 run-benchmarks # Note: we save the cache here before doing linting so that if linting fails, we can rebuild quickly # for follow-up fixes - save_cache: key: ccache-debug-linux-x86_64-v3-{{ .Branch }}-{{ epoch }} paths: - - ~/.ccache + - ~/.cache/ccache - run: scripts/clang-tidy-only-diff.sh 4 - run: sudo make -C build install - run: make -C build package @@ -81,6 +68,7 @@ jobs: - restore_cache: keys: - ccache-release-linux-x86_64-v3-{{ .Branch }} + - ccache-release-linux-x86_64-v3-master - ccache-release-linux-x86_64-v3 - run: | mkdir build @@ -93,12 +81,10 @@ jobs: - run: make -C build -j8 tests # leaks in glibc we cant control for - run: export ASAN_OPTIONS=detect_leaks=0 && make -C build -j8 check - - run: make -C build -j8 benchmarks - - run: make -C build -j8 run-benchmarks - save_cache: key: ccache-release-linux-x86_64-v3-{{ .Branch }}-{{ epoch }} paths: - - ~/.ccache + - ~/.cache/ccache - run: sudo make -C build install - run: make -C build package @@ -115,6 +101,7 @@ jobs: - restore_cache: keys: - ccache-release-linux-arm_64-v3-{{ .Branch }} + - ccache-release-linux-arm_64-v3-master - ccache-release-linux-arm_64-v3 - run: | mkdir build @@ -127,37 +114,13 @@ jobs: # leaks in glibc we cant control for - run: export ASAN_OPTIONS=detect_leaks=0 && make -C build -j8 check - run: make -C build -j8 benchmarks - - run: make -C build -j8 run-benchmarks - save_cache: - key: ccache-release-linux-arm_64-v3-{{ .Branch }}-{{ epoch }} + key: ccache-release-linux-arm_64-v3-{{ .Branch }} paths: - - ~/.ccache + - ~/.cache/ccache - run: sudo make -C build install - run: make -C build package - build-osx: - executor: macos - resource_class: macos.m1.medium.gen1 - steps: - - checkout - - mac_deps - - run: git submodule sync && git submodule update --init - - restore_cache: - keys: - - ccache-release-macos-{{ .Branch }} - - ccache-release-macos - - run: cmake -B build -DENABLE_SINGLE_FILES_WERROR=OFF - - run: make -C build -j8 - - run: make -C build utrecht_tiles - - run: make -C build -j8 tests - - run: make -C build -j8 check - - run: make -C build -j8 benchmarks - - run: make -C build run-benchmarks - - save_cache: - key: ccache-release-macos-{{ epoch }} - paths: - - ~/.ccache - workflows: version: 2 build_test_publish: @@ -174,7 +137,3 @@ workflows: filters: tags: ignore: /.*/ - - build-osx: - filters: - tags: - ignore: /.*/ diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml new file mode 100644 index 0000000000..0f1a714cae --- /dev/null +++ b/.github/workflows/osx.yml @@ -0,0 +1,88 @@ +name: OSX CI +on: + push: + paths-ignore: + - '*.md' + - .circleci/ + - bench/ + - docs/ + - run_route_scripts/ + - test_requests/ + branches: + - master + pull_request: + paths-ignore: + - '*.md' + - .circleci/ + - bench/ + - docs/ + - run_route_scripts/ + - test_requests/ + branches: + - master + workflow_dispatch: + inputs: + debug_enabled: + type: boolean + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: false + +jobs: + build: + runs-on: macos-14 + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + HOMEBREW_NO_AUTO_UPDATE=1 brew install autoconf automake protobuf cmake ccache libtool sqlite3 libspatialite luajit curl wget czmq lz4 spatialite-tools unzip boost + sudo python -m pip install --break-system-packages requests shapely + git clone https://github.com/kevinkreiser/prime_server --recurse-submodules && cd prime_server && ./autogen.sh && ./configure && make -j$(sysctl -n hw.logicalcpu) && sudo make install + + - name: Get branch name + run: echo "VALHALLA_BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_ENV + + - name: Restore ccache + id: cache-ccache-restore + uses: actions/cache/restore@v4 + with: + path: ~/Library/Caches/ccache + key: ccache-osx-${{ env.VALHALLA_BRANCH }} + restore-keys: | + ccache-osx-master + ccache-osx- + + - name: Configure CMake + run: cmake -B build -DENABLE_SINGLE_FILES_WERROR=OFF + + - name: Build Valhalla + run: make -C build -j$(sysctl -n hw.logicalcpu) + + - name: Build Tests + run: make -C build -j$(sysctl -n hw.logicalcpu) tests + + - name: Run Tests + run: make -C build -j$(sysctl -n hw.logicalcpu) check + + - name: Save ccache + id: cache-ccache-save + uses: actions/cache/save@v4 + with: + path: ~/Library/Caches/ccache + key: ${{ steps.cache-ccache-restore.outputs.cache-primary-key }} + + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + # only run this if manually invoked or a previous job failed + if: ${{ (github.event_name == 'workflow_dispatch' && inputs.debug_enabled) || failure() }} + with: + detached: true + timeout-minutes: 15 + diff --git a/src/valhalla_run_route.cc b/src/valhalla_run_route.cc index 19193fae29..e3af0f90dc 100644 --- a/src/valhalla_run_route.cc +++ b/src/valhalla_run_route.cc @@ -227,7 +227,7 @@ const valhalla::TripLeg* PathTest(GraphReader& reader, if (ret) { LOG_INFO("RouteMatcher succeeded"); } else { - LOG_ERROR("RouteMatcher failed"); + LOG_ERROR("RouteMatcher failed."); } } From d642158c07e31a19d378fe9b3628c016882a637d Mon Sep 17 00:00:00 2001 From: Nils Date: Tue, 6 Feb 2024 14:30:46 +0100 Subject: [PATCH 023/198] fix config generator with thor.costmatrix_allow_second_pass (#4567) --- CHANGELOG.md | 1 + scripts/valhalla_build_config | 4 ++-- src/tyr/route_serializer_osrm.cc | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1a386b516..b52d529336 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ * FIXED: set capped speed for truck at 90 KPH [#4493](https://github.com/valhalla/valhalla/pull/4493) * FIXED: Config singleton multiple instantiation issue [#4521](https://github.com/valhalla/valhalla/pull/4521) * FIXED: Prevent GetShortcut to run into an infinite loop [#4532](https://github.com/valhalla/valhalla/pull/4532) + * FIXED: fix config generator with thor.costmatrix_allow_second_pass [#4567](https://github.com/valhalla/valhalla/pull/4567) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/scripts/valhalla_build_config b/scripts/valhalla_build_config index 862409da38..651e0d2a24 100755 --- a/scripts/valhalla_build_config +++ b/scripts/valhalla_build_config @@ -119,7 +119,7 @@ config = { 'max_reserved_labels_count_dijkstras': 4000000, 'max_reserved_labels_count_bidir_dijkstras': 2000000, 'costmatrix_check_reverse_connection': False, - 'costmatrix_second_pass': False, + 'costmatrix_allow_second_pass': False, 'max_reserved_locations_costmatrix': 25, 'clear_reserved_memory': False, 'extended_search': False, @@ -371,7 +371,7 @@ help_text = { }, 'source_to_target_algorithm': 'Which matrix algorithm should be used, one of "timedistancematrix" or "costmatrix". If blank, the optimal will be selected.', 'costmatrix_check_reverse_connection': 'Whether to check for expansion connections on the reverse tree, which has an adverse effect on performance', - 'costmatrix_second_pass': "Whether to allow a second pass for unfound CostMatrix connections, where we turn off destination-only, relax hierarchies and expand into 'semi-islands'b", + 'costmatrix_allow_second_pass': "Whether to allow a second pass for unfound CostMatrix connections, where we turn off destination-only, relax hierarchies and expand into 'semi-islands'b", 'service': {'proxy': 'IPC linux domain socket file location'}, 'max_reserved_labels_count_astar': 'Maximum capacity allowed to keep reserved for unidirectional A*.', 'max_reserved_labels_count_bidir_astar': 'Maximum capacity allowed to keep reserved for bidirectional A*.', diff --git a/src/tyr/route_serializer_osrm.cc b/src/tyr/route_serializer_osrm.cc index 2ad579d061..540152216d 100644 --- a/src/tyr/route_serializer_osrm.cc +++ b/src/tyr/route_serializer_osrm.cc @@ -567,7 +567,7 @@ json::ArrayPtr intersections(const valhalla::DirectionsLeg::Maneuver& maneuver, // Sort edges by increasing bearing and update the in/out edge indexes std::sort(edges.begin(), edges.end()); - uint32_t incoming_index, outgoing_index; + uint32_t incoming_index = 0, outgoing_index = 0; for (uint32_t n = 0; n < edges.size(); ++n) { if (edges[n].in_edge) { incoming_index = n; From 1c2ff987d76a54e994e3e3edcaa99370d1e8f9a3 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Thu, 8 Feb 2024 19:55:04 -0500 Subject: [PATCH 024/198] Add missing `break` to graphconstants.h (#4577) --- valhalla/baldr/graphconstants.h | 1 + 1 file changed, 1 insertion(+) diff --git a/valhalla/baldr/graphconstants.h b/valhalla/baldr/graphconstants.h index 298da5d42c..ca950dbc71 100644 --- a/valhalla/baldr/graphconstants.h +++ b/valhalla/baldr/graphconstants.h @@ -764,6 +764,7 @@ inline float GetOffsetForHeading(RoadClass road_class, Use use) { case Use::kPedestrian: case Use::kBridleway: { offset *= 0.5f; + break; } default: break; From 129c55b2c53bd5ac97aa236e3ef06890c1f7a093 Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Fri, 9 Feb 2024 13:55:51 -0500 Subject: [PATCH 025/198] attempt to fix infinite loop in isochrones generation (#4547) --- .github/workflows/{syntax.yml => lint.yml} | 2 +- CHANGELOG.md | 1 + src/thor/isochrone.cc | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) rename .github/workflows/{syntax.yml => lint.yml} (99%) diff --git a/.github/workflows/syntax.yml b/.github/workflows/lint.yml similarity index 99% rename from .github/workflows/syntax.yml rename to .github/workflows/lint.yml index 50a9caaccd..2fe16a7453 100644 --- a/.github/workflows/syntax.yml +++ b/.github/workflows/lint.yml @@ -1,4 +1,4 @@ -name: syntax +name: lint on: [pull_request] jobs: diff --git a/CHANGELOG.md b/CHANGELOG.md index b52d529336..37c48d4d12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ * FIXED: Config singleton multiple instantiation issue [#4521](https://github.com/valhalla/valhalla/pull/4521) * FIXED: Prevent GetShortcut to run into an infinite loop [#4532](https://github.com/valhalla/valhalla/pull/4532) * FIXED: fix config generator with thor.costmatrix_allow_second_pass [#4567](https://github.com/valhalla/valhalla/pull/4567) + * FIXED: infinite loop or other random corruption in isochrones when retrieving partial shape of an edge [#4547](https://github.com/valhalla/valhalla/pull/4547) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/thor/isochrone.cc b/src/thor/isochrone.cc index 4550f60d0b..26b51f31d3 100644 --- a/src/thor/isochrone.cc +++ b/src/thor/isochrone.cc @@ -17,10 +17,18 @@ constexpr float METRIC_PADDING = 10.f; template std::vector> OriginEdgeShape(const std::vector>& pts, double distance_along) { + // just the endpoint really + if (distance_along == 0) + return {pts.back(), pts.back()}; + + // consume shape until we reach the desired distance double suffix_len = 0; for (auto from = std::next(pts.rbegin()), to = pts.rbegin(); from != pts.rend(); ++from, ++to) { + // add whatever this segment of shape contributes to the overall distance PrecisionT len = from->Distance(*to); suffix_len += len; + + // we have enough distance now, lets find the exact stopping point along the geom if (suffix_len >= distance_along) { auto interpolated = from->PointAlongSegment(*to, (suffix_len - distance_along) / len); std::vector> res(pts.rbegin(), from); @@ -29,6 +37,8 @@ std::vector> OriginEdgeShape(const std::vector Date: Sun, 11 Feb 2024 16:14:14 +0100 Subject: [PATCH 026/198] OSRM Serializer: Adds voice instructions (#4506) --- CHANGELOG.md | 1 + docs/docs/api/turn-by-turn/api-reference.md | 5 +- proto/options.proto | 1 + src/tyr/route_serializer_osrm.cc | 158 +++++++++++++++++ src/worker.cc | 4 + test/gurka/test_osrm_serializer.cc | 180 ++++++++++++++++++++ 6 files changed, 347 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37c48d4d12..f2c8e471a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,7 @@ * CHANGED: Do not reclassify ferry connections when no hierarchies are to be generated [#4487](https://github.com/valhalla/valhalla/pull/4487) * ADDED: Added a config option to sort nodes spatially during graph building [#4455](https://github.com/valhalla/valhalla/pull/4455) * ADDED: Timezone info in route and matrix responses [#4491](https://github.com/valhalla/valhalla/pull/4491) + * ADDED: Support for `voiceInstructions` attribute in OSRM serializer via `voice_instructions` request parameter [#4506](https://github.com/valhalla/valhalla/pull/4506) * CHANGED: use pkg-config to find spatialite & geos and remove our cmake modules; upgraded conan's boost to 1.83.0 in the process [#4253](https://github.com/valhalla/valhalla/pull/4253) * ADDED: Added aggregation logic to filter stage of tile building [#4512](https://github.com/valhalla/valhalla/pull/4512) * UPDATED: tz to 2023d [#4519](https://github.com/valhalla/valhalla/pull/4519) diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index c2055973e2..6fdc13a4fe 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -271,12 +271,13 @@ Directions options should be specified at the top level of the JSON object. | `directions_type` | An enum with 3 values.
  • `none` indicating no maneuvers or instructions should be returned.
  • `maneuvers` indicating that only maneuvers be returned.
  • `instructions` indicating that maneuvers with instructions should be returned (this is the default if not specified).
| | `format` | Four options are available:
  • `json` is default valhalla routing directions JSON format
  • `gpx` returns the route as a GPX (GPS exchange format) XML track
  • `osrm` creates a OSRM compatible route directions JSON
  • `pbf` formats the result using protocol buffers
| | `banner_instructions` | If the format is `osrm`, this boolean indicates if each step should have the additional `bannerInstructions` attribute, which can be displayed in some navigation system SDKs. | +| `voice_instructions` | If the format is `osrm`, this boolean indicates if each step should have the additional `voiceInstructions` attribute, which can be heard in some navigation system SDKs. | | `alternates` | A number denoting how many alternate routes should be provided. There may be no alternates or less alternates than the user specifies. Alternates are not yet supported on multipoint routes (that is, routes with more than 2 locations). They are also not supported on time dependent routes. | -For example a bus request with the result in Spanish using the OSRM (Open Source Routing Machine) format with the additional bannerInstructions in the steps would use the following json: +For example a bus request with the result in Spanish using the OSRM (Open Source Routing Machine) format with the additional bannerInstructions and voiceInstructions in the steps would use the following json: ```json -{"locations":[{"lat":40.730930,"lon":-73.991379},{"lat":40.749706,"lon":-73.991562}],"format":"osrm","costing":"bus","banner_instructions":true,"language":"es-ES"} +{"locations":[{"lat":40.730930,"lon":-73.991379},{"lat":40.749706,"lon":-73.991562}],"format":"osrm","costing":"bus","banner_instructions":true,"voice_instructions":true,"language":"es-ES"} ``` ##### Supported language tags diff --git a/proto/options.proto b/proto/options.proto index e7bdc3a478..b69f654c32 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -488,4 +488,5 @@ message Options { // or when CostMatrix is the selected matrix mode. bool banner_instructions = 55; // Whether to return bannerInstructions in the OSRM serializer response float elevation_interval = 56; // Interval for sampling elevation along the route path. [default = 0.0]; + bool voice_instructions = 57; // Whether to return voiceInstructions in the OSRM serializer response } diff --git a/src/tyr/route_serializer_osrm.cc b/src/tyr/route_serializer_osrm.cc index 540152216d..dcef6ca96c 100644 --- a/src/tyr/route_serializer_osrm.cc +++ b/src/tyr/route_serializer_osrm.cc @@ -96,6 +96,10 @@ const constexpr PointLL::first_type DOUGLAS_PEUCKER_THRESHOLDS[19] = { 2.6, // z18 }; +const constexpr double SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION = 15.0; +const constexpr double SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION = 5.0; +const constexpr double APPROXIMATE_VERBAL_POSTRANSITION_LENGTH = 110; + inline double clamp(const double lat) { return std::max(std::min(lat, double(EPSG3857_MAX_LATITUDE)), double(-EPSG3857_MAX_LATITUDE)); } @@ -1424,6 +1428,147 @@ void maneuver_geometry(json::MapPtr& step, } } +// The idea is that the instructions come a fixed amount of seconds before the maneuver takes place. +// For whatever reasons, a distance in meters from the end of the maneuver needs to be provided +// though. When different speeds are used on the road, they all need to be taken into account. This +// function calculates the distance before the end of the maneuver by checking the elapsed_cost +// seconds of each edges and accumulates their distances until the seconds threshold is passed. The +// speed of this last edge is then used to subtract the distance so that the the seconds until the end +// are exactly the provided amount of seconds. +float distance_along_geometry(const valhalla::DirectionsLeg::Maneuver* prev_maneuver, + valhalla::odin::EnhancedTripLeg* etp, + const double distance, + const uint32_t target_seconds) { + uint32_t node_index = prev_maneuver->end_path_index(); + double end_node_elapsed_seconds = etp->node(node_index).cost().elapsed_cost().seconds(); + double begin_node_elapsed_seconds = + etp->node(prev_maneuver->begin_path_index()).cost().elapsed_cost().seconds(); + + // If the maneuver is too short, simply return its distance. + if (end_node_elapsed_seconds - begin_node_elapsed_seconds < target_seconds) { + return distance; + } + + float accumulated_distance_km = 0; + float previous_accumulated_distance_km = 0; + double accumulated_seconds = 0; + double previous_accumulated_seconds = 0; + // Find the node after which the instructions should be heard: + while (accumulated_seconds < target_seconds && node_index >= prev_maneuver->begin_path_index()) { + node_index -= 1; + // not really accumulating seconds ourselves, but it happens elsewhere: + previous_accumulated_seconds = accumulated_seconds; + accumulated_seconds = + end_node_elapsed_seconds - etp->node(node_index).cost().elapsed_cost().seconds(); + previous_accumulated_distance_km = accumulated_distance_km; + accumulated_distance_km += etp->GetCurrEdge(node_index)->length_km(); + } + // The node_index now indicates the node AFTER which the target_seconds will be reached + // we now have to subtract the surplus distance (based on seconds) of this edge from the + // accumulated_distance_km + auto surplus_percentage = + (accumulated_seconds - target_seconds) / (accumulated_seconds - previous_accumulated_seconds); + accumulated_distance_km -= + (accumulated_distance_km - previous_accumulated_distance_km) * surplus_percentage; + if (accumulated_distance_km * 1000 > distance) { + return distance; + } else { + return accumulated_distance_km * 1000; // in meters + } +} + +// Populate the voiceInstructions within a step. +json::ArrayPtr voice_instructions(const valhalla::DirectionsLeg::Maneuver* prev_maneuver, + const valhalla::DirectionsLeg::Maneuver& maneuver, + const double distance, + const uint32_t maneuver_index, + valhalla::odin::EnhancedTripLeg* etp) { + // voiceInstructions is an array, because there may be similar voice instructions. + // When the step is long enough, there may be multiple voice instructions. + json::ArrayPtr voice_instructions_array = json::array({}); + + // distanceAlongGeometry is the distance along the current step from where on this + // voice instruction should be played. It is measured from the end of the maneuver. + // Using the maneuver length (distance) as the distanceAlongGeometry plays + // right at the beginning of the maneuver. A distanceAlongGeometry of 10 is + // shortly (10 meters at the given speed) after the maneuver has started. + // The voice_instruction_beginning starts shortly after the beginning of the step. + // The voice_instruction_end starts shortly before the end of the step. + float distance_before_verbal_transition_alert_instruction = -1; + float distance_before_verbal_pre_transition_instruction = -1; + if (prev_maneuver) { + distance_before_verbal_transition_alert_instruction = + distance_along_geometry(prev_maneuver, etp, distance, + SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION); + distance_before_verbal_pre_transition_instruction = + distance_along_geometry(prev_maneuver, etp, distance, + SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION); + if (maneuver_index == 1 && !prev_maneuver->verbal_pre_transition_instruction().empty()) { + // For depart maneuver, we always want to hear the verbal_pre_transition_instruction + // right at the beginning of the navigation. This is something like: + // Drive West on XYZ Street. + // This voice_instruction_start is only created once. It is always played, even when + // the maneuver would otherwise be too short. + json::MapPtr voice_instruction_start = json::map({}); + voice_instruction_start->emplace("distanceAlongGeometry", json::fixed_t{distance, 1}); + voice_instruction_start->emplace("announcement", + prev_maneuver->verbal_pre_transition_instruction()); + voice_instructions_array->emplace_back(std::move(voice_instruction_start)); + } else if (distance > distance_before_verbal_transition_alert_instruction + + APPROXIMATE_VERBAL_POSTRANSITION_LENGTH && + !prev_maneuver->verbal_post_transition_instruction().empty()) { + // In all other cases we want to play the verbal_post_transition_instruction shortly + // after the maneuver has started but only if there is sufficient time to play both + // the upcoming verbal_pre_transition_instruction and the verbal_post_transition_instruction + // itself. The approximation here is that the verbal_post_transition_instruction takes 100 + // meters to play + the 10 meters after the maneuver start which is added so that the + // instruction is not played directly on the intersection where the maneuver starts. + json::MapPtr voice_instruction_beginning = json::map({}); + voice_instruction_beginning->emplace("distanceAlongGeometry", json::fixed_t{distance - 10, 1}); + voice_instruction_beginning->emplace("announcement", + prev_maneuver->verbal_post_transition_instruction()); + voice_instructions_array->emplace_back(std::move(voice_instruction_beginning)); + } + } + + if (!maneuver.verbal_transition_alert_instruction().empty()) { + json::MapPtr voice_instruction_end = json::map({}); + if (maneuver_index == 1 && distance_before_verbal_transition_alert_instruction == distance) { + // For the depart maneuver we want to play both the verbal_post_transition_instruction and + // the verbal_transition_alert_instruction even if the maneuver is too short. + voice_instruction_end->emplace("distanceAlongGeometry", json::fixed_t{distance / 2, 1}); + } else { + // In all other cases we use distance_before_verbal_transition_alert_instruction value + // as it is capped to the maneuver length + voice_instruction_end + ->emplace("distanceAlongGeometry", + json::fixed_t{distance_before_verbal_transition_alert_instruction, 1}); + } + voice_instruction_end->emplace("announcement", maneuver.verbal_transition_alert_instruction()); + voice_instructions_array->emplace_back(std::move(voice_instruction_end)); + } + + if (!maneuver.verbal_pre_transition_instruction().empty()) { + json::MapPtr voice_instruction_end = json::map({}); + if (maneuver_index == 1 && distance_before_verbal_pre_transition_instruction >= distance / 2) { + // For the depart maneuver we want to play the verbal_post_transition_instruction, + // the verbal_transition_alert_instruction and + // the verbal_pre_transition_instruction even if the maneuver is too short. + voice_instruction_end->emplace("distanceAlongGeometry", json::fixed_t{distance / 4, 1}); + } else { + // In all other cases we use distance_before_verbal_pre_transition_instruction value + // as it is capped to the maneuver length + voice_instruction_end->emplace("distanceAlongGeometry", + json::fixed_t{distance_before_verbal_pre_transition_instruction, + 1}); + } + voice_instruction_end->emplace("announcement", maneuver.verbal_pre_transition_instruction()); + voice_instructions_array->emplace_back(std::move(voice_instruction_end)); + } + + return voice_instructions_array; +} + // Get the mode std::string get_mode(const valhalla::DirectionsLeg::Maneuver& maneuver, const bool arrive_maneuver, @@ -1700,6 +1845,19 @@ json::ArrayPtr serialize_legs(const google::protobuf::RepeatedPtrFieldemplace("voiceInstructions", + voice_instructions(prev_maneuver, maneuver, prev_distance, + maneuver_index, &etp)); + } + if (arrive_maneuver) { + step->emplace("voiceInstructions", + voice_instructions(prev_maneuver, maneuver, distance, maneuver_index, &etp)); + } + } + // Add junction_name if not the start maneuver std::string junction_name = get_sign_elements(sign.junction_names()); if (!depart_maneuver && !junction_name.empty()) { diff --git a/src/worker.cc b/src/worker.cc index 4c1ee7820c..7c7f33bf00 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -1196,6 +1196,10 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { options.set_banner_instructions( rapidjson::get(doc, "/banner_instructions", options.banner_instructions())); + // whether to return voiceInstructions in OSRM serializer, default false + options.set_voice_instructions( + rapidjson::get(doc, "/voice_instructions", options.voice_instructions())); + // whether to include roundabout_exit maneuvers, default true auto roundabout_exits = rapidjson::get(doc, "/roundabout_exits", diff --git a/test/gurka/test_osrm_serializer.cc b/test/gurka/test_osrm_serializer.cc index a91155d54b..e7b3461a46 100644 --- a/test/gurka/test_osrm_serializer.cc +++ b/test/gurka/test_osrm_serializer.cc @@ -520,6 +520,186 @@ TEST(Standalone, HeadingNumberAutoRoute) { // clang-format on } +class VoiceInstructions : public ::testing::Test { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + constexpr double gridsize_metres = 50; + + const std::string ascii_map = R"( + X Y + \ | --M--N + \ | __ -- ¯¯ + A----------BCD< + | ¯¯ -- __ + | --E--F + Z + )"; + + const gurka::ways ways = + {{"AB", {{"highway", "primary"}, {"maxspeed", "80"}, {"name", "10th Avenue SE"}}}, + {"BC", {{"highway", "primary"}, {"maxspeed", "50"}, {"name", "10th Avenue SE"}}}, + {"CD", {{"highway", "primary"}, {"maxspeed", "30"}, {"name", "10th Avenue SE"}}}, + {"CX", {{"highway", "primary"}, {"maxspeed", "30"}, {"name", "Sidestreet"}}}, + {"DMN", {{"highway", "primary"}, {"maxspeed", "30"}, {"name", "Heinrich Street"}}}, + {"DEF", {{"highway", "primary"}, {"maxspeed", "30"}, {"name", "Alfred Street"}}}, + {"YDZ", {{"highway", "primary"}, {"name", "Market Street"}, {"oneway", "yes"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {0.0, 0.0}); + + const std::unordered_map build_config{ + {"mjolnir.data_processing.use_admin_db", "false"}}; + + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/osrm_serializer_voice", build_config); + } + + rapidjson::Document json_request(const std::string& from, const std::string& to) { + const std::string& request = + (boost::format( + R"({"locations":[{"lat":%s,"lon":%s},{"lat":%s,"lon":%s}],"costing":"auto","voice_instructions":true})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + auto result = gurka::do_action(valhalla::Options::route, map, request); + return gurka::convert_to_json(result, Options::Format::Options_Format_osrm); + } +}; + +gurka::map VoiceInstructions::map = {}; + +TEST_F(VoiceInstructions, VoiceInstructionsPresent) { + auto json = json_request("A", "F"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + // Validate that each step has voiceInstructions with announcement and distanceAlongGeometry + for (int step = 0; step < steps.Size(); ++step) { + ASSERT_TRUE(steps[step].HasMember("voiceInstructions")); + ASSERT_TRUE(steps[step]["voiceInstructions"].IsArray()); + + EXPECT_GT(steps[step]["voiceInstructions"].Size(), 0); + for (int instr = 0; instr < steps[step]["voiceInstructions"].GetArray().Size(); ++instr) { + ASSERT_TRUE(steps[step]["voiceInstructions"][instr].HasMember("announcement")); + ASSERT_TRUE(steps[step]["voiceInstructions"][instr].HasMember("distanceAlongGeometry")); + } + } +} + +// depart_instruction +// +// 13 grids * 50m/grid = 650m +// => distanceAlongGeometry = 650m +// +// verbal_transition_alert_instruction +// +// The idea is that the instructions come a fixed amount of seconds before the maneuver takes place. +// For whatever reasons, a distance in meters from the end of the maneuver needs to be provided +// though. When different speeds are used on the road, they all need to be taken into account. +// +// CD: 50m / 30km/h = 50m * 3,600s / 30,000m = 50m * 0.12s/m = 6s +// BC: 50m / 50km/h = 50m * 3,600s / 50,000m = 50m * 0.072s/m = 3.6s +// SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION = 15s +// AB: 15s - 6s - 3.6s = 5.4s +// 5.4s * 80 km/h = 5.4s * 80,000m / 3600s = 120m +// => distanceAlongGeometry = 120m + 50m + 50m = 220m +// +// verbal_pre_transition_instruction +// +// SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION = 5s +// CD: 5s * 30km/h = 5s * 30,000m / 3600s ~= 42m +TEST_F(VoiceInstructions, DistanceAlongGeometryVoiceInstructions) { + auto json = json_request("A", "D"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + auto depart_instruction = steps[0]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ( + depart_instruction["announcement"].GetString(), + "Drive east on 10th Avenue SE. Then, in 700 meters, You will arrive at your destination."); + EXPECT_EQ(depart_instruction["distanceAlongGeometry"].GetFloat(), 650.0); + auto verbal_transition_alert_instruction = steps[0]["voiceInstructions"][1].GetObject(); + EXPECT_STREQ(verbal_transition_alert_instruction["announcement"].GetString(), + "You will arrive at your destination."); + EXPECT_EQ(round(verbal_transition_alert_instruction["distanceAlongGeometry"].GetFloat()), 220); + auto verbal_pre_transition_instruction = steps[0]["voiceInstructions"][2].GetObject(); + EXPECT_STREQ(verbal_pre_transition_instruction["announcement"].GetString(), + "You have arrived at your destination."); + EXPECT_EQ(round(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat()), 42); +} + +TEST_F(VoiceInstructions, ShortDepartVoiceInstructions) { + auto json = json_request("C", "F"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + EXPECT_EQ(steps[0]["voiceInstructions"].Size(), 3); + + auto depart_instruction = steps[0]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ(depart_instruction["announcement"].GetString(), + "Drive east on 10th Avenue SE. Then Bear right onto Alfred Street."); + EXPECT_EQ(depart_instruction["distanceAlongGeometry"].GetFloat(), 50.0); + auto verbal_transition_alert_instruction = steps[0]["voiceInstructions"][1].GetObject(); + EXPECT_STREQ(verbal_transition_alert_instruction["announcement"].GetString(), + "Bear right onto Alfred Street."); + EXPECT_EQ(verbal_transition_alert_instruction["distanceAlongGeometry"].GetFloat(), 25.0); + auto verbal_pre_transition_instruction = steps[0]["voiceInstructions"][2].GetObject(); + EXPECT_STREQ(verbal_pre_transition_instruction["announcement"].GetString(), + "Bear right onto Alfred Street."); + EXPECT_EQ(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat(), 12.5); +} + +TEST_F(VoiceInstructions, ShortIntermediateStepVoiceInstructions) { + auto json = json_request("X", "Z"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + EXPECT_EQ(steps[1]["voiceInstructions"].Size(), 2); // No verbal_post_transition_instruction + + auto verbal_transition_alert_instruction = steps[1]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ(verbal_transition_alert_instruction["announcement"].GetString(), + "Turn right onto Market Street."); + EXPECT_EQ(verbal_transition_alert_instruction["distanceAlongGeometry"].GetFloat(), 50.0); + + auto verbal_pre_transition_instruction = steps[1]["voiceInstructions"][1].GetObject(); + EXPECT_STREQ(verbal_pre_transition_instruction["announcement"].GetString(), + "Turn right onto Market Street. Then You will arrive at your destination."); + // ~= 38.2 + EXPECT_GT(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat(), 38); + EXPECT_LT(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat(), 39); +} + +TEST_F(VoiceInstructions, AllVoiceInstructions) { + auto json = json_request("A", "F"); + auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); + + auto depart_instruction = steps[0]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ(depart_instruction["announcement"].GetString(), + "Drive east on 10th Avenue SE. Then Bear right onto Alfred Street."); + EXPECT_EQ(depart_instruction["distanceAlongGeometry"].GetFloat(), 650.0); + + auto bear_right_instruction = steps[0]["voiceInstructions"][1].GetObject(); + EXPECT_STREQ(bear_right_instruction["announcement"].GetString(), "Bear right onto Alfred Street."); + EXPECT_EQ(round(bear_right_instruction["distanceAlongGeometry"].GetFloat()), 220); + + auto continue_instruction = steps[1]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ(continue_instruction["announcement"].GetString(), "Continue for 900 meters."); + EXPECT_EQ(continue_instruction["distanceAlongGeometry"].GetFloat(), 847.0); + + auto arrive_instruction = steps[1]["voiceInstructions"][1].GetObject(); + EXPECT_STREQ(arrive_instruction["announcement"].GetString(), + "You will arrive at your destination."); + // ~= 125 + EXPECT_GT(arrive_instruction["distanceAlongGeometry"].GetFloat(), 124); + EXPECT_LT(arrive_instruction["distanceAlongGeometry"].GetFloat(), 126); + + auto final_arrive_instruction = steps[1]["voiceInstructions"][2].GetObject(); + EXPECT_STREQ(final_arrive_instruction["announcement"].GetString(), + "You have arrived at your destination."); + // ~= 42 + EXPECT_GT(final_arrive_instruction["distanceAlongGeometry"].GetFloat(), 41); + EXPECT_LT(final_arrive_instruction["distanceAlongGeometry"].GetFloat(), 43); + + auto last_instruction = steps[2]["voiceInstructions"][0].GetObject(); + EXPECT_STREQ(last_instruction["announcement"].GetString(), "You will arrive at your destination."); + EXPECT_EQ(last_instruction["distanceAlongGeometry"].GetFloat(), 0.0); +} + TEST(Standalone, BannerInstructions) { const std::string ascii_map = R"( A-------------1-B---X From edb4ab080dbb061d03df5b4d07ff78f07a9269f5 Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Sun, 11 Feb 2024 10:49:57 -0500 Subject: [PATCH 027/198] fiddling with tar_index test failures (#4490) --- test/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3415db7c5d..59e9c014f0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,12 +39,12 @@ set(tests aabb2 access_restriction actor admin attributes_controller configurati streetnames_us streetname_us tilehierarchy tiles transitdeparture transitroute transitschedule transitstop turn turnlanes util_midgard util_skadi vector2 verbal_text_formatter verbal_text_formatter_us verbal_text_formatter_us_co verbal_text_formatter_us_tx viterbi_search compression filesystem traffictile - incident_loading worker_nullptr_tiles tar_index curl_tilegetter) + incident_loading worker_nullptr_tiles curl_tilegetter) if(ENABLE_DATA_TOOLS) list(APPEND tests astar astar_bikeshare complexrestriction countryaccess edgeinfobuilder graphbuilder graphparser graphtilebuilder graphreader isochrone predictive_traffic idtable mapmatch matrix matrix_bss minbb multipoint_routes - names node_search reach recover_shortcut refs search servicedays shape_attributes signinfo summary urban + names node_search reach recover_shortcut refs search servicedays shape_attributes signinfo summary urban tar_index thor_worker timedep_paths timeparsing trivial_paths uniquenames util_mjolnir utrecht lua alternates) if(ENABLE_HTTP) list(APPEND tests http_tiles) From 957c06e4c668ae155f9b27612907f46b4ea25f53 Mon Sep 17 00:00:00 2001 From: Nils Date: Sun, 11 Feb 2024 17:35:51 +0100 Subject: [PATCH 028/198] fix win vcpkg caching (#4573) --- .github/workflows/osx.yml | 9 +++---- .github/workflows/win.yml | 57 ++++++++++++++++++++++----------------- docs/docs/building.md | 2 +- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 0f1a714cae..c0db8d0b8b 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -29,7 +29,7 @@ on: default: false jobs: - build: + build_osx: runs-on: macos-14 steps: - uses: actions/checkout@v4 @@ -67,16 +67,15 @@ jobs: - name: Build Tests run: make -C build -j$(sysctl -n hw.logicalcpu) tests - - - name: Run Tests - run: make -C build -j$(sysctl -n hw.logicalcpu) check - name: Save ccache - id: cache-ccache-save uses: actions/cache/save@v4 with: path: ~/Library/Caches/ccache key: ${{ steps.cache-ccache-restore.outputs.cache-primary-key }} + + - name: Run Tests + run: make -C build -j$(sysctl -n hw.logicalcpu) check - name: Setup tmate session uses: mxschmitt/action-tmate@v3 diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index 7f3d785e18..1028bca0b6 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -30,10 +30,6 @@ on: required: false default: false -defaults: - run: - shell: bash - env: BUILD_TYPE: Release MSVC_VERSION: '2022' @@ -42,7 +38,7 @@ env: VCPKG_DISABLE_COMPILER_TRACKING: ON jobs: - build: + build_win: runs-on: windows-latest steps: - uses: actions/checkout@v4 @@ -52,26 +48,18 @@ jobs: # we add a custom triplet to avoid cache misses as much as possible # https://github.com/microsoft/vcpkg/issues/26346#issuecomment-1319244766 - name: Configure vckpg + shell: bash run: | - echo "VCPKG_ROOT=${{ github.workspace }}/vcpkg" >> $GITHUB_ENV echo "VCPKG_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake" >> $GITHUB_ENV echo "VCPKG_OVERLAY_TRIPLETS=${{ github.workspace }}/vcpkg/custom-triplets" >> $GITHUB_ENV echo "VCPKG_DEFAULT_TRIPLET=custom-x64-windows" >> $GITHUB_ENV echo "VCPKG_DEFAULT_BINARY_CACHE=${{ github.workspace }}/vcpkg/archives" >> $GITHUB_ENV - - name: Cache vcpkg - id: cache-vcpkg - uses: actions/cache@v3 - with: - path: ${{ env.VCPKG_ROOT }} - key: vcpkg=${{ env.VCPKG_VERSION }}-msvc=${{ env.MSVC_VERSION }}-json=${{ hashFiles('vcpkg.json') }}-v3 - enableCrossOsArchive: true - - name: Install GNU make & awk run: choco install gawk make - - if: ${{ steps.cache-vcpkg.outputs.cache-hit != 'true' }} - name: Bootstrap vcpkg and install packages (if cache miss) + - name: Install vcpkg + shell: bash run: | git clone https://github.com/microsoft/vcpkg.git cd vcpkg @@ -83,16 +71,26 @@ jobs: echo "set(VCPKG_BUILD_TYPE release)" >> "$TRIPLET_FILE" echo "set(VCPKG_DISABLE_COMPILER_TRACKING $VCPKG_DISABLE_COMPILER_TRACKING)" >> "$TRIPLET_FILE" cmd.exe /c bootstrap-vcpkg.bat - vcpkg.exe install \ - --vcpkg-root="$VCPKG_ROOT" \ - --overlay-triplets="$VCPKG_OVERLAY_TRIPLETS" \ - --triplet="$VCPKG_DEFAULT_TRIPLET" \ - $VCPKG_INSTALL_OPTIONS + + # make sure we save vcpkg packages even if build fails + # note, we don't use vcpkg "command line" mode, but "cmake manifest" mode + - name: Restore vcpkg packages + id: vcpkg-restore + uses: actions/cache/restore@v3 + with: + key: vcpkg=${{ env.VCPKG_VERSION }}-msvc=${{ env.MSVC_VERSION }}-json=${{ hashFiles('vcpkg.json') }} + path: | + vcpkg/* + !vcpkg/downloads + !vcpkg/docs + !vcpkg/buildtrees + vcpkg/downloads/tools - name: Setup Developer Command Prompt for VS uses: ilammy/msvc-dev-cmd@v1 - name: Configure CMake + shell: bash run: | cmake --version cmake -B build \ @@ -111,14 +109,26 @@ jobs: -DENABLE_CCACHE=OFF \ -DENABLE_SERVICES=OFF \ -DENABLE_BENCHMARKS=OFF \ - -DPREFER_EXTERNAL=ON \ + -DPREFER_EXTERNAL_DEPS=ON + + - if: ${{ steps.vcpkg-restore.outputs.cache-hit != 'true' }} + name: Save vcpkg packages (if cache miss) + uses: actions/cache/save@v3 + with: + key: ${{ steps.vcpkg-restore.outputs.cache-primary-key }} + path: | + vcpkg/* + !vcpkg/downloads + !vcpkg/docs + !vcpkg/buildtrees + vcpkg/downloads/tools - name: Build Valhalla - shell: powershell run: | cmake --build build --config Release -- /clp:ErrorsOnly /p:BuildInParallel=true /m:4 - name: Test Executable + shell: bash run: | set PATH=$PATH:${{ github.workspace }}/build/vcpkg_installed/$BUILD_TYPE/bin ./build/$BUILD_TYPE/valhalla_build_tiles.exe -c ./test/win/valhalla.json ./test/data/utrecht_netherlands.osm.pbf @@ -130,5 +140,4 @@ jobs: if: ${{ (github.event_name == 'workflow_dispatch' && inputs.debug_enabled) || failure() }} with: detached: true - timeout-minutes: 15 diff --git a/docs/docs/building.md b/docs/docs/building.md index 06bd1c0800..30ad5fde32 100644 --- a/docs/docs/building.md +++ b/docs/docs/building.md @@ -44,8 +44,8 @@ git clone https://github.com/microsoft/vcpkg && git -C vcpkg checkout echo "VCPKG_BUILD_TYPE release" >> vcpkg/triplets/x64-linux.cmake # windows: echo.set(VCPKG_BUILD_TYPE release)>> .\vcpkg\triplets\x64-windows.cmake # osx: echo "VCPKG_BUILD_TYPE release" >> vcpkg/triplets/arm64-osx.cmake -./vcpkg/vcpkg install +# vcpkg will install everything during cmake configuration # if you want to ENABLE_SERVICES=ON, install https://github.com/kevinkreiser/prime_server#build-and-install (no Windows) cmake -B build -DCMAKE_TOOLCHAIN_FILE=$PWD/vcpkg/scripts/buildsystems/vcpkg.cmake -DENABLE_SERVICE=OFF cmake --build build -- -j$(nproc) From ba5730e9c7caf03a9a3e35032cd5c626f9877a09 Mon Sep 17 00:00:00 2001 From: Nils Date: Sun, 11 Feb 2024 22:02:18 +0100 Subject: [PATCH 029/198] remove bench (#4560) Co-authored-by: Kevin Kreiser --- .circleci/config.yml | 6 +- .github/workflows/mingw-build.yml | 1 - .github/workflows/osx.yml | 2 - .github/workflows/win.yml | 3 - .gitmodules | 3 - CHANGELOG.md | 1 + CMakeLists.txt | 6 - Dockerfile | 2 +- bench/CMakeLists.txt | 41 -- bench/meili/CMakeLists.txt | 1 - bench/meili/config.json | 111 ----- bench/meili/fixtures/3km_loop_utrecht.json | 102 ----- .../fixtures/intersection_matching1.json | 17 - .../fixtures/intersection_matching2.json | 27 -- .../fixtures/intersection_matching3.json | 32 -- bench/meili/mapmatch.cc | 122 ----- bench/thor/CMakeLists.txt | 4 - bench/thor/costmatrix.cc | 118 ----- bench/thor/isochrone.cc | 58 --- bench/thor/reach.cc | 52 --- bench/thor/routes.cc | 426 ------------------ codecov.yml | 1 - scripts/format.sh | 2 +- third_party/benchmark | 1 - 24 files changed, 5 insertions(+), 1134 deletions(-) delete mode 100644 bench/CMakeLists.txt delete mode 100644 bench/meili/CMakeLists.txt delete mode 100644 bench/meili/config.json delete mode 100644 bench/meili/fixtures/3km_loop_utrecht.json delete mode 100644 bench/meili/fixtures/intersection_matching1.json delete mode 100644 bench/meili/fixtures/intersection_matching2.json delete mode 100644 bench/meili/fixtures/intersection_matching3.json delete mode 100644 bench/meili/mapmatch.cc delete mode 100644 bench/thor/CMakeLists.txt delete mode 100644 bench/thor/costmatrix.cc delete mode 100644 bench/thor/isochrone.cc delete mode 100644 bench/thor/reach.cc delete mode 100644 bench/thor/routes.cc delete mode 160000 third_party/benchmark diff --git a/.circleci/config.yml b/.circleci/config.yml index baebdbd38c..8dd5cde962 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,7 +40,6 @@ jobs: - run: make -C build -j8 - run: make -C build utrecht_tiles - run: make -C build -j8 tests - - run: make -C build -j8 benchmarks # Note: we save the cache here before doing linting so that if linting fails, we can rebuild quickly # for follow-up fixes - save_cache: @@ -75,7 +74,7 @@ jobs: cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=On -DENABLE_PYTHON_BINDINGS=On -DLOGGING_LEVEL=TRACE \ -DCPACK_GENERATOR=DEB -DCPACK_PACKAGE_VERSION_SUFFIX="-0ubuntu1-$(lsb_release -sc)" -DENABLE_SANITIZERS=ON \ - -DBENCHMARK_ENABLE_WERROR=Off -DENABLE_SINGLE_FILES_WERROR=Off + -DENABLE_SINGLE_FILES_WERROR=Off - run: make -C build -j8 - run: make -C build utrecht_tiles - run: make -C build -j8 tests @@ -107,13 +106,12 @@ jobs: mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=On -DENABLE_PYTHON_BINDINGS=On -DCPACK_GENERATOR=DEB \ - -DCPACK_PACKAGE_VERSION_SUFFIX="-0ubuntu1-$(lsb_release -sc)" -DBENCHMARK_ENABLE_WERROR=Off -DENABLE_SINGLE_FILES_WERROR=Off + -DCPACK_PACKAGE_VERSION_SUFFIX="-0ubuntu1-$(lsb_release -sc)" -DENABLE_SINGLE_FILES_WERROR=Off - run: make -C build -j8 - run: make -C build utrecht_tiles - run: make -C build -j8 tests # leaks in glibc we cant control for - run: export ASAN_OPTIONS=detect_leaks=0 && make -C build -j8 check - - run: make -C build -j8 benchmarks - save_cache: key: ccache-release-linux-arm_64-v3-{{ .Branch }} paths: diff --git a/.github/workflows/mingw-build.yml b/.github/workflows/mingw-build.yml index 2f28bdbfb4..18cac6ffc7 100644 --- a/.github/workflows/mingw-build.yml +++ b/.github/workflows/mingw-build.yml @@ -39,7 +39,6 @@ jobs: -DENABLE_PYTHON_BINDINGS=ON \ -DENABLE_CCACHE=OFF \ -DENABLE_TESTS=OFF \ - -DENABLE_BENCHMARKS=OFF \ -DLOGGING_LEVEL=DEBUG \ -DBoost_PROGRAM_OPTIONS_LIBRARY=/usr/x86_64-w64-mingw32/sys-root/mingw/lib/libboost_program_options-mt-x64.dll.a \ -DPYTHON_EXECUTABLE=/usr/x86_64-w64-mingw32/bin/python3 \ diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index c0db8d0b8b..004233182c 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -4,7 +4,6 @@ on: paths-ignore: - '*.md' - .circleci/ - - bench/ - docs/ - run_route_scripts/ - test_requests/ @@ -14,7 +13,6 @@ on: paths-ignore: - '*.md' - .circleci/ - - bench/ - docs/ - run_route_scripts/ - test_requests/ diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index 1028bca0b6..80a0abcc14 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -4,7 +4,6 @@ on: paths-ignore: - '*.md' - .circleci/ - - bench/ - docs/ - run_route_scripts/ - test/ @@ -15,7 +14,6 @@ on: paths-ignore: - '*.md' - .circleci/ - - bench/ - docs/ - run_route_scripts/ - test/ @@ -108,7 +106,6 @@ jobs: -DENABLE_TESTS=OFF \ -DENABLE_CCACHE=OFF \ -DENABLE_SERVICES=OFF \ - -DENABLE_BENCHMARKS=OFF \ -DPREFER_EXTERNAL_DEPS=ON - if: ${{ steps.vcpkg-restore.outputs.cache-hit != 'true' }} diff --git a/.gitmodules b/.gitmodules index 2524d3e61a..862b33e230 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,9 +22,6 @@ [submodule "third_party/microtar"] path = third_party/microtar url = https://github.com/rxi/microtar.git -[submodule "third_party/benchmark"] - path = third_party/benchmark - url = https://github.com/google/benchmark [submodule "third_party/robin-hood-hashing"] path = third_party/robin-hood-hashing url = https://github.com/martinus/robin-hood-hashing.git diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c8e471a8..f04230e0dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * **Removed** * REMOVED: needs_ci_run script [#4423](https://github.com/valhalla/valhalla/pull/4423) * REMOVED: unused vehicle types in AutoCost and segway; renamed kTruck to "truck" instead of "tractor_trailer" [#4430](https://github.com/valhalla/valhalla/pull/4430) + * REMOVED: ./bench and related files/code [#4560](https://github.com/valhalla/valhalla/pull/4560) * **Bug Fix** * FIXED: gcc13 was missing some std header includes [#4154](https://github.com/valhalla/valhalla/pull/4154) * FIXED: when reclassifying ferry edges, remove destonly from ways only if the connecting way was destonly [#4118](https://github.com/valhalla/valhalla/pull/4118) diff --git a/CMakeLists.txt b/CMakeLists.txt index 50905cebb3..63ccd718c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,6 @@ option(ENABLE_ADDRESS_SANITIZER "Use memory sanitizer for Debug build" OFF) option(ENABLE_UNDEFINED_SANITIZER "Use UB sanitizer for Debug build" OFF) option(ENABLE_TESTS "Enable Valhalla tests" ON) option(ENABLE_WERROR "Convert compiler warnings to errors. Requires ENABLE_COMPILER_WARNINGS=ON to take effect" OFF) -option(ENABLE_BENCHMARKS "Enable microbenchmarking" ON) option(ENABLE_THREAD_SAFE_TILE_REF_COUNT "If ON uses shared_ptr as tile reference(i.e. it is thread safe)" OFF) option(ENABLE_SINGLE_FILES_WERROR "Convert compiler warnings to errors for single files" ON) option(PREFER_EXTERNAL_DEPS "Whether to use internally vendored headers or find the equivalent external package" OFF) @@ -333,11 +332,6 @@ if(ENABLE_TESTS) add_subdirectory(test) endif() -# NOTE(mookerji): Windows CI seems to break on the gbench build, so shelve Win32 support for now. -if(ENABLE_BENCHMARKS AND ENABLE_DATA_TOOLS AND NOT WIN32) - add_subdirectory(bench) -endif() - ## Coverage report targets if(ENABLE_COVERAGE) find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat) diff --git a/Dockerfile b/Dockerfile index 52d940f2e3..4e14a12aec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,7 +33,7 @@ RUN rm -rf build && mkdir build # configure the build with symbols turned on so that crashes can be triaged WORKDIR /usr/local/src/valhalla/build # switch back to -DCMAKE_BUILD_TYPE=RelWithDebInfo and uncomment the block below if you want debug symbols -RUN cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc -DENABLE_SINGLE_FILES_WERROR=Off -DBENCHMARK_ENABLE_WERROR=Off +RUN cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc -DENABLE_SINGLE_FILES_WERROR=Off RUN make all -j${CONCURRENCY:-$(nproc)} RUN make install diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt deleted file mode 100644 index ceab3775a5..0000000000 --- a/bench/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -set(BENCHMARK_ENABLE_TESTING OFF) -add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/benchmark - ${CMAKE_BINARY_DIR}/benchmark) -set_target_properties(benchmark PROPERTIES FOLDER "Dependencies") -set_target_properties(benchmark_main PROPERTIES FOLDER "Dependencies") - -# Custom targets building and running all microbenchmarks in the project -add_custom_target(benchmarks) -set_target_properties(benchmarks PROPERTIES FOLDER "Benchmarks") -add_custom_target(run-benchmarks) -set_target_properties(run-benchmarks PROPERTIES FOLDER "Benchmarks") - -# Benchmarks generally require utrecht test tiles to be present, so add this dependency by default. -add_dependencies(benchmarks utrecht_tiles) - -# Macro for defining Google Benchmark microbenchmark targets -macro (add_valhalla_benchmark target_file) - set(target_name benchmark-${target_file}) - add_executable(${target_name} - ${target_file}.cc) - set_target_properties(${target_name} PROPERTIES FOLDER "Benchmarks") - set_target_properties(${target_name} - PROPERTIES - COMPILE_DEFINITIONS - VALHALLA_SOURCE_DIR="${VALHALLA_SOURCE_DIR}/") - target_link_libraries(${target_name} valhalla_test benchmark::benchmark) - add_dependencies(benchmarks ${target_name}) - add_dependencies(${target_name} utrecht_tiles) - # Add a custom target running the benchmark - add_custom_target(run-${target_name} - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${target_name} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMENT "Running ${target_name} in ${CMAKE_CURRENT_BINARY_DIR}" - DEPENDS ${target_name}) - set_target_properties(run-${target_name} PROPERTIES FOLDER "Benchmarks") - add_dependencies(run-${target_name} utrecht_tiles) - add_dependencies(run-benchmarks run-${target_name}) -endmacro() - -add_subdirectory(meili) -add_subdirectory(thor) diff --git a/bench/meili/CMakeLists.txt b/bench/meili/CMakeLists.txt deleted file mode 100644 index 6d817a025e..0000000000 --- a/bench/meili/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_valhalla_benchmark(mapmatch) diff --git a/bench/meili/config.json b/bench/meili/config.json deleted file mode 100644 index 44a02b8b26..0000000000 --- a/bench/meili/config.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "mjolnir": { - "tile_dir": "test/data/utrecht_tiles", - "concurrency": 1 - }, - "loki": { - "actions": [ - "route", - "sources_to_targets", - "trace_route", - "trace_attributes" - ], - "logging": { - "long_request": 100 - }, - "service_defaults": { - "minimum_reachability": 50, - "radius": 0, - "search_cutoff": 35000, - "node_snap_tolerance": 5, - "street_side_tolerance": 5, - "street_side_max_distance": 1000, - "heading_tolerance": 60 - } - }, - "thor": { - "logging": { - "long_request": 110 - } - }, - "meili": { - "customizable": [ - "turn_penalty_factor", - "max_route_distance_factor", - "max_route_time_factor", - "search_radius" - ], - "mode": "auto", - "grid": { - "cache_size": 100240, - "size": 500 - }, - "default": { - "beta": 3, - "breakage_distance": 2000, - "geometry": false, - "gps_accuracy": 5, - "interpolation_distance": 10, - "max_route_distance_factor": 5, - "max_route_time_factor": 5, - "max_search_radius": 200, - "route": true, - "search_radius": 15, - "sigma_z": 4.07, - "turn_penalty_factor": 200 - } - }, - "service_limits": { - "auto": { - "max_distance": 5000000, - "max_locations": 20, - "max_matrix_distance": 400000, - "max_matrix_location_pairs": 2500 - }, - "auto_shorter": { - "max_distance": 5000000, - "max_locations": 20, - "max_matrix_distance": 400000, - "max_matrix_location_pairs": 2500 - }, - "isochrone": { - "max_contours": 4, - "max_distance": 25000, - "max_locations": 1, - "max_time_contour": 120, - "max_distance_contour": 200 - }, - "max_exclude_locations": 50, - "max_radius": 200, - "max_reachability": 100, - "max_alternates": 2, - "max_exclude_polygons_length": 10000, - "max_distance_disable_hierarchy_culling": 0, - "multimodal": { - "max_distance": 500000, - "max_locations": 50, - "max_matrix_distance": 0, - "max_matrix_location_pairs": 0 - }, - "pedestrian": { - "max_distance": 250000, - "max_locations": 50, - "max_matrix_distance": 200000, - "max_matrix_location_pairs": 2500, - "max_transit_walking_distance": 10000, - "min_transit_walking_distance": 1 - }, - "skadi": { - "max_shape": 750000, - "min_resample": 10 - }, - "trace": { - "max_distance": 200000, - "max_gps_accuracy": 100, - "max_search_radius": 100, - "max_shape": 16000, - "max_alternates": 3, - "max_alternates_shape": 100 - } - } -} diff --git a/bench/meili/fixtures/3km_loop_utrecht.json b/bench/meili/fixtures/3km_loop_utrecht.json deleted file mode 100644 index e3858b1999..0000000000 --- a/bench/meili/fixtures/3km_loop_utrecht.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "costing": "auto", - "format": "osrm", - "shape_match": "map_snap", - "shape": [ - { - "lon": 5.08531221, - "lat": 52.0938563, - "type": "break" - }, - { - "lon": 5.0865867, - "lat": 52.0930211, - "type": "break" - }, - { - "lon": 5.08769141, - "lat": 52.0923946, - "type": "break" - }, - { - "lon": 5.0896245, - "lat": 52.0912591, - "type": "break" - }, - { - "lon": 5.0909416, - "lat": 52.090737, - "type": "break" - }, - { - "lon": 5.0926623, - "lat": 52.0905021, - "type": "break" - }, - { - "lon": 5.0946379, - "lat": 52.090737, - "type": "break" - }, - { - "lon": 5.0961035, - "lat": 52.0907892, - "type": "break" - }, - { - "lon": 5.097442, - "lat": 52.0909328, - "type": "break" - }, - { - "lon": 5.09884401, - "lat": 52.09115474, - "type": "break" - }, - { - "lon": 5.100416, - "lat": 52.0913244, - "type": "break" - }, - { - "lon": 5.101733, - "lat": 52.09137664, - "type": "break" - }, - { - "lon": 5.1034112, - "lat": 52.0915854, - "type": "break" - }, - { - "lon": 5.10351751, - "lat": 52.09202915, - "type": "break" - }, - { - "lon": 5.102345, - "lat": 52.0929627, - "type": "break" - }, - { - "lon": 5.0959337, - "lat": 52.093477899999996, - "type": "break" - }, - { - "lon": 5.0932129, - "lat": 52.0939153, - "type": "break" - }, - { - "lon": 5.08858141, - "lat": 52.094623799999994, - "type": "break" - }, - { - "lon": 5.0858904, - "lat": 52.0958159, - "type": "break" - } - ] -} diff --git a/bench/meili/fixtures/intersection_matching1.json b/bench/meili/fixtures/intersection_matching1.json deleted file mode 100644 index 88b84c375e..0000000000 --- a/bench/meili/fixtures/intersection_matching1.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "costing": "auto", - "format": "osrm", - "shape_match": "map_snap", - "shape": [ - { - "lat": 52.0981267, - "lon": 5.129618, - "type": "break" - }, - { - "lat": 52.098128, - "lon": 5.129725, - "type": "break" - } - ] -} diff --git a/bench/meili/fixtures/intersection_matching2.json b/bench/meili/fixtures/intersection_matching2.json deleted file mode 100644 index 39cb22ed5f..0000000000 --- a/bench/meili/fixtures/intersection_matching2.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "costing": "auto", - "format": "osrm", - "shape_match": "map_snap", - "shape": [ - { - "lat": 52.0981346, - "lon": 5.1300437, - "type": "break" - }, - { - "lat": 52.0981145, - "lon": 5.1309431, - "type": "break" - }, - { - "lat": 52.0980642, - "lon": 5.1314993, - "type": "break" - }, - { - "lat": 52.0971149, - "lon": 5.1311002, - "type": "break" - } - ] -} diff --git a/bench/meili/fixtures/intersection_matching3.json b/bench/meili/fixtures/intersection_matching3.json deleted file mode 100644 index 3b365ea8fe..0000000000 --- a/bench/meili/fixtures/intersection_matching3.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "costing": "auto", - "format": "osrm", - "shape_match": "map_snap", - "shape": [ - { - "lat": 52.0951641, - "lon": 5.1285609, - "type": "break" - }, - { - "lat": 52.0952055, - "lon": 5.1292756, - "type": "break" - }, - { - "lat": 52.095258, - "lon": 5.1301359, - "type": "break" - }, - { - "lat": 52.0952939, - "lon": 5.130902, - "type": "break" - }, - { - "lat": 52.0944788, - "lon": 5.1304066, - "type": "break" - } - ] -} diff --git a/bench/meili/mapmatch.cc b/bench/meili/mapmatch.cc deleted file mode 100644 index 005cfc995c..0000000000 --- a/bench/meili/mapmatch.cc +++ /dev/null @@ -1,122 +0,0 @@ -#include -#include - -#include -#include - -#include "baldr/rapidjson_utils.h" -#include "meili/map_matcher_factory.h" -#include "meili/measurement.h" -#include "sif/costconstants.h" -#include "sif/costfactory.h" -#include "tyr/actor.h" - -using namespace valhalla::midgard; -using namespace valhalla::meili; -using namespace valhalla::sif; - -namespace { - -#if !defined(VALHALLA_SOURCE_DIR) -#define VALHALLA_SOURCE_DIR -#endif - -constexpr float kGpsAccuracyMeters = 4.07; -constexpr float kSearchRadiusMeters = 50; - -// Inline benchmarks for OfflineMatch - -class OfflineMapmatchFixture : public benchmark::Fixture { -public: - void SetUp(const ::benchmark::State& state) { - (void)state; - InitEngineConfig(); - InitMapMatcher(); - } - - void TearDown(const ::benchmark::State& state) { - (void)state; - matcher_factory_->ClearCache(); - mapmatcher_->Clear(); - matcher_factory_.reset(); - mapmatcher_.reset(); - } - -private: - void InitEngineConfig() { - rapidjson::read_json(VALHALLA_SOURCE_DIR "bench/meili/config.json", config_); - const rapidjson::Document doc; - valhalla::sif::ParseCosting(doc, "/costing_options", options_); - options_.set_costing_type(valhalla::Costing::auto_); - } - - void InitMapMatcher() { - matcher_factory_ = std::make_shared(config_); - mapmatcher_.reset(matcher_factory_->Create(options_)); - } - -protected: - // Mapmatcher implementation - std::shared_ptr mapmatcher_; - std::shared_ptr matcher_factory_; - // Mapmatching configuration - boost::property_tree::ptree config_; - valhalla::Options options_; -}; - -std::vector BuildMeasurements(float gps_accuracy, float search_radius) { - std::vector meas; - meas.emplace_back(PointLL(5.09806, 52.09110), gps_accuracy, search_radius); - meas.emplace_back(PointLL(5.09769, 52.09050), gps_accuracy, search_radius); - meas.emplace_back(PointLL(5.09679, 52.09098), gps_accuracy, search_radius); - return meas; -} - -BENCHMARK_DEFINE_F(OfflineMapmatchFixture, BasicOfflineMatch)(benchmark::State& state) { - logging::Configure({{"type", ""}}); - const auto& meas = BuildMeasurements(kGpsAccuracyMeters, kSearchRadiusMeters); - for (auto _ : state) { - benchmark::DoNotOptimize(mapmatcher_->OfflineMatch(meas)); - } -} - -BENCHMARK_REGISTER_F(OfflineMapmatchFixture, BasicOfflineMatch); - -// Load fixture files, intended to mirror test cases defined in test/mapmatch.cc. - -std::string LoadFile(const std::string& filename) { - std::stringstream ss; - std::string line; - std::ifstream input_file; - input_file.open(filename.c_str()); - while (std::getline(input_file, line)) { - ss << line; - } - return ss.str(); -} - -const std::vector kBenchmarkCases = { - // Intersection matching test cases - VALHALLA_SOURCE_DIR "bench/meili/fixtures/intersection_matching1.json", - VALHALLA_SOURCE_DIR "bench/meili/fixtures/intersection_matching2.json", - VALHALLA_SOURCE_DIR "bench/meili/fixtures/intersection_matching3.json", - // 2.8km loop in Utrecht - VALHALLA_SOURCE_DIR "bench/meili/fixtures/3km_loop_utrecht.json", -}; - -static void BM_ManyCases(benchmark::State& state) { - logging::Configure({{"type", ""}}); - boost::property_tree::ptree config; - rapidjson::read_json(VALHALLA_SOURCE_DIR "bench/meili/config.json", config); - valhalla::tyr::actor_t actor(config, true); - const std::string test_case(LoadFile(kBenchmarkCases[state.range(0)])); - for (auto _ : state) { - benchmark::DoNotOptimize(actor.trace_route(test_case)); - } -} - -BENCHMARK(BM_ManyCases)->DenseRange(0, kBenchmarkCases.size() - 1); - -} // namespace - -BENCHMARK_MAIN(); diff --git a/bench/thor/CMakeLists.txt b/bench/thor/CMakeLists.txt deleted file mode 100644 index de77da6b45..0000000000 --- a/bench/thor/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_valhalla_benchmark(costmatrix) -add_valhalla_benchmark(routes) -add_valhalla_benchmark(isochrone) -add_valhalla_benchmark(reach) diff --git a/bench/thor/costmatrix.cc b/bench/thor/costmatrix.cc deleted file mode 100644 index e8380c7334..0000000000 --- a/bench/thor/costmatrix.cc +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include -#include -#include - -#include "baldr/graphreader.h" -#include "loki/search.h" -#include "midgard/pointll.h" -#include "sif/autocost.h" -#include "sif/costfactory.h" -#include "thor/costmatrix.h" -#include - -using namespace valhalla; - -namespace { - -boost::property_tree::ptree json_to_pt(const std::string& json) { - std::stringstream ss; - ss << json; - boost::property_tree::ptree pt; - rapidjson::read_json(ss, pt); - return pt; -} - -const auto config = json_to_pt(R"({ - "mjolnir":{"tile_dir":"test/data/utrecht_tiles", "concurrency": 1}, - "loki":{ - "actions":["sources_to_targets"], - "logging":{"long_request": 100}, - "service_defaults":{"minimum_reachability": 50,"radius": 0,"search_cutoff": 35000, "node_snap_tolerance": 5, "street_side_tolerance": 5, "street_side_max_distance": 1000, "heading_tolerance": 60} - }, - "thor":{ - "logging":{"long_request": 100} - }, - "meili":{ - "grid": { - "cache_size": 100240, - "size": 500 - } - }, - "service_limits": { - "auto": {"max_distance": 5000000.0, "max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "auto_shorter": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "bicycle": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500}, - "bus": {"max_distance": 5000000.0,"max_locations": 50,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "hov": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "taxi": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "isochrone": {"max_contours": 4,"max_distance": 25000.0,"max_locations": 1,"max_time_contour": 120,"max_distance_contour":200}, - "max_exclude_locations": 50,"max_radius": 200,"max_reachability": 100,"max_alternates":2,"max_exclude_polygons_length":10000, - "multimodal": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 0.0,"max_matrix_location_pairs": 0}, - "pedestrian": {"max_distance": 250000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500,"max_transit_walking_distance": 10000,"min_transit_walking_distance": 1}, - "skadi": {"max_shape": 750000,"min_resample": 10.0}, - "trace": {"max_distance": 200000.0,"max_gps_accuracy": 100.0,"max_search_radius": 100,"max_shape": 16000,"max_best_paths":4,"max_best_paths_shape":100}, - "transit": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500}, - "truck": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500} - } - })"); - -constexpr float kMaxRange = 256; - -static void BM_UtrechtCostMatrix(benchmark::State& state) { - const int size = state.range(0); - baldr::GraphReader reader(config.get_child("mjolnir")); - - // Generate N random locations within the Utrect bounding box; - std::vector locations; - const double min_lon = 5.0163; - const double max_lon = 5.1622; - const double min_lat = 52.0469999; - const double max_lat = 52.1411; - - std::mt19937 gen(0); // Seed with the same value for consistent benchmarking - std::uniform_real_distribution<> lng_distribution(min_lon, max_lon); - std::uniform_real_distribution<> lat_distribution(min_lat, max_lat); - - locations.reserve(size); - for (int i = 0; i < size; i++) { - locations.emplace_back(midgard::PointLL{lng_distribution(gen), lat_distribution(gen)}); - } - - Api request; - auto& options = *request.mutable_options(); - options.set_costing_type(Costing::auto_); - rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); - sif::TravelMode mode; - auto costs = sif::CostFactory().CreateModeCosting(options, mode); - auto cost = costs[static_cast(mode)]; - - auto& sources = *options.mutable_sources(); - const auto projections = loki::Search(locations, reader, cost); - if (projections.size() == 0) { - throw std::runtime_error("Found no matching locations"); - } - for (const auto& projection : projections) { - auto* p = sources.Add(); - baldr::PathLocation::toPBF(projection.second, p, reader); - } - - thor::CostMatrix matrix; - for (auto _ : state) { - matrix.SourceToTarget(request, reader, costs, mode, 100000.); - matrix.Clear(); - request.clear_matrix(); - } - state.counters["Routes"] = benchmark::Counter(size, benchmark::Counter::kIsIterationInvariantRate); -} - -BENCHMARK(BM_UtrechtCostMatrix) - ->Unit(benchmark::kMillisecond) - ->RangeMultiplier(2) - ->Range(1, kMaxRange); - -} // namespace - -BENCHMARK_MAIN(); diff --git a/bench/thor/isochrone.cc b/bench/thor/isochrone.cc deleted file mode 100644 index f67867ce24..0000000000 --- a/bench/thor/isochrone.cc +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include -#include - -#include "baldr/graphreader.h" -//#include - -#include "loki/worker.h" -#include "thor/worker.h" - -#include "test.h" - -using namespace valhalla; - -namespace { - -// Maximum isochrone range to test during the benchmark -// This benchmark is on the Utrecht tiles we test against, so -// 30 minutes pretty much gets you to the edge from the middle -// in any direction -constexpr float kMaxDurationMinutes = 120; - -// Test the core isochrone calculation algorithm -void BM_IsochroneUtrecht(benchmark::State& state) { - const int size = state.range(0); - - const auto config = - test::make_config("test/data/utrecht_tiles", {}, - {{"additional_data", "mjolnir.traffic_extract", "mjolnir.tile_extract"}}); - valhalla::loki::loki_worker_t loki_worker(config); - valhalla::thor::thor_worker_t thor_worker(config); - - const auto request_json = - R"({"locations":[{"lat":52.078937,"lon":5.115321}],"costing":"auto","contours":[{"time":)" + - std::to_string(size) + R"(}],"polygons":false,"denoise":1,"generalize":20})"; - - // compute the isochrone - valhalla::Api request; - valhalla::ParseApi(request_json, Options::isochrone, request); - loki_worker.isochrones(request); - - for (auto _ : state) { - auto response_json = thor_worker.isochrones(request); - // std::cout << response_json << std::endl; - } -} - -BENCHMARK(BM_IsochroneUtrecht) - ->Unit(benchmark::kMillisecond) - ->RangeMultiplier(2) - ->Range(1, kMaxDurationMinutes) - ->Repetitions(10); - -} // namespace - -BENCHMARK_MAIN(); diff --git a/bench/thor/reach.cc b/bench/thor/reach.cc deleted file mode 100644 index de5801a966..0000000000 --- a/bench/thor/reach.cc +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include -#include - -#include "baldr/graphreader.h" -#include "loki/reach.h" -#include "sif/costfactory.h" -#include "test.h" - -using namespace valhalla; - -namespace { - -// Test the core isochrone calculation algorithm -void BM_ReachUtrecht(benchmark::State& state) { - - const auto config = - test::make_config("test/data/utrecht_tiles", {}, - {{"additional_data", "mjolnir.traffic_extract", "mjolnir.tile_extract"}}); - - // get tile access - GraphReader reader(config.get_child("mjolnir")); - - auto costing = sif::CostFactory{}.Create(Costing::auto_); - loki::Reach reach_finder; - - using Edge = std::pair; - std::vector edges; - - for (auto tile_id : reader.GetTileSet()) { - auto tile = reader.GetGraphTile(tile_id); - for (GraphId edge_id = tile->header()->graphid(); - edge_id.id() < tile->header()->directededgecount(); ++edge_id) { - const auto* edge = tile->directededge(edge_id); - edges.emplace_back(edge_id, edge); - } - } - - for (auto _ : state) { - for (const auto& edge : edges) { - auto reach = reach_finder(edge.second, edge.first, 50, reader, costing, kInbound | kOutbound); - } - } -} - -BENCHMARK(BM_ReachUtrecht)->Unit(benchmark::kMillisecond)->Repetitions(10); - -} // namespace - -BENCHMARK_MAIN(); diff --git a/bench/thor/routes.cc b/bench/thor/routes.cc deleted file mode 100644 index f588966b0c..0000000000 --- a/bench/thor/routes.cc +++ /dev/null @@ -1,426 +0,0 @@ -#include -#include -#include -#include -#include - -#include "baldr/graphreader.h" -#include "loki/search.h" -#include "midgard/logging.h" -#include "midgard/pointll.h" -#include "sif/autocost.h" -#include "sif/costfactory.h" -#include "test.h" -#include "thor/bidirectional_astar.h" -#include "thor/unidirectional_astar.h" -#include - -using namespace valhalla; - -namespace { - -void create_costing_options(Options& options) { - options.set_costing_type(Costing::auto_); - rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); -} - -boost::property_tree::ptree json_to_pt(const std::string& json) { - std::stringstream ss; - ss << json; - boost::property_tree::ptree pt; - rapidjson::read_json(ss, pt); - return pt; -} - -boost::property_tree::ptree build_config(const char* live_traffic_tar) { - return json_to_pt(R"({ - "mjolnir":{ - "traffic_extract": "test/data/utrecht_tiles/)" + - std::string(live_traffic_tar) + R"(", - "tile_dir": "test/data/utrecht_tiles", - "concurrency": 1 - }, - "loki":{ - "actions":["route"], - "logging":{"long_request": 100}, - "service_defaults":{ - "minimum_reachability": 10, - "radius": 50, - "search_cutoff": 35000, - "node_snap_tolerance": 5, - "street_side_tolerance": 5, - "heading_tolerance": 360 - } - }, - "thor":{ - "logging":{"long_request": 100} - }, - "meili":{ - "grid": { - "cache_size": 100240, - "size": 500 - } - }, - "service_limits": { - "auto": {"max_distance": 5000000.0, "max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "auto_shorter": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "bicycle": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500}, - "bus": {"max_distance": 5000000.0,"max_locations": 50,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "hov": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "taxi": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500}, - "isochrone": {"max_contours": 4,"max_distance": 25000.0,"max_locations": 1,"max_time_contour": 120,"max_distance_contour":200}, - "max_exclude_locations": 50,"max_radius": 200,"max_reachability": 100,"max_alternates":2,"max_exclude_polygons_length":10000, - "multimodal": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 0.0,"max_matrix_location_pairs": 0}, - "pedestrian": {"max_distance": 250000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500,"max_transit_walking_distance": 10000,"min_transit_walking_distance": 1}, - "skadi": {"max_shape": 750000,"min_resample": 10.0}, - "trace": {"max_distance": 200000.0,"max_gps_accuracy": 100.0,"max_search_radius": 100,"max_shape": 16000,"max_best_paths":4,"max_best_paths_shape":100}, - "transit": {"max_distance": 500000.0,"max_locations": 50,"max_matrix_distance": 200000.0,"max_matrix_location_pairs": 2500}, - "truck": {"max_distance": 5000000.0,"max_locations": 20,"max_matrix_distance": 400000.0,"max_matrix_location_pairs": 2500} - } - })"); -} - -constexpr float kMaxRange = 256; - -static void BM_UtrechtBidirectionalAstar(benchmark::State& state) { - const auto config = build_config("generated-live-data.tar"); - test::build_live_traffic_data(config); - - std::mt19937 gen(0); // Seed with the same value for consistent benchmarking - { - // Something to generate traffic with - std::uniform_real_distribution<> traffic_dist(0., 1.); - // This fraction of edges have live traffic - float has_live_traffic = 0.2; - - // Make some updates to the traffic .tar file. - // Generate traffic data - std::function - generate_traffic = [&gen, &traffic_dist, - &has_live_traffic](baldr::GraphReader& reader, baldr::TrafficTile& tile, - int index, baldr::TrafficSpeed* current) -> void { - baldr::GraphId tile_id(tile.header->tile_id); - if (traffic_dist(gen) < has_live_traffic) { - current->breakpoint1 = 255; - current->overall_encoded_speed = traffic_dist(gen) * 100; - } else { - } - }; - test::customize_live_traffic_data(config, generate_traffic); - } - - auto clean_reader = test::make_clean_graphreader(config.get_child("mjolnir")); - - std::vector locations; - - Options options; - create_costing_options(options); - sif::TravelMode mode; - auto costs = sif::CostFactory().CreateModeCosting(options, mode); - auto cost = costs[static_cast(mode)]; - - // A few locations around Utrecht. Origins and destinations are constructed - // from these for the queries - locations.emplace_back(midgard::PointLL{5.115873, 52.099247}); - locations.emplace_back(midgard::PointLL{5.117328, 52.099464}); - locations.emplace_back(midgard::PointLL{5.114576, 52.101841}); - locations.emplace_back(midgard::PointLL{5.114598, 52.103607}); - locations.emplace_back(midgard::PointLL{5.112481, 52.074073}); - locations.emplace_back(midgard::PointLL{5.135983, 52.110116}); - locations.emplace_back(midgard::PointLL{5.095273, 52.108956}); - locations.emplace_back(midgard::PointLL{5.110077, 52.062043}); - locations.emplace_back(midgard::PointLL{5.025595, 52.067372}); - - const auto projections = loki::Search(locations, *clean_reader, cost); - if (projections.size() == 0) { - throw std::runtime_error("Found no matching locations"); - } - - std::vector origins; - std::vector destinations; - - { - auto it = projections.cbegin(); - if (it == projections.cend()) { - throw std::runtime_error("Found no matching locations"); - } - while (true) { - auto origin = valhalla::Location{}; - baldr::PathLocation::toPBF(it->second, &origin, *clean_reader); - ++it; - if (it == projections.cend()) { - break; - } - origins.push_back(origin); - destinations.push_back(valhalla::Location{}); - baldr::PathLocation::toPBF(it->second, &destinations.back(), *clean_reader); - } - } - - if (origins.size() == 0) { - throw std::runtime_error("No origins available for test"); - } - - std::size_t route_size = 0; - - thor::BidirectionalAStar astar; - for (auto _ : state) { - for (int i = 0; i < origins.size(); ++i) { - // LOG_WARN("Running index "+std::to_string(i)); - auto result = astar.GetBestPath(origins[i], destinations[i], *clean_reader, costs, - sif::TravelMode::kDrive); - astar.Clear(); - route_size += 1; - } - } - if (route_size == 0) { - throw std::runtime_error("Failed all routes"); - } - state.counters["Routes"] = route_size; -} - -void customize_traffic(const boost::property_tree::ptree& config, - baldr::GraphId& target_edge_id, - const int target_speed) { - test::build_live_traffic_data(config); - // Make some updates to the traffic .tar file. - // Generate traffic data - std::function - generate_traffic = [&target_edge_id, &target_speed](baldr::GraphReader& reader, - baldr::TrafficTile& tile, int index, - baldr::TrafficSpeed* current) -> void { - baldr::GraphId tile_id(tile.header->tile_id); - auto edge_id = baldr::GraphId(tile_id.tileid(), tile_id.level(), index); - if (edge_id == target_edge_id) { - current->breakpoint1 = 255; - current->overall_encoded_speed = target_speed >> 1; - current->encoded_speed1 = target_speed >> 1; - } - }; - test::customize_live_traffic_data(config, generate_traffic); -} - -BENCHMARK(BM_UtrechtBidirectionalAstar)->Unit(benchmark::kMillisecond); - -/* - * A set of fixed random routes across the globe. Taken from test_requests/random.txt - */ -std::vector global_locations = - {midgard::PointLL{-8.336801, 33.286377}, midgard::PointLL{5.872467, 50.575802}, - midgard::PointLL{11.524066, 3.862927}, midgard::PointLL{30.490564, -22.948921}, - midgard::PointLL{21.407406, 12.212897}, midgard::PointLL{21.408346, 12.209968}, - midgard::PointLL{17.784868, 44.147346}, midgard::PointLL{15.582961, 45.906693}, - midgard::PointLL{8.685453, 39.226093}, midgard::PointLL{2.142843, 52.584072}, - midgard::PointLL{16.960527, 52.423416}, midgard::PointLL{18.670666, 54.35183}, - midgard::PointLL{85.840378, 12.75919}, midgard::PointLL{84.008522, 9.926284}, - midgard::PointLL{20.597891, 41.602592}, midgard::PointLL{20.880438, 41.886894}, - midgard::PointLL{8.057651, 52.261757}, midgard::PointLL{6.309168, 49.66972}, - midgard::PointLL{37.617016, 55.746685}, midgard::PointLL{37.623234, 55.746956}, - midgard::PointLL{66.781364, 10.485166}, midgard::PointLL{68.890945, 10.163307}, - midgard::PointLL{13.961927, 15.293241}, midgard::PointLL{13.967668, 15.293}, - midgard::PointLL{4.126567, 51.035511}, midgard::PointLL{5.887219, 49.531387}, - midgard::PointLL{9.22713, 49.130718}, midgard::PointLL{11.066673, 49.452415}, - midgard::PointLL{95.504768, 18.474234}, midgard::PointLL{95.494049, 18.483744}, - midgard::PointLL{135.590393, 34.623756}, midgard::PointLL{135.521576, 34.759117}, - midgard::PointLL{3.953196, 36.537201}, midgard::PointLL{3.178043, 36.726479}, - midgard::PointLL{35.160679, 32.520855}, midgard::PointLL{35.766632, 32.706535}, - midgard::PointLL{1.74563, 53.791374}, midgard::PointLL{2.110271, 53.535301}, - midgard::PointLL{21.016792, 41.08852}, midgard::PointLL{21.020342, 41.080669}}; - -template -void BM_GlobalFixedRandom(benchmark::State& state, const std::string& planet_path) { - - if (planet_path.empty()) { - state.SkipWithError( - "No planet file specified, please supply --planet-path=X on the command line"); - return; - } - - auto config = - test::make_config("test/data/utrecht_tiles", {}, - {{"additional_data", "mjolnir.traffic_extract", "mjolnir.tile_dir"}}); - config.put("mjolnir.tile_extract", planet_path); - - auto clean_reader = test::make_clean_graphreader(config.get_child("mjolnir")); - - Options options; - create_costing_options(options); - sif::TravelMode mode; - auto costs = sif::CostFactory().CreateModeCosting(options, mode); - auto cost = costs[static_cast(mode)]; - - std::vector locations(global_locations.begin() + state.range(0), - global_locations.begin() + state.range(0) + 2); - - const auto projections = loki::Search(locations, *clean_reader, cost); - if (projections.size() == 0) { - throw std::runtime_error("Found no matching locations"); - } - - std::vector origins; - std::vector destinations; - - { - auto it = projections.cbegin(); - if (it == projections.cend()) { - throw std::runtime_error("Found no matching locations"); - } - while (true) { - auto origin = valhalla::Location{}; - origin.set_date_time("2021-04-01T00:00:00"); - baldr::PathLocation::toPBF(it->second, &origin, *clean_reader); - ++it; - if (it == projections.cend()) { - break; - } - origins.push_back(origin); - destinations.push_back(valhalla::Location{}); - baldr::PathLocation::toPBF(it->second, &destinations.back(), *clean_reader); - destinations.back().set_date_time("2021-04-01T00:00:00"); - } - } - - if (origins.size() == 0) { - throw std::runtime_error("No origins available for test"); - } - - Algorithm algorithm; - { - // Do it once to warmup - auto result = algorithm.GetBestPath(origins.front(), destinations.front(), *clean_reader, costs, - sif::TravelMode::kDrive); - std::string coords = std::to_string(locations[0].latlng_.lng()) + "," + - std::to_string(locations[0].latlng_.lat()) + " -> " + - std::to_string(locations[1].latlng_.lng()) + "," + - std::to_string(locations[1].latlng_.lat()); - if (result.empty() || result.front().empty()) { - state.SkipWithError(std::string("Route " + coords + " returned no result").c_str()); - return; - } - algorithm.Clear(); - // std::cout << coords << " = route eta of " - // << std::to_string(result.back().back().elapsed_cost.secs) << " seconds" << std::endl; - } - for (auto _ : state) { - auto result = algorithm.GetBestPath(origins.front(), destinations.front(), *clean_reader, costs, - sif::TravelMode::kDrive); - algorithm.Clear(); - } -} - -/** Benchmarks the GetSpeed function */ -static void BM_GetSpeed(benchmark::State& state) { - - const auto config = build_config("get-speed.tar"); - auto tgt_edge_id = baldr::GraphId(3196, 0, 3221); - const auto tgt_speed = 50; - customize_traffic(config, tgt_edge_id, tgt_speed); - - auto clean_reader = test::make_clean_graphreader(config.get_child("mjolnir")); - - auto tile = clean_reader->GetGraphTile(baldr::GraphId(tgt_edge_id)); - if (tile == nullptr) { - throw std::runtime_error("Target tile not found"); - } - auto edge = tile->directededge(tgt_edge_id); - if (edge == nullptr) { - throw std::runtime_error("Target edge not found"); - } - - if (tile->GetSpeed(edge, 255, 1) != tgt_speed) { - fprintf(stderr, "ERROR: tgt_speed: %i, GetSpeed(...): %i\n", tgt_speed, - tile->GetSpeed(edge, 255, 1)); - throw std::runtime_error("Target edge was not at target speed"); - } - - for (auto _ : state) { - tile->GetSpeed(edge, 255, 1); - } -} - -BENCHMARK(BM_GetSpeed)->Unit(benchmark::kNanosecond); - -/** Benchmarks the Allowed function */ -static void BM_Sif_Allowed(benchmark::State& state) { - - const auto config = build_config("sif-allowed.tar"); - auto tgt_edge_id = baldr::GraphId(3196, 0, 3221); - auto tgt_speed = 100; - customize_traffic(config, tgt_edge_id, tgt_speed); - - auto clean_reader = test::make_clean_graphreader(config.get_child("mjolnir")); - - Options options; - create_costing_options(options); - sif::TravelMode mode; - auto costs = sif::CostFactory().CreateModeCosting(options, mode); - auto cost = costs[static_cast(mode)]; - - auto tile = clean_reader->GetGraphTile(baldr::GraphId(tgt_edge_id)); - if (tile == nullptr) { - throw std::runtime_error("Target tile not found"); - } - auto edge = tile->directededge(tgt_edge_id); - if (edge == nullptr) { - throw std::runtime_error("Target edge not found"); - } - - if (tile->GetSpeed(edge, 255, 1) != tgt_speed) { - throw std::runtime_error("Target edge was not at target speed"); - } - - // Mock a phony predecessor - // auto pred = sif::EdgeLabel(0, tgt_edge_id, edge, costs, 1.0, 1.0, - // sif::TravelMode::kDrive,10,sif::Cost()); - auto pred = sif::EdgeLabel(); - uint8_t restriction_idx; - - for (auto _ : state) { - cost->Allowed(edge, false, pred, tile, tgt_edge_id, 0, 0, restriction_idx); - } -} - -BENCHMARK(BM_Sif_Allowed)->Unit(benchmark::kNanosecond); - -} // namespace - -int main(int argc, char** argv) { - - logging::Configure({{"type", ""}}); - - std::string planet_path = ""; - int num_routes = 0; - - for (int i = 0; i < argc; i++) { - if (std::string(argv[i]).find("--planet-path=") != std::string::npos) { - planet_path = std::string(argv[i]).substr(strlen("--planet-path=")); - std::cerr << "Registered planet_path = " << planet_path << std::endl; - } else if (std::string(argv[i]).find("--num-routes=") != std::string::npos) { - num_routes = std::atoi(argv[i] + strlen("--num-routes=")); - if (num_routes == 0) { - std::cerr << "num-routes must be > 0" << std::endl; - return 1; - } else { - std::cerr << "Registered num_routes = " << num_routes << std::endl; - } - } - } - - if (!planet_path.empty() && num_routes > 0) { - ::benchmark::RegisterBenchmark("BM_GlobalFixedRandom", BM_GlobalFixedRandom, - planet_path) - ->Unit(benchmark::kMillisecond) - ->DenseRange(0, num_routes); - ::benchmark::RegisterBenchmark("BM_GlobalFixedRandom", BM_GlobalFixedRandom, - planet_path) - ->Unit(benchmark::kMillisecond) - ->DenseRange(0, num_routes); - ::benchmark::RegisterBenchmark("BM_GlobalFixedRandom", - BM_GlobalFixedRandom, planet_path) - ->Unit(benchmark::kMillisecond) - ->DenseRange(0, num_routes); - } - ::benchmark::Initialize(&argc, argv); - ::benchmark::RunSpecifiedBenchmarks(); -} diff --git a/codecov.yml b/codecov.yml index 629965e1be..7750fa7857 100644 --- a/codecov.yml +++ b/codecov.yml @@ -2,6 +2,5 @@ coverage: ignore: - third_party/.* - - bench/.* comment: off diff --git a/scripts/format.sh b/scripts/format.sh index 619087a19a..5d9b68248c 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -23,7 +23,7 @@ readonly CLANG_FORMAT=$(pwd)/mason_packages/.link/bin/clang-format echo "Using clang-format $CLANG_FORMAT_VERSION from ${CLANG_FORMAT}" -find src valhalla test bench -type f -name '*.h' -o -name '*.cc' \ +find src valhalla test -type f -name '*.h' -o -name '*.cc' \ | xargs -I{} -P ${NPROC} ${CLANG_FORMAT} -i -style=file {} # Python setup diff --git a/third_party/benchmark b/third_party/benchmark deleted file mode 160000 index f30c99a7c8..0000000000 --- a/third_party/benchmark +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f30c99a7c861e8cabc6d3d9c0b60a4f218c7f87a From 30068b15c34bf0379ae7c286cd2693c4c14cd0be Mon Sep 17 00:00:00 2001 From: Greg Knisely Date: Tue, 13 Feb 2024 13:04:23 -0500 Subject: [PATCH 030/198] Aggregation updates: update opposing local idx after aggregating the edges, added classification check for aggregation, and shortcut length changes (#4570) Co-authored-by: David Nesbitt --- CHANGELOG.md | 1 + src/baldr/shortcut_recovery.h | 10 +- src/mjolnir/elevationbuilder.cc | 14 +- src/mjolnir/graphenhancer.cc | 39 ------ src/mjolnir/graphfilter.cc | 224 +++++++++++++++++++++----------- src/mjolnir/graphtilebuilder.cc | 7 +- src/mjolnir/shortcutbuilder.cc | 40 +++--- src/mjolnir/util.cc | 35 +++++ test/gurka/test_shortcut.cc | 8 +- valhalla/mjolnir/util.h | 17 +++ 10 files changed, 246 insertions(+), 149 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f04230e0dd..fa7a67e7b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ * FIXED: Prevent GetShortcut to run into an infinite loop [#4532](https://github.com/valhalla/valhalla/pull/4532) * FIXED: fix config generator with thor.costmatrix_allow_second_pass [#4567](https://github.com/valhalla/valhalla/pull/4567) * FIXED: infinite loop or other random corruption in isochrones when retrieving partial shape of an edge [#4547](https://github.com/valhalla/valhalla/pull/4547) + * FIXED: Aggregation updates: update opposing local idx after aggregating the edges, added classification check for aggregation, and shortcut length changes [#4570](https://github.com/valhalla/valhalla/pull/4570) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/baldr/shortcut_recovery.h b/src/baldr/shortcut_recovery.h index 85d2051696..63010d7d3d 100644 --- a/src/baldr/shortcut_recovery.h +++ b/src/baldr/shortcut_recovery.h @@ -136,6 +136,10 @@ struct shortcut_recovery_t { uint32_t accumulated_length = current_edge->length(); // walk edges until we find the same ending node as the shortcut + // Use a roundoff error based on number of edges when comparing sum of individual + // edge lengths to shortcut length. + uint32_t edge_count = 1; + uint32_t roundoff_error = 1; while (current_edge->endnode() != shortcut->endnode()) { // get the node at the end of the last edge we added const NodeInfo* node = reader.GetEndNode(current_edge, tile); @@ -174,7 +178,9 @@ struct shortcut_recovery_t { } // if we didnt add an edge or we went over the length we failed - if (current_edge == nullptr || accumulated_length > shortcut->length()) { + ++edge_count; + roundoff_error = std::round(edge_count * 0.5) + 1; + if (current_edge == nullptr || accumulated_length > (shortcut->length() + roundoff_error)) { LOG_TRACE("Unable to recover shortcut for edgeid " + std::to_string(shortcut_id) + " | accumulated_length: " + std::to_string(accumulated_length) + " | shortcut_length: " + std::to_string(shortcut->length())); @@ -183,7 +189,7 @@ struct shortcut_recovery_t { } // we somehow got to the end via a shorter path - if (accumulated_length < shortcut->length()) { + if (accumulated_length < (shortcut->length() - roundoff_error)) { LOG_TRACE("Unable to recover shortcut for edgeid (accumulated_length < shortcut->length()) " + std::to_string(shortcut_id) + " | accumulated_length: " + std::to_string(accumulated_length) + diff --git a/src/mjolnir/elevationbuilder.cc b/src/mjolnir/elevationbuilder.cc index 1346085a9a..cd77909a85 100644 --- a/src/mjolnir/elevationbuilder.cc +++ b/src/mjolnir/elevationbuilder.cc @@ -58,10 +58,8 @@ std::vector encode_edge_elevation(const std::unique_ptr encode_btf_elevation(const std::unique_ptrnode(edge.endnode().id()); - - // Iterate through the directed edges and return when the end node matches the specified - // node, the length matches, and the shape matches (or edgeinfo offset matches) - const DirectedEdge* directededge = endnodetile->directededge(nodeinfo->edge_index()); - for (uint32_t i = 0; i < nodeinfo->edge_count(); i++, directededge++) { - if (directededge->endnode() == startnode && directededge->length() == edge.length()) { - // If in the same tile and the edgeinfo offset matches then the shape and names will match - if (endnodetile == tile && directededge->edgeinfo_offset() == edge.edgeinfo_offset()) { - return i; - } else { - // Need to compare shape if not in the same tile or different EdgeInfo (could be different - // names in opposing directions) - if (shapes_match(tile->edgeinfo(&edge).shape(), - endnodetile->edgeinfo(directededge).shape())) { - return i; - } - } - } - } - LOG_ERROR("Could not find opposing edge index"); - return kMaxEdgesPerNode; -} - bool ConsistentNames(const std::string& country_code, const std::vector>& names1, const std::vector>& names2) { diff --git a/src/mjolnir/graphfilter.cc b/src/mjolnir/graphfilter.cc index d85da4e358..63c927d6bb 100644 --- a/src/mjolnir/graphfilter.cc +++ b/src/mjolnir/graphfilter.cc @@ -56,22 +56,25 @@ bool ExpandFromNode(GraphReader& reader, uint64_t& way_id, const graph_tile_ptr& prev_tile, GraphId prev_node, - GraphId current_node); + GraphId current_node, + const RoadClass& rc, + bool validate); /* * Expand from the current node - * @param reader Graph reader. - * @param shape shape that we need to update - * @param en current end node that we started at - * @param from_node node that we started from - * @param isos country ISOs. Used to see if we cross into a new country - * @param forward traverse in the forward or backward direction + * @param reader Graph reader. + * @param shape shape that we need to update + * @param en current end node that we started at + * @param from_node node that we started from + * @param isos country ISOs. Used to see if we cross into a new country + * @param forward traverse in the forward or backward direction * @param visited_nodes nodes that we already visited. don't visit again - * @param way_id only interested in edges with this way_id - * @param prev_tile previous tile - * @param prev_node previous node - * @param current_node current node - * @param node_info current node's info + * @param way_id only interested in edges with this way_id + * @param prev_tile previous tile + * @param prev_node previous node + * @param current_node current node + * @param node_info current node's info + * @param validate Are we validating data? * */ bool ExpandFromNodeInner(GraphReader& reader, @@ -85,7 +88,9 @@ bool ExpandFromNodeInner(GraphReader& reader, const graph_tile_ptr& prev_tile, GraphId prev_node, GraphId current_node, - const NodeInfo* node_info) { + const NodeInfo* node_info, + const RoadClass& rc, + bool validate) { for (size_t j = 0; j < node_info->edge_count(); ++j) { GraphId edge_id(prev_tile->id().tileid(), prev_tile->id().level(), node_info->edge_index() + j); @@ -98,7 +103,6 @@ bool ExpandFromNodeInner(GraphReader& reader, } const NodeInfo* en_info = tile->node(de->endnode().id()); - // check the direction, if we looped back, or are we done if ((de->endnode() != prev_node) && (de->forward() == forward) && (de->endnode() != from_node || (de->endnode() == from_node && visited_nodes.size() > 1))) { @@ -106,13 +110,15 @@ bool ExpandFromNodeInner(GraphReader& reader, (en_info->mode_change() || (node_info->mode_change() && !en_info->mode_change()))) { // If this edge has special attributes, then we can't aggregate - if (!CanAggregate(de)) { + if (!CanAggregate(de) || de->classification() != rc) { way_id = 0; return false; } - if (isos.size() >= 1) { - isos.insert(tile->admin(en_info->admin_index())->country_iso()); + if (validate) { + if (isos.size() >= 1) { + isos.insert(tile->admin(en_info->admin_index())->country_iso()); + } } else { std::list edgeshape = valhalla::midgard::decode7>(edge_info.encoded_shape()); @@ -141,7 +147,7 @@ bool ExpandFromNodeInner(GraphReader& reader, // expand with the same way_id found = ExpandFromNode(reader, shape, en, from_node, isos, forward, visited_nodes, way_id, - tile, current_node, de->endnode()); + tile, current_node, de->endnode(), rc, validate); if (found) { return true; } @@ -156,17 +162,18 @@ bool ExpandFromNodeInner(GraphReader& reader, /* * Expand from the next node which is now our new current node - * @param reader Graph reader. - * @param shape shape that we need to update - * @param en current end node that we started at - * @param from_node node that we started from - * @param isos country ISOs. Used to see if we cross into a new country - * @param forward traverse in the forward or backward direction + * @param reader Graph reader. + * @param shape shape that we need to update + * @param en current end node that we started at + * @param from_node node that we started from + * @param isos country ISOs. Used to see if we cross into a new country + * @param forward traverse in the forward or backward direction * @param visited_nodes nodes that we already visited. don't visit again - * @param way_id only interested in edges with this way_id - * @param prev_tile previous tile - * @param prev_node previous node - * @param current_node current node + * @param way_id only interested in edges with this way_id + * @param prev_tile previous tile + * @param prev_node previous node + * @param current_node current node + * @param validate Are we validating data? * */ bool ExpandFromNode(GraphReader& reader, @@ -179,7 +186,9 @@ bool ExpandFromNode(GraphReader& reader, uint64_t& way_id, const graph_tile_ptr& prev_tile, GraphId prev_node, - GraphId current_node) { + GraphId current_node, + const RoadClass& rc, + bool validate) { auto tile = prev_tile; if (tile->id() != current_node.Tile_Base()) { @@ -189,7 +198,7 @@ bool ExpandFromNode(GraphReader& reader, auto* node_info = tile->node(current_node); // expand from the current node return ExpandFromNodeInner(reader, shape, en, from_node, isos, forward, visited_nodes, way_id, tile, - prev_node, current_node, node_info); + prev_node, current_node, node_info, rc, validate); } bool Aggregate(GraphId& start_node, @@ -199,12 +208,14 @@ bool Aggregate(GraphId& start_node, const GraphId& from_node, uint64_t& way_id, std::unordered_set& isos, - bool forward) { + const RoadClass& rc, + bool forward, + bool validate) { graph_tile_ptr tile = reader.GetGraphTile(start_node); std::unordered_set visited_nodes{start_node}; return ExpandFromNode(reader, shape, en, from_node, isos, forward, visited_nodes, way_id, tile, - GraphId(), start_node); + GraphId(), start_node, rc, validate); } /** @@ -261,6 +272,7 @@ void FilterTiles(GraphReader& reader, // Iterate through directed edges outbound from this node std::vector wayid; + std::vector classification; std::vector endnode; const NodeInfo* nodeinfo = tile->node(nodeid); std::string begin_node_iso = tile->admin(nodeinfo->admin_index())->country_iso(); @@ -338,6 +350,7 @@ void FilterTiles(GraphReader& reader, edgeinfo.GetTypes(), added, diff_names); newedge.set_edgeinfo_offset(edge_info_offset); wayid.push_back(edgeinfo.wayid()); + classification.push_back(directededge->classification()); endnode.push_back(directededge->endnode()); if (directededge->endnode().tile_value() != tile->header()->graphid().tile_value()) { @@ -378,7 +391,12 @@ void FilterTiles(GraphReader& reader, // edge attributes should match), don't end at same node (no loops), no traffic signal, // no signs exist at the node(named_intersection), does not have different // names, and end node of edges are not in a different tile. - if (edge_filtered && edge_count == 2 && wayid[0] == wayid[1] && endnode[0] != endnode[1] && + // + // Note: The classification check is here due to the reclassification of ferries. Found + // that some edges that were split at pedestrian edges had different classifications due to + // the reclassification of ferry edges (e.g., https://www.openstreetmap.org/way/204337649) + if (edge_filtered && edge_count == 2 && wayid[0] == wayid[1] && + classification[0] == classification[1] && endnode[0] != endnode[1] && !nodeinfo->traffic_signal() && !nodeinfo->named_intersection() && !diff_names && !diff_tile) { @@ -429,32 +447,21 @@ void GetAggregatedData(GraphReader& reader, const GraphId& from_node, const graph_tile_ptr& tile, const DirectedEdge* directededge) { + std::unordered_set isos; + bool isForward = directededge->forward(); + auto id = directededge->endnode(); + if (!isForward) { + std::reverse(shape.begin(), shape.end()); + } - // Get the tile at the end node. Skip if node is in another tile. - // mode_change is not set for end nodes that are in diff tiles - if (directededge->endnode().tile_value() == tile->header()->graphid().tile_value()) { - - // original edge data. - const auto& edgeinfo = tile->edgeinfo(directededge); - const NodeInfo* en_info = tile->node(directededge->endnode().id()); - - // do we need to aggregate? - if (en_info->mode_change()) { - std::unordered_set isos; - bool isForward = directededge->forward(); - auto id = directededge->endnode(); - if (!isForward) { - std::reverse(shape.begin(), shape.end()); - } - // walk in the correct direction. - uint64_t wayid = edgeinfo.wayid(); - if (Aggregate(id, reader, shape, en, from_node, wayid, isos, isForward)) { - aggregated++; // count the current edge - // flip the shape back for storing in edgeinfo - if (!isForward) { - std::reverse(shape.begin(), shape.end()); - } - } + // walk in the correct direction. + uint64_t wayid = tile->edgeinfo(directededge).wayid(); + if (Aggregate(id, reader, shape, en, from_node, wayid, isos, directededge->classification(), + isForward, false)) { + aggregated++; // count the current edge + // flip the shape back for storing in edgeinfo + if (!isForward) { + std::reverse(shape.begin(), shape.end()); } } } @@ -510,12 +517,13 @@ void ValidateData(GraphReader& reader, // walk in the correct direction. uint64_t wayid = edgeinfo.wayid(); - if (!Aggregate(id, reader, shape, en, from_node, wayid, isos, isForward)) { + if (!Aggregate(id, reader, shape, en, from_node, wayid, isos, directededge->classification(), + isForward, true)) { // LOG_WARN("ValidateData - failed to validate node. Will not aggregate."); // for debugging only // std::cout << "End node: " << directededge->endnode().value << " WayId: " << // edgeinfo.wayid() - // << std::endl; + // << std::endl; if (wayid == 0) { // This edge has special attributes, we can't aggregate no_agg_ways.insert(edgeinfo.wayid()); @@ -689,16 +697,24 @@ void AggregateTiles(GraphReader& reader, std::unordered_map& o // Names can be different in the forward and backward direction diff_names = tilebuilder.OpposingEdgeInfoDiffers(tile, directededge); - bool added; const auto& edgeinfo = tile->edgeinfo(directededge); std::string encoded_shape = edgeinfo.encoded_shape(); std::list shape = valhalla::midgard::decode7>(encoded_shape); + // Aggregate if end node is marked and in same tile + bool aggregated = false; GraphId en = directededge->endnode(); - // Do the actual aggregation. - GetAggregatedData(reader, shape, en, nodeid, tile, directededge); - newedge.set_endnode(en); + if (en.tile_value() == tile_id) { + if (tile->node(en.id())->mode_change()) { + GetAggregatedData(reader, shape, en, nodeid, tile, directededge); + newedge.set_endnode(en); + aggregated = true; + } + } + + // Hammerhead specific. bike network not saved to edgeinfo + bool added; encoded_shape = encode7(shape); uint32_t w = hasher(encoded_shape + std::to_string(edgeinfo.wayid())); uint32_t edge_info_offset = @@ -707,17 +723,14 @@ void AggregateTiles(GraphReader& reader, std::unordered_map& o edgeinfo.GetNames(), edgeinfo.GetTaggedValues(), edgeinfo.GetLinguisticTaggedValues(), edgeinfo.GetTypes(), added, diff_names); + newedge.set_edgeinfo_offset(edge_info_offset); - // length - auto length = valhalla::midgard::length(shape); - - // Compute a curvature metric [0-15]. TODO - use resampled polyline? - uint32_t curvature = compute_curvature(shape); - - newedge.set_length(length); - newedge.set_curvature(curvature); + // Update length and curvature if the edge was aggregated + if (aggregated) { + newedge.set_length(valhalla::midgard::length(shape)); + newedge.set_curvature(compute_curvature(shape)); + } - newedge.set_edgeinfo_offset(edge_info_offset); // Add directed edge tilebuilder.directededges().emplace_back(std::move(newedge)); ++edge_count; @@ -776,7 +789,6 @@ void AggregateTiles(GraphReader& reader, std::unordered_map& o */ void UpdateEndNodes(GraphReader& reader, std::unordered_map& old_to_new) { LOG_INFO("Update end nodes of directed edges"); - // Iterate through all tiles in the local level auto local_tiles = reader.GetTileSet(TileHierarchy::levels().back().level); for (const auto& tile_id : local_tiles) { @@ -826,6 +838,66 @@ void UpdateEndNodes(GraphReader& reader, std::unordered_map& o } } +/** + * Update Opposing Edge Index of all directed edges. + * @param reader Graph reader. + */ +void UpdateOpposingEdgeIndex(GraphReader& reader) { + LOG_INFO("Update Opposing Edge Index of directed edges"); + + // Iterate through all tiles in the local level + auto local_tiles = reader.GetTileSet(TileHierarchy::levels().back().level); + for (const auto& tile_id : local_tiles) { + GraphTileBuilder tilebuilder(reader.tile_dir(), tile_id, false); + + // Get the graph tile. Read from this tile to create the new tile. + graph_tile_ptr tile = reader.GetGraphTile(tile_id); + assert(tile); + + // Copy nodes (they do not change) + std::vector nodes; + size_t n = tile->header()->nodecount(); + nodes.reserve(n); + const NodeInfo* orig_nodes = tile->node(0); + std::copy(orig_nodes, orig_nodes + n, std::back_inserter(nodes)); + + // Iterate through all directed edges - update end nodes + std::vector directededges; + + GraphId nodeid(tile_id.tileid(), tile_id.level(), 0); + for (uint32_t i = 0; i < tile->header()->nodecount(); ++i, ++nodeid) { + const NodeInfo* nodeinfo = tile->node(nodeid); + GraphId edgeid(nodeid.tileid(), nodeid.level(), nodeinfo->edge_index()); + for (uint32_t j = 0; j < nodeinfo->edge_count(); ++j, ++edgeid) { + // Check if the directed edge should be included + const DirectedEdge* edge = tile->directededge(edgeid); + + // Copy the edge to the directededges vector and update the end node + directededges.push_back(*edge); + DirectedEdge& new_edge = directededges.back(); + + // Get the tile at the end node + graph_tile_ptr endnodetile; + if (tile->id() == edge->endnode().Tile_Base()) { + endnodetile = tile; + } else { + endnodetile = reader.GetGraphTile(edge->endnode()); + } + + // Set the opposing index on the local level + new_edge.set_opp_local_idx(GetOpposingEdgeIndex(endnodetile, nodeid, tile, *edge)); + } + } + + // Update the tile with new directededges. + tilebuilder.Update(nodes, directededges); + + if (reader.OverCommitted()) { + reader.Trim(); + } + } +} + } // namespace namespace valhalla { @@ -876,6 +948,10 @@ void GraphFilter::Filter(const boost::property_tree::ptree& pt) { reader.Clear(); UpdateEndNodes(reader, old_to_new); + // Update Opposing Edge Index. Clear the GraphReader cache first. + reader.Clear(); + UpdateOpposingEdgeIndex(reader); + LOG_INFO("Done GraphFilter"); } diff --git a/src/mjolnir/graphtilebuilder.cc b/src/mjolnir/graphtilebuilder.cc index 0df1ebe701..3abbfafa71 100644 --- a/src/mjolnir/graphtilebuilder.cc +++ b/src/mjolnir/graphtilebuilder.cc @@ -180,9 +180,10 @@ GraphTileBuilder::GraphTileBuilder(const std::string& tile_dir, // Verify the offsets match as we create the edge info builder list if (offset != edge_info_offset_) { - LOG_WARN("GraphTileBuilder TileID: " + std::to_string(header_->graphid().tileid()) + - " offset stored in directed edge: = " + std::to_string(offset) + - " current ei offset= " + std::to_string(edge_info_offset_)); + LOG_ERROR("GraphTileBuilder TileID: " + std::to_string(header_->graphid().tileid()) + + " offset stored in directed edge: = " + std::to_string(offset) + + " current ei offset= " + std::to_string(edge_info_offset_)); + throw std::runtime_error("EdgeInfo offsets incorrect when reading GraphTile"); } // At this time, encoded elevation is empty and does not need to be serialized... diff --git a/src/mjolnir/shortcutbuilder.cc b/src/mjolnir/shortcutbuilder.cc index 48921a2409..ae2de4b0b2 100644 --- a/src/mjolnir/shortcutbuilder.cc +++ b/src/mjolnir/shortcutbuilder.cc @@ -271,16 +271,16 @@ bool CanContract(GraphReader& reader, } // Connect 2 edges shape and update the next end node in the new level -uint32_t ConnectEdges(GraphReader& reader, - const GraphId& startnode, - const GraphId& edgeid, - std::list& shape, - GraphId& endnode, - uint32_t& opp_local_idx, - uint32_t& restrictions, - float& average_density, - float& total_duration, - float& total_truck_duration) { +void ConnectEdges(GraphReader& reader, + const GraphId& startnode, + const GraphId& edgeid, + std::list& shape, + GraphId& endnode, + uint32_t& opp_local_idx, + uint32_t& restrictions, + float& average_density, + float& total_duration, + float& total_truck_duration) { // Get the tile and directed edge. auto tile = reader.GetGraphTile(startnode); const DirectedEdge* directededge = tile->directededge(edgeid); @@ -324,9 +324,8 @@ uint32_t ConnectEdges(GraphReader& reader, // Add to the weighted average average_density += directededge->length() * directededge->density(); - // Update the end node and return the length + // Update the end node endnode = directededge->endnode(); - return directededge->length(); } // Check if the edge is entering a contracted node @@ -379,18 +378,18 @@ uint32_t AddShortcutEdges(GraphReader& reader, if (IsEnteringEdgeOfContractedNode(reader, end_node, edge_id)) { // Form a shortcut edge. DirectedEdge newedge = *directededge; - uint32_t length = newedge.length(); // For computing weighted density and total turn duration along the shortcut - float average_density = length * newedge.density(); + uint32_t edge_length = newedge.length(); + float average_density = edge_length * newedge.density(); uint32_t const speed = tile->GetSpeed(directededge, kNoFlowMask); assert(speed != 0); - float total_duration = length / (speed * kKPHtoMetersPerSec); + float total_duration = edge_length / (speed * kKPHtoMetersPerSec); uint32_t const truck_speed = std::min(tile->GetSpeed(directededge, kNoFlowMask, kInvalidSecondsOfWeek, true), directededge->truck_speed() ? directededge->truck_speed() : kMaxAssumedTruckSpeed); assert(truck_speed != 0); - float total_truck_duration = directededge->length() / (truck_speed * kKPHtoMetersPerSec); + float total_truck_duration = edge_length / (truck_speed * kKPHtoMetersPerSec); // Get the shape for this edge. If this initial directed edge is not // forward - reverse the shape so the edge info stored is forward for @@ -452,12 +451,17 @@ uint32_t AddShortcutEdges(GraphReader& reader, // end node in the new level). Keep track of the last restriction // on the connected shortcut - need to set that so turn restrictions // off of shortcuts work properly - length += ConnectEdges(reader, end_node, next_edge_id, shape, end_node, opp_local_idx, rst, - average_density, total_duration, total_truck_duration); + ConnectEdges(reader, end_node, next_edge_id, shape, end_node, opp_local_idx, rst, + average_density, total_duration, total_truck_duration); } + // Names can be different in the forward and backward direction bool diff_names = tilebuilder.OpposingEdgeInfoDiffers(tile, directededge); + // Get the length from the shape. This prevents roundoff issues when forming + // elevation. + uint32_t length = valhalla::midgard::length(shape); + // Add the edge info. Use length and number of shape points to match an // edge in case multiple shortcut edges exist between the 2 nodes. // Test whether this shape is forward or reverse (in case an existing diff --git a/src/mjolnir/util.cc b/src/mjolnir/util.cc index 89b98cafb3..b7d476c8d5 100644 --- a/src/mjolnir/util.cc +++ b/src/mjolnir/util.cc @@ -170,6 +170,41 @@ bool shapes_match(const std::vector& shape1, const std::vector } } +/** + * Get the index of the opposing edge at the end node. This is on the local hierarchy, + * before adding transition and shortcut edges. Make sure that even if the end nodes + * and lengths match that the correct edge is selected (match shape) since some loops + * can have the same length and end node. + */ +uint32_t GetOpposingEdgeIndex(const graph_tile_ptr& endnodetile, + const baldr::GraphId& startnode, + const graph_tile_ptr& tile, + const baldr::DirectedEdge& edge) { + // Get the nodeinfo at the end of the edge + const baldr::NodeInfo* nodeinfo = endnodetile->node(edge.endnode().id()); + + // Iterate through the directed edges and return when the end node matches the specified + // node, the length matches, and the shape matches (or edgeinfo offset matches) + const baldr::DirectedEdge* directededge = endnodetile->directededge(nodeinfo->edge_index()); + for (uint32_t i = 0; i < nodeinfo->edge_count(); i++, directededge++) { + if (directededge->endnode() == startnode && directededge->length() == edge.length()) { + // If in the same tile and the edgeinfo offset matches then the shape and names will match + if (endnodetile == tile && directededge->edgeinfo_offset() == edge.edgeinfo_offset()) { + return i; + } else { + // Need to compare shape if not in the same tile or different EdgeInfo (could be different + // names in opposing directions) + if (shapes_match(tile->edgeinfo(&edge).shape(), + endnodetile->edgeinfo(directededge).shape())) { + return i; + } + } + } + } + LOG_ERROR("Could not find opposing edge index"); + return baldr::kMaxEdgesPerNode; +} + std::shared_ptr make_spatialite_cache(sqlite3* handle) { if (!handle) { return nullptr; diff --git a/test/gurka/test_shortcut.cc b/test/gurka/test_shortcut.cc index ad47fd1d70..b16f357fc9 100644 --- a/test/gurka/test_shortcut.cc +++ b/test/gurka/test_shortcut.cc @@ -394,8 +394,8 @@ TEST(Shortcuts, ShortcutRestrictions) { gurka::findEdge(reader, layout, "highway", end_node, baldr::GraphId{}, 0, true); EXPECT_TRUE(std::get<1>(shortcut)->is_shortcut()); EXPECT_TRUE(std::get<3>(shortcut)->is_shortcut()); - EXPECT_EQ(std::get<1>(shortcut)->length(), 3000); - EXPECT_EQ(std::get<3>(shortcut)->length(), 3000); + EXPECT_NEAR(std::get<1>(shortcut)->length(), 3000, 1); + EXPECT_NEAR(std::get<3>(shortcut)->length(), 3000, 1); } // test the right edges are really superseded by a shortcut @@ -435,7 +435,7 @@ TEST(Shortcuts, ShortcutRestrictions) { gurka::findEdge(reader2, layout, "highway", end_node, baldr::GraphId{}, 0, true); EXPECT_TRUE(std::get<1>(shortcut)->is_shortcut()); EXPECT_TRUE(std::get<3>(shortcut)->is_shortcut()); - EXPECT_EQ(std::get<1>(shortcut)->length(), 7500); - EXPECT_EQ(std::get<3>(shortcut)->length(), 7500); + EXPECT_NEAR(std::get<1>(shortcut)->length(), 7500, 1); + EXPECT_NEAR(std::get<3>(shortcut)->length(), 7500, 1); } } diff --git a/valhalla/mjolnir/util.h b/valhalla/mjolnir/util.h index 99e23c4922..39d4947577 100644 --- a/valhalla/mjolnir/util.h +++ b/valhalla/mjolnir/util.h @@ -12,7 +12,9 @@ // needs to be after sqlite include #include +#include #include +#include #include #include #include @@ -127,6 +129,21 @@ std::string remove_double_quotes(const std::string& s); bool shapes_match(const std::vector& shape1, const std::vector& shape2); +/** + * Get the index of the opposing edge at the end node. This is on the local hierarchy, + * before adding transition and shortcut edges. Make sure that even if the end nodes + * and lengths match that the correct edge is selected (match shape) since some loops + * can have the same length and end node. + * @param endnodetile Graph tile at the end node. + * @param startnode Start node of the directed edge. + * @param tile Graph tile of the edge + * @param directededge Directed edge to match. + */ +uint32_t GetOpposingEdgeIndex(const baldr::graph_tile_ptr& endnodetile, + const baldr::GraphId& startnode, + const graph_tile_ptr& tile, + const baldr::DirectedEdge& edge); + /** * Compute a curvature metric given an edge shape. * @param shape Shape of an edge (list of lat,lon vertices). From e7a5c162a666fd4a0bda05dd17053888241eb7b2 Mon Sep 17 00:00:00 2001 From: grzezlo <13370844+grzezlo@users.noreply.github.com> Date: Wed, 14 Feb 2024 14:39:37 +0100 Subject: [PATCH 031/198] Blind user mode. (#3694) Co-authored-by: Christian Beiwinkel --- CHANGELOG.md | 3 +- docs/docs/api/turn-by-turn/api-reference.md | 1 + locales/bg-BG.json | 146 ++++++++++++--- locales/ca-ES.json | 146 ++++++++++++--- locales/cs-CZ.json | 146 ++++++++++++--- locales/da-DK.json | 146 ++++++++++++--- locales/de-DE.json | 146 ++++++++++++--- locales/el-GR.json | 146 ++++++++++++--- locales/en-GB.json | 146 ++++++++++++--- locales/en-US-x-pirate.json | 146 ++++++++++++--- locales/en-US.json | 146 ++++++++++++--- locales/es-ES.json | 146 ++++++++++++--- locales/et-EE.json | 146 ++++++++++++--- locales/fi-FI.json | 146 ++++++++++++--- locales/fr-FR.json | 146 ++++++++++++--- locales/hi-IN.json | 146 ++++++++++++--- locales/hu-HU.json | 146 ++++++++++++--- locales/it-IT.json | 146 ++++++++++++--- locales/ja-JP.json | 146 ++++++++++++--- locales/nb-NO.json | 146 ++++++++++++--- locales/nl-NL.json | 146 ++++++++++++--- locales/pl-PL.json | 166 ++++++++++++++---- locales/pt-BR.json | 146 ++++++++++++--- locales/pt-PT.json | 146 ++++++++++++--- locales/ro-RO.json | 146 ++++++++++++--- locales/ru-RU.json | 148 +++++++++++++--- locales/sk-SK.json | 148 +++++++++++++--- locales/sl-SI.json | 146 ++++++++++++--- locales/sv-SE.json | 146 ++++++++++++--- locales/tr-TR.json | 146 ++++++++++++--- locales/uk-UA.json | 146 ++++++++++++--- proto/common.proto | 1 + proto/trip.proto | 2 + src/odin/enhancedtrippath.cc | 7 +- src/odin/maneuver.cc | 43 ++++- src/odin/maneuversbuilder.cc | 57 +++++- src/odin/narrative_dictionary.cc | 14 ++ src/odin/narrativebuilder.cc | 94 ++++++++-- src/sif/pedestriancost.cc | 2 +- src/thor/triplegbuilder.cc | 59 ++++++- taginfo.json | 7 + test/gurka/test_costing_type_blind.cc | 146 +++++++++++++++ .../test_route_with_narrative_languages.cc | 4 +- test/narrative_dictionary.cc | 6 +- valhalla/odin/enhancedtrippath.h | 15 ++ valhalla/odin/maneuver.h | 18 ++ valhalla/odin/narrative_dictionary.h | 26 +++ valhalla/odin/narrativebuilder.h | 2 + valhalla/proto_conversions.h | 12 +- valhalla/sif/costconstants.h | 2 +- 50 files changed, 3967 insertions(+), 812 deletions(-) create mode 100644 test/gurka/test_costing_type_blind.cc diff --git a/CHANGELOG.md b/CHANGELOG.md index fa7a67e7b9..0db52f603f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Release Date: 2023-??-?? Valhalla 3.4.1 +## Release Date: 2024-??-?? Valhalla 3.4.1 * **Removed** * REMOVED: needs_ci_run script [#4423](https://github.com/valhalla/valhalla/pull/4423) * REMOVED: unused vehicle types in AutoCost and segway; renamed kTruck to "truck" instead of "tractor_trailer" [#4430](https://github.com/valhalla/valhalla/pull/4430) @@ -90,6 +90,7 @@ * ADDED: matrix second pass for connections not found in the first pass, analogous to /route [#4536](https://github.com/valhalla/valhalla/pull/4536) * UPDATED: cxxopts to 3.1.1 [#4541](https://github.com/valhalla/valhalla/pull/4541) * CHANGED: make use of vendored libraries optional (other than libraries which are not commonly in package managers or only used for testing) [#4544](https://github.com/valhalla/valhalla/pull/4544) + * ADDED: Improved instructions for blind users [#3694](https://github.com/valhalla/valhalla/pull/3694) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 6fdc13a4fe..18c131d0a6 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -221,6 +221,7 @@ These options are available for pedestrian costing methods. | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | | `transit_start_end_max_distance` | A pedestrian option that can be added to the request to extend the defaults (2145 meters or approximately 1.5 miles). This is the maximum walking distance at the beginning or end of a route.| | `transit_transfer_max_distance` | A pedestrian option that can be added to the request to extend the defaults (800 meters or 0.5 miles). This is the maximum walking distance between transfers.| +| `type` | If set to `blind`, enables additional route instructions, especially usefull for blind users: Announcing crossed streets, the stairs, bridges, tunnels, gates and bollards, which need to be passed on route; information about traffic signals on crosswalks; route numbers not announced for named routes. Default `foot` | ##### Transit costing options diff --git a/locales/bg-BG.json b/locales/bg-BG.json index 622eb709d4..4529332389 100644 --- a/locales/bg-BG.json +++ b/locales/bg-BG.json @@ -45,7 +45,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -85,7 +88,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -143,7 +149,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " километра", @@ -229,7 +241,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ферибот", "example_phrases": { @@ -395,7 +413,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ферибот", "example_phrases": { @@ -448,7 +469,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -949,7 +985,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -984,7 +1023,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -1023,7 +1065,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -1076,7 +1121,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "километри", @@ -1399,7 +1453,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -1439,7 +1496,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -1495,7 +1555,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "километри", @@ -1958,7 +2024,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -1998,7 +2067,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -2040,7 +2112,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -2088,7 +2163,10 @@ "пешеходата пътека", "вело алеята", "пътя за планински велосипеди", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ляво", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/ca-ES.json b/locales/ca-ES.json index bac124f069..ca5f2d042d 100644 --- a/locales/ca-ES.json +++ b/locales/ca-ES.json @@ -45,7 +45,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -85,7 +88,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -143,7 +149,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilòmetres", @@ -229,7 +241,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -949,7 +985,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -984,7 +1023,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -1023,7 +1065,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -1076,7 +1121,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilòmetres", @@ -1399,7 +1453,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -1439,7 +1496,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -1495,7 +1555,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilòmetres", @@ -1958,7 +2024,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -1998,7 +2067,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -2040,7 +2112,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -2088,7 +2163,10 @@ "la vorera", "el carril bici", "la pista de bicicleta de muntanya", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "l'esquerra", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/cs-CZ.json b/locales/cs-CZ.json index f0d29224c7..d5d42b66d9 100644 --- a/locales/cs-CZ.json +++ b/locales/cs-CZ.json @@ -45,7 +45,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -85,7 +88,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -143,7 +149,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrů", @@ -229,7 +241,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "chodník", "tcyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Trajekt", "example_phrases": { @@ -395,7 +413,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Trajekt", "example_phrases": { @@ -448,7 +469,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -949,7 +985,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -984,7 +1023,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -1023,7 +1065,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -1076,7 +1121,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrů", @@ -1399,7 +1453,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -1439,7 +1496,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -1495,7 +1555,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrů", @@ -1958,7 +2024,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -1998,7 +2067,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -2040,7 +2112,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -2088,7 +2163,10 @@ "chodník", "cyklostezka", "stezka pro horská kola", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vlevo", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/da-DK.json b/locales/da-DK.json index 94fad718ac..c8618f3764 100644 --- a/locales/da-DK.json +++ b/locales/da-DK.json @@ -45,7 +45,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -85,7 +88,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -143,7 +149,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -229,7 +241,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Færge", "example_phrases": { @@ -395,7 +413,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Færge", "example_phrases": { @@ -448,7 +469,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -949,7 +985,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -984,7 +1023,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1023,7 +1065,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1076,7 +1121,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1399,7 +1453,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1439,7 +1496,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1495,7 +1555,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1958,7 +2024,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1998,7 +2067,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2040,7 +2112,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2088,7 +2163,10 @@ "fortovet", "cykelstien", "mountainbike-sporet", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/de-DE.json b/locales/de-DE.json index e3e79d9966..750d55a283 100644 --- a/locales/de-DE.json +++ b/locales/de-DE.json @@ -45,7 +45,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -85,7 +88,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -143,7 +149,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " Kilometer", @@ -229,7 +241,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Fähre", "example_phrases": { @@ -395,7 +413,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Fähre", "example_phrases": { @@ -448,7 +469,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -949,7 +985,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -984,7 +1023,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1023,7 +1065,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -1076,7 +1121,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " Kilometer", @@ -1399,7 +1453,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1439,7 +1496,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1495,7 +1555,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " Kilometer", @@ -1958,7 +2024,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -1998,7 +2067,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -2040,7 +2112,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -2088,7 +2163,10 @@ "Fußweg", "Radweg", "Mountainbike Strecke", - "Fußgängerübergang" + "Fußgängerübergang", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Den Aufzug nehmen.", diff --git a/locales/el-GR.json b/locales/el-GR.json index 74d56c4824..a0f3efd415 100644 --- a/locales/el-GR.json +++ b/locales/el-GR.json @@ -45,7 +45,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -85,7 +88,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -143,7 +149,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " χιλιόμετρα", @@ -229,7 +241,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Πλοίο", "example_phrases": { @@ -395,7 +413,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Πλοίο", "example_phrases": { @@ -448,7 +469,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -949,7 +985,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -984,7 +1023,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -1023,7 +1065,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -1076,7 +1121,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " χιλιόμετρα", @@ -1399,7 +1453,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -1439,7 +1496,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -1495,7 +1555,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " χιλιόμετρα", @@ -1958,7 +2024,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -1998,7 +2067,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -2040,7 +2112,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -2088,7 +2163,10 @@ "διάβαση πεζών", "ποδηλατοδρόμος ", "μονοπάτι ποδηλασίας βουνού", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "αριστερά", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/en-GB.json b/locales/en-GB.json index b68ea280d4..9e27c98552 100644 --- a/locales/en-GB.json +++ b/locales/en-GB.json @@ -43,7 +43,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -83,7 +86,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -141,7 +147,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -173,7 +182,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometres", @@ -227,7 +239,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -364,7 +379,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -393,7 +411,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -446,7 +467,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -543,7 +567,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -723,7 +750,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -751,7 +781,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -900,7 +933,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -947,7 +983,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -982,7 +1021,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1021,7 +1063,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1074,7 +1119,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1114,7 +1162,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1161,7 +1212,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometres", @@ -1397,7 +1451,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1437,7 +1494,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1493,7 +1553,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1581,7 +1644,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometres", @@ -1956,7 +2022,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1996,7 +2065,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2038,7 +2110,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2086,7 +2161,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2171,6 +2249,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the lift.", diff --git a/locales/en-US-x-pirate.json b/locales/en-US-x-pirate.json index 826e3ca309..db2aee4b63 100644 --- a/locales/en-US-x-pirate.json +++ b/locales/en-US-x-pirate.json @@ -45,7 +45,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -85,7 +88,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -143,7 +149,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -229,7 +241,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -949,7 +985,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -984,7 +1023,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1023,7 +1065,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1076,7 +1121,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -1399,7 +1453,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1439,7 +1496,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1495,7 +1555,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -1958,7 +2024,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "port", @@ -1998,7 +2067,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "port", @@ -2040,7 +2112,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "port", @@ -2088,7 +2163,10 @@ "solid ground", "th' bike pirate way", "th' treasure trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "port", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/en-US.json b/locales/en-US.json index fbd1eb226e..88630241a0 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -45,7 +45,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -85,7 +88,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -143,7 +149,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -229,7 +241,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -949,7 +985,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -984,7 +1023,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1023,7 +1065,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1076,7 +1121,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -1399,7 +1453,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1439,7 +1496,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1495,7 +1555,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometers", @@ -1958,7 +2024,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -1998,7 +2067,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2040,7 +2112,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2088,7 +2163,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "left", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/es-ES.json b/locales/es-ES.json index 3599b3b4b2..d50d8e3468 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -45,7 +45,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -85,7 +88,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -143,7 +149,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilómetros", @@ -229,7 +241,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferri", "example_phrases": { @@ -395,7 +413,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferri", "example_phrases": { @@ -448,7 +469,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -949,7 +985,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -984,7 +1023,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -1023,7 +1065,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -1076,7 +1121,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilómetros", @@ -1399,7 +1453,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -1439,7 +1496,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -1495,7 +1555,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilómetros", @@ -1958,7 +2024,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -1998,7 +2067,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -2040,7 +2112,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -2088,7 +2163,10 @@ "la calzada", "el carril bici", "el sendero para bicicletas de montaña", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "izquierda", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Tome el ascensor.", diff --git a/locales/et-EE.json b/locales/et-EE.json index b1b1f1e102..1b186c2829 100644 --- a/locales/et-EE.json +++ b/locales/et-EE.json @@ -45,7 +45,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -85,7 +88,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -143,7 +149,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilomeetrit", @@ -229,7 +241,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Praam", "example_phrases": { @@ -395,7 +413,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Praam", "example_phrases": { @@ -448,7 +469,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakule", @@ -949,7 +985,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakule", @@ -984,7 +1023,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakule", @@ -1023,7 +1065,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -1076,7 +1121,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilomeetrit", @@ -1399,7 +1453,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -1439,7 +1496,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -1495,7 +1555,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilomeetrit", @@ -1958,7 +2024,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -1998,7 +2067,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -2040,7 +2112,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -2088,7 +2163,10 @@ "läbipääs", "rattatee", "rada mägiratastele", - "ülekäigurajal" + "ülekäigurajal", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasakul", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Astu lifti.", diff --git a/locales/fi-FI.json b/locales/fi-FI.json index 8b06698a55..6b96240faf 100644 --- a/locales/fi-FI.json +++ b/locales/fi-FI.json @@ -45,7 +45,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -85,7 +88,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -143,7 +149,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometriä", @@ -229,7 +241,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Lautta", "example_phrases": { @@ -395,7 +413,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Lautta", "example_phrases": { @@ -448,7 +469,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalla", @@ -949,7 +985,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalla", @@ -984,7 +1023,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalla", @@ -1023,7 +1065,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalla", @@ -1076,7 +1121,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometriä", @@ -1399,7 +1453,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -1439,7 +1496,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -1495,7 +1555,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometriä", @@ -1958,7 +2024,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -1998,7 +2067,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -2040,7 +2112,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -2088,7 +2163,10 @@ "kävelyreitti", "pyöräilyreitti", "maastopyöräreitti", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vasemmalle", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/fr-FR.json b/locales/fr-FR.json index 2ade4c228e..294fa4cc08 100644 --- a/locales/fr-FR.json +++ b/locales/fr-FR.json @@ -45,7 +45,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -85,7 +88,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -143,7 +149,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilomètres", @@ -229,7 +241,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -949,7 +985,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -984,7 +1023,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -1023,7 +1065,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -1076,7 +1121,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilomètres", @@ -1399,7 +1453,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -1439,7 +1496,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -1495,7 +1555,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilomètres", @@ -1958,7 +2024,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -1998,7 +2067,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -2040,7 +2112,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -2088,7 +2163,10 @@ "l'allée", "la piste cyclable", "la piste de vélo de montagne", - "le passage protégé" + "le passage protégé", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "gauche", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Prenez l'ascenseur.", diff --git a/locales/hi-IN.json b/locales/hi-IN.json index 1614c4753e..aa2e01035d 100644 --- a/locales/hi-IN.json +++ b/locales/hi-IN.json @@ -45,7 +45,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -85,7 +88,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -143,7 +149,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " किलोमीटर", @@ -229,7 +241,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -949,7 +985,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -984,7 +1023,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -1023,7 +1065,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -1076,7 +1121,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " किलोमीटर", @@ -1399,7 +1453,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -1439,7 +1496,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -1495,7 +1555,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " किलोमीटर", @@ -1958,7 +2024,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -1998,7 +2067,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -2040,7 +2112,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -2088,7 +2163,10 @@ "रास्ते", "साइकिल रास्ते", "माउंटेन साइकिल पगडंडी", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "बाएं", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/hu-HU.json b/locales/hu-HU.json index 8fa76dfb84..fc97c5a708 100644 --- a/locales/hu-HU.json +++ b/locales/hu-HU.json @@ -45,7 +45,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpár út", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -85,7 +88,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -143,7 +149,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilométerek", @@ -229,7 +241,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Komp", "example_phrases": { @@ -395,7 +413,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Komp", "example_phrases": { @@ -448,7 +469,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "a sétány", "kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "a sétány", "a kerékpárút", "a hegyi kerkpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -949,7 +985,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -984,7 +1023,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -1023,7 +1065,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -1076,7 +1121,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilométerek", @@ -1399,7 +1453,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -1439,7 +1496,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -1495,7 +1555,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilométerek", @@ -1958,7 +2024,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -1998,7 +2067,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -2040,7 +2112,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -2088,7 +2163,10 @@ "a sétány", "a kerékpárút", "a hegyi kerékpárút", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "balra", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Menjen a lifttel.", diff --git a/locales/it-IT.json b/locales/it-IT.json index 70e066b1d0..2d3029f362 100644 --- a/locales/it-IT.json +++ b/locales/it-IT.json @@ -45,7 +45,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -85,7 +88,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -143,7 +149,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "chilometri", @@ -229,7 +241,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Traghetto", "example_phrases": { @@ -395,7 +413,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Traghetto", "example_phrases": { @@ -448,7 +469,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -949,7 +985,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -984,7 +1023,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -1023,7 +1065,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -1076,7 +1121,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " chilometri", @@ -1399,7 +1453,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -1439,7 +1496,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -1495,7 +1555,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " chilometri", @@ -1958,7 +2024,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -1998,7 +2067,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -2040,7 +2112,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -2088,7 +2163,10 @@ "la passerella", "la pista ciclabile", "il percorso per mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sinistra", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/ja-JP.json b/locales/ja-JP.json index 6f88ff2ce2..54042425d9 100644 --- a/locales/ja-JP.json +++ b/locales/ja-JP.json @@ -45,7 +45,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -85,7 +88,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -143,7 +149,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "キロメートル", @@ -229,7 +241,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "フェリー", "example_phrases": { @@ -395,7 +413,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "フェリー", "example_phrases": { @@ -448,7 +469,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -949,7 +985,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -984,7 +1023,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -1023,7 +1065,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -1076,7 +1121,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "キロメートル", @@ -1399,7 +1453,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -1439,7 +1496,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -1495,7 +1555,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "キロメートル", @@ -1958,7 +2024,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -1998,7 +2067,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -2040,7 +2112,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -2088,7 +2163,10 @@ "歩道", "自転車道", "自転車道", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "左", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/nb-NO.json b/locales/nb-NO.json index 059d7aa923..1a4f42b530 100644 --- a/locales/nb-NO.json +++ b/locales/nb-NO.json @@ -45,7 +45,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -85,7 +88,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -143,7 +149,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -229,7 +241,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferje", "example_phrases": { @@ -395,7 +413,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferje", "example_phrases": { @@ -448,7 +469,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -949,7 +985,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -984,7 +1023,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1023,7 +1065,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1076,7 +1121,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1399,7 +1453,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1439,7 +1496,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1495,7 +1555,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1958,7 +2024,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -1998,7 +2067,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2040,7 +2112,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2088,7 +2163,10 @@ "gangveien", "sykkelveien", "terrengsykkelstien", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "venstre", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/nl-NL.json b/locales/nl-NL.json index 63fd829162..20597df4b1 100644 --- a/locales/nl-NL.json +++ b/locales/nl-NL.json @@ -45,7 +45,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -85,7 +88,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -143,7 +149,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -229,7 +241,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Veerboot", "example_phrases": { @@ -395,7 +413,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Veerboot", "example_phrases": { @@ -448,7 +469,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -949,7 +985,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -984,7 +1023,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -1023,7 +1065,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -1076,7 +1121,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1399,7 +1453,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1439,7 +1496,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1495,7 +1555,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1958,7 +2024,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -1998,7 +2067,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -2040,7 +2112,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "links", @@ -2088,7 +2163,10 @@ "het voetpad", "het fietspad", "het mountainbikepad", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "Links", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Neem de lift.", diff --git a/locales/pl-PL.json b/locales/pl-PL.json index 22ed49ee0e..8809846f69 100644 --- a/locales/pl-PL.json +++ b/locales/pl-PL.json @@ -45,7 +45,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -85,7 +88,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -143,7 +149,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "metric_lengths": [ " km", @@ -229,7 +241,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "ferry_label": "prom", "example_phrases": { @@ -395,7 +413,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "ferry_label": "prom", "example_phrases": { @@ -448,7 +469,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "lewej strony", @@ -949,7 +985,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "lewej strony", @@ -984,7 +1023,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "lewej strony", @@ -1023,7 +1065,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "lewej strony", @@ -1076,7 +1121,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "metric_lengths": [ " km", @@ -1399,7 +1453,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -1439,7 +1496,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -1495,7 +1555,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "metric_lengths": [ " km", @@ -1948,8 +2014,8 @@ "turn": { "phrases": { "0": "Skręć .", - "1": "Skręć w stronę: .", - "2": "Skręć w stronę: . Kontynuuj wzdłuż: .", + "1": "Skręć w .", + "2": "Skręć w . Kontynuuj wzdłuż: .", "3": "Skręć , pozostając na: .", "4": "Skręć na .", "5": "Skręć , kierunek: ." @@ -1958,7 +2024,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -1988,8 +2057,8 @@ "turn_verbal": { "phrases": { "0": "Skręć .", - "1": "Skręć w stronę: .", - "2": "Skręć w stronę: .", + "1": "Skręć w .", + "2": "Skręć w .", "3": "Skręć , pozostając na: .", "4": "Skręć na .", "5": "Skręć , kierunek: ." @@ -1998,7 +2067,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -2028,7 +2100,7 @@ "uturn": { "phrases": { "0": "Zawróć .", - "1": "Zawróć w stronę: .", + "1": "Zawróć w .", "2": "Zawróć , pozostając na: .", "3": "Zawróć na: .", "4": "Zawróć na: w stronę: .", @@ -2040,7 +2112,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -2076,7 +2151,7 @@ "uturn_verbal": { "phrases": { "0": "Zawróć .", - "1": "Zawróć w stronę: .", + "1": "Zawróć w .", "2": "Zawróć , pozostając na: .", "3": "Zawróć na: .", "4": "Zawróć na: w stronę: .", @@ -2088,7 +2163,10 @@ "chodnik", "ścieżka rowerowa", "górski szlak rowerowy", - "przejście dla pieszych" + "przejście dla pieszych", + "schody", + "most", + "tunel" ], "relative_directions": [ "w lewo", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Przejdź .", + "1": "Przejdź na światłach ." + }, + "object_labels": [ + "bramę", + "słupki", + "skrzyżowanie dróg" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Skorzystaj z windy.", @@ -2180,10 +2274,10 @@ }, "example_phrases": { "0": [ - "Take the elevator." + "Wsiądź do windy." ], "1": [ - "Take the elevator to Level 1." + "Wsiądź do windy i jedź na poziom 1." ] } }, @@ -2194,10 +2288,10 @@ }, "example_phrases": { "0": [ - "Take the stairs." + "Wejdź na schody." ], "1": [ - "Take the stairs to Level 2." + "Przejdź schodami na poziom 2." ] } }, diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 67e198ae8b..019f6fabac 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -43,7 +43,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -83,7 +86,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -141,7 +147,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -173,7 +182,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilômetros", @@ -227,7 +239,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -364,7 +379,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Balsa", "example_phrases": { @@ -393,7 +411,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Balsa", "example_phrases": { @@ -446,7 +467,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -543,7 +567,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -723,7 +750,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -751,7 +781,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -900,7 +933,10 @@ "a estrada pedonal", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -947,7 +983,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -982,7 +1021,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -1021,7 +1063,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -1074,7 +1119,10 @@ "calçada", "ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1114,7 +1162,10 @@ "calçada", "ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1161,7 +1212,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilômetros", @@ -1397,7 +1451,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -1437,7 +1494,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -1493,7 +1553,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1581,7 +1644,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilômetros", @@ -1956,7 +2022,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -1996,7 +2065,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -2038,7 +2110,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -2086,7 +2161,10 @@ "a calçada", "a ciclovia", "a trilha de mountain bike", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "à esquerda", @@ -2171,6 +2249,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/pt-PT.json b/locales/pt-PT.json index c580c9e10c..8b5300fcde 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -45,7 +45,10 @@ "o passeio", "a ciclovia", "o trilho para bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -85,7 +88,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -143,7 +149,10 @@ "o passeio", "a ciclovia", "o trilho de bibicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilómetros", @@ -229,7 +241,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -395,7 +413,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Ferry", "example_phrases": { @@ -448,7 +469,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -949,7 +985,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -984,7 +1023,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -1023,7 +1065,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -1076,7 +1121,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilómetros", @@ -1399,7 +1453,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -1439,7 +1496,10 @@ "o passeio", "a ciclovia", "o trilho de bicileta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -1495,7 +1555,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " quilómetros", @@ -1958,7 +2024,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -1998,7 +2067,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -2040,7 +2112,10 @@ "o passeio", "a ciclovia", "o trilho de bicilceta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -2088,7 +2163,10 @@ "o passeio", "a ciclovia", "o trilho de bicicleta de montanha", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "esquerda", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/ro-RO.json b/locales/ro-RO.json index cc66be3a12..aab7bcfe66 100644 --- a/locales/ro-RO.json +++ b/locales/ro-RO.json @@ -45,7 +45,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "trecerea de pietoni" + "trecerea de pietoni", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -85,7 +88,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "trecerea de pietoni" + "trecerea de pietoni", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -143,7 +149,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "trecerea de pietoni" + "trecerea de pietoni", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "trecerea de pietoni" + "trecerea de pietoni", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometri", @@ -229,7 +241,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Feribot", "example_phrases": { @@ -395,7 +413,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Feribot", "example_phrases": { @@ -448,7 +469,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "alee", "pistă de biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "alee", "pistă de biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -949,7 +985,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -984,7 +1023,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -1023,7 +1065,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -1076,7 +1121,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometri", @@ -1399,7 +1453,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -1439,7 +1496,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -1495,7 +1555,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "alee", "pistă de biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometri", @@ -1958,7 +2024,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -1998,7 +2067,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -2040,7 +2112,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -2088,7 +2163,10 @@ "alee", "pistă pentru biciclete", "traseu pentru ciclism montan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "stânga", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/ru-RU.json b/locales/ru-RU.json index 52c6f3fe1c..e1dcdecacb 100644 --- a/locales/ru-RU.json +++ b/locales/ru-RU.json @@ -45,7 +45,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -85,7 +88,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -143,7 +149,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " километров", @@ -229,7 +241,10 @@ "пешеходная дорожка", "велосипедная дорожка", "дорожка для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Паром", "example_phrases": { @@ -395,7 +413,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Паром", "example_phrases": { @@ -448,7 +469,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "левее", @@ -949,7 +985,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "левее", @@ -984,7 +1023,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "левее", @@ -1023,7 +1065,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "левой стороны", @@ -1076,7 +1121,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " километров", @@ -1399,7 +1453,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -1439,7 +1496,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходному переходу", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -1495,7 +1555,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходному переходу" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " километров", @@ -1958,7 +2024,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -1998,7 +2067,10 @@ "пешеходную дорожку", "велосипедную дорожку", "дорожку для горных велосипедов", - "пешеходный переход" + "пешеходный переход", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -2039,8 +2111,11 @@ "empty_street_name_labels": [ "пешеходной дорожке", "велосипедной дорожке", + "пешеходном переходе", "дорожке для горных велосипедов", - "пешеходном переходе" + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -2088,7 +2163,10 @@ "пешеходной дорожке", "велосипедной дорожке", "дорожке для горных велосипедов", - "пешеходном переходе" + "пешеходном переходе", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "налево", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Воспользуйтесь лифтом.", @@ -2224,7 +2318,7 @@ "пешеходной дорожке", "велосипедную дорожку", "дорожка для горных велосипедов", - "пешеходному переходу" + "пешеходный переход" ], "example_phrases": { "0": [ diff --git a/locales/sk-SK.json b/locales/sk-SK.json index 6b6f587828..d41f248a10 100644 --- a/locales/sk-SK.json +++ b/locales/sk-SK.json @@ -43,9 +43,12 @@ }, "empty_street_name_labels": [ "chodník", - "cyklocesta", +"cyklocesta", "cesta pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -85,7 +88,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -143,7 +149,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -229,7 +241,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "trajekt", "example_phrases": { @@ -395,7 +413,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "trajekt", "example_phrases": { @@ -448,7 +469,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -949,7 +985,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -984,7 +1023,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -1023,7 +1065,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -1076,7 +1121,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -1399,7 +1453,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -1439,7 +1496,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -1495,7 +1555,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "chodníku", "cykloceste", "ceste pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -1958,7 +2024,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -1998,7 +2067,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -2040,7 +2112,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -2088,7 +2163,10 @@ "chodník", "cyklocestu", "cestu pre horské bicykle", - "priechod pre chodcov" + "priechod pre chodcov", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vľavo", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/sl-SI.json b/locales/sl-SI.json index 00f974b643..0a2e8fb9b0 100644 --- a/locales/sl-SI.json +++ b/locales/sl-SI.json @@ -45,7 +45,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -85,7 +88,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -143,7 +149,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -229,7 +241,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Trajekt", "example_phrases": { @@ -395,7 +413,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Trajekt", "example_phrases": { @@ -448,7 +469,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "the walkway", "the cycleway", "the mountain bike trail", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -949,7 +985,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -984,7 +1023,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -1023,7 +1065,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -1076,7 +1121,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -1399,7 +1453,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -1439,7 +1496,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -1495,7 +1555,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "pešpoti", "kolesarski stezi", "stezi za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometrov", @@ -1958,7 +2024,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -1998,7 +2067,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -2040,7 +2112,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -2088,7 +2163,10 @@ "pešpot", "kolesarsko stezo", "stezo za gorsko kolo", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "levo", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/sv-SE.json b/locales/sv-SE.json index 0b9fa77ba5..859f7d444b 100644 --- a/locales/sv-SE.json +++ b/locales/sv-SE.json @@ -45,7 +45,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -85,7 +88,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -143,7 +149,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -229,7 +241,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Färja", "example_phrases": { @@ -395,7 +413,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Färja", "example_phrases": { @@ -448,7 +469,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -949,7 +985,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -984,7 +1023,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -1023,7 +1065,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -1076,7 +1121,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1399,7 +1453,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -1439,7 +1496,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -1495,7 +1555,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometer", @@ -1958,7 +2024,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -1998,7 +2067,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -2040,7 +2112,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -2088,7 +2163,10 @@ "gångvägen", "cykelbanan", "terrängcykelbanan", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "vänster", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/tr-TR.json b/locales/tr-TR.json index ff5b0f0dcb..193d5b6940 100644 --- a/locales/tr-TR.json +++ b/locales/tr-TR.json @@ -45,7 +45,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -85,7 +88,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -143,7 +149,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " kilometre", @@ -229,7 +241,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Feribot", "example_phrases": { @@ -395,7 +413,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Feribot", "example_phrases": { @@ -448,7 +469,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -949,7 +985,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -984,7 +1023,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -1023,7 +1065,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -1076,7 +1121,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "kilometre", @@ -1399,7 +1453,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -1439,7 +1496,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -1495,7 +1555,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ "kilometre", @@ -1958,7 +2024,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -1998,7 +2067,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -2040,7 +2112,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -2088,7 +2163,10 @@ "yaya geçidi", "bisiklet yolu", "dağ bisikleti yolu", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "sol", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/locales/uk-UA.json b/locales/uk-UA.json index ade762af37..fc1a180739 100644 --- a/locales/uk-UA.json +++ b/locales/uk-UA.json @@ -45,7 +45,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -85,7 +88,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -143,7 +149,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -175,7 +184,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " кілометрів", @@ -229,7 +241,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -366,7 +381,10 @@ "на пішохідній доріжці", "на велодоріжці", "на шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Пором", "example_phrases": { @@ -395,7 +413,10 @@ "на пішохідній доріжці", "на велодоріжці", "на шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "ferry_label": "Пором", "example_phrases": { @@ -448,7 +469,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -545,7 +569,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -725,7 +752,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -753,7 +783,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -902,7 +935,10 @@ "пішохідну доріжку", "велодоріжку", "шлях для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -949,7 +985,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -984,7 +1023,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -1023,7 +1065,10 @@ "пішохідну доріжку", "велодоріжку", "шлях для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -1076,7 +1121,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1116,7 +1164,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1163,7 +1214,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " кілометрів", @@ -1399,7 +1453,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -1439,7 +1496,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -1495,7 +1555,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "example_phrases": { "0": [ @@ -1583,7 +1646,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "metric_lengths": [ " кілометрів", @@ -1958,7 +2024,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -1998,7 +2067,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -2040,7 +2112,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -2088,7 +2163,10 @@ "пішохідній доріжці", "велодоріжці", "шляху для гірських велосипедів", - "the crosswalk" + "the crosswalk", + "the stairs", + "the bridge", + "the tunnel" ], "relative_directions": [ "ліворуч", @@ -2173,6 +2251,22 @@ ] } }, + "pass": { + "phrases": { + "0": "Pass .", + "1": "Pass traffic signals on ." + }, + "object_labels": [ + "the gate", + "the bollards", + "ways intersection" + ], + "example_phrases": { + "0": [ + "Pass the gate." + ] + } + }, "elevator": { "phrases": { "0": "Take the elevator.", diff --git a/proto/common.proto b/proto/common.proto index a99d74d317..1b1c7b58db 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -371,6 +371,7 @@ enum VehicleType { enum PedestrianType { kFoot = 0; kWheelchair = 1; + kBlind = 2; } enum BicycleType { diff --git a/proto/trip.proto b/proto/trip.proto index 223acdfd08..b49af1d981 100644 --- a/proto/trip.proto +++ b/proto/trip.proto @@ -193,6 +193,7 @@ message TripLeg { RoadClass road_class = 8; uint32 lane_count = 9; TripSign sign = 10; + repeated StreetName name = 21; // street names } message Cost { @@ -236,6 +237,7 @@ message TripLeg { PathCost cost = 12; // how much cost did it take at this node in the path repeated PathCost recosts = 13; // how much cost did it take at this node in the path for recostings BikeShareStationInfo bss_info = 14; + bool traffic_signal = 21; } message Admin { diff --git a/src/odin/enhancedtrippath.cc b/src/odin/enhancedtrippath.cc index 42e7ea6659..595f2feda5 100644 --- a/src/odin/enhancedtrippath.cc +++ b/src/odin/enhancedtrippath.cc @@ -131,10 +131,9 @@ const std::string& TripLeg_VehicleType_Name(int v) { } const std::string& TripLeg_PedestrianType_Name(int v) { - static const std::unordered_map values{ - {0, "kFoot"}, - {1, "kWheelchair"}, - }; + static const std::unordered_map values{{0, "kFoot"}, + {1, "kWheelchair"}, + {2, "kBlind"}}; auto f = values.find(v); if (f == values.cend()) throw std::runtime_error("Missing value in protobuf enum to string"); diff --git a/src/odin/maneuver.cc b/src/odin/maneuver.cc index a4747e29c8..d2ea4ff5eb 100644 --- a/src/odin/maneuver.cc +++ b/src/odin/maneuver.cc @@ -135,7 +135,8 @@ Maneuver::Maneuver() imminent_verbal_multi_cue_(false), distant_verbal_multi_cue_(false), to_stay_on_(false), drive_on_right_(true), has_time_restrictions_(false), has_right_traversable_outbound_intersecting_edge_(false), - has_left_traversable_outbound_intersecting_edge_(false), + has_left_traversable_outbound_intersecting_edge_(false), has_node_type_(false), + traffic_signal_(false), is_steps_(false), is_bridge_(false), is_tunnel_(false), bss_maneuver_type_(DirectionsLeg_Maneuver_BssManeuverType_kNoneAction), include_verbal_pre_transition_length_(false), contains_obvious_maneuver_(false), roundabout_exit_count_(0), has_combined_enter_exit_roundabout_(false), roundabout_length_(0.0f), @@ -160,6 +161,46 @@ void Maneuver::set_type(const DirectionsLeg_Maneuver_Type& type) { type_ = type; } +// Set the node type. +void Maneuver::set_node_type(const TripLeg_Node_Type type) { + node_type_ = type; + has_node_type_ = true; +} +/** + * Gets the node type. + * @return Returns the node type. + */ +TripLeg_Node_Type Maneuver::node_type() const { + return node_type_; +} +bool Maneuver::has_node_type() const { + return has_node_type_; +} +bool Maneuver::traffic_signal() const { + return traffic_signal_; +} +void Maneuver::set_traffic_signal(bool traffic_signal) { + traffic_signal_ = traffic_signal; +} +bool Maneuver::is_steps() const { + return is_steps_; +} +void Maneuver::set_steps(bool steps) { + is_steps_ = steps; +} +bool Maneuver::is_bridge() const { + return is_bridge_; +} +void Maneuver::set_bridge(bool bridge) { + is_bridge_ = bridge; +} +bool Maneuver::is_tunnel() const { + return is_tunnel_; +} +void Maneuver::set_tunnel(bool tunnel) { + is_tunnel_ = tunnel; +} + bool Maneuver::IsStartType() const { return ((type_ == DirectionsLeg_Maneuver_Type_kStart) || (type_ == DirectionsLeg_Maneuver_Type_kStartLeft) || diff --git a/src/odin/maneuversbuilder.cc b/src/odin/maneuversbuilder.cc index bc7f17f81b..4723116381 100644 --- a/src/odin/maneuversbuilder.cc +++ b/src/odin/maneuversbuilder.cc @@ -240,6 +240,7 @@ std::list ManeuversBuilder::Produce() { // Step through nodes in reverse order to produce maneuvers // excluding the last and first nodes for (int i = (trip_path_->GetLastNodeIndex() - 1); i > 0; --i) { + auto node = trip_path_->GetEnhancedNode(i); #ifdef LOGGING_LEVEL_TRACE auto prev_edge = trip_path_->GetPrevEdge(i); @@ -253,7 +254,6 @@ std::list ManeuversBuilder::Produce() { LOG_TRACE(std::string(" curr_edge=") + (curr_edge ? curr_edge->ToString() : "NONE")); LOG_TRACE(std::string(" prev2curr_turn_degree=") + std::to_string(prev2curr_turn_degree) + " is a " + Turn::GetTypeString(Turn::GetType(prev2curr_turn_degree))); - auto node = trip_path_->GetEnhancedNode(i); for (size_t z = 0; z < node->intersecting_edge_size(); ++z) { auto intersecting_edge = node->GetIntersectingEdge(z); auto xturn_degree = GetTurnDegree(prev_edge->end_heading(), intersecting_edge->begin_heading()); @@ -278,7 +278,34 @@ std::list ManeuversBuilder::Produce() { std::string(" | left_similar_traversable_outbound =") + std::to_string(xedge_counts.left_similar_traversable_outbound)); #endif - + if (trip_path_->GetCurrEdge(i)->pedestrian_type() == PedestrianType::kBlind) { + switch (node->type()) { + case TripLeg_Node_Type_kStreetIntersection: { + std::vector> name_list; + for (size_t z = 0; z < node->intersecting_edge_size(); ++z) { + auto intersecting_edge = node->GetIntersectingEdge(z); + for (const auto& name : intersecting_edge->name()) { + std::pair cur_street = {name.value(), name.is_route_number()}; + if (std::find(name_list.begin(), name_list.end(), cur_street) == name_list.end()) + name_list.push_back(cur_street); + } + } + if (!name_list.empty()) { + maneuvers.front().set_cross_street_names(name_list); + maneuvers.front().set_node_type(node->type()); + if (node->traffic_signal()) + maneuvers.front().set_traffic_signal(true); + } + break; + } + case TripLeg_Node_Type_kGate: + case TripLeg_Node_Type_kBollard: + maneuvers.front().set_node_type(node->type()); + break; + default: + break; + } + } if (CanManeuverIncludePrevEdge(maneuvers.front(), i)) { UpdateManeuver(maneuvers.front(), i); } else { @@ -513,6 +540,19 @@ void ManeuversBuilder::Combine(std::list& maneuvers) { ++next_man; } // Do not combine + // if has node_type + else if ((curr_man->pedestrian_type() == PedestrianType::kBlind && + next_man->pedestrian_type() == PedestrianType::kBlind) && + (curr_man->has_node_type() || next_man->has_node_type() || curr_man->is_steps() || + next_man->is_steps() || curr_man->is_bridge() || next_man->is_bridge() || + curr_man->is_tunnel() || next_man->is_tunnel())) { + LOG_TRACE("+++ Do Not Combine: if has node type+++"); + // Update with no combine + prev_man = curr_man; + curr_man = next_man; + ++next_man; + } + // Do not combine // if trail type is different (unnamed/named pedestrian/bike/mtb) else if (curr_man->trail_type() != next_man->trail_type()) { LOG_TRACE("+++ Do Not Combine: if trail type is different +++"); @@ -1251,6 +1291,14 @@ void ManeuversBuilder::InitializeManeuver(Maneuver& maneuver, int node_index) { } } + if (maneuver.pedestrian_type() == PedestrianType::kBlind) { + if (prev_edge->use() == TripLeg_Use_kStepsUse) + maneuver.set_steps(true); + if (prev_edge->bridge()) + maneuver.set_bridge(true); + if (prev_edge->tunnel()) + maneuver.set_tunnel(true); + } // TODO - what about street names; maybe check name flag UpdateManeuver(maneuver, node_index); } @@ -1381,7 +1429,6 @@ void ManeuversBuilder::UpdateManeuver(Maneuver& maneuver, int node_index) { } void ManeuversBuilder::FinalizeManeuver(Maneuver& maneuver, int node_index) { - auto prev_edge = trip_path_->GetPrevEdge(node_index); auto curr_edge = trip_path_->GetCurrEdge(node_index); auto node = trip_path_->GetEnhancedNode(node_index); @@ -1482,7 +1529,6 @@ void ManeuversBuilder::FinalizeManeuver(Maneuver& maneuver, int node_index) { maneuver.set_begin_street_names(std::move(curr_edge_names)); } } - if (node->type() == TripLeg_Node_Type::TripLeg_Node_Type_kBikeShare && prev_edge && (prev_edge->travel_mode() == TravelMode::kBicycle) && maneuver.travel_mode() == TravelMode::kPedestrian) { @@ -2063,6 +2109,9 @@ bool ManeuversBuilder::CanManeuverIncludePrevEdge(Maneuver& maneuver, int node_i auto node = trip_path_->GetEnhancedNode(node_index); auto turn_degree = GetTurnDegree(prev_edge->end_heading(), curr_edge->begin_heading()); + if (curr_edge->pedestrian_type() == PedestrianType::kBlind && maneuver.has_node_type()) { + return false; + } if (node->type() == TripLeg_Node_Type::TripLeg_Node_Type_kBikeShare) { return false; } diff --git a/src/odin/narrative_dictionary.cc b/src/odin/narrative_dictionary.cc index a71117a82e..7920017f65 100644 --- a/src/odin/narrative_dictionary.cc +++ b/src/odin/narrative_dictionary.cc @@ -315,6 +315,10 @@ void NarrativeDictionary::Load(const boost::property_tree::ptree& narrative_pt) // Populate approach_verbal_alert_subset Load(approach_verbal_alert_subset, narrative_pt.get_child(kApproachVerbalAlertKey)); + ///////////////////////////////////////////////////////////////////////////// + LOG_TRACE("Populate pass_subset..."); + // Populate pass_subset + Load(pass_subset, narrative_pt.get_child(kPassKey)); /////////////////////////////////////////////////////////////////////////// LOG_TRACE("Populate elevator_subset"); // Populate elevator_subset @@ -569,6 +573,16 @@ void NarrativeDictionary::Load(ApproachVerbalAlertSubset& approach_verbal_alert_ as_vector(approach_verbal_alert_subset_pt, kUsCustomaryLengthsKey); } +void NarrativeDictionary::Load(PassSubset& pass_handle, + const boost::property_tree::ptree& pass_subset_pt) { + + // Populate phrases + Load(static_cast(pass_handle), pass_subset_pt); + + // Populate object_labels + pass_handle.object_labels = as_vector(pass_subset_pt, kObjectLabelsKey); +} + void NarrativeDictionary::Load(EnterBuildingSubset& enter_building_handle, const boost::property_tree::ptree& enter_building_subset_pt) { diff --git a/src/odin/narrativebuilder.cc b/src/odin/narrativebuilder.cc index a35cabb0b8..1d200fd547 100644 --- a/src/odin/narrativebuilder.cc +++ b/src/odin/narrativebuilder.cc @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -491,19 +492,27 @@ void NarrativeBuilder::Build(std::list& maneuvers) { } case DirectionsLeg_Maneuver_Type_kContinue: default: { - // Set instruction - maneuver.set_instruction(FormContinueInstruction(maneuver)); + if (maneuver.has_node_type()) { + std::string instr = FormPassInstruction(maneuver); + // Set instruction + maneuver.set_instruction(instr); + // Set verbal pre transition instruction + maneuver.set_verbal_pre_transition_instruction(instr); + } else { + // Set instruction + maneuver.set_instruction(FormContinueInstruction(maneuver)); - // Set verbal transition alert instruction - maneuver.set_verbal_transition_alert_instruction( - FormVerbalAlertContinueInstruction(maneuver)); + // Set verbal transition alert instruction + maneuver.set_verbal_transition_alert_instruction( + FormVerbalAlertContinueInstruction(maneuver)); - // Set verbal pre transition instruction - maneuver.set_verbal_pre_transition_instruction(FormVerbalContinueInstruction(maneuver)); + // Set verbal pre transition instruction + maneuver.set_verbal_pre_transition_instruction(FormVerbalContinueInstruction(maneuver)); - // Set verbal post transition instruction - maneuver.set_verbal_post_transition_instruction( - FormVerbalPostTransitionInstruction(maneuver)); + // Set verbal post transition instruction + maneuver.set_verbal_post_transition_instruction( + FormVerbalPostTransitionInstruction(maneuver)); + } break; } } @@ -4313,6 +4322,52 @@ std::string NarrativeBuilder::FormTransitPlatformCountLabel( return transit_stop_count_labels.at(kPluralCategoryOtherKey); } +std::string NarrativeBuilder::FormPassInstruction(Maneuver& maneuver) { + // "0": "Pass .", + // "1": "Pass traffic lights on .", + std::string instruction; + instruction.reserve(kInstructionInitialCapacity); + + // Determine which phrase to use + uint8_t phrase_id = 0; + std::string object_label; + auto dictionary_object_index = kStreetIntersectionIndex; // kGateIndex; + if (maneuver.has_node_type()) { + switch (maneuver.node_type()) { + case TripLeg_Node_Type_kGate: + dictionary_object_index = kGateIndex; + break; + case TripLeg_Node_Type_kBollard: + dictionary_object_index = kBollardIndex; + break; + case TripLeg_Node_Type_kStreetIntersection: + dictionary_object_index = kStreetIntersectionIndex; + if (maneuver.traffic_signal()) + phrase_id = 1; + if (maneuver.HasCrossStreetNames()) + object_label = FormStreetNames(maneuver, maneuver.cross_street_names()); + break; + default: + break; + } + if (object_label.empty()) + object_label = dictionary_.pass_subset.object_labels.at(dictionary_object_index); + } + + // Set instruction to the determined tagged phrase + instruction = dictionary_.pass_subset.phrases.at(std::to_string(phrase_id)); + + // Replace phrase tags with values + boost::replace_all(instruction, kObjectLabelTag, object_label); + + // If enabled, form articulated prepositions + if (articulated_preposition_enabled_) { + FormArticulatedPrepositions(instruction); + } + + return instruction; +} + std::string NarrativeBuilder::GetPluralCategory(size_t count) { if (count == 1) { return kPluralCategoryOneKey; @@ -4567,11 +4622,19 @@ NarrativeBuilder::FormStreetNames(const Maneuver& maneuver, // If empty street names string // then determine if walkway or bike path if (enhance_empty_street_names && street_names_string.empty() && empty_street_name_labels) { - + // Set names in blind user mode: + if (maneuver.pedestrian_type() == PedestrianType::kBlind) { + if (maneuver.is_steps()) + street_names_string = empty_street_name_labels->at(kStepsIndex); + else if (maneuver.is_bridge()) + street_names_string = empty_street_name_labels->at(kBridgeIndex); + else if (maneuver.is_tunnel()) + street_names_string = empty_street_name_labels->at(kTunnelIndex); + } // If pedestrian travel mode on unnamed footway // then set street names string to walkway. Additionally, if the path // is a pedestrian crossing, use appropriate phrasing. - if ((maneuver.travel_mode() == TravelMode::kPedestrian) && maneuver.unnamed_walkway()) { + else if ((maneuver.travel_mode() == TravelMode::kPedestrian) && maneuver.unnamed_walkway()) { auto dictionary_index = maneuver.pedestrian_crossing() ? kPedestrianCrossingIndex : kWalkwayIndex; street_names_string = empty_street_name_labels->at(dictionary_index); @@ -4579,13 +4642,14 @@ NarrativeBuilder::FormStreetNames(const Maneuver& maneuver, // If bicycle travel mode on unnamed cycleway // then set street names string to cycleway - if ((maneuver.travel_mode() == TravelMode::kBicycle) && maneuver.unnamed_cycleway()) { + else if ((maneuver.travel_mode() == TravelMode::kBicycle) && maneuver.unnamed_cycleway()) { street_names_string = empty_street_name_labels->at(kCyclewayIndex); } // If bicycle travel mode on unnamed mountain bike trail // then set street names string to mountain bike trail - if ((maneuver.travel_mode() == TravelMode::kBicycle) && maneuver.unnamed_mountain_bike_trail()) { + else if ((maneuver.travel_mode() == TravelMode::kBicycle) && + maneuver.unnamed_mountain_bike_trail()) { street_names_string = empty_street_name_labels->at(kMountainBikeTrailIndex); } } @@ -4623,6 +4687,8 @@ std::string NarrativeBuilder::FormStreetNames(const StreetNames& street_names, void NarrativeBuilder::FormVerbalMultiCue(std::list& maneuvers) { Maneuver* prev_maneuver = nullptr; for (auto& maneuver : maneuvers) { + if (maneuver.pedestrian_type() == PedestrianType::kBlind) + continue; if (prev_maneuver && IsVerbalMultiCuePossible(*prev_maneuver, maneuver)) { // Determine if imminent or distant verbal multi-cue // if previous maneuver has an intersecting traversable outbound edge diff --git a/src/sif/pedestriancost.cc b/src/sif/pedestriancost.cc index 9ff6d35c5e..a082764b98 100644 --- a/src/sif/pedestriancost.cc +++ b/src/sif/pedestriancost.cc @@ -594,7 +594,7 @@ PedestrianCost::PedestrianCost(const Costing& costing) access_mask_ = kWheelchairAccess; minimal_allowed_surface_ = Surface::kCompacted; } else { - type_ = PedestrianType::kFoot; + type_ = type == "blind" ? PedestrianType::kBlind : PedestrianType::kFoot; access_mask_ = kPedestrianAccess; minimal_allowed_surface_ = Surface::kPath; } diff --git a/src/thor/triplegbuilder.cc b/src/thor/triplegbuilder.cc index 60d74956e5..69916c33de 100644 --- a/src/thor/triplegbuilder.cc +++ b/src/thor/triplegbuilder.cc @@ -654,6 +654,21 @@ void AddSignInfo(const AttributesController& controller, } } +void FilterUnneededStreetNumbers( + std::vector>& names_and_types) { + if (names_and_types.size() < 2) { + return; + } + auto it = names_and_types.begin(); + while (it != names_and_types.end()) { + if (std::get<1>(*it) == true && names_and_types.size() > 1) { + it = names_and_types.erase(it); + } else { + it++; + } + } +} + /** * Set elevation along the edge if requested. * @param trip_edge Trip path edge to add elevation. @@ -750,7 +765,8 @@ void AddTripIntersectingEdge(const AttributesController& controller, uint32_t local_edge_index, const NodeInfo* nodeinfo, TripLeg_Node* trip_node, - const DirectedEdge* intersecting_de) { + const DirectedEdge* intersecting_de, + bool blind_instructions) { TripLeg_IntersectingEdge* intersecting_edge = trip_node->add_intersecting_edge(); // Set the heading for the intersecting edge if requested @@ -806,6 +822,21 @@ void AddTripIntersectingEdge(const AttributesController& controller, intersecting_edge->set_curr_name_consistency(directededge->name_consistency(local_edge_index)); } + // Add names to edge if requested + if (controller.attributes.at(kEdgeNames)) { + // Get the edgeinfo + auto edgeinfo = graphtile->edgeinfo(intersecting_de); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + if (blind_instructions) + FilterUnneededStreetNumbers(names_and_types); + intersecting_edge->mutable_name()->Reserve(names_and_types.size()); + for (const auto& name_and_type : names_and_types) { + auto* trip_edge_name = intersecting_edge->mutable_name()->Add(); + trip_edge_name->set_value(std::get<0>(name_and_type)); + trip_edge_name->set_is_route_number(std::get<1>(name_and_type)); + } + } + // Set the use for the intersecting edge if requested if (controller(kNodeIntersectingEdgeUse)) { intersecting_edge->set_use(GetTripLegUse(intersecting_de->use())); @@ -846,6 +877,7 @@ void AddTripIntersectingEdge(const AttributesController& controller, * @param prior_opp_local_index opposing edge local index of previous edge in the path * @param graphreader graph reader for graph access * @param trip_node pbf node in the pbf structure we are building + * @param blind_instructions whether instructions for blind users are requested */ void AddIntersectingEdges(const AttributesController& controller, const graph_tile_ptr& start_tile, @@ -854,7 +886,8 @@ void AddIntersectingEdges(const AttributesController& controller, const DirectedEdge* prev_de, uint32_t prior_opp_local_index, GraphReader& graphreader, - valhalla::TripLeg::Node* trip_node) { + valhalla::TripLeg::Node* trip_node, + const bool blind_instructions) { /* Add connected edges from the start node. Do this after the first trip edge is added @@ -897,7 +930,8 @@ void AddIntersectingEdges(const AttributesController& controller, // Add intersecting edges on the same hierarchy level and not on the path AddTripIntersectingEdge(controller, start_tile, directededge, prev_de, - intersecting_edge->localedgeidx(), node, trip_node, intersecting_edge); + intersecting_edge->localedgeidx(), node, trip_node, intersecting_edge, + blind_instructions); } // Add intersecting edges on different levels (follow NodeTransitions) @@ -923,7 +957,7 @@ void AddIntersectingEdges(const AttributesController& controller, AddTripIntersectingEdge(controller, endtile, directededge, prev_de, intersecting_edge2->localedgeidx(), nodeinfo2, trip_node, - intersecting_edge2); + intersecting_edge2, blind_instructions); } } } @@ -1006,6 +1040,7 @@ void ProcessTunnelBridgeTaggedValue(valhalla::StreetName* trip_edge_name, * @param start_node_idx The start node index * @param has_junction_name True if named junction exists, false otherwise * @param start_tile The start tile of the start node + * @param blind_instructions Whether instructions should be generated for blind users * */ TripLeg_Edge* AddTripEdge(const AttributesController& controller, @@ -1024,7 +1059,8 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, const bool has_junction_name, const graph_tile_ptr& start_tile, const uint8_t restrictions_idx, - float elapsed_secs) { + float elapsed_secs, + bool blind_instructions) { // Index of the directed edge within the tile uint32_t idx = edge.id(); @@ -1035,7 +1071,9 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, // Add names to edge if requested if (controller(kEdgeNames)) { - const auto names_and_types = edgeinfo.GetNamesAndTypes(true); + auto names_and_types = edgeinfo.GetNamesAndTypes(true); + if (blind_instructions) + FilterUnneededStreetNumbers(names_and_types); trip_edge->mutable_name()->Reserve(names_and_types.size()); const auto linguistics = edgeinfo.GetLinguisticMap(); @@ -1741,6 +1779,8 @@ void TripLegBuilder::Build( if (controller(kNodeType)) { trip_node->set_type(GetTripLegNodeType(node->type())); + if (node->traffic_signal()) + trip_node->set_traffic_signal(true); } if (node->intersection() == IntersectionType::kFork) { @@ -1790,7 +1830,8 @@ void TripLegBuilder::Build( AddTripEdge(controller, edge, edge_itr->trip_id, multimodal_builder.block_id, mode, travel_type, costing, directededge, node->drive_on_right(), trip_node, graphtile, time_info, startnode.id(), node->named_intersection(), start_tile, - edge_itr->restriction_index, edge_itr->elapsed_cost.secs); + edge_itr->restriction_index, edge_itr->elapsed_cost.secs, + travel_type == PedestrianType::kBlind && mode == sif::TravelMode::kPedestrian); // some information regarding shape/length trimming float trim_start_pct = is_first_edge ? start_pct : 0; @@ -1952,7 +1993,9 @@ void TripLegBuilder::Build( // node and end node) of a shortcut that was recovered. if (startnode.Is_Valid() && !edge_itr->start_node_is_recovered) { AddIntersectingEdges(controller, start_tile, node, directededge, prev_de, prior_opp_local_index, - graphreader, trip_node); + graphreader, trip_node, + travel_type == PedestrianType::kBlind && + mode == sif::TravelMode::kPedestrian); } ////////////// Prepare for the next iteration diff --git a/taginfo.json b/taginfo.json index c1dfcd5a90..a5fd649579 100644 --- a/taginfo.json +++ b/taginfo.json @@ -1128,6 +1128,13 @@ ], "description": "Allows access for all modes unless the mode override is set." }, + { + "key": "crossing", + "value": "traffic_signals", + "object_types": [ + "node" + ] + }, { "key": "cyclestreet", "value": "yes", diff --git a/test/gurka/test_costing_type_blind.cc b/test/gurka/test_costing_type_blind.cc new file mode 100644 index 0000000000..c9ecb0d77d --- /dev/null +++ b/test/gurka/test_costing_type_blind.cc @@ -0,0 +1,146 @@ +#include "gurka.h" +#include + +using namespace valhalla; + +// Make sure we get instructions for passing a bridge +TEST(CostingTypeBlind, Bridge) { + + const std::string& ascii_map = R"( + A---B-------C---D + )"; + const gurka::ways ways = { + {"AB", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + {"BC", {{"name", ""}, {"highway", "footway"}, {"bridge", "yes"}}}, + {"CD", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/costing_type_blind_bridge"); + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "pedestrian", + {{"/costing_options/pedestrian/type", "blind"}}); + + // Verify instructions for start and end maneuvers + gurka::assert::raw::expect_instructions_at_maneuver_index(result, 1, "Continue on the bridge.", "", + "Continue on the bridge.", + "Continue on the bridge.", + "Continue for 400 meters."); +} + +// Make sure we get instructions for passing a tunnel +TEST(CostingTypeBlind, Tunnel) { + + const std::string& ascii_map = R"( + A---B-------C---D + )"; + const gurka::ways ways = { + {"AB", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + {"BC", {{"name", ""}, {"highway", "footway"}, {"tunnel", "yes"}}}, + {"CD", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/costing_type_blind_tunnel"); + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "pedestrian", + {{"/costing_options/pedestrian/type", "blind"}}); + + // Verify instructions for start and end maneuvers + gurka::assert::raw::expect_instructions_at_maneuver_index(result, 1, "Continue on the tunnel.", "", + "Continue on the tunnel.", + "Continue on the tunnel.", + "Continue for 400 meters."); +} + +// Make sure we get instructions for passing a traffic sign +TEST(CostingTypeBlind, TrafficSignal) { + + const std::string& ascii_map = R"( + E + | + A---B-------C---D + | + F + )"; + const gurka::ways ways = { + {"AB", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + {"BC", {{"name", ""}, {"highway", "footway"}}}, + {"CD", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + {"BE", + { + {"name", "crossing street"}, + {"highway", "secondary"}, + }}, + {"BF", + { + {"name", "crossing street"}, + {"highway", "secondary"}, + }}, + }; + + const gurka::nodes nodes = {{"B", {{"highway", "traffic_signals"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50); + auto map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/costing_type_blind_traffic_sign"); + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "pedestrian", + {{"/costing_options/pedestrian/type", "blind"}}); + + // Verify instructions for start and end maneuvers + gurka::assert::raw:: + expect_instructions_at_maneuver_index(result, 1, "Pass traffic signals on crossing street.", "", + "", "Pass traffic signals on crossing street.", ""); +} + +TEST(CostingTypeBlind, Bollard) { + + const std::string& ascii_map = R"( + A---B-------C---D + )"; + const gurka::ways ways = { + {"AB", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + {"BC", {{"name", "normal street"}, {"highway", "footway"}}}, + {"CD", + { + {"name", "normal street"}, + {"highway", "footway"}, + }}, + }; + + const gurka::nodes nodes = {{"B", {{"barrier", "bollard"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 50); + auto map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/costing_type_blind_bollard"); + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "pedestrian", + {{"/costing_options/pedestrian/type", "blind"}}); + + // Verify instructions for start and end maneuvers + gurka::assert::raw::expect_instructions_at_maneuver_index(result, 1, "Pass the bollards.", "", "", + "Pass the bollards.", ""); +} \ No newline at end of file diff --git a/test/gurka/test_route_with_narrative_languages.cc b/test/gurka/test_route_with_narrative_languages.cc index bfcc3aee91..eeef393f9b 100644 --- a/test/gurka/test_route_with_narrative_languages.cc +++ b/test/gurka/test_route_with_narrative_languages.cc @@ -86,8 +86,8 @@ const std::vector> RouteWithNarrativeLanguag {"nb-NO", "Ta til høyre inn på Main Street."}, {"nl", "Sla rechtsaf naar Main Street."}, {"nl-NL", "Sla rechtsaf naar Main Street."}, - {"pl", "Skręć w prawo w stronę: Main Street."}, - {"pl-PL", "Skręć w prawo w stronę: Main Street."}, + {"pl", "Skręć w prawo w Main Street."}, + {"pl-PL", "Skręć w prawo w Main Street."}, {"pt-BR", "Vire à direita para Main Street."}, {"pt", "Vire à direita em direção à Main Street."}, {"pt-PT", "Vire à direita em direção à Main Street."}, diff --git a/test/narrative_dictionary.cc b/test/narrative_dictionary.cc index fcfca5d6ae..1f6c716f26 100644 --- a/test/narrative_dictionary.cc +++ b/test/narrative_dictionary.cc @@ -13,9 +13,9 @@ using namespace valhalla::odin; namespace { // Expected strings -const std::vector kExpectedEmptyStreetNameLabels = {"the walkway", "the cycleway", - "the mountain bike trail", - "the crosswalk"}; +const std::vector kExpectedEmptyStreetNameLabels = + {"the walkway", "the cycleway", "the mountain bike trail", "the crosswalk", "the stairs", + "the bridge", "the tunnel"}; const std::vector kExpectedCardinalDirections = {"north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest"}; diff --git a/valhalla/odin/enhancedtrippath.h b/valhalla/odin/enhancedtrippath.h index 108a1abcc1..b28ecee143 100644 --- a/valhalla/odin/enhancedtrippath.h +++ b/valhalla/odin/enhancedtrippath.h @@ -467,6 +467,18 @@ class EnhancedTripLeg_IntersectingEdge { public: EnhancedTripLeg_IntersectingEdge(TripLeg_IntersectingEdge* mutable_intersecting_edge); + int name_size() const { + return mutable_intersecting_edge_->name_size(); + } + + const ::valhalla::StreetName& name(int index) const { + return mutable_intersecting_edge_->name(index); + } + + const ::google::protobuf::RepeatedPtrField<::valhalla::StreetName>& name() const { + return mutable_intersecting_edge_->name(); + } + uint32_t begin_heading() const { return mutable_intersecting_edge_->begin_heading(); } @@ -602,6 +614,9 @@ class EnhancedTripLeg_Node { return mutable_node_->type(); } + bool traffic_signal() const { + return mutable_node_->traffic_signal(); + } double elapsed_time() const { return mutable_node_->cost().elapsed_cost().seconds(); } diff --git a/valhalla/odin/maneuver.h b/valhalla/odin/maneuver.h index a21a9e4212..095126a695 100644 --- a/valhalla/odin/maneuver.h +++ b/valhalla/odin/maneuver.h @@ -59,6 +59,18 @@ class Maneuver { bool IsRightType() const; bool IsLeftType() const; + void set_node_type(TripLeg_Node_Type type); + TripLeg_Node_Type node_type() const; + bool has_node_type() const; + bool traffic_signal() const; + void set_traffic_signal(bool traffic_signal); + bool is_steps() const; + void set_steps(bool steps); + bool is_bridge() const; + void set_bridge(bool bridge); + bool is_tunnel() const; + void set_tunnel(bool tunnel); + const StreetNames& street_names() const; void set_street_names(const std::vector>& names); void set_street_names(std::unique_ptr&& street_names); @@ -407,6 +419,12 @@ class Maneuver { protected: DirectionsLeg_Maneuver_Type type_; + TripLeg_Node_Type node_type_; + bool has_node_type_; + bool traffic_signal_; + bool is_steps_; + bool is_bridge_; + bool is_tunnel_; std::unique_ptr street_names_; std::unique_ptr begin_street_names_; std::unique_ptr cross_street_names_; diff --git a/valhalla/odin/narrative_dictionary.h b/valhalla/odin/narrative_dictionary.h index ac7b58e538..e4d8b705ef 100644 --- a/valhalla/odin/narrative_dictionary.h +++ b/valhalla/odin/narrative_dictionary.h @@ -76,6 +76,7 @@ constexpr auto kPostTransitionVerbalKey = "instructions.post_transition_verbal"; constexpr auto kPostTransitTransitionVerbalKey = "instructions.post_transition_transit_verbal"; constexpr auto kVerbalMultiCueKey = "instructions.verbal_multi_cue"; constexpr auto kApproachVerbalAlertKey = "instructions.approach_verbal_alert"; +constexpr auto kPassKey = "instructions.pass"; constexpr auto kElevatorKey = "instructions.elevator"; constexpr auto kStepsKey = "instructions.steps"; constexpr auto kEscalatorKey = "instructions.escalator"; @@ -95,6 +96,7 @@ constexpr auto kFerryLabelKey = "ferry_label"; constexpr auto kStationLabelKey = "station_label"; constexpr auto kEmptyTransitNameLabelsKey = "empty_transit_name_labels"; constexpr auto kTransitStopCountLabelsKey = "transit_stop_count_labels"; +constexpr auto kObjectLabelsKey = "object_labels"; constexpr auto kPluralCategoryZeroKey = "zero"; constexpr auto kPluralCategoryOneKey = "one"; @@ -108,6 +110,14 @@ constexpr auto kWalkwayIndex = 0; constexpr auto kCyclewayIndex = 1; constexpr auto kMountainBikeTrailIndex = 2; constexpr auto kPedestrianCrossingIndex = 3; +constexpr auto kStepsIndex = 4; +constexpr auto kBridgeIndex = 5; +constexpr auto kTunnelIndex = 6; + +// object label indexes +constexpr auto kGateIndex = 0; +constexpr auto kBollardIndex = 1; +constexpr auto kStreetIntersectionIndex = 2; // Metric length indexes constexpr auto kKilometersIndex = 0; @@ -134,6 +144,7 @@ constexpr auto kCrossStreetNamesTag = ""; constexpr auto kRoundaboutExitStreetNamesTag = ""; constexpr auto kRoundaboutExitBeginStreetNamesTag = ""; constexpr auto kRampExitNumbersVisualTag = ""; +constexpr auto kObjectLabelTag = ""; constexpr auto kLengthTag = ""; constexpr auto kDestinationTag = ""; constexpr auto kCurrentVerbalCueTag = ""; @@ -245,6 +256,10 @@ struct ApproachVerbalAlertSubset : PhraseSet { std::vector us_customary_lengths; }; +struct PassSubset : PhraseSet { + std::vector object_labels; +}; + struct EnterBuildingSubset : PhraseSet { std::vector empty_street_name_labels; }; @@ -376,6 +391,8 @@ class NarrativeDictionary { // Approach verbal alert ApproachVerbalAlertSubset approach_verbal_alert_subset; + // Pass + PassSubset pass_subset; // Elevator PhraseSet elevator_subset; @@ -594,6 +611,15 @@ class NarrativeDictionary { void Load(ApproachVerbalAlertSubset& approach_verbal_alert_handle, const boost::property_tree::ptree& approach_verbal_alert_subset_pt); + /** + * Loads the specified 'pass' instruction subset with the localized + * narrative instructions contained in the specified property tree. + * + * @param pass_handle The 'pass' structure to populate. + * @param pass_subset_pt The 'pass' property tree. + */ + void Load(PassSubset& pass_handle, const boost::property_tree::ptree& pass_subset_pt); + /** * Loads the specified 'enter_building' instruction subset with the localized * narrative instructions contained in the specified property tree. diff --git a/valhalla/odin/narrativebuilder.h b/valhalla/odin/narrativebuilder.h index ce7b8810ad..9ed444e3dd 100644 --- a/valhalla/odin/narrativebuilder.h +++ b/valhalla/odin/narrativebuilder.h @@ -436,6 +436,8 @@ class NarrativeBuilder { size_t stop_count, const std::unordered_map& transit_platform_count_labels); + std::string FormPassInstruction(Maneuver& maneuver); + /** * Returns the plural category based on the value of the specified * count and the language rules. diff --git a/valhalla/proto_conversions.h b/valhalla/proto_conversions.h index ab68e0b523..7dda741eb9 100644 --- a/valhalla/proto_conversions.h +++ b/valhalla/proto_conversions.h @@ -41,14 +41,12 @@ inline VehicleType GetTripLegVehicleType(const uint8_t type) { } // Associate pedestrian types to TripLeg proto -constexpr PedestrianType kTripLegPedestrianType[] = { - PedestrianType::kFoot, - PedestrianType::kWheelchair, -}; +constexpr PedestrianType kTripLegPedestrianType[] = {PedestrianType::kFoot, + PedestrianType::kWheelchair, + PedestrianType::kBlind}; inline PedestrianType GetTripLegPedestrianType(const uint8_t type) { - return (type <= static_cast(sif::PedestrianType::kWheelchair)) - ? kTripLegPedestrianType[type] - : kTripLegPedestrianType[0]; + return (type <= static_cast(sif::PedestrianType::kBlind)) ? kTripLegPedestrianType[type] + : kTripLegPedestrianType[0]; } // Associate bicycle types to TripLeg proto diff --git a/valhalla/sif/costconstants.h b/valhalla/sif/costconstants.h index 7792ceb99f..0251d8e941 100644 --- a/valhalla/sif/costconstants.h +++ b/valhalla/sif/costconstants.h @@ -34,7 +34,7 @@ enum class VehicleType : uint8_t { }; // Pedestrian travel type -enum class PedestrianType : uint8_t { kFoot = 0, kWheelchair = 1 }; +enum class PedestrianType : uint8_t { kFoot = 0, kWheelchair = 1, kBlind = 2 }; // Bicycle travel type enum class BicycleType : uint8_t { From efff65d35c68c9198d504e07087d3ae2b71e5e1f Mon Sep 17 00:00:00 2001 From: David Nesbitt Date: Thu, 15 Feb 2024 07:00:11 -0500 Subject: [PATCH 032/198] Fix roundoff issue in Tiles (#4585) --- CHANGELOG.md | 1 + test/tiles.cc | 12 ++++++++++++ valhalla/midgard/tiles.h | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0db52f603f..b31aa23c88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,7 @@ * UPDATED: cxxopts to 3.1.1 [#4541](https://github.com/valhalla/valhalla/pull/4541) * CHANGED: make use of vendored libraries optional (other than libraries which are not commonly in package managers or only used for testing) [#4544](https://github.com/valhalla/valhalla/pull/4544) * ADDED: Improved instructions for blind users [#3694](https://github.com/valhalla/valhalla/pull/3694) + * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/test/tiles.cc b/test/tiles.cc index f8742c7c56..9c08975873 100644 --- a/test/tiles.cc +++ b/test/tiles.cc @@ -569,6 +569,18 @@ TEST(Tiles, test_intersect_bbox_rounding) { EXPECT_EQ(bin_id, 0); } +TEST(Tiles, float_roundoff_issue) { + AABB2 world_box{-180, -90, 180, 90}; + Tiles t(world_box, 0.25, 5); + + PointLL ll(179.999978, -16.805363); + auto tile_id = t.TileId(ll); + EXPECT_EQ(tile_id, 421919); + auto base_ll = t.Base(tile_id); + EXPECT_EQ(base_ll.lat(), -17.0); + EXPECT_EQ(base_ll.lng(), 179.75); +} + } // namespace int main(int argc, char* argv[]) { diff --git a/valhalla/midgard/tiles.h b/valhalla/midgard/tiles.h index f4b43641c4..afede60786 100644 --- a/valhalla/midgard/tiles.h +++ b/valhalla/midgard/tiles.h @@ -123,7 +123,7 @@ template class Tiles { * @param y y coordinate * @return Returns the tile row. Returns -1 if outside the tile system bounds. */ - int32_t Row(const float y) const { + int32_t Row(const typename coord_t::value_type y) const { // Return -1 if outside the tile system bounds if (y < tilebounds_.miny() || y > tilebounds_.maxy()) { return -1; @@ -139,7 +139,7 @@ template class Tiles { * @param x x coordinate * @return Returns the tile column. Returns -1 if outside the tile system bounds. */ - int32_t Col(const float x) const { + int32_t Col(const typename coord_t::value_type x) const { // Return -1 if outside the tile system bounds if (x < tilebounds_.minx() || x > tilebounds_.maxx()) { return -1; From 2a0ed14fd7feb95ea3fdb3cd9cba10902df78c13 Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 19 Feb 2024 11:31:52 +0000 Subject: [PATCH 033/198] use range macros in costings consistently (#4587) --- src/sif/dynamiccost.cc | 3 +-- src/sif/truckcost.cc | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/sif/dynamiccost.cc b/src/sif/dynamiccost.cc index cd9e93d094..b0f6c3ee0d 100644 --- a/src/sif/dynamiccost.cc +++ b/src/sif/dynamiccost.cc @@ -482,8 +482,7 @@ void ParseBaseCostOptions(const rapidjson::Value& json, JSON_PBF_DEFAULT(co, cfg.include_hov2_, json, "/include_hov2", include_hov2); JSON_PBF_DEFAULT(co, cfg.include_hov3_, json, "/include_hov3", include_hov3); - co->set_fixed_speed( - kFixedSpeedRange(rapidjson::get(json, "/fixed_speed", co->fixed_speed()))); + JSON_PBF_RANGED_DEFAULT_V2(co, kFixedSpeedRange, json, "/fixed_speed", fixed_speed); } void ParseCosting(const rapidjson::Document& doc, diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index 1fffd14172..860c475930 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -53,7 +53,7 @@ constexpr float kDefaultTruckAxleLoad = 9.07f; // Metric Tons (20,000 lbs) constexpr float kDefaultTruckHeight = 4.11f; // Meters (13 feet 6 inches) constexpr float kDefaultTruckWidth = 2.6f; // Meters (102.36 inches) constexpr float kDefaultTruckLength = 21.64f; // Meters (71 feet) -constexpr uint8_t kDefaultAxleCount = 5; // 5 axles for above truck config +constexpr uint32_t kDefaultAxleCount = 5; // 5 axles for above truck config // Turn costs based on side of street driving constexpr float kRightSideTurnCosts[] = {kTCStraight, kTCSlight, kTCFavorable, @@ -95,7 +95,7 @@ constexpr ranged_default_t kTruckHeightRange{0, kDefaultTruckHeight, 10.0 constexpr ranged_default_t kTruckWidthRange{0, kDefaultTruckWidth, 10.0f}; constexpr ranged_default_t kTruckLengthRange{0, kDefaultTruckLength, 50.0f}; constexpr ranged_default_t kUseTollsRange{0, kDefaultUseTolls, 1.0f}; -constexpr ranged_default_t kAxleCountRange{2, kDefaultAxleCount, 20}; +constexpr ranged_default_t kAxleCountRange{2, kDefaultAxleCount, 20}; constexpr ranged_default_t kUseHighwaysRange{0, kDefaultUseHighways, 1.0f}; constexpr ranged_default_t kTopSpeedRange{10, kMaxAssumedTruckSpeed, kMaxSpeedKph}; @@ -711,8 +711,7 @@ void ParseTruckCostOptions(const rapidjson::Document& doc, JSON_PBF_RANGED_DEFAULT(co, kTruckLengthRange, json, "/length", length); JSON_PBF_RANGED_DEFAULT(co, kUseTollsRange, json, "/use_tolls", use_tolls); JSON_PBF_RANGED_DEFAULT(co, kUseHighwaysRange, json, "/use_highways", use_highways); - co->set_axle_count( - kAxleCountRange(rapidjson::get(json, "/axle_count", co->axle_count()))); + JSON_PBF_RANGED_DEFAULT_V2(co, kAxleCountRange, json, "/axle_count", axle_count); JSON_PBF_RANGED_DEFAULT(co, kTopSpeedRange, json, "/top_speed", top_speed); } From 812386f06691c1f5da4cd8aa48a9a0626c3c7407 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Mon, 19 Feb 2024 14:38:45 +0100 Subject: [PATCH 034/198] Isochrone PBF output and multipolygon support (#4575) --- CHANGELOG.md | 1 + docs/docs/api/isochrone/api-reference.md | 8 +- proto/CMakeLists.txt | 3 +- proto/api.proto | 4 +- proto/isochrone.proto | 28 +++ proto/options.proto | 2 +- src/midgard/util.cc | 91 +++++++ src/thor/isochrone_action.cc | 14 +- src/tyr/isochrone_serializer.cc | 302 +++++++++++++++++------ src/tyr/serializers.cc | 5 + src/valhalla_run_isochrone.cc | 2 +- src/worker.cc | 3 +- test/gurka/test_pbf_api.cc | 19 +- test/isochrone.cc | 103 ++++++-- valhalla/midgard/util.h | 9 + valhalla/tyr/serializers.h | 2 +- 16 files changed, 466 insertions(+), 130 deletions(-) create mode 100644 proto/isochrone.proto diff --git a/CHANGELOG.md b/CHANGELOG.md index b31aa23c88..707e72e82a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -92,6 +92,7 @@ * CHANGED: make use of vendored libraries optional (other than libraries which are not commonly in package managers or only used for testing) [#4544](https://github.com/valhalla/valhalla/pull/4544) * ADDED: Improved instructions for blind users [#3694](https://github.com/valhalla/valhalla/pull/3694) * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) + * ADDED: isochrone proper polygon support & pbf output for isochrone [#4575](https://github.com/valhalla/valhalla/pull/4575) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/docs/docs/api/isochrone/api-reference.md b/docs/docs/api/isochrone/api-reference.md index 2f1a864149..9972a5ad87 100644 --- a/docs/docs/api/isochrone/api-reference.md +++ b/docs/docs/api/isochrone/api-reference.md @@ -44,7 +44,7 @@ The isochrone service uses the `auto`, `bicycle`, `pedestrian`, and `multimodal` | `date_time` | The local date and time at the location.
  • `type`
    • 0 - Current departure time.
    • 1 - Specified departure time.
    • 2 - Specified arrival time. Note: This is not yet implemented for `multimodal`.
  • `value` - the date and time specified in ISO 8601 format (YYYY-MM-DDThh:mm) in the local time zone of departure or arrival. For example, "2016-07-03T08:06"
| | `id` | Name of the isochrone request. If `id` is specified, the name is returned with the response. | | `contours` | A JSON array of contour objects with the time in minutes or distance in kilometers and color to use for each isochrone contour. You can specify up to four contours (by default).
  • `time` - A floating point value specifying the time in minutes for the contour.
  • `distance` - A floating point value specifying the distance in kilometers for the contour.
  • `color` - The color for the output of the contour. Specify it as a [Hex value](http://www.w3schools.com/colors/colors_hexadecimal.asp), but without the `#`, such as `"color":"ff0000"` for red. If no color is specified, the isochrone service will assign a default color to the output.
You can only specify **one metric per contour**, i.e. `time` or `distance`. | -| `polygons` | A Boolean value to determine whether to return geojson polygons or linestrings as the contours. The default is `false`, which returns lines; when `true`, polygons are returned. Note: When `polygons` is `true`, any contour that forms a ring is returned as a polygon. | +| `polygons` | A Boolean value to determine whether to return geojson polygons or linestrings as the contours. The default is `false`, which returns lines; when `true`, polygons are returned. Note: When `polygons` is `true`, a feature's geometry type can be either `Polygon` or `MultiPolygon`, depending on the number of exterior rings formed for a given interval. | | `denoise` | A floating point value from `0` to `1` (default of `1`) which can be used to remove smaller contours. A value of `1` will only return the largest contour for a given time value. A value of `0.5` drops any contours that are less than half the area of the largest contour in the set of contours for that same time value. | | `generalize` | A floating point value in meters used as the tolerance for [Douglas-Peucker](https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm) generalization. Note: Generalization of contours can lead to self-intersections, as well as intersections of adjacent contours. | | `show_locations` | A boolean indicating whether the input locations should be returned as MultiPoint features: one feature for the exact input coordinates and one feature for the coordinates of the network node it snapped to. Default false. @@ -63,8 +63,6 @@ Most JavaScript-based GeoJSON renderers, including [Leaflet](http://leafletjs.co When making a map, drawing the isochrone contours as lines is more straightforward than polygons, and, therefore, currently is the default and recommended method. When deciding between the output as lines and polygons, consider your use case and the additional styling considerations involved with polygons. For example, fills should be rendered as semi-transparent over the other map layers so they are visible, although you may have more flexibility when using a vector-based map. In addition, polygons from multiple contour levels do not have overlapping areas cut out or removed. In other words, the outer contours include the areas of any inner contours, causing the colors and transparencies to blend when multiple contour polygons are drawn at the same time. -The Valhalla team has plans to improving the polygon isochrone output and rendering capabilities, including by demoting some rings to be inners of other rings and removing potential self-intersections in polygon geometries. - ## Future work on the isochrone service The Isochrone service is in active development. To report software issues or suggest enhancements, open an issue in the [Valhalla GitHub repository](https://github.com/valhalla/valhalla/issues). @@ -72,9 +70,9 @@ The Isochrone service is in active development. To report software issues or sug Several other options are being considered as future service enhancements. These include: * ~~Using distance rather than time for each unit.~~ -* Generating outer contours or contours with interior holes for regions that cannot be accessed within the specified time. +* ~~Generating outer contours or contours with interior holes for regions that cannot be accessed within the specified time.~~ * ~~Options to control the minimum size of interior holes.~~ -* Removing self intersections from polygonal contours. +* ~~Removing self intersections from polygonal contours.~~ * ~~Allowing multiple locations to compute the region reachable from any of the locations within a specified time.~~ * ~~Generating contours with reverse access logic to see the region that can reach a specific location within the specified time.~~ * Returning raster data for potential animation using OpenGL shaders. This also has analysis use for being able to query thousands of locations to determine the time to each location, including improvements with one-to-many requests to the Valhalla Time-Distance Matrix service. diff --git a/proto/CMakeLists.txt b/proto/CMakeLists.txt index b5e6d67bc4..f58a7c76a9 100644 --- a/proto/CMakeLists.txt +++ b/proto/CMakeLists.txt @@ -11,7 +11,8 @@ set(protobuf_descriptors transit_fetch.proto incidents.proto status.proto - matrix.proto) + matrix.proto + isochrone.proto) if(ENABLE_DATA_TOOLS) # Only mjolnir needs the OSM PBF descriptors diff --git a/proto/api.proto b/proto/api.proto index c0dead1a16..448713c309 100644 --- a/proto/api.proto +++ b/proto/api.proto @@ -8,6 +8,7 @@ import public "directions.proto"; // the directions, filled out by odin import public "info.proto"; // statistics about the request, filled out by loki/thor/odin import public "status.proto"; // info for status endpoint import public "matrix.proto"; // the matrix results +import public "isochrone.proto"; // the isochrone results message Api { // this is the request to the api @@ -17,7 +18,8 @@ message Api { Trip trip = 2; // trace_attributes Directions directions = 3; // route, optimized_route, trace_route, centroid Status status = 4; // status - Matrix matrix = 5; // sources_to_targets + Matrix matrix = 5; // sources_to_targets + Isochrone isochrone = 6; // isochrone //TODO: isochrone //TODO: matrix //TODO: locate diff --git a/proto/isochrone.proto b/proto/isochrone.proto new file mode 100644 index 0000000000..31a1ac27f8 --- /dev/null +++ b/proto/isochrone.proto @@ -0,0 +1,28 @@ +syntax = "proto3"; + +option optimize_for = LITE_RUNTIME; +package valhalla; + +message Isochrone { + + enum metric_type { + time = 0; + distance = 1; + } + + message Geometry { + repeated sint32 coords = 1 [packed=true]; + } + + message Contour { + repeated Geometry geometries = 2; // if polygon first one is outer rest are inners, though this is a problem when we allow multi location isochrones + } + + message Interval { + metric_type metric = 1; // time or distance enum + float metric_value = 2; // the target metric, eg 15min + repeated Contour contours = 3; + } + + repeated Interval intervals = 1; +} \ No newline at end of file diff --git a/proto/options.proto b/proto/options.proto index b69f654c32..f6c4ca3194 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -52,8 +52,8 @@ message PbfFieldSelector { bool directions = 3; // /route /trace_route /optimized_route /centroid bool status = 4; // /status bool matrix = 5; // sources_to_targets + bool isochrone = 6; // TODO: enable these once we have objects for them - // bool isochrone = 5; // bool locate = 7; // bool height = 8; // bool expansion = 9; diff --git a/src/midgard/util.cc b/src/midgard/util.cc index e728fc2858..f1fcdba1f1 100644 --- a/src/midgard/util.cc +++ b/src/midgard/util.cc @@ -47,6 +47,70 @@ resample_at_1hz(const std::vector& segments) { return resampled; } +/** + * determines the quadrant of pt1 relative to pt2 + * + * +-----+-----+ + * | | | + * | 1 | 0 | + * | | | + * +----pt2----+ + * | | | + * | 2 | 3 | + * | | | + * +-----+-----+ + * + */ +template int8_t quadrant_type(const coord_t& pt1, const coord_t& pt2) { + return (pt1.first > pt2.first) ? ((pt1.second > pt2.second) ? 0 : 3) + : ((pt1.second > pt2.second) ? 1 : 2); +} +template int8_t quadrant_type(const valhalla::midgard::PointLL&, + const valhalla::midgard::PointLL&); + +/** + * get the x intercept of an edge {pt1, pt2} with a horizontal line at a given y + */ +template +typename coord_t::first_type +x_intercept(const coord_t& pt1, const coord_t& pt2, const typename coord_t::first_type& y) { + return pt2.first - ((pt2.second - y) * ((pt1.first - pt2.first) / (pt1.second - pt2.second))); +} + +template valhalla::midgard::PointLL::first_type +x_intercept(const valhalla::midgard::PointLL&, + const valhalla::midgard::PointLL&, + const valhalla::midgard::PointLL::first_type&); + +template +void adjust_delta(int8_t& delta, + const coord_t& vertex, + const coord_t& next_vertex, + const coord_t& p) { + switch (delta) { + /* make quadrant deltas wrap around */ + case 3: + delta = -1; + break; + case -3: + delta = 1; + break; + /* when a quadrant was skipped, check if clockwise or counter-clockwise */ + case 2: + case -2: + if (x_intercept(vertex, next_vertex, p.second) > p.first) + delta = -(delta); + break; + } +} + +template void adjust_delta(int8_t&, + const valhalla::midgard::PointLL&, + const valhalla::midgard::PointLL&, + const valhalla::midgard::PointLL& + +); + } // namespace namespace valhalla { @@ -545,6 +609,33 @@ template bool intersect(const PointLL& u, template bool intersect(const Point2& u, const Point2& v, const Point2& a, const Point2& b, Point2& i); +template +bool point_in_poly(const coord_t& pt, const container_t& poly) { + int8_t quad, next_quad, delta, angle; + quad = quadrant_type(poly.front(), pt); + angle = 0; + + auto it = poly.begin(); + for (size_t i = 0; i < poly.size(); ++i) { + const coord_t vertex = *it; + it++; + if (it == poly.end()) { + it = poly.begin(); + } + const coord_t& next_vertex = *it; + next_quad = quadrant_type(next_vertex, pt); + delta = next_quad - quad; + adjust_delta(delta, vertex, next_vertex, pt); + angle = angle + delta; + quad = next_quad; + } + return (angle == 4) || (angle == -4); +}; + +template bool point_in_poly>( + const valhalla::midgard::PointLL&, + const std::list&); + template typename container_t::value_type::first_type polygon_area(const container_t& polygon) { // Shoelace formula diff --git a/src/thor/isochrone_action.cc b/src/thor/isochrone_action.cc index 4c031e071e..9bb3630d64 100644 --- a/src/thor/isochrone_action.cc +++ b/src/thor/isochrone_action.cc @@ -17,13 +17,13 @@ std::string thor_worker_t::isochrones(Api& request) { auto costing = parse_costing(request); // name of the metric (time/distance, value, color) - std::vector::contour_interval_t> contours; + std::vector::contour_interval_t> intervals; for (const auto& contour : options.contours()) { if (contour.has_time_case()) { - contours.emplace_back(0, contour.time(), "time", contour.color()); + intervals.emplace_back(0, contour.time(), "time", contour.color()); } if (contour.has_distance_case()) { - contours.emplace_back(1, contour.distance(), "distance", contour.color()); + intervals.emplace_back(1, contour.distance(), "distance", contour.color()); } } @@ -46,11 +46,11 @@ std::string thor_worker_t::isochrones(Api& request) { // we have parallel vectors of contour properties and the actual geojson features // this method sorts the contour specifications by metric (time or distance) and then by value // with the largest values coming first. eg (60min, 30min, 10min, 40km, 10km) - auto isolines = - grid->GenerateContours(contours, options.polygons(), options.denoise(), options.generalize()); + auto contours = + grid->GenerateContours(intervals, options.polygons(), options.denoise(), options.generalize()); - // make the final json - std::string ret = tyr::serializeIsochrones(request, contours, isolines, options.polygons(), + // make the final output (pbf or json) + std::string ret = tyr::serializeIsochrones(request, intervals, contours, options.polygons(), options.show_locations()); return ret; diff --git a/src/tyr/isochrone_serializer.cc b/src/tyr/isochrone_serializer.cc index 24c8768038..6f549e5e05 100644 --- a/src/tyr/isochrone_serializer.cc +++ b/src/tyr/isochrone_serializer.cc @@ -12,73 +12,182 @@ using namespace valhalla::baldr::json; namespace { using rgba_t = std::tuple; + +using namespace valhalla; +using namespace tyr; +using namespace midgard; +using contour_t = std::list; // single ring +using feature_t = std::list; // rings per interval +using contours_t = std::vector>; // all rings +using contour_group_t = std::vector; +using grouped_contours_t = std::vector; +using contour_interval_t = std::tuple; + +grouped_contours_t GroupContours(const bool polygons, const feature_t& contours) { + grouped_contours_t results; + + // if the user requested linestrings, we'll give them linestrings + if (!polygons || contours.size() < 2) { + std::for_each(contours.begin(), contours.end(), + [&results](const contour_t& c) { results.push_back({&c}); }); + return results; + } + + // inner rings have negative area + auto isExteriorRing = [](const contour_t& c) -> bool { return polygon_area(c) > 0; }; + + std::vector inner_ptrs; + std::for_each(contours.begin(), contours.end(), + [&results, &inner_ptrs, isExteriorRing](const contour_t& c) { + if (isExteriorRing(c)) { + results.push_back({&c}); + } else { + inner_ptrs.push_back(&c); + } + }); + + // exactly one exterior ring, so all inners go in one group + if (results.size() == 1) { + std::for_each(inner_ptrs.begin(), inner_ptrs.end(), + [&results](const contour_t* c) { results[0].push_back(c); }); + return results; + } + + // iterate over outer rings and for each inner ring check if the inner ring is within the exterior + // ring + for (const auto* inner : inner_ptrs) { + + // get the first point of the ring (could be any though) + const PointLL& inner_pt = inner->front(); + bool found_exterior = false; + // go over exterior rings from smallest to largest + for (size_t i = results.size(); i > 0; --i) { + const contour_t& ext = *results[i - 1][0]; + + // inner is within exterior ring if any of its points lies within the exterior ring + // if (inner_pt.WithinPolygon(ext)) { + if (point_in_poly(inner_pt, ext)) { + results[i - 1].push_back(inner); + found_exterior = true; + break; + } + } + + // TODO: reverse winding and make it an exterior ring + if (!found_exterior) { + LOG_WARN("No exterior ring contour found for inner contour."); + } + } + + return results; } -namespace valhalla { -namespace tyr { +std::string getIntervalColor(std::vector& intervals, size_t interval_idx) { + std::stringstream hex; + // color was supplied + if (!std::get<3>(intervals[interval_idx]).empty()) { + hex << "#" << std::get<3>(intervals[interval_idx]); + } // or we computed it.. + else { + auto h = interval_idx * (150.f / intervals.size()); + auto c = .5f; + auto x = c * (1 - std::abs(std::fmod(h / 60.f, 2.f) - 1)); + auto m = .25f; + rgba_t color = h < 60 ? rgba_t{m + c, m + x, m} + : (h < 120 ? rgba_t{m + x, m + c, m} : rgba_t{m, m + c, m + x}); + hex << "#" << std::hex << static_cast(std::get<0>(color) * 255 + .5f) << std::hex + << static_cast(std::get<1>(color) * 255 + .5f) << std::hex + << static_cast(std::get<2>(color) * 255 + .5f); + } + return hex.str(); +} -std::string serializeIsochrones(const Api& request, - std::vector::contour_interval_t>& intervals, - midgard::GriddedData<2>::contours_t& contours, - bool polygons, - bool show_locations) { +void addLocations(Api& request, valhalla::baldr::json::ArrayPtr& features) { + int idx = 0; + for (const auto& location : request.options().locations()) { + // first add all snapped points as MultiPoint feature per origin point + auto snapped_points_array = array({}); + std::unordered_set snapped_points; + for (const auto& path_edge : location.correlation().edges()) { + const PointLL& snapped_current = PointLL(path_edge.ll().lng(), path_edge.ll().lat()); + // remove duplicates of path_edges in case the snapped object is a node + if (snapped_points.insert(snapped_current).second) { + snapped_points_array->push_back( + array({fixed_t{snapped_current.lng(), 6}, fixed_t{snapped_current.lat(), 6}})); + } + }; + features->emplace_back(map({{"type", std::string("Feature")}, + {"properties", map({{"type", std::string("snapped")}, + {"location_index", static_cast(idx)}})}, + {"geometry", map({{"type", std::string("MultiPoint")}, + {"coordinates", snapped_points_array}})}})); + + // then each user input point as separate Point feature + const valhalla::LatLng& input_latlng = location.ll(); + const auto input_array = array({fixed_t{input_latlng.lng(), 6}, fixed_t{input_latlng.lat(), 6}}); + features->emplace_back( + map({{"type", std::string("Feature")}, + {"properties", + map({{"type", std::string("input")}, {"location_index", static_cast(idx)}})}, + {"geometry", map({{"type", std::string("Point")}, {"coordinates", input_array}})}})); + idx++; + } +} + +std::string serializeIsochroneJson(Api& request, + std::vector& intervals, + contours_t& contours, + bool show_locations, + bool polygons) { // for each contour interval int i = 0; auto features = array({}); assert(intervals.size() == contours.size()); for (size_t contour_index = 0; contour_index < intervals.size(); ++contour_index) { const auto& interval = intervals[contour_index]; - const auto& feature_collection = contours[contour_index]; - - // color was supplied - std::stringstream hex; - if (!std::get<3>(interval).empty()) { - hex << "#" << std::get<3>(interval); - } // or we computed it.. - else { - auto h = i * (150.f / intervals.size()); - auto c = .5f; - auto x = c * (1 - std::abs(std::fmod(h / 60.f, 2.f) - 1)); - auto m = .25f; - rgba_t color = h < 60 ? rgba_t{m + c, m + x, m} - : (h < 120 ? rgba_t{m + x, m + c, m} : rgba_t{m, m + c, m + x}); - hex << "#" << std::hex << static_cast(std::get<0>(color) * 255 + .5f) << std::hex - << static_cast(std::get<1>(color) * 255 + .5f) << std::hex - << static_cast(std::get<2>(color) * 255 + .5f); - } + const auto& interval_contours = contours[contour_index]; + + std::string hex = getIntervalColor(intervals, i); ++i; // for each feature on that interval - for (const auto& feature : feature_collection) { - // for each contour in that feature + for (const auto& feature : interval_contours) { + grouped_contours_t groups = GroupContours(polygons, feature); auto geom = array({}); - for (const auto& contour : feature) { - // make some geometry - auto coords = array({}); - for (const auto& coord : contour) { - coords->push_back(array({fixed_t{coord.first, 6}, fixed_t{coord.second, 6}})); - } - // its either a ring - if (polygons) { - geom->emplace_back(coords); - // or a single line, if someone has more than one contour per feature they messed up - } else { - geom = coords; + // each group is a polygon consisting of an exterior ring and possibly inner rings + for (const auto& group : groups) { + auto poly = array({}); + for (const auto& ring : group) { + auto ring_coords = array({}); + for (const auto& pair : *ring) { + ring_coords->push_back(array({fixed_t{pair.lng(), 6}, fixed_t{pair.lat(), 6}})); + } + if (polygons) { + poly->emplace_back(ring_coords); + } else { + poly = ring_coords; + } } + geom->emplace_back(poly); } + // add a feature features->emplace_back(map({ {"type", std::string("Feature")}, - {"geometry", map({ - {"type", std::string(polygons ? "Polygon" : "LineString")}, - {"coordinates", geom}, - })}, + {"geometry", + map({ + {"type", std::string(polygons ? groups.size() > 1 ? "MultiPolygon" : "Polygon" + : "LineString")}, + {"coordinates", + polygons && geom->size() > 1 ? geom : geom->at(0)}, // unwrap linestring, or polygon + // if there's only one + })}, {"properties", map({ {"metric", std::get<2>(interval)}, {"contour", baldr::json::float_t{std::get<1>(interval)}}, - {"color", hex.str()}, // lines - {"fill", hex.str()}, // geojson.io polys - {"fillColor", hex.str()}, // leaflet polys + {"color", hex}, // lines + {"fill", hex}, // geojson.io polys + {"fillColor", hex}, // leaflet polys {"opacity", fixed_t{.33f, 2}}, // lines {"fill-opacity", fixed_t{.33f, 2}}, // geojson.io polys {"fillOpacity", fixed_t{.33f, 2}}, // leaflet polys @@ -86,43 +195,10 @@ std::string serializeIsochrones(const Api& request, })); } } - // Add input and snapped locations to the geojson - if (show_locations) { - int idx = 0; - for (const auto& location : request.options().locations()) { - // first add all snapped points as MultiPoint feature per origin point - auto snapped_points_array = array({}); - std::unordered_set snapped_points; - for (const auto& path_edge : location.correlation().edges()) { - const midgard::PointLL& snapped_current = - midgard::PointLL(path_edge.ll().lng(), path_edge.ll().lat()); - // remove duplicates of path_edges in case the snapped object is a node - if (snapped_points.insert(snapped_current).second) { - snapped_points_array->push_back( - array({fixed_t{snapped_current.lng(), 6}, fixed_t{snapped_current.lat(), 6}})); - } - }; - features->emplace_back(map( - {{"type", std::string("Feature")}, - {"properties", - map({{"type", std::string("snapped")}, {"location_index", static_cast(idx)}})}, - {"geometry", - map({{"type", std::string("MultiPoint")}, {"coordinates", snapped_points_array}})}})); - - // then each user input point as separate Point feature - const valhalla::LatLng& input_latlng = location.ll(); - const auto input_array = - array({fixed_t{input_latlng.lng(), 6}, fixed_t{input_latlng.lat(), 6}}); - features->emplace_back(map( - {{"type", std::string("Feature")}, - {"properties", - map({{"type", std::string("input")}, {"location_index", static_cast(idx)}})}, - {"geometry", map({{"type", std::string("Point")}, {"coordinates", input_array}})}})); - idx++; - } - } - // make the collection + if (show_locations) + addLocations(request, features); + auto feature_collection = map({ {"type", std::string("FeatureCollection")}, {"features", features}, @@ -142,5 +218,67 @@ std::string serializeIsochrones(const Api& request, return ss.str(); } + +std::string serializeIsochronePbf(Api& request, + std::vector& intervals, + const contours_t& contours) { + // construct pbf output + Isochrone& isochrone = *request.mutable_isochrone(); + + // construct contours + for (size_t isoline_index = 0; isoline_index < contours.size(); ++isoline_index) { + const auto& contour = contours[isoline_index]; + const auto& interval = intervals[isoline_index]; + + auto* interval_pbf = isochrone.mutable_intervals()->Add(); + interval_pbf->set_metric(std::get<2>(interval) == "time" ? Isochrone::time : Isochrone::distance); + + interval_pbf->set_metric_value(std::get<1>(interval)); + + // for each feature + for (const auto& feature : contour) { + grouped_contours_t groups = GroupContours(true, feature); + + // for each group of rings (first is outer, rest is inner) + for (const std::vector& group_ptr : groups) { + auto* contour_pbf = interval_pbf->mutable_contours()->Add(); + + // construct a geometry + for (const std::list* ring : group_ptr) { + std::cerr << "Rings: " << ring->size() << std::endl; + + auto* geom = contour_pbf->mutable_geometries()->Add(); + for (PointLL pair : *ring) { + geom->add_coords(round(pair.lng() * 1e6)); + geom->add_coords(round(pair.lat() * 1e6)); + } + } + } + } + } + + return serializePbf(request); +} + +} // namespace + +namespace valhalla { +namespace tyr { + +std::string serializeIsochrones(Api& request, + std::vector::contour_interval_t>& intervals, + midgard::GriddedData<2>::contours_t& contours, + bool polygons, + bool show_locations) { + + switch (request.options().format()) { + case Options_Format_pbf: + return serializeIsochronePbf(request, intervals, contours); + case Options_Format_json: + return serializeIsochroneJson(request, intervals, contours, show_locations, polygons); + default: + throw; + } +} } // namespace tyr } // namespace valhalla diff --git a/src/tyr/serializers.cc b/src/tyr/serializers.cc index ef417fd739..d70d0a08ec 100644 --- a/src/tyr/serializers.cc +++ b/src/tyr/serializers.cc @@ -213,6 +213,9 @@ std::string serializePbf(Api& request) { case Options::sources_to_targets: selection.set_matrix(true); break; + case Options::isochrone: + selection.set_isochrone(true); + break; // should never get here, actions which dont have pbf yet return json default: throw std::logic_error("Requested action is not yet serializable as pbf"); @@ -238,6 +241,8 @@ std::string serializePbf(Api& request) { request.clear_options(); if (!selection.matrix()) request.clear_matrix(); + if (!selection.isochrone()) + request.clear_isochrone(); // serialize the bytes auto bytes = request.SerializeAsString(); diff --git a/src/valhalla_run_isochrone.cc b/src/valhalla_run_isochrone.cc index fa3175840e..44eda59701 100644 --- a/src/valhalla_run_isochrone.cc +++ b/src/valhalla_run_isochrone.cc @@ -178,7 +178,7 @@ int main(int argc, char* argv[]) { // Compute the isotile auto t1 = std::chrono::high_resolution_clock::now(); - Isochrone isochrone; + valhalla::thor::Isochrone isochrone; auto expansion_type = routetype == "multimodal" ? ExpansionType::multimodal : (reverse ? ExpansionType::reverse : ExpansionType::forward); diff --git a/src/worker.cc b/src/worker.cc index 7c7f33bf00..ca219938dd 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -653,7 +653,8 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { Options::centroid, Options::trace_attributes, Options::status, - Options::sources_to_targets}; + Options::sources_to_targets, + Options::isochrone}; // if its not a pbf supported action we reset to json if (pbf_actions.count(options.action()) == 0) { options.set_format(Options::json); diff --git a/test/gurka/test_pbf_api.cc b/test/gurka/test_pbf_api.cc index a31fb19949..9e0b8ec71b 100644 --- a/test/gurka/test_pbf_api.cc +++ b/test/gurka/test_pbf_api.cc @@ -34,7 +34,8 @@ TEST(pbf_api, pbf_in_out) { Options::centroid, Options::trace_attributes, Options::status, - Options::sources_to_targets}; + Options::sources_to_targets, + Options::isochrone}; PbfFieldSelector select_all; select_all.set_directions(true); @@ -42,10 +43,11 @@ TEST(pbf_api, pbf_in_out) { select_all.set_status(true); select_all.set_options(true); select_all.set_matrix(true); + select_all.set_isochrone(true); for (int action = Options::no_action + 1; action <= Options::Action_MAX; ++action) { // don't have convenient support of these in gurka yet - if (action == Options::isochrone || action == Options::expansion) + if (action == Options::expansion) continue; // do the regular request with json in and out @@ -54,6 +56,10 @@ TEST(pbf_api, pbf_in_out) { if (action == Options::sources_to_targets) { expected_pbf = gurka::do_action(Options::Action(action), map, {"A", "C"}, {"I", "G"}, "pedestrian", {}, {}, &expected_json, &request_json); + } else if (action == Options::isochrone) { + expected_pbf = + gurka::do_action(Options::Action(action), map, {"A"}, "pedestrian", + {{"/contours/0/time", "10"}}, {}, &expected_json, "break", &request_json); } else { expected_pbf = gurka::do_action(Options::Action(action), map, {"A", "C", "I", "G"}, "pedestrian", {}, {}, &expected_json, "break", &request_json); @@ -78,19 +84,19 @@ TEST(pbf_api, pbf_in_out) { pbf_out.mutable_options()->set_format(Options::pbf); pbf_out.mutable_options()->mutable_pbf_field_selector()->CopyFrom(select_all); - auto format = pbf_out.options().format(); auto pbf_bytes = gurka::do_action(map, pbf_out); Api actual_pbf; EXPECT_TRUE(actual_pbf.ParseFromString(pbf_bytes)); EXPECT_EQ(actual_pbf.trip().SerializeAsString(), expected_pbf.trip().SerializeAsString()); EXPECT_TRUE(actual_pbf.has_options()); EXPECT_TRUE(actual_pbf.has_trip() || action == Options::status || - action == Options::sources_to_targets); + action == Options::sources_to_targets || action == Options::isochrone); EXPECT_TRUE(actual_pbf.has_directions() || action != Options::trace_route || action != Options::route); EXPECT_TRUE(actual_pbf.has_status() || action != Options::status); EXPECT_TRUE(actual_pbf.has_info() || action == Options::status); EXPECT_TRUE(actual_pbf.has_matrix() || action != Options::sources_to_targets); + EXPECT_TRUE(actual_pbf.has_isochrone() || action != Options::isochrone); // lets try it again but this time we'll disable all the fields but one Api slimmed; @@ -103,7 +109,7 @@ TEST(pbf_api, pbf_in_out) { EXPECT_TRUE(actual_slimmed.ParseFromString(pbf_bytes)); EXPECT_FALSE(actual_slimmed.has_options()); EXPECT_TRUE(actual_slimmed.has_trip() || action == Options::status || - action == Options::sources_to_targets); + action == Options::sources_to_targets || action == Options::isochrone); EXPECT_FALSE(actual_slimmed.has_directions()); EXPECT_FALSE(actual_slimmed.has_status()); EXPECT_TRUE(actual_slimmed.has_info() || action == Options::status); @@ -117,7 +123,8 @@ TEST(pbf_api, pbf_in_out) { actual_slimmed.Clear(); EXPECT_TRUE(actual_slimmed.ParseFromString(pbf_bytes)); EXPECT_FALSE(actual_slimmed.has_options()); - EXPECT_TRUE(actual_slimmed.has_trip() || action != Options::trace_attributes); + EXPECT_TRUE(actual_slimmed.has_trip() || action != Options::trace_attributes || + action != Options::isochrone); EXPECT_TRUE(actual_slimmed.has_directions() || action != Options::trace_route || action != Options::route); EXPECT_TRUE(actual_slimmed.has_status() || action != Options::status); diff --git a/test/isochrone.cc b/test/isochrone.cc index b63a36abe1..37eed4be9a 100644 --- a/test/isochrone.cc +++ b/test/isochrone.cc @@ -30,13 +30,33 @@ using rp = rapidjson::Pointer; namespace { -const auto cfg = test::make_config(VALHALLA_BUILD_DIR "test/data/utrecht_tiles"); +const auto cfg = test::make_config(VALHALLA_BUILD_DIR "test/data/utrecht_tiles", + {{"service_limits.isochrone.max_locations", "2"}}); void check_coords(const rapidjson::Value& a, const rapidjson::Value& b) { EXPECT_NEAR(a.GetArray()[0].GetDouble(), b.GetArray()[0].GetDouble(), 0.00002); EXPECT_NEAR(a.GetArray()[1].GetDouble(), b.GetArray()[1].GetDouble(), 0.00002); } +void test_iso_shape_equality(const rapidjson::GenericArray& actual_geom, + const rapidjson::GenericArray& expected_geom) { + // different platforms can end up having some slightly different floating point wobble + // to avoid failing tests we measure shape similarity and fail if its too far out of whack + std::vector actual, expected; + for (size_t j = 0; j < std::max(expected_geom.Size(), actual_geom.Size()); ++j) { + if (j < actual_geom.Size()) { + auto c = actual_geom[j].GetArray(); + actual.emplace_back(c[0].GetDouble(), c[1].GetDouble()); + } + if (j < expected_geom.Size()) { + auto c = expected_geom[j].GetArray(); + expected.emplace_back(c[0].GetDouble(), c[1].GetDouble()); + } + } + // TODO: maybe use macro here to have higher tolerance only apply on ARM arch + ASSERT_TRUE(test::shape_equality(actual, expected, 41)); +} + void try_isochrone(loki_worker_t& loki_worker, thor_worker_t& thor_worker, const std::string& test_request, @@ -77,38 +97,55 @@ void try_isochrone(loki_worker_t& loki_worker, rp("/features/" + std::to_string(i) + "/geometry/type").Get(expected_response)->GetString(); ASSERT_EQ(actual_type, expected_type); + std::string coord_selector = "/features/" + std::to_string(i) + "/geometry/coordinates"; // point is special if (expected_type == "Point") { check_coords(*rp("/features/" + std::to_string(i) + "/geometry/coordinates").Get(response), *rp("/features/" + std::to_string(i) + "/geometry/coordinates") .Get(expected_response)); } // iteration required - else { + else if (expected_type == "LineString" || expected_type == "Polygon" || + expected_type == "MultiPoint") { // same geom appx - auto actual_geom = rp("/features/" + std::to_string(i) + "/geometry/coordinates" + - (actual_type == "Polygon" ? "/0" : "")) - .Get(response) - ->GetArray(); - auto expected_geom = rp("/features/" + std::to_string(i) + "/geometry/coordinates" + - (expected_type == "Polygon" ? "/0" : "")) - .Get(expected_response) - ->GetArray(); - - std::vector actual, expected; - for (size_t j = 0; j < std::max(expected_geom.Size(), actual_geom.Size()); ++j) { - if (j < actual_geom.Size()) { - auto c = actual_geom[j].GetArray(); - actual.emplace_back(c[0].GetDouble(), c[1].GetDouble()); - } - if (j < expected_geom.Size()) { - auto c = expected_geom[j].GetArray(); - expected.emplace_back(c[0].GetDouble(), c[1].GetDouble()); + uint32_t size = (actual_type == "LineString" || actual_type == "MultiPoint") + ? 1 + : rp(coord_selector).Get(response)->GetArray().Size(); + for (uint32_t j = 0; j < size; ++j) { + auto actual_geom = + rp(coord_selector + (actual_type == "Polygon" ? "/" + std::to_string(j) : "")) + .Get(response) + ->GetArray(); + auto expected_geom = + rp(coord_selector + (expected_type == "Polygon" ? "/" + std::to_string(j) : "")) + .Get(expected_response) + ->GetArray(); + test_iso_shape_equality(actual_geom, expected_geom); + } + } else { // MultiPolygon + uint32_t actual_poly_size = rp(coord_selector).Get(response)->GetArray().Size(); + uint32_t expected_poly_size = rp(coord_selector).Get(expected_response)->GetArray().Size(); + + ASSERT_EQ(actual_poly_size, expected_poly_size); + + // for each polygon + for (uint32_t j = 0; j < actual_poly_size; ++j) { + + uint32_t actual_ring_count = rp(coord_selector).Get(response)->GetArray()[j].Size(); + uint32_t expected_ring_count = rp(coord_selector).Get(response)->GetArray()[j].Size(); + ASSERT_EQ(actual_ring_count, expected_ring_count); + // test equality of each ring + for (uint32_t k = 0; k < actual_ring_count; ++k) { + auto actual_geom = rp(coord_selector + "/" + std::to_string(j) + "/" + std::to_string(k)) + .Get(response) + ->GetArray(); + auto expected_geom = rp(coord_selector + "/" + std::to_string(j) + "/" + std::to_string(k)) + .Get(expected_response) + ->GetArray(); + + // tests exterior ring equality + test_iso_shape_equality(actual_geom, expected_geom); } } - - // different platforms can end up having some slightly different floating point wobble - // to avoid failing tests we measure shape similarity and fail if its too far out of whack - ASSERT_TRUE(test::shape_equality(actual, expected, 33)); } } } @@ -168,6 +205,24 @@ TEST(Isochrones, Basic) { R"({"features":[{"properties":{"fill-opacity":0.33,"fillColor":"#bf4040","opacity":0.33,"fill":"#bf4040","fillOpacity":0.33,"color":"#bf4040","contour":15,"metric":"time"},"geometry":{"coordinates":[[5.108321,52.106133],[5.105321,52.106242],[5.103227,52.105031],[5.101321,52.105126],[5.100321,52.104612],[5.098321,52.104950],[5.094681,52.103937],[5.094321,52.101694],[5.095785,52.101401],[5.098321,52.098534],[5.100321,52.098287],[5.101321,52.097284],[5.103321,52.097293],[5.103321,52.095801],[5.103029,52.096645],[5.102321,52.096791],[5.095321,52.096753],[5.094321,52.097731],[5.088081,52.097697],[5.087983,52.098275],[5.089502,52.098756],[5.089643,52.099615],[5.090729,52.099937],[5.089321,52.100487],[5.088321,52.100213],[5.087321,52.101688],[5.085751,52.100937],[5.085321,52.099826],[5.082580,52.098937],[5.085321,52.098574],[5.085732,52.097937],[5.085321,52.097544],[5.081321,52.097320],[5.080051,52.097937],[5.080203,52.096819],[5.081875,52.095937],[5.079321,52.095542],[5.078765,52.094937],[5.079911,52.093527],[5.081638,52.092937],[5.078917,52.092342],[5.078764,52.090937],[5.079917,52.088533],[5.081712,52.087937],[5.081321,52.087147],[5.080254,52.087004],[5.079886,52.083937],[5.081540,52.082156],[5.081661,52.079937],[5.080818,52.079440],[5.078731,52.079347],[5.077896,52.079512],[5.077321,52.080426],[5.076321,52.080581],[5.076165,52.079937],[5.077321,52.078611],[5.082321,52.078291],[5.082568,52.077184],[5.084321,52.075779],[5.084576,52.074682],[5.082885,52.073937],[5.084614,52.071644],[5.082187,52.071803],[5.081899,52.072515],[5.079135,52.074751],[5.078965,52.075293],[5.079752,52.075937],[5.078542,52.077158],[5.077321,52.077268],[5.075321,52.075447],[5.070758,52.074937],[5.071321,52.074578],[5.074321,52.074450],[5.074954,52.073570],[5.076549,52.073165],[5.076575,52.071683],[5.075321,52.071450],[5.074808,52.070937],[5.074994,52.069610],[5.075984,52.068937],[5.075914,52.065530],[5.081321,52.065241],[5.084070,52.062686],[5.086663,52.062279],[5.087321,52.061429],[5.089321,52.061093],[5.089444,52.062060],[5.088578,52.063194],[5.088887,52.064371],[5.090321,52.064512],[5.091321,52.063497],[5.092724,52.063340],[5.093321,52.062497],[5.094321,52.064097],[5.094649,52.063265],[5.095460,52.063076],[5.095733,52.062349],[5.100727,52.058343],[5.101298,52.056914],[5.102338,52.056920],[5.102894,52.057364],[5.104321,52.056186],[5.106380,52.055996],[5.105321,52.054503],[5.104321,52.054945],[5.103599,52.054659],[5.103433,52.053825],[5.102665,52.053593],[5.102435,52.052823],[5.100981,52.052277],[5.100321,52.051343],[5.096519,52.051135],[5.096321,52.051628],[5.096115,52.051143],[5.097017,52.050633],[5.102321,52.050477],[5.104321,52.048435],[5.104894,52.050364],[5.106321,52.050394],[5.107706,52.051322],[5.108321,52.050495],[5.111913,52.049529],[5.112321,52.049087],[5.114310,52.050937],[5.113321,52.051958],[5.112934,52.051324],[5.111029,52.051645],[5.112002,52.052937],[5.112126,52.055132],[5.114321,52.055681],[5.115482,52.056776],[5.121321,52.057218],[5.123321,52.059146],[5.125321,52.059215],[5.126321,52.058232],[5.127519,52.059135],[5.128186,52.056802],[5.131812,52.056428],[5.132321,52.056061],[5.136323,52.056935],[5.137321,52.057547],[5.139321,52.057085],[5.139895,52.058363],[5.141624,52.059634],[5.141735,52.060937],[5.142808,52.061450],[5.144321,52.063187],[5.150021,52.057637],[5.151782,52.057398],[5.152321,52.056021],[5.152321,52.058197],[5.150823,52.058439],[5.147866,52.062482],[5.146158,52.063774],[5.145868,52.065484],[5.144321,52.065722],[5.144200,52.066058],[5.145905,52.066353],[5.146321,52.067168],[5.147530,52.067146],[5.148321,52.063899],[5.148740,52.066518],[5.151091,52.068167],[5.152321,52.068296],[5.153321,52.067337],[5.154655,52.067271],[5.156321,52.065622],[5.158078,52.065937],[5.156855,52.066471],[5.155643,52.068259],[5.154321,52.068486],[5.152914,52.069937],[5.153321,52.070437],[5.157335,52.070937],[5.152321,52.071514],[5.151321,52.070638],[5.149107,52.072937],[5.151746,52.073512],[5.152321,52.075389],[5.153321,52.073865],[5.153943,52.075315],[5.155525,52.075734],[5.155380,52.077996],[5.153321,52.076444],[5.151876,52.077937],[5.153321,52.079469],[5.154548,52.079710],[5.155321,52.080761],[5.156130,52.080937],[5.156124,52.082134],[5.156835,52.082937],[5.156094,52.083164],[5.155658,52.082601],[5.154034,52.082224],[5.153670,52.081589],[5.152321,52.081506],[5.151583,52.080675],[5.149878,52.080380],[5.149526,52.079732],[5.148963,52.079937],[5.149164,52.081094],[5.151126,52.083132],[5.153768,52.083490],[5.153716,52.085937],[5.154647,52.087611],[5.155630,52.087937],[5.154321,52.089447],[5.151321,52.089430],[5.150858,52.089937],[5.151321,52.090504],[5.154088,52.090937],[5.153601,52.091217],[5.153321,52.093413],[5.152232,52.094026],[5.150321,52.093097],[5.149321,52.093808],[5.148321,52.093005],[5.147924,52.093540],[5.148321,52.094324],[5.149321,52.094187],[5.149858,52.095400],[5.151321,52.095321],[5.151894,52.097364],[5.154121,52.097937],[5.153491,52.098107],[5.153321,52.098841],[5.152974,52.098284],[5.152321,52.098260],[5.150908,52.098524],[5.150321,52.099513],[5.147207,52.099051],[5.146821,52.098937],[5.148732,52.098348],[5.148625,52.097634],[5.145321,52.097622],[5.144793,52.097937],[5.146181,52.098937],[5.145730,52.099937],[5.146519,52.100937],[5.146321,52.101629],[5.145019,52.101240],[5.144912,52.099937],[5.144321,52.099476],[5.142828,52.100937],[5.143321,52.101667],[5.144485,52.101937],[5.142656,52.102937],[5.140321,52.102566],[5.140038,52.102220],[5.137321,52.102151],[5.136321,52.103072],[5.135895,52.102363],[5.134155,52.102103],[5.133783,52.099937],[5.134321,52.099378],[5.135598,52.099214],[5.135580,52.096937],[5.134666,52.096592],[5.134321,52.095878],[5.132462,52.096078],[5.132120,52.096736],[5.130321,52.097066],[5.129321,52.098041],[5.128321,52.098234],[5.126988,52.099604],[5.124891,52.099367],[5.124313,52.098929],[5.124334,52.102937],[5.120321,52.103242],[5.119321,52.102682],[5.116321,52.105625],[5.115321,52.104350],[5.114321,52.104814],[5.112321,52.104898],[5.111321,52.104483],[5.110652,52.105268],[5.108321,52.106133]],"type":"LineString"},"type":"Feature"},{"geometry":{"coordinates":[[5.115328,52.078940]],"type":"MultiPoint"},"properties":{"location_index":0,"type":"snapped"},"type":"Feature"},{"geometry":{"coordinates":[5.115321,52.078937],"type":"Point"},"properties":{"location_index":0,"type":"input"},"type":"Feature"}],"type":"FeatureCollection"})"; try_isochrone(loki_worker, thor_worker, request, expected); } + + // multi-location + { + const auto request = + R"({"costing":"auto","locations":[{"lon":5.086633,"lat":52.075911},{"lon":5.128852,"lat":52.109455}],"contours":[{"time":2}],"denoise":0,"generalize":100,"polygons":true})"; + const auto expected = + R"({"features":[{"properties":{"fill-opacity":0.33,"fillColor":"#bf4040","opacity":0.33,"fill":"#bf4040","fillOpacity":0.33,"color":"#bf4040","contour":2,"metric":"time"},"geometry":{"coordinates":[[[[5.097852,52.083605],[5.092852,52.081801],[5.088852,52.083234],[5.084989,52.079318],[5.081579,52.079728],[5.091017,52.069291],[5.092793,52.070455],[5.090706,52.071601],[5.093852,52.072047],[5.094362,52.074945],[5.096972,52.074335],[5.095137,52.077455],[5.097140,52.078455],[5.097852,52.083605]]],[[[5.135852,52.111906],[5.122630,52.109677],[5.125079,52.104682],[5.131567,52.103455],[5.133852,52.107889],[5.138735,52.109455],[5.136394,52.109997],[5.135852,52.111906]]]],"type":"MultiPolygon"},"type":"Feature"}],"type":"FeatureCollection"})"; + try_isochrone(loki_worker, thor_worker, request, expected); + } + + // holes + { + const auto request = + R"({"costing":"auto","locations":[{"lon":5.042799,"lat":52.093199}],"contours":[{"time":1}],"denoise":0,"generalize":0,"polygons":true})"; + const auto expected = + R"({"features":[{"properties":{"fill-opacity":0.33,"fillColor":"#bf4040","opacity":0.33,"fill":"#bf4040","fillOpacity":0.33,"color":"#bf4040","contour":1,"metric":"time"},"geometry":{"coordinates":[[[5.045799,52.097113],[5.045609,52.096389],[5.045676,52.096199],[5.045666,52.096066],[5.045581,52.095418],[5.045567,52.095199],[5.045254,52.094744],[5.044799,52.094582],[5.044403,52.094803],[5.044163,52.095199],[5.044032,52.095432],[5.043799,52.095523],[5.043400,52.095598],[5.043190,52.095590],[5.042799,52.095720],[5.042367,52.095631],[5.042161,52.095561],[5.041799,52.095469],[5.041527,52.095471],[5.041071,52.095471],[5.040799,52.095474],[5.040602,52.095396],[5.040450,52.095199],[5.040290,52.094708],[5.040280,52.094680],[5.040116,52.094199],[5.040004,52.093994],[5.039799,52.093869],[5.039495,52.093895],[5.039043,52.094199],[5.038919,52.094319],[5.038799,52.094404],[5.038532,52.094466],[5.038066,52.094466],[5.037799,52.094582],[5.037714,52.094284],[5.037742,52.094199],[5.037736,52.094136],[5.037799,52.093590],[5.037966,52.093366],[5.037944,52.093199],[5.038467,52.092867],[5.038453,52.092545],[5.038578,52.092199],[5.038637,52.092037],[5.038799,52.091994],[5.039295,52.091695],[5.039569,52.091199],[5.039495,52.090895],[5.039617,52.090381],[5.039419,52.090199],[5.039721,52.090121],[5.039799,52.090100],[5.039974,52.090024],[5.040658,52.090058],[5.040799,52.089352],[5.040865,52.089265],[5.041009,52.089199],[5.041619,52.089019],[5.041799,52.089031],[5.041953,52.089045],[5.042508,52.088908],[5.042799,52.088922],[5.043037,52.088961],[5.043467,52.089199],[5.043594,52.089404],[5.043799,52.089847],[5.043856,52.090142],[5.043843,52.090199],[5.043923,52.090323],[5.044099,52.090899],[5.044517,52.090917],[5.044799,52.091046],[5.044977,52.091021],[5.045318,52.091199],[5.045073,52.091473],[5.045140,52.091858],[5.045055,52.092199],[5.045329,52.092669],[5.045799,52.092930],[5.046160,52.092838],[5.046485,52.092885],[5.046799,52.092622],[5.047059,52.092939],[5.047427,52.093199],[5.047577,52.093421],[5.047799,52.093565],[5.048184,52.093584],[5.048744,52.093254],[5.048799,52.093269],[5.048965,52.094034],[5.049327,52.094199],[5.048887,52.094287],[5.048799,52.094420],[5.048690,52.094308],[5.048046,52.094446],[5.047799,52.094385],[5.047419,52.094579],[5.047212,52.094612],[5.047127,52.094871],[5.046920,52.095199],[5.046927,52.095327],[5.046799,52.095577],[5.046439,52.095839],[5.046181,52.096199],[5.046142,52.096542],[5.045799,52.097113]],[[5.044201,52.093601],[5.044464,52.093199],[5.044483,52.092883],[5.044539,52.092460],[5.044557,52.092199],[5.044391,52.091791],[5.044441,52.091557],[5.044172,52.091572],[5.043799,52.091373],[5.043601,52.091397],[5.043105,52.091505],[5.042799,52.091535],[5.042392,52.091792],[5.042242,52.092199],[5.042445,52.092553],[5.042799,52.092699],[5.043132,52.092866],[5.043299,52.093199],[5.043449,52.093549],[5.043799,52.093738],[5.044201,52.093601]]],"type":"Polygon"},"type":"Feature"}],"type":"FeatureCollection"})"; + try_isochrone(loki_worker, thor_worker, request, expected); + } } TEST(Isochrones, OriginEdge) { diff --git a/valhalla/midgard/util.h b/valhalla/midgard/util.h index 64b044d0dc..052162be37 100644 --- a/valhalla/midgard/util.h +++ b/valhalla/midgard/util.h @@ -468,6 +468,15 @@ template struct iterable_t { template bool intersect(const coord_t& u, const coord_t& v, const coord_t& a, const coord_t& b, coord_t& i); +/** + * Check whether a given point lies within a polygon. Uses the simplified winding number algorithm + * (http://www.graphicsgems.org/gemsiv/ptpoly_weiler/) + * + * @return true if the point lies within the given polygon + */ +template +bool point_in_poly(const coord_t& pt, const container_t& poly); + /** * Compute the area of a polygon. If your polygon is not twisted or self intersecting * this will return a positive value for counterclockwise wound polygons and negative otherwise. diff --git a/valhalla/tyr/serializers.h b/valhalla/tyr/serializers.h index 8158570fcf..ecb9fdb319 100644 --- a/valhalla/tyr/serializers.h +++ b/valhalla/tyr/serializers.h @@ -38,7 +38,7 @@ std::string serializeMatrix(Api& request); * @param grid_contours the contours generated from the grid * @param colors the #ABC123 hex string color used in geojson fill color */ -std::string serializeIsochrones(const Api& request, +std::string serializeIsochrones(Api& request, std::vector::contour_interval_t>& intervals, midgard::GriddedData<2>::contours_t& contours, bool polygons = true, From 5ce3c3a5532bc2c24491c72c325cae9316bcf113 Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 19 Feb 2024 16:30:14 +0000 Subject: [PATCH 035/198] refactor how costing options are parsed (#4589) --- src/sif/autocost.cc | 4 +-- src/sif/bicyclecost.cc | 3 +- src/sif/dynamiccost.cc | 10 +++--- src/sif/motorcyclecost.cc | 3 +- src/sif/motorscootercost.cc | 3 +- src/sif/pedestriancost.cc | 3 +- src/sif/transitcost.cc | 3 +- src/sif/truckcost.cc | 3 +- src/valhalla_path_comparison.cc | 11 +++--- src/worker.cc | 61 +++++++++++++++++++++------------ test/astar.cc | 2 +- test/factory.cc | 5 ++- test/map_matcher_factory.cc | 6 ++-- test/parse_request.cc | 33 ++++++++++-------- valhalla/sif/costfactory.h | 20 +++++------ valhalla/sif/dynamiccost.h | 17 ++++++++- 16 files changed, 114 insertions(+), 73 deletions(-) diff --git a/src/sif/autocost.cc b/src/sif/autocost.cc index 0d2d660453..34662ab292 100644 --- a/src/sif/autocost.cc +++ b/src/sif/autocost.cc @@ -1044,8 +1044,8 @@ template std::shared_ptr make_autocost_from_json(const std::string& property, T testVal, const std::string& extra_json = "") { std::stringstream ss; - ss << R"({"costing_options":{"auto":{")" << property << R"(":)" << testVal << "}}" << extra_json - << "}"; + ss << R"({"costing": "auto", "costing_options":{"auto":{")" << property << R"(":)" << testVal + << "}}" << extra_json << "}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return std::make_shared(request.options().costings().find(Costing::auto_)->second); diff --git a/src/sif/bicyclecost.cc b/src/sif/bicyclecost.cc index 9ab737ab73..887b312d87 100644 --- a/src/sif/bicyclecost.cc +++ b/src/sif/bicyclecost.cc @@ -909,7 +909,8 @@ class TestBicycleCost : public BicycleCost { TestBicycleCost* make_bicyclecost_from_json(const std::string& property, float testVal) { std::stringstream ss; - ss << R"({"costing_options":{"bicycle":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "bicycle", "costing_options":{"bicycle":{")" << property << R"(":)" << testVal + << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TestBicycleCost(request.options().costings().find(Costing::bicycle)->second); diff --git a/src/sif/dynamiccost.cc b/src/sif/dynamiccost.cc index b0f6c3ee0d..5416fb7de9 100644 --- a/src/sif/dynamiccost.cc +++ b/src/sif/dynamiccost.cc @@ -488,16 +488,16 @@ void ParseBaseCostOptions(const rapidjson::Value& json, void ParseCosting(const rapidjson::Document& doc, const std::string& costing_options_key, Options& options) { - // if specified, get the costing options in there - for (auto i = Costing::Type_MIN; i <= Costing::Type_MAX; i = Costing::Type(i + 1)) { + // get the needed costing options in there + for (const auto& costing_type : kCostingTypeMapping.at(options.costing_type())) { // Create the costing options key - const auto& costing_str = valhalla::Costing_Enum_Name(i); + const auto& costing_str = valhalla::Costing_Enum_Name(costing_type); if (costing_str.empty()) continue; const auto key = costing_options_key + "/" + costing_str; // Parse the costing options - auto& costing = (*options.mutable_costings())[i]; - ParseCosting(doc, key, &costing, i); + auto& costing = (*options.mutable_costings())[costing_type]; + ParseCosting(doc, key, &costing, costing_type); } } diff --git a/src/sif/motorcyclecost.cc b/src/sif/motorcyclecost.cc index 0f5451b86a..43388c91d3 100644 --- a/src/sif/motorcyclecost.cc +++ b/src/sif/motorcyclecost.cc @@ -613,7 +613,8 @@ class TestMotorcycleCost : public MotorcycleCost { TestMotorcycleCost* make_motorcyclecost_from_json(const std::string& property, float testVal) { std::stringstream ss; - ss << R"({"costing_options":{"motorcycle":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "motorcycle", "costing_options":{"motorcycle":{")" << property << R"(":)" + << testVal << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TestMotorcycleCost(request.options().costings().find(Costing::motorcycle)->second); diff --git a/src/sif/motorscootercost.cc b/src/sif/motorscootercost.cc index 5134380fff..2ac26e8c13 100644 --- a/src/sif/motorscootercost.cc +++ b/src/sif/motorscootercost.cc @@ -640,7 +640,8 @@ class TestMotorScooterCost : public MotorScooterCost { TestMotorScooterCost* make_motorscootercost_from_json(const std::string& property, float testVal) { std::stringstream ss; - ss << R"({"costing_options":{"motor_scooter":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "motor_scooter", "costing_options":{"motor_scooter":{")" << property << R"(":)" + << testVal << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TestMotorScooterCost(request.options().costings().find(Costing::motor_scooter)->second); diff --git a/src/sif/pedestriancost.cc b/src/sif/pedestriancost.cc index a082764b98..3d71a925fa 100644 --- a/src/sif/pedestriancost.cc +++ b/src/sif/pedestriancost.cc @@ -888,7 +888,8 @@ TestPedestrianCost* make_pedestriancost_from_json(const std::string& property, float testVal, const std::string& /*type*/) { std::stringstream ss; - ss << R"({"costing_options":{"pedestrian":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "pedestrian", "costing_options":{"pedestrian":{")" << property << R"(":)" + << testVal << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TestPedestrianCost(request.options().costings().find(Costing::pedestrian)->second); diff --git a/src/sif/transitcost.cc b/src/sif/transitcost.cc index df59647266..37e1603e1f 100644 --- a/src/sif/transitcost.cc +++ b/src/sif/transitcost.cc @@ -716,7 +716,8 @@ namespace { TransitCost* make_transitcost_from_json(const std::string& property, float testVal) { std::stringstream ss; - ss << R"({"costing_options":{"transit":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "transit", "costing_options":{"transit":{")" << property << R"(":)" << testVal + << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TransitCost(request.options().costings().find(Costing::transit)->second); diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index 860c475930..52eec75ab2 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -748,7 +748,8 @@ class TestTruckCost : public TruckCost { TestTruckCost* make_truckcost_from_json(const std::string& property, float testVal) { std::stringstream ss; - ss << R"({"costing_options":{"truck":{")" << property << R"(":)" << testVal << "}}}"; + ss << R"({"costing": "truck", "costing_options":{"truck":{")" << property << R"(":)" << testVal + << "}}}"; Api request; ParseApi(ss.str(), valhalla::Options::route, request); return new TestTruckCost(request.options().costings().find(Costing::truck)->second); diff --git a/src/valhalla_path_comparison.cc b/src/valhalla_path_comparison.cc index 53e37ad941..f75d5ba1dd 100644 --- a/src/valhalla_path_comparison.cc +++ b/src/valhalla_path_comparison.cc @@ -267,11 +267,6 @@ int main(int argc, char* argv[]) { // Get something we can use to fetch tiles valhalla::baldr::GraphReader reader(config.get_child("mjolnir")); - if (!map_match) { - rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", *request.mutable_options()); - } - // Construct costing valhalla::Costing::Type costing; if (valhalla::Costing_Enum_Parse(routetype, &costing)) { @@ -279,6 +274,12 @@ int main(int argc, char* argv[]) { } else { throw std::runtime_error("No costing method found"); } + + if (!map_match) { + rapidjson::Document doc; + sif::ParseCosting(doc, "/costing_options", *request.mutable_options()); + } + valhalla::sif::TravelMode mode; auto mode_costings = valhalla::sif::CostFactory{}.CreateModeCosting(request.options(), mode); auto cost_ptr = mode_costings[static_cast(mode)]; diff --git a/src/worker.cc b/src/worker.cc index ca219938dd..bacc47eabf 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -586,6 +586,38 @@ void parse_contours(const rapidjson::Document& doc, } } +// parse all costings needed to fulfill the request, including recostings +void parse_recostings(const rapidjson::Document& doc, + const std::string& key, + valhalla::Options& options) { + // make sure we only have unique recosting names in the end + std::unordered_set names; + auto check_name = [&names](const valhalla::Costing& recosting) -> void { + if (!recosting.has_name_case()) { + throw valhalla_exception_t{127}; + } else if (!names.insert(recosting.name()).second) { + throw valhalla_exception_t{128}; + } + }; + + // look either in JSON & PBF + auto recostings = rapidjson::get_child_optional(doc, "/recostings"); + if (recostings && recostings->IsArray()) { + names.reserve(recostings->GetArray().Size()); + for (size_t i = 0; i < recostings->GetArray().Size(); ++i) { + // parse the options + std::string key = "/recostings/" + std::to_string(i); + sif::ParseCosting(doc, key, options.add_recostings()); + check_name(*options.recostings().rbegin()); + } + } else if (options.recostings().size()) { + for (auto& recosting : *options.mutable_recostings()) { + check_name(recosting); + sif::ParseCosting(doc, key, &recosting, recosting.type()); + } + } +} + /** * This function takes a json document and parses it into an options (request pbf) object. * The implementation is such that if you passed an already filled out options object the @@ -747,7 +779,7 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { rapidjson::SetValueByPointer(doc, "/costing_options/auto/ignore_closures", true); } - // set the costing based on the name given, redundant for pbf input + // set the costing based on the name given and parse its costing options Costing::Type costing; if (!valhalla::Costing_Enum_Parse(costing_str, &costing)) throw valhalla_exception_t{125, "'" + costing_str + "'"}; @@ -843,6 +875,12 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { } } + // TODO(nils): if we parse the costing options before the ignore_closures logic, + // the gurka_closure_penalty test fails. investigate why.. intuitively it makes no sense, + // as in the above logic the costing options aren't even parsed yet, + // so how can it determine "ignore_closures" there? + sif::ParseCosting(doc, "/costing_options", options); + // if any of the locations params have a date_time object in their locations, we'll remember // only /sources_to_targets will parse more than one location collection and there it's fine bool had_date_time = false; @@ -971,27 +1009,8 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { options.set_verbose(rapidjson::get(doc, "/verbose", options.verbose())); } - // Parse all of the costing options in their specified order - sif::ParseCosting(doc, "/costing_options", options); - // parse any named costings for re-costing a given path - auto recostings = rapidjson::get_child_optional(doc, "/recostings"); - if (recostings && recostings->IsArray()) { - // make sure we only have unique recosting names in the end - std::unordered_set names; - names.reserve(recostings->GetArray().Size()); - for (size_t i = 0; i < recostings->GetArray().Size(); ++i) { - // parse the options - std::string key = "/recostings/" + std::to_string(i); - sif::ParseCosting(doc, key, options.add_recostings()); - if (!options.recostings().rbegin()->has_name_case()) { - throw valhalla_exception_t{127}; - } else if (!names.insert(options.recostings().rbegin()->name()).second) { - throw valhalla_exception_t{128}; - } - } - // TODO: throw if not all names are unique? - } + parse_recostings(doc, "/recostings", options); // get the locations in there parse_locations(doc, api, "locations", 130, ignore_closures, had_date_time); diff --git a/test/astar.cc b/test/astar.cc index 3074bfbea4..f2868007fc 100644 --- a/test/astar.cc +++ b/test/astar.cc @@ -206,8 +206,8 @@ void make_tile() { void create_costing_options(Options& options, Costing::Type costing) { const rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); options.set_costing_type(costing); + sif::ParseCosting(doc, "/costing_options", options); } // Convert locations to format needed by PathAlgorithm std::vector ToPBFLocations(const std::vector& locations, diff --git a/test/factory.cc b/test/factory.cc index 381211ea4f..3facb253bf 100644 --- a/test/factory.cc +++ b/test/factory.cc @@ -15,14 +15,13 @@ namespace { TEST(Factory, Register) { Options options; const rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); CostFactory factory; options.set_costing_type(Costing::auto_); + sif::ParseCosting(doc, "/costing_options", options); auto car = factory.Create(options); options.set_costing_type(Costing::bicycle); + sif::ParseCosting(doc, "/costing_options", options); auto bike = factory.Create(options); - options.set_costing_type(Costing::multimodal); - EXPECT_THROW(factory.Create(options), std::runtime_error); auto truck = factory.Create(Costing::truck); } diff --git a/test/map_matcher_factory.cc b/test/map_matcher_factory.cc index 31a785f04a..e16b509130 100644 --- a/test/map_matcher_factory.cc +++ b/test/map_matcher_factory.cc @@ -23,8 +23,8 @@ using ptree = boost::property_tree::ptree; void create_costing_options(Costing::Type costing, Options& options) { const rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); options.set_costing_type(costing); + sif::ParseCosting(doc, "/costing_options", options); } TEST(MapMatcherFactory, TestMapMatcherFactory) { @@ -124,7 +124,7 @@ TEST(MapMatcherFactory, TestMapMatcherFactory) { delete matcher; - options.set_costing_type(Costing::bicycle); + create_costing_options(Costing::bicycle, options); matcher = factory.Create(options); EXPECT_EQ(matcher->travelmode(), sif::TravelMode::kBicycle) << "should read costing in options correctly again"; @@ -167,7 +167,7 @@ TEST(MapMatcherFactory, TestMapMatcher) { Options options; create_costing_options(Costing::auto_, options); auto auto_matcher = factory.Create(options); - options.set_costing_type(Costing::pedestrian); + create_costing_options(Costing::pedestrian, options); auto pedestrian_matcher = factory.Create(options); // Share the same pool diff --git a/test/parse_request.cc b/test/parse_request.cc index ea02fa7a7d..0d3e2d9d6a 100644 --- a/test/parse_request.cc +++ b/test/parse_request.cc @@ -254,8 +254,8 @@ std::string get_request_str(const std::string& grandparent_key, const std::string& parent_key, const std::string& key, const bool specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + key + R"(":)" + - std::string(specified_value ? "true" : "false") + R"(}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + key + R"(":)" + std::string(specified_value ? "true" : "false") + R"(}}})"; } std::string get_request_str(const std::string& key, const float expected_value) { @@ -271,8 +271,8 @@ std::string get_request_str(const std::string& grandparent_key, const std::string& parent_key, const std::string& key, const float specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + key + R"(":)" + - std::to_string(specified_value) + R"(}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + key + R"(":)" + std::to_string(specified_value) + R"(}}})"; } std::string get_request_str(const std::string& grandparent_key, @@ -281,16 +281,17 @@ std::string get_request_str(const std::string& grandparent_key, const std::string& sibling_value, const std::string& key, const float specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + sibling_key + R"(":")" + - sibling_value + R"(",")" + key + R"(":)" + std::to_string(specified_value) + R"(}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + sibling_key + R"(":")" + sibling_value + R"(",")" + key + R"(":)" + + std::to_string(specified_value) + R"(}}})"; } std::string get_request_str(const std::string& grandparent_key, const std::string& parent_key, const std::string& key, const uint32_t specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + key + R"(":)" + - std::to_string(specified_value) + R"(}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + key + R"(":)" + std::to_string(specified_value) + R"(}}})"; } std::string get_request_str(const std::string& grandparent_key, @@ -299,16 +300,17 @@ std::string get_request_str(const std::string& grandparent_key, const std::string& sibling_value, const std::string& key, const uint32_t specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + sibling_key + R"(":")" + - sibling_value + R"(",")" + key + R"(":)" + std::to_string(specified_value) + R"(}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + sibling_key + R"(":")" + sibling_value + R"(",")" + key + R"(":)" + + std::to_string(specified_value) + R"(}}})"; } std::string get_request_str(const std::string& grandparent_key, const std::string& parent_key, const std::string& key, const std::string& specified_value) { - return R"({")" + grandparent_key + R"(":{")" + parent_key + R"(":{")" + key + R"(":")" + - specified_value + R"("}}})"; + return R"({"costing":")" + parent_key + R"(",")" + grandparent_key + R"(":{")" + parent_key + + R"(":{")" + key + R"(":")" + specified_value + R"("}}})"; } std::string get_request_str(const std::string& key, const uint32_t expected_value) { @@ -359,8 +361,9 @@ std::string get_filter_request_str(const std::string& costing, const std::string& filter_type, const valhalla::FilterAction filter_action, const std::vector& filter_ids) { - return R"({"costing_options":{")" + costing + R"(":{"filters":{")" + filter_type + R"(":{)" + - get_kv_str("action", filter_action) + R"(,)" + get_kv_str("ids", filter_ids) + R"(}}}}})"; + return R"({"costing":")" + costing + R"(",)" + R"("costing_options":{")" + costing + + R"(":{"filters":{")" + filter_type + R"(":{)" + get_kv_str("action", filter_action) + + R"(,)" + get_kv_str("ids", filter_ids) + R"(}}}}})"; } Api get_request(const std::string& request_str, const Options::Action action) { @@ -1642,8 +1645,8 @@ void test_closure_factor_parsing(const Costing::Type costing_type, // Create costing options (reference: /test/astar.cc) void create_costing_options(Options& options, Costing::Type type) { const rapidjson::Document doc; - sif::ParseCosting(doc, "/costing_options", options); options.set_costing_type(type); + sif::ParseCosting(doc, "/costing_options", options); } // Set disable_hierarchy_pruning to true in costing options diff --git a/valhalla/sif/costfactory.h b/valhalla/sif/costfactory.h index 3702d78edd..a512f59720 100644 --- a/valhalla/sif/costfactory.h +++ b/valhalla/sif/costfactory.h @@ -43,6 +43,7 @@ class CostFactory { Register(Costing::pedestrian, CreatePedestrianCost); Register(Costing::truck, CreateTruckCost); Register(Costing::transit, CreateTransitCost); + Register(Costing::multimodal, CreateNoCost); // dummy so it behaves like the rest Register(Costing::none_, CreateNoCost); Register(Costing::bikeshare, CreateBikeShareCost); } @@ -100,21 +101,18 @@ class CostFactory { mode_costing_t CreateModeCosting(const Options& options, TravelMode& mode) { mode_costing_t mode_costing; - // Set travel mode and construct costing + // Set travel mode and construct costing(s) for this type + for (const auto& costing : kCostingTypeMapping.at(options.costing_type())) { + valhalla::sif::cost_ptr_t cost = Create(options.costings().find(costing)->second); + mode = cost->travel_mode(); + mode_costing[static_cast(mode)] = cost; + } if (options.costing_type() == Costing::multimodal || options.costing_type() == Costing::transit || options.costing_type() == Costing::bikeshare) { - // For multi-modal we construct costing for all modes and set the - // initial mode to pedestrian. (TODO - allow other initial modes) - mode_costing[0] = Create(options.costings().find(Costing::auto_)->second); - mode_costing[1] = Create(options.costings().find(Costing::pedestrian)->second); - mode_costing[2] = Create(options.costings().find(Costing::bicycle)->second); - mode_costing[3] = Create(options.costings().find(Costing::transit)->second); + // For multi-modal we set the initial mode to pedestrian. (TODO - allow other initial modes) mode = valhalla::sif::TravelMode::kPedestrian; - } else { - valhalla::sif::cost_ptr_t cost = Create(options); - mode = cost->travel_mode(); - mode_costing[static_cast(mode)] = cost; } + return mode_costing; } diff --git a/valhalla/sif/dynamiccost.h b/valhalla/sif/dynamiccost.h index eee48d92be..fb7ad7eaf0 100644 --- a/valhalla/sif/dynamiccost.h +++ b/valhalla/sif/dynamiccost.h @@ -92,6 +92,21 @@ using namespace valhalla::midgard; namespace valhalla { namespace sif { +const std::unordered_map> kCostingTypeMapping{ + {Costing::none_, {Costing::none_}}, + {Costing::bicycle, {Costing::bicycle}}, + {Costing::bus, {Costing::bus}}, + {Costing::motor_scooter, {Costing::motor_scooter}}, + {Costing::multimodal, {Costing::multimodal, Costing::transit, Costing::pedestrian}}, + {Costing::pedestrian, {Costing::pedestrian}}, + {Costing::transit, {Costing::transit, Costing::pedestrian}}, + {Costing::truck, {Costing::truck}}, + {Costing::motorcycle, {Costing::motorcycle}}, + {Costing::taxi, {Costing::taxi}}, + {Costing::auto_, {Costing::auto_}}, + {Costing::bikeshare, {Costing::bikeshare, Costing::pedestrian, Costing::bicycle}}, +}; + const sif::Cost kNoCost(0.0f, 0.0f); // Default unit size (seconds) for cost sorting. @@ -1224,7 +1239,7 @@ void ParseBaseCostOptions(const rapidjson::Value& json, const BaseCostingOptionsConfig& cfg); /** - * Parses all the costing options for all supported costings + * Parses all the costing options for all needed costings * @param doc json document * @param costing_options_key the key in the json document where the options are located * @param options where to store the parsed costing From 5d250d21ad06e396ddb731d471273780bbfa1655 Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 19 Feb 2024 17:28:09 +0000 Subject: [PATCH 036/198] Update CHANGELOG.md (#4590) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 707e72e82a..49d9dfe985 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Release Date: 2024-??-?? Valhalla 3.4.1 +## Unreleased * **Removed** * REMOVED: needs_ci_run script [#4423](https://github.com/valhalla/valhalla/pull/4423) * REMOVED: unused vehicle types in AutoCost and segway; renamed kTruck to "truck" instead of "tractor_trailer" [#4430](https://github.com/valhalla/valhalla/pull/4430) From 1fa1e017089b95d86cb00529617bfa540cfbbb87 Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Thu, 22 Feb 2024 14:00:57 -0600 Subject: [PATCH 037/198] Remove warnings test gurka wunused variable (#4598) --- test/gurka/CMakeLists.txt | 10 ------ test/gurka/test_access.cc | 4 +-- .../test_admin_sidewalk_crossing_override.cc | 2 -- test/gurka/test_admin_uk_override.cc | 2 -- test/gurka/test_guidance_views.cc | 1 - test/gurka/test_guidance_views_signposts.cc | 1 - .../test_instructions_named_roundabout.cc | 1 - test/gurka/test_instructions_roundabout.cc | 1 - test/gurka/test_languages.cc | 36 ------------------- test/gurka/test_precision.cc | 3 +- test/gurka/test_simple_restrictions.cc | 1 - test/gurka/test_use_direction_on_ways.cc | 1 - test/gurka/test_use_lit.cc | 1 - 13 files changed, 2 insertions(+), 62 deletions(-) diff --git a/test/gurka/CMakeLists.txt b/test/gurka/CMakeLists.txt index 1c6bd54e49..fd20e7806c 100644 --- a/test/gurka/CMakeLists.txt +++ b/test/gurka/CMakeLists.txt @@ -11,14 +11,8 @@ if(ENABLE_DATA_TOOLS) # "-Werror" flag # Avoid adding a filename to this list set(TESTS_WITH_WARNINGS - test_access.cc - test_admin_sidewalk_crossing_override.cc - test_admin_uk_override.cc test_elevation.cc test_gtfs.cc - test_guidance_views.cc - test_guidance_views_signposts.cc - test_instructions_named_roundabout.cc test_instructions_roundabout.cc test_landmarks.cc test_languages.cc @@ -29,14 +23,10 @@ if(ENABLE_DATA_TOOLS) test_pbf_api.cc test_phonemes.cc test_phonemes_w_langs.cc - test_precision.cc test_recost.cc - test_simple_restrictions.cc test_time_tracking.cc test_traffic.cc test_traffic_smoothing.cc - test_use_direction_on_ways.cc - test_use_lit.cc ) ## Add executable targets diff --git a/test/gurka/test_access.cc b/test/gurka/test_access.cc index 488367c1bf..54cbfca82c 100644 --- a/test/gurka/test_access.cc +++ b/test/gurka/test_access.cc @@ -96,7 +96,7 @@ TEST(Standalone, AccessPsvNode) { constexpr double gridsize_metres = 10; const std::string ascii_map = R"( - + A---B---C---D---E | | F | @@ -138,7 +138,6 @@ class Accessibility : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; const std::string ascii_map = R"( A----B----C @@ -464,7 +463,6 @@ class MtbAccess : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; // A--B const std::string ascii_map = R"(A----B----C)"; diff --git a/test/gurka/test_admin_sidewalk_crossing_override.cc b/test/gurka/test_admin_sidewalk_crossing_override.cc index 3f833f8575..9756ad8a89 100644 --- a/test/gurka/test_admin_sidewalk_crossing_override.cc +++ b/test/gurka/test_admin_sidewalk_crossing_override.cc @@ -70,8 +70,6 @@ void GetAdminData(const std::string& dbname, std::string sql = "SELECT admin_level, name from admins;"; uint32_t result = 0; - bool dor = true; - bool intersection_name = false; ret = sqlite3_prepare_v2(db_handle, sql.c_str(), sql.length(), &stmt, 0); if (ret == SQLITE_OK || ret == SQLITE_ERROR) { diff --git a/test/gurka/test_admin_uk_override.cc b/test/gurka/test_admin_uk_override.cc index 99e7159ddb..ef6d328e4c 100644 --- a/test/gurka/test_admin_uk_override.cc +++ b/test/gurka/test_admin_uk_override.cc @@ -66,8 +66,6 @@ void GetAdminData(const std::string& dbname, std::string sql = "SELECT admin_level, name from admins;"; uint32_t result = 0; - bool dor = true; - bool intersection_name = false; ret = sqlite3_prepare_v2(db_handle, sql.c_str(), sql.length(), &stmt, 0); if (ret == SQLITE_OK || ret == SQLITE_ERROR) { diff --git a/test/gurka/test_guidance_views.cc b/test/gurka/test_guidance_views.cc index 9031a022c3..2a3b4fb366 100644 --- a/test/gurka/test_guidance_views.cc +++ b/test/gurka/test_guidance_views.cc @@ -12,7 +12,6 @@ class GuidanceViews : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; // A--B-BASE-C--D-OVERLAY-E--F const std::string ascii_map = R"( diff --git a/test/gurka/test_guidance_views_signposts.cc b/test/gurka/test_guidance_views_signposts.cc index 87456bd236..4cdcc79b13 100644 --- a/test/gurka/test_guidance_views_signposts.cc +++ b/test/gurka/test_guidance_views_signposts.cc @@ -12,7 +12,6 @@ class GuidanceViews_Signboards : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; // A--B-C-SIGNBOARD-D-E--F const std::string ascii_map = R"( diff --git a/test/gurka/test_instructions_named_roundabout.cc b/test/gurka/test_instructions_named_roundabout.cc index 9003e90037..b233c76b1f 100644 --- a/test/gurka/test_instructions_named_roundabout.cc +++ b/test/gurka/test_instructions_named_roundabout.cc @@ -68,7 +68,6 @@ TEST_F(InstructionsNamedRoundabout, RoundaboutEnterOnly) { gurka::assert::raw::expect_maneuvers(result, {DirectionsLeg_Maneuver_Type_kStart, DirectionsLeg_Maneuver_Type_kRoundaboutEnter, DirectionsLeg_Maneuver_Type_kDestination}); - int maneuver_index = 1; // TODO: known issue - future update to end on a roundabout // Verify the enter_roundabout instructions diff --git a/test/gurka/test_instructions_roundabout.cc b/test/gurka/test_instructions_roundabout.cc index ace5f143d0..65e5b8a201 100644 --- a/test/gurka/test_instructions_roundabout.cc +++ b/test/gurka/test_instructions_roundabout.cc @@ -66,7 +66,6 @@ TEST_F(InstructionsRoundabout, RoundaboutEnterOnly) { gurka::assert::raw::expect_maneuvers(result, {DirectionsLeg_Maneuver_Type_kStart, DirectionsLeg_Maneuver_Type_kRoundaboutEnter, DirectionsLeg_Maneuver_Type_kDestination}); - int maneuver_index = 1; // TODO: known issue - future update to end on a roundabout // Verify the enter_roundabout instructions diff --git a/test/gurka/test_languages.cc b/test/gurka/test_languages.cc index a2922e4d80..09b937e343 100644 --- a/test/gurka/test_languages.cc +++ b/test/gurka/test_languages.cc @@ -111,8 +111,6 @@ class RouteWithStreetnameAndSign_en_UnitedStates : public ::testing::Test { EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-82.68811, 40.22535}); @@ -676,8 +674,6 @@ class RouteWithStreetnameAndSign_fr_nl_BrusselsBelgium : public ::testing::Test EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {4.3516970, 50.8465573}); @@ -1283,8 +1279,6 @@ class RouteWithStreetnameAndSign_ru_be_MinskBelarus : public ::testing::Test { EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {27.56191, 53.90246}); @@ -1890,8 +1884,6 @@ class RouteWithStreetnameAndSign_cy_en_Wales : public ::testing::Test { EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-3.73895, 52.29282}); @@ -2436,8 +2428,6 @@ class RouteWithStreetnameAndSign_fr_nl_BrusselsBelgiumRightLeft : public ::testi EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {4.3516970, 50.8465573}); @@ -2611,8 +2601,6 @@ class RouteWithStreetnameAndSign_en_USForwardBackwardWithName : public ::testing EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-76.69980, 40.25882}); @@ -2765,8 +2753,6 @@ class RouteWithStreetnameAndSign_en_USForwardBackwardNoName : public ::testing:: EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-76.69980, 40.25882}); @@ -3038,8 +3024,6 @@ class RouteWithStreetnameAndSign_fr_de_FribourgSwitzerlandMulti : public ::testi EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {7.159328, 46.805244}); @@ -3187,8 +3171,6 @@ class RouteWithStreetnameAndSign_rm_de_BivioSwitzerland : public ::testing::Test EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {9.65035, 46.46977}); @@ -3342,8 +3324,6 @@ class RouteWithStreetnameAndSign_de_ZurichSwitzerland : public ::testing::Test { EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {8.5355, 47.3726}); @@ -3483,8 +3463,6 @@ class RouteWithStreetnameAndSign_fr_nl_EupenBelgium : public ::testing::Test { EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {6.03475, 50.62766}); @@ -3746,8 +3724,6 @@ class RouteWithStreetnameAndSign_ja_en_Japan : public ::testing::Test { EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {139.79079, 35.69194}); @@ -4555,8 +4531,6 @@ class RouteWithStreetnameAndSign_en_fr_OttawaCanada : public ::testing::Test { EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-75.6625, 45.3940}); @@ -5243,8 +5217,6 @@ class RouteWithStreetnameAndSign_en_fr_QuebecCanada : public ::testing::Test { EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-71.2593, 46.8111}); @@ -5839,8 +5811,6 @@ class RouteWithStreetnameAndSign_en_ms_ta_zh_Singapore : public ::testing::Test EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {103.87149, 1.32510}); @@ -6141,8 +6111,6 @@ class RouteWithStreetnameAndSign_ja_en_JapanPronunciations : public ::testing::T EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {139.79079, 35.69194}); @@ -6633,8 +6601,6 @@ class RouteWithStreetnameAndSign_en_USMultiWithNameDash : public ::testing::Test EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-76.69980, 40.25882}); @@ -6786,8 +6752,6 @@ class RouteWithStreetnameAndSign_en_USMultiWithNameSlash : public ::testing::Tes EXPECT_TRUE(created); } - constexpr double gridsize = 100; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-76.69980, 40.25882}); diff --git a/test/gurka/test_precision.cc b/test/gurka/test_precision.cc index b7e825ba79..33c544ba6d 100644 --- a/test/gurka/test_precision.cc +++ b/test/gurka/test_precision.cc @@ -9,7 +9,6 @@ class Precision : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; const std::string ascii_map = R"( A-1--B-C----\ @@ -81,4 +80,4 @@ TEST_F(Precision, PartialOffsetCheckThree) { EXPECT_NEAR(shape[i].lat(), expected_shape[i].lat(), 0.000001); EXPECT_NEAR(shape[i].lng(), expected_shape[i].lng(), 0.000001); } -} \ No newline at end of file +} diff --git a/test/gurka/test_simple_restrictions.cc b/test/gurka/test_simple_restrictions.cc index 03eeec3363..70d3b48c0a 100644 --- a/test/gurka/test_simple_restrictions.cc +++ b/test/gurka/test_simple_restrictions.cc @@ -9,7 +9,6 @@ class SimpleRestrictions : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; const std::string ascii_map = R"( A---1B----C diff --git a/test/gurka/test_use_direction_on_ways.cc b/test/gurka/test_use_direction_on_ways.cc index 7fb750a37c..a2a149403f 100644 --- a/test/gurka/test_use_direction_on_ways.cc +++ b/test/gurka/test_use_direction_on_ways.cc @@ -12,7 +12,6 @@ class UseDirectionOnWays : public ::testing::Test { static gurka::map map; static void SetUpTestSuite() { - constexpr double gridsize = 100; const std::string ascii_map = R"( A--B diff --git a/test/gurka/test_use_lit.cc b/test/gurka/test_use_lit.cc index 8f10c28754..c9a35ecd7c 100644 --- a/test/gurka/test_use_lit.cc +++ b/test/gurka/test_use_lit.cc @@ -48,7 +48,6 @@ TEST_F(UseLitTest, LitHighway) { valhalla::Api default_route = gurka::do_action(valhalla::Options::route, speed_map, {"A", "C"}, "pedestrian"); - float default_time = getDuration(default_route); valhalla::Api lit_route = gurka::do_action(valhalla::Options::route, speed_map, {"A", "C"}, "pedestrian", options); From 055db1a630c2d11e8b70ed33707e750a843d90a1 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Fri, 23 Feb 2024 22:41:14 +0100 Subject: [PATCH 038/198] Fix out-of-range linestrings in expansion results (#4603) --- CHANGELOG.md | 1 + src/thor/isochrone.cc | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49d9dfe985..da0c684181 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -253,6 +253,7 @@ * FIXED: Building Valhalla as a submodule [#3781](https://github.com/valhalla/valhalla/issues/3781) * FIXED: Fixed invalid time detection in GetSpeed [#3800](https://github.com/valhalla/valhalla/pull/3800) * FIXED: Osmway struct update: added up to 33 and not 32 [#3808](https://github.com/valhalla/valhalla/pull/3808) + * FIXED: Fix out-of-range linestrings in expansion [#4603](https://github.com/valhalla/valhalla/pull/4603) * **Enhancement** * CHANGED: Pronunciation for names and destinations [#3132](https://github.com/valhalla/valhalla/pull/3132) diff --git a/src/thor/isochrone.cc b/src/thor/isochrone.cc index 26b51f31d3..64c3811c49 100644 --- a/src/thor/isochrone.cc +++ b/src/thor/isochrone.cc @@ -322,8 +322,10 @@ ExpansionRecommendation Isochrone::ShouldExpand(baldr::GraphReader& /*graphreade // max but need to consider others so we just continue here. Tells MMExpand function to skip // updating or pushing the label back // prune the edge if its start is above max contour - if (time > max_seconds_ && dist > max_meters_) - return ExpansionRecommendation::prune_expansion; + + ExpansionRecommendation recommendation = (time > max_seconds_ && dist > max_meters_) + ? ExpansionRecommendation::prune_expansion + : ExpansionRecommendation::continue_expansion; // track expansion if (inner_expansion_callback_ && (time <= (max_seconds_ - METRIC_PADDING * kSecondsPerMinute) || @@ -334,8 +336,7 @@ ExpansionRecommendation Isochrone::ShouldExpand(baldr::GraphReader& /*graphreade } else if (expansion_callback_) { expansion_callback_ = nullptr; } - - return ExpansionRecommendation::continue_expansion; + return recommendation; }; void Isochrone::GetExpansionHints(uint32_t& bucket_count, uint32_t& edge_label_reservation) const { From 3b824c9575d809586937d3fa95c974d985a1693a Mon Sep 17 00:00:00 2001 From: Nils Date: Sat, 24 Feb 2024 20:45:24 +0000 Subject: [PATCH 039/198] make shortcut logging more useful (#4607) Co-authored-by: Kevin Kreiser --- src/mjolnir/shortcutbuilder.cc | 39 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/mjolnir/shortcutbuilder.cc b/src/mjolnir/shortcutbuilder.cc index ae2de4b0b2..d9d36fbec0 100644 --- a/src/mjolnir/shortcutbuilder.cc +++ b/src/mjolnir/shortcutbuilder.cc @@ -337,18 +337,18 @@ bool IsEnteringEdgeOfContractedNode(GraphReader& reader, const GraphId& nodeid, } // Add shortcut edges (if they should exist) from the specified node -uint32_t AddShortcutEdges(GraphReader& reader, - const graph_tile_ptr& tile, - GraphTileBuilder& tilebuilder, - const GraphId& start_node, - const uint32_t edge_index, - const uint32_t edge_count, - std::unordered_map& shortcuts) { +std::pair AddShortcutEdges(GraphReader& reader, + const graph_tile_ptr& tile, + GraphTileBuilder& tilebuilder, + const GraphId& start_node, + const uint32_t edge_index, + const uint32_t edge_count, + std::unordered_map& shortcuts) { // Shortcut edges have to start at a node that is not contracted - return if // this node can be contracted. EdgePairs edgepairs; if (CanContract(reader, tile, start_node, edgepairs)) { - return 0; + return {uint32_t(0), uint32_t(0)}; } // Check if this is the last edge in a shortcut (if the endnode cannot be contracted). @@ -359,6 +359,7 @@ uint32_t AddShortcutEdges(GraphReader& reader, // Iterate through directed edges of the base node uint32_t shortcut = 0; uint32_t shortcut_count = 0; + uint32_t total_edge_count = 0; GraphId edge_id(start_node.tileid(), start_node.level(), edge_index); for (uint32_t i = 0; i < edge_count; i++, ++edge_id) { // Skip transit connection edges. @@ -376,6 +377,7 @@ uint32_t AddShortcutEdges(GraphReader& reader, // edge of the contracted node. GraphId end_node = directededge->endnode(); if (IsEnteringEdgeOfContractedNode(reader, end_node, edge_id)) { + total_edge_count++; // Form a shortcut edge. DirectedEdge newedge = *directededge; @@ -453,6 +455,7 @@ uint32_t AddShortcutEdges(GraphReader& reader, // off of shortcuts work properly ConnectEdges(reader, end_node, next_edge_id, shape, end_node, opp_local_idx, rst, average_density, total_duration, total_truck_duration); + total_edge_count++; } // Names can be different in the forward and backward direction @@ -554,16 +557,17 @@ uint32_t AddShortcutEdges(GraphReader& reader, LOG_WARN("Exceeding max shortcut edges from a node at LL = " + std::to_string(ll.lat()) + "," + std::to_string(ll.lng())); } - return shortcut_count; + return {shortcut_count, total_edge_count}; } // Form shortcuts for tiles in this level. -uint32_t FormShortcuts(GraphReader& reader, const TileLevel& level) { +std::pair FormShortcuts(GraphReader& reader, const TileLevel& level) { // Iterate through the tiles at this level (TODO - can we mark the tiles // the tiles that shortcuts end within?) reader.Clear(); bool added = false; uint32_t shortcut_count = 0; + uint32_t total_edge_count = 0; uint32_t ntiles = level.tiles.TileCount(); uint32_t tile_level = (uint32_t)level.level; graph_tile_ptr tile; @@ -605,8 +609,10 @@ uint32_t FormShortcuts(GraphReader& reader, const TileLevel& level) { // Add shortcut edges first. std::unordered_map shortcuts; - shortcut_count += AddShortcutEdges(reader, tile, tilebuilder, node_id, old_edge_index, - old_edge_count, shortcuts); + auto stats = AddShortcutEdges(reader, tile, tilebuilder, node_id, old_edge_index, + old_edge_count, shortcuts); + shortcut_count += stats.first; + total_edge_count += stats.second; // Copy the rest of the directed edges from this node GraphId edgeid(tileid, tile_level, old_edge_index); @@ -711,7 +717,7 @@ uint32_t FormShortcuts(GraphReader& reader, const TileLevel& level) { reader.Trim(); } } - return shortcut_count; + return {shortcut_count, total_edge_count}; } } // namespace @@ -736,8 +742,11 @@ void ShortcutBuilder::Build(const boost::property_tree::ptree& pt) { for (; tile_level != TileHierarchy::levels().rend(); ++tile_level) { // Create shortcuts on this level LOG_INFO("Creating shortcuts on level " + std::to_string(tile_level->level)); - [[maybe_unused]] uint32_t count = FormShortcuts(reader, *tile_level); - LOG_INFO("Finished with " + std::to_string(count) + " shortcuts"); + [[maybe_unused]] auto stats = FormShortcuts(reader, *tile_level); + [[maybe_unused]] uint32_t avg = stats.first ? (stats.second / stats.first) : 0; + LOG_INFO("Finished with " + std::to_string(stats.first) + " shortcuts superseding " + + std::to_string(stats.second) + " edges, average ~" + std::to_string(avg) + + " edges per shortcut"); } } From f025e81048d68191d7df3b3475bcb90d462ca2a8 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Tue, 27 Feb 2024 06:56:52 +0100 Subject: [PATCH 040/198] filter out tagged values in intersecting edge street names (#4604) --- CHANGELOG.md | 1 + src/thor/triplegbuilder.cc | 86 ++++++++++++++++++++++---------------- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index da0c684181..b65d254608 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ * FIXED: fix config generator with thor.costmatrix_allow_second_pass [#4567](https://github.com/valhalla/valhalla/pull/4567) * FIXED: infinite loop or other random corruption in isochrones when retrieving partial shape of an edge [#4547](https://github.com/valhalla/valhalla/pull/4547) * FIXED: Aggregation updates: update opposing local idx after aggregating the edges, added classification check for aggregation, and shortcut length changes [#4570](https://github.com/valhalla/valhalla/pull/4570) + * FIXED: Use helper function for only parsing out names from DirectedEdge when populating intersecting edges [#4604](https://github.com/valhalla/valhalla/pull/4604) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/thor/triplegbuilder.cc b/src/thor/triplegbuilder.cc index 69916c33de..6fd95ae072 100644 --- a/src/thor/triplegbuilder.cc +++ b/src/thor/triplegbuilder.cc @@ -747,6 +747,37 @@ void SetElevation(TripLeg_Edge* trip_edge, } } +void ProcessNonTaggedValue(valhalla::StreetName* trip_edge_name, + const LinguisticMap& linguistics, + const std::tuple& name_and_type, + const uint8_t name_index) { + // Assign name and type + trip_edge_name->set_value(std::get<0>(name_and_type)); + trip_edge_name->set_is_route_number(std::get<1>(name_and_type)); + + const auto iter = linguistics.find(name_index); + + if (iter != linguistics.end()) { + + auto lang = static_cast(std::get(iter->second)); + if (lang != Language::kNone) { + trip_edge_name->set_language_tag(GetTripLanguageTag(lang)); + } + + auto alphabet = static_cast( + std::get(iter->second)); + + if (alphabet != PronunciationAlphabet::kNone) { + + auto* pronunciation = trip_edge_name->mutable_pronunciation(); + pronunciation->set_alphabet( + GetTripPronunciationAlphabet(static_cast( + std::get(iter->second)))); + pronunciation->set_value(std::get(iter->second)); + } + } +} + /** * Add trip intersecting edge. * @param controller Controller to determine which attributes to set. @@ -824,16 +855,30 @@ void AddTripIntersectingEdge(const AttributesController& controller, // Add names to edge if requested if (controller.attributes.at(kEdgeNames)) { - // Get the edgeinfo + auto edgeinfo = graphtile->edgeinfo(intersecting_de); auto names_and_types = edgeinfo.GetNamesAndTypes(true); - if (blind_instructions) + if (blind_instructions) { FilterUnneededStreetNumbers(names_and_types); + } intersecting_edge->mutable_name()->Reserve(names_and_types.size()); + const auto linguistics = edgeinfo.GetLinguisticMap(); + + uint8_t name_index = 0; for (const auto& name_and_type : names_and_types) { - auto* trip_edge_name = intersecting_edge->mutable_name()->Add(); - trip_edge_name->set_value(std::get<0>(name_and_type)); - trip_edge_name->set_is_route_number(std::get<1>(name_and_type)); + switch (std::get<2>(name_and_type)) { + case kNotTagged: { + ProcessNonTaggedValue(intersecting_edge->mutable_name()->Add(), linguistics, name_and_type, + name_index); + break; + } + default: + // Skip the rest tagged names + LOG_TRACE(std::string("skipped tagged value= ") + + std::to_string(std::get<2>(name_and_type))); + break; + } + ++name_index; } } @@ -963,37 +1008,6 @@ void AddIntersectingEdges(const AttributesController& controller, } } -void ProcessNonTaggedValue(valhalla::StreetName* trip_edge_name, - const LinguisticMap& linguistics, - const std::tuple& name_and_type, - const uint8_t name_index) { - // Assign name and type - trip_edge_name->set_value(std::get<0>(name_and_type)); - trip_edge_name->set_is_route_number(std::get<1>(name_and_type)); - - const auto iter = linguistics.find(name_index); - - if (iter != linguistics.end()) { - - auto lang = static_cast(std::get(iter->second)); - if (lang != Language::kNone) { - trip_edge_name->set_language_tag(GetTripLanguageTag(lang)); - } - - auto alphabet = static_cast( - std::get(iter->second)); - - if (alphabet != PronunciationAlphabet::kNone) { - - auto* pronunciation = trip_edge_name->mutable_pronunciation(); - pronunciation->set_alphabet( - GetTripPronunciationAlphabet(static_cast( - std::get(iter->second)))); - pronunciation->set_value(std::get(iter->second)); - } - } -} - void ProcessTunnelBridgeTaggedValue(valhalla::StreetName* trip_edge_name, const LinguisticMap& linguistics, const std::tuple& name_and_type, From 6648fb9e83e9e850f3a225b1c64d5d74c98a5ce9 Mon Sep 17 00:00:00 2001 From: Greg Knisely Date: Tue, 27 Feb 2024 10:58:56 -0500 Subject: [PATCH 041/198] Osmnode size reduction (#4605) --- CHANGELOG.md | 1 + src/mjolnir/graphbuilder.cc | 86 ++++--- src/mjolnir/osmdata.cc | 2 + src/mjolnir/pbfgraphparser.cc | 205 +++++++++------- src/mjolnir/util.cc | 7 +- test/countryaccess.cc | 6 +- test/graphbuilder.cc | 9 +- test/graphparser.cc | 34 ++- test/signinfo.cc | 49 ++-- test/utrecht.cc | 4 +- valhalla/mjolnir/graphbuilder.h | 7 + valhalla/mjolnir/osmdata.h | 20 +- valhalla/mjolnir/osmnode.h | 327 ++----------------------- valhalla/mjolnir/osmnodelinguistic.h | 345 +++++++++++++++++++++++++++ valhalla/mjolnir/pbfgraphparser.h | 4 + 15 files changed, 630 insertions(+), 476 deletions(-) create mode 100644 valhalla/mjolnir/osmnodelinguistic.h diff --git a/CHANGELOG.md b/CHANGELOG.md index b65d254608..f32e59e8b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ * FIXED: infinite loop or other random corruption in isochrones when retrieving partial shape of an edge [#4547](https://github.com/valhalla/valhalla/pull/4547) * FIXED: Aggregation updates: update opposing local idx after aggregating the edges, added classification check for aggregation, and shortcut length changes [#4570](https://github.com/valhalla/valhalla/pull/4570) * FIXED: Use helper function for only parsing out names from DirectedEdge when populating intersecting edges [#4604](https://github.com/valhalla/valhalla/pull/4604) + * FIXED: Osmnode size reduction: Fixed excessive disk space for planet build [#4605](https://github.com/valhalla/valhalla/pull/4605) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/mjolnir/graphbuilder.cc b/src/mjolnir/graphbuilder.cc index 6a930845e2..48262d5490 100644 --- a/src/mjolnir/graphbuilder.cc +++ b/src/mjolnir/graphbuilder.cc @@ -18,6 +18,7 @@ #include "midgard/logging.h" #include "midgard/pointll.h" #include "midgard/polyline2.h" +#include "midgard/sequence.h" #include "midgard/tiles.h" #include "midgard/util.h" #include "mjolnir/admin.h" @@ -394,6 +395,7 @@ void BuildTileSet(const std::string& ways_file, const std::string& edges_file, const std::string& complex_restriction_from_file, const std::string& complex_restriction_to_file, + const std::string& linguistic_node_file, const std::string& tile_dir, const OSMData& osmdata, std::map::const_iterator tile_start, @@ -408,6 +410,7 @@ void BuildTileSet(const std::string& ways_file, sequence nodes(nodes_file, false); sequence complex_restrictions_from(complex_restriction_from_file, false); sequence complex_restrictions_to(complex_restriction_to_file, false); + sequence linguistic_node(linguistic_node_file, false); auto database = pt.get_optional("admin"); bool infer_internal_intersections = @@ -916,8 +919,8 @@ void BuildTileSet(const std::string& ways_file, bool has_guide = GraphBuilder::CreateSignInfoList(node, w, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - (directededge.use() == Use::kRamp), + default_languages, linguistic_node, signs, linguistics, + fork, forward, (directededge.use() == Use::kRamp), (directededge.use() == Use::kTurnChannel)); // add signs if signs exist // and directed edge if forward access and auto use @@ -1144,9 +1147,12 @@ void BuildTileSet(const std::string& ways_file, std::vector node_names; std::vector node_langs; - + OSMNodeLinguistic ling; + if (node.linguistic_info_index() != 0) { + ling = linguistic_node.at(node.linguistic_info_index()); + } OSMWay::ProcessNamesPronunciations(osmdata.node_names, default_languages, - node.name_index(), node.name_lang_index(), node_names, + node.name_index(), ling.name_lang_index(), node_names, node_langs, false); std::vector signs; @@ -1157,14 +1163,14 @@ void BuildTileSet(const std::string& ways_file, jeita_langs; GraphBuilder::GetPronunciationTokens(osmdata.node_names, default_languages, - node.name_pronunciation_ipa_index(), - node.name_pronunciation_ipa_lang_index(), - node.name_pronunciation_nt_sampa_index(), - node.name_pronunciation_nt_sampa_lang_index(), - node.name_pronunciation_katakana_index(), - node.name_pronunciation_katakana_lang_index(), - node.name_pronunciation_jeita_index(), - node.name_pronunciation_jeita_lang_index(), + ling.name_pronunciation_ipa_index(), + ling.name_pronunciation_ipa_lang_index(), + ling.name_pronunciation_nt_sampa_index(), + ling.name_pronunciation_nt_sampa_lang_index(), + ling.name_pronunciation_katakana_index(), + ling.name_pronunciation_katakana_lang_index(), + ling.name_pronunciation_jeita_index(), + ling.name_pronunciation_jeita_lang_index(), ipa_tokens, ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, katakana_langs, jeita_tokens, jeita_langs); @@ -1283,6 +1289,7 @@ void BuildLocalTiles(const unsigned int thread_count, const std::string& edges_file, const std::string& complex_from_restriction_file, const std::string& complex_to_restriction_file, + const std::string& linguistic_node_file, const std::map& tiles, const std::string& tile_dir, DataQuality& stats, @@ -1317,7 +1324,8 @@ void BuildLocalTiles(const unsigned int thread_count, threads[i].reset(new std::thread(BuildTileSet, std::cref(ways_file), std::cref(way_nodes_file), std::cref(nodes_file), std::cref(edges_file), std::cref(complex_from_restriction_file), - std::cref(complex_to_restriction_file), std::cref(tile_dir), + std::cref(complex_to_restriction_file), + std::cref(linguistic_node_file), std::cref(tile_dir), std::cref(osmdata), tile_start, tile_end, tile_creation_date, std::cref(pt.get_child("mjolnir")), std::ref(results[i]))); } @@ -1413,6 +1421,7 @@ void GraphBuilder::Build(const boost::property_tree::ptree& pt, const std::string& edges_file, const std::string& complex_from_restriction_file, const std::string& complex_to_restriction_file, + const std::string& linguistic_node_file, const std::map& tiles) { // Reclassify links (ramps). Cannot do this when building tiles since the // edge list needs to be modified. ReclassifyLinks also infers turn channels @@ -1442,8 +1451,8 @@ void GraphBuilder::Build(const boost::property_tree::ptree& pt, pt.get("mjolnir.concurrency", std::thread::hardware_concurrency())); std::string tile_dir = pt.get("mjolnir.tile_dir"); BuildLocalTiles(threads, osmdata, ways_file, way_nodes_file, nodes_file, edges_file, - complex_from_restriction_file, complex_to_restriction_file, tiles, tile_dir, stats, - pt); + complex_from_restriction_file, complex_to_restriction_file, linguistic_node_file, + tiles, tile_dir, stats, pt); stats.LogStatistics(); } @@ -1705,6 +1714,7 @@ bool GraphBuilder::CreateSignInfoList( const std::map, uint32_t>& langMap, const OSMData& osmdata, const std::vector>& default_languages, + sequence& linguistic_node, std::vector& exit_list, std::vector& linguistics, bool fork, @@ -1836,17 +1846,22 @@ bool GraphBuilder::CreateSignInfoList( add_katakana, add_jeita); } else if (node.has_ref() && !fork && ramp) { + OSMNodeLinguistic ling; + if (node.linguistic_info_index() != 0) { + ling = linguistic_node.at(node.linguistic_info_index()); + } + way.ProcessNamesPronunciations(osmdata.node_names, default_languages, node.ref_index(), - node.ref_lang_index(), sign_names, sign_langs, false); + ling.ref_lang_index(), sign_names, sign_langs, false); has_phoneme = - get_pronunciations(osmdata.node_names, default_languages, node.ref_pronunciation_ipa_index(), - node.ref_pronunciation_ipa_lang_index(), - node.ref_pronunciation_nt_sampa_index(), - node.ref_pronunciation_nt_sampa_lang_index(), - node.ref_pronunciation_katakana_index(), - node.ref_pronunciation_katakana_lang_index(), - node.ref_pronunciation_jeita_index(), - node.ref_pronunciation_jeita_lang_index(), ipa_tokens, ipa_langs, + get_pronunciations(osmdata.node_names, default_languages, ling.ref_pronunciation_ipa_index(), + ling.ref_pronunciation_ipa_lang_index(), + ling.ref_pronunciation_nt_sampa_index(), + ling.ref_pronunciation_nt_sampa_lang_index(), + ling.ref_pronunciation_katakana_index(), + ling.ref_pronunciation_katakana_lang_index(), + ling.ref_pronunciation_jeita_index(), + ling.ref_pronunciation_jeita_lang_index(), ipa_tokens, ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, katakana_langs, jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, add_katakana, add_jeita); } @@ -2127,17 +2142,22 @@ bool GraphBuilder::CreateSignInfoList( sign_type = Sign::Type::kExitName; // Get the name from OSMData using the name index + OSMNodeLinguistic ling; + if (node.linguistic_info_index() != 0) { + ling = linguistic_node.at(node.linguistic_info_index()); + } + way.ProcessNamesPronunciations(osmdata.node_names, default_languages, node.name_index(), - node.name_lang_index(), sign_names, sign_langs, false); + ling.name_lang_index(), sign_names, sign_langs, false); has_phoneme = - get_pronunciations(osmdata.node_names, default_languages, node.name_pronunciation_ipa_index(), - node.name_pronunciation_ipa_lang_index(), - node.name_pronunciation_nt_sampa_index(), - node.name_pronunciation_nt_sampa_lang_index(), - node.name_pronunciation_katakana_index(), - node.name_pronunciation_katakana_lang_index(), - node.name_pronunciation_jeita_index(), - node.name_pronunciation_jeita_lang_index(), ipa_tokens, ipa_langs, + get_pronunciations(osmdata.node_names, default_languages, ling.name_pronunciation_ipa_index(), + ling.name_pronunciation_ipa_lang_index(), + ling.name_pronunciation_nt_sampa_index(), + ling.name_pronunciation_nt_sampa_lang_index(), + ling.name_pronunciation_katakana_index(), + ling.name_pronunciation_katakana_lang_index(), + ling.name_pronunciation_jeita_index(), + ling.name_pronunciation_jeita_lang_index(), ipa_tokens, ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, katakana_langs, jeita_tokens, jeita_langs, add_ipa, add_nt_sampa, add_katakana, add_jeita); } diff --git a/src/mjolnir/osmdata.cc b/src/mjolnir/osmdata.cc index 3ea914971b..591e4a57db 100644 --- a/src/mjolnir/osmdata.cc +++ b/src/mjolnir/osmdata.cc @@ -551,6 +551,7 @@ bool OSMData::write_to_temp_files(const std::string& tile_dir) { file.write(reinterpret_cast(&node_ref_count), sizeof(uint64_t)); file.write(reinterpret_cast(&node_name_count), sizeof(uint64_t)); file.write(reinterpret_cast(&node_exit_to_count), sizeof(uint64_t)); + file.write(reinterpret_cast(&node_linguistic_count), sizeof(uint64_t)); file.close(); // Write the rest of OSMData @@ -594,6 +595,7 @@ bool OSMData::read_from_temp_files(const std::string& tile_dir) { file.read(reinterpret_cast(&node_ref_count), sizeof(uint64_t)); file.read(reinterpret_cast(&node_name_count), sizeof(uint64_t)); file.read(reinterpret_cast(&node_exit_to_count), sizeof(uint64_t)); + file.read(reinterpret_cast(&node_linguistic_count), sizeof(uint64_t)); file.close(); // Read the other data diff --git a/src/mjolnir/pbfgraphparser.cc b/src/mjolnir/pbfgraphparser.cc index 81e04693b4..24663c5fbb 100644 --- a/src/mjolnir/pbfgraphparser.cc +++ b/src/mjolnir/pbfgraphparser.cc @@ -1896,6 +1896,7 @@ struct graph_callback : public OSMPBF::Callback { bool named_toll_node = false; OSMNode n; + OSMNodeLinguistic linguistics; n.set_id(osmid); n.set_latlng(lng, lat); bool intersection = false; @@ -2056,9 +2057,9 @@ struct graph_callback : public OSMPBF::Callback { } } if (boost::algorithm::starts_with(t, "name:")) { - ProcessPronunciationTag(OSMLinguistic::Type::kNodeName, alphabet, &n); + ProcessPronunciationTag(OSMLinguistic::Type::kNodeName, alphabet, &linguistics); } else if (boost::algorithm::starts_with(t, "ref:")) { - ProcessPronunciationTag(OSMLinguistic::Type::kNodeRef, alphabet, &n); + ProcessPronunciationTag(OSMLinguistic::Type::kNodeRef, alphabet, &linguistics); } } } @@ -2067,37 +2068,43 @@ struct graph_callback : public OSMPBF::Callback { std::string l = language_; ProcessName(name_w_lang_, name_, language_); n.set_name_index(osmdata_.node_names.index(name_)); - n.set_name_lang_index(osmdata_.node_names.index(language_)); + linguistics.set_name_lang_index(osmdata_.node_names.index(language_)); // begin ref logic l = ref_language_; ProcessName(ref_w_lang_, ref_, ref_language_); n.set_ref_index(osmdata_.node_names.index(ref_)); - n.set_ref_lang_index(osmdata_.node_names.index(ref_language_)); + linguistics.set_ref_lang_index(osmdata_.node_names.index(ref_language_)); ProcessPronunciationName(OSMLinguistic::Type::kNodeName, PronunciationAlphabet::kIpa, name_ipa_, - &n); + &linguistics); ProcessPronunciationName(OSMLinguistic::Type::kNodeName, PronunciationAlphabet::kKatakana, - name_katakana_, &n); + name_katakana_, &linguistics); ProcessPronunciationName(OSMLinguistic::Type::kNodeName, PronunciationAlphabet::kJeita, - name_jeita_, &n); + name_jeita_, &linguistics); ProcessPronunciationName(OSMLinguistic::Type::kNodeName, PronunciationAlphabet::kNtSampa, - name_nt_sampa_, &n); + name_nt_sampa_, &linguistics); ProcessPronunciationName(OSMLinguistic::Type::kNodeRef, PronunciationAlphabet::kIpa, ref_ipa_, - &n); + &linguistics); ProcessPronunciationName(OSMLinguistic::Type::kNodeRef, PronunciationAlphabet::kKatakana, - ref_katakana_, &n); + ref_katakana_, &linguistics); ProcessPronunciationName(OSMLinguistic::Type::kNodeRef, PronunciationAlphabet::kJeita, ref_jeita_, - &n); + &linguistics); ProcessPronunciationName(OSMLinguistic::Type::kNodeRef, PronunciationAlphabet::kNtSampa, - ref_nt_sampa_, &n); + ref_nt_sampa_, &linguistics); + + if (!linguistics.isEmpty()) { + n.set_linguistic_info_index(osmdata_.node_linguistic_count); + node_linguistics_->push_back(linguistics); + ++osmdata_.node_linguistic_count; + } // Different types of named nodes are tagged as a named intersection n.set_named_intersection(named_junction || named_toll_node); @@ -4036,7 +4043,8 @@ struct graph_callback : public OSMPBF::Callback { sequence* access, sequence* complex_restrictions_from, sequence* complex_restrictions_to, - sequence* bss_nodes) { + sequence* bss_nodes, + sequence* node_linguistics) { // reset the pointers (either null them out or set them to something valid) ways_.reset(ways); way_nodes_.reset(way_nodes); @@ -4044,34 +4052,42 @@ struct graph_callback : public OSMPBF::Callback { complex_restrictions_from_.reset(complex_restrictions_from); complex_restrictions_to_.reset(complex_restrictions_to); bss_nodes_.reset(bss_nodes); + node_linguistics_.reset(node_linguistics); + + if (node_linguistics != nullptr) { + // push empty struct at index 0 + OSMNodeLinguistic ling; + node_linguistics_->push_back(ling); + ++osmdata_.node_linguistic_count; + } } void ProcessPronunciationTag(const OSMLinguistic::Type& type, const PronunciationAlphabet& alphabet, - OSMNode* n = nullptr) { + OSMNodeLinguistic* linguistics = nullptr) { uint32_t name = 0, lang = 0; uint8_t t = static_cast(type); uint8_t alpha = static_cast(alphabet); - if (n) { + if (linguistics) { if (type == OSMLinguistic::Type::kNodeName) { switch (alphabet) { case PronunciationAlphabet::kIpa: - name = n->name_pronunciation_ipa_index(); - lang = n->name_pronunciation_ipa_lang_index(); + name = linguistics->name_pronunciation_ipa_index(); + lang = linguistics->name_pronunciation_ipa_lang_index(); break; case PronunciationAlphabet::kKatakana: - name = n->name_pronunciation_katakana_index(); - lang = n->name_pronunciation_katakana_lang_index(); + name = linguistics->name_pronunciation_katakana_index(); + lang = linguistics->name_pronunciation_katakana_lang_index(); break; case PronunciationAlphabet::kJeita: - name = n->name_pronunciation_jeita_index(); - lang = n->name_pronunciation_jeita_lang_index(); + name = linguistics->name_pronunciation_jeita_index(); + lang = linguistics->name_pronunciation_jeita_lang_index(); break; case PronunciationAlphabet::kNtSampa: - name = n->name_pronunciation_nt_sampa_index(); - lang = n->name_pronunciation_nt_sampa_lang_index(); + name = linguistics->name_pronunciation_nt_sampa_index(); + lang = linguistics->name_pronunciation_nt_sampa_lang_index(); break; case PronunciationAlphabet::kNone: break; @@ -4079,20 +4095,20 @@ struct graph_callback : public OSMPBF::Callback { } else if (type == OSMLinguistic::Type::kNodeRef) { switch (alphabet) { case PronunciationAlphabet::kIpa: - name = n->ref_pronunciation_ipa_index(); - lang = n->ref_pronunciation_ipa_lang_index(); + name = linguistics->ref_pronunciation_ipa_index(); + lang = linguistics->ref_pronunciation_ipa_lang_index(); break; case PronunciationAlphabet::kKatakana: - name = n->ref_pronunciation_katakana_index(); - lang = n->ref_pronunciation_katakana_lang_index(); + name = linguistics->ref_pronunciation_katakana_index(); + lang = linguistics->ref_pronunciation_katakana_lang_index(); break; case PronunciationAlphabet::kJeita: - name = n->ref_pronunciation_jeita_index(); - lang = n->ref_pronunciation_jeita_lang_index(); + name = linguistics->ref_pronunciation_jeita_index(); + lang = linguistics->ref_pronunciation_jeita_lang_index(); break; case PronunciationAlphabet::kNtSampa: - name = n->ref_pronunciation_nt_sampa_index(); - lang = n->ref_pronunciation_nt_sampa_lang_index(); + name = linguistics->ref_pronunciation_nt_sampa_index(); + lang = linguistics->ref_pronunciation_nt_sampa_lang_index(); break; case PronunciationAlphabet::kNone: break; @@ -4106,14 +4122,15 @@ struct graph_callback : public OSMPBF::Callback { std::string name_w_lang, language; if (name != 0) - name_w_lang = !n ? osmdata_.name_offset_map.name(name) : osmdata_.node_names.name(name); + name_w_lang = + !linguistics ? osmdata_.name_offset_map.name(name) : osmdata_.node_names.name(name); if (lang != 0) - language = !n ? osmdata_.name_offset_map.name(lang) : osmdata_.node_names.name(lang); + language = !linguistics ? osmdata_.name_offset_map.name(lang) : osmdata_.node_names.name(lang); ProcessNameTag(tag_, name_w_lang, language, true); - SavePronunciationData(t, alpha, name_w_lang, language, n); + SavePronunciationData(t, alpha, name_w_lang, language, linguistics); } void ProcessLeftRightPronunciationTag(const OSMLinguistic::Type& type, @@ -4141,30 +4158,36 @@ struct graph_callback : public OSMPBF::Callback { const uint8_t alphabet, const std::string& pronunciation, const std::string& language, - OSMNode* n = nullptr) { + OSMNodeLinguistic* linguistics = nullptr) { if (!pronunciation.empty()) { has_pronunciation_tags_ = true; - if (n) { + if (linguistics) { OSMLinguistic::Type t = static_cast(type); PronunciationAlphabet alpha = static_cast(alphabet); if (t == OSMLinguistic::Type::kNodeName) { switch (alpha) { case PronunciationAlphabet::kIpa: - n->set_name_pronunciation_ipa_index(osmdata_.node_names.index(pronunciation)); - n->set_name_pronunciation_ipa_lang_index(osmdata_.node_names.index(language)); + linguistics->set_name_pronunciation_ipa_index(osmdata_.node_names.index(pronunciation)); + linguistics->set_name_pronunciation_ipa_lang_index(osmdata_.node_names.index(language)); break; case PronunciationAlphabet::kKatakana: - n->set_name_pronunciation_katakana_index(osmdata_.node_names.index(pronunciation)); - n->set_name_pronunciation_katakana_lang_index(osmdata_.node_names.index(language)); + linguistics->set_name_pronunciation_katakana_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_name_pronunciation_katakana_lang_index( + osmdata_.node_names.index(language)); break; case PronunciationAlphabet::kJeita: - n->set_name_pronunciation_jeita_index(osmdata_.node_names.index(pronunciation)); - n->set_name_pronunciation_jeita_lang_index(osmdata_.node_names.index(language)); + linguistics->set_name_pronunciation_jeita_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_name_pronunciation_jeita_lang_index( + osmdata_.node_names.index(language)); break; case PronunciationAlphabet::kNtSampa: - n->set_name_pronunciation_nt_sampa_index(osmdata_.node_names.index(pronunciation)); - n->set_name_pronunciation_nt_sampa_lang_index(osmdata_.node_names.index(language)); + linguistics->set_name_pronunciation_nt_sampa_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_name_pronunciation_nt_sampa_lang_index( + osmdata_.node_names.index(language)); break; case PronunciationAlphabet::kNone: break; @@ -4172,20 +4195,26 @@ struct graph_callback : public OSMPBF::Callback { } else if (t == OSMLinguistic::Type::kNodeRef) { switch (alpha) { case PronunciationAlphabet::kIpa: - n->set_ref_pronunciation_ipa_index(osmdata_.node_names.index(pronunciation)); - n->set_ref_pronunciation_ipa_lang_index(osmdata_.node_names.index(language)); + linguistics->set_ref_pronunciation_ipa_index(osmdata_.node_names.index(pronunciation)); + linguistics->set_ref_pronunciation_ipa_lang_index(osmdata_.node_names.index(language)); break; case PronunciationAlphabet::kKatakana: - n->set_ref_pronunciation_katakana_index(osmdata_.node_names.index(pronunciation)); - n->set_ref_pronunciation_katakana_lang_index(osmdata_.node_names.index(language)); + linguistics->set_ref_pronunciation_katakana_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_ref_pronunciation_katakana_lang_index( + osmdata_.node_names.index(language)); break; case PronunciationAlphabet::kJeita: - n->set_ref_pronunciation_jeita_index(osmdata_.node_names.index(pronunciation)); - n->set_ref_pronunciation_jeita_lang_index(osmdata_.node_names.index(language)); + linguistics->set_ref_pronunciation_jeita_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_ref_pronunciation_jeita_lang_index( + osmdata_.node_names.index(language)); break; case PronunciationAlphabet::kNtSampa: - n->set_ref_pronunciation_nt_sampa_index(osmdata_.node_names.index(pronunciation)); - n->set_ref_pronunciation_nt_sampa_lang_index(osmdata_.node_names.index(language)); + linguistics->set_ref_pronunciation_nt_sampa_index( + osmdata_.node_names.index(pronunciation)); + linguistics->set_ref_pronunciation_nt_sampa_lang_index( + osmdata_.node_names.index(language)); break; case PronunciationAlphabet::kNone: break; @@ -4322,30 +4351,30 @@ struct graph_callback : public OSMPBF::Callback { void ProcessPronunciationName(const OSMLinguistic::Type& type, const PronunciationAlphabet& alphabet, std::string& name_w_pronunciation, - OSMNode* n = nullptr) { + OSMNodeLinguistic* linguistics = nullptr) { uint32_t name_index = 0, lang_index = 0; std::string language, name_pronunciation_w_lang; uint8_t t = static_cast(type); uint8_t alpha = static_cast(alphabet); - if (n) { + if (linguistics) { if (type == OSMLinguistic::Type::kNodeName) { switch (alphabet) { case PronunciationAlphabet::kIpa: - name_index = n->name_pronunciation_ipa_index(); - lang_index = n->name_pronunciation_ipa_lang_index(); + name_index = linguistics->name_pronunciation_ipa_index(); + lang_index = linguistics->name_pronunciation_ipa_lang_index(); break; case PronunciationAlphabet::kKatakana: - name_index = n->name_pronunciation_katakana_index(); - lang_index = n->name_pronunciation_katakana_lang_index(); + name_index = linguistics->name_pronunciation_katakana_index(); + lang_index = linguistics->name_pronunciation_katakana_lang_index(); break; case PronunciationAlphabet::kJeita: - name_index = n->name_pronunciation_jeita_index(); - lang_index = n->name_pronunciation_jeita_lang_index(); + name_index = linguistics->name_pronunciation_jeita_index(); + lang_index = linguistics->name_pronunciation_jeita_lang_index(); break; case PronunciationAlphabet::kNtSampa: - name_index = n->name_pronunciation_nt_sampa_index(); - lang_index = n->name_pronunciation_nt_sampa_lang_index(); + name_index = linguistics->name_pronunciation_nt_sampa_index(); + lang_index = linguistics->name_pronunciation_nt_sampa_lang_index(); break; case PronunciationAlphabet::kNone: break; @@ -4353,20 +4382,20 @@ struct graph_callback : public OSMPBF::Callback { } else if (type == OSMLinguistic::Type::kNodeRef) { switch (alphabet) { case PronunciationAlphabet::kIpa: - name_index = n->ref_pronunciation_ipa_index(); - lang_index = n->ref_pronunciation_ipa_lang_index(); + name_index = linguistics->ref_pronunciation_ipa_index(); + lang_index = linguistics->ref_pronunciation_ipa_lang_index(); break; case PronunciationAlphabet::kKatakana: - name_index = n->ref_pronunciation_katakana_index(); - lang_index = n->ref_pronunciation_katakana_lang_index(); + name_index = linguistics->ref_pronunciation_katakana_index(); + lang_index = linguistics->ref_pronunciation_katakana_lang_index(); break; case PronunciationAlphabet::kJeita: - name_index = n->ref_pronunciation_jeita_index(); - lang_index = n->ref_pronunciation_jeita_lang_index(); + name_index = linguistics->ref_pronunciation_jeita_index(); + lang_index = linguistics->ref_pronunciation_jeita_lang_index(); break; case PronunciationAlphabet::kNtSampa: - name_index = n->ref_pronunciation_nt_sampa_index(); - lang_index = n->ref_pronunciation_nt_sampa_lang_index(); + name_index = linguistics->ref_pronunciation_nt_sampa_index(); + lang_index = linguistics->ref_pronunciation_nt_sampa_lang_index(); break; case PronunciationAlphabet::kNone: break; @@ -4378,16 +4407,16 @@ struct graph_callback : public OSMPBF::Callback { } if (name_index != 0) - name_pronunciation_w_lang = - !n ? osmdata_.name_offset_map.name(name_index) : osmdata_.node_names.name(name_index); + name_pronunciation_w_lang = !linguistics ? osmdata_.name_offset_map.name(name_index) + : osmdata_.node_names.name(name_index); if (lang_index != 0) - language = - !n ? osmdata_.name_offset_map.name(lang_index) : osmdata_.node_names.name(lang_index); + language = !linguistics ? osmdata_.name_offset_map.name(lang_index) + : osmdata_.node_names.name(lang_index); ProcessName(name_pronunciation_w_lang, name_w_pronunciation, language); - SavePronunciationData(t, alpha, name_w_pronunciation, language, n); + SavePronunciationData(t, alpha, name_w_pronunciation, language, linguistics); } void ProcessPronunciationLRFBName(const std::string& pronunciation_name, @@ -4810,8 +4839,7 @@ struct graph_callback : public OSMPBF::Callback { // user entered access std::unique_ptr> access_; - // way pronunciations - // GREG std::unique_ptr> pronunciation_; + // from complex restrictions std::unique_ptr> complex_restrictions_from_; // used to find out if a wayid is the to edge for a complex restriction @@ -4820,6 +4848,9 @@ struct graph_callback : public OSMPBF::Callback { // bss nodes std::unique_ptr> bss_nodes_; + // node linguistics + std::unique_ptr> node_linguistics_; + // used to set "culdesac" labels to loop roads correctly culdesac_processor culdesac_processor_; @@ -4880,7 +4911,7 @@ OSMData PBFGraphParser::ParseWays(const boost::property_tree::ptree& pt, callback.reset(new sequence(ways_file, true), new sequence(way_nodes_file, true), - new sequence(access_file, true), nullptr, nullptr, nullptr); + new sequence(access_file, true), nullptr, nullptr, nullptr, nullptr); // Parse the ways and find all node Ids needed (those that are part of a // way's node list. Iterate through each pbf input file. LOG_INFO("Parsing ways..."); @@ -4898,7 +4929,7 @@ OSMData PBFGraphParser::ParseWays(const boost::property_tree::ptree& pt, LOG_INFO("Finished with " + std::to_string(osmdata.osm_way_count) + " routable ways containing " + std::to_string(osmdata.osm_way_node_count) + " nodes"); - callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); // we need to sort the access tags so that we can easily find them. LOG_INFO("Sorting osm access tags by way id..."); @@ -4947,7 +4978,7 @@ void PBFGraphParser::ParseRelations(const boost::property_tree::ptree& pt, callback.reset(nullptr, nullptr, nullptr, new sequence(complex_restriction_from_file, true), - new sequence(complex_restriction_to_file, true), nullptr); + new sequence(complex_restriction_to_file, true), nullptr, nullptr); // Parse relations. LOG_INFO("Parsing relations..."); @@ -4963,7 +4994,7 @@ void PBFGraphParser::ParseRelations(const boost::property_tree::ptree& pt, LOG_INFO("Finished with " + std::to_string(osmdata.lane_connectivity_map.size()) + " lane connections"); - callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); // Sort complex restrictions. Keep this scoped so the file handles are closed when done sorting. LOG_INFO("Sorting complex restrictions by from id..."); @@ -4987,6 +5018,7 @@ void PBFGraphParser::ParseNodes(const boost::property_tree::ptree& pt, const std::vector& input_files, const std::string& way_nodes_file, const std::string& bss_nodes_file, + const std::string& linguistic_node_file, OSMData& osmdata) { // TODO: option 1: each one threads makes an osmdata and we splice them together at the end // option 2: synchronize around adding things to a single osmdata. will have to test to see @@ -5023,17 +5055,17 @@ void PBFGraphParser::ParseNodes(const boost::property_tree::ptree& pt, callback.last_relation_ = 0; // we send a null way_nodes file so that only the bike share stations are parsed callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, - new sequence(bss_nodes_file, create)); + new sequence(bss_nodes_file, create), nullptr); OSMPBF::Parser::parse(file_handle, static_cast(OSMPBF::Interest::NODES), callback); create = false; } // Since the sequence must be flushed before reading it... - callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); LOG_INFO("Found " + std::to_string(sequence{bss_nodes_file, false}.size()) + " bss nodes..."); } - callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); // we need to sort the refs so that we can easily (sequentially) update them // during node processing, we use memory mapping here because otherwise we aren't @@ -5053,7 +5085,7 @@ void PBFGraphParser::ParseNodes(const boost::property_tree::ptree& pt, // each time we parse nodes we have to run through the way nodes file from the beginning because // because osm node ids are only sorted at the single pbf file level callback.reset(nullptr, new sequence(way_nodes_file, false), nullptr, nullptr, - nullptr, nullptr); + nullptr, nullptr, new sequence(linguistic_node_file, true)); callback.current_way_node_index_ = callback.last_node_ = callback.last_way_ = callback.last_relation_ = 0; OSMPBF::Parser::parse(file_handle, @@ -5062,7 +5094,7 @@ void PBFGraphParser::ParseNodes(const boost::property_tree::ptree& pt, callback); } uint64_t max_osm_id = callback.last_node_; - callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + callback.reset(nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); LOG_INFO("Finished with " + std::to_string(osmdata.osm_node_count) + " nodes contained in routable ways"); @@ -5093,6 +5125,7 @@ void PBFGraphParser::ParseNodes(const boost::property_tree::ptree& pt, LOG_INFO("Number of nodes with refs (exits) = " + std::to_string(osmdata.node_ref_count)); LOG_INFO("Number of nodes with exit_to = " + std::to_string(osmdata.node_exit_to_count)); LOG_INFO("Number of nodes with names = " + std::to_string(osmdata.node_name_count)); + LOG_INFO("Number of nodes with linguistics = " + std::to_string(osmdata.node_linguistic_count)); LOG_INFO("Number of way refs = " + std::to_string(osmdata.way_ref.size())); LOG_INFO("Number of reverse way refs = " + std::to_string(osmdata.way_ref_rev.size())); LOG_INFO("Unique Node Strings (names, refs, etc.) = " + std::to_string(osmdata.node_names.Size())); diff --git a/src/mjolnir/util.cc b/src/mjolnir/util.cc index b7d476c8d5..308e143ec5 100644 --- a/src/mjolnir/util.cc +++ b/src/mjolnir/util.cc @@ -52,6 +52,7 @@ const std::string tile_manifest_file = "tile_manifest.json"; const std::string access_file = "access.bin"; const std::string pronunciation_file = "pronunciation.bin"; const std::string bss_nodes_file = "bss_nodes.bin"; +const std::string linguistic_node_file = "linguistics_node.bin"; const std::string cr_from_file = "complex_from_restrictions.bin"; const std::string cr_to_file = "complex_to_restrictions.bin"; const std::string new_to_old_file = "new_nodes_to_old_nodes.bin"; @@ -271,6 +272,7 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, std::string tile_manifest = tile_dir + tile_manifest_file; std::string access_bin = tile_dir + access_file; std::string bss_nodes_bin = tile_dir + bss_nodes_file; + std::string linguistic_node_bin = tile_dir + linguistic_node_file; std::string cr_from_bin = tile_dir + cr_from_file; std::string cr_to_bin = tile_dir + cr_to_file; std::string new_to_old_bin = tile_dir + new_to_old_file; @@ -320,7 +322,7 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, // Read the OSM protocol buffer file. Callbacks for nodes // are defined within the PBFParser class PBFGraphParser::ParseNodes(config.get_child("mjolnir"), input_files, way_nodes_bin, bss_nodes_bin, - osm_data); + linguistic_node_bin, osm_data); // Free all protobuf memory - cannot use the protobuffer lib after this! if (release_osmpbf_memory) { @@ -364,7 +366,7 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, // Build the graph using the OSMNodes and OSMWays from the parser GraphBuilder::Build(config, osm_data, ways_bin, way_nodes_bin, nodes_bin, edges_bin, cr_from_bin, - cr_to_bin, tiles); + cr_to_bin, linguistic_node_bin, tiles); } // Enhance the local level of the graph. This adds information to the local @@ -447,6 +449,7 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, remove_temp_file(bss_nodes_bin); remove_temp_file(cr_from_bin); remove_temp_file(cr_to_bin); + remove_temp_file(linguistic_node_bin); remove_temp_file(new_to_old_bin); remove_temp_file(old_to_new_bin); remove_temp_file(tile_manifest); diff --git a/test/countryaccess.cc b/test/countryaccess.cc index d33fc0c39b..25cc90ee99 100644 --- a/test/countryaccess.cc +++ b/test/countryaccess.cc @@ -95,6 +95,7 @@ void CountryAccess(const std::string& config_file) { std::string cr_from_file = "test_from_cr_amsterdam.bin"; std::string cr_to_file = "test_to_cr_amsterdam.bin"; std::string bss_nodes_file = "test_bss_nodes_amsterdam.bin"; + std::string linguistic_node_file = "test_linguistic_node_amsterdam.bin"; // Parse Amsterdam OSM data auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), @@ -107,7 +108,7 @@ void CountryAccess(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/amsterdam.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); std::map tiles = GraphBuilder::BuildEdges(conf.get_child("mjolnir"), ways_file, way_nodes_file, nodes_file, @@ -115,7 +116,7 @@ void CountryAccess(const std::string& config_file) { // Build the graph using the OSMNodes and OSMWays from the parser GraphBuilder::Build(conf, osmdata, ways_file, way_nodes_file, nodes_file, edges_file, cr_from_file, - cr_to_file, tiles); + cr_to_file, linguistic_node_file, tiles); // load a tile and test the default access. GraphId id(820099, 2, 0); @@ -253,6 +254,7 @@ void CountryAccess(const std::string& config_file) { remove_temp_file(access_file); remove_temp_file(cr_from_file); remove_temp_file(cr_to_file); + remove_temp_file(linguistic_node_file); } TEST(CountryAccess, Basic) { diff --git a/test/graphbuilder.cc b/test/graphbuilder.cc index 1ead308702..f7173d91d3 100644 --- a/test/graphbuilder.cc +++ b/test/graphbuilder.cc @@ -45,6 +45,7 @@ const std::string nodes_file = "test_nodes_harrisburg.bin"; const std::string to_restriction_file = "test_to_complex_restrictions_harrisburg.bin"; const std::string way_nodes_file = "test_way_nodes_harrisburg.bin"; const std::string ways_file = "test_ways_harrisburg.bin"; +const std::string linguistic_node_file = "test_linguistic_node_harrisburg.bin"; // Test output from construct edges and that the expected number of tiles are produced from the // build tiles step that follows. @@ -64,7 +65,7 @@ TEST(GraphBuilder, TestConstructEdges) { // This directory should be empty filesystem::remove_all(tile_dir); GraphBuilder::Build(config, osm_data, ways_file, way_nodes_file, nodes_file, edges_file, - from_restriction_file, to_restriction_file, tiles); + from_restriction_file, to_restriction_file, linguistic_node_file, tiles); GraphReader reader(config.get_child("mjolnir")); EXPECT_EQ(reader.GetTileSet(2).size(), 4); // Clear the tile directory so it doesn't interfere with the next test with graphreader. @@ -86,7 +87,7 @@ TEST(Graphbuilder, TestConstructEdgesSubset) { // This directory should be empty filesystem::remove_all(tile_dir); GraphBuilder::Build(config, osm_data, ways_file, way_nodes_file, nodes_file, edges_file, - from_restriction_file, to_restriction_file, tiles); + from_restriction_file, to_restriction_file, linguistic_node_file, tiles); GraphReader reader(config.get_child("mjolnir")); EXPECT_EQ(reader.GetTileSet(2).size(), 1); EXPECT_TRUE(reader.DoesTileExist(GraphId{5993698})); @@ -163,7 +164,8 @@ class HarrisburgTestSuiteEnv : public ::testing::Environment { way_nodes_file, access_file); PBFGraphParser::ParseRelations(mjolnir_config, input_files, from_restriction_file, to_restriction_file, osmdata); - PBFGraphParser::ParseNodes(mjolnir_config, input_files, way_nodes_file, bss_file, osmdata); + PBFGraphParser::ParseNodes(mjolnir_config, input_files, way_nodes_file, bss_file, + linguistic_node_file, osmdata); } void TearDown() override { @@ -173,6 +175,7 @@ class HarrisburgTestSuiteEnv : public ::testing::Environment { filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); filesystem::remove(bss_file); + filesystem::remove(linguistic_node_file); } }; diff --git a/test/graphparser.cc b/test/graphparser.cc index 82367998c2..6038aaec4e 100644 --- a/test/graphparser.cc +++ b/test/graphparser.cc @@ -58,6 +58,7 @@ void BollardsGatesAndAccess(const std::string& config_file) { std::string from_restriction_file = "test_from_complex_restrictions.bin"; std::string to_restriction_file = "test_to_complex_restrictions.bin"; std::string bss_nodes_file = "test_bss_nodes.bin"; + std::string linguistic_node_file = "test_linguistic_node.bin"; auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), @@ -70,7 +71,7 @@ void BollardsGatesAndAccess(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/liechtenstein-latest.osm.pbf"}, - way_nodes_file, bss_nodes_file, osmdata); + way_nodes_file, bss_nodes_file, linguistic_node_file, osmdata); sequence way_nodes(way_nodes_file, false); way_nodes.sort(node_predicate); @@ -211,6 +212,7 @@ void BollardsGatesAndAccess(const std::string& config_file) { filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); filesystem::remove(bss_nodes_file); + filesystem::remove(linguistic_node_file); } void RemovableBollards(const std::string& config_file) { @@ -223,6 +225,7 @@ void RemovableBollards(const std::string& config_file) { std::string from_restriction_file = "test_from_complex_restrictions.bin"; std::string to_restriction_file = "test_to_complex_restrictions.bin"; std::string bss_nodes_file = "test_bss_nodes.bin"; + std::string linguistic_node_file = "test_linguistic_node.bin"; auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, ways_file, @@ -234,7 +237,7 @@ void RemovableBollards(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); sequence way_nodes(way_nodes_file, false); way_nodes.sort(node_predicate); @@ -253,6 +256,7 @@ void RemovableBollards(const std::string& config_file) { filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); filesystem::remove(bss_nodes_file); + filesystem::remove(linguistic_node_file); } void Exits(const std::string& config_file) { @@ -265,6 +269,7 @@ void Exits(const std::string& config_file) { std::string from_restriction_file = "test_from_complex_restrictions.bin"; std::string to_restriction_file = "test_to_complex_restrictions.bin"; std::string bss_nodes_file = "test_bss_nodes.bin"; + std::string linguistic_node_file = "test_linguistic_node.bin"; auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/harrisburg.osm.pbf"}, @@ -276,7 +281,7 @@ void Exits(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/harrisburg.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); sequence way_nodes(way_nodes_file, false); way_nodes.sort(node_predicate); @@ -303,6 +308,7 @@ void Exits(const std::string& config_file) { filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); filesystem::remove(bss_nodes_file); + filesystem::remove(linguistic_node_file); } void Baltimore(const std::string& config_file) { @@ -315,6 +321,7 @@ void Baltimore(const std::string& config_file) { std::string from_restriction_file = "test_from_complex_restrictions.bin"; std::string to_restriction_file = "test_to_complex_restrictions.bin"; std::string bss_nodes_file = "test_bss_nodes.bin"; + std::string linguistic_node_file = "test_linguistic_node.bin"; auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/baltimore.osm.pbf"}, @@ -326,7 +333,7 @@ void Baltimore(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/baltimore.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); sequence ways(ways_file, false); ways.sort(way_predicate); @@ -429,6 +436,7 @@ void Baltimore(const std::string& config_file) { filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); filesystem::remove(bss_nodes_file); + filesystem::remove(linguistic_node_file); } void Bike(const std::string& config_file) { @@ -441,6 +449,7 @@ void Bike(const std::string& config_file) { std::string from_restriction_file = "test_from_complex_restrictions.bin"; std::string to_restriction_file = "test_to_complex_restrictions.bin"; std::string bss_nodes_file = "test_bss_nodes.bin"; + std::string linguistic_node_file = "test_linguistic_node.bin"; auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bike.osm.pbf"}, ways_file, @@ -452,7 +461,7 @@ void Bike(const std::string& config_file) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bike.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); sequence ways(ways_file, false); ways.sort(way_predicate); @@ -524,6 +533,7 @@ void Bike(const std::string& config_file) { filesystem::remove(access_file); filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); + filesystem::remove(linguistic_node_file); } void Bus(const std::string& config_file) { @@ -536,6 +546,7 @@ void Bus(const std::string& config_file) { std::string from_restriction_file = "test_from_complex_restrictions.bin"; std::string to_restriction_file = "test_to_complex_restrictions.bin"; std::string bss_nodes_file = "test_bss_nodes.bin"; + std::string linguistic_node_file = "test_linguistic_node.bin"; auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bus.osm.pbf"}, ways_file, @@ -546,7 +557,7 @@ void Bus(const std::string& config_file) { to_restriction_file, osmdata); PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bus.osm.pbf"}, - way_nodes_file, bss_nodes_file, osmdata); + way_nodes_file, bss_nodes_file, linguistic_node_file, osmdata); sequence ways(ways_file, false); ways.sort(way_predicate); @@ -605,6 +616,7 @@ void Bus(const std::string& config_file) { filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); filesystem::remove(bss_nodes_file); + filesystem::remove(linguistic_node_file); } void BicycleTrafficSignals(const std::string& config_file) { @@ -617,6 +629,7 @@ void BicycleTrafficSignals(const std::string& config_file) { std::string from_restriction_file = "test_from_complex_restrictions.bin"; std::string to_restriction_file = "test_to_complex_restrictions.bin"; std::string bss_nodes_file = "test_bss_nodes.bin"; + std::string linguistic_node_file = "test_linguistic_node.bin"; auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/nyc.osm.pbf"}, ways_file, @@ -627,7 +640,7 @@ void BicycleTrafficSignals(const std::string& config_file) { to_restriction_file, osmdata); PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/nyc.osm.pbf"}, - way_nodes_file, bss_nodes_file, osmdata); + way_nodes_file, bss_nodes_file, linguistic_node_file, osmdata); sequence way_nodes(way_nodes_file, false); way_nodes.sort(node_predicate); @@ -654,6 +667,7 @@ void BicycleTrafficSignals(const std::string& config_file) { filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); filesystem::remove(bss_nodes_file); + filesystem::remove(linguistic_node_file); } void DoConfig() { @@ -720,6 +734,7 @@ TEST(GraphParser, TestImportBssNode) { std::string from_restriction_file = "test_from_complex_restrictions.bin"; std::string to_restriction_file = "test_to_complex_restrictions.bin"; std::string bss_nodes_file = "test_bss_nodes.bin"; + std::string linguistic_node_file = "test_linguistic_node.bin"; auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, ways_file, @@ -731,7 +746,7 @@ TEST(GraphParser, TestImportBssNode) { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, way_nodes_file, - bss_nodes_file, osmdata); + bss_nodes_file, linguistic_node_file, osmdata); GraphReader reader(conf.get_child("mjolnir")); @@ -740,7 +755,7 @@ TEST(GraphParser, TestImportBssNode) { edges_file); GraphBuilder::Build(conf, osmdata, ways_file, way_nodes_file, nodes_file, edges_file, - from_restriction_file, to_restriction_file, tiles); + from_restriction_file, to_restriction_file, linguistic_node_file, tiles); BssBuilder::Build(conf, osmdata, bss_nodes_file); @@ -818,6 +833,7 @@ TEST(GraphParser, TestImportBssNode) { filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); filesystem::remove(bss_nodes_file); + filesystem::remove(linguistic_node_file); } } // namespace diff --git a/test/signinfo.cc b/test/signinfo.cc index 9a31991880..a64ba0f8b9 100644 --- a/test/signinfo.cc +++ b/test/signinfo.cc @@ -6,7 +6,6 @@ #include "mjolnir/graphbuilder.h" -using namespace std; using namespace valhalla::mjolnir; using namespace valhalla::baldr; using valhalla::mjolnir::GraphBuilder; @@ -31,10 +30,12 @@ TEST(Signinfo, ExitToTest) { std::vector signs; std::vector linguistics; std::vector> default_languages; + const std::string linguistic_node_file = "test_linguistic_node.bin"; + sequence linguistic_node(linguistic_node_file, true); bool has_guide = GraphBuilder::CreateSignInfoList(exit_node, way, pronunciationMap, langMap, - osmdata, default_languages, signs, linguistics, - fork, forward, true, false); + osmdata, default_languages, linguistic_node, + signs, linguistics, fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -50,8 +51,8 @@ TEST(Signinfo, ExitToTest) { node.set_exit_to_index(osmdata.node_names.index("US 11;To I 81;Carlisle;Harrisburg")); signs.clear(); has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - true, false); + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -72,8 +73,8 @@ TEST(Signinfo, ExitToTest) { node.set_exit_to_index(osmdata.node_names.index("US 11;Toward I 81;Carlisle;Harrisburg")); has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - true, false); + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -94,8 +95,8 @@ TEST(Signinfo, ExitToTest) { node.set_exit_to_index(osmdata.node_names.index("I 95 To I 695")); has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - true, false); + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -112,8 +113,8 @@ TEST(Signinfo, ExitToTest) { node.set_exit_to_index(osmdata.node_names.index("I 495 Toward I 270")); has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - true, false); + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -131,8 +132,8 @@ TEST(Signinfo, ExitToTest) { osmdata.node_names.index("I 495 Toward I 270 To I 95")); // default to toward. Punt on parsing. has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - true, false); + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -150,8 +151,8 @@ TEST(Signinfo, ExitToTest) { auto index = osmdata.name_offset_map.index("I 495 North"); way.set_destination_ref_index(index); has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - true, false); + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -168,8 +169,8 @@ TEST(Signinfo, ExitToTest) { index = osmdata.name_offset_map.index("I 495 North"); way.set_destination_ref_index(index); has_guide = GraphBuilder::CreateSignInfoList(node, way, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - false, true); + default_languages, linguistic_node, signs, linguistics, + fork, forward, false, true); EXPECT_TRUE(has_guide) << "Guides should not be Exits"; @@ -188,8 +189,8 @@ TEST(Signinfo, ExitToTest) { auto index2 = osmdata.name_offset_map.index("I 695 North"); way2.set_destination_ref_to_index(index2); has_guide = GraphBuilder::CreateSignInfoList(node, way2, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - true, false); + default_languages, linguistic_node, signs, linguistics, + fork, forward, true, false); EXPECT_FALSE(has_guide) << "Exits should not be Guides"; @@ -208,8 +209,8 @@ TEST(Signinfo, ExitToTest) { auto index3 = osmdata.name_offset_map.index("I 695 North"); way3.set_destination_ref_to_index(index3); has_guide = GraphBuilder::CreateSignInfoList(node, way2, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - false, true); + default_languages, linguistic_node, signs, linguistics, + fork, forward, false, true); EXPECT_TRUE(has_guide) << "Guides should not be Exits"; @@ -226,8 +227,8 @@ TEST(Signinfo, ExitToTest) { // exit_node signs.clear(); has_guide = GraphBuilder::CreateSignInfoList(exit_node, way2, pronunciationMap, langMap, osmdata, - default_languages, signs, linguistics, fork, forward, - false, true); + default_languages, linguistic_node, signs, linguistics, + fork, forward, false, true); EXPECT_TRUE(has_guide) << "Guides should not be Exits"; @@ -240,6 +241,8 @@ TEST(Signinfo, ExitToTest) { } else { FAIL() << "destination ref I 695 North failed to create exist sign. No exit 5 should exist."; } + + filesystem::remove(linguistic_node_file); } } // namespace diff --git a/test/utrecht.cc b/test/utrecht.cc index f22106d083..ede7264632 100644 --- a/test/utrecht.cc +++ b/test/utrecht.cc @@ -28,6 +28,7 @@ std::string access_file = "test_access_utrecht.bin"; std::string from_restriction_file = "test_from_complex_restrictions_utrecht.bin"; std::string to_restriction_file = "test_to_complex_restrictions_utrecht.bin"; std::string bss_file = "test_bss_nodes_utrecht.bin"; +std::string linguistic_node_file = "test_linguistic_node_utrecht.bin"; const auto node_predicate = [](const OSMWayNode& a, const OSMWayNode& b) { return a.node.osmid_ < b.node.osmid_; @@ -228,7 +229,7 @@ class UtrecthTestSuiteEnv : public ::testing::Environment { PBFGraphParser::ParseNodes(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/utrecht_netherlands.osm.pbf"}, - way_nodes_file, bss_file, osmdata); + way_nodes_file, bss_file, linguistic_node_file, osmdata); } void TearDown() override { @@ -238,6 +239,7 @@ class UtrecthTestSuiteEnv : public ::testing::Environment { filesystem::remove(from_restriction_file); filesystem::remove(to_restriction_file); filesystem::remove(bss_file); + filesystem::remove(linguistic_node_file); } }; diff --git a/valhalla/mjolnir/graphbuilder.h b/valhalla/mjolnir/graphbuilder.h index a1dd9d6319..44860001fe 100644 --- a/valhalla/mjolnir/graphbuilder.h +++ b/valhalla/mjolnir/graphbuilder.h @@ -7,9 +7,11 @@ #include #include +#include #include #include +#include #include namespace valhalla { @@ -35,6 +37,9 @@ class GraphBuilder { * not in memory * @param complex_to_restriction_file where to store the to complex restrictions so they are not * in memory + * @param linguistic_node_file where to store the to linguistic info so they are not in + * memory + * */ static void Build(const boost::property_tree::ptree& pt, const OSMData& osmdata, @@ -44,6 +49,7 @@ class GraphBuilder { const std::string& edges_file, const std::string& complex_from_restriction_file, const std::string& complex_to_restriction_file, + const std::string& linguistic_node_file, const std::map& tiles); static std::map BuildEdges(const ptree& conf, @@ -125,6 +131,7 @@ class GraphBuilder { const std::map, uint32_t>& langMap, const OSMData& osmdata, const std::vector>& default_languages, + midgard::sequence& linguistic_node, std::vector& exits, std::vector& linguistics, bool fork, diff --git a/valhalla/mjolnir/osmdata.h b/valhalla/mjolnir/osmdata.h index 4bf436844d..c6f1829e26 100644 --- a/valhalla/mjolnir/osmdata.h +++ b/valhalla/mjolnir/osmdata.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -90,15 +91,16 @@ struct OSMData { */ static void cleanup_temp_files(const std::string& tile_dir); - uint64_t max_changeset_id_; // The largest/newest changeset id encountered when parsing OSM data - uint64_t osm_node_count; // Count of osm nodes - uint64_t osm_way_count; // Count of osm ways - uint64_t osm_way_node_count; // Count of osm nodes on osm ways - uint64_t node_count; // Count of all nodes in the graph - uint64_t edge_count; // Estimated count of edges in the graph - uint64_t node_ref_count; // Number of node with ref - uint64_t node_name_count; // Number of nodes with names - uint64_t node_exit_to_count; // Number of nodes with exit_to + uint64_t max_changeset_id_; // The largest/newest changeset id encountered when parsing OSM data + uint64_t osm_node_count; // Count of osm nodes + uint64_t osm_way_count; // Count of osm ways + uint64_t osm_way_node_count; // Count of osm nodes on osm ways + uint64_t node_count; // Count of all nodes in the graph + uint64_t edge_count; // Estimated count of edges in the graph + uint64_t node_ref_count; // Number of node with ref + uint64_t node_name_count; // Number of nodes with names + uint64_t node_exit_to_count; // Number of nodes with exit_to + uint64_t node_linguistic_count; // Number of nodes with linguistic info // Stores simple restrictions. Indexed by the from way Id RestrictionsMultiMap restrictions; diff --git a/valhalla/mjolnir/osmnode.h b/valhalla/mjolnir/osmnode.h index 0feebc6627..66fa073917 100644 --- a/valhalla/mjolnir/osmnode.h +++ b/valhalla/mjolnir/osmnode.h @@ -57,29 +57,12 @@ struct OSMNode { uint32_t cash_only_toll_ : 1; uint32_t spare1_ : 5; - // pronunciations / langs - uint32_t name_lang_index_; - uint32_t name_pronunciation_ipa_index_; - uint32_t name_pronunciation_ipa_lang_index_; - uint32_t name_pronunciation_nt_sampa_index_; - uint32_t name_pronunciation_nt_sampa_lang_index_; - uint32_t name_pronunciation_katakana_index_; - uint32_t name_pronunciation_katakana_lang_index_; - uint32_t name_pronunciation_jeita_index_; - uint32_t name_pronunciation_jeita_lang_index_; - uint32_t ref_lang_index_; - uint32_t ref_pronunciation_ipa_index_; - uint32_t ref_pronunciation_ipa_lang_index_; - uint32_t ref_pronunciation_nt_sampa_index_; - uint32_t ref_pronunciation_nt_sampa_lang_index_; - uint32_t ref_pronunciation_katakana_index_; - uint32_t ref_pronunciation_katakana_lang_index_; - uint32_t ref_pronunciation_jeita_index_; - uint32_t ref_pronunciation_jeita_lang_index_; - // bss information uint32_t bss_info_; + // linguistic information + uint32_t linguistic_info_index_; + // Lat,lng of the node at fixed 7digit precision uint32_t lng7_; uint32_t lat7_; @@ -153,22 +136,6 @@ struct OSMNode { return name_index_; } - /** - * Sets the index for name: - * @param idx Index for the languages. - */ - void set_name_lang_index(const uint32_t idx) { - name_lang_index_ = idx; - } - - /** - * Get the name: index. - * @return Returns the index for the languages. - */ - uint32_t name_lang_index() const { - return name_lang_index_; - } - /** * Does the node have a name. Check if name_index is non-zero */ @@ -195,22 +162,6 @@ struct OSMNode { return ref_index_; } - /** - * Sets the index for ref: - * @param idx Index for the languages. - */ - void set_ref_lang_index(const uint32_t idx) { - ref_lang_index_ = idx; - } - - /** - * Get the ref: index. - * @return Returns the index for the languages. - */ - uint32_t ref_lang_index() const { - return ref_lang_index_; - } - /** * Does the node have ref information. Checks if exit_ref_index is non-zero */ @@ -478,262 +429,6 @@ struct OSMNode { return urban_; } - /** - * Sets the index for the ref ipa pronunciation - * @param idx Index for the reference ipa pronunciation. - */ - void set_ref_pronunciation_ipa_index(const uint32_t idx) { - ref_pronunciation_ipa_index_ = idx; - } - - /** - * Get the ref ipa pronunciation index. - * @return Returns the index for the ref ipa pronunciation. - */ - uint32_t ref_pronunciation_ipa_index() const { - return ref_pronunciation_ipa_index_; - } - - /** - * Sets the index for the ref ipa lang pronunciation - * @param idx Index for the reference ipa lang pronunciation. - */ - void set_ref_pronunciation_ipa_lang_index(const uint32_t idx) { - ref_pronunciation_ipa_lang_index_ = idx; - } - - /** - * Get the ref ipa pronunciation lang index. - * @return Returns the index for the ref ipa lang pronunciation. - */ - uint32_t ref_pronunciation_ipa_lang_index() const { - return ref_pronunciation_ipa_lang_index_; - } - - /** - * Sets the index for the ref nt-sampa pronunciation - * @param idx Index for the reference nt-sampa pronunciation. - */ - void set_ref_pronunciation_nt_sampa_index(const uint32_t idx) { - ref_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the ref nt-sampa pronunciation index. - * @return Returns the index for the ref nt-sampa pronunciation. - */ - uint32_t ref_pronunciation_nt_sampa_index() const { - return ref_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for the ref nt-sampa lang pronunciation - * @param idx Index for the reference nt-sampa lang pronunciation. - */ - void set_ref_pronunciation_nt_sampa_lang_index(const uint32_t idx) { - ref_pronunciation_nt_sampa_lang_index_ = idx; - } - - /** - * Get the ref nt-sampa pronunciation lang index. - * @return Returns the index for the ref nt-sampa lang pronunciation. - */ - uint32_t ref_pronunciation_nt_sampa_lang_index() const { - return ref_pronunciation_nt_sampa_lang_index_; - } - - /** - * Sets the index for the ref katakana pronunciation - * @param idx Index for the reference katakana pronunciation. - */ - void set_ref_pronunciation_katakana_index(const uint32_t idx) { - ref_pronunciation_katakana_index_ = idx; - } - - /** - * Get the ref katakana pronunciation index. - * @return Returns the index for the ref katakana pronunciation. - */ - uint32_t ref_pronunciation_katakana_index() const { - return ref_pronunciation_katakana_index_; - } - - /** - * Sets the index for the ref katakana lang pronunciation - * @param idx Index for the reference katakana lang pronunciation. - */ - void set_ref_pronunciation_katakana_lang_index(const uint32_t idx) { - ref_pronunciation_katakana_lang_index_ = idx; - } - - /** - * Get the ref katakana pronunciation lang index. - * @return Returns the index for the ref katakana lang pronunciation. - */ - uint32_t ref_pronunciation_katakana_lang_index() const { - return ref_pronunciation_katakana_lang_index_; - } - - /** - * Sets the index for the ref jeita pronunciation - * @param idx Index for the reference jeita pronunciation. - */ - void set_ref_pronunciation_jeita_index(const uint32_t idx) { - ref_pronunciation_jeita_index_ = idx; - } - - /** - * Get the ref jeita pronunciation index. - * @return Returns the index for the ref jeita pronunciation. - */ - uint32_t ref_pronunciation_jeita_index() const { - return ref_pronunciation_jeita_index_; - } - - /** - * Sets the index for the ref jeita lang pronunciation - * @param idx Index for the reference jeita lang pronunciation. - */ - void set_ref_pronunciation_jeita_lang_index(const uint32_t idx) { - ref_pronunciation_jeita_lang_index_ = idx; - } - - /** - * Get the ref jeita pronunciation lang index. - * @return Returns the index for the ref jeita lang pronunciation. - */ - uint32_t ref_pronunciation_jeita_lang_index() const { - return ref_pronunciation_jeita_lang_index_; - } - - /** - * Sets the index for the name ipa pronunciation - * @param idx Index for the nameerence ipa pronunciation. - */ - void set_name_pronunciation_ipa_index(const uint32_t idx) { - name_pronunciation_ipa_index_ = idx; - } - - /** - * Get the name ipa pronunciation index. - * @return Returns the index for the name ipa pronunciation. - */ - uint32_t name_pronunciation_ipa_index() const { - return name_pronunciation_ipa_index_; - } - - /** - * Sets the index for the name ipa lang pronunciation - * @param idx Index for the nameerence ipa lang pronunciation. - */ - void set_name_pronunciation_ipa_lang_index(const uint32_t idx) { - name_pronunciation_ipa_lang_index_ = idx; - } - - /** - * Get the name ipa pronunciation lang index. - * @return Returns the index for the name ipa lang pronunciation. - */ - uint32_t name_pronunciation_ipa_lang_index() const { - return name_pronunciation_ipa_lang_index_; - } - - /** - * Sets the index for the name nt-sampa pronunciation - * @param idx Index for the nameerence nt-sampa pronunciation. - */ - void set_name_pronunciation_nt_sampa_index(const uint32_t idx) { - name_pronunciation_nt_sampa_index_ = idx; - } - - /** - * Get the name nt-sampa pronunciation index. - * @return Returns the index for the name nt-sampa pronunciation. - */ - uint32_t name_pronunciation_nt_sampa_index() const { - return name_pronunciation_nt_sampa_index_; - } - - /** - * Sets the index for the name nt-sampa lang pronunciation - * @param idx Index for the nameerence nt-sampa lang pronunciation. - */ - void set_name_pronunciation_nt_sampa_lang_index(const uint32_t idx) { - name_pronunciation_nt_sampa_lang_index_ = idx; - } - - /** - * Get the name nt-sampa pronunciation lang index. - * @return Returns the index for the name nt-sampa lang pronunciation. - */ - uint32_t name_pronunciation_nt_sampa_lang_index() const { - return name_pronunciation_nt_sampa_lang_index_; - } - - /** - * Sets the index for the name katakana pronunciation - * @param idx Index for the nameerence katakana pronunciation. - */ - void set_name_pronunciation_katakana_index(const uint32_t idx) { - name_pronunciation_katakana_index_ = idx; - } - - /** - * Get the name katakana pronunciation index. - * @return Returns the index for the name katakana pronunciation. - */ - uint32_t name_pronunciation_katakana_index() const { - return name_pronunciation_katakana_index_; - } - - /** - * Sets the index for the name katakana lang pronunciation - * @param idx Index for the nameerence katakana lang pronunciation. - */ - void set_name_pronunciation_katakana_lang_index(const uint32_t idx) { - name_pronunciation_katakana_lang_index_ = idx; - } - - /** - * Get the name katakana pronunciation lang index. - * @return Returns the index for the name katakana lang pronunciation. - */ - uint32_t name_pronunciation_katakana_lang_index() const { - return name_pronunciation_katakana_lang_index_; - } - - /** - * Sets the index for the name jeita pronunciation - * @param idx Index for the nameerence jeita pronunciation. - */ - void set_name_pronunciation_jeita_index(const uint32_t idx) { - name_pronunciation_jeita_index_ = idx; - } - - /** - * Get the name jeita pronunciation index. - * @return Returns the index for the name jeita pronunciation. - */ - uint32_t name_pronunciation_jeita_index() const { - return name_pronunciation_jeita_index_; - } - - /** - * Sets the index for the name jeita lang pronunciation - * @param idx Index for the nameerence jeita lang pronunciation. - */ - void set_name_pronunciation_jeita_lang_index(const uint32_t idx) { - name_pronunciation_jeita_lang_index_ = idx; - } - - /** - * Get the name jeita pronunciation lang index. - * @return Returns the index for the name jeita lang pronunciation. - */ - uint32_t name_pronunciation_jeita_lang_index() const { - return name_pronunciation_jeita_lang_index_; - } - /** * Set the country iso code index * @param country iso code Index into the 2 char Country ISO Code. @@ -854,6 +549,22 @@ struct OSMNode { uint32_t bss_info_index() const { return bss_info_; } + + /** + * Sets the index for the linguistic info + * @param idx Index for the linguistic info. + */ + void set_linguistic_info_index(const uint32_t idx) { + linguistic_info_index_ = idx; + } + + /** + * Get the linguistic info index. + * @return Returns the index for the linguistic info. + */ + uint32_t linguistic_info_index() const { + return linguistic_info_index_; + } }; } // namespace mjolnir diff --git a/valhalla/mjolnir/osmnodelinguistic.h b/valhalla/mjolnir/osmnodelinguistic.h new file mode 100644 index 0000000000..4c85a543e7 --- /dev/null +++ b/valhalla/mjolnir/osmnodelinguistic.h @@ -0,0 +1,345 @@ +#ifndef VALHALLA_MJOLNIR_OSMNODELINGUISTIC_H_ +#define VALHALLA_MJOLNIR_OSMNODELINGUISTIC_H_ + +#include + +namespace valhalla { +namespace mjolnir { + +/** + * OSM node linguistic information. + */ +struct OSMNodeLinguistic { + + OSMNodeLinguistic() { + memset(this, 0, sizeof(OSMNodeLinguistic)); + } + + /** + * Sets the index for name: + * @param idx Index for the languages. + */ + void set_name_lang_index(const uint32_t idx) { + name_lang_index_ = idx; + } + + /** + * Get the name: index. + * @return Returns the index for the languages. + */ + uint32_t name_lang_index() const { + return name_lang_index_; + } + + /** + * Sets the index for the name ipa pronunciation + * @param idx Index for the nameerence ipa pronunciation. + */ + void set_name_pronunciation_ipa_index(const uint32_t idx) { + name_pronunciation_ipa_index_ = idx; + } + + /** + * Get the name ipa pronunciation index. + * @return Returns the index for the name ipa pronunciation. + */ + uint32_t name_pronunciation_ipa_index() const { + return name_pronunciation_ipa_index_; + } + + /** + * Sets the index for the name ipa lang pronunciation + * @param idx Index for the nameerence ipa lang pronunciation. + */ + void set_name_pronunciation_ipa_lang_index(const uint32_t idx) { + name_pronunciation_ipa_lang_index_ = idx; + } + + /** + * Get the name ipa pronunciation lang index. + * @return Returns the index for the name ipa lang pronunciation. + */ + uint32_t name_pronunciation_ipa_lang_index() const { + return name_pronunciation_ipa_lang_index_; + } + + /** + * Sets the index for the name nt-sampa pronunciation + * @param idx Index for the nameerence nt-sampa pronunciation. + */ + void set_name_pronunciation_nt_sampa_index(const uint32_t idx) { + name_pronunciation_nt_sampa_index_ = idx; + } + + /** + * Get the name nt-sampa pronunciation index. + * @return Returns the index for the name nt-sampa pronunciation. + */ + uint32_t name_pronunciation_nt_sampa_index() const { + return name_pronunciation_nt_sampa_index_; + } + + /** + * Sets the index for the name nt-sampa lang pronunciation + * @param idx Index for the nameerence nt-sampa lang pronunciation. + */ + void set_name_pronunciation_nt_sampa_lang_index(const uint32_t idx) { + name_pronunciation_nt_sampa_lang_index_ = idx; + } + + /** + * Get the name nt-sampa pronunciation lang index. + * @return Returns the index for the name nt-sampa lang pronunciation. + */ + uint32_t name_pronunciation_nt_sampa_lang_index() const { + return name_pronunciation_nt_sampa_lang_index_; + } + + /** + * Sets the index for the name katakana pronunciation + * @param idx Index for the nameerence katakana pronunciation. + */ + void set_name_pronunciation_katakana_index(const uint32_t idx) { + name_pronunciation_katakana_index_ = idx; + } + + /** + * Get the name katakana pronunciation index. + * @return Returns the index for the name katakana pronunciation. + */ + uint32_t name_pronunciation_katakana_index() const { + return name_pronunciation_katakana_index_; + } + + /** + * Sets the index for the name katakana lang pronunciation + * @param idx Index for the nameerence katakana lang pronunciation. + */ + void set_name_pronunciation_katakana_lang_index(const uint32_t idx) { + name_pronunciation_katakana_lang_index_ = idx; + } + + /** + * Get the name katakana pronunciation lang index. + * @return Returns the index for the name katakana lang pronunciation. + */ + uint32_t name_pronunciation_katakana_lang_index() const { + return name_pronunciation_katakana_lang_index_; + } + + /** + * Sets the index for the name jeita pronunciation + * @param idx Index for the nameerence jeita pronunciation. + */ + void set_name_pronunciation_jeita_index(const uint32_t idx) { + name_pronunciation_jeita_index_ = idx; + } + + /** + * Get the name jeita pronunciation index. + * @return Returns the index for the name jeita pronunciation. + */ + uint32_t name_pronunciation_jeita_index() const { + return name_pronunciation_jeita_index_; + } + + /** + * Sets the index for the name jeita lang pronunciation + * @param idx Index for the nameerence jeita lang pronunciation. + */ + void set_name_pronunciation_jeita_lang_index(const uint32_t idx) { + name_pronunciation_jeita_lang_index_ = idx; + } + + /** + * Get the name jeita pronunciation lang index. + * @return Returns the index for the name jeita lang pronunciation. + */ + uint32_t name_pronunciation_jeita_lang_index() const { + return name_pronunciation_jeita_lang_index_; + } + + /** + * Sets the index for ref: + * @param idx Index for the languages. + */ + void set_ref_lang_index(const uint32_t idx) { + ref_lang_index_ = idx; + } + + /** + * Get the ref: index. + * @return Returns the index for the languages. + */ + uint32_t ref_lang_index() const { + return ref_lang_index_; + } + + /** + * Sets the index for the ref ipa pronunciation + * @param idx Index for the reference ipa pronunciation. + */ + void set_ref_pronunciation_ipa_index(const uint32_t idx) { + ref_pronunciation_ipa_index_ = idx; + } + + /** + * Get the ref ipa pronunciation index. + * @return Returns the index for the ref ipa pronunciation. + */ + uint32_t ref_pronunciation_ipa_index() const { + return ref_pronunciation_ipa_index_; + } + + /** + * Sets the index for the ref ipa lang pronunciation + * @param idx Index for the reference ipa lang pronunciation. + */ + void set_ref_pronunciation_ipa_lang_index(const uint32_t idx) { + ref_pronunciation_ipa_lang_index_ = idx; + } + + /** + * Get the ref ipa pronunciation lang index. + * @return Returns the index for the ref ipa lang pronunciation. + */ + uint32_t ref_pronunciation_ipa_lang_index() const { + return ref_pronunciation_ipa_lang_index_; + } + + /** + * Sets the index for the ref nt-sampa pronunciation + * @param idx Index for the reference nt-sampa pronunciation. + */ + void set_ref_pronunciation_nt_sampa_index(const uint32_t idx) { + ref_pronunciation_nt_sampa_index_ = idx; + } + + /** + * Get the ref nt-sampa pronunciation index. + * @return Returns the index for the ref nt-sampa pronunciation. + */ + uint32_t ref_pronunciation_nt_sampa_index() const { + return ref_pronunciation_nt_sampa_index_; + } + + /** + * Sets the index for the ref nt-sampa lang pronunciation + * @param idx Index for the reference nt-sampa lang pronunciation. + */ + void set_ref_pronunciation_nt_sampa_lang_index(const uint32_t idx) { + ref_pronunciation_nt_sampa_lang_index_ = idx; + } + + /** + * Get the ref nt-sampa pronunciation lang index. + * @return Returns the index for the ref nt-sampa lang pronunciation. + */ + uint32_t ref_pronunciation_nt_sampa_lang_index() const { + return ref_pronunciation_nt_sampa_lang_index_; + } + + /** + * Sets the index for the ref katakana pronunciation + * @param idx Index for the reference katakana pronunciation. + */ + void set_ref_pronunciation_katakana_index(const uint32_t idx) { + ref_pronunciation_katakana_index_ = idx; + } + + /** + * Get the ref katakana pronunciation index. + * @return Returns the index for the ref katakana pronunciation. + */ + uint32_t ref_pronunciation_katakana_index() const { + return ref_pronunciation_katakana_index_; + } + + /** + * Sets the index for the ref katakana lang pronunciation + * @param idx Index for the reference katakana lang pronunciation. + */ + void set_ref_pronunciation_katakana_lang_index(const uint32_t idx) { + ref_pronunciation_katakana_lang_index_ = idx; + } + + /** + * Get the ref katakana pronunciation lang index. + * @return Returns the index for the ref katakana lang pronunciation. + */ + uint32_t ref_pronunciation_katakana_lang_index() const { + return ref_pronunciation_katakana_lang_index_; + } + + /** + * Sets the index for the ref jeita pronunciation + * @param idx Index for the reference jeita pronunciation. + */ + void set_ref_pronunciation_jeita_index(const uint32_t idx) { + ref_pronunciation_jeita_index_ = idx; + } + + /** + * Get the ref jeita pronunciation index. + * @return Returns the index for the ref jeita pronunciation. + */ + uint32_t ref_pronunciation_jeita_index() const { + return ref_pronunciation_jeita_index_; + } + + /** + * Sets the index for the ref jeita lang pronunciation + * @param idx Index for the reference jeita lang pronunciation. + */ + void set_ref_pronunciation_jeita_lang_index(const uint32_t idx) { + ref_pronunciation_jeita_lang_index_ = idx; + } + + /** + * Get the ref jeita pronunciation lang index. + * @return Returns the index for the ref jeita lang pronunciation. + */ + uint32_t ref_pronunciation_jeita_lang_index() const { + return ref_pronunciation_jeita_lang_index_; + } + + /** + * Has any index been set? + * @return True is nothing is set + */ + bool isEmpty() const { + return (name_lang_index_ == 0 && name_pronunciation_ipa_index_ == 0 && + name_pronunciation_ipa_lang_index_ == 0 && name_pronunciation_nt_sampa_index_ == 0 && + name_pronunciation_nt_sampa_lang_index_ == 0 && name_pronunciation_katakana_index_ == 0 && + name_pronunciation_katakana_lang_index_ == 0 && name_pronunciation_jeita_index_ == 0 && + name_pronunciation_jeita_lang_index_ == 0 && ref_lang_index_ == 0 && + ref_pronunciation_ipa_index_ == 0 && ref_pronunciation_ipa_lang_index_ == 0 && + ref_pronunciation_nt_sampa_index_ == 0 && ref_pronunciation_nt_sampa_lang_index_ == 0 && + ref_pronunciation_katakana_index_ == 0 && ref_pronunciation_katakana_lang_index_ == 0 && + ref_pronunciation_jeita_index_ == 0 && ref_pronunciation_jeita_lang_index_ == 0); + } + + // pronunciations / langs + uint32_t name_lang_index_; + uint32_t name_pronunciation_ipa_index_; + uint32_t name_pronunciation_ipa_lang_index_; + uint32_t name_pronunciation_nt_sampa_index_; + uint32_t name_pronunciation_nt_sampa_lang_index_; + uint32_t name_pronunciation_katakana_index_; + uint32_t name_pronunciation_katakana_lang_index_; + uint32_t name_pronunciation_jeita_index_; + uint32_t name_pronunciation_jeita_lang_index_; + uint32_t ref_lang_index_; + uint32_t ref_pronunciation_ipa_index_; + uint32_t ref_pronunciation_ipa_lang_index_; + uint32_t ref_pronunciation_nt_sampa_index_; + uint32_t ref_pronunciation_nt_sampa_lang_index_; + uint32_t ref_pronunciation_katakana_index_; + uint32_t ref_pronunciation_katakana_lang_index_; + uint32_t ref_pronunciation_jeita_index_; + uint32_t ref_pronunciation_jeita_lang_index_; +}; +} // namespace mjolnir +} // namespace valhalla + +#endif // VALHALLA_MJOLNIR_OSMLINGUISTIC_H diff --git a/valhalla/mjolnir/pbfgraphparser.h b/valhalla/mjolnir/pbfgraphparser.h index 3ad43cb11e..a3530b5d11 100644 --- a/valhalla/mjolnir/pbfgraphparser.h +++ b/valhalla/mjolnir/pbfgraphparser.h @@ -55,12 +55,16 @@ class PBFGraphParser { * @param input_files the protobuf files to parse * @param way_nodes_file where to store the nodes so they are not in memory * @param bss_nodes_file where to store the bss nodes so they are not in memory + * @param linguistic_node_file where to store the linguistic information for nodes so + * they are not in memory + * * @param osmdata OSM data */ static void ParseNodes(const boost::property_tree::ptree& pt, const std::vector& input_files, const std::string& way_nodes_file, const std::string& bss_nodes_file, + const std::string& linguistic_node_file, OSMData& osmdata); }; From 3794239655f1dde6426421d63d973271dcd53b45 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Mon, 4 Mar 2024 14:37:29 +0100 Subject: [PATCH 042/198] GeoTIFF serialization for isochrones (#4594) --- .circleci/config.yml | 6 +- .github/workflows/mingw-build.yml | 2 + .github/workflows/osx.yml | 4 +- .github/workflows/win.yml | 3 +- CHANGELOG.md | 1 + CMakeLists.txt | 27 +++- cmake/ValhallaPkgConfig.cmake | 3 + docs/docs/api/isochrone/api-reference.md | 8 +- docs/docs/api/turn-by-turn/api-reference.md | 1 + docs/docs/building.md | 3 +- docs/docs/thor/isochrones.md | 2 +- proto/options.proto | 1 + scripts/install-linux-deps.sh | 1 + src/proto_conversions.cc | 12 +- src/thor/CMakeLists.txt | 2 +- src/thor/isochrone_action.cc | 11 +- src/tyr/CMakeLists.txt | 6 +- src/tyr/isochrone_serializer.cc | 116 ++++++++++++- src/valhalla_run_isochrone.cc | 30 ++-- src/valhalla_service.cc | 8 + src/worker.cc | 7 + test/gurka/test_avoids.cc | 1 + test/gurka/test_barrier_uturns.cc | 1 + test/gurka/test_bidir_search.cc | 1 + test/isochrone.cc | 171 ++++++++++++++++++++ valhalla/midgard/gridded_data.h | 36 ++++- valhalla/tyr/serializers.h | 5 +- vcpkg.json | 1 + 28 files changed, 411 insertions(+), 59 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8dd5cde962..68f9e8b288 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,7 +35,7 @@ jobs: cd build cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=On -DCPACK_GENERATOR=DEB \ -DENABLE_COMPILER_WARNINGS=On -DENABLE_WERROR=Off -DCMAKE_EXPORT_COMPILE_COMMANDS=On \ - -DCMAKE_CXX_FLAGS="-fuse-ld=lld" -DLOGGING_LEVEL=INFO -DENABLE_PYTHON_BINDINGS=On + -DCMAKE_CXX_FLAGS="-fuse-ld=lld" -DLOGGING_LEVEL=INFO -DENABLE_PYTHON_BINDINGS=On -DENABLE_GDAL=On - run: python3 ./scripts/valhalla_build_config - run: make -C build -j8 - run: make -C build utrecht_tiles @@ -74,7 +74,7 @@ jobs: cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=On -DENABLE_PYTHON_BINDINGS=On -DLOGGING_LEVEL=TRACE \ -DCPACK_GENERATOR=DEB -DCPACK_PACKAGE_VERSION_SUFFIX="-0ubuntu1-$(lsb_release -sc)" -DENABLE_SANITIZERS=ON \ - -DENABLE_SINGLE_FILES_WERROR=Off + -DENABLE_SINGLE_FILES_WERROR=Off -DENABLE_GDAL=On - run: make -C build -j8 - run: make -C build utrecht_tiles - run: make -C build -j8 tests @@ -106,7 +106,7 @@ jobs: mkdir build cd build cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=On -DENABLE_PYTHON_BINDINGS=On -DCPACK_GENERATOR=DEB \ - -DCPACK_PACKAGE_VERSION_SUFFIX="-0ubuntu1-$(lsb_release -sc)" -DENABLE_SINGLE_FILES_WERROR=Off + -DCPACK_PACKAGE_VERSION_SUFFIX="-0ubuntu1-$(lsb_release -sc)" -DENABLE_SINGLE_FILES_WERROR=Off -DENABLE_GDAL=On - run: make -C build -j8 - run: make -C build utrecht_tiles - run: make -C build -j8 tests diff --git a/.github/workflows/mingw-build.yml b/.github/workflows/mingw-build.yml index 18cac6ffc7..e3dfd581ea 100644 --- a/.github/workflows/mingw-build.yml +++ b/.github/workflows/mingw-build.yml @@ -18,6 +18,7 @@ jobs: mingw64-boost \ mingw64-protobuf \ mingw64-geos \ + mingw64-gdal \ mingw64-python3 \ protobuf-compiler - uses: actions/checkout@v2 @@ -39,6 +40,7 @@ jobs: -DENABLE_PYTHON_BINDINGS=ON \ -DENABLE_CCACHE=OFF \ -DENABLE_TESTS=OFF \ + -DENABLE_GDAL=ON \ -DLOGGING_LEVEL=DEBUG \ -DBoost_PROGRAM_OPTIONS_LIBRARY=/usr/x86_64-w64-mingw32/sys-root/mingw/lib/libboost_program_options-mt-x64.dll.a \ -DPYTHON_EXECUTABLE=/usr/x86_64-w64-mingw32/bin/python3 \ diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 004233182c..100c22dd4d 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -40,7 +40,7 @@ jobs: - name: Install dependencies run: | - HOMEBREW_NO_AUTO_UPDATE=1 brew install autoconf automake protobuf cmake ccache libtool sqlite3 libspatialite luajit curl wget czmq lz4 spatialite-tools unzip boost + HOMEBREW_NO_AUTO_UPDATE=1 brew install autoconf automake protobuf cmake ccache libtool sqlite3 libspatialite luajit curl wget czmq lz4 spatialite-tools unzip boost gdal sudo python -m pip install --break-system-packages requests shapely git clone https://github.com/kevinkreiser/prime_server --recurse-submodules && cd prime_server && ./autogen.sh && ./configure && make -j$(sysctl -n hw.logicalcpu) && sudo make install @@ -58,7 +58,7 @@ jobs: ccache-osx- - name: Configure CMake - run: cmake -B build -DENABLE_SINGLE_FILES_WERROR=OFF + run: cmake -B build -DENABLE_SINGLE_FILES_WERROR=OFF -DENABLE_GDAL=ON - name: Build Valhalla run: make -C build -j$(sysctl -n hw.logicalcpu) diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index 80a0abcc14..e3755c8df1 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -106,7 +106,8 @@ jobs: -DENABLE_TESTS=OFF \ -DENABLE_CCACHE=OFF \ -DENABLE_SERVICES=OFF \ - -DPREFER_EXTERNAL_DEPS=ON + -DPREFER_EXTERNAL_DEPS=ON \ + -DENABLE_GDAL=ON - if: ${{ steps.vcpkg-restore.outputs.cache-hit != 'true' }} name: Save vcpkg packages (if cache miss) diff --git a/CHANGELOG.md b/CHANGELOG.md index f32e59e8b9..c8f15ea664 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,7 @@ * ADDED: Improved instructions for blind users [#3694](https://github.com/valhalla/valhalla/pull/3694) * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) * ADDED: isochrone proper polygon support & pbf output for isochrone [#4575](https://github.com/valhalla/valhalla/pull/4575) + * ADDED: return isotile grid as geotiff [#4594](https://github.com/valhalla/valhalla/pull/4594) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/CMakeLists.txt b/CMakeLists.txt index 63ccd718c0..b1f238201a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ # This is secondary build configuration provided for convenient development # on Windows and using CMake-enabled IDEs. # -cmake_minimum_required(VERSION 3.12 FATAL_ERROR) +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) project(valhalla LANGUAGES CXX C) include(FindPkgConfig) @@ -36,6 +36,7 @@ option(ENABLE_SINGLE_FILES_WERROR "Convert compiler warnings to errors for singl option(PREFER_EXTERNAL_DEPS "Whether to use internally vendored headers or find the equivalent external package" OFF) # useful to workaround issues likes this https://stackoverflow.com/questions/24078873/cmake-generated-xcode-project-wont-compile option(ENABLE_STATIC_LIBRARY_MODULES "If ON builds Valhalla modules as STATIC library targets" OFF) +option(ENABLE_GDAL "Whether to include GDAL; currently only used for raster serialization of isotile grid" OFF) set(LOGGING_LEVEL "" CACHE STRING "Logging level, default is INFO") set_property(CACHE LOGGING_LEVEL PROPERTY STRINGS "NONE;ALL;ERROR;WARN;INFO;DEBUG;TRACE") @@ -96,8 +97,6 @@ include(ValhallaSourceGroups) # We use pkg-config for (almost) all dependencies: # - CMake Find* modules are versioned with CMake, not the packages, see protobuf > 21.12 # - it's more trivial to use custom installations via PKG_CONFIG_PATH env var -find_package(PkgConfig REQUIRED) -find_package(Threads REQUIRED) pkg_check_modules(ZLIB REQUIRED IMPORTED_TARGET zlib) pkg_check_modules(LZ4 REQUIRED IMPORTED_TARGET liblz4) @@ -113,12 +112,19 @@ if (ENABLE_HTTP OR ENABLE_DATA_TOOLS) endif() endif() +# prefer CONFIG mode over MODULE mode, which versions configuration on the package, not CMake +# NOTE: this is only supported for cmake >= 3.15, but shouldn't be a problem in real life +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) + # Boost has no .pc file.. find_package(Boost 1.71 REQUIRED) add_definitions(-DBOOST_NO_CXX11_SCOPED_ENUMS) add_definitions(-DBOOST_ALLOW_DEPRECATED_HEADERS) add_definitions(-DBOOST_BIND_GLOBAL_PLACEHOLDERS) +find_package(PkgConfig REQUIRED) +find_package(Threads REQUIRED) + # resolve vendored libraries set(date_include_dir ${VALHALLA_SOURCE_DIR}/third_party/date/include) set(rapidjson_include_dir ${CMAKE_SOURCE_DIR}/third_party/rapidjson/include) @@ -167,14 +173,10 @@ endif() # Protobuf is non-trivial to include via pkg-config, pkg_check_modules has no way to check # for protoc location in a platform agnostic manner -# prefer CONFIG mode over MODULE mode, which versions configuration on the package, not CMake -# NOTE: this is only supported for cmake >= 3.15, but shouldn't be a problem in real life -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) # newer protobuf versions require a compat bool set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "") find_package(Protobuf REQUIRED) # and turn it off again -set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF) message(STATUS "Using protoc from ${Protobuf_PROTOC_EXECUTABLE}") message(STATUS "Using pbf headers from ${Protobuf_INCLUDE_DIRS}") message(STATUS "Using pbf libs from ${PROTOBUF_LIBRARIES}") @@ -189,6 +191,16 @@ else() message(FATAL_ERROR "Required target protobuf::libprotobuf-lite or protobuf::libprotobuf is not defined") endif() +# gdal +set(gdal_target "") +if (ENABLE_GDAL) + find_package(GDAL REQUIRED) + add_compile_definitions(ENABLE_GDAL) + set(gdal_target GDAL::GDAL) +endif() + +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF) + # libprime_server # see https://gitlab.kitware.com/cmake/cmake/-/issues/19467 set(libprime_server_targets "") @@ -203,6 +215,7 @@ if(ENABLE_SERVICES) endif() endif() + ## Mjolnir and associated executables if(ENABLE_DATA_TOOLS) add_compile_definitions(DATA_TOOLS) diff --git a/cmake/ValhallaPkgConfig.cmake b/cmake/ValhallaPkgConfig.cmake index 3649a24dd3..a0483c801a 100644 --- a/cmake/ValhallaPkgConfig.cmake +++ b/cmake/ValhallaPkgConfig.cmake @@ -33,6 +33,9 @@ function(configure_valhalla_pc) if(ENABLE_SERVICES) list(APPEND REQUIRES libprime_server) endif() + if(ENABLE_GDAL) + list(APPEND REQUIRES gdal) + endif() if(WIN32 AND NOT MINGW) list(APPEND LIBS_PRIVATE -lole32 -lshell32) else() diff --git a/docs/docs/api/isochrone/api-reference.md b/docs/docs/api/isochrone/api-reference.md index 9972a5ad87..27d82a047b 100644 --- a/docs/docs/api/isochrone/api-reference.md +++ b/docs/docs/api/isochrone/api-reference.md @@ -51,9 +51,9 @@ The isochrone service uses the `auto`, `bicycle`, `pedestrian`, and `multimodal` ## Outputs of the Isochrone service -In the service response, the isochrone contours are returned as [GeoJSON](http://geojson.org/), which can be integrated into mapping applications. +In the service response, the isochrone contours are returned as [GeoJSON](http://geojson.org/), which can be integrated into mapping applications. Alternatively, the grid data that underlies these contours can be returned as a [GeoTIFF](https://www.ogc.org/standard/geotiff/). -The contours are calculated using rasters and are returned as either polygon or line features, depending on your input setting for the `polygons` parameter. If an isochrone request has been named using the optional `&id=` input, then the `id` is returned as a name property for the feature collection within the GeoJSON response. A `metric` attribute lets you know whether it's a `distance` or `time` contour. A warnings array may also be included. This array may contain warning objects informing about deprecated request parameters, clamped values etc. | +The isochrone service returns contours as GeoJSON line or polygon features for the requested intervals (depending on the value of the `polygons` request parameter). These contours are calculated using a two dimensional grid. If the `format` request parameter is set to `geotiff`, the underlying grid data is returned directly instead of the contours derived from it. It will return one band for each requested metric (i.e. one for `time` and one for `distance`). If an isochrone request has been named using the optional `&id=` input, then the `id` is returned as a name property for the feature collection within the GeoJSON response. A `metric` attribute lets you know whether it's a `distance` or `time` contour. A warnings array may also be included. This array may contain warning objects informing about deprecated request parameters, clamped values etc. | See the [HTTP return codes](../turn-by-turn/api-reference.md#http-status-codes-and-conditions) for more on messages you might receive from the service. @@ -63,6 +63,8 @@ Most JavaScript-based GeoJSON renderers, including [Leaflet](http://leafletjs.co When making a map, drawing the isochrone contours as lines is more straightforward than polygons, and, therefore, currently is the default and recommended method. When deciding between the output as lines and polygons, consider your use case and the additional styling considerations involved with polygons. For example, fills should be rendered as semi-transparent over the other map layers so they are visible, although you may have more flexibility when using a vector-based map. In addition, polygons from multiple contour levels do not have overlapping areas cut out or removed. In other words, the outer contours include the areas of any inner contours, causing the colors and transparencies to blend when multiple contour polygons are drawn at the same time. +(TODO: write something about rendering the GeoTIFF output.) + ## Future work on the isochrone service The Isochrone service is in active development. To report software issues or suggest enhancements, open an issue in the [Valhalla GitHub repository](https://github.com/valhalla/valhalla/issues). @@ -75,7 +77,7 @@ Several other options are being considered as future service enhancements. These * ~~Removing self intersections from polygonal contours.~~ * ~~Allowing multiple locations to compute the region reachable from any of the locations within a specified time.~~ * ~~Generating contours with reverse access logic to see the region that can reach a specific location within the specified time.~~ -* Returning raster data for potential animation using OpenGL shaders. This also has analysis use for being able to query thousands of locations to determine the time to each location, including improvements with one-to-many requests to the Valhalla Time-Distance Matrix service. +* ~~Returning raster data for potential animation using OpenGL shaders. This also has analysis use for being able to query thousands of locations to determine the time to each location, including improvements with one-to-many requests to the Valhalla Time-Distance Matrix service.~~ ## Data credits diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 18c131d0a6..1ee45e701e 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -620,4 +620,5 @@ The codes correspond to code returned from a particular [Valhalla project](https |**5xx** | **Tyr project codes** | |500 | Failed to parse intermediate request format | |501 | Failed to parse TripDirections | +|504 | GeoTIFF serialization not supported by service | |599 | Unknown | diff --git a/docs/docs/building.md b/docs/docs/building.md index 30ad5fde32..7296b815bc 100644 --- a/docs/docs/building.md +++ b/docs/docs/building.md @@ -27,6 +27,7 @@ Important build options include: | `-DENABLE_ADDRESS_SANITIZER` (`ON` / `OFF`) | Build with address sanitizer (defaults to off).| | `-DENABLE_UNDEFINED_SANITIZER` (`ON` / `OFF`) | Build with undefined behavior sanitizer (defaults to off).| | `-DPREFER_SYSTEM_DEPS` (`ON` / `OFF`) | Whether to use internally vendored headers or find the equivalent external package (defaults to off).| +| `-DENABLE_GDAL` (`ON` / `OFF`) | Whether to include GDAL as a dependency (used for GeoTIFF serialization of isochrone grid) (defaults to off).| ### Building with `vcpkg` - any platform @@ -138,7 +139,7 @@ When importing `libvalhalla` as a dependency in a project, it's important to kno To resolve `libvalhalla`'s linker/library paths/options, we recommend to use `pkg-config` or `pkg_check_modules` (in CMake). -Currently, `rapidjson`, `date` & `dirent` (Win only) headers are vendored in `third_party`. Consuming applications are encouraged to use `pkg-config` to resolve Valhalla and its dependencies which will automatically install those headers to `/path/to/include/valhalla/third_pary/{rapidjson, date, dirent.h}` and can be `#include`d appropriately. +Currently, `rapidjson`, `date` & `dirent` (Win only) headers are vendored in `third_party`. Consuming applications are encouraged to use `pkg-config` to resolve Valhalla and its dependencies which will automatically install those headers to `/path/to/include/valhalla/third_party/{rapidjson, date, dirent.h}` and can be `#include`d appropriately. ## Running Valhalla server on Unix diff --git a/docs/docs/thor/isochrones.md b/docs/docs/thor/isochrones.md index 74e4eedbe0..54d47675b3 100644 --- a/docs/docs/thor/isochrones.md +++ b/docs/docs/thor/isochrones.md @@ -27,7 +27,7 @@ The 2-D grid is used to find the isocrhone contours by using a well-known contou After forming sets of contour polygons, KEVIN -please write a paragraph or 2 to describe how the contours are formed and output! -This 2-D grid of times can be useful for other purposes as well. It provides a very fast way to query a single location to see how long it takes to get there from the test location. Ultimately this could be a way to do very large one-to-many matrices. At this time we do not return the 2-D array of times, but this is a possibility in the future. +This 2-D grid can be useful for other purposes as well. It provides a very fast way to query a single location to see how long it takes to get there from the test location. Ultimately this could be a way to do very large one-to-many matrices. Where is it? ------------ diff --git a/proto/options.proto b/proto/options.proto index f6c4ca3194..122db3b360 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -340,6 +340,7 @@ message Options { gpx = 1; osrm = 2; pbf = 3; + geotiff = 4; } enum Action { diff --git a/scripts/install-linux-deps.sh b/scripts/install-linux-deps.sh index 4b3e3bae80..eb2b65d16d 100755 --- a/scripts/install-linux-deps.sh +++ b/scripts/install-linux-deps.sh @@ -22,6 +22,7 @@ env DEBIAN_FRONTEND=noninteractive sudo apt install --yes --quiet \ libboost-all-dev \ libcurl4-openssl-dev \ libczmq-dev \ + libgdal-dev \ libgeos++-dev \ libgeos-dev \ libluajit-5.1-dev \ diff --git a/src/proto_conversions.cc b/src/proto_conversions.cc index bcab5517f3..f4172b5b0c 100644 --- a/src/proto_conversions.cc +++ b/src/proto_conversions.cc @@ -268,10 +268,8 @@ const std::string& ShapeMatch_Enum_Name(const ShapeMatch match) { bool Options_Format_Enum_Parse(const std::string& format, Options::Format* f) { static const std::unordered_map formats{ - {"json", Options::json}, - {"gpx", Options::gpx}, - {"osrm", Options::osrm}, - {"pbf", Options::pbf}, + {"json", Options::json}, {"gpx", Options::gpx}, {"osrm", Options::osrm}, + {"pbf", Options::pbf}, {"geotiff", Options::geotiff}, }; auto i = formats.find(format); if (i == formats.cend()) @@ -282,10 +280,8 @@ bool Options_Format_Enum_Parse(const std::string& format, Options::Format* f) { const std::string& Options_Format_Enum_Name(const Options::Format match) { static const std::unordered_map formats{ - {Options::json, "json"}, - {Options::gpx, "gpx"}, - {Options::osrm, "osrm"}, - {Options::pbf, "pbf"}, + {Options::json, "json"}, {Options::gpx, "gpx"}, {Options::osrm, "osrm"}, + {Options::pbf, "pbf"}, {Options::geotiff, "geotiff"}, }; auto i = formats.find(match); return i == formats.cend() ? empty_str : i->second; diff --git a/src/thor/CMakeLists.txt b/src/thor/CMakeLists.txt index adcd0af750..d486d202b2 100644 --- a/src/thor/CMakeLists.txt +++ b/src/thor/CMakeLists.txt @@ -56,4 +56,4 @@ valhalla_module(NAME thor valhalla::meili ${valhalla_protobuf_targets} Boost::boost - ${libprime_server_targets}) + ${libprime_server_targets}) \ No newline at end of file diff --git a/src/thor/isochrone_action.cc b/src/thor/isochrone_action.cc index 9bb3630d64..6faf094e06 100644 --- a/src/thor/isochrone_action.cc +++ b/src/thor/isochrone_action.cc @@ -43,15 +43,8 @@ std::string thor_worker_t::isochrones(Api& request) { if (options.action() == Options_Action_expansion) return ""; - // we have parallel vectors of contour properties and the actual geojson features - // this method sorts the contour specifications by metric (time or distance) and then by value - // with the largest values coming first. eg (60min, 30min, 10min, 40km, 10km) - auto contours = - grid->GenerateContours(intervals, options.polygons(), options.denoise(), options.generalize()); - - // make the final output (pbf or json) - std::string ret = tyr::serializeIsochrones(request, intervals, contours, options.polygons(), - options.show_locations()); + // make the final output (pbf, json or geotiff) + std::string ret = tyr::serializeIsochrones(request, intervals, grid); return ret; } diff --git a/src/tyr/CMakeLists.txt b/src/tyr/CMakeLists.txt index 197e14b53b..9c0b22dee2 100644 --- a/src/tyr/CMakeLists.txt +++ b/src/tyr/CMakeLists.txt @@ -20,6 +20,8 @@ set(system_includes ${date_include_dir} $<$:${dirent_include_dir}>) + + valhalla_module(NAME tyr SOURCES ${sources} @@ -44,4 +46,6 @@ valhalla_module(NAME tyr valhalla::odin valhalla::proto ${valhalla_protobuf_targets} - Boost::boost) + Boost::boost + ${gdal_target} + ) diff --git a/src/tyr/isochrone_serializer.cc b/src/tyr/isochrone_serializer.cc index 6f549e5e05..a1b504158e 100644 --- a/src/tyr/isochrone_serializer.cc +++ b/src/tyr/isochrone_serializer.cc @@ -2,12 +2,17 @@ #include "baldr/json.h" #include "midgard/point2.h" #include "midgard/pointll.h" +#include "thor/worker.h" #include "tyr/serializers.h" #include #include #include +#ifdef ENABLE_GDAL +#include +#endif + using namespace valhalla::baldr::json; namespace { @@ -21,6 +26,7 @@ using feature_t = std::list; // rings per interval using contours_t = std::vector>; // all rings using contour_group_t = std::vector; using grouped_contours_t = std::vector; +// dimension, value (seconds/meters), name (time/distance), color using contour_interval_t = std::tuple; grouped_contours_t GroupContours(const bool polygons, const feature_t& contours) { @@ -134,6 +140,91 @@ void addLocations(Api& request, valhalla::baldr::json::ArrayPtr& features) { } } +#ifdef ENABLE_GDAL +// get a temporary file name suffix for GDAL's virtual file system +std::string GenerateTmpFName() { + std::stringstream ss; + ss << "/vsimem/" << std::this_thread::get_id() << "_" + << std::chrono::high_resolution_clock::now().time_since_epoch().count(); + return ss.str(); +} + +std::string serializeGeoTIFF(Api& request, const std::shared_ptr>& isogrid) { + + // time, distance + std::vector metrics{false, false}; + for (auto& contour : request.options().contours()) { + metrics[0] = metrics[0] || contour.has_time_case(); + metrics[1] = metrics[1] || contour.has_distance_case(); + } + + auto box = isogrid->MinExtent(); + int32_t ext_x = box[2] - box[0]; + int32_t ext_y = box[3] - box[1]; + + // for GDALs virtual fs + std::string name = GenerateTmpFName(); + + auto nbands = std::count(metrics.begin(), metrics.end(), true); + char** geotiff_options = NULL; + geotiff_options = CSLSetNameValue(geotiff_options, "COMPRESS", "PACKBITS"); + + // TODO: check if there's an easy way to only instantiate the driver once + auto driver_manager = GetGDALDriverManager(); + auto geotiff_driver = driver_manager->GetDriverByName("GTiff"); + auto geotiff_dataset = + geotiff_driver->Create(name.c_str(), ext_x, ext_y, nbands, GDT_UInt16, geotiff_options); + + OGRSpatialReference spatial_ref; + spatial_ref.SetWellKnownGeogCS("EPSG:4326"); + double geo_transform[6] = {isogrid->TileBounds(isogrid->TileId(box[0], box[1])).minx(), // minx + isogrid->TileSize(), + 0, + isogrid->TileBounds(isogrid->TileId(box[0], box[1])).miny(), // miny + 0, + isogrid->TileSize()}; + + geotiff_dataset->SetGeoTransform(geo_transform); + geotiff_dataset->SetSpatialRef(const_cast(&spatial_ref)); + + for (size_t metric_idx = 0; metric_idx < metrics.size(); ++metric_idx) { + if (!metrics[metric_idx]) + continue; // only create bands for requested metrics + uint16_t* data = new uint16_t[ext_x * ext_y]; + + // seconds or 10 meter steps + float scale_factor = metric_idx == 0 ? 60 : 100; + for (int32_t i = 0; i < ext_y; ++i) { + for (int32_t j = 0; j < ext_x; ++j) { + auto tileid = isogrid->TileId(j + box[0], i + box[1]); + data[i * ext_x + j] = + static_cast(isogrid->DataAt(tileid, metric_idx) * scale_factor); + } + } + auto band = geotiff_dataset->GetRasterBand(nbands == 2 ? (metric_idx + 1) : 1); + band->SetNoDataValue(std::numeric_limits::max()); + band->SetDescription(metric_idx == 0 ? "Time (seconds)" : "Distance (10m)"); + + CPLErr err = band->RasterIO(GF_Write, 0, 0, ext_x, ext_y, data, ext_x, ext_y, GDT_UInt16, 0, 0); + + delete[] data; + + if (err != CE_None) { + throw valhalla_exception_t{599, "Unknown error when writing GeoTIFF."}; + } + } + + GDALClose(geotiff_dataset); + vsi_l_offset bufferlength; + GByte* bytes = VSIGetMemFileBuffer(name.c_str(), &bufferlength, TRUE); + + // TODO: there's gotta be way to do this without copying + std::string data(reinterpret_cast(bytes), bufferlength); + + return data; +}; +#endif + std::string serializeIsochroneJson(Api& request, std::vector& intervals, contours_t& contours, @@ -267,15 +358,30 @@ namespace tyr { std::string serializeIsochrones(Api& request, std::vector::contour_interval_t>& intervals, - midgard::GriddedData<2>::contours_t& contours, - bool polygons, - bool show_locations) { + const std::shared_ptr>& isogrid) { + + // only generate if json or pbf output is requested + contours_t contours; switch (request.options().format()) { case Options_Format_pbf: - return serializeIsochronePbf(request, intervals, contours); case Options_Format_json: - return serializeIsochroneJson(request, intervals, contours, show_locations, polygons); + // we have parallel vectors of contour properties and the actual geojson features + // this method sorts the contour specifications by metric (time or distance) and then by value + // with the largest values coming first. eg (60min, 30min, 10min, 40km, 10km) + contours = + isogrid->GenerateContours(intervals, request.options().polygons(), + request.options().denoise(), request.options().generalize()); + return request.options().format() == Options_Format_json + ? serializeIsochroneJson(request, intervals, contours, + request.options().show_locations(), + request.options().polygons()) + : serializeIsochronePbf(request, intervals, contours); + +#ifdef ENABLE_GDAL + case Options_Format_geotiff: + return serializeGeoTIFF(request, isogrid); +#endif default: throw; } diff --git a/src/valhalla_run_isochrone.cc b/src/valhalla_run_isochrone.cc index 44eda59701..a96394caf0 100644 --- a/src/valhalla_run_isochrone.cc +++ b/src/valhalla_run_isochrone.cc @@ -20,6 +20,10 @@ #include "tyr/serializers.h" #include "worker.h" +#ifdef ENABLE_GDAL +#include +#endif + using namespace valhalla; using namespace valhalla::midgard; using namespace valhalla::baldr; @@ -51,7 +55,7 @@ int main(int argc, char* argv[]) { "\"auto\",\"contours\":[{\"time\":15,\"color\":\"ff0000\"}]}'", cxxopts::value()) ("c,config", "Valhalla configuration file", cxxopts::value()) ("i,inline-config", "Inline JSON config", cxxopts::value()) - ("f,file", "GeoJSON file name. If omitted program will print to stdout.", cxxopts::value()); + ("f,file", "file name. If omitted program will print to stdout.", cxxopts::value()); // clang-format on auto result = options.parse(argc, argv); @@ -79,6 +83,12 @@ int main(int argc, char* argv[]) { // Process json request Api request; ParseApi(json_str, valhalla::Options::isochrone, request); + +#ifdef ENABLE_GDAL + if (request.options().format() == Options_Format_geotiff) { + GDALRegister_GTiff(); + } +#endif auto& options = *request.mutable_options(); // Get the denoise parameter @@ -182,7 +192,7 @@ int main(int argc, char* argv[]) { auto expansion_type = routetype == "multimodal" ? ExpansionType::multimodal : (reverse ? ExpansionType::reverse : ExpansionType::forward); - auto isotile = isochrone.Expand(expansion_type, request, reader, mode_costing, mode); + auto isogrid = isochrone.Expand(expansion_type, request, reader, mode_costing, mode); auto t2 = std::chrono::high_resolution_clock::now(); uint32_t msecs = std::chrono::duration_cast(t2 - t1).count(); @@ -190,27 +200,23 @@ int main(int argc, char* argv[]) { // Generate contours t2 = std::chrono::high_resolution_clock::now(); - auto contours = isotile->GenerateContours(contour_times, polygons, denoise, generalize); auto t3 = std::chrono::high_resolution_clock::now(); msecs = std::chrono::duration_cast(t3 - t2).count(); LOG_INFO("Contour Generation took " + std::to_string(msecs) + " ms"); - // Serialize to GeoJSON - std::string geojson = - valhalla::tyr::serializeIsochrones(request, contour_times, contours, polygons, show_locations); - + std::string res = valhalla::tyr::serializeIsochrones(request, contour_times, isogrid); auto t4 = std::chrono::high_resolution_clock::now(); msecs = std::chrono::duration_cast(t4 - t3).count(); - LOG_INFO("GeoJSON serialization took " + std::to_string(msecs) + " ms"); + LOG_INFO("Isochrone serialization took " + std::to_string(msecs) + " ms"); msecs = std::chrono::duration_cast(t4 - t1).count(); LOG_INFO("Isochrone took " + std::to_string(msecs) + " ms"); if (!filename.empty()) { - std::ofstream geojsonOut(filename, std::ofstream::out); - geojsonOut << geojson; - geojsonOut.close(); + std::ofstream resOut(filename, std::ofstream::out); + resOut << res; + resOut.close(); } else { - std::cout << "\n" << geojson << std::endl; + std::cout << "\n" << res << std::endl; } // Shutdown protocol buffer library diff --git a/src/valhalla_service.cc b/src/valhalla_service.cc index 95c753259d..4defc13195 100644 --- a/src/valhalla_service.cc +++ b/src/valhalla_service.cc @@ -17,6 +17,10 @@ using namespace prime_server; #endif +#ifdef ENABLE_GDAL +#include +#endif + #include "config.h" #include "midgard/logging.h" @@ -39,6 +43,10 @@ int main(int argc, char** argv) { } #endif +#ifdef ENABLE_GDAL + GDALRegister_GTiff(); +#endif + // config file // TODO: validate the config std::string config_file(argv[1]); diff --git a/src/worker.cc b/src/worker.cc index bacc47eabf..fed19befb2 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -138,6 +138,8 @@ const std::unordered_map error_codes{ {445, {445, "Shape match algorithm specification in api request is incorrect. Please see documentation for valid shape_match input.", 400, HTTP_400, OSRM_INVALID_URL, "wrong_match_type"}}, {499, {499, "Unknown", 400, HTTP_400, OSRM_INVALID_URL, "unknown"}}, {503, {503, "Leg count mismatch", 400, HTTP_400, OSRM_INVALID_URL, "wrong_number_of_legs"}}, + {504, {504, "This service does not support GeoTIFF serialization.", 400, HTTP_400, OSRM_INVALID_VALUE, "unknown"}}, + {599, {599, "Unknown serialization error", 400, HTTP_400, OSRM_INVALID_VALUE, "unknown"}}, }; // unordered map for warning pairs @@ -695,6 +697,11 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { options.clear_jsonp(); } } +#ifndef ENABLE_GDAL + else if (options.format() == Options::geotiff) { + throw valhalla_exception_t{504}; + } +#endif auto units = rapidjson::get_optional(doc, "/units"); if (units && ((*units == "miles") || (*units == "mi"))) { diff --git a/test/gurka/test_avoids.cc b/test/gurka/test_avoids.cc index c8d6ad8c55..64a9d1547b 100644 --- a/test/gurka/test_avoids.cc +++ b/test/gurka/test_avoids.cc @@ -1,4 +1,5 @@ #include "gurka.h" +#include #include #include #include diff --git a/test/gurka/test_barrier_uturns.cc b/test/gurka/test_barrier_uturns.cc index 247e4563a4..56d140b949 100644 --- a/test/gurka/test_barrier_uturns.cc +++ b/test/gurka/test_barrier_uturns.cc @@ -1,4 +1,5 @@ #include "gurka.h" +#include #include using namespace valhalla; diff --git a/test/gurka/test_bidir_search.cc b/test/gurka/test_bidir_search.cc index 55ca43e2eb..48016dc587 100644 --- a/test/gurka/test_bidir_search.cc +++ b/test/gurka/test_bidir_search.cc @@ -1,5 +1,6 @@ #include "gurka.h" #include "test.h" +#include #include diff --git a/test/isochrone.cc b/test/isochrone.cc index 37eed4be9a..1d890b15b9 100644 --- a/test/isochrone.cc +++ b/test/isochrone.cc @@ -14,6 +14,10 @@ #include #include +#ifdef ENABLE_GDAL +#include +#endif + using point_type = boost::geometry::model::d2::point_xy; using polygon_type = boost::geometry::model::polygon; using boost::geometry::within; @@ -329,6 +333,173 @@ TEST(Isochrones, test_max_reserved_labels_count) { isochrone.Clear(); } +#ifdef ENABLE_GDAL + +void check_raster_edges(size_t x, size_t y, uint16_t* data) { + + // make sure the outer "edges" are not 0 + for (size_t i = 0; i < y; ++i) { + // if not in first or last row + if (i != 0 || i != y - 1) { + // just check first and last element in the row + ASSERT_NE(data[i * y], 0); + ASSERT_NE(data[i * y + x], 0); + continue; + } + + // else check the whole row + for (size_t j = 0; j < x; ++j) { + ASSERT_NE(data[i * y + j], 0); + } + } +} + +TEST(Isochrones, test_geotiff_output_distance) { + GDALRegister_GTiff(); + loki_worker_t loki_worker(cfg); + thor_worker_t thor_worker(cfg); + + const auto request = + R"({"costing":"auto","locations":[{"lon":5.042799,"lat":52.093199}],"contours":[{"distance":1}], "format": "geotiff"})"; + Api request_pbf; + ParseApi(request, Options::isochrone, request_pbf); + loki_worker.isochrones(request_pbf); + std::string geotiff = thor_worker.isochrones(request_pbf); + + std::string name = "/vsimem/test_isogrid_geotiff_d.tif"; + unsigned char buffer[geotiff.length()]; + std::copy(geotiff.cbegin(), geotiff.cend(), buffer); + auto handle = VSIFileFromMemBuffer(name.c_str(), buffer, static_cast(geotiff.size()), 0); + auto geotiff_dataset = GDALDataset::FromHandle(GDALOpen(name.c_str(), GA_ReadOnly)); + int x = geotiff_dataset->GetRasterXSize(); + int y = geotiff_dataset->GetRasterYSize(); + GDALRasterBand* band = geotiff_dataset->GetRasterBand(1); + uint16_t data_array[x * y]; + CPLErr err = band->RasterIO(GF_Read, 0, 0, x, y, data_array, x, y, GDT_UInt16, 0, 0); + double min_max[2]; + + band->ComputeRasterMinMax(0, min_max); + + ASSERT_EQ(err, CE_None); + ASSERT_NE(x, 0); + ASSERT_NE(y, 0); + ASSERT_EQ(static_cast(min_max[0]), 0); + ASSERT_EQ(static_cast(min_max[1]), 1100); + ASSERT_EQ(band->GetNoDataValue(), std::numeric_limits::max()); + size_t array_size = x * y; + + check_raster_edges(x, y, data_array); + + // make sure there are some grid cells whose metric value is neither 0 nor the max + bool no_intermediate_values = true; + for (size_t i = 0; i < array_size; ++i) { + if (data_array[i] > 0 && data_array[i] < min_max[1]) + no_intermediate_values = false; + } + ASSERT_EQ(no_intermediate_values, false); + VSIFCloseL(handle); +} + +TEST(Isochrones, test_geotiff_output_time) { + GDALRegister_GTiff(); + loki_worker_t loki_worker(cfg); + thor_worker_t thor_worker(cfg); + + const auto request = + R"({"costing":"auto","locations":[{"lon":5.042799,"lat":52.093199}],"contours":[{"time":1}], "format": "geotiff"})"; + Api request_pbf; + ParseApi(request, Options::isochrone, request_pbf); + loki_worker.isochrones(request_pbf); + std::string geotiff = thor_worker.isochrones(request_pbf); + + std::string name = "/vsimem/test_isogrid_geotiff_t.tif"; + unsigned char buffer[geotiff.length()]; + std::copy(geotiff.cbegin(), geotiff.cend(), buffer); + auto handle = VSIFileFromMemBuffer(name.c_str(), buffer, static_cast(geotiff.size()), 0); + auto geotiff_dataset = GDALDataset::FromHandle(GDALOpen(name.c_str(), GA_ReadOnly)); + int x = geotiff_dataset->GetRasterXSize(); + int y = geotiff_dataset->GetRasterYSize(); + GDALRasterBand* band = geotiff_dataset->GetRasterBand(1); + uint16_t data_array[x * y]; + CPLErr err = band->RasterIO(GF_Read, 0, 0, x, y, data_array, x, y, GDT_UInt16, 0, 0); + double min_max[2]; + + band->ComputeRasterMinMax(0, min_max); + + ASSERT_EQ(err, CE_None); + ASSERT_GT(x, 0); + ASSERT_GT(y, 0); + ASSERT_EQ(static_cast(min_max[0]), 0); + ASSERT_EQ(static_cast(min_max[1]), 660); + ASSERT_EQ(band->GetNoDataValue(), std::numeric_limits::max()); + size_t array_size = x * y; + + check_raster_edges(x, y, data_array); + + // make sure there are some grid cells whose metric value is neither 0 nor the max + bool no_intermediate_values = true; + for (size_t i = 0; i < array_size; ++i) { + if (data_array[i] > 0 && data_array[i] < min_max[1]) + no_intermediate_values = false; + } + ASSERT_EQ(no_intermediate_values, false); + VSIFCloseL(handle); +} + +// test request with two metrics +TEST(Isochrones, test_geotiff_output_time_distance) { + GDALRegister_GTiff(); + loki_worker_t loki_worker(cfg); + thor_worker_t thor_worker(cfg); + + const auto request = + R"({"costing":"auto","locations":[{"lon":5.042799,"lat":52.093199}],"contours":[{"time":1},{"distance":2}], "format": "geotiff"})"; + Api request_pbf; + ParseApi(request, Options::isochrone, request_pbf); + loki_worker.isochrones(request_pbf); + std::string geotiff = thor_worker.isochrones(request_pbf); + + std::string name = "/vsimem/test_isogrid_geotiff_td.tif"; + unsigned char buffer[geotiff.length()]; + std::copy(geotiff.cbegin(), geotiff.cend(), buffer); + auto handle = VSIFileFromMemBuffer(name.c_str(), buffer, static_cast(geotiff.size()), 0); + auto geotiff_dataset = GDALDataset::FromHandle(GDALOpen(name.c_str(), GA_ReadOnly)); + int x = geotiff_dataset->GetRasterXSize(); + int y = geotiff_dataset->GetRasterYSize(); + + // time, distance + std::array expected_max{660, 1200}; + + for (int b = 1; b <= 2; ++b) { + GDALRasterBand* band = geotiff_dataset->GetRasterBand(b); + uint16_t data_array[x * y]; + CPLErr err = band->RasterIO(GF_Read, 0, 0, x, y, data_array, x, y, GDT_UInt16, 0, 0); + double min_max[2]; + + band->ComputeRasterMinMax(0, min_max); + + ASSERT_EQ(err, CE_None); + ASSERT_NE(x, 0); + ASSERT_NE(y, 0); + ASSERT_EQ(static_cast(min_max[0]), 0); + ASSERT_EQ(static_cast(min_max[1]), expected_max[b - 1]); + ASSERT_EQ(band->GetNoDataValue(), std::numeric_limits::max()); + size_t array_size = x * y; + + check_raster_edges(x, y, data_array); + + // make sure there are some grid cells whose metric value is neither 0 nor the max + bool no_intermediate_values = true; + for (size_t j = 0; j < array_size; ++j) { + if (data_array[j] > 0 && data_array[j] < min_max[1]) + no_intermediate_values = false; + } + ASSERT_EQ(no_intermediate_values, false); + } + VSIFCloseL(handle); +} +#endif + } // namespace int main(int argc, char* argv[]) { diff --git a/valhalla/midgard/gridded_data.h b/valhalla/midgard/gridded_data.h index da1d675487..11d4e0d55f 100644 --- a/valhalla/midgard/gridded_data.h +++ b/valhalla/midgard/gridded_data.h @@ -61,6 +61,14 @@ template class GriddedData : public Tiles { } } + float DataAt(size_t tileid, size_t metricid) const { + return data_[tileid][metricid]; + } + + float MaxValue(size_t metricidx) const { + return max_value_[metricidx]; + } + using contour_t = std::list; using feature_t = std::list; using contours_t = std::vector>; @@ -365,7 +373,6 @@ template class GriddedData : public Tiles { } // some info about the area the image covers - auto c = this->TileBounds().Center(); auto h = this->tilesize_ / 2; // for each contour for (auto& collection : contours) { @@ -414,6 +421,33 @@ template class GriddedData : public Tiles { return contours; } + /** + * Determine the smallest subgrid that contains all valid (i.e. non-max) values + + * @return array with 4 elements: minimum column, minimum row, maximum column, maximum row + */ + const std::array MinExtent() const { + // minx, miny, maxx, maxy + std::array box = {this->ncolumns_ / 2, this->nrows_ / 2, this->ncolumns_ / 2, + this->nrows_ / 2}; + + for (int32_t i = 0; i < this->nrows_; ++i) { + for (int32_t j = 0; j < this->ncolumns_; ++j) { + if (data_[this->TileId(j, i)][0] < max_value_[0] || + data_[this->TileId(j, i)][1] < max_value_[1]) { + // pad by 1 row/column as a sanity check + box[0] = std::min(std::max(j - 1, 0), box[0]); + box[1] = std::min(std::max(i - 1, 0), box[1]); + // +1 extra because range is exclusive + box[2] = std::max(std::min(j + 2, this->ncolumns_ - 1), box[2]); + box[3] = std::max(std::min(i + 2, this->ncolumns_ - 1), box[3]); + } + } + } + + return box; + } + protected: value_type max_value_; // Maximum value stored in the tile std::vector data_; // Data value within each tile diff --git a/valhalla/tyr/serializers.h b/valhalla/tyr/serializers.h index ecb9fdb319..062d9dc985 100644 --- a/valhalla/tyr/serializers.h +++ b/valhalla/tyr/serializers.h @@ -40,10 +40,7 @@ std::string serializeMatrix(Api& request); */ std::string serializeIsochrones(Api& request, std::vector::contour_interval_t>& intervals, - midgard::GriddedData<2>::contours_t& contours, - bool polygons = true, - bool show_locations = false); - + const std::shared_ptr>& isogrid); /** * Turn heights and ranges into a height response * diff --git a/vcpkg.json b/vcpkg.json index cafb7d935f..fbb848fe00 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -17,6 +17,7 @@ "name": "dirent", "platform": "windows" }, + "gdal", "geos", "libspatialite", "pkgconf", From 482bcf29455231d8d42c66c71aae9f5b3840a083 Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 4 Mar 2024 15:19:05 +0100 Subject: [PATCH 043/198] print stages in valhalla_build_tiles --help (#4611) --- src/argparse_utils.h | 7 ++++++- src/mjolnir/valhalla_build_tiles.cc | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/argparse_utils.h b/src/argparse_utils.h index 18f4f952bf..e102bad15d 100644 --- a/src/argparse_utils.h +++ b/src/argparse_utils.h @@ -21,6 +21,7 @@ * @param config The config which will be populated here * @param log The logging config node's key. If empty, logging will not be configured. * @param use_threads Whether this program multi-threads + * @param extra_help Optional function pointer to print more stuff to the end of the help message. * * @returns true if the program should continue, false if we should EXIT_SUCCESS * @throws cxxopts::exceptions::exception Thrown if there's no valid configuration @@ -30,9 +31,13 @@ bool parse_common_args(const std::string& program, const cxxopts::ParseResult& result, boost::property_tree::ptree& conf, const std::string& log, - const bool use_threads = false) { + const bool use_threads = false, + std::function extra_help = nullptr) { if (result.count("help")) { std::cout << opts.help() << "\n"; + if (extra_help) { + extra_help(); + }; return false; } diff --git a/src/mjolnir/valhalla_build_tiles.cc b/src/mjolnir/valhalla_build_tiles.cc index 0ef39323e5..3269cdf05c 100644 --- a/src/mjolnir/valhalla_build_tiles.cc +++ b/src/mjolnir/valhalla_build_tiles.cc @@ -57,7 +57,7 @@ int main(int argc, char** argv) { options.parse_positional({"input_files"}); options.positional_help("OSM PBF file(s)"); auto result = options.parse(argc, argv); - if (!parse_common_args(program, options, result, config, "mjolnir.logging", true)) + if (!parse_common_args(program, options, result, config, "mjolnir.logging", true, &list_stages)) return EXIT_SUCCESS; // Convert stage strings to BuildStage From 929718b9bcc5ba6b8033fdfe5e43b51af1d63cac Mon Sep 17 00:00:00 2001 From: Greg Knisely Date: Tue, 12 Mar 2024 12:14:45 -0400 Subject: [PATCH 044/198] Conflict with signinfo's temporary linguistic node sequence file caused test failures. (#4625) --- CHANGELOG.md | 1 + test/graphparser.cc | 200 ++++++++++++++------------------------------ test/signinfo.cc | 2 +- 3 files changed, 65 insertions(+), 138 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8f15ea664..3e5c6aa4b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,7 @@ * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) * ADDED: isochrone proper polygon support & pbf output for isochrone [#4575](https://github.com/valhalla/valhalla/pull/4575) * ADDED: return isotile grid as geotiff [#4594](https://github.com/valhalla/valhalla/pull/4594) + * FIXED: Conflict with signinfo's temporary linguistic node sequence file caused test failures. [#4625](https://github.com/valhalla/valhalla/pull/4625) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/test/graphparser.cc b/test/graphparser.cc index 6038aaec4e..4df5671a1f 100644 --- a/test/graphparser.cc +++ b/test/graphparser.cc @@ -28,6 +28,16 @@ namespace { const std::string config_file = "test/test_config_gp"; +std::string ways_file = "test_ways.bin"; +std::string way_nodes_file = "test_way_nodes.bin"; +std::string nodes_file = "test_nodes.bin"; +std::string edges_file = "test_edges.bin"; +std::string access_file = "test_access.bin"; +std::string from_restriction_file = "test_from_complex_restrictions.bin"; +std::string to_restriction_file = "test_to_complex_restrictions.bin"; +std::string bss_nodes_file = "test_bss_nodes.bin"; +std::string linguistic_node_file = "test_linguistic_node.bin"; + const auto node_predicate = [](const OSMWayNode& a, const OSMWayNode& b) { return a.node.osmid_ < b.node.osmid_; }; @@ -48,18 +58,55 @@ OSMWay GetWay(uint32_t way_id, sequence& ways) { return *found; } +void DoConfig() { + std::ofstream file; + try { + file.open(config_file, std::ios_base::trunc); + file << "{ \ + \"mjolnir\": { \ + \"id_table_size\": 1000, \ + \"tile_dir\": \"test/data/parser_tiles\" \ + } \ + }"; + } catch (...) {} + file.close(); +} + +// must do clean up here vs TearDown() as we are building data +// in the same directory multiple times +void CleanUp() { + if (filesystem::exists(ways_file)) + filesystem::remove(ways_file); + + if (filesystem::exists(way_nodes_file)) + filesystem::remove(way_nodes_file); + + if (filesystem::exists(nodes_file)) + filesystem::remove(nodes_file); + + if (filesystem::exists(edges_file)) + filesystem::remove(edges_file); + + if (filesystem::exists(access_file)) + filesystem::remove(access_file); + + if (filesystem::exists(from_restriction_file)) + filesystem::remove(from_restriction_file); + + if (filesystem::exists(to_restriction_file)) + filesystem::remove(to_restriction_file); + + if (filesystem::exists(bss_nodes_file)) + filesystem::remove(bss_nodes_file); + + if (filesystem::exists(linguistic_node_file)) + filesystem::remove(linguistic_node_file); +} + void BollardsGatesAndAccess(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - std::string linguistic_node_file = "test_linguistic_node.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/liechtenstein-latest.osm.pbf"}, @@ -206,27 +253,13 @@ void BollardsGatesAndAccess(const std::string& config_file) { EXPECT_TRUE((bike_network & kMcn) && (bike_network & kRcn) && way_75786176.bike_network() == 0) << "rcn and mtb not marked on way 75786176."; - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); - filesystem::remove(linguistic_node_file); + CleanUp(); } void RemovableBollards(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - std::string linguistic_node_file = "test_linguistic_node.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, ways_file, way_nodes_file, access_file); @@ -250,27 +283,13 @@ void RemovableBollards(const std::string& config_file) { kEmergencyAccess | kPedestrianAccess | kWheelchairAccess | kBicycleAccess | kMopedAccess | kMotorcycleAccess); - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); - filesystem::remove(linguistic_node_file); + CleanUp(); } void Exits(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - std::string linguistic_node_file = "test_linguistic_node.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/harrisburg.osm.pbf"}, ways_file, way_nodes_file, access_file); @@ -302,27 +321,13 @@ void Exits(const std::string& config_file) { EXPECT_EQ(osmdata.node_names.name(node.exit_to_index()), "PA441") << "node exit_to not set correctly ."; - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); - filesystem::remove(linguistic_node_file); + CleanUp(); } void Baltimore(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - std::string linguistic_node_file = "test_linguistic_node.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/baltimore.osm.pbf"}, ways_file, way_nodes_file, access_file); @@ -430,27 +435,13 @@ void Baltimore(const std::string& config_file) { FAIL() << "98040438 restriction test failed."; } - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); - filesystem::remove(linguistic_node_file); + CleanUp(); } void Bike(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - std::string linguistic_node_file = "test_linguistic_node.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bike.osm.pbf"}, ways_file, way_nodes_file, access_file); @@ -528,26 +519,13 @@ void Bike(const std::string& config_file) { EXPECT_TRUE(way_156539491.moped_forward()); EXPECT_TRUE(way_156539491.bike_backward()); - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(linguistic_node_file); + CleanUp(); } void Bus(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - std::string linguistic_node_file = "test_linguistic_node.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/bus.osm.pbf"}, ways_file, way_nodes_file, access_file); @@ -610,27 +588,13 @@ void Bus(const std::string& config_file) { EXPECT_FALSE(way_225895737.bus_backward()); EXPECT_FALSE(way_225895737.bike_backward()); - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); - filesystem::remove(linguistic_node_file); + CleanUp(); } void BicycleTrafficSignals(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string access_file = "test_access.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - std::string linguistic_node_file = "test_linguistic_node.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/nyc.osm.pbf"}, ways_file, way_nodes_file, access_file); @@ -661,27 +625,7 @@ void BicycleTrafficSignals(const std::string& config_file) { << "Bike rental at a shop not marked as intersection." */ - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); - filesystem::remove(linguistic_node_file); -} - -void DoConfig() { - std::ofstream file; - try { - file.open(config_file, std::ios_base::trunc); - file << "{ \ - \"mjolnir\": { \ - \"id_table_size\": 1000, \ - \"tile_dir\": \"test/data/parser_tiles\" \ - } \ - }"; - } catch (...) {} - file.close(); + CleanUp(); } TEST(GraphParser, TestBollardsGatesAndAccess) { @@ -726,16 +670,6 @@ TEST(GraphParser, TestImportBssNode) { conf.put("mjolnir.import_bike_share_stations", true); - std::string ways_file = "test_ways.bin"; - std::string way_nodes_file = "test_way_nodes.bin"; - std::string nodes_file = "test_nodes.bin"; - std::string edges_file = "test_edges.bin"; - std::string access_file = "test_access.bin"; - std::string from_restriction_file = "test_from_complex_restrictions.bin"; - std::string to_restriction_file = "test_to_complex_restrictions.bin"; - std::string bss_nodes_file = "test_bss_nodes.bin"; - std::string linguistic_node_file = "test_linguistic_node.bin"; - auto osmdata = PBFGraphParser::ParseWays(conf.get_child("mjolnir"), {VALHALLA_SOURCE_DIR "test/data/rome.osm.pbf"}, ways_file, way_nodes_file, access_file); @@ -825,15 +759,7 @@ TEST(GraphParser, TestImportBssNode) { kPedestrianAccess); check_edge_attribute(local_tile->directededge(edge_idx_2 + count_2 - 2), kPedestrianAccess, kBicycleAccess); - - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(bss_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_nodes_file); - filesystem::remove(linguistic_node_file); + CleanUp(); } } // namespace diff --git a/test/signinfo.cc b/test/signinfo.cc index a64ba0f8b9..c544bad0d6 100644 --- a/test/signinfo.cc +++ b/test/signinfo.cc @@ -30,7 +30,7 @@ TEST(Signinfo, ExitToTest) { std::vector signs; std::vector linguistics; std::vector> default_languages; - const std::string linguistic_node_file = "test_linguistic_node.bin"; + const std::string linguistic_node_file = "test_sign_linguistic_node.bin"; sequence linguistic_node(linguistic_node_file, true); bool has_guide = GraphBuilder::CreateSignInfoList(exit_node, way, pronunciationMap, langMap, From 7f4cc6f098caeb70c5514ff1b098475f89645872 Mon Sep 17 00:00:00 2001 From: Nils Date: Wed, 13 Mar 2024 14:26:11 +0100 Subject: [PATCH 045/198] fix CostMatrix for trivial routes with oneways (#4626) --- CHANGELOG.md | 1 + src/thor/costmatrix.cc | 159 +++++++++++++++++++++----------------- test/gurka/gurka.h | 1 + test/gurka/test_matrix.cc | 30 +++++++ test/test.h | 1 + valhalla/sif/edgelabel.h | 2 +- 6 files changed, 122 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e5c6aa4b3..49dc12b027 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ * FIXED: Aggregation updates: update opposing local idx after aggregating the edges, added classification check for aggregation, and shortcut length changes [#4570](https://github.com/valhalla/valhalla/pull/4570) * FIXED: Use helper function for only parsing out names from DirectedEdge when populating intersecting edges [#4604](https://github.com/valhalla/valhalla/pull/4604) * FIXED: Osmnode size reduction: Fixed excessive disk space for planet build [#4605](https://github.com/valhalla/valhalla/pull/4605) + * FIXED: CostMatrix for trivial routes with oneways [#4626](https://github.com/valhalla/valhalla/pull/4626) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 2386aa1229..1a1cb17986 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -166,6 +166,7 @@ bool CostMatrix::SourceToTarget(Api& request, // search from all source locations. Connections between the 2 search // spaces is checked during the forward search. uint32_t n = 0; + uint32_t interrupt_n = 0; while (true) { // First iterate over all targets, then over all sources: we only for sure // check the connection between both trees on the forward search, so reverse @@ -252,9 +253,10 @@ bool CostMatrix::SourceToTarget(Api& request, throw valhalla_exception_t{430}; } // Allow this process to be aborted - if (interrupt_ && (n++ % kInterruptIterationsInterval) == 0) { + if (interrupt_ && (interrupt_n++ % kInterruptIterationsInterval) == 0) { (*interrupt_)(); } + n++; } // resize/reserve all properties of Matrix on first pass only @@ -726,26 +728,26 @@ bool CostMatrix::Expand(const uint32_t index, // Check if the edge on the forward search connects to a reached edge // on the reverse search trees. void CostMatrix::CheckForwardConnections(const uint32_t source, - const BDEdgeLabel& pred, + const BDEdgeLabel& fwd_pred, const uint32_t n, GraphReader& graphreader) { // Disallow connections that are part of an uturn on an internal edge - if (pred.internal_turn() != InternalTurn::kNoTurn) { + if (fwd_pred.internal_turn() != InternalTurn::kNoTurn) { return; } // Disallow connections that are part of a complex restriction. // TODO - validate that we do not need to "walk" the paths forward // and backward to see if they match a restriction. - if (pred.on_complex_rest()) { + if (fwd_pred.on_complex_rest()) { // TODO(nils): bidir a* is digging deeper return; } // Get the opposing edge. Get a list of target locations whose reverse // search has reached this edge. - GraphId oppedge = pred.opp_edgeid(); - auto targets = targets_->find(oppedge); + GraphId rev_edgeid = fwd_pred.opp_edgeid(); + auto targets = targets_->find(rev_edgeid); if (targets == targets_->end()) { return; } @@ -766,42 +768,48 @@ void CostMatrix::CheckForwardConnections(const uint32_t source, // If we came down here, we know this opposing edge is either settled, or it's a // target correlated edge which hasn't been pulled out of the queue yet, so a path // has been found to the end node of this directed edge - const auto& opp_edgestate = edgestatus_[MATRIX_REV][target]; - EdgeStatusInfo oppedgestatus = opp_edgestate.Get(oppedge); - const auto& opp_edgelabels = edgelabel_[MATRIX_REV][target]; - uint32_t opp_predidx = opp_edgelabels[oppedgestatus.index()].predecessor(); - const BDEdgeLabel& opp_el = opp_edgelabels[oppedgestatus.index()]; + const auto& rev_edgestate = edgestatus_[MATRIX_REV][target]; + EdgeStatusInfo rev_edgestatus = rev_edgestate.Get(rev_edgeid); + const auto& rev_edgelabels = edgelabel_[MATRIX_REV][target]; + uint32_t rev_predidx = rev_edgelabels[rev_edgestatus.index()].predecessor(); + const BDEdgeLabel& rev_label = rev_edgelabels[rev_edgestatus.index()]; // Special case - common edge for source and target are both initial edges - if (pred.predecessor() == kInvalidLabel && opp_predidx == kInvalidLabel) { - // TODO: shouldnt this use seconds? why is this using cost!? - float s = std::abs(pred.cost().secs + opp_el.cost().secs - opp_el.transition_cost().cost); + if (fwd_pred.predecessor() == kInvalidLabel && rev_predidx == kInvalidLabel) { + // bail if either edge wasn't allowed (see notes in SetSources/Targets) + if (!fwd_pred.path_id() || !rev_label.path_id()) { + return; + } + + // remember: transition_cost is abused in SetSources/Targets: cost is secs, secs is length + float s = + std::abs(fwd_pred.cost().secs + rev_label.cost().secs - rev_label.transition_cost().cost); // Update best connection and set found = true. // distance computation only works with the casts. - uint32_t d = - std::abs(static_cast(pred.path_distance()) + static_cast(opp_el.path_distance()) - - static_cast(opp_el.transition_cost().secs)); - best_connection_[idx].Update(pred.edgeid(), oppedge, Cost(s, s), d); + uint32_t d = std::abs(static_cast(fwd_pred.path_distance()) + + static_cast(rev_label.path_distance()) - + static_cast(rev_label.transition_cost().secs)); + best_connection_[idx].Update(fwd_pred.edgeid(), rev_edgeid, Cost(s, s), d); best_connection_[idx].found = true; // Update status and update threshold if this is the last location // to find for this source or target UpdateStatus(source, target); } else { - float oppcost = (opp_predidx == kInvalidLabel) ? 0.f : opp_edgelabels[opp_predidx].cost().cost; - float c = pred.cost().cost + oppcost + opp_el.transition_cost().cost; + float oppcost = (rev_predidx == kInvalidLabel) ? 0.f : rev_edgelabels[rev_predidx].cost().cost; + float c = fwd_pred.cost().cost + oppcost + rev_label.transition_cost().cost; // Check if best connection if (c < best_connection_[idx].cost.cost) { - float oppsec = (opp_predidx == kInvalidLabel) ? 0.f : opp_edgelabels[opp_predidx].cost().secs; + float oppsec = (rev_predidx == kInvalidLabel) ? 0.f : rev_edgelabels[rev_predidx].cost().secs; uint32_t oppdist = - (opp_predidx == kInvalidLabel) ? 0U : opp_edgelabels[opp_predidx].path_distance(); - float s = pred.cost().secs + oppsec + opp_el.transition_cost().secs; - uint32_t d = pred.path_distance() + oppdist; + (rev_predidx == kInvalidLabel) ? 0U : rev_edgelabels[rev_predidx].path_distance(); + float s = fwd_pred.cost().secs + oppsec + rev_label.transition_cost().secs; + uint32_t d = fwd_pred.path_distance() + oppdist; // Update best connection and set a threshold - best_connection_[idx].Update(pred.edgeid(), oppedge, Cost(c, s), d); + best_connection_[idx].Update(fwd_pred.edgeid(), rev_edgeid, Cost(c, s), d); if (best_connection_[idx].max_iterations == 0) { best_connection_[idx].max_iterations = n + GetThreshold(mode_, edgelabel_[MATRIX_FORW][source].size() + @@ -815,11 +823,11 @@ void CostMatrix::CheckForwardConnections(const uint32_t source, } // setting this edge as connected if (expansion_callback_) { - auto prev_pred = pred.predecessor() == kInvalidLabel + auto prev_pred = fwd_pred.predecessor() == kInvalidLabel ? GraphId{} - : edgelabel_[MATRIX_FORW][source][pred.predecessor()].edgeid(); - expansion_callback_(graphreader, pred.edgeid(), prev_pred, "costmatrix", "c", pred.cost().secs, - pred.path_distance(), pred.cost().cost); + : edgelabel_[MATRIX_FORW][source][fwd_pred.predecessor()].edgeid(); + expansion_callback_(graphreader, fwd_pred.edgeid(), prev_pred, "costmatrix", "c", + fwd_pred.cost().secs, fwd_pred.path_distance(), fwd_pred.cost().cost); } } @@ -852,56 +860,65 @@ void CostMatrix::CheckReverseConnections(const uint32_t target, // Iterate through the sources for (auto source : sources->second) { - uint32_t idx = source * locs_count_[MATRIX_REV] + target; - if (best_connection_[idx].found) { + uint32_t source_idx = source * locs_count_[MATRIX_REV] + target; + if (best_connection_[source_idx].found) { continue; } // Update any targets whose threshold has been reached - if (best_connection_[idx].max_iterations > 0 && n > best_connection_[idx].max_iterations) { - best_connection_[idx].found = true; + if (best_connection_[source_idx].max_iterations > 0 && + n > best_connection_[source_idx].max_iterations) { + best_connection_[source_idx].found = true; continue; } // If this edge has been reached then a shortest path has been found // to the end node of this directed edge. - EdgeStatusInfo oppedgestatus = edgestatus_[MATRIX_FORW][source].Get(fwd_edgeid); - if (oppedgestatus.set() != EdgeSet::kUnreachedOrReset) { - const auto& edgelabels = edgelabel_[MATRIX_FORW][source]; - uint32_t predidx = edgelabels[oppedgestatus.index()].predecessor(); - const BDEdgeLabel& opp_el = edgelabels[oppedgestatus.index()]; + EdgeStatusInfo fwd_edgestatus = edgestatus_[MATRIX_FORW][source].Get(fwd_edgeid); + if (fwd_edgestatus.set() != EdgeSet::kUnreachedOrReset) { + const auto& fwd_edgelabels = edgelabel_[MATRIX_FORW][source]; + uint32_t fwd_predidx = fwd_edgelabels[fwd_edgestatus.index()].predecessor(); + const BDEdgeLabel& fwd_label = fwd_edgelabels[fwd_edgestatus.index()]; // Special case - common edge for source and target are both initial edges - if (rev_pred.predecessor() == kInvalidLabel && predidx == kInvalidLabel) { - // TODO: shouldnt this use seconds? why is this using cost!? - float s = std::abs(rev_pred.cost().secs + opp_el.cost().secs - opp_el.transition_cost().cost); + if (rev_pred.predecessor() == kInvalidLabel && fwd_predidx == kInvalidLabel) { + // bail if either edge wasn't allowed + if (!rev_pred.path_id() || !fwd_label.path_id()) { + return; + } + + // remember: transition_cost is abused in SetSources/Targets: cost is secs, secs is length + float s = + std::abs(rev_pred.cost().secs + fwd_label.cost().secs - fwd_label.transition_cost().cost); // Update best connection and set found = true. // distance computation only works with the casts. uint32_t d = std::abs(static_cast(rev_pred.path_distance()) + - static_cast(opp_el.path_distance()) - - static_cast(opp_el.transition_cost().secs)); - best_connection_[idx].Update(fwd_edgeid, rev_pred.edgeid(), Cost(s, s), d); - best_connection_[idx].found = true; + static_cast(fwd_label.path_distance()) - + static_cast(fwd_label.transition_cost().secs)); + best_connection_[source_idx].Update(fwd_edgeid, rev_pred.edgeid(), Cost(s, s), d); + best_connection_[source_idx].found = true; // Update status and update threshold if this is the last location // to find for this source or target UpdateStatus(source, target); } else { - float oppcost = (predidx == kInvalidLabel) ? 0 : edgelabels[predidx].cost().cost; - float c = rev_pred.cost().cost + oppcost + opp_el.transition_cost().cost; + float oppcost = (fwd_predidx == kInvalidLabel) ? 0 : fwd_edgelabels[fwd_predidx].cost().cost; + float c = rev_pred.cost().cost + oppcost + fwd_label.transition_cost().cost; // Check if best connection - if (c < best_connection_[idx].cost.cost) { - float oppsec = (predidx == kInvalidLabel) ? 0 : edgelabels[predidx].cost().secs; - uint32_t oppdist = (predidx == kInvalidLabel) ? 0 : edgelabels[predidx].path_distance(); - float s = rev_pred.cost().secs + oppsec + opp_el.transition_cost().secs; - uint32_t d = rev_pred.path_distance() + oppdist; + if (c < best_connection_[source_idx].cost.cost) { + float fwd_sec = + (fwd_predidx == kInvalidLabel) ? 0 : fwd_edgelabels[fwd_predidx].cost().secs; + uint32_t fwd_dist = + (fwd_predidx == kInvalidLabel) ? 0 : fwd_edgelabels[fwd_predidx].path_distance(); + float s = rev_pred.cost().secs + fwd_sec + fwd_label.transition_cost().secs; + uint32_t d = rev_pred.path_distance() + fwd_dist; // Update best connection and set a threshold - best_connection_[idx].Update(fwd_edgeid, rev_pred.edgeid(), Cost(c, s), d); - if (best_connection_[idx].max_iterations == 0) { - best_connection_[idx].max_iterations = + best_connection_[source_idx].Update(fwd_edgeid, rev_pred.edgeid(), Cost(c, s), d); + if (best_connection_[source_idx].max_iterations == 0) { + best_connection_[source_idx].max_iterations = n + GetThreshold(mode_, edgelabel_[MATRIX_FORW][source].size() + edgelabel_[MATRIX_REV][target].size()); } @@ -1001,16 +1018,16 @@ void CostMatrix::SetSources(GraphReader& graphreader, // TODO: assumes 1m/s which is a maximum penalty this could vary per costing model cost.cost += edge.distance(); - // Store the edge cost and length in the transition cost (so we can - // recover the full length and cost for cases where origin and - // destination are on the same edge - Cost ec(std::round(edgecost.secs), static_cast(directededge->length())); - // Set the initial not_thru flag to false. There is an issue with not_thru // flags on small loops. Set this to false here to override this for now. + // 2 adjustments related only to properly handle trivial routes: + // - "transition_cost" is used to store the traversed secs & length + // - "path_id" is used to store whether the edge is even allowed (e.g. no oneway) + Cost ec(std::round(edgecost.secs), static_cast(directededge->length())); BDEdgeLabel edge_label(kInvalidLabel, edgeid, oppedge, directededge, cost, mode_, ec, d, false, true, static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn, -1, 0, + InternalTurn::kNoTurn, -1, + static_cast(costing_->Allowed(directededge, tile)), directededge->destonly() || (costing_->is_hgv() && directededge->destonly_hgv())); edge_label.set_not_thru(false); @@ -1064,11 +1081,12 @@ void CostMatrix::SetTargets(baldr::GraphReader& graphreader, const DirectedEdge* directededge = tile->directededge(edgeid); // Get the opposing directed edge, continue if we cannot get it - GraphId opp_edge_id = graphreader.GetOpposingEdgeId(edgeid); + graph_tile_ptr opp_tile = tile; + GraphId opp_edge_id = graphreader.GetOpposingEdgeId(edgeid, opp_tile); if (!opp_edge_id.Is_Valid()) { continue; } - const DirectedEdge* opp_dir_edge = graphreader.GetOpposingEdge(edgeid); + const DirectedEdge* opp_dir_edge = graphreader.GetOpposingEdge(edgeid, opp_tile); // Get cost. Get distance along the remainder of this edge. // Use the directed edge for costing, as this is the forward direction @@ -1083,16 +1101,16 @@ void CostMatrix::SetTargets(baldr::GraphReader& graphreader, // TODO: assumes 1m/s which is a maximum penalty this could vary per costing model cost.cost += edge.distance(); - // Store the edge cost and length in the transition cost (so we can - // recover the full length and cost for cases where origin and - // destination are on the same edge - Cost ec(std::round(edgecost.secs), static_cast(directededge->length())); - // Set the initial not_thru flag to false. There is an issue with not_thru // flags on small loops. Set this to false here to override this for now. + // 2 adjustments related only to properly handle trivial routes: + // - "transition_cost" is used to store the traversed secs & length + // - "path_id" is used to store whether the opp edge is even allowed (e.g. no oneway) + Cost ec(std::round(edgecost.secs), static_cast(directededge->length())); BDEdgeLabel edge_label(kInvalidLabel, opp_edge_id, edgeid, opp_dir_edge, cost, mode_, ec, d, false, true, static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn, -1, 0, + InternalTurn::kNoTurn, -1, + static_cast(costing_->Allowed(opp_dir_edge, opp_tile)), opp_dir_edge->destonly() || (costing_->is_hgv() && opp_dir_edge->destonly_hgv())); edge_label.set_not_thru(false); @@ -1103,8 +1121,7 @@ void CostMatrix::SetTargets(baldr::GraphReader& graphreader, uint32_t idx = edgelabel_[MATRIX_REV][index].size(); edgelabel_[MATRIX_REV][index].push_back(std::move(edge_label)); adjacency_[MATRIX_REV][index].add(idx); - edgestatus_[MATRIX_REV][index].Set(opp_edge_id, EdgeSet::kUnreachedOrReset, idx, - graphreader.GetGraphTile(opp_edge_id)); + edgestatus_[MATRIX_REV][index].Set(opp_edge_id, EdgeSet::kUnreachedOrReset, idx, opp_tile); (*targets_)[opp_edge_id].push_back(index); } index++; diff --git a/test/gurka/gurka.h b/test/gurka/gurka.h index faaa341340..1d3320b868 100644 --- a/test/gurka/gurka.h +++ b/test/gurka/gurka.h @@ -26,6 +26,7 @@ #include "tyr/actor.h" #include "tyr/serializers.h" +#include #include #include diff --git a/test/gurka/test_matrix.cc b/test/gurka/test_matrix.cc index 786994bf37..b0e3cfcf88 100644 --- a/test/gurka/test_matrix.cc +++ b/test/gurka/test_matrix.cc @@ -766,6 +766,7 @@ TEST(StandAlone, MatrixSecondPass) { EXPECT_FALSE(api.matrix().second_pass(1)); EXPECT_GT(api.matrix().times(2), 0.f); EXPECT_TRUE(api.matrix().second_pass(2)); + EXPECT_GT(api.matrix().distances(2), api.matrix().distances(1)); EXPECT_GT(api.matrix().times(2), api.matrix().times(1)); // I -> I & K -> K shouldn't be processed a second time either @@ -774,3 +775,32 @@ TEST(StandAlone, MatrixSecondPass) { EXPECT_TRUE(api.info().warnings(0).description().find('2') != std::string::npos); } } + +TEST(StandAlone, CostMatrixTrivialRoutes) { + const std::string ascii_map = R"( + A---B--2->-1--C---D + | | + | | + E--3---4--F + )"; + const gurka::ways ways = { + {"AB", {{"highway", "residential"}}}, {"BC", {{"highway", "residential"}, {"oneway", "yes"}}}, + {"CD", {{"highway", "residential"}}}, {"BE", {{"highway", "residential"}}}, + {"EF", {{"highway", "residential"}}}, {"FC", {{"highway", "residential"}}}, + }; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + auto map = + gurka::buildtiles(layout, ways, {}, {}, VALHALLA_BUILD_DIR "test/data/costmatrix_trivial"); + + // test the against-oneway case + { + auto matrix = gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, "auto"); + EXPECT_EQ(matrix.matrix().distances(0), 2200); + } + + // test the normal trivial case + { + auto matrix = gurka::do_action(valhalla::Options::sources_to_targets, map, {"3"}, {"4"}, "auto"); + EXPECT_EQ(matrix.matrix().distances(0), 400); + } +} diff --git a/test/test.h b/test/test.h index 4f8c815f49..a7f9678e4e 100644 --- a/test/test.h +++ b/test/test.h @@ -23,6 +23,7 @@ #include #include +#include #include namespace test { diff --git a/valhalla/sif/edgelabel.h b/valhalla/sif/edgelabel.h index c49ee5504d..471cacc836 100644 --- a/valhalla/sif/edgelabel.h +++ b/valhalla/sif/edgelabel.h @@ -441,7 +441,7 @@ class EdgeLabel { /** * Derived label class used for recosting paths within the LabelCallback. - * transition_cost is added to the label for use when recoting a path. + * transition_cost is added to the label for use when recosting a path. */ class PathEdgeLabel : public EdgeLabel { public: From 40ce71b759b1ea7aafff230a7622a8f085c6ef52 Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Wed, 13 Mar 2024 15:38:27 -0400 Subject: [PATCH 046/198] use a singleton pattern to register the gdal driver once in one place (#4628) --- CHANGELOG.md | 1 + CMakeLists.txt | 17 +++++++++++------ src/tyr/CMakeLists.txt | 6 ++---- src/tyr/isochrone_serializer.cc | 19 ++++++++++++++++++- src/valhalla_run_isochrone.cc | 9 --------- src/valhalla_service.cc | 8 -------- test/isochrone.cc | 3 --- 7 files changed, 32 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49dc12b027..adbe9dcc43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ * FIXED: Use helper function for only parsing out names from DirectedEdge when populating intersecting edges [#4604](https://github.com/valhalla/valhalla/pull/4604) * FIXED: Osmnode size reduction: Fixed excessive disk space for planet build [#4605](https://github.com/valhalla/valhalla/pull/4605) * FIXED: CostMatrix for trivial routes with oneways [#4626](https://github.com/valhalla/valhalla/pull/4626) + * FIXED: some entry points to creating geotiff isochrones output did not register the geotiff driver before attempting to use it [#4628](https://github.com/valhalla/valhalla/pull/4628) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1f238201a..209e06d3d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ option(ENABLE_SINGLE_FILES_WERROR "Convert compiler warnings to errors for singl option(PREFER_EXTERNAL_DEPS "Whether to use internally vendored headers or find the equivalent external package" OFF) # useful to workaround issues likes this https://stackoverflow.com/questions/24078873/cmake-generated-xcode-project-wont-compile option(ENABLE_STATIC_LIBRARY_MODULES "If ON builds Valhalla modules as STATIC library targets" OFF) -option(ENABLE_GDAL "Whether to include GDAL; currently only used for raster serialization of isotile grid" OFF) +option(ENABLE_GDAL "Whether to include GDAL; currently only used for raster serialization of isotile grid" ON) set(LOGGING_LEVEL "" CACHE STRING "Logging level, default is INFO") set_property(CACHE LOGGING_LEVEL PROPERTY STRINGS "NONE;ALL;ERROR;WARN;INFO;DEBUG;TRACE") @@ -191,12 +191,17 @@ else() message(FATAL_ERROR "Required target protobuf::libprotobuf-lite or protobuf::libprotobuf is not defined") endif() -# gdal -set(gdal_target "") +# unless you said you didnt want gdal we try to turn it on, if we cant we tell you +set(GDAL_TARGET "") if (ENABLE_GDAL) - find_package(GDAL REQUIRED) - add_compile_definitions(ENABLE_GDAL) - set(gdal_target GDAL::GDAL) + find_package(GDAL QUIET) + if (GDAL_FOUND) + set(GDAL_TARGET GDAL::GDAL) + add_compile_definitions(ENABLE_GDAL) + message(STATUS "Gdal support is enabled") + else() + message(WARNING "Unable to enable gdal support") + endif() endif() set(CMAKE_FIND_PACKAGE_PREFER_CONFIG OFF) diff --git a/src/tyr/CMakeLists.txt b/src/tyr/CMakeLists.txt index 9c0b22dee2..571d2b0de1 100644 --- a/src/tyr/CMakeLists.txt +++ b/src/tyr/CMakeLists.txt @@ -20,8 +20,6 @@ set(system_includes ${date_include_dir} $<$:${dirent_include_dir}>) - - valhalla_module(NAME tyr SOURCES ${sources} @@ -46,6 +44,6 @@ valhalla_module(NAME tyr valhalla::odin valhalla::proto ${valhalla_protobuf_targets} - Boost::boost - ${gdal_target} + Boost::boost + ${GDAL_TARGET} ) diff --git a/src/tyr/isochrone_serializer.cc b/src/tyr/isochrone_serializer.cc index a1b504158e..22943370e4 100644 --- a/src/tyr/isochrone_serializer.cc +++ b/src/tyr/isochrone_serializer.cc @@ -16,6 +16,23 @@ using namespace valhalla::baldr::json; namespace { + +// allows us to only ever register the driver once per process without having to put it +// in every executable that might call into this code +struct gdal_singleton_t { + static const gdal_singleton_t& get() { + static const gdal_singleton_t instance; + return instance; + } + +private: + gdal_singleton_t() { +#ifdef ENABLE_GDAL + GDALRegister_GTiff(); +#endif + } +}; + using rgba_t = std::tuple; using namespace valhalla; @@ -169,7 +186,7 @@ std::string serializeGeoTIFF(Api& request, const std::shared_ptrGetDriverByName("GTiff"); auto geotiff_dataset = diff --git a/src/valhalla_run_isochrone.cc b/src/valhalla_run_isochrone.cc index a96394caf0..ac61bedff9 100644 --- a/src/valhalla_run_isochrone.cc +++ b/src/valhalla_run_isochrone.cc @@ -20,10 +20,6 @@ #include "tyr/serializers.h" #include "worker.h" -#ifdef ENABLE_GDAL -#include -#endif - using namespace valhalla; using namespace valhalla::midgard; using namespace valhalla::baldr; @@ -84,11 +80,6 @@ int main(int argc, char* argv[]) { Api request; ParseApi(json_str, valhalla::Options::isochrone, request); -#ifdef ENABLE_GDAL - if (request.options().format() == Options_Format_geotiff) { - GDALRegister_GTiff(); - } -#endif auto& options = *request.mutable_options(); // Get the denoise parameter diff --git a/src/valhalla_service.cc b/src/valhalla_service.cc index 4defc13195..95c753259d 100644 --- a/src/valhalla_service.cc +++ b/src/valhalla_service.cc @@ -17,10 +17,6 @@ using namespace prime_server; #endif -#ifdef ENABLE_GDAL -#include -#endif - #include "config.h" #include "midgard/logging.h" @@ -43,10 +39,6 @@ int main(int argc, char** argv) { } #endif -#ifdef ENABLE_GDAL - GDALRegister_GTiff(); -#endif - // config file // TODO: validate the config std::string config_file(argv[1]); diff --git a/test/isochrone.cc b/test/isochrone.cc index 1d890b15b9..667402fb6b 100644 --- a/test/isochrone.cc +++ b/test/isochrone.cc @@ -355,7 +355,6 @@ void check_raster_edges(size_t x, size_t y, uint16_t* data) { } TEST(Isochrones, test_geotiff_output_distance) { - GDALRegister_GTiff(); loki_worker_t loki_worker(cfg); thor_worker_t thor_worker(cfg); @@ -401,7 +400,6 @@ TEST(Isochrones, test_geotiff_output_distance) { } TEST(Isochrones, test_geotiff_output_time) { - GDALRegister_GTiff(); loki_worker_t loki_worker(cfg); thor_worker_t thor_worker(cfg); @@ -448,7 +446,6 @@ TEST(Isochrones, test_geotiff_output_time) { // test request with two metrics TEST(Isochrones, test_geotiff_output_time_distance) { - GDALRegister_GTiff(); loki_worker_t loki_worker(cfg); thor_worker_t thor_worker(cfg); From 28eaf7a827715c682cfbc5634eb1d4bd3ca1fddd Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Wed, 13 Mar 2024 23:40:39 +0100 Subject: [PATCH 047/198] "soft" ignore restrictions (#4606) Co-authored-by: Nils --- CHANGELOG.md | 3 +- docs/docs/api/turn-by-turn/api-reference.md | 4 + proto/options.proto | 1 + src/sif/autocost.cc | 8 +- src/sif/bicyclecost.cc | 4 +- src/sif/dynamiccost.cc | 5 + src/sif/motorcyclecost.cc | 4 +- src/sif/motorscootercost.cc | 4 +- src/sif/truckcost.cc | 4 +- test/gurka/test_closure_penalty.cc | 2 + test/gurka/test_conditional_restrictions.cc | 5 +- test/gurka/test_ignore_restrictions.cc | 180 ++++++++++++++++++++ test/gurka/test_truck_restrictions.cc | 3 +- valhalla/sif/dynamiccost.h | 34 +++- 14 files changed, 242 insertions(+), 19 deletions(-) create mode 100644 test/gurka/test_ignore_restrictions.cc diff --git a/CHANGELOG.md b/CHANGELOG.md index adbe9dcc43..fba618e7bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ * FIXED: Aggregation updates: update opposing local idx after aggregating the edges, added classification check for aggregation, and shortcut length changes [#4570](https://github.com/valhalla/valhalla/pull/4570) * FIXED: Use helper function for only parsing out names from DirectedEdge when populating intersecting edges [#4604](https://github.com/valhalla/valhalla/pull/4604) * FIXED: Osmnode size reduction: Fixed excessive disk space for planet build [#4605](https://github.com/valhalla/valhalla/pull/4605) + * FIXED: Conflict with signinfo's temporary linguistic node sequence file caused test failures. [#4625](https://github.com/valhalla/valhalla/pull/4625) * FIXED: CostMatrix for trivial routes with oneways [#4626](https://github.com/valhalla/valhalla/pull/4626) * FIXED: some entry points to creating geotiff isochrones output did not register the geotiff driver before attempting to use it [#4628](https://github.com/valhalla/valhalla/pull/4628) * **Enhancement** @@ -98,7 +99,7 @@ * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) * ADDED: isochrone proper polygon support & pbf output for isochrone [#4575](https://github.com/valhalla/valhalla/pull/4575) * ADDED: return isotile grid as geotiff [#4594](https://github.com/valhalla/valhalla/pull/4594) - * FIXED: Conflict with signinfo's temporary linguistic node sequence file caused test failures. [#4625](https://github.com/valhalla/valhalla/pull/4625) + * ADDED: `ignore_non_vehicular_restrictions` parameter for truck costing [#4606](https://github.com/valhalla/valhalla/pull/4606) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 1ee45e701e..1676f3895c 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -121,6 +121,10 @@ These options are available for `auto`, `bus`, and `truck` costing methods. | `fixed_speed` | Fixed speed the vehicle can go. Used to override the calculated speed. Can be useful if speed of vehicle is known. `fixed_speed` must be between 1 and 252 KPH. The default value is 0 KPH which disables fixed speed and falls back to the standard calculated speed based on the road attribution. | | `ignore_closures` | If set to `true`, ignores all closures, marked due to live traffic closures, during routing. **Note:** This option cannot be set if `location.search_filter.exclude_closures` is also specified in the request and will return an error if it is | | `closure_factor` | A factor that penalizes the cost when traversing a closed edge (eg: if `search_filter.exclude_closures` is `false` for origin and/or destination location and the route starts/ends on closed edges). Its value can range from `1.0` - don't penalize closed edges, to `10.0` - apply high cost penalty to closed edges. Default value is `9.0`. **Note:** This factor is applicable only for motorized modes of transport, i.e `auto`, `motorcycle`, `motor_scooter`, `bus`, `truck` & `taxi`. | +| `ignore_restrictions` | If set to `true`, ignores any restrictions (e.g. turn/dimensional/conditional restrictions). Especially useful for matching GPS traces to the road network regardless of restrictions. Default is `false`. | +| `ignore_non_vehicular_restrictions` | Similar to `ignore_restrictions`, but will respect restrictions that impact vehicle safety, such as weight and size restrictions. | +| `ignore_access` | Will ignore mode-specific access tags. Especially useful for matching GPS traces to the road network regardless of restrictions. Default is `false`. | +| `ignore_closures` | Will ignore traffic closures. Default is `false`. | ###### Other costing options The following options are available for `auto`, `bus`, `taxi`, and `truck` costing methods. diff --git a/proto/options.proto b/proto/options.proto index 122db3b360..79549f216a 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -312,6 +312,7 @@ message Costing { uint32 axle_count = 81; float use_lit = 82; bool disable_hierarchy_pruning = 83; + bool ignore_non_vehicular_restrictions = 84; } oneof has_options { diff --git a/src/sif/autocost.cc b/src/sif/autocost.cc index 34662ab292..93a3dbd648 100644 --- a/src/sif/autocost.cc +++ b/src/sif/autocost.cc @@ -421,7 +421,7 @@ bool AutoCost::Allowed(const baldr::DirectedEdge* edge, // a not thru region and a heading selected an edge entering the // region. if (!IsAccessible(edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_restrictions_) || + ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_turn_restrictions_) || edge->surface() == Surface::kImpassable || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || (pred.closure_pruning() && IsClosed(edge, tile)) || @@ -446,7 +446,7 @@ bool AutoCost::AllowedReverse(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || @@ -808,7 +808,7 @@ bool BusCost::AllowedReverse(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || @@ -987,7 +987,7 @@ bool TaxiCost::AllowedReverse(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || diff --git a/src/sif/bicyclecost.cc b/src/sif/bicyclecost.cc index 887b312d87..46a07b54d6 100644 --- a/src/sif/bicyclecost.cc +++ b/src/sif/bicyclecost.cc @@ -545,7 +545,7 @@ bool BicycleCost::Allowed(const baldr::DirectedEdge* edge, if (!IsAccessible(edge) || edge->is_shortcut() || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx() && pred.mode() == TravelMode::kBicycle) || - (!ignore_restrictions_ && (pred.restrictions() & (1 << edge->localedgeidx()))) || + (!ignore_turn_restrictions_ && (pred.restrictions() & (1 << edge->localedgeidx()))) || IsUserAvoidEdge(edgeid)) { return false; } @@ -582,7 +582,7 @@ bool BicycleCost::AllowedReverse(const baldr::DirectedEdge* edge, opp_edge->use() == Use::kPlatformConnection || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx() && pred.mode() == TravelMode::kBicycle) || - (!ignore_restrictions_ && (opp_edge->restrictions() & (1 << pred.opp_local_idx()))) || + (!ignore_turn_restrictions_ && (opp_edge->restrictions() & (1 << pred.opp_local_idx()))) || IsUserAvoidEdge(opp_edgeid)) { return false; } diff --git a/src/sif/dynamiccost.cc b/src/sif/dynamiccost.cc index 5416fb7de9..c92ec6e33a 100644 --- a/src/sif/dynamiccost.cc +++ b/src/sif/dynamiccost.cc @@ -151,6 +151,9 @@ DynamicCost::DynamicCost(const Costing& costing, closure_factor_(kDefaultClosureFactor), flow_mask_(kDefaultFlowMask), shortest_(costing.options().shortest()), ignore_restrictions_(costing.options().ignore_restrictions()), + ignore_non_vehicular_restrictions_(costing.options().ignore_non_vehicular_restrictions()), + ignore_turn_restrictions_(costing.options().ignore_restrictions() || + costing.options().ignore_non_vehicular_restrictions()), ignore_oneways_(costing.options().ignore_oneways()), ignore_access_(costing.options().ignore_access()), ignore_closures_(costing.options().ignore_closures()), @@ -389,6 +392,8 @@ void ParseBaseCostOptions(const rapidjson::Value& json, JSON_PBF_DEFAULT(co, false, json, "/ignore_oneways", ignore_oneways); JSON_PBF_DEFAULT(co, false, json, "/ignore_access", ignore_access); JSON_PBF_DEFAULT(co, false, json, "/ignore_closures", ignore_closures); + JSON_PBF_DEFAULT_V2(co, false, json, "/ignore_non_vehicular_restrictions", + ignore_non_vehicular_restrictions); // shortest JSON_PBF_DEFAULT(co, false, json, "/shortest", shortest); diff --git a/src/sif/motorcyclecost.cc b/src/sif/motorcyclecost.cc index 43388c91d3..1f716bd499 100644 --- a/src/sif/motorcyclecost.cc +++ b/src/sif/motorcyclecost.cc @@ -355,7 +355,7 @@ bool MotorcycleCost::Allowed(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_restrictions_) || + ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_turn_restrictions_) || (edge->surface() > kMinimumMotorcycleSurface) || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || (pred.closure_pruning() && IsClosed(edge, tile))) { @@ -379,7 +379,7 @@ bool MotorcycleCost::AllowedReverse(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || (opp_edge->surface() > kMinimumMotorcycleSurface) || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile))) { diff --git a/src/sif/motorscootercost.cc b/src/sif/motorscootercost.cc index 2ac26e8c13..7fe8270f05 100644 --- a/src/sif/motorscootercost.cc +++ b/src/sif/motorscootercost.cc @@ -376,7 +376,7 @@ bool MotorScooterCost::Allowed(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_restrictions_) || + ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_turn_restrictions_) || (edge->surface() > kMinimumScooterSurface) || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || (pred.closure_pruning() && IsClosed(edge, tile))) { @@ -400,7 +400,7 @@ bool MotorScooterCost::AllowedReverse(const baldr::DirectedEdge* edge, // Check access, U-turn, and simple turn restriction. // Allow U-turns at dead-end nodes. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || (opp_edge->surface() > kMinimumScooterSurface) || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile))) { diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index 52eec75ab2..94b8b4ba42 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -439,7 +439,7 @@ inline bool TruckCost::Allowed(const baldr::DirectedEdge* edge, uint8_t& restriction_idx) const { // Check access, U-turn, and simple turn restriction. if (!IsAccessible(edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_restrictions_) || + ((pred.restrictions() & (1 << edge->localedgeidx())) && (!ignore_turn_restrictions_)) || edge->surface() == Surface::kImpassable || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly_hgv()) || (pred.closure_pruning() && IsClosed(edge, tile)) || @@ -463,7 +463,7 @@ bool TruckCost::AllowedReverse(const baldr::DirectedEdge* edge, uint8_t& restriction_idx) const { // Check access, U-turn, and simple turn restriction. if (!IsAccessible(opp_edge) || (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx()) || - ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_restrictions_) || + ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly_hgv()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || diff --git a/test/gurka/test_closure_penalty.cc b/test/gurka/test_closure_penalty.cc index a98a78e082..437a84b4f9 100644 --- a/test/gurka/test_closure_penalty.cc +++ b/test/gurka/test_closure_penalty.cc @@ -1,3 +1,5 @@ +#include + #include "gurka.h" #include "test.h" diff --git a/test/gurka/test_conditional_restrictions.cc b/test/gurka/test_conditional_restrictions.cc index d161168d6b..390c3a48d1 100644 --- a/test/gurka/test_conditional_restrictions.cc +++ b/test/gurka/test_conditional_restrictions.cc @@ -97,8 +97,9 @@ class ConditionalRestrictions : public ::testing::Test { }; const auto layout = gurka::detail::map_to_coordinates(ascii_map, grid_size_meters); - map = gurka::buildtiles(layout, ways, {}, {}, "test/data/conditional_restrictions", - {{"mjolnir.timezone", {"test/data/tz.sqlite"}}}); + map = gurka::buildtiles(layout, ways, {}, {}, + VALHALLA_BUILD_DIR "test/data/conditional_restrictions", + {{"mjolnir.timezone", {VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}}); } }; diff --git a/test/gurka/test_ignore_restrictions.cc b/test/gurka/test_ignore_restrictions.cc new file mode 100644 index 0000000000..6ccf458cba --- /dev/null +++ b/test/gurka/test_ignore_restrictions.cc @@ -0,0 +1,180 @@ + +#include "gurka.h" +#include "test.h" +#include + +using namespace valhalla; + +std::string get_access_mode(const std::string& costing_mode) { + if (costing_mode == "auto") { + return "motorcar"; + } else if (costing_mode == "truck") { + return "hgv"; + } else if (costing_mode == "motorcycle") { + return "motorcycle"; + } else if (costing_mode == "taxi") { + return "taxi"; + } else if (costing_mode == "bus") { + return "bus"; + } else if (costing_mode == "motor_scooter") { + return "moped"; + } else if (costing_mode == "bicycle") { + return "bicycle"; + } + + throw std::runtime_error("unexpected costing mode " + costing_mode + "."); +} + +class CommonRestrictionTest : public ::testing::TestWithParam { +protected: + static gurka::nodelayout layout; + + static void SetUpTestSuite() { + constexpr double gridsize = 500; + + const std::string map = R"( + A----------B-----C----D + | + E + | + | + F + )"; + + layout = gurka::detail::map_to_coordinates(map, gridsize); + } +}; +gurka::nodelayout CommonRestrictionTest::layout = {}; + +TEST_P(CommonRestrictionTest, IgnoreCommonRestrictions) { + const std::string& costing = GetParam(); + const gurka::ways ways = { + {"AB", {{"highway", "secondary"}}}, + {"BC", {{"highway", "secondary"}}}, + {"CD", {{"highway", "secondary"}}}, + {"AE", {{"highway", "secondary"}}}, + {"EF", + {{"highway", "secondary"}, {get_access_mode(costing) + ":conditional", "no @ (09:00-18:00)"}}}, + }; + const gurka::relations relations = {{{ + {gurka::way_member, "AB", "from"}, + {gurka::way_member, "BC", "to"}, + {gurka::node_member, "B", "via"}, + }, + {{"type", "restriction"}, {"restriction", "no_straight_on"}}}}; + gurka::map map = + gurka::buildtiles(layout, ways, {}, relations, "test/data/ignore_non_vehicular_restrictions", + {{"mjolnir.timezone", {VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}}); + // first, route through turn restriction, should fail... + try { + valhalla::Api route = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, costing, {}); + FAIL() << "Expected valhalla_exception_t."; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + } + + // ...but succeed with ignore_non_vehicular_restrictions + valhalla::Api route = + gurka::do_action(valhalla::Options::route, map, {"A", "D"}, costing, + {{"/costing_options/" + costing + "/ignore_non_vehicular_restrictions", "1"}}); + gurka::assert::raw::expect_path(route, {"AB", "BC", "CD"}); + + // second, route through time based access restrictions, should fail... + try { + valhalla::Api route = + gurka::do_action(valhalla::Options::route, map, {"A", "F"}, costing, + {{"/date_time/type", "1"}, {"/date_time/value", "2020-10-10T13:00"}}); + FAIL() << "Expected route to fail."; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected different error code."; + } + + //...but succeed with ignore_non_vehicular_restrictions + valhalla::Api route_succeed = + gurka::do_action(valhalla::Options::route, map, {"A", "F"}, costing, + {{"/costing_options/" + costing + "/ignore_non_vehicular_restrictions", "1"}, + {"/date_time/type", "1"}, + {"/date_time/value", "2020-10-10T13:00"}}); + gurka::assert::raw::expect_path(route_succeed, {"AE", "EF"}); +} + +// check that dimensional restrictions are not affected +TEST_P(CommonRestrictionTest, IgnoreCommonRestrictionsFail) { + const std::string& costing = GetParam(); + + if (costing == "motorcycle" || costing == "bicycle" || costing == "motor_scooter") + return; // no height restrictions for these + + const gurka::ways ways = { + {"AB", {{"highway", "secondary"}}}, {"BC", {{"highway", "secondary"}, {"maxheight", "2.5"}}}, + {"CD", {{"highway", "secondary"}}}, {"AE", {{"highway", "secondary"}}}, + {"EF", {{"highway", "secondary"}}}, + }; + gurka::map map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/ignore_non_vehicular_restrictions", + {{"mjolnir.timezone", {VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}}); + // should fail, too low + try { + valhalla::Api route = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, costing, + {{"/costing_options/" + costing + "/height", "3"}}); + FAIL() << "Expected valhalla_exception_t."; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + } + + // still too low + try { + valhalla::Api route = + gurka::do_action(valhalla::Options::route, map, {"A", "D"}, costing, + {{"/costing_options/" + costing + "/ignore_non_vehicular_restrictions", "1"}, + {"/costing_options/" + costing + "/height", "3"}}); + FAIL() << "Expected valhalla_exception_t."; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + } +} +INSTANTIATE_TEST_SUITE_P( + CommonRestrictionsTest, + CommonRestrictionTest, + ::testing::Values("auto", "truck", "motorcycle", "taxi", "bus", "bicycle", "motor_scooter")); + +// make sure truck weight restrictions are not affected by the request parameter +TEST(CommonRestrictionsFail, Truck) { + + constexpr double gridsize = 500; + + const std::string ascii_map = R"( + A----------B-----C----D + )"; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + const gurka::ways ways = {{"AB", {{"highway", "residential"}}}, + {"BC", {{"highway", "residential"}, {"maxheight", "2.5"}}}, + {"CD", {{"highway", "residential"}}}}; + + gurka::map map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/ignore_non_vehicular_restrictions_truck", + {{"mjolnir.timezone", {VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}}); + + // too long + try { + valhalla::Api route = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "truck", + {{"/costing_options/truck/height", "3"}}); + + FAIL() << "Expected valhalla_exception_t."; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected to fail with a different error code."; + } + + // ...still too long + try { + valhalla::Api route = + gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "truck", + {{"/costing_options/truck/ignore_non_vehicular_restrictions", "1"}, + {"/costing_options/truck/height", "3"}}); + FAIL() << "Expected no route to be found."; + + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected to fail with a different error code."; + } +} \ No newline at end of file diff --git a/test/gurka/test_truck_restrictions.cc b/test/gurka/test_truck_restrictions.cc index 7368bb8dbf..9c11d099f4 100644 --- a/test/gurka/test_truck_restrictions.cc +++ b/test/gurka/test_truck_restrictions.cc @@ -49,6 +49,7 @@ TEST_P(TruckRestrictionTest, NotAllowed) { try { gurka::do_action(Options::route, map, {"A", "D"}, "truck", {{"/costing_options/truck/" + option, v}}); + FAIL() << "Expected no path to be found"; } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { FAIL() << "Expected valhalla_exception_t."; }; @@ -65,7 +66,7 @@ INSTANTIATE_TEST_SUITE_P(TruckRestrictions, ::testing::Values(std::pair{"height", "6"}, std::pair{"width", "4"}, std::pair{"length", "30"}, - std::pair{"hazmat", "true"}, + std::pair{"hazmat", "1"}, std::pair{"axle_load", "11"}, std::pair{"axle_count", "10"})); diff --git a/valhalla/sif/dynamiccost.h b/valhalla/sif/dynamiccost.h index fb7ad7eaf0..441a64c1c5 100644 --- a/valhalla/sif/dynamiccost.h +++ b/valhalla/sif/dynamiccost.h @@ -87,6 +87,26 @@ : def)); \ } +/** + * same as above, but for costing options without pbf's awful oneof + * + * @param costing_options pointer to protobuf costing options object + * @param def the default value which is used when neither json nor pbf is provided + * @param json rapidjson value object which should contain user provided costing options + * @param json_key the json key to use to pull a user provided value out of the json + * @param option_name the name of the option will be set on the costing options object + */ + +#define JSON_PBF_DEFAULT_V2(costing_options, def, json, json_key, option_name) \ + { \ + costing_options->set_##option_name( \ + rapidjson::get::type>::type>(json, json_key, \ + costing_options->option_name() \ + ? costing_options->option_name() \ + : def)); \ + } + using namespace valhalla::midgard; namespace valhalla { @@ -427,6 +447,9 @@ class DynamicCost { thor::EdgeStatus* edgestatus = nullptr, const uint64_t current_time = 0, const uint32_t tz_index = 0) const { + if (ignore_turn_restrictions_) + return false; + // Lambda to get the next predecessor EdgeLabel (that is not a transition) auto next_predecessor = [&edge_labels](const EdgeLabel* label) { // Get the next predecessor - make sure it is valid. Continue to get @@ -596,9 +619,10 @@ class DynamicCost { const auto& restriction = restrictions[i]; // Compare the time to the time-based restrictions baldr::AccessType access_type = restriction.type(); - if (access_type == baldr::AccessType::kTimedAllowed || - access_type == baldr::AccessType::kTimedDenied || - access_type == baldr::AccessType::kDestinationAllowed) { + if (!ignore_non_vehicular_restrictions_ && + (access_type == baldr::AccessType::kTimedAllowed || + access_type == baldr::AccessType::kTimedDenied || + access_type == baldr::AccessType::kDestinationAllowed)) { // TODO: if(i > baldr::kInvalidRestriction) LOG_ERROR("restriction index overflow"); restriction_idx = static_cast(i); @@ -997,6 +1021,10 @@ class DynamicCost { bool shortest_; bool ignore_restrictions_{false}; + bool ignore_non_vehicular_restrictions_{false}; + // not a requestion parameter, it's true if either ignore_restrictions_ or + // ignore_non_vehicular_restrictions_ is true + bool ignore_turn_restrictions_{false}; bool ignore_oneways_{false}; bool ignore_access_{false}; bool ignore_closures_{false}; From 663e04eee755a6d4368f2b266347cbd3e75c0449 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 14 Mar 2024 03:36:24 +0100 Subject: [PATCH 048/198] add libgdal to final docker image (#4629) --- CHANGELOG.md | 1 + Dockerfile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fba618e7bf..ba20431916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ * FIXED: Conflict with signinfo's temporary linguistic node sequence file caused test failures. [#4625](https://github.com/valhalla/valhalla/pull/4625) * FIXED: CostMatrix for trivial routes with oneways [#4626](https://github.com/valhalla/valhalla/pull/4626) * FIXED: some entry points to creating geotiff isochrones output did not register the geotiff driver before attempting to use it [#4628](https://github.com/valhalla/valhalla/pull/4628) + * FIXED: libgdal wasn't installed in docker image, so it never worked in docker [#4629](https://github.com/valhalla/valhalla/pull/4629) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/Dockerfile b/Dockerfile index 4e14a12aec..ebcdd52605 100644 --- a/Dockerfile +++ b/Dockerfile @@ -73,7 +73,7 @@ COPY --from=builder /usr/lib/python3/dist-packages/valhalla/* /usr/lib/python3/d # install all the posix locales that we support RUN export DEBIAN_FRONTEND=noninteractive && apt update && \ apt install -y \ - libcurl4 libczmq4 libluajit-5.1-2 \ + libcurl4 libczmq4 libluajit-5.1-2 libgdal32 \ libprotobuf-lite32 libsqlite3-0 libsqlite3-mod-spatialite libzmq5 zlib1g \ curl gdb locales parallel python3-minimal python3-distutils python-is-python3 \ spatialite-bin unzip wget && rm -rf /var/lib/apt/lists/* From b6bad3777dc0a4d4117325527a274fc99400232c Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 18 Mar 2024 12:19:30 +0100 Subject: [PATCH 049/198] fixed CostMatrix shapes for routes against trivial oneways (#4633) --- CHANGELOG.md | 1 + src/thor/costmatrix.cc | 7 ++++--- test/gurka/test_matrix.cc | 26 +++++++++++++++++++++----- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba20431916..2f2ced4e40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,7 @@ * FIXED: CostMatrix for trivial routes with oneways [#4626](https://github.com/valhalla/valhalla/pull/4626) * FIXED: some entry points to creating geotiff isochrones output did not register the geotiff driver before attempting to use it [#4628](https://github.com/valhalla/valhalla/pull/4628) * FIXED: libgdal wasn't installed in docker image, so it never worked in docker [#4629](https://github.com/valhalla/valhalla/pull/4629) + * FIXED: CostMatrix shapes for routes against trivial oneways [#4633](https://github.com/valhalla/valhalla/pull/4633) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 1a1cb17986..db67b68210 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -1243,9 +1243,10 @@ std::string CostMatrix::RecostFormPath(GraphReader& graphreader, auto source_vertex = PointLL{source_edge.ll().lng(), source_edge.ll().lat()}; auto target_vertex = PointLL{target_edge.ll().lng(), target_edge.ll().lat()}; std::vector points; - for (const auto& path_edge : path_edges) { - auto is_first_edge = path_edge == path_edges.front(); - auto is_last_edge = path_edge == path_edges.back(); + for (uint32_t i = 0; i < path_edges.size(); i++) { + auto& path_edge = path_edges[i]; + auto is_first_edge = i == 0; + auto is_last_edge = i == (path_edges.size() - 1); const auto* de = graphreader.directededge(path_edge, tile); auto edge_shp = tile->edgeinfo(de).shape(); diff --git a/test/gurka/test_matrix.cc b/test/gurka/test_matrix.cc index b0e3cfcf88..3500fcdaec 100644 --- a/test/gurka/test_matrix.cc +++ b/test/gurka/test_matrix.cc @@ -504,8 +504,9 @@ TEST(StandAlone, CostMatrixShapes) { /* EXPECT_EQ(result.matrix().shapes(0), encoded); EXPECT_EQ(res_doc.Parse(res.c_str())["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject()["shape"], - encoded); res.erase(); + encoded); */ + res.erase(); // trivial route reverse // has a bug: https://github.com/valhalla/valhalla/issues/4433, but it's band-aided for now @@ -517,8 +518,9 @@ TEST(StandAlone, CostMatrixShapes) { /* EXPECT_EQ(result.matrix().shapes(0), encoded); EXPECT_EQ(res_doc.Parse(res.c_str())["sources_to_targets"].GetArray()[0].GetArray()[0].GetObject()["shape"], - encoded); res.erase(); + encoded); */ + res.erase(); // timedistancematrix @@ -788,19 +790,33 @@ TEST(StandAlone, CostMatrixTrivialRoutes) { {"CD", {{"highway", "residential"}}}, {"BE", {{"highway", "residential"}}}, {"EF", {{"highway", "residential"}}}, {"FC", {{"highway", "residential"}}}, }; - const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); auto map = gurka::buildtiles(layout, ways, {}, {}, VALHALLA_BUILD_DIR "test/data/costmatrix_trivial"); + std::unordered_map options = {{"/shape_format", "polyline6"}}; + // test the against-oneway case { - auto matrix = gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, "auto"); + auto matrix = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, "auto", options); EXPECT_EQ(matrix.matrix().distances(0), 2200); + + std::vector oneway_vertices; + for (auto& node : {"1", "C", "F", "E", "B", "2"}) { + oneway_vertices.push_back(layout[node]); + } + auto encoded = encode>(oneway_vertices, 1e6); + EXPECT_EQ(matrix.matrix().shapes(0), encoded); } // test the normal trivial case { - auto matrix = gurka::do_action(valhalla::Options::sources_to_targets, map, {"3"}, {"4"}, "auto"); + auto matrix = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"3"}, {"4"}, "auto", options); EXPECT_EQ(matrix.matrix().distances(0), 400); + + auto encoded = encode>({layout["3"], layout["4"]}, 1e6); + EXPECT_EQ(matrix.matrix().shapes(0), encoded); } } From 181eed995458608ef488db2983d16dca86a20f2f Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Tue, 19 Mar 2024 05:11:07 -0600 Subject: [PATCH 050/198] Simple warning fix on test gurka (#4637) --- test/gurka/CMakeLists.txt | 5 ----- test/gurka/test_elevation.cc | 2 +- test/gurka/test_landmarks.cc | 2 +- test/gurka/test_match.cc | 3 ++- test/gurka/test_traffic.cc | 2 -- test/gurka/test_traffic_smoothing.cc | 2 +- 6 files changed, 5 insertions(+), 11 deletions(-) diff --git a/test/gurka/CMakeLists.txt b/test/gurka/CMakeLists.txt index fd20e7806c..6e8a28733d 100644 --- a/test/gurka/CMakeLists.txt +++ b/test/gurka/CMakeLists.txt @@ -11,13 +11,10 @@ if(ENABLE_DATA_TOOLS) # "-Werror" flag # Avoid adding a filename to this list set(TESTS_WITH_WARNINGS - test_elevation.cc test_gtfs.cc test_instructions_roundabout.cc - test_landmarks.cc test_languages.cc test_locate.cc - test_match.cc test_multi_level_loki.cc test_osrm_serializer.cc test_pbf_api.cc @@ -25,8 +22,6 @@ if(ENABLE_DATA_TOOLS) test_phonemes_w_langs.cc test_recost.cc test_time_tracking.cc - test_traffic.cc - test_traffic_smoothing.cc ) ## Add executable targets diff --git a/test/gurka/test_elevation.cc b/test/gurka/test_elevation.cc index 0f7a8c7fe9..ba2916a019 100644 --- a/test/gurka/test_elevation.cc +++ b/test/gurka/test_elevation.cc @@ -220,7 +220,7 @@ TEST(Standalone, ElevationCompareToSkadi) { result.Parse(route_json.c_str()); for (size_t leg_index = 0; leg_index < waypoints.size() - 1; ++leg_index) { - auto s = + [[maybe_unused]] auto s = rapidjson::get_child_optional(result, ("/trip/legs/" + std::to_string(leg_index) + "/shape") .c_str()); diff --git a/test/gurka/test_landmarks.cc b/test/gurka/test_landmarks.cc index c4855213e2..53eece209c 100644 --- a/test/gurka/test_landmarks.cc +++ b/test/gurka/test_landmarks.cc @@ -620,7 +620,7 @@ TEST(LandmarkTest, TestLandmarksInManeuvers) { "Checking landmarks in maneuver failed: cannot find the maneuver in the expected result!"); } ASSERT_EQ(result_landmarks.size(), expected->second.size()); - for (auto i = 0; i < result_landmarks.size(); ++i) { + for (size_t i = 0; i < result_landmarks.size(); ++i) { EXPECT_EQ(result_landmarks[i], expected->second[i]); } } diff --git a/test/gurka/test_match.cc b/test/gurka/test_match.cc index 3198b862c5..3be216b114 100644 --- a/test/gurka/test_match.cc +++ b/test/gurka/test_match.cc @@ -262,8 +262,9 @@ uint32_t speed_from_edge(const valhalla::Api& api, bool compare_with_previous_ed node.cost().elapsed_cost().seconds() - node.cost().transition_cost().seconds()) / 3600.0; auto new_kmh = static_cast(km / h + .5); - if (is_valid(kmh) && compare_with_previous_edge) + if (is_valid(kmh) && compare_with_previous_edge) { EXPECT_EQ(kmh, new_kmh); + } kmh = new_kmh; } return kmh; diff --git a/test/gurka/test_traffic.cc b/test/gurka/test_traffic.cc index 613852a869..411cde3707 100644 --- a/test/gurka/test_traffic.cc +++ b/test/gurka/test_traffic.cc @@ -296,8 +296,6 @@ TEST(Traffic, CutGeoms) { uint32_t index, baldr::TrafficSpeed* current) -> void { baldr::GraphId tile_id(tile.header->tile_id); auto BD = gurka::findEdge(reader, map.nodes, "BD", "D", tile_id); - baldr::TrafficSpeed* existing = - const_cast(tile.speeds + index); current->breakpoint1 = 255; if (std::get<1>(BD) != nullptr && std::get<0>(BD).id() == index) { current->overall_encoded_speed = 0; diff --git a/test/gurka/test_traffic_smoothing.cc b/test/gurka/test_traffic_smoothing.cc index ef31753916..1f3d76fa40 100644 --- a/test/gurka/test_traffic_smoothing.cc +++ b/test/gurka/test_traffic_smoothing.cc @@ -558,7 +558,7 @@ class MapMatchWithTraffic : public ::testing::Test { traffic_speed->breakpoint1 = 255; }); - test::customize_historical_traffic(map.config, [](DirectedEdge& e) { + test::customize_historical_traffic(map.config, [](DirectedEdge&) { // speeds for every 5 min bucket of the week std::array historical; historical.fill(6); From dff70059721996faf42dc49f4482358436d79f6b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 15:52:57 +0100 Subject: [PATCH 051/198] Bump tz to 2024a (#4643) Co-authored-by: github-actions[bot] Co-authored-by: Nils --- CHANGELOG.md | 1 + scripts/valhalla_build_timezones | 2 +- third_party/tz | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f2ced4e40..dc20913253 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -102,6 +102,7 @@ * ADDED: isochrone proper polygon support & pbf output for isochrone [#4575](https://github.com/valhalla/valhalla/pull/4575) * ADDED: return isotile grid as geotiff [#4594](https://github.com/valhalla/valhalla/pull/4594) * ADDED: `ignore_non_vehicular_restrictions` parameter for truck costing [#4606](https://github.com/valhalla/valhalla/pull/4606) + * UPDATED: tz database to 2024a [#4643](https://github.com/valhalla/valhalla/pull/4643) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/scripts/valhalla_build_timezones b/scripts/valhalla_build_timezones index 98fbddeee4..fb47dbb9fb 100755 --- a/scripts/valhalla_build_timezones +++ b/scripts/valhalla_build_timezones @@ -21,7 +21,7 @@ fi rm -rf dist rm -f ./timezones-with-oceans.shapefile.zip -url="https://github.com/evansiroky/timezone-boundary-builder/releases/download/2023d/timezones-with-oceans.shapefile.zip" +url="https://github.com/evansiroky/timezone-boundary-builder/releases/download/2024a/timezones-with-oceans.shapefile.zip" echo "downloading timezone polygon file." 1>&2 curl -L -s -o ./timezones-with-oceans.shapefile.zip ${url} || error_exit "curl failed for ${url}" diff --git a/third_party/tz b/third_party/tz index cc48c2dfa2..380c07cef0 160000 --- a/third_party/tz +++ b/third_party/tz @@ -1 +1 @@ -Subproject commit cc48c2dfa2f3c21d25ab108bba978b0307ecf0e8 +Subproject commit 380c07cef01c71c1f93e9709d9f8c79b91cff063 From b491610ae2bfda537894bf553f643633806490e8 Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Fri, 22 Mar 2024 09:11:53 -0600 Subject: [PATCH 052/198] Fix unused variable test dir simple fixes (#4646) --- test/CMakeLists.txt | 7 ------- test/logging.cc | 2 +- test/mapmatch.cc | 14 +------------- test/polyline2.cc | 2 +- test/routing.cc | 6 +++--- test/util_midgard.cc | 5 ++--- test/vector2.cc | 4 ++-- test/viterbi_search.cc | 5 +---- 8 files changed, 11 insertions(+), 34 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 59e9c014f0..490d10d404 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -77,25 +77,18 @@ set(tests_with_warnings graphparser graphtilebuilder gridded_data - logging maneuversbuilder - mapmatch narrativebuilder node_search - polyline2 parse_request - routing sample search sequence statsd traffictile - util_midgard util_odin utrecht timedep_paths - vector2 - viterbi_search worker ) diff --git a/test/logging.cc b/test/logging.cc index 91d0972fed..4aa5c9ee0e 100644 --- a/test/logging.cc +++ b/test/logging.cc @@ -61,7 +61,7 @@ TEST(Logging, FileLoggerTest) { int exit_code = 0; for (auto& result : results) { try { - size_t count = result.get(); + result.get(); } catch (std::exception& e) { exit_code++; } } diff --git a/test/mapmatch.cc b/test/mapmatch.cc index c5446c7392..ea61dfae03 100644 --- a/test/mapmatch.cc +++ b/test/mapmatch.cc @@ -115,18 +115,6 @@ std::string json_escape(const std::string& unescaped) { return escaped; } -std::string output_shape(const valhalla::Api& api) { - std::stringstream shape; - for (const auto& r : api.directions().routes()) { - shape << "new route" << std::endl; - for (const auto& l : r.legs()) { - shape << std::fixed << std::setprecision(3) << "Time : " << l.summary().time() - << ", length : " << l.summary().length() << ", shape : " << l.shape() << std::endl; - } - } - return shape.str(); -} - void compare_results(const valhalla::Api& expected, const valhalla::Api& result) { // check the number of routes match ASSERT_EQ(result.trip().routes_size(), expected.trip().routes_size()) @@ -1090,7 +1078,7 @@ TEST(Mapmatch, test_discontinuity_on_same_edge) { for (size_t i = 0; i < test_cases.size(); ++i) { auto result = tester.match(test_cases[i]); EXPECT_EQ(result.trip().routes_size(), test_ans_num_routes[i]); - int j = 0, k = 0; + int j = 0; for (const auto& route : result.trip().routes()) { ASSERT_EQ(route.legs_size(), test_ans_num_legs[i][j++]) << "Expected " + std::to_string(test_ans_num_legs[i][j - 1]) + " legs but got " + diff --git a/test/polyline2.cc b/test/polyline2.cc index 36d9981db2..f53d88e8d3 100644 --- a/test/polyline2.cc +++ b/test/polyline2.cc @@ -15,7 +15,7 @@ namespace { template void TryGeneralizeAndLength(Polyline2>& pl, const float& gen, const float& res) { - uint32_t size = pl.Generalize(gen); + pl.Generalize(gen); std::vector> pts = pl.pts(); diff --git a/test/routing.cc b/test/routing.cc index 1f5ba23bd0..07c0da181b 100644 --- a/test/routing.cc +++ b/test/routing.cc @@ -17,9 +17,9 @@ struct simple_label { }; void Add(baldr::DoubleBucketQueue& adjlist, const std::vector& costs) { - uint32_t idx = 0; - for (const auto cost : costs) { - adjlist.add(idx++); + // C++20 use iota + for (uint32_t idx = 0; idx < costs.size(); ++idx) { + adjlist.add(idx); } } diff --git a/test/util_midgard.cc b/test/util_midgard.cc index 6f66bec9e4..934554b819 100644 --- a/test/util_midgard.cc +++ b/test/util_midgard.cc @@ -27,7 +27,7 @@ TEST(UtilMidgard, TestRangedDefaultT) { for (unsigned i = 0; i < 100; ++i) { ranged_default_t testRange{lower, defaultDistributor(generator), upper}; - float defaultVal = testRange.def; + float testVal = testDistributor(generator); float finalVal = testRange(testVal); @@ -198,7 +198,6 @@ TEST(UtilMidgard, TestResample) { auto length = pl.Length(); resampled = resample_polyline(input_shape, length, resolution); size_t n = std::round(length / resolution); - float sample_distance = length / n; EXPECT_EQ(resampled.size(), n + 1) << "resample_polyline - Sampled polyline is not the expected length"; } @@ -650,7 +649,7 @@ TEST(UtilMidgard, TestExpandLocation) { EXPECT_GE(area, 199.0f * 199.0f); // Should throw an exception if negative value is sent - EXPECT_THROW(AABB2 box = ExpandMeters(loc, -10.0f);, std::invalid_argument) + EXPECT_THROW(ExpandMeters(loc, -10.0f);, std::invalid_argument) << "ExpandLocation: should throw exception with negative meters supplied"; } diff --git a/test/vector2.cc b/test/vector2.cc index e3f6693934..e0aeca2f30 100644 --- a/test/vector2.cc +++ b/test/vector2.cc @@ -45,7 +45,7 @@ TEST(Vector2, TestCtorPoint2Point2) { } void TryCtorVector2(const Vector2& v, const Vector2& expected) { - Vector2 result(v); + const Vector2& result(v); EXPECT_EQ(expected, result); } @@ -187,7 +187,7 @@ void TryOpMultiplication(const Vector2& v, const float scalar, const Vector2& ex EXPECT_EQ(expected, result) << "scalar pre"; Vector2 result2 = scalar * v; - EXPECT_EQ(expected, result) << "scalar post"; + EXPECT_EQ(expected, result2) << "scalar post"; } TEST(Vector2, TestOpMultiplication) { diff --git a/test/viterbi_search.cc b/test/viterbi_search.cc index b336f647d2..e45ef59ce7 100644 --- a/test/viterbi_search.cc +++ b/test/viterbi_search.cc @@ -68,8 +68,7 @@ template void print_path(iterator_t rbegin, iterator_t ren void AddColumns(IViterbiSearch& vs, const std::vector& columns) { StateId::Time time = 0; for (const auto& column : columns) { - uint32_t idx = 0; - for (const auto& state : column) { + for (uint32_t idx = 0; idx < column.size(); ++idx) { StateId stateid(time, idx); const auto added = vs.AddStateId(stateid); @@ -77,7 +76,6 @@ void AddColumns(IViterbiSearch& vs, const std::vector& columns) { << " must be added"; ASSERT_TRUE(vs.HasStateId(stateid)) << "must contain it"; - idx++; } time++; } @@ -338,7 +336,6 @@ class TransitionCostModel { float operator()(const StateId& lhs, const StateId& rhs) const { const auto& left = get_state(columns_, lhs); - const auto& right = get_state(columns_, rhs); const auto it = left.transition_costs.find(rhs.id()); if (it == left.transition_costs.end()) { return -1.0; From 6fb3eb774f8df9f1b529bccf33909bf12f59d474 Mon Sep 17 00:00:00 2001 From: TheTonda <32519975+TheTonda@users.noreply.github.com> Date: Sun, 24 Mar 2024 14:31:39 +0530 Subject: [PATCH 053/198] FIXED Issue #4652: src/thor/unidirectional_astar.cc doesnt work for date_time type 2, i.e. for arrival (#4653) Co-authored-by: Kevin Kreiser --- CHANGELOG.md | 1 + src/thor/unidirectional_astar.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc20913253..b840692361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ * FIXED: some entry points to creating geotiff isochrones output did not register the geotiff driver before attempting to use it [#4628](https://github.com/valhalla/valhalla/pull/4628) * FIXED: libgdal wasn't installed in docker image, so it never worked in docker [#4629](https://github.com/valhalla/valhalla/pull/4629) * FIXED: CostMatrix shapes for routes against trivial oneways [#4633](https://github.com/valhalla/valhalla/pull/4633) + * FIXED: unidirectional_astar.cc doesn't work for date_time type = 2 #4652(https://github.com/valhalla/valhalla/issues/4652) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/thor/unidirectional_astar.cc b/src/thor/unidirectional_astar.cc index b740f4b935..c0c4df74fa 100644 --- a/src/thor/unidirectional_astar.cc +++ b/src/thor/unidirectional_astar.cc @@ -466,7 +466,7 @@ std::vector> UnidirectionalAStar Date: Mon, 25 Mar 2024 16:10:48 +0100 Subject: [PATCH 054/198] some bug fixes ported over from #4638 (#4642) --- CHANGELOG.md | 1 + src/sif/recost.cc | 2 +- src/thor/astar_bss.cc | 4 +-- src/thor/bidirectional_astar.cc | 44 ++++++++++++++++--------------- src/thor/costmatrix.cc | 30 +++++++++++---------- src/thor/dijkstras.cc | 9 +++---- src/thor/multimodal.cc | 4 +-- src/thor/route_action.cc | 1 + src/thor/timedistancebssmatrix.cc | 12 ++++----- src/thor/timedistancematrix.cc | 36 ++++++++++++++++--------- src/thor/unidirectional_astar.cc | 6 ++--- valhalla/sif/costfactory.h | 6 +++++ 12 files changed, 88 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b840692361..c1b0650332 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ * FIXED: libgdal wasn't installed in docker image, so it never worked in docker [#4629](https://github.com/valhalla/valhalla/pull/4629) * FIXED: CostMatrix shapes for routes against trivial oneways [#4633](https://github.com/valhalla/valhalla/pull/4633) * FIXED: unidirectional_astar.cc doesn't work for date_time type = 2 #4652(https://github.com/valhalla/valhalla/issues/4652) + * FIXED: a few fixes around the routing algorithms [#4626](https://github.com/valhalla/valhalla/pull/4642) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/sif/recost.cc b/src/sif/recost.cc index f7c422af81..499fa9398b 100644 --- a/src/sif/recost.cc +++ b/src/sif/recost.cc @@ -91,7 +91,7 @@ void recost_forward(baldr::GraphReader& reader, // TODO: if this edge begins a restriction, we need to start popping off edges into queue // so that we can find if we reach the end of the restriction. then we need to replay the // queued edges as normal - uint8_t time_restrictions_TODO = -1; + uint8_t time_restrictions_TODO = baldr::kInvalidRestriction; // if its not time dependent set to 0 for Allowed method below const uint64_t localtime = offset_time.valid ? offset_time.local_time : 0; // we should call 'Allowed' method even if 'ignore_access' flag is true in order to diff --git a/src/thor/astar_bss.cc b/src/thor/astar_bss.cc index f0857de070..c6cf719117 100644 --- a/src/thor/astar_bss.cc +++ b/src/thor/astar_bss.cc @@ -221,7 +221,7 @@ void AStarBSSAlgorithm::ExpandForward(GraphReader& graphreader, // Add to the adjacency list and edge labels. uint32_t idx = edgelabels_.size(); edgelabels_.emplace_back(pred_idx, edgeid, GraphId(), directededge, newcost, sortcost, dist, mode, - transition_cost, false, true, false, InternalTurn::kNoTurn, + transition_cost, false, false, false, InternalTurn::kNoTurn, baldr::kInvalidRestriction); *current_es = {EdgeSet::kTemporary, idx}; adjacencylist_.add(idx); @@ -446,7 +446,7 @@ void AStarBSSAlgorithm::SetOrigin(GraphReader& graphreader, // of the path. uint32_t d = static_cast(directededge->length() * (1.0f - edge.percent_along())); BDEdgeLabel edge_label(kInvalidLabel, edgeid, directededge, cost, sortcost, dist, - travel_mode_t::kPedestrian, baldr::kInvalidRestriction, true, false, + travel_mode_t::kPedestrian, baldr::kInvalidRestriction, false, false, sif::InternalTurn::kNoTurn); // Set the origin flag and path distance edge_label.set_origin(); diff --git a/src/thor/bidirectional_astar.cc b/src/thor/bidirectional_astar.cc index 61de6281f9..1d31b9bef2 100644 --- a/src/thor/bidirectional_astar.cc +++ b/src/thor/bidirectional_astar.cc @@ -237,7 +237,7 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, // or if a complex restriction prevents transition onto this edge. // if its not time dependent set to 0 for Allowed and Restricted methods below const uint64_t localtime = time_info.valid ? time_info.local_time : 0; - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; if (FORWARD) { // Why is is_dest false? // We have to consider next cases: @@ -309,7 +309,9 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, : astarheuristic_reverse_.Get(t2->get_node_ll(meta.edge->endnode()), dist)); // not_thru_pruning_ is only set to false on the 2nd pass in route_action. - bool thru = not_thru_pruning_ ? (pred.not_thru_pruning() || !meta.edge->not_thru()) : false; + // We allow settling not_thru edges so we can connect both trees on them. + bool not_thru_pruning = + not_thru_pruning_ ? (pred.not_thru_pruning() || !meta.edge->not_thru()) : false; // Add edge label, add to the adjacency list and set edge status uint32_t idx = 0; @@ -321,7 +323,7 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, dist = astarheuristic_reverse_.GetDistance(t2->get_node_ll(meta.edge->endnode())); } edgelabels_forward_.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, - sortcost, dist, mode_, transition_cost, thru, + sortcost, dist, mode_, transition_cost, not_thru_pruning, (pred.closure_pruning() || !costing_->IsClosed(meta.edge, tile)), static_cast(flow_sources & kDefaultFlowMask), costing_->TurnType(pred.opp_local_idx(), nodeinfo, meta.edge), @@ -337,8 +339,8 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, dist = astarheuristic_forward_.GetDistance(t2->get_node_ll(meta.edge->endnode())); } edgelabels_reverse_.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, - sortcost, dist, mode_, transition_cost, thru, - (pred.closure_pruning() || !costing_->IsClosed(meta.edge, tile)), + sortcost, dist, mode_, transition_cost, not_thru_pruning, + (pred.closure_pruning() || !costing_->IsClosed(opp_edge, t2)), static_cast(flow_sources & kDefaultFlowMask), costing_->TurnType(meta.edge->localedgeidx(), nodeinfo, opp_edge, opp_pred_edge), @@ -422,14 +424,14 @@ void BidirectionalAStar::Expand(baldr::GraphReader& graphreader, // If so, it means we are attempting a u-turn. In that case, lets wait with evaluating // this edge until last. If any other edges were emplaced, it means we should not // even try to evaluate a u-turn since u-turns should only happen for deadends - uturn_meta = pred.opp_local_idx() == meta.edge->localedgeidx() ? meta : uturn_meta; + bool is_uturn = pred.opp_local_idx() == meta.edge->localedgeidx(); + uturn_meta = is_uturn ? meta : uturn_meta; // Expand but only if this isnt the uturn, we'll try that later if nothing else works out - disable_uturn = - (pred.opp_local_idx() != meta.edge->localedgeidx() && - ExpandInner(graphreader, pred, opp_pred_edge, nodeinfo, pred_idx, meta, - shortcuts, tile, offset_time)) || - disable_uturn; + disable_uturn = (!is_uturn && ExpandInner(graphreader, pred, opp_pred_edge, + nodeinfo, pred_idx, meta, + shortcuts, tile, offset_time)) || + disable_uturn; } // Handle transitions - expand from the end node of each transition @@ -1011,7 +1013,7 @@ void BidirectionalAStar::SetOrigin(GraphReader& graphreader, dist = astarheuristic_reverse_.GetDistance(nodeinfo->latlng(endtile->header()->base_ll())); } edgelabels_forward_.emplace_back(kInvalidLabel, edgeid, directededge, cost, sortcost, dist, mode_, - -1, !(costing_->IsClosed(directededge, tile)), + kInvalidRestriction, !(costing_->IsClosed(directededge, tile)), static_cast(flow_sources & kDefaultFlowMask), sif::InternalTurn::kNoTurn, 0, directededge->destonly() || @@ -1028,9 +1030,9 @@ void BidirectionalAStar::SetOrigin(GraphReader& graphreader, // flags on small loops. Set this to false here to override this for now. edgelabels_forward_.back().set_not_thru(false); - pruning_disabled_at_origin_ = pruning_disabled_at_origin_ || - !edgelabels_forward_.back().closure_pruning() || - !edgelabels_forward_.back().not_thru_pruning(); + pruning_disabled_at_origin_ = + pruning_disabled_at_origin_ || !edgelabels_forward_.back().closure_pruning() || + !edgelabels_forward_.back().not_thru_pruning() || edgelabels_forward_.back().destonly(); } // Set the origin timezone @@ -1109,9 +1111,9 @@ void BidirectionalAStar::SetDestination(GraphReader& graphreader, dist, mode_, c, !opp_dir_edge->not_thru(), !(costing_->IsClosed(directededge, tile)), static_cast(flow_sources & kDefaultFlowMask), - sif::InternalTurn::kNoTurn, -1, - opp_dir_edge->destonly() || - (costing_->is_hgv() && opp_dir_edge->destonly_hgv())); + sif::InternalTurn::kNoTurn, kInvalidRestriction, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv())); adjacencylist_reverse_.add(idx); // setting this edge as reached, sending the opposing because this is the reverse tree @@ -1124,9 +1126,9 @@ void BidirectionalAStar::SetDestination(GraphReader& graphreader, // flags on small loops. Set this to false here to override this for now. edgelabels_reverse_.back().set_not_thru(false); - pruning_disabled_at_destination_ = pruning_disabled_at_destination_ || - !edgelabels_reverse_.back().closure_pruning() || - !edgelabels_reverse_.back().not_thru_pruning(); + pruning_disabled_at_destination_ = + pruning_disabled_at_destination_ || !edgelabels_reverse_.back().closure_pruning() || + !edgelabels_reverse_.back().not_thru_pruning() || edgelabels_reverse_.back().destonly(); } } diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index db67b68210..b260177e41 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -461,7 +461,7 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, auto& edgelabels = edgelabel_[FORWARD][index]; // Skip this edge if no access is allowed (based on costing method) // or if a complex restriction prevents transition onto this edge. - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; if (FORWARD) { if (!costing_->Allowed(meta.edge, false, pred, tile, meta.edge_id, time_info.local_time, time_info.timezone_index, restriction_idx) || @@ -512,16 +512,16 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, } // not_thru_pruning_ is only set to false on the 2nd pass in matrix_action. - // TODO(nils): one of these cases where I think reverse tree should look at the opposing edge, - // not the expanding one, same for quite some attributes below (and same in bidir a*) - bool thru = not_thru_pruning_ ? (pred.not_thru_pruning() || !meta.edge->not_thru()) : false; + // We allow settling not_thru edges so we can connect both trees on them. + bool not_thru_pruning = + not_thru_pruning_ ? (pred.not_thru_pruning() || !meta.edge->not_thru()) : false; // Add edge label, add to the adjacency list and set edge status uint32_t idx = edgelabels.size(); *meta.edge_status = {EdgeSet::kTemporary, idx}; if (FORWARD) { edgelabels.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, mode_, tc, - pred_dist, thru, + pred_dist, not_thru_pruning, (pred.closure_pruning() || !costing_->IsClosed(meta.edge, tile)), static_cast(flow_sources & kDefaultFlowMask), costing_->TurnType(pred.opp_local_idx(), nodeinfo, meta.edge), @@ -530,8 +530,8 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, (costing_->is_hgv() && meta.edge->destonly_hgv())); } else { edgelabels.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, mode_, tc, - pred_dist, thru, - (pred.closure_pruning() || !costing_->IsClosed(meta.edge, tile)), + pred_dist, not_thru_pruning, + (pred.closure_pruning() || !costing_->IsClosed(opp_edge, t2)), static_cast(flow_sources & kDefaultFlowMask), costing_->TurnType(meta.edge->localedgeidx(), nodeinfo, opp_edge, opp_pred_edge), @@ -1024,9 +1024,10 @@ void CostMatrix::SetSources(GraphReader& graphreader, // - "transition_cost" is used to store the traversed secs & length // - "path_id" is used to store whether the edge is even allowed (e.g. no oneway) Cost ec(std::round(edgecost.secs), static_cast(directededge->length())); - BDEdgeLabel edge_label(kInvalidLabel, edgeid, oppedge, directededge, cost, mode_, ec, d, false, - true, static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn, -1, + BDEdgeLabel edge_label(kInvalidLabel, edgeid, oppedge, directededge, cost, mode_, ec, d, + !directededge->not_thru(), !(costing_->IsClosed(directededge, tile)), + static_cast(flow_sources & kDefaultFlowMask), + InternalTurn::kNoTurn, kInvalidRestriction, static_cast(costing_->Allowed(directededge, tile)), directededge->destonly() || (costing_->is_hgv() && directededge->destonly_hgv())); @@ -1108,11 +1109,12 @@ void CostMatrix::SetTargets(baldr::GraphReader& graphreader, // - "path_id" is used to store whether the opp edge is even allowed (e.g. no oneway) Cost ec(std::round(edgecost.secs), static_cast(directededge->length())); BDEdgeLabel edge_label(kInvalidLabel, opp_edge_id, edgeid, opp_dir_edge, cost, mode_, ec, d, - false, true, static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn, -1, + !opp_dir_edge->not_thru(), !(costing_->IsClosed(directededge, tile)), + static_cast(flow_sources & kDefaultFlowMask), + InternalTurn::kNoTurn, kInvalidRestriction, static_cast(costing_->Allowed(opp_dir_edge, opp_tile)), - opp_dir_edge->destonly() || - (costing_->is_hgv() && opp_dir_edge->destonly_hgv())); + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv())); edge_label.set_not_thru(false); // Add EdgeLabel to the adjacency list (but do not set its status). diff --git a/src/thor/dijkstras.cc b/src/thor/dijkstras.cc index dae5ac87b1..24174d02c3 100644 --- a/src/thor/dijkstras.cc +++ b/src/thor/dijkstras.cc @@ -178,7 +178,7 @@ void Dijkstras::ExpandInner(baldr::GraphReader& graphreader, // Check if the edge is allowed or if a restriction occurs EdgeStatus* todo = nullptr; - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; // is_dest is false, because it is a traversal algorithm in this context, not a path search // algorithm. In other words, destination edges are not defined for this Dijkstra's algorithm. const bool is_dest = false; @@ -259,7 +259,7 @@ void Dijkstras::ExpandInner(baldr::GraphReader& graphreader, } else { bdedgelabels_.emplace_back(pred_idx, edgeid, oppedgeid, directededge, newcost, mode_, transition_cost, path_dist, false, - (pred.closure_pruning() || !costing_->IsClosed(directededge, tile)), + (pred.closure_pruning() || !costing_->IsClosed(opp_edge, t2)), static_cast(flow_sources & kDefaultFlowMask), costing_->TurnType(directededge->localedgeidx(), nodeinfo, opp_edge, opp_pred_edge), @@ -524,7 +524,7 @@ void Dijkstras::ExpandForwardMultiModal(GraphReader& graphreader, // costing - assume if you get a transit edge you walked to the transit stop uint32_t tripid = 0; uint32_t blockid = 0; - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; const bool is_dest = false; if (directededge->IsTransitLine()) { // Check if transit costing allows this edge @@ -818,11 +818,10 @@ void Dijkstras::SetOriginLocations(GraphReader& graphreader, // Construct the edge label. Set the predecessor edge index to invalid // to indicate the origin of the path. uint32_t idx = bdedgelabels_.size(); - int restriction_idx = -1; bdedgelabels_.emplace_back(kInvalidLabel, edgeid, opp_edge_id, directededge, cost, mode_, Cost{}, path_dist, false, !(costing_->IsClosed(directededge, tile)), static_cast(flow_sources & kDefaultFlowMask), - InternalTurn::kNoTurn, restriction_idx, multipath_ ? path_id : 0, + InternalTurn::kNoTurn, kInvalidRestriction, multipath_ ? path_id : 0, directededge->destonly() || (costing_->is_hgv() && directededge->destonly_hgv())); // Set the origin flag diff --git a/src/thor/multimodal.cc b/src/thor/multimodal.cc index d57086263c..498ad6bd6f 100644 --- a/src/thor/multimodal.cc +++ b/src/thor/multimodal.cc @@ -751,7 +751,7 @@ bool MultiModalPathAlgorithm::ExpandFromNode(baldr::GraphReader& graphreader, // Add edge label, add to the adjacency list and set edge status uint32_t idx = edgelabels.size(); edgelabels.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, mode_, - walking_distance, baldr::kInvalidRestriction, true, false, + walking_distance, baldr::kInvalidRestriction, false, false, InternalTurn::kNoTurn); *es = {EdgeSet::kTemporary, idx}; adjlist.add(idx); @@ -809,7 +809,7 @@ bool MultiModalPathAlgorithm::CanReachDestination(const valhalla::Location& dest // we cannot do transition_cost on this label yet because we have no predecessor, but when we find // it, we will do an update on it and set the real transition cost based on the path to it edgelabels.emplace_back(kInvalidLabel, oppedge, diredge, cost, cost.cost, mode_, length, - baldr::kInvalidRestriction, true, false, InternalTurn::kNoTurn); + baldr::kInvalidRestriction, false, false, InternalTurn::kNoTurn); adjlist.add(label_idx); edgestatus.Set(oppedge, EdgeSet::kTemporary, label_idx, tile); label_idx++; diff --git a/src/thor/route_action.cc b/src/thor/route_action.cc index 0ed8fc0095..9d048ee216 100644 --- a/src/thor/route_action.cc +++ b/src/thor/route_action.cc @@ -305,6 +305,7 @@ std::vector> thor_worker_t::get_path(PathAlgorithm* // If bidirectional A* disable use of destination-only edges on the // first pass. If there is a failure, we allow them on the second pass. // Other path algorithms can use destination-only edges on the first pass. + // TODO(nils): why not others with destonly pruning? it gets a 2nd pass as well cost->set_allow_destination_only(path_algorithm == &bidir_astar ? false : true); cost->set_pass(0); diff --git a/src/thor/timedistancebssmatrix.cc b/src/thor/timedistancebssmatrix.cc index cb993af199..b5493c8b11 100644 --- a/src/thor/timedistancebssmatrix.cc +++ b/src/thor/timedistancebssmatrix.cc @@ -94,7 +94,7 @@ void TimeDistanceBSSMatrix::Expand(GraphReader& graphreader, // Skip this edge if permanently labeled (best path already found to this // directed edge), if no access is allowed to this edge (based on costing // method), or if a complex restriction prevents this path. - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; const bool is_dest = dest_edges_.find(edgeid.value) != dest_edges_.cend(); if (FORWARD) { if (!current_costing->Allowed(directededge, is_dest, pred, tile, edgeid, 0, 0, @@ -138,7 +138,7 @@ void TimeDistanceBSSMatrix::Expand(GraphReader& graphreader, // Add to the adjacency list and edge labels. uint32_t idx = edgelabels_.size(); edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, mode, - path_distance, restriction_idx, true, false, InternalTurn::kNoTurn); + path_distance, restriction_idx, false, false, InternalTurn::kNoTurn); *es = {EdgeSet::kTemporary, idx}; adjacencylist_.add(idx); } @@ -315,11 +315,11 @@ void TimeDistanceBSSMatrix::SetOrigin(GraphReader& graphreader, const valhalla:: dist = static_cast(directededge->length() * percent_along); } else { - opp_edge_id = graphreader.GetOpposingEdgeId(edgeid); + opp_edge_id = graphreader.GetOpposingEdgeId(edgeid, endtile); if (!opp_edge_id.Is_Valid()) { continue; } - opp_dir_edge = graphreader.GetOpposingEdge(edgeid); + opp_dir_edge = graphreader.GetOpposingEdge(edgeid, endtile); cost = pedestrian_costing_->EdgeCost(opp_dir_edge, endtile, time_info, flow_sources) * edge.percent_along(); dist = static_cast(directededge->length() * edge.percent_along()); @@ -335,11 +335,11 @@ void TimeDistanceBSSMatrix::SetOrigin(GraphReader& graphreader, const valhalla:: // of the path. Set the origin flag if (FORWARD) { edgelabels_.emplace_back(kInvalidLabel, edgeid, directededge, cost, cost.cost, - travel_mode_t::kPedestrian, dist, baldr::kInvalidRestriction, true, + travel_mode_t::kPedestrian, dist, baldr::kInvalidRestriction, false, false, InternalTurn::kNoTurn); } else { edgelabels_.emplace_back(kInvalidLabel, opp_edge_id, opp_dir_edge, cost, cost.cost, - travel_mode_t::kPedestrian, dist, baldr::kInvalidRestriction, true, + travel_mode_t::kPedestrian, dist, baldr::kInvalidRestriction, false, false, InternalTurn::kNoTurn); } edgelabels_.back().set_origin(); diff --git a/src/thor/timedistancematrix.cc b/src/thor/timedistancematrix.cc index a3762e8ae7..c8abb4a633 100644 --- a/src/thor/timedistancematrix.cc +++ b/src/thor/timedistancematrix.cc @@ -105,7 +105,7 @@ void TimeDistanceMatrix::Expand(GraphReader& graphreader, // Skip this edge if permanently labeled (best path already found to this // directed edge), if no access is allowed to this edge (based on costing // method), or if a complex restriction prevents this path. - uint8_t restriction_idx = -1; + uint8_t restriction_idx = kInvalidRestriction; const bool is_dest = dest_edges_.find(edgeid) != dest_edges_.cend(); if (FORWARD) { if (!costing_->Allowed(directededge, is_dest, pred, tile, edgeid, offset_time.local_time, @@ -149,16 +149,26 @@ void TimeDistanceMatrix::Expand(GraphReader& graphreader, // Add to the adjacency list and edge labels. uint32_t idx = edgelabels_.size(); - sif::InternalTurn turn_type = - FORWARD ? costing_->TurnType(pred.opp_local_idx(), nodeinfo, directededge) - : costing_->TurnType(directededge->localedgeidx(), nodeinfo, opp_edge, opp_pred_edge); - - edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, mode_, - path_distance, restriction_idx, - (pred.closure_pruning() || !costing_->IsClosed(directededge, tile)), - static_cast(flow_sources & kDefaultFlowMask), turn_type, 0, - directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + if (FORWARD) { + edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, mode_, + path_distance, restriction_idx, + (pred.closure_pruning() || !(costing_->IsClosed(directededge, tile))), + 0 != (flow_sources & kDefaultFlowMask), + costing_->TurnType(pred.opp_local_idx(), nodeinfo, directededge), 0, + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv())); + } else { + edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, mode_, + path_distance, restriction_idx, + (pred.closure_pruning() || !(costing_->IsClosed(opp_edge, t2))), + 0 != (flow_sources & kDefaultFlowMask), + costing_->TurnType(directededge->localedgeidx(), nodeinfo, opp_edge, + opp_pred_edge), + 0, + opp_edge->destonly() || + (costing_->is_hgv() && opp_edge->destonly_hgv())); + } + *es = {EdgeSet::kTemporary, idx}; adjacencylist_.add(idx); } @@ -370,8 +380,8 @@ void TimeDistanceMatrix::SetOrigin(GraphReader& graphreader, baldr::kInvalidRestriction, !costing_->IsClosed(directededge, tile), static_cast(flow_sources & kDefaultFlowMask), InternalTurn::kNoTurn, 0, - opp_dir_edge->destonly() || - (costing_->is_hgv() && opp_dir_edge->destonly_hgv())); + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv())); } edgelabels_.back().set_origin(); adjacencylist_.add(edgelabels_.size() - 1); diff --git a/src/thor/unidirectional_astar.cc b/src/thor/unidirectional_astar.cc index c0c4df74fa..15a055334d 100644 --- a/src/thor/unidirectional_astar.cc +++ b/src/thor/unidirectional_astar.cc @@ -281,7 +281,7 @@ inline bool UnidirectionalAStar::ExpandInner( edgelabels_.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, cost, sortcost, dist, mode_, transition_cost, (pred.not_thru_pruning() || !meta.edge->not_thru()), - (pred.closure_pruning() || !(costing_->IsClosed(meta.edge, tile))), + (pred.closure_pruning() || !(costing_->IsClosed(opp_edge, endtile))), 0 != (flow_sources & kDefaultFlowMask), costing_->TurnType(meta.edge->localedgeidx(), nodeinfo, opp_edge, opp_pred_edge), @@ -765,8 +765,8 @@ void UnidirectionalAStar::SetOrigin( !(costing_->IsClosed(directededge, tile)), 0 != (flow_sources & kDefaultFlowMask), sif::InternalTurn::kNoTurn, kInvalidRestriction, 0, - opp_dir_edge->destonly() || - (costing_->is_hgv() && opp_dir_edge->destonly_hgv())); + directededge->destonly() || + (costing_->is_hgv() && directededge->destonly_hgv())); } auto& edge_label = edgelabels_.back(); diff --git a/valhalla/sif/costfactory.h b/valhalla/sif/costfactory.h index a512f59720..a88eaed361 100644 --- a/valhalla/sif/costfactory.h +++ b/valhalla/sif/costfactory.h @@ -101,6 +101,7 @@ class CostFactory { mode_costing_t CreateModeCosting(const Options& options, TravelMode& mode) { mode_costing_t mode_costing; + mode = TravelMode::kMaxTravelMode; // Set travel mode and construct costing(s) for this type for (const auto& costing : kCostingTypeMapping.at(options.costing_type())) { valhalla::sif::cost_ptr_t cost = Create(options.costings().find(costing)->second); @@ -112,6 +113,11 @@ class CostFactory { // For multi-modal we set the initial mode to pedestrian. (TODO - allow other initial modes) mode = valhalla::sif::TravelMode::kPedestrian; } + // this should never happen + if (mode == TravelMode::kMaxTravelMode) { + throw std::runtime_error("sif::CostFactory couldn't find a valid TravelMode for " + + Costing_Enum_Name(options.costing_type())); + } return mode_costing; } From 6f9f6371c3c439a068d34dcdcd761aec4f731a80 Mon Sep 17 00:00:00 2001 From: Nils Date: Tue, 26 Mar 2024 12:44:41 +0100 Subject: [PATCH 055/198] no need to search for GDAL when building data (#4651) --- CHANGELOG.md | 1 + CMakeLists.txt | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1b0650332..16af4e7693 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ * FIXED: CostMatrix shapes for routes against trivial oneways [#4633](https://github.com/valhalla/valhalla/pull/4633) * FIXED: unidirectional_astar.cc doesn't work for date_time type = 2 #4652(https://github.com/valhalla/valhalla/issues/4652) * FIXED: a few fixes around the routing algorithms [#4626](https://github.com/valhalla/valhalla/pull/4642) + * FIXED: no need to search for GDAL when building data [#4651](https://github.com/valhalla/valhalla/pull/4651) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/CMakeLists.txt b/CMakeLists.txt index 209e06d3d0..3af4452a73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,8 +192,9 @@ else() endif() # unless you said you didnt want gdal we try to turn it on, if we cant we tell you +# we don't need it to build tiles (yet) set(GDAL_TARGET "") -if (ENABLE_GDAL) +if (ENABLE_GDAL AND (ENABLE_SERVICES OR ENABLE_TOOLS OR ENABLE_PYTHON_BINDINGS)) find_package(GDAL QUIET) if (GDAL_FOUND) set(GDAL_TARGET GDAL::GDAL) From f29329d686c52bd68713719d426fe5c2a4096286 Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Tue, 26 Mar 2024 11:13:24 -0600 Subject: [PATCH 056/198] Fix warnings test dir simple fixes more files (#4660) --- test/CMakeLists.txt | 5 ----- test/compression.cc | 14 ++++++------ test/countryaccess.cc | 12 ----------- test/edgecollapser.cc | 47 +++++++++++++++++++++-------------------- test/edgeinfobuilder.cc | 6 +++--- test/encode.cc | 12 ----------- 6 files changed, 35 insertions(+), 61 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 490d10d404..9553f436e4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -67,11 +67,6 @@ endif() # "-Werror" flag # Avoid adding a test to this list set(tests_with_warnings - compression - countryaccess - encode - edgecollapser - edgeinfobuilder filesystem graphbuilder graphparser diff --git a/test/compression.cc b/test/compression.cc index f0509db6db..1c3bed9a43 100644 --- a/test/compression.cc +++ b/test/compression.cc @@ -68,11 +68,11 @@ TEST(Compression, roundtrip) { } TEST(Compression, fail_deflate) { - auto deflate_src_fail = [](z_stream& s) -> int { + auto deflate_src_fail = []([[maybe_unused]] z_stream& s) -> int { throw std::runtime_error("you cant catch me"); return Z_FINISH; }; - auto deflate_dst_fail = [](z_stream& s) -> void { + auto deflate_dst_fail = []([[maybe_unused]] z_stream& s) -> void { throw std::runtime_error("im the gingerbread man"); }; @@ -89,16 +89,18 @@ TEST(Compression, fail_deflate) { } TEST(Compression, fail_inflate) { - auto inflate_src_fail = [](z_stream& s) -> void { throw std::runtime_error("you cant catch me"); }; + auto inflate_src_fail = []([[maybe_unused]] z_stream& s) -> void { + throw std::runtime_error("you cant catch me"); + }; std::string bad = "this isn't gzipped"; auto inflate_src_fail2 = [&bad](z_stream& s) -> void { s.next_in = static_cast(static_cast(&bad[0])); s.avail_in = static_cast(bad.size() * sizeof(std::string::value_type)); }; - auto inflate_src_fail3 = [](z_stream& s) -> void { + auto inflate_src_fail3 = []([[maybe_unused]] z_stream& s) -> void { /* Nothing to do, simulates 'cannot inflate' - reproducible if no disk space. */ }; - auto inflate_dst_fail = [](z_stream& s) -> int { + auto inflate_dst_fail = []([[maybe_unused]] z_stream& s) -> int { throw std::runtime_error("im the gingerbread man"); return Z_NO_FLUSH; }; @@ -128,7 +130,7 @@ TEST(Compression, fail_inflate) { inflate_dst_fail)) << "dst should fail"; - bool inflate_result; + bool inflate_result{false}; EXPECT_NO_THROW( inflate_result = valhalla::baldr::inflate(inflate_src_fail3, std::bind(inflate_dst, std::placeholders::_1, diff --git a/test/countryaccess.cc b/test/countryaccess.cc index 25cc90ee99..eee9e824bd 100644 --- a/test/countryaccess.cc +++ b/test/countryaccess.cc @@ -59,20 +59,8 @@ const auto node_predicate = [](const OSMWayNode& a, const OSMWayNode& b) { return a.node.osmid_ < b.node.osmid_; }; -OSMNode GetNode(uint64_t node_id, sequence& way_nodes) { - auto found = way_nodes.find({node_id}, node_predicate); - EXPECT_NE(found, way_nodes.end()) << "Couldn't find node: " + std::to_string(node_id); - return (*found).node; -} - auto way_predicate = [](const OSMWay& a, const OSMWay& b) { return a.osmwayid_ < b.osmwayid_; }; -OSMWay GetWay(uint32_t way_id, sequence& ways) { - auto found = ways.find({way_id}, way_predicate); - EXPECT_NE(found, ways.end()) << "Couldn't find way: " + std::to_string(way_id); - return *found; -} - void CountryAccess(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); diff --git a/test/edgecollapser.cc b/test/edgecollapser.cc index ff3c95a1e9..7e4f63d38a 100644 --- a/test/edgecollapser.cc +++ b/test/edgecollapser.cc @@ -105,12 +105,12 @@ struct test_graph_reader : public vb::GraphReader { TEST(EdgeCollapser, TestCollapseEdgeSimple) { vb::GraphId base_id = vb::TileHierarchy::GetGraphId(valhalla::midgard::PointLL(0, 0), 0); - // simplest graph with a collapsible node: - // - // /---(edge 0)-->\ /---(edge 1)-->\ - // (node 0) (node 1) (node 2) - // \<--(edge 2)---/ \<--(edge 3)---/ - // + /* simplest graph with a collapsible node: + + /---(edge 0)-->\ /---(edge 1)-->\ + (node 0) (node 1) (node 2) + \<--(edge 2)---/ \<--(edge 3)---/ + */ valhalla::midgard::PointLL base_ll(0.0f, 0.0f); graph_tile_builder builder; builder.append_node(base_ll, 0.00f, 0.0f, 1, 0); @@ -154,16 +154,17 @@ TEST(EdgeCollapser, TestCollapseEdgeSimple) { TEST(EdgeCollapser, TestCollapseEdgeJunction) { vb::GraphId base_id = vb::TileHierarchy::GetGraphId(valhalla::midgard::PointLL(0, 0), 0); - // simplest graph with a non-collapsible node: - // - // /---(edge 0)-->\ /---(edge 1)-->\ - // (node 0) (node 1) (node 2) - // \<--(edge 2)---/ / \ \<--(edge 4)---/ - // | | - // (edge 5) ^ v (edge 3) - // | | - // \ / - // (node 3) + /* simplest graph with a non-collapsible node: + + /---(edge 0)-->\ /---(edge 1)-->\ + (node 0) (node 1) (node 2) + \<--(edge 2)---/ / \ \<--(edge 4)---/ + | | + (edge 5) ^ v (edge 3) + | | + \ / + (node 3) + */ valhalla::midgard::PointLL base_ll(0.0f, 0.0f); graph_tile_builder builder; builder.append_node(base_ll, 0.00f, 0.00f, 1, 0); @@ -210,13 +211,13 @@ TEST(EdgeCollapser, TestCollapseEdgeJunction) { TEST(EdgeCollapser, TestCollapseEdgeChain) { vb::GraphId base_id = vb::TileHierarchy::GetGraphId(valhalla::midgard::PointLL(0, 0), 0); - // graph with 3 collapsible edges, all chained together. (e.g: think of the - // middle segment as a bridge). - // - // /---(e0)-->\ /---(e1)-->\ /---(e3)-->\ - // (n0) (n1) (n2) (n3) - // \<--(e2)---/ \<--(e4)---/ \<--(e5)---/ - // + /* graph with 3 collapsible edges, all chained together. (e.g: think of the + middle segment as a bridge). + + /---(e0)-->\ /---(e1)-->\ /---(e3)-->\ + (n0) (n1) (n2) (n3) + \<--(e2)---/ \<--(e4)---/ \<--(e5)---/ + */ valhalla::midgard::PointLL base_ll(0.0f, 0.0f); graph_tile_builder builder; builder.append_node(base_ll, 0.00f, 0.0f, 1, 0); diff --git a/test/edgeinfobuilder.cc b/test/edgeinfobuilder.cc index dcd86f398f..cbdd7fa120 100644 --- a/test/edgeinfobuilder.cc +++ b/test/edgeinfobuilder.cc @@ -66,9 +66,9 @@ TEST(EdgeInfoBuilder, TestWriteRead) { // Name std::vector name_info_list; - name_info_list.push_back({963}); - name_info_list.push_back({957}); - name_info_list.push_back({862}); + name_info_list.push_back({963, 0, 0, 0, 0}); + name_info_list.push_back({957, 0, 0, 0, 0}); + name_info_list.push_back({862, 0, 0, 0, 0}); eibuilder.set_name_info_list(name_info_list); // Shape diff --git a/test/encode.cc b/test/encode.cc index fdf619755e..37a346d67c 100644 --- a/test/encode.cc +++ b/test/encode.cc @@ -19,18 +19,6 @@ void assert_approx_equal(const container_t& a, const container_t& b, const float } } -// need ostream operators for some of these types -std::string to_string(const container_t& points) { - std::string out = "{"; - for (const auto& p : points) { - out += "{" + std::to_string(p.first) + ", " + std::to_string(p.second) + "}"; - } - out += "}"; - if (out.length() > 2) - out.erase(out.end() - 3, out.end() - 1); - return out; -} - void do_polyline_pair(const container_t& points, const std::string& encoded) { auto enc_answer = encode(points); EXPECT_EQ(enc_answer, encoded) << "Simple polyline encoding failed"; From 0a36ae1e7327c13742dad21b3b31c8c56ff50a5c Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Tue, 26 Mar 2024 14:26:59 -0600 Subject: [PATCH 057/198] Marking unused code (#4661) --- test/gurka/CMakeLists.txt | 1 - test/gurka/test_instructions_named_roundabout.cc | 13 ++++++++----- test/gurka/test_instructions_roundabout.cc | 12 +++++++----- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/test/gurka/CMakeLists.txt b/test/gurka/CMakeLists.txt index 6e8a28733d..7c516a1b29 100644 --- a/test/gurka/CMakeLists.txt +++ b/test/gurka/CMakeLists.txt @@ -12,7 +12,6 @@ if(ENABLE_DATA_TOOLS) # Avoid adding a filename to this list set(TESTS_WITH_WARNINGS test_gtfs.cc - test_instructions_roundabout.cc test_languages.cc test_locate.cc test_multi_level_loki.cc diff --git a/test/gurka/test_instructions_named_roundabout.cc b/test/gurka/test_instructions_named_roundabout.cc index b233c76b1f..27b591f79c 100644 --- a/test/gurka/test_instructions_named_roundabout.cc +++ b/test/gurka/test_instructions_named_roundabout.cc @@ -71,11 +71,14 @@ TEST_F(InstructionsNamedRoundabout, RoundaboutEnterOnly) { // TODO: known issue - future update to end on a roundabout // Verify the enter_roundabout instructions - // gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, - // "Enter the roundabout.", - // "Enter Dupont Circle.", - // "Enter Dupont Circle.", - // "Enter Dupont Circle.", ""); +#if 0 + int maneuver_index = 1; + gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, + "Enter the roundabout.", + "Enter Dupont Circle.", + "Enter Dupont Circle.", + "Enter Dupont Circle.", ""); +#endif } // enter_roundabout_verbal diff --git a/test/gurka/test_instructions_roundabout.cc b/test/gurka/test_instructions_roundabout.cc index 65e5b8a201..02325886cd 100644 --- a/test/gurka/test_instructions_roundabout.cc +++ b/test/gurka/test_instructions_roundabout.cc @@ -69,11 +69,13 @@ TEST_F(InstructionsRoundabout, RoundaboutEnterOnly) { // TODO: known issue - future update to end on a roundabout // Verify the enter_roundabout instructions - // gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, - // "Enter the roundabout.", "Enter - // the roundabout.", "Enter the - // roundabout.", "Enter the - // roundabout.", ""); +#if 0 + gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, + "Enter the roundabout.", + "Enter the roundabout.", + "Enter the roundabout.", + "Enter the roundabout.", ""); +#endif } // enter_roundabout_verbal From 58929bd063981c8a2d8544afa56b069d78afa495 Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Tue, 26 Mar 2024 17:36:10 -0600 Subject: [PATCH 058/198] (test/gurka/gurka.*) Using C++17 filesystem (#4662) --- test/gurka/gurka.cc | 8 ++++---- test/gurka/gurka.h | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/test/gurka/gurka.cc b/test/gurka/gurka.cc index 3a07c947f0..32005fa2c3 100644 --- a/test/gurka/gurka.cc +++ b/test/gurka/gurka.cc @@ -2,7 +2,6 @@ #include "baldr/graphid.h" #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" -#include "filesystem.h" #include "loki/worker.h" #include "midgard/constants.h" #include "midgard/encoded.h" @@ -29,6 +28,7 @@ #include #include +#include #include #include #include @@ -461,9 +461,9 @@ map buildtiles(const nodelayout& layout, throw std::runtime_error("Can't use / for tests, as we need to clean it out first"); } - if (filesystem::exists(workdir)) - filesystem::remove_all(workdir); - filesystem::create_directories(workdir); + if (std::filesystem::exists(workdir)) + std::filesystem::remove_all(workdir); + std::filesystem::create_directories(workdir); auto pbf_filename = workdir + "/map.pbf"; std::cerr << "[ ] generating map PBF at " << pbf_filename << std::endl; diff --git a/test/gurka/gurka.h b/test/gurka/gurka.h index 1d3320b868..5609a33a61 100644 --- a/test/gurka/gurka.h +++ b/test/gurka/gurka.h @@ -12,7 +12,6 @@ #include "baldr/graphid.h" #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" -#include "filesystem.h" #include "loki/worker.h" #include "midgard/constants.h" #include "midgard/encoded.h" From 1ae956d9986d719035e549cf06b97847e1fc04a7 Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Fri, 29 Mar 2024 06:21:43 -0600 Subject: [PATCH 059/198] (test/gurka/) Using C++17 filesystem with few changes (#4664) --- test/gurka/gurka.cc | 2 +- .../test_admin_sidewalk_crossing_override.cc | 7 ++++--- test/gurka/test_admin_uk_override.cc | 6 +++--- test/gurka/test_config_speed.cc | 10 ++++++---- test/gurka/test_elevation.cc | 6 ++++-- test/gurka/test_landmarks.cc | 16 ++++++++-------- test/gurka/test_route_summary.cc | 5 +++-- test/gurka/test_stop_signs.cc | 6 ++++-- test/gurka/test_traffic_signals.cc | 6 ++++-- test/gurka/test_yield_signs.cc | 6 ++++-- 10 files changed, 41 insertions(+), 29 deletions(-) diff --git a/test/gurka/gurka.cc b/test/gurka/gurka.cc index 32005fa2c3..6fece2c239 100644 --- a/test/gurka/gurka.cc +++ b/test/gurka/gurka.cc @@ -28,7 +28,7 @@ #include #include -#include +#include #include #include #include diff --git a/test/gurka/test_admin_sidewalk_crossing_override.cc b/test/gurka/test_admin_sidewalk_crossing_override.cc index 9756ad8a89..201bf3731b 100644 --- a/test/gurka/test_admin_sidewalk_crossing_override.cc +++ b/test/gurka/test_admin_sidewalk_crossing_override.cc @@ -1,7 +1,8 @@ +#include + #include #include "baldr/admin.h" -#include "filesystem.h" #include "gurka.h" #include "mjolnir/admin.h" #include "mjolnir/adminbuilder.h" @@ -119,8 +120,8 @@ TEST(AdminTest, TestBuildAdminFromPBF) { // Create test/data/admin/map.pbf const std::string workdir = "test/data/admin_belarus"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_admin_uk_override.cc b/test/gurka/test_admin_uk_override.cc index ef6d328e4c..e6680b21ee 100644 --- a/test/gurka/test_admin_uk_override.cc +++ b/test/gurka/test_admin_uk_override.cc @@ -1,7 +1,7 @@ +#include #include #include "baldr/admin.h" -#include "filesystem.h" #include "gurka.h" #include "mjolnir/admin.h" #include "mjolnir/adminbuilder.h" @@ -115,8 +115,8 @@ TEST(AdminTest, TestBuildAdminFromPBF) { // Create test/data/admin/map.pbf const std::string workdir = "test/data/admin_uk"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_config_speed.cc b/test/gurka/test_config_speed.cc index 9d4e33e05a..105608a297 100644 --- a/test/gurka/test_config_speed.cc +++ b/test/gurka/test_config_speed.cc @@ -2,6 +2,8 @@ #include "src/mjolnir/speed_assigner.h" #include +#include + using namespace valhalla; TEST(Standalone, DefaultSpeedConfig) { @@ -129,9 +131,9 @@ TEST(Standalone, DefaultSpeedConfig) { } } - if (!filesystem::create_directories("test/data")) + if (!std::filesystem::exists("test/data") && !std::filesystem::create_directories("test/data")) throw std::runtime_error("couldn't create directories"); - filesystem::remove("test/data/speed_config.json"); + std::filesystem::remove("test/data/speed_config.json"); { std::ofstream speed_config("test/data/speed_config.json"); @@ -281,9 +283,9 @@ TEST(Standalone, SuburbanSpeedConfig) { } } - if (!filesystem::create_directories("test/data")) + if (!std::filesystem::exists("test/data") && !std::filesystem::create_directories("test/data")) throw std::runtime_error("couldn't create directories"); - filesystem::remove("test/data/speed_config_suburban.json"); + std::filesystem::remove("test/data/speed_config_suburban.json"); { std::ofstream speed_config("test/data/speed_config_suburban.json"); diff --git a/test/gurka/test_elevation.cc b/test/gurka/test_elevation.cc index ba2916a019..1c9656ea7f 100644 --- a/test/gurka/test_elevation.cc +++ b/test/gurka/test_elevation.cc @@ -5,6 +5,8 @@ #include "loki/worker.h" #include "midgard/pointll.h" +#include + #include #include #include @@ -125,8 +127,8 @@ TEST(Standalone, ElevationCompareToSkadi) { } } - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_landmarks.cc b/test/gurka/test_landmarks.cc index 53eece209c..b449bed419 100644 --- a/test/gurka/test_landmarks.cc +++ b/test/gurka/test_landmarks.cc @@ -248,8 +248,8 @@ TEST(LandmarkTest, TestBuildDatabase) { } TEST(LandmarkTest, TestParseLandmarks) { - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } @@ -393,8 +393,8 @@ TEST(LandmarkTest, TestTileStoreLandmarks) { } TEST(LandmarkTest, TestAddLandmarksToTiles) { - if (!filesystem::exists(workdir_tiles)) { - bool created = filesystem::create_directories(workdir_tiles); + if (!std::filesystem::exists(workdir_tiles)) { + bool created = std::filesystem::create_directories(workdir_tiles); EXPECT_TRUE(created); } @@ -429,8 +429,8 @@ TEST(LandmarkTest, TestAddLandmarksToTiles) { // hierarchy max level", and "Could not compute FileSuffix for GraphId with invalid tile // id:0/245760/0". We need to fix it in the future. TEST(LandmarkTest, DISABLED_ErrorTest) { - if (!filesystem::exists(workdir_tiles)) { - bool created = filesystem::create_directories(workdir_tiles); + if (!std::filesystem::exists(workdir_tiles)) { + bool created = std::filesystem::create_directories(workdir_tiles); EXPECT_TRUE(created); } @@ -508,8 +508,8 @@ TEST(LandmarkTest, TestLandmarksInManeuvers) { const std::string db_path = workdir + "/landmarks.sqlite"; const std::string pbf = workdir + "/map.pbf"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_route_summary.cc b/test/gurka/test_route_summary.cc index cf4d3d96a6..0ad3c1f8e5 100644 --- a/test/gurka/test_route_summary.cc +++ b/test/gurka/test_route_summary.cc @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -112,7 +113,7 @@ TEST(TestRouteSummary, GetSummary) { gurka::assert::osrm::expect_summaries(result, {expected_route_summary0, expected_route_summary1, expected_route_summary2}); - filesystem::remove_all(workdir); + std::filesystem::remove_all(workdir); } TEST(TestRouteSummary, DupSummaryFix) { @@ -202,5 +203,5 @@ TEST(TestRouteSummary, DupSummaryFix) { const std::string expected_route_summary1 = "RT 1, RT 2, RT 4"; gurka::assert::osrm::expect_summaries(result, {expected_route_summary0, expected_route_summary1}); - filesystem::remove_all(workdir); + std::filesystem::remove_all(workdir); } diff --git a/test/gurka/test_stop_signs.cc b/test/gurka/test_stop_signs.cc index c09b6fe3ed..135ece6580 100644 --- a/test/gurka/test_stop_signs.cc +++ b/test/gurka/test_stop_signs.cc @@ -1,3 +1,5 @@ +#include + #include "gurka.h" #include "test/test.h" #include @@ -72,8 +74,8 @@ TEST(Standalone, StopSigns) { const std::string workdir = "test/data/gurka_stop_signs"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_traffic_signals.cc b/test/gurka/test_traffic_signals.cc index a45a6810d3..46611e3224 100644 --- a/test/gurka/test_traffic_signals.cc +++ b/test/gurka/test_traffic_signals.cc @@ -1,3 +1,5 @@ +#include + #include "gurka.h" #include "test/test.h" #include @@ -73,8 +75,8 @@ TEST(Standalone, TrafficSignals) { const std::string workdir = "test/data/gurka_traffic_signals"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } diff --git a/test/gurka/test_yield_signs.cc b/test/gurka/test_yield_signs.cc index de1fe8f803..771eaa53a3 100644 --- a/test/gurka/test_yield_signs.cc +++ b/test/gurka/test_yield_signs.cc @@ -1,3 +1,5 @@ +#include + #include "gurka.h" #include "test/test.h" #include @@ -72,8 +74,8 @@ TEST(Standalone, yieldSigns) { const std::string workdir = "test/data/gurka_yeild_signs"; - if (!filesystem::exists(workdir)) { - bool created = filesystem::create_directories(workdir); + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); EXPECT_TRUE(created); } From fdf1204f860f7eb27bacd5caaefac5898692b340 Mon Sep 17 00:00:00 2001 From: Nils Date: Tue, 2 Apr 2024 11:34:07 +0200 Subject: [PATCH 060/198] truck penalty for hgv=no edges (#4650) --- CHANGELOG.md | 1 + docs/docs/api/turn-by-turn/api-reference.md | 1 + lua/graph.lua | 22 +++--- proto/options.proto | 3 + src/mjolnir/pbfgraphparser.cc | 3 +- src/mjolnir/restrictionbuilder.cc | 2 +- src/sif/truckcost.cc | 38 +++++++--- src/thor/bidirectional_astar.cc | 12 ++- src/thor/costmatrix.cc | 12 ++- src/thor/dijkstras.cc | 12 ++- src/thor/timedistancematrix.cc | 12 ++- src/thor/unidirectional_astar.cc | 12 ++- test/gurka/test_matrix.cc | 69 +++++++++++++++++ test/gurka/test_route.cc | 82 +++++++++++++++++++++ valhalla/sif/dynamiccost.h | 18 ++++- valhalla/sif/edgelabel.h | 49 +++++++++--- 16 files changed, 291 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16af4e7693..77ce4845d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,6 +106,7 @@ * ADDED: return isotile grid as geotiff [#4594](https://github.com/valhalla/valhalla/pull/4594) * ADDED: `ignore_non_vehicular_restrictions` parameter for truck costing [#4606](https://github.com/valhalla/valhalla/pull/4606) * UPDATED: tz database to 2024a [#4643](https://github.com/valhalla/valhalla/pull/4643) + * ADDED: `hgv_no_penalty` costing option to allow penalized truck access to `hgv=no` edges [#4650](https://github.com/valhalla/valhalla/pull/4650) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 1676f3895c..a5961c38b7 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -148,6 +148,7 @@ The following options are available for `truck` costing. | `axle_load` | The axle load of the truck (in metric tons). Default 9.07. | | `axle_count` | The axle count of the truck. Default 5. | | `hazmat` | A value indicating if the truck is carrying hazardous materials. Default false. | +| `hgv_no_access_penalty` | A penalty applied to roads with no HGV/truck access. If set to a value less than 43200 seconds, HGV will be allowed on these roads and the penalty applies. Default 43200, i.e. trucks are not allowed. | ##### Bicycle costing options The default bicycle costing is tuned toward road bicycles with a slight preference for using [cycleways](http://wiki.openstreetmap.org/wiki/Key:cycleway) or roads with bicycle lanes. Bicycle routes use regular roads where needed or where no direct bicycle lane options exist, but avoid roads without bicycle access. The costing model recognizes several factors unique to bicycle travel and offers several options for tuning bicycle routes. Several factors unique to travel by bicycle influence the resulting route. diff --git a/lua/graph.lua b/lua/graph.lua index b27d4af43b..0067cf1c25 100644 --- a/lua/graph.lua +++ b/lua/graph.lua @@ -91,7 +91,7 @@ access = { ["forestry"] = "false", ["destination"] = "true", ["customers"] = "true", -["official"] = "false", +["official"] = "true", ["public"] = "true", ["restricted"] = "true", ["allowed"] = "true", @@ -155,7 +155,7 @@ motor_vehicle = { ["forestry"] = "false", ["destination"] = "true", ["customers"] = "true", -["official"] = "false", +["official"] = "true", ["public"] = "true", ["restricted"] = "true", ["allowed"] = "true", @@ -232,7 +232,7 @@ bus = { ["restricted"] = "true", ["destination"] = "true", ["delivery"] = "false", -["official"] = "false", +["official"] = "true", ["permit"] = "true" } @@ -245,7 +245,7 @@ taxi = { ["restricted"] = "true", ["destination"] = "true", ["delivery"] = "false", -["official"] = "false", +["official"] = "true", ["permit"] = "true" } @@ -270,10 +270,10 @@ truck = { ["agricultural"] = "false", ["private"] = "true", ["discouraged"] = "false", -["permissive"] = "false", +["permissive"] = "true", ["unsuitable"] = "false", ["agricultural;forestry"] = "false", -["official"] = "false", +["official"] = "true", ["forestry"] = "false", ["destination;delivery"] = "true", ["permit"] = "true", @@ -537,7 +537,7 @@ motor_cycle_node = { ["forestry"] = 0, ["destination"] = 1024, ["customers"] = 1024, -["official"] = 0, +["official"] = 1024, ["public"] = 1024, ["restricted"] = 1024, ["allowed"] = 1024, @@ -553,7 +553,7 @@ bus_node = { ["restricted"] = 64, ["destination"] = 64, ["delivery"] = 0, -["official"] = 0, +["official"] = 64, ["permit"] = 64 } @@ -566,7 +566,7 @@ taxi_node = { ["restricted"] = 32, ["destination"] = 32, ["delivery"] = 0, -["official"] = 0, +["official"] = 32, ["permit"] = 32 } @@ -580,10 +580,10 @@ truck_node = { ["agricultural"] = 0, ["private"] = 8, ["discouraged"] = 0, -["permissive"] = 0, +["permissive"] = 8, ["unsuitable"] = 0, ["agricultural;forestry"] = 0, -["official"] = 0, +["official"] = 8, ["forestry"] = 0, ["destination;delivery"] = 8, ["permit"] = 8, diff --git a/proto/options.proto b/proto/options.proto index 79549f216a..afb3866ccc 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -313,6 +313,9 @@ message Costing { float use_lit = 82; bool disable_hierarchy_pruning = 83; bool ignore_non_vehicular_restrictions = 84; + oneof has_hgv_no_access_penalty { + float hgv_no_access_penalty = 85; + } } oneof has_options { diff --git a/src/mjolnir/pbfgraphparser.cc b/src/mjolnir/pbfgraphparser.cc index 24663c5fbb..3312c05eff 100644 --- a/src/mjolnir/pbfgraphparser.cc +++ b/src/mjolnir/pbfgraphparser.cc @@ -4990,7 +4990,8 @@ void PBFGraphParser::ParseRelations(const boost::property_tree::ptree& pt, OSMPBF::Interest::CHANGESETS), callback); } - LOG_INFO("Finished with " + std::to_string(osmdata.restrictions.size()) + " simple restrictions"); + LOG_INFO("Finished with " + std::to_string(osmdata.restrictions.size()) + + " simple turn restrictions"); LOG_INFO("Finished with " + std::to_string(osmdata.lane_connectivity_map.size()) + " lane connections"); diff --git a/src/mjolnir/restrictionbuilder.cc b/src/mjolnir/restrictionbuilder.cc index 6c8c050a81..da322d954e 100644 --- a/src/mjolnir/restrictionbuilder.cc +++ b/src/mjolnir/restrictionbuilder.cc @@ -752,7 +752,7 @@ void RestrictionBuilder::Build(const boost::property_tree::ptree& pt, std::vector> promises(threads.size()); // Start the threads - LOG_INFO("Adding Restrictions at level " + std::to_string(tl->level)); + LOG_INFO("Adding complex turn restrictions at level " + std::to_string(tl->level)); for (size_t i = 0; i < threads.size(); ++i) { threads[i].reset(new std::thread(build, std::cref(complex_from_restrictions_file), std::cref(complex_to_restrictions_file), diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index 94b8b4ba42..de5c415939 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -88,16 +88,17 @@ constexpr float kSurfaceFactor[] = { }; // Valid ranges and defaults -constexpr ranged_default_t kLowClassPenaltyRange{0, kDefaultLowClassPenalty, kMaxPenalty}; -constexpr ranged_default_t kTruckWeightRange{0, kDefaultTruckWeight, 100.0f}; -constexpr ranged_default_t kTruckAxleLoadRange{0, kDefaultTruckAxleLoad, 40.0f}; -constexpr ranged_default_t kTruckHeightRange{0, kDefaultTruckHeight, 10.0f}; -constexpr ranged_default_t kTruckWidthRange{0, kDefaultTruckWidth, 10.0f}; -constexpr ranged_default_t kTruckLengthRange{0, kDefaultTruckLength, 50.0f}; -constexpr ranged_default_t kUseTollsRange{0, kDefaultUseTolls, 1.0f}; +constexpr ranged_default_t kLowClassPenaltyRange{0.f, kDefaultLowClassPenalty, kMaxPenalty}; +constexpr ranged_default_t kTruckWeightRange{0.f, kDefaultTruckWeight, 100.0f}; +constexpr ranged_default_t kTruckAxleLoadRange{0.f, kDefaultTruckAxleLoad, 40.0f}; +constexpr ranged_default_t kTruckHeightRange{0.f, kDefaultTruckHeight, 10.0f}; +constexpr ranged_default_t kTruckWidthRange{0.f, kDefaultTruckWidth, 10.0f}; +constexpr ranged_default_t kTruckLengthRange{0.f, kDefaultTruckLength, 50.0f}; +constexpr ranged_default_t kUseTollsRange{0.f, kDefaultUseTolls, 1.0f}; constexpr ranged_default_t kAxleCountRange{2, kDefaultAxleCount, 20}; -constexpr ranged_default_t kUseHighwaysRange{0, kDefaultUseHighways, 1.0f}; -constexpr ranged_default_t kTopSpeedRange{10, kMaxAssumedTruckSpeed, kMaxSpeedKph}; +constexpr ranged_default_t kUseHighwaysRange{0.f, kDefaultUseHighways, 1.0f}; +constexpr ranged_default_t kTopSpeedRange{10.f, kMaxAssumedTruckSpeed, kMaxSpeedKph}; +constexpr ranged_default_t kHGVNoAccessRange{0.f, kMaxPenalty, kMaxPenalty}; BaseCostingOptionsConfig GetBaseCostOptsConfig() { BaseCostingOptionsConfig cfg{}; @@ -311,6 +312,9 @@ class TruckCost : public DynamicCost { // Density factor used in edge transition costing std::vector trans_density_factor_; + + // determine if we should allow hgv=no edges and penalize them instead + float no_hgv_access_penalty_; }; // Constructor @@ -368,6 +372,12 @@ TruckCost::TruckCost(const Costing& costing) for (uint32_t d = 0; d < 16; d++) { density_factor_[d] = 0.85f + (d * 0.025f); } + + // determine what to do with hgv=no edges + bool no_hgv_access_penalty_active = !(costing_options.hgv_no_access_penalty() == kMaxPenalty); + no_hgv_access_penalty_ = no_hgv_access_penalty_active * costing_options.hgv_no_access_penalty(); + // set the access mask to both car & truck if that penalty is active + access_mask_ = no_hgv_access_penalty_active ? (kAutoAccess | kTruckAccess) : kTruckAccess; } // Destructor @@ -551,6 +561,10 @@ Cost TruckCost::TransitionCost(const baldr::DirectedEdge* edge, c.cost += low_class_penalty_; } + // Penalty if the request wants to avoid hgv=no edges instead of disallowing + c.cost += + no_hgv_access_penalty_ * (pred.has_hgv_access() && !(edge->forwardaccess() & kTruckAccess)); + // Transition time = turncost * stopimpact * densityfactor if (edge->stopimpact(idx) > 0 && !shortest_) { float turn_cost; @@ -624,6 +638,10 @@ Cost TruckCost::TransitionCostReverse(const uint32_t idx, c.cost += low_class_penalty_; } + // Penalty if the request wants to avoid hgv=no edges instead of disallowing + c.cost += no_hgv_access_penalty_ * + ((pred->forwardaccess() & kTruckAccess) && !(edge->forwardaccess() & kTruckAccess)); + // Transition time = turncost * stopimpact * densityfactor if (edge->stopimpact(idx) > 0 && !shortest_) { float turn_cost; @@ -713,6 +731,8 @@ void ParseTruckCostOptions(const rapidjson::Document& doc, JSON_PBF_RANGED_DEFAULT(co, kUseHighwaysRange, json, "/use_highways", use_highways); JSON_PBF_RANGED_DEFAULT_V2(co, kAxleCountRange, json, "/axle_count", axle_count); JSON_PBF_RANGED_DEFAULT(co, kTopSpeedRange, json, "/top_speed", top_speed); + JSON_PBF_RANGED_DEFAULT(co, kHGVNoAccessRange, json, "/hgv_no_access_penalty", + hgv_no_access_penalty); } cost_ptr_t CreateTruckCost(const Costing& costing_options) { diff --git a/src/thor/bidirectional_astar.cc b/src/thor/bidirectional_astar.cc index 1d31b9bef2..e6907a2e22 100644 --- a/src/thor/bidirectional_astar.cc +++ b/src/thor/bidirectional_astar.cc @@ -329,7 +329,8 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, costing_->TurnType(pred.opp_local_idx(), nodeinfo, meta.edge), restriction_idx, 0, meta.edge->destonly() || - (costing_->is_hgv() && meta.edge->destonly_hgv())); + (costing_->is_hgv() && meta.edge->destonly_hgv()), + meta.edge->forwardaccess() & kTruckAccess); adjacencylist_forward_.add(idx); } else { idx = edgelabels_reverse_.size(); @@ -346,7 +347,8 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, opp_pred_edge), restriction_idx, 0, opp_edge->destonly() || - (costing_->is_hgv() && opp_edge->destonly_hgv())); + (costing_->is_hgv() && opp_edge->destonly_hgv()), + opp_edge->forwardaccess() & kTruckAccess); adjacencylist_reverse_.add(idx); } @@ -1017,7 +1019,8 @@ void BidirectionalAStar::SetOrigin(GraphReader& graphreader, static_cast(flow_sources & kDefaultFlowMask), sif::InternalTurn::kNoTurn, 0, directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); adjacencylist_forward_.add(idx); // setting this edge as reached @@ -1113,7 +1116,8 @@ void BidirectionalAStar::SetDestination(GraphReader& graphreader, static_cast(flow_sources & kDefaultFlowMask), sif::InternalTurn::kNoTurn, kInvalidRestriction, directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); adjacencylist_reverse_.add(idx); // setting this edge as reached, sending the opposing because this is the reverse tree diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index b260177e41..21e440c93d 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -527,7 +527,8 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, costing_->TurnType(pred.opp_local_idx(), nodeinfo, meta.edge), restriction_idx, 0, meta.edge->destonly() || - (costing_->is_hgv() && meta.edge->destonly_hgv())); + (costing_->is_hgv() && meta.edge->destonly_hgv()), + meta.edge->forwardaccess() & kTruckAccess); } else { edgelabels.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, newcost, mode_, tc, pred_dist, not_thru_pruning, @@ -536,7 +537,8 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, costing_->TurnType(meta.edge->localedgeidx(), nodeinfo, opp_edge, opp_pred_edge), restriction_idx, 0, - opp_edge->destonly() || (costing_->is_hgv() && opp_edge->destonly_hgv())); + opp_edge->destonly() || (costing_->is_hgv() && opp_edge->destonly_hgv()), + opp_edge->forwardaccess() & kTruckAccess); } adj.add(idx); // mark the edge as settled for the connection check @@ -1030,7 +1032,8 @@ void CostMatrix::SetSources(GraphReader& graphreader, InternalTurn::kNoTurn, kInvalidRestriction, static_cast(costing_->Allowed(directededge, tile)), directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); edge_label.set_not_thru(false); // Add EdgeLabel to the adjacency list (but do not set its status). @@ -1114,7 +1117,8 @@ void CostMatrix::SetTargets(baldr::GraphReader& graphreader, InternalTurn::kNoTurn, kInvalidRestriction, static_cast(costing_->Allowed(opp_dir_edge, opp_tile)), directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); edge_label.set_not_thru(false); // Add EdgeLabel to the adjacency list (but do not set its status). diff --git a/src/thor/dijkstras.cc b/src/thor/dijkstras.cc index 24174d02c3..1e4099b067 100644 --- a/src/thor/dijkstras.cc +++ b/src/thor/dijkstras.cc @@ -254,7 +254,8 @@ void Dijkstras::ExpandInner(baldr::GraphReader& graphreader, costing_->TurnType(pred.opp_local_idx(), nodeinfo, directededge), restriction_idx, pred.path_id(), directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); } else { bdedgelabels_.emplace_back(pred_idx, edgeid, oppedgeid, directededge, newcost, mode_, @@ -265,7 +266,8 @@ void Dijkstras::ExpandInner(baldr::GraphReader& graphreader, opp_pred_edge), restriction_idx, pred.path_id(), opp_edge->destonly() || - (costing_->is_hgv() && opp_edge->destonly_hgv())); + (costing_->is_hgv() && opp_edge->destonly_hgv()), + opp_edge->forwardaccess() & kTruckAccess); } adjacencylist_.add(idx); } @@ -823,7 +825,8 @@ void Dijkstras::SetOriginLocations(GraphReader& graphreader, static_cast(flow_sources & kDefaultFlowMask), InternalTurn::kNoTurn, kInvalidRestriction, multipath_ ? path_id : 0, directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); // Set the origin flag bdedgelabels_.back().set_origin(); @@ -914,7 +917,8 @@ void Dijkstras::SetDestinationLocations( static_cast(flow_sources & kDefaultFlowMask), InternalTurn::kNoTurn, restriction_idx, multipath_ ? path_id : 0, directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); adjacencylist_.add(idx); edgestatus_.Set(opp_edge_id, EdgeSet::kTemporary, idx, opp_tile, multipath_ ? path_id : 0); } diff --git a/src/thor/timedistancematrix.cc b/src/thor/timedistancematrix.cc index c8abb4a633..e6176b8a6e 100644 --- a/src/thor/timedistancematrix.cc +++ b/src/thor/timedistancematrix.cc @@ -156,7 +156,8 @@ void TimeDistanceMatrix::Expand(GraphReader& graphreader, 0 != (flow_sources & kDefaultFlowMask), costing_->TurnType(pred.opp_local_idx(), nodeinfo, directededge), 0, directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); } else { edgelabels_.emplace_back(pred_idx, edgeid, directededge, newcost, newcost.cost, mode_, path_distance, restriction_idx, @@ -166,7 +167,8 @@ void TimeDistanceMatrix::Expand(GraphReader& graphreader, opp_pred_edge), 0, opp_edge->destonly() || - (costing_->is_hgv() && opp_edge->destonly_hgv())); + (costing_->is_hgv() && opp_edge->destonly_hgv()), + opp_edge->forwardaccess() & kTruckAccess); } *es = {EdgeSet::kTemporary, idx}; @@ -374,14 +376,16 @@ void TimeDistanceMatrix::SetOrigin(GraphReader& graphreader, static_cast(flow_sources & kDefaultFlowMask), InternalTurn::kNoTurn, 0, directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); } else { edgelabels_.emplace_back(kInvalidLabel, opp_edge_id, opp_dir_edge, cost, cost.cost, mode_, dist, baldr::kInvalidRestriction, !costing_->IsClosed(directededge, tile), static_cast(flow_sources & kDefaultFlowMask), InternalTurn::kNoTurn, 0, directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); } edgelabels_.back().set_origin(); adjacencylist_.add(edgelabels_.size() - 1); diff --git a/src/thor/unidirectional_astar.cc b/src/thor/unidirectional_astar.cc index 15a055334d..036f9c2bea 100644 --- a/src/thor/unidirectional_astar.cc +++ b/src/thor/unidirectional_astar.cc @@ -276,7 +276,8 @@ inline bool UnidirectionalAStar::ExpandInner( costing_->TurnType(pred.opp_local_idx(), nodeinfo, meta.edge), restriction_idx, 0, meta.edge->destonly() || - (costing_->is_hgv() && meta.edge->destonly_hgv())); + (costing_->is_hgv() && meta.edge->destonly_hgv()), + meta.edge->forwardaccess() & kTruckAccess); } else { edgelabels_.emplace_back(pred_idx, meta.edge_id, opp_edge_id, meta.edge, cost, sortcost, dist, mode_, transition_cost, @@ -287,7 +288,8 @@ inline bool UnidirectionalAStar::ExpandInner( opp_pred_edge), restriction_idx, 0, opp_edge->destonly() || - (costing_->is_hgv() && opp_edge->destonly_hgv())); + (costing_->is_hgv() && opp_edge->destonly_hgv()), + opp_edge->forwardaccess() & kTruckAccess); } auto& edge_label = edgelabels_.back(); @@ -758,7 +760,8 @@ void UnidirectionalAStar::SetOrigin( 0 != (flow_sources & kDefaultFlowMask), sif::InternalTurn::kNoTurn, kInvalidRestriction, 0, directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); } else { edgelabels_.emplace_back(kInvalidLabel, opp_edge_id, edgeid, opp_dir_edge, cost, sortcost, dist, mode_, Cost{}, false, @@ -766,7 +769,8 @@ void UnidirectionalAStar::SetOrigin( 0 != (flow_sources & kDefaultFlowMask), sif::InternalTurn::kNoTurn, kInvalidRestriction, 0, directededge->destonly() || - (costing_->is_hgv() && directededge->destonly_hgv())); + (costing_->is_hgv() && directededge->destonly_hgv()), + directededge->forwardaccess() & kTruckAccess); } auto& edge_label = edgelabels_.back(); diff --git a/test/gurka/test_matrix.cc b/test/gurka/test_matrix.cc index 3500fcdaec..960841ebdf 100644 --- a/test/gurka/test_matrix.cc +++ b/test/gurka/test_matrix.cc @@ -820,3 +820,72 @@ TEST(StandAlone, CostMatrixTrivialRoutes) { EXPECT_EQ(matrix.matrix().shapes(0), encoded); } } + +TEST(StandAlone, HGVNoAccessPenalty) { + // if hgv_no_penalty is on we should still respect the maxweight restriction on CD + // so we should take the next-best hgv=no edge with JK + const std::string ascii_map = R"( + A-1-------B----C----D----E--2-------F + | | + J----K + | | + | | + L----M + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "residential"}, {"hgv", "no"}}}, + {"BC", {{"highway", "residential"}}}, + {"CD", {{"highway", "residential"}, {"hgv", "no"}, {"maxweight", "3.5"}}}, + {"DE", {{"highway", "residential"}}}, + {"EF", {{"highway", "residential"}, {"hgv", "no"}}}, + {"CJ", {{"highway", "residential"}}}, + {"JK", {{"highway", "residential"}, {"hgv", "no"}}}, + {"JLMK", {{"highway", "residential"}}}, + {"KD", {{"highway", "residential"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/hgv_no_access_penalty", + {{"service_limits.max_timedep_distance_matrix", "50000"}}); + + std::unordered_map cost_matrix = + {{"/costing_options/truck/hgv_no_access_penalty", "2000"}, + {"/sources/0/date_time", "2024-03-20T09:00"}, + {"/prioritize_bidirectional", "1"}}; + std::unordered_map td_matrix = + {{"/costing_options/truck/hgv_no_access_penalty", "2000"}, + {"/sources/0/date_time", "2024-03-20T09:00"}}; + + // do both costmatrix & timedistancematrix + std::vector> options = {cost_matrix, td_matrix}; + for (auto& truck_options : options) { + + // by default, take the detour via LM + // NOTE, we're not snapping to the hgv=no edges either + { + auto matrix = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, "truck"); + EXPECT_EQ(matrix.matrix().distances(0), 2500); + } + + // with a high hgv_no_penalty also take the detour via LM, but do snap to the hgv=no edges + { + auto matrix = gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, + "truck", truck_options); + // TODO(nils): timedistancematrix seems to have a tiny bug where time options result in slightly + // less distances + EXPECT_NEAR(matrix.matrix().distances(0), 3600, 2); + } + + // with a low hgv_no_penalty take the JK edge + { + truck_options["/costing_options/truck/hgv_no_access_penalty"] = "10"; + auto matrix = gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, + "truck", truck_options); + // TODO(nils): timedistancematrix seems to have a tiny bug where time options result in slightly + // less distances + EXPECT_NEAR(matrix.matrix().distances(0), 3000, 2); + } + } +} diff --git a/test/gurka/test_route.cc b/test/gurka/test_route.cc index a7ab49095a..2d62d04aca 100644 --- a/test/gurka/test_route.cc +++ b/test/gurka/test_route.cc @@ -1153,3 +1153,85 @@ TEST_F(DateTimeTest, Invariant) { } } } + +TEST(StandAlone, HGVNoAccessPenalty) { + // if hgv_no_penalty is on we should still respect the maxweight restriction on CD + // so we should take the next-best hgv=no edge with JK + const std::string ascii_map = R"( + A-1--B----C----D----E--2-F----G----H--3-I + | | + J----K + | | + | | + L----M + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "residential"}, {"hgv", "no"}}}, + {"BC", {{"highway", "residential"}}}, + {"CD", {{"highway", "residential"}, {"hgv", "no"}, {"maxweight", "3.5"}}}, + {"DE", {{"highway", "residential"}}}, + {"EF", {{"highway", "residential"}, {"hgv", "no"}}}, + {"FG", {{"highway", "residential"}, {"hgv", "no"}}}, + {"GH", {{"highway", "residential"}, {"hgv", "no"}}}, + {"HI", {{"highway", "residential"}, {"hgv", "no"}}}, + {"CJ", {{"highway", "residential"}}}, + {"JK", {{"highway", "residential"}, {"hgv", "no"}}}, + {"JLMK", {{"highway", "residential"}}}, + {"KD", {{"highway", "residential"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 100); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/hgv_no_access_penalty"); + + std::unordered_map no_time = { + {"/costing_options/truck/hgv_no_access_penalty", "2000"}}; + std::unordered_map with_depart_at = + {{"/costing_options/truck/hgv_no_access_penalty", "2000"}, + {"/locations/0/date_time", "2024-03-20T09:00"}}; + std::unordered_map with_arrive_by = + {{"/costing_options/truck/hgv_no_access_penalty", "2000"}, + {"/locations/1/date_time", "2024-03-20T09:00"}}; + + auto get_leg_cost = [](const valhalla::Api& response) { + return response.trip().routes(0).legs(0).node().rbegin()->cost().elapsed_cost().cost(); + }; + + // do both bidirectional & both unidirectional a* + std::vector> options = {no_time, with_depart_at, + with_arrive_by}; + for (auto& truck_options : options) { + + // by default, take the detour via LM + // NOTE, we're not snapping to the hgv=no edges either + { + auto route = gurka::do_action(valhalla::Options::route, map, {"1", "2"}, "truck"); + gurka::assert::raw::expect_path(route, {"BC", "CJ", "JLMK", "KD", "DE"}); + } + + // with a high hgv_no_penalty also take the detour via LM, but do snap to the hgv=no edges + { + auto route = + gurka::do_action(valhalla::Options::route, map, {"1", "2"}, "truck", truck_options); + gurka::assert::raw::expect_path(route, {"AB", "BC", "CJ", "JLMK", "KD", "DE", "EF"}); + } + + // with a low hgv_no_penalty take the JK edge + { + truck_options["/costing_options/truck/hgv_no_access_penalty"] = "10"; + auto route = + gurka::do_action(valhalla::Options::route, map, {"1", "2"}, "truck", truck_options); + gurka::assert::raw::expect_path(route, {"AB", "BC", "CJ", "JK", "KD", "DE", "EF"}); + } + + // if all hgv=no and a high hgv_no_penalty, truck should not trigger the penalty at all + // so cost should be similar to car + { + truck_options["/costing_options/truck/hgv_no_access_penalty"] = "2000"; + auto route_car = gurka::do_action(valhalla::Options::route, map, {"2", "3"}, "auto"); + auto route_truck = + gurka::do_action(valhalla::Options::route, map, {"2", "3"}, "truck", truck_options); + EXPECT_NEAR(get_leg_cost(route_car), get_leg_cost(route_truck), 300.0); + } + } +} diff --git a/valhalla/sif/dynamiccost.h b/valhalla/sif/dynamiccost.h index 441a64c1c5..02efa7e056 100644 --- a/valhalla/sif/dynamiccost.h +++ b/valhalla/sif/dynamiccost.h @@ -497,10 +497,10 @@ class DynamicCost { // If forward, check if the edge marks the end of a restriction, else check // if the edge marks the start of a complex restriction. - if ((forward && (edge->end_restriction() & access_mode())) || - (!forward && (edge->start_restriction() & access_mode()))) { + if ((forward && (edge->end_restriction() & access_mask_)) || + (!forward && (edge->start_restriction() & access_mask_))) { // Get complex restrictions. Return false if no restrictions are found - auto restrictions = tile->GetRestrictions(forward, edgeid, access_mode()); + auto restrictions = tile->GetRestrictions(forward, edgeid, access_mask_); if (restrictions.size() == 0) { return false; } @@ -599,6 +599,18 @@ class DynamicCost { baldr::DateTime::get_tz_db().from_index(tz_index)); } + /*** + * Evaluates mode-specific and time-dependent access restrictions, including a binary + * search to get the tile's access restrictions. + * + * @param access_mode The access mode to get restrictions for + * @param edge The edge to check for restrictions + * @param is_dest Is there a destination on the edge? + * @param tile The edge's tile + * @param current_time Needed for time dependent restrictions + * @param tz_index The current timezone index + * @param restriction_idx Records the restriction in the tile for later retrieval + */ inline bool EvaluateRestrictions(uint32_t access_mode, const baldr::DirectedEdge* edge, const bool is_dest, diff --git a/valhalla/sif/edgelabel.h b/valhalla/sif/edgelabel.h index 471cacc836..d5a9dc1b23 100644 --- a/valhalla/sif/edgelabel.h +++ b/valhalla/sif/edgelabel.h @@ -39,7 +39,7 @@ class EdgeLabel { endnode_(baldr::kInvalidGraphId), use_(0), classification_(0), shortcut_(0), dest_only_(0), origin_(0), destination_(0), toll_(0), not_thru_(0), deadend_(0), on_complex_rest_(0), closure_pruning_(0), path_id_(0), restriction_idx_(0), internal_turn_(0), unpaved_(0), - has_measured_speed_(0), cost_(0, 0), sortcost_(0) { + has_measured_speed_(0), hgv_access_(0), cost_(0, 0), sortcost_(0) { assert(path_id_ <= baldr::kMaxMultiPathId); } @@ -61,6 +61,7 @@ class EdgeLabel { * @param path_id When searching more than one path at a time this denotes which path * the this label is tracking * @param destonly Destination only, either mode-specific or general + * @param hgv_access Whether HGV is allowed */ EdgeLabel(const uint32_t predecessor, const baldr::GraphId& edgeid, @@ -74,7 +75,8 @@ class EdgeLabel { const bool has_measured_speed, const InternalTurn internal_turn, const uint8_t path_id = 0, - const bool destonly = false) + const bool destonly = false, + const bool hgv_access = false) : predecessor_(predecessor), path_distance_(path_distance), restrictions_(edge->restrictions()), edgeid_(edgeid), opp_index_(edge->opp_index()), opp_local_idx_(edge->opp_local_idx()), mode_(static_cast(mode)), endnode_(edge->endnode()), @@ -86,7 +88,8 @@ class EdgeLabel { edge->end_restriction()), closure_pruning_(closure_pruning), path_id_(path_id), restriction_idx_(restriction_idx), internal_turn_(static_cast(internal_turn)), unpaved_(edge->unpaved()), - has_measured_speed_(has_measured_speed), cost_(cost), sortcost_(sortcost) { + has_measured_speed_(has_measured_speed), hgv_access_(hgv_access), cost_(cost), + sortcost_(sortcost) { dest_only_ = destonly ? destonly : edge->destonly(); assert(path_id_ <= baldr::kMaxMultiPathId); } @@ -371,6 +374,14 @@ class EdgeLabel { return unpaved_; } + /** + * Does it have HGV access? + * @return Returns true if the (opposing) edge had HGV access + */ + bool has_hgv_access() const { + return hgv_access_; + } + protected: // predecessor_: Index to the predecessor edge label information. // Note: invalid predecessor value uses all 32 bits (so if this needs to @@ -433,7 +444,9 @@ class EdgeLabel { // Flag indicating edge is an unpaved road. uint32_t unpaved_ : 1; uint32_t has_measured_speed_ : 1; - uint32_t spare : 13; + // Flag if this edge had HGV access + uint32_t hgv_access_ : 1; + uint32_t spare : 12; Cost cost_; // Cost and elapsed time along the path. float sortcost_; // Sort cost - includes A* heuristic. @@ -469,6 +482,7 @@ class PathEdgeLabel : public EdgeLabel { * @param path_id When searching more than one path at a time this denotes which path * the this label is tracking * @param destonly Destination only, either mode-specific or general + * @param hgv_access Whether HGV is allowed */ PathEdgeLabel(const uint32_t predecessor, const baldr::GraphId& edgeid, @@ -483,7 +497,8 @@ class PathEdgeLabel : public EdgeLabel { const bool has_measured_speed, const InternalTurn internal_turn, const uint8_t path_id = 0, - const bool destonly = false) + const bool destonly = false, + const bool hgv_access = false) : EdgeLabel(predecessor, edgeid, edge, @@ -496,7 +511,8 @@ class PathEdgeLabel : public EdgeLabel { has_measured_speed, internal_turn, path_id, - destonly), + destonly, + hgv_access), transition_cost_(transition_cost) { assert(path_id_ <= baldr::kMaxMultiPathId); } @@ -549,6 +565,7 @@ class BDEdgeLabel : public EdgeLabel { * @param path_id When searching more than one path at a time this denotes which path * the this label is tracking * @param destonly Destination only, either mode-specific or general + * @param hgv_access Whether HGV is allowed */ BDEdgeLabel(const uint32_t predecessor, const baldr::GraphId& edgeid, @@ -565,7 +582,8 @@ class BDEdgeLabel : public EdgeLabel { const sif::InternalTurn internal_turn, const uint8_t restriction_idx, const uint8_t path_id = 0, - const bool destonly = false) + const bool destonly = false, + const bool hgv_access = false) : EdgeLabel(predecessor, edgeid, edge, @@ -578,7 +596,8 @@ class BDEdgeLabel : public EdgeLabel { has_measured_speed, internal_turn, path_id, - destonly), + destonly, + hgv_access), transition_cost_(transition_cost), opp_edgeid_(oppedgeid), not_thru_pruning_(not_thru_pruning), distance_(dist) { } @@ -604,6 +623,7 @@ class BDEdgeLabel : public EdgeLabel { * @param path_id When searching more than one path at a time this denotes which path * the this label is tracking * @param destonly Destination only, either mode-specific or general + * @param hgv_access Whether HGV is allowed */ BDEdgeLabel(const uint32_t predecessor, const baldr::GraphId& edgeid, @@ -619,7 +639,8 @@ class BDEdgeLabel : public EdgeLabel { const sif::InternalTurn internal_turn, const uint8_t restriction_idx, const uint8_t path_id = 0, - const bool destonly = false) + const bool destonly = false, + const bool hgv_access = false) : EdgeLabel(predecessor, edgeid, edge, @@ -632,7 +653,8 @@ class BDEdgeLabel : public EdgeLabel { has_measured_speed, internal_turn, path_id, - destonly), + destonly, + hgv_access), transition_cost_(transition_cost), opp_edgeid_(oppedgeid), not_thru_pruning_(not_thru_pruning), distance_(0.0f) { } @@ -655,6 +677,7 @@ class BDEdgeLabel : public EdgeLabel { * @param path_id When searching more than one path at a time this denotes which path * the this label is tracking * @param destonly Destination only, either mode-specific or general + * @param hgv_access Whether HGV is allowed */ BDEdgeLabel(const uint32_t predecessor, const baldr::GraphId& edgeid, @@ -668,7 +691,8 @@ class BDEdgeLabel : public EdgeLabel { const bool has_measured_speed, const sif::InternalTurn internal_turn, const uint8_t path_id = 0, - const bool destonly = false) + const bool destonly = false, + const bool hgv_access = false) : EdgeLabel(predecessor, edgeid, edge, @@ -681,7 +705,8 @@ class BDEdgeLabel : public EdgeLabel { has_measured_speed, internal_turn, path_id, - destonly), + destonly, + hgv_access), transition_cost_({}), not_thru_pruning_(!edge->not_thru()), distance_(dist) { opp_edgeid_ = {}; } From 4c4dd99eeab9572a54fa5fb4dcf158107e9bb8f5 Mon Sep 17 00:00:00 2001 From: Luke Seelenbinder Date: Wed, 3 Apr 2024 19:12:07 +0300 Subject: [PATCH 061/198] Significantly improve performance of graphbuilder (#4669) --- .gitignore | 1 + CHANGELOG.md | 3 +- src/mjolnir/admin.cc | 116 ++++++++++++++++--------------- src/mjolnir/graphbuilder.cc | 133 +++++++++++++++++++----------------- src/mjolnir/util.cc | 10 +-- valhalla/mjolnir/admin.h | 25 ++++--- 6 files changed, 156 insertions(+), 132 deletions(-) diff --git a/.gitignore b/.gitignore index 036d2a39c6..d66fafb158 100644 --- a/.gitignore +++ b/.gitignore @@ -70,5 +70,6 @@ vcpkg*/ # documentation site/ +*.gph .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ce4845d9..99776053d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,7 +86,7 @@ * CHANGED: date_time refactor as a preparation to return DST/timezone related offset in the response [#4365](https://github.com/valhalla/valhalla/pull/4365) * ADDED: find connection on backward search for bidir matrix algo [#4329](https://github.com/valhalla/valhalla/pull/4329) * FIXED: Fix segfault in OSRM serializer with bannerInstructions when destination is on roundabout [#4480](https://github.com/valhalla/valhalla/pull/4481) - * CHANGED: Adujustment of walk speed when walking on slight downhill [#4302](https://github.com/valhalla/valhalla/pull/4302) + * CHANGED: Adjustment of walk speed when walking on slight downhill [#4302](https://github.com/valhalla/valhalla/pull/4302) * CHANGED: Do not reclassify ferry connections when no hierarchies are to be generated [#4487](https://github.com/valhalla/valhalla/pull/4487) * ADDED: Added a config option to sort nodes spatially during graph building [#4455](https://github.com/valhalla/valhalla/pull/4455) * ADDED: Timezone info in route and matrix responses [#4491](https://github.com/valhalla/valhalla/pull/4491) @@ -107,6 +107,7 @@ * ADDED: `ignore_non_vehicular_restrictions` parameter for truck costing [#4606](https://github.com/valhalla/valhalla/pull/4606) * UPDATED: tz database to 2024a [#4643](https://github.com/valhalla/valhalla/pull/4643) * ADDED: `hgv_no_penalty` costing option to allow penalized truck access to `hgv=no` edges [#4650](https://github.com/valhalla/valhalla/pull/4650) + * CHANGED: Significantly improve performance of graphbuilder [#4669](https://github.com/valhalla/valhalla/pull/4669) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/mjolnir/admin.cc b/src/mjolnir/admin.cc index f5b45e02fd..9aca8ca78e 100644 --- a/src/mjolnir/admin.cc +++ b/src/mjolnir/admin.cc @@ -37,7 +37,7 @@ uint32_t GetMultiPolyId(const std::multimap& polys uint32_t index = 0; point_type p(ll.lng(), ll.lat()); for (const auto& poly : polys) { - if (boost::geometry::covered_by(p, poly.second)) { + if (bg::covered_by(p, poly.second, bg::strategy::within::crossings_multiply())) { const auto& admin = graphtile.admins_builder(poly.first); if (!admin.state_offset()) index = poly.first; @@ -55,8 +55,9 @@ uint32_t GetMultiPolyId(const std::multimap& polys point_type p(ll.lng(), ll.lat()); for (const auto& poly : polys) { // TODO: we recently discovered that boost::geometry doesn't do bbox checks to speed things up - if (boost::geometry::covered_by(p, poly.second)) + if (bg::covered_by(p, poly.second, bg::strategy::within::crossings_multiply())) { return poly.first; + } } return index; } @@ -70,52 +71,31 @@ uint32_t GetMultiPolyId(const std::multimap& polys // languages that will be considered for any name* or destination* keys. Basically, we only support // the languages that are on the signs in that area. Note: The first pair always contains an empty // language which makes the name key with no language the most important key. -std::vector> -GetMultiPolyIndexes(const std::vector>& polys, - const PointLL& ll) { - - auto process_languages = [](const std::vector& langs, bool is_default, - std::vector>& languages) { - for (const auto& l : langs) { - if (stringLanguage(l) != Language::kNone) { - std::vector>::iterator it = - std::find_if(languages.begin(), languages.end(), - [&l](const std::pair& p) { return p.first == l; }); - if (it == languages.end()) { - languages.emplace_back(l, false); - } else if (is_default) { // fr - nl or fr;en in default lang column - it->second = false; - } - } - } - }; - +std::vector> GetMultiPolyIndexes(const language_poly_index& polys, + const PointLL& ll) { std::vector> languages; - std::vector>::iterator it; - std::string lang; // first entry is blank for the default name languages.emplace_back("", false); point_type p(ll.lng(), ll.lat()); - for (const auto& poly : polys) { - - if (boost::geometry::covered_by(p, std::get<1>(poly))) { - lang = std::get<0>(poly); - it = std::find_if(languages.begin(), languages.end(), - [&lang](const std::pair& p) { return p.first == lang; }); - - if (it == languages.end()) { - std::vector langs = GetTagTokens(std::get<0>(poly), " - "); - if (langs.size() >= 2) { - process_languages(langs, std::get<2>(poly), languages); - } else { - langs = GetTagTokens(std::get<0>(poly)); - if (langs.size() >= 2) { - process_languages(langs, std::get<2>(poly), languages); - } else { - languages.emplace_back(std::get<0>(poly), std::get<2>(poly)); + for (auto it = polys.qbegin(bg::index::covers(p)); it != polys.qend(); ++it) { + // Make sure the point is in the admin poly (not just its envelope) + if (bg::covered_by(p, std::get<1>(*it), bg::strategy::within::crossings_multiply())) { + auto& langs = std::get<2>(*it); + bool is_default = std::get<3>(*it); + + for (const auto& l : langs) { + if (stringLanguage(l) != Language::kNone) { + auto needle = + std::find_if(languages.begin(), languages.end(), + [&l](const std::pair& p) { return p.first == l; }); + + if (needle == languages.end()) { + languages.emplace_back(l, is_default); + } else if (is_default) { // fr - nl or fr;en in default lang column + needle->second = false; } } } @@ -181,6 +161,18 @@ std::multimap GetTimeZones(sqlite3* db_handle, return polys; } +/*** + * Parses a language tag into a vector of individual tokens. + */ +std::vector ParseLanguageTokens(const std::string& lang_tag) { + auto langs = GetTagTokens(lang_tag, " - "); + if (langs.size() == 1) { + langs = GetTagTokens(langs.at(0)); + } + + return langs; +} + void GetData(sqlite3* db_handle, sqlite3_stmt* stmt, const std::string& sql, @@ -188,7 +180,7 @@ void GetData(sqlite3* db_handle, std::multimap& polys, std::unordered_map& drive_on_right, std::unordered_map& allow_intersection_names, - std::vector>& language_ploys, + language_poly_index& language_ploys, bool languages_only = false) { uint32_t result = 0; bool dor = true; @@ -253,15 +245,21 @@ void GetData(sqlite3* db_handle, uint32_t index = tilebuilder.AddAdmin(country_name, state_name, country_iso, state_iso); multi_polygon_type multi_poly; - boost::geometry::read_wkt(geom, multi_poly); + bg::read_wkt(geom, multi_poly); polys.emplace(index, multi_poly); drive_on_right.emplace(index, dor); allow_intersection_names.emplace(index, intersection_name); - if (!default_language.empty()) - language_ploys.push_back(std::make_tuple(default_language, multi_poly, true)); - if (!supported_languages.empty()) - language_ploys.push_back(std::make_tuple(supported_languages, multi_poly, false)); + bg::model::box box{}; + bg::envelope(multi_poly, box); + if (!default_language.empty()) { + auto langs = ParseLanguageTokens(default_language); + language_ploys.insert(std::make_tuple(box, multi_poly, langs, true)); + } + if (!supported_languages.empty()) { + auto langs = ParseLanguageTokens(supported_languages); + language_ploys.insert(std::make_tuple(box, multi_poly, langs, false)); + } } else { @@ -281,12 +279,18 @@ void GetData(sqlite3* db_handle, } multi_polygon_type multi_poly; - boost::geometry::read_wkt(geom, multi_poly); + bg::read_wkt(geom, multi_poly); + bg::model::box box{}; + bg::envelope(multi_poly, box); - if (!default_language.empty()) - language_ploys.push_back(std::make_tuple(default_language, multi_poly, true)); - if (!supported_languages.empty()) - language_ploys.push_back(std::make_tuple(supported_languages, multi_poly, false)); + if (!default_language.empty()) { + auto langs = ParseLanguageTokens(default_language); + language_ploys.insert(std::make_tuple(box, multi_poly, langs, true)); + } + if (!supported_languages.empty()) { + auto langs = ParseLanguageTokens(supported_languages); + language_ploys.insert(std::make_tuple(box, multi_poly, langs, false)); + } } result = sqlite3_step(stmt); @@ -303,7 +307,7 @@ std::multimap GetAdminInfo(sqlite3* db_handle, std::unordered_map& drive_on_right, std::unordered_map& allow_intersection_names, - std::vector>& language_ploys, + language_poly_index& language_polys, const AABB2& aabb, GraphTileBuilder& tilebuilder) { std::multimap polys; @@ -326,7 +330,7 @@ GetAdminInfo(sqlite3* db_handle, sql += std::to_string(aabb.miny()) + ", " + std::to_string(aabb.maxx()) + ","; sql += std::to_string(aabb.maxy()) + ")) order by admin_level desc, name;"; GetData(db_handle, stmt, sql, tilebuilder, polys, drive_on_right, allow_intersection_names, - language_ploys, true); + language_polys, true); // state query sql = "SELECT country.name, state.name, country.iso_code, "; @@ -342,7 +346,7 @@ GetAdminInfo(sqlite3* db_handle, sql += std::to_string(aabb.miny()) + ", " + std::to_string(aabb.maxx()) + ","; sql += std::to_string(aabb.maxy()) + ")) order by state.name, country.name;"; GetData(db_handle, stmt, sql, tilebuilder, polys, drive_on_right, allow_intersection_names, - language_ploys); + language_polys); // country query sql = "SELECT name, \"\", iso_code, \"\", drive_on_right, allow_intersection_names, admin_level, "; @@ -356,7 +360,7 @@ GetAdminInfo(sqlite3* db_handle, sql += std::to_string(aabb.miny()) + ", " + std::to_string(aabb.maxx()) + ","; sql += std::to_string(aabb.maxy()) + ")) order by name;"; GetData(db_handle, stmt, sql, tilebuilder, polys, drive_on_right, allow_intersection_names, - language_ploys); + language_polys); if (stmt) { // just in case something bad happened. sqlite3_finalize(stmt); diff --git a/src/mjolnir/graphbuilder.cc b/src/mjolnir/graphbuilder.cc index 48262d5490..62ff3a7923 100644 --- a/src/mjolnir/graphbuilder.cc +++ b/src/mjolnir/graphbuilder.cc @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -398,8 +399,8 @@ void BuildTileSet(const std::string& ways_file, const std::string& linguistic_node_file, const std::string& tile_dir, const OSMData& osmdata, - std::map::const_iterator tile_start, - std::map::const_iterator tile_end, + std::queue>& tiles, + std::mutex& tiles_lock, const uint32_t tile_creation_date, const boost::property_tree::ptree& pt, std::promise& result) { @@ -462,10 +463,21 @@ void BuildTileSet(const std::string& ways_file, std::map, uint32_t> langMap; //////////////////////////////////////////////////////////////////////////// // Iterate over tiles - for (; tile_start != tile_end; ++tile_start) { + while (true) { + tiles_lock.lock(); + if (tiles.empty()) { + // all work done + tiles_lock.unlock(); + break; + } + + auto tile = tiles.front(); + tiles.pop(); + tiles_lock.unlock(); + try { // What actually writes the tile - GraphId tile_id = tile_start->first.Tile_Base(); + GraphId tile_id = tile.first.Tile_Base(); GraphTileBuilder graphtile(tile_dir, tile_id, false); // Information about tile creation @@ -483,11 +495,11 @@ void BuildTileSet(const std::string& ways_file, std::multimap admin_polys; std::unordered_map drive_on_right; std::unordered_map allow_intersection_names; - std::vector> language_ploys; + language_poly_index language_polys; if (admin_db_handle) { admin_polys = GetAdminInfo(admin_db_handle, drive_on_right, allow_intersection_names, - language_ploys, tiling.TileBounds(id), graphtile); + language_polys, tiling.TileBounds(id), graphtile); if (admin_polys.size() == 1) { // TODO - check if tile bounding box is entirely inside the polygon... tile_within_one_admin = true; @@ -505,12 +517,12 @@ void BuildTileSet(const std::string& ways_file, //////////////////////////////////////////////////////////////////////// // Iterate over nodes in the tile - auto node_itr = nodes[tile_start->second]; + auto node_itr = nodes[tile.second]; // to avoid realloc we guess how many edges there might be in a given tile geo_attribute_cache.clear(); - geo_attribute_cache.reserve(5 * (std::next(tile_start) == tile_end - ? nodes.end() - node_itr - : std::next(tile_start)->second - tile_start->second)); + // geo_attribute_cache.reserve(5 * (std::next(tile_start) == tile_end + // ? nodes.end() - node_itr + // : std::next(tile_start)->second - tile_start->second)); while (node_itr != nodes.end() && (*node_itr).graph_id.Tile_Base() == tile_id) { // amalgamate all the node duplicates into one and the edges that connect to it @@ -518,7 +530,7 @@ void BuildTileSet(const std::string& ways_file, auto bundle = collect_node_edges(node_itr, nodes, edges); // Make sure node has edges - if (bundle.node_edges.size() == 0) { + if (bundle.node_edges.empty()) { LOG_ERROR("Node has no edges - skip"); continue; } @@ -535,7 +547,7 @@ void BuildTileSet(const std::string& ways_file, admin_index = (tile_within_one_admin) ? admin_polys.begin()->first : GetMultiPolyId(admin_polys, node_ll, graphtile); dor = drive_on_right[admin_index]; - default_languages = GetMultiPolyIndexes(language_ploys, node_ll); + default_languages = GetMultiPolyIndexes(language_polys, node_ll); } else { admin_index = graphtile.AddAdmin("", "", osmdata.node_names.name(node.country_iso_index()), @@ -1256,14 +1268,14 @@ void BuildTileSet(const std::string& ways_file, graphtile.StoreTileData(); // Made a tile - LOG_DEBUG((boost::format("Wrote tile %1%: %2% bytes") % tile_start->first % + LOG_DEBUG((boost::format("Wrote tile %1%: %2% bytes") % tile.first % graphtile.header_builder().end_offset()) .str()); } // Whatever happens in Vegas.. catch (std::exception& e) { // ..gets sent back to the main thread result.set_exception(std::current_exception()); - LOG_ERROR((boost::format("Failed tile %1%: %2%") % tile_start->first % e.what()).str()); + LOG_ERROR((boost::format("Failed tile %1%: %2%") % tile.first % e.what()).str()); return; } } @@ -1308,26 +1320,24 @@ void BuildLocalTiles(const unsigned int thread_count, std::vector> results(threads.size()); // Divvy up the work - size_t floor = tiles.size() / threads.size(); - size_t at_ceiling = tiles.size() - (threads.size() * floor); - std::map::const_iterator tile_start, tile_end = tiles.begin(); + std::queue> tile_queue; + std::mutex tile_lock; + for (auto id : tiles) { + tile_queue.emplace(id); + } // Atomically pass around stats info for (size_t i = 0; i < threads.size(); ++i) { - // Figure out how many this thread will work on (either ceiling or floor) - size_t tile_count = (i < at_ceiling ? floor + 1 : floor); - // Where the range begins - tile_start = tile_end; - // Where the range ends - std::advance(tile_end, tile_count); // Make the thread - threads[i].reset(new std::thread(BuildTileSet, std::cref(ways_file), std::cref(way_nodes_file), - std::cref(nodes_file), std::cref(edges_file), - std::cref(complex_from_restriction_file), - std::cref(complex_to_restriction_file), - std::cref(linguistic_node_file), std::cref(tile_dir), - std::cref(osmdata), tile_start, tile_end, tile_creation_date, - std::cref(pt.get_child("mjolnir")), std::ref(results[i]))); + threads[i] = + std::make_shared(BuildTileSet, std::cref(ways_file), std::cref(way_nodes_file), + std::cref(nodes_file), std::cref(edges_file), + std::cref(complex_from_restriction_file), + std::cref(complex_to_restriction_file), + std::cref(linguistic_node_file), std::cref(tile_dir), + std::cref(osmdata), std::ref(tile_queue), std::ref(tile_lock), + tile_creation_date, std::cref(pt.get_child("mjolnir")), + std::ref(results[i])); } // Join all the threads to wait for them to finish up their work @@ -1354,8 +1364,7 @@ void BuildLocalTiles(const unsigned int thread_count, } // namespace -namespace valhalla { -namespace mjolnir { +namespace valhalla::mjolnir { // Returns the grid Id within the tile. A tile is subdivided into a nxn grid. // The grid Id within the tile is used to sort nodes spatially. @@ -1449,7 +1458,12 @@ void GraphBuilder::Build(const boost::property_tree::ptree& pt, unsigned int threads = std::max(static_cast(1), pt.get("mjolnir.concurrency", std::thread::hardware_concurrency())); - std::string tile_dir = pt.get("mjolnir.tile_dir"); + + auto tile_dir = pt.get("mjolnir.tile_dir"); + // Disable sqlite3 internal memory tracking (results in a high-contention mutex, and we don't care + // about marginal sqlite memory usage). + sqlite3_config(SQLITE_CONFIG_MEMSTATUS, false); + BuildLocalTiles(threads, osmdata, ways_file, way_nodes_file, nodes_file, edges_file, complex_from_restriction_file, complex_to_restriction_file, linguistic_node_file, tiles, tile_dir, stats, pt); @@ -1563,8 +1577,7 @@ void GraphBuilder::AddLanguage(const size_t index, static_cast(index), 0, 0}; - linguistics.emplace_back( - std::string(reinterpret_cast(&header), kLinguisticHeaderSize)); + linguistics.emplace_back(reinterpret_cast(&header), kLinguisticHeaderSize); } } @@ -1591,7 +1604,7 @@ void GraphBuilder::AddPronunciationsWithLang(std::vector& pronuncia for (size_t i = 0; i < pronunciation_tokens.size(); i++) { auto& t = pronunciation_tokens[i]; - if (indexMap.size() != 0) { + if (!indexMap.empty()) { auto index = indexMap.find(i); if (index != indexMap.end()) header.name_index_ = index->second; @@ -1599,30 +1612,29 @@ void GraphBuilder::AddPronunciationsWithLang(std::vector& pronuncia continue; } - if (!t.size()) { // pronunciation is blank. just add the lang + if (t.empty()) { // pronunciation is blank. just add the lang - if (!pronunciation_langs.size()) + if (pronunciation_langs.empty()) continue; header.language_ = static_cast(pronunciation_langs.at(i)); header.length_ = 0; header.phonetic_alphabet_ = static_cast(baldr::PronunciationAlphabet::kNone); - pronunciations.emplace_back( - std::string(reinterpret_cast(&header), kLinguisticHeaderSize)); + pronunciations.emplace_back(reinterpret_cast(&header), kLinguisticHeaderSize); } else { header.phonetic_alphabet_ = static_cast(verbal_type); header.length_ = t.size(); header.language_ = - (pronunciation_langs.size() ? static_cast(pronunciation_langs.at(i)) - : static_cast(baldr::Language::kNone)); + (!pronunciation_langs.empty() ? static_cast(pronunciation_langs.at(i)) + : static_cast(baldr::Language::kNone)); pronunciations.emplace_back( (std::string(reinterpret_cast(&header), kLinguisticHeaderSize) + t)); } - if (indexMap.size() == 0) + if (indexMap.empty()) ++header.name_index_; } return pronunciations; @@ -1632,17 +1644,17 @@ void GraphBuilder::AddPronunciationsWithLang(std::vector& pronuncia std::map indexMap; size_t k = key; - if ((pronunciation_langs.size() == 0 && token_langs.size() == 0) || - ((pronunciation_langs.size() != 0 && token_langs.size() == 0) && + if ((pronunciation_langs.empty() && token_langs.empty()) || + ((!pronunciation_langs.empty() && token_langs.empty()) && (pronunciation_langs.size() <= token_size))) process = true; else { std::pair::iterator, bool> ret; - for (size_t i = 0; i < token_langs.size(); i++) { + for (auto& token_lang : token_langs) { for (size_t j = 0; j < pronunciation_langs.size(); j++) { - if (token_langs[i] == pronunciation_langs[j]) { + if (token_lang == pronunciation_langs[j]) { ret = indexMap.insert(std::make_pair(j, k)); - if (ret.second == false) // already used + if (!ret.second) // already used continue; else { k++; @@ -1653,7 +1665,7 @@ void GraphBuilder::AddPronunciationsWithLang(std::vector& pronuncia } } if (!found) { - lang_map.emplace(k, token_langs[i]); + lang_map.emplace(k, token_lang); k++; } found = false; @@ -1765,10 +1777,10 @@ bool GraphBuilder::CreateSignInfoList( ipa_langs, nt_sampa_tokens, nt_sampa_langs, katakana_tokens, katakana_langs, jeita_tokens, jeita_langs); - add_ipa = (ipa_tokens.size() != 0); - add_nt_sampa = (nt_sampa_tokens.size() != 0); - add_katakana = (katakana_tokens.size() != 0); - add_jeita = (jeita_tokens.size() != 0); + add_ipa = !ipa_tokens.empty(); + add_nt_sampa = !nt_sampa_tokens.empty(); + add_katakana = !katakana_tokens.empty(); + add_jeita = !jeita_tokens.empty(); return (add_ipa || add_nt_sampa || add_katakana || add_jeita); }; @@ -1794,7 +1806,7 @@ bool GraphBuilder::CreateSignInfoList( } for (size_t i = 0; i < refs.size(); ++i) { - if (lang_map.size() != 0) { + if (!lang_map.empty()) { // only add a language if it has not already been added during pronunciation processing auto itr = lang_map.find(key); if (itr != lang_map.end()) { @@ -1824,17 +1836,17 @@ bool GraphBuilder::CreateSignInfoList( // NUMBER // Exit sign number bool has_phoneme = false; - const uint8_t ipa = static_cast(PronunciationAlphabet::kIpa); - const uint8_t nt_sampa = static_cast(PronunciationAlphabet::kNtSampa); - const uint8_t katakana = static_cast(PronunciationAlphabet::kKatakana); - const uint8_t jeita = static_cast(PronunciationAlphabet::kJeita); + const auto ipa = static_cast(PronunciationAlphabet::kIpa); + const auto nt_sampa = static_cast(PronunciationAlphabet::kNtSampa); + const auto katakana = static_cast(PronunciationAlphabet::kKatakana); + const auto jeita = static_cast(PronunciationAlphabet::kJeita); if (way.junction_ref_index() != 0) { way.ProcessNamesPronunciations(osmdata.name_offset_map, default_languages, way.junction_ref_index(), way.junction_ref_lang_index(), sign_names, sign_langs, false); - const uint8_t t = static_cast(OSMLinguistic::Type::kJunctionRef); + const auto t = static_cast(OSMLinguistic::Type::kJunctionRef); has_phoneme = get_pronunciations(osmdata.name_offset_map, default_languages, get_pronunciation_index(t, ipa), get_lang_index(t, ipa), @@ -2260,5 +2272,4 @@ bool GraphBuilder::CreateSignInfoList( return (has_guide || has_guidance_view_jct || has_guidance_view_signboard); } -} // namespace mjolnir -} // namespace valhalla +} // namespace valhalla::mjolnir diff --git a/src/mjolnir/util.cc b/src/mjolnir/util.cc index 308e143ec5..e1e250a948 100644 --- a/src/mjolnir/util.cc +++ b/src/mjolnir/util.cc @@ -70,9 +70,11 @@ namespace mjolnir { */ std::vector GetTagTokens(const std::string& tag_value, char delim) { std::vector tokens; - boost::algorithm::split(tokens, tag_value, - std::bind(std::equal_to(), delim, std::placeholders::_1), - boost::algorithm::token_compress_off); + boost::algorithm::split( + tokens, tag_value, + [delim](auto&& PH1) { return std::equal_to()(delim, std::forward(PH1)); }, + boost::algorithm::token_compress_off); + return tokens; } @@ -214,7 +216,7 @@ std::shared_ptr make_spatialite_cache(sqlite3* handle) { spatialite_singleton_t::get_instance(); void* conn = spatialite_alloc_connection(); spatialite_init_ex(handle, conn, 0); - return std::shared_ptr(conn, [](void* c) { spatialite_cleanup_ex(c); }); + return {conn, [](void* c) { spatialite_cleanup_ex(c); }}; } bool build_tile_set(const boost::property_tree::ptree& original_config, diff --git a/valhalla/mjolnir/admin.h b/valhalla/mjolnir/admin.h index 4a9163c283..725e3b0569 100644 --- a/valhalla/mjolnir/admin.h +++ b/valhalla/mjolnir/admin.h @@ -17,14 +17,19 @@ using namespace valhalla::baldr; using namespace valhalla::midgard; +namespace bg = boost::geometry; namespace valhalla { namespace mjolnir { // Geometry types for admin queries -typedef boost::geometry::model::d2::point_xy point_type; -typedef boost::geometry::model::polygon polygon_type; -typedef boost::geometry::model::multi_polygon multi_polygon_type; +typedef bg::model::d2::point_xy point_type; +typedef bg::model::polygon polygon_type; +typedef bg::model::multi_polygon multi_polygon_type; +typedef bg::index::rtree< + std::tuple, multi_polygon_type, std::vector, bool>, + bg::index::rstar<16>> + language_poly_index; /** * Get the dbhandle of a sqlite db. Used for timezones and admins DBs. @@ -54,13 +59,12 @@ uint32_t GetMultiPolyId(const std::multimap& polys /** * Get the vector of languages for this LL. Used by admin areas. Checks if the pointLL is covered_by * the poly. - * @param polys tuple that contains a language, poly, is_default_language. + * @param language_polys tuple that contains a language, poly, is_default_language. * @param ll point that needs to be checked. * @return Returns the vector of pairs {language, is_default_language} */ std::vector> -GetMultiPolyIndexes(const std::vector>& polys, - const PointLL& ll); +GetMultiPolyIndexes(const language_poly_index& language_ploys, const PointLL& ll); /** * Get the timezone polys from the db @@ -80,7 +84,7 @@ std::multimap GetTimeZones(sqlite3* db_handle, * road * @param default_languages ordered map that is used for lower admins that have an * default language set - * @param language_ploys ordered map that is used for lower admins that have an + * @param language_polys ordered map that is used for lower admins that have an * default language set * @param languages_only should we only process the languages with this query */ @@ -90,7 +94,7 @@ void GetData(sqlite3* db_handle, GraphTileBuilder& tilebuilder, std::multimap& polys, std::unordered_map& drive_on_right, - std::vector>& language_ploys, + language_poly_index& language_polys, bool languages_only); /** @@ -102,7 +106,7 @@ void GetData(sqlite3* db_handle, * names for this country * @param default_languages ordered map that is used for lower admins that have an * default language set - * @param language_ploys ordered map that is used for lower admins that have an + * @param language_polys ordered map that is used for lower admins that have an * default language set * @param aabb bb of the tile * @param tilebuilder Graph tile builder @@ -111,7 +115,7 @@ std::multimap GetAdminInfo(sqlite3* db_handle, std::unordered_map& drive_on_right, std::unordered_map& allow_intersection_names, - std::vector>& language_ploys, + language_poly_index& language_polys, const AABB2& aabb, GraphTileBuilder& tilebuilder); @@ -123,4 +127,5 @@ std::unordered_map> GetCountryAccess(sqlite3* db_h } // namespace mjolnir } // namespace valhalla + #endif // VALHALLA_MJOLNIR_ADMIN_H_ From 9338820c31ec6d3a5f41d42728d1ccf54bc04000 Mon Sep 17 00:00:00 2001 From: Trietes <33934221+Trietes@users.noreply.github.com> Date: Thu, 4 Apr 2024 11:01:04 +0200 Subject: [PATCH 062/198] improved turn by turn api reference (#4675) --- CHANGELOG.md | 1 + docs/docs/api/turn-by-turn/api-reference.md | 23 +++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99776053d5..4c2171d6ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,6 +108,7 @@ * UPDATED: tz database to 2024a [#4643](https://github.com/valhalla/valhalla/pull/4643) * ADDED: `hgv_no_penalty` costing option to allow penalized truck access to `hgv=no` edges [#4650](https://github.com/valhalla/valhalla/pull/4650) * CHANGED: Significantly improve performance of graphbuilder [#4669](https://github.com/valhalla/valhalla/pull/4669) + * UPDATED: Improved turn by turn api reference documentation [#4675](https://github.com/valhalla/valhalla/pull/4675) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index a5961c38b7..304405b3a9 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -171,7 +171,7 @@ These additional options are available for bicycle costing methods. | `use_ferry` | This value indicates the willingness to take ferries. This is a range of values between 0 and 1. Values near 0 attempt to avoid ferries and values near 1 will favor ferries. Note that sometimes ferries are required to complete a route so values of 0 are not guaranteed to avoid ferries entirely. The default value is 0.5. | | `use_living_streets` | This value indicates the willingness to take living streets. This is a range of values between 0 and 1. Values near 0 attempt to avoid living streets and values from 0.5 to 1 will currently have no effect on route selection. The default value is 0.5. Note that sometimes living streets are required to complete a route so values of 0 are not guaranteed to avoid living streets entirely. | | `avoid_bad_surfaces` | This value is meant to represent how much a cyclist wants to avoid roads with poor surfaces relative to the bicycle type being used. This is a range of values between 0 and 1. When the value is 0, there is no penalization of roads with different surface types; only bicycle speed on each surface is taken into account. As the value approaches 1, roads with poor surfaces for the bike are penalized heavier so that they are only taken if they significantly improve travel time. When the value is equal to 1, all bad surfaces are completely disallowed from routing, including start and end points. The default value is 0.25. | -|`bss_return_cost`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to give the time will be used to return a rental bike. This value will be displayed in the final directions and used to calculate the whole duation. The default value is 120 seconds.| +|`bss_return_cost`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to give the time will be used to return a rental bike. This value will be displayed in the final directions and used to calculate the whole duration. The default value is 120 seconds.| |`bss_return_penalty`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to describe the potential effort to return a rental bike. This value won't be displayed and used only inside of the algorithm.| | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | @@ -183,8 +183,8 @@ All of the options described above for autos also apply to motor_scooter costing | Motor_scooter options | Description | | :-------------------------- | :----------- | | `top_speed` | Top speed the motorized scooter can go. Used to avoid roads with higher speeds than this value. For `motor_scooter` this value must be between 20 and 120 KPH. The default value is 45 KPH (~28 MPH) | -| `use_primary` | A riders's propensity to use primary roads. This is a range of values from 0 to 1, where 0 attempts to avoid primary roads, and 1 indicates the rider is more comfortable riding on primary roads. Based on the `use_primary` factor, roads with certain classifications and higher speeds are penalized in an attempt to avoid them when finding the best path. The default value is 0.5. | -| `use_hills` | A riders's desire to tackle hills in their routes. This is a range of values from 0 to 1, where 0 attempts to avoid hills and steep grades even if it means a longer (time and distance) path, while 1 indicates the rider does not fear hills and steeper grades. Based on the `use_hills` factor, penalties are applied to roads based on elevation change and grade. These penalties help the path avoid hilly roads in favor of flatter roads or less steep grades where available. Note that it is not always possible to find alternate paths to avoid hills (for example when route locations are in mountainous areas). The default value is 0.5. | +| `use_primary` | A rider's propensity to use primary roads. This is a range of values from 0 to 1, where 0 attempts to avoid primary roads, and 1 indicates the rider is more comfortable riding on primary roads. Based on the `use_primary` factor, roads with certain classifications and higher speeds are penalized in an attempt to avoid them when finding the best path. The default value is 0.5. | +| `use_hills` | A rider's desire to tackle hills in their routes. This is a range of values from 0 to 1, where 0 attempts to avoid hills and steep grades even if it means a longer (time and distance) path, while 1 indicates the rider does not fear hills and steeper grades. Based on the `use_hills` factor, penalties are applied to roads based on elevation change and grade. These penalties help the path avoid hilly roads in favor of flatter roads or less steep grades where available. Note that it is not always possible to find alternate paths to avoid hills (for example when route locations are in mountainous areas). The default value is 0.5. | | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | | `disable_hierarchy_pruning` | Disable hierarchies to calculate the actual optimal route. The default is `false`. **Note:** This could be quite a performance drainer so there is a upper limit of distance. If the upper limit is exceeded, this option will always be `false`. | @@ -221,12 +221,12 @@ These options are available for pedestrian costing methods. | `service_penalty` | A penalty applied for transition to generic service road. The default penalty is 0. | | `service_factor` | A factor that modifies (multiplies) the cost when generic service roads are encountered. The default `service_factor` is 1. | | `max_hiking_difficulty` | This value indicates the maximum difficulty of hiking trails that is allowed. Values between 0 and 6 are allowed. The values correspond to *sac_scale* values within OpenStreetMap, see reference [here](https://wiki.openstreetmap.org/wiki/Key:sac_scale). The default value is 1 which means that well cleared trails that are mostly flat or slightly sloped are allowed. Higher difficulty trails can be allowed by specifying a higher value for max_hiking_difficulty. -|`bss_rent_cost`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to give the time will be used to rent a bike from a bike share station. This value will be displayed in the final directions and used to calculate the whole duation. The default value is 120 seconds.| +|`bss_rent_cost`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to give the time will be used to rent a bike from a bike share station. This value will be displayed in the final directions and used to calculate the whole duration. The default value is 120 seconds.| |`bss_rent_penalty`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to describe the potential effort to rent a bike from a bike share station. This value won't be displayed and used only inside of the algorithm.| | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | | `transit_start_end_max_distance` | A pedestrian option that can be added to the request to extend the defaults (2145 meters or approximately 1.5 miles). This is the maximum walking distance at the beginning or end of a route.| | `transit_transfer_max_distance` | A pedestrian option that can be added to the request to extend the defaults (800 meters or 0.5 miles). This is the maximum walking distance between transfers.| -| `type` | If set to `blind`, enables additional route instructions, especially usefull for blind users: Announcing crossed streets, the stairs, bridges, tunnels, gates and bollards, which need to be passed on route; information about traffic signals on crosswalks; route numbers not announced for named routes. Default `foot` | +| `type` | If set to `blind`, enables additional route instructions, especially useful for blind users: Announcing crossed streets, the stairs, bridges, tunnels, gates and bollards, which need to be passed on route; information about traffic signals on crosswalks; route numbers not announced for named routes. Default `foot` | ##### Transit costing options @@ -358,9 +358,9 @@ The summary JSON object includes: | :---- | :----------- | | `time` | Estimated elapsed time to complete the trip. | | `length` | Distance traveled for the entire trip. Units are either miles or kilometers based on the input units specified. | -| `has_toll`| Flag indicating if the the path uses one or more toll segments. | -| `has_highway`| Flag indicating if the the path uses one or more highway segments. | -| `has_ferry`| Flag indicating if the the path uses one or more ferry segments. | +| `has_toll`| Flag indicating if the path uses one or more toll segments. | +| `has_highway`| Flag indicating if the path uses one or more highway segments. | +| `has_ferry`| Flag indicating if the path uses one or more ferry segments. | | `min_lat` | Minimum latitude of a bounding box containing the route. | | `min_lon` | Minimum longitude of a bounding box containing the route. | | `max_lat` | Maximum latitude of a bounding box containing the route. | @@ -449,6 +449,11 @@ kTransitConnectionDestination = 35; kPostTransitConnectionDestination = 36; kMergeRight = 37; kMergeLeft = 38; +kElevatorEnter = 39; +kStepsEnter = 40; +kEscalatorEnter = 41; +kBuildingEnter = 42; +kBuildingExit = 43; ``` The maneuver `sign` may contain four lists of interchange sign elements as follows: @@ -475,7 +480,7 @@ A maneuver `transit_info` includes: | `headsign` | The sign on a public transport vehicle that identifies the route destination to passengers. For example "ASTORIA - DITMARS BLVD". | | `color` | The numeric color value associated with a transit route. The value for yellow would be "16567306". | | `text_color` | The numeric text color value associated with a transit route. The value for black would be "0". | -| `description` | The description of the the transit route. For example "Trains operate from Ditmars Boulevard, Queens, to Stillwell Avenue, Brooklyn, at all times. N trains in Manhattan operate along Broadway and across the Manhattan Bridge to and from Brooklyn. Trains in Brooklyn operate along 4th Avenue, then through Borough Park to Gravesend. Trains typically operate local in Queens, and either express or local in Manhattan and Brooklyn, depending on the time. Late night trains operate via Whitehall Street, Manhattan. Late night service is local". | +| `description` | The description of the transit route. For example "Trains operate from Ditmars Boulevard, Queens, to Stillwell Avenue, Brooklyn, at all times. N trains in Manhattan operate along Broadway and across the Manhattan Bridge to and from Brooklyn. Trains in Brooklyn operate along 4th Avenue, then through Borough Park to Gravesend. Trains typically operate local in Queens, and either express or local in Manhattan and Brooklyn, depending on the time. Late night trains operate via Whitehall Street, Manhattan. Late night service is local". | | `operator_onestop_id` | Global operator/agency identifier. | | `operator_name` | Operator/agency name. For example, "BART", "King County Marine Division", and so on. Short name is used over long name. | | `operator_url` | Operator/agency URL. For example, "http://web.mta.info/". | From 2b438eebc3255da877dcb76c371fd952bfeab46c Mon Sep 17 00:00:00 2001 From: Vicky Vergara Date: Fri, 5 Apr 2024 10:31:24 -0600 Subject: [PATCH 063/198] Remove filesystem h from test simple removal 2 (#4668) --- test/CMakeLists.txt | 5 +---- test/countryaccess.cc | 17 ++++++----------- test/graphparser.cc | 38 +++++++++++++++++++------------------- test/loki_service.cc | 9 +++++---- test/matrix.cc | 8 +++----- test/node_search.cc | 9 +++++---- test/search.cc | 7 ++++--- test/test.cc | 6 +++--- test/util_mjolnir.cc | 6 +++--- test/utrecht.cc | 28 +++++++++------------------- valhalla/mjolnir/osmdata.h | 4 ++-- 11 files changed, 60 insertions(+), 77 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9553f436e4..2f56325ff9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(${VALHALLA_SOURCE_DIR}/third_party/googletest ${CMAKE_BINARY_DIR}/googletest) + set_target_properties(gtest PROPERTIES FOLDER "Dependencies") set_target_properties(gtest_main PROPERTIES FOLDER "Dependencies") set_target_properties(gmock PROPERTIES FOLDER "Dependencies") @@ -69,20 +70,16 @@ endif() set(tests_with_warnings filesystem graphbuilder - graphparser graphtilebuilder gridded_data maneuversbuilder narrativebuilder - node_search parse_request sample - search sequence statsd traffictile util_odin - utrecht timedep_paths worker ) diff --git a/test/countryaccess.cc b/test/countryaccess.cc index eee9e824bd..fa4b7063f3 100644 --- a/test/countryaccess.cc +++ b/test/countryaccess.cc @@ -1,4 +1,5 @@ -#include "filesystem.h" +#include + #include "midgard/sequence.h" #include "mjolnir/graphbuilder.h" #include "mjolnir/graphenhancer.h" @@ -33,8 +34,8 @@ const std::string config_file = "test/test_config_country"; // Remove a temporary file if it exists void remove_temp_file(const std::string& fname) { - if (filesystem::exists(fname)) { - filesystem::remove(fname); + if (std::filesystem::exists(fname)) { + std::filesystem::remove(fname); } } @@ -55,12 +56,6 @@ void write_config(const std::string& filename) { file.close(); } -const auto node_predicate = [](const OSMWayNode& a, const OSMWayNode& b) { - return a.node.osmid_ < b.node.osmid_; -}; - -auto way_predicate = [](const OSMWay& a, const OSMWay& b) { return a.osmwayid_ < b.osmwayid_; }; - void CountryAccess(const std::string& config_file) { boost::property_tree::ptree conf; rapidjson::read_json(config_file, conf); @@ -69,8 +64,8 @@ void CountryAccess(const std::string& config_file) { GraphReader graph_reader(conf.get_child("mjolnir")); for (const auto& level : TileHierarchy::levels()) { auto level_dir = graph_reader.tile_dir() + "/" + std::to_string(level.level); - if (filesystem::exists(level_dir) && !filesystem::is_empty(level_dir)) { - filesystem::remove_all(level_dir); + if (std::filesystem::exists(level_dir) && !std::filesystem::is_empty(level_dir)) { + std::filesystem::remove_all(level_dir); } } diff --git a/test/graphparser.cc b/test/graphparser.cc index 4df5671a1f..9cd7a7a2b4 100644 --- a/test/graphparser.cc +++ b/test/graphparser.cc @@ -1,7 +1,6 @@ #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" #include "baldr/tilehierarchy.h" -#include "filesystem.h" #include "midgard/sequence.h" #include "mjolnir/bssbuilder.h" #include "mjolnir/graphbuilder.h" @@ -11,6 +10,7 @@ #include #include +#include #include #include "baldr/directededge.h" @@ -75,32 +75,32 @@ void DoConfig() { // must do clean up here vs TearDown() as we are building data // in the same directory multiple times void CleanUp() { - if (filesystem::exists(ways_file)) - filesystem::remove(ways_file); + if (std::filesystem::exists(ways_file)) + std::filesystem::remove(ways_file); - if (filesystem::exists(way_nodes_file)) - filesystem::remove(way_nodes_file); + if (std::filesystem::exists(way_nodes_file)) + std::filesystem::remove(way_nodes_file); - if (filesystem::exists(nodes_file)) - filesystem::remove(nodes_file); + if (std::filesystem::exists(nodes_file)) + std::filesystem::remove(nodes_file); - if (filesystem::exists(edges_file)) - filesystem::remove(edges_file); + if (std::filesystem::exists(edges_file)) + std::filesystem::remove(edges_file); - if (filesystem::exists(access_file)) - filesystem::remove(access_file); + if (std::filesystem::exists(access_file)) + std::filesystem::remove(access_file); - if (filesystem::exists(from_restriction_file)) - filesystem::remove(from_restriction_file); + if (std::filesystem::exists(from_restriction_file)) + std::filesystem::remove(from_restriction_file); - if (filesystem::exists(to_restriction_file)) - filesystem::remove(to_restriction_file); + if (std::filesystem::exists(to_restriction_file)) + std::filesystem::remove(to_restriction_file); - if (filesystem::exists(bss_nodes_file)) - filesystem::remove(bss_nodes_file); + if (std::filesystem::exists(bss_nodes_file)) + std::filesystem::remove(bss_nodes_file); - if (filesystem::exists(linguistic_node_file)) - filesystem::remove(linguistic_node_file); + if (std::filesystem::exists(linguistic_node_file)) + std::filesystem::remove(linguistic_node_file); } void BollardsGatesAndAccess(const std::string& config_file) { diff --git a/test/loki_service.cc b/test/loki_service.cc index 9b4432182c..4225d79cfd 100644 --- a/test/loki_service.cc +++ b/test/loki_service.cc @@ -1,5 +1,7 @@ #include "test.h" + #include +#include #include "baldr/rapidjson_utils.h" #include "midgard/logging.h" @@ -12,7 +14,6 @@ #include #include -#include "filesystem.h" #include "loki/worker.h" #include "odin/worker.h" #include "thor/worker.h" @@ -379,9 +380,9 @@ boost::property_tree::ptree make_config(const std::vector& whitelis "centroid", "status", }) { - auto run_dir = VALHALLA_BUILD_DIR "test" + std::string(1, filesystem::path::preferred_separator) + - "loki_service_tmp"; - if (!filesystem::is_directory(run_dir) && !filesystem::create_directories(run_dir)) + auto run_dir = VALHALLA_BUILD_DIR "test" + + std::string(1, std::filesystem::path::preferred_separator) + "loki_service_tmp"; + if (!std::filesystem::is_directory(run_dir) && !std::filesystem::create_directories(run_dir)) throw std::runtime_error("Couldnt make directory to run from"); auto config = test::make_config(run_dir, diff --git a/test/matrix.cc b/test/matrix.cc index 82ee5b2eb2..a362be740b 100644 --- a/test/matrix.cc +++ b/test/matrix.cc @@ -136,8 +136,6 @@ const std::unordered_map kMaxDistances = { {"taxi", 43200.0f}, }; // a scale factor to apply to the score so that we bias towards closer results more -constexpr float kDistanceScale = 10.f; - const auto cfg = test::make_config("test/data/utrecht_tiles"); const auto test_request = R"({ @@ -174,7 +172,7 @@ const auto test_request_osrm = R"({ })"; // clang-format off -std::vector> matrix_answers = {{28, 28}, {2027, 1837}, {2403, 2213}, {4163, 3838}, +std::vector> matrix_answers = {{28, 28}, {2027, 1837}, {2403, 2213}, {4163, 3838}, {1519, 1398}, {1808, 1638}, {2061, 1951}, {3944, 3639}, {2311, 2111}, {701, 641}, {0, 0}, {2821, 2626}, {5562, 5177}, {3952, 3707}, {4367, 4107}, {1825, 1680}}; @@ -309,7 +307,7 @@ TEST(Matrix, test_timedistancematrix_forward) { // expected results are the same as `matrix_answers`, but without the last origin // clang-format off - std::vector> expected_results = {{28, 28}, {2027, 1837}, {2403, 2213}, {4163, 3838}, + std::vector> expected_results = {{28, 28}, {2027, 1837}, {2403, 2213}, {4163, 3838}, {1519, 1398}, {1808, 1638}, {2061, 1951}, {3944, 3639}, {2311, 2111}, {701, 641}, {0, 0}, {2821, 2626}}; // clang-format on @@ -360,7 +358,7 @@ TEST(Matrix, test_timedistancematrix_reverse) { // expected results are the same as `matrix_answers`, but without the last target // clang-format off - std::vector> expected_results = {{28, 28}, {2027, 1837}, {2403, 2213}, + std::vector> expected_results = {{28, 28}, {2027, 1837}, {2403, 2213}, {1519, 1398}, {1808, 1638}, {2061, 1951}, {2311, 2111}, {701, 641}, {0, 0}, {5562, 5177}, {3952, 3707}, {4367, 4107}}; diff --git a/test/node_search.cc b/test/node_search.cc index ea2466906f..f2ba87d603 100644 --- a/test/node_search.cc +++ b/test/node_search.cc @@ -1,5 +1,7 @@ #include "loki/node_search.h" + #include +#include #include "baldr/rapidjson_utils.h" #include @@ -17,7 +19,6 @@ namespace vm = valhalla::midgard; namespace vb = valhalla::baldr; -#include "filesystem.h" #include "mjolnir/directededgebuilder.h" #include "mjolnir/graphtilebuilder.h" #include "mjolnir/graphvalidator.h" @@ -41,7 +42,7 @@ struct graph_writer { void write_tiles(); private: - const uint8_t m_level; + [[maybe_unused]] const uint8_t m_level; std::unordered_map> m_builders; }; @@ -272,8 +273,8 @@ void graph_builder::write_tiles(uint8_t level) const { void make_tile() { // make sure that all the old tiles are gone before trying to make new ones. - if (filesystem::is_directory(test_tile_dir)) { - filesystem::remove_all(test_tile_dir); + if (std::filesystem::is_directory(test_tile_dir)) { + std::filesystem::remove_all(test_tile_dir); } graph_builder builder; diff --git a/test/search.cc b/test/search.cc index e5cefc746c..4fdd76c2b5 100644 --- a/test/search.cc +++ b/test/search.cc @@ -1,5 +1,7 @@ #include "loki/search.h" + #include +#include #include #include @@ -9,7 +11,6 @@ #include "baldr/location.h" #include "baldr/pathlocation.h" #include "baldr/tilehierarchy.h" -#include "filesystem.h" #include "midgard/pointll.h" #include "midgard/vector2.h" #include "sif/nocost.h" @@ -50,8 +51,8 @@ std::pair c({tile_id.tileid(), tile_id.level(), 2}, {.01, .01} std::pair d({tile_id.tileid(), tile_id.level(), 3}, {.2, .1}); void clean_tiles() { - if (filesystem::is_directory(tile_dir)) { - filesystem::remove_all(tile_dir); + if (std::filesystem::is_directory(tile_dir)) { + std::filesystem::remove_all(tile_dir); } } diff --git a/test/test.cc b/test/test.cc index cb06940e45..ac7aa7807d 100644 --- a/test/test.cc +++ b/test/test.cc @@ -8,6 +8,7 @@ #include "mjolnir/graphtilebuilder.h" #include +#include #include #include #include @@ -23,7 +24,6 @@ #include -#include "filesystem.h" #include "microtar.h" namespace { @@ -442,8 +442,8 @@ void build_live_traffic_data(const boost::property_tree::ptree& config, std::string tile_dir = config.get("mjolnir.tile_dir"); std::string traffic_extract = config.get("mjolnir.traffic_extract"); - filesystem::path parent_dir = filesystem::path(traffic_extract).parent_path(); - if (!filesystem::exists(parent_dir)) { + std::filesystem::path parent_dir = std::filesystem::path(traffic_extract).parent_path(); + if (!std::filesystem::exists(parent_dir)) { std::stringstream ss; ss << "Traffic extract directory " << parent_dir.string() << " does not exist"; throw std::runtime_error(ss.str()); diff --git a/test/util_mjolnir.cc b/test/util_mjolnir.cc index 82ca6a5530..b4a3e7b04d 100644 --- a/test/util_mjolnir.cc +++ b/test/util_mjolnir.cc @@ -1,3 +1,4 @@ +#include #include #include #include @@ -6,7 +7,6 @@ #include "baldr/graphid.h" #include "baldr/rapidjson_utils.h" -#include "filesystem.h" #include "mjolnir/util.h" #include "test.h" @@ -36,8 +36,8 @@ TEST(UtilMjolnir, BuildTileSet) { EXPECT_TRUE(build_tile_set(config, {VALHALLA_SOURCE_DIR "test/data/harrisburg.osm.pbf"}, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kCleanup)); // Clear the tile directory so it doesn't interfere with the next test. - filesystem::remove_all(tile_dir); - EXPECT_TRUE(!filesystem::exists(tile_dir)); + std::filesystem::remove_all(tile_dir); + EXPECT_TRUE(!std::filesystem::exists(tile_dir)); } TEST(UtilMjolnir, TileManifestReadFromFile) { diff --git a/test/utrecht.cc b/test/utrecht.cc index ede7264632..e3b6aea833 100644 --- a/test/utrecht.cc +++ b/test/utrecht.cc @@ -1,4 +1,5 @@ -#include "filesystem.h" +#include + #include "midgard/sequence.h" #include "mjolnir/osmnode.h" #include "mjolnir/pbfgraphparser.h" @@ -30,17 +31,6 @@ std::string to_restriction_file = "test_to_complex_restrictions_utrecht.bin"; std::string bss_file = "test_bss_nodes_utrecht.bin"; std::string linguistic_node_file = "test_linguistic_node_utrecht.bin"; -const auto node_predicate = [](const OSMWayNode& a, const OSMWayNode& b) { - return a.node.osmid_ < b.node.osmid_; -}; - -OSMNode GetNode(uint64_t node_id, sequence& way_nodes) { - auto found = way_nodes.find({node_id}, node_predicate); - if (found == way_nodes.end()) - throw std::runtime_error("Couldn't find node: " + std::to_string(node_id)); - return (*found).node; -} - auto way_predicate = [](const OSMWay& a, const OSMWay& b) { return a.osmwayid_ < b.osmwayid_; }; OSMWay GetWay(uint32_t way_id, sequence& ways) { @@ -233,13 +223,13 @@ class UtrecthTestSuiteEnv : public ::testing::Environment { } void TearDown() override { - filesystem::remove(ways_file); - filesystem::remove(way_nodes_file); - filesystem::remove(access_file); - filesystem::remove(from_restriction_file); - filesystem::remove(to_restriction_file); - filesystem::remove(bss_file); - filesystem::remove(linguistic_node_file); + std::filesystem::remove(ways_file); + std::filesystem::remove(way_nodes_file); + std::filesystem::remove(access_file); + std::filesystem::remove(from_restriction_file); + std::filesystem::remove(to_restriction_file); + std::filesystem::remove(bss_file); + std::filesystem::remove(linguistic_node_file); } }; diff --git a/valhalla/mjolnir/osmdata.h b/valhalla/mjolnir/osmdata.h index c6f1829e26..1dced82929 100644 --- a/valhalla/mjolnir/osmdata.h +++ b/valhalla/mjolnir/osmdata.h @@ -25,8 +25,8 @@ enum class OSMType : uint8_t { kNode, kWay, kRelation }; // Structure to store OSM node information and associate it to an OSM way struct OSMWayNode { OSMNode node; - uint32_t way_index; - uint32_t way_shape_node_index; + uint32_t way_index = 0; + uint32_t way_shape_node_index = 0; }; // OSM bicycle data (stored within OSMData) From fca9135dd8e27b05d8dee3782c441f4965029c5c Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 8 Apr 2024 16:16:26 +0200 Subject: [PATCH 064/198] contract edges with differing non-conditional access restrictions (#4613) --- .gitignore | 1 + CHANGELOG.md | 7 +- src/mjolnir/restrictionbuilder.cc | 4 +- src/mjolnir/shortcutbuilder.cc | 119 +++++++++++------- test/astar.cc | 81 +++++++++++- test/gurka/gurka.cc | 1 + test/gurka/test_64bit_wayid.cc | 12 +- test/gurka/test_gtfs.cc | 2 +- test/gurka/test_landmarks.cc | 3 + test/gurka/test_shortcut.cc | 130 ++++++++++---------- test/scripts/test_valhalla_build_extract.py | 6 +- valhalla/baldr/accessrestriction.h | 2 +- 12 files changed, 243 insertions(+), 125 deletions(-) diff --git a/.gitignore b/.gitignore index d66fafb158..50020889ae 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ CMakeLists.txt.user .idea /.tidytmp vcpkg*/ +*.log # python .venv diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c2171d6ca..279e22fbc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,9 @@ * FIXED: unidirectional_astar.cc doesn't work for date_time type = 2 #4652(https://github.com/valhalla/valhalla/issues/4652) * FIXED: a few fixes around the routing algorithms [#4626](https://github.com/valhalla/valhalla/pull/4642) * FIXED: no need to search for GDAL when building data [#4651](https://github.com/valhalla/valhalla/pull/4651) + * FIXED: Fix segfault in OSRM serializer with bannerInstructions when destination is on roundabout [#4480](https://github.com/valhalla/valhalla/pull/4481) + * FIXED: Fix segfault in costmatrix (date_time and time zone always added). [#4530](https://github.com/valhalla/valhalla/pull/4530) + * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) @@ -85,7 +88,6 @@ * UPDATED: updated country access overrides [#4460](https://github.com/valhalla/valhalla/pull/4460) * CHANGED: date_time refactor as a preparation to return DST/timezone related offset in the response [#4365](https://github.com/valhalla/valhalla/pull/4365) * ADDED: find connection on backward search for bidir matrix algo [#4329](https://github.com/valhalla/valhalla/pull/4329) - * FIXED: Fix segfault in OSRM serializer with bannerInstructions when destination is on roundabout [#4480](https://github.com/valhalla/valhalla/pull/4481) * CHANGED: Adjustment of walk speed when walking on slight downhill [#4302](https://github.com/valhalla/valhalla/pull/4302) * CHANGED: Do not reclassify ferry connections when no hierarchies are to be generated [#4487](https://github.com/valhalla/valhalla/pull/4487) * ADDED: Added a config option to sort nodes spatially during graph building [#4455](https://github.com/valhalla/valhalla/pull/4455) @@ -94,14 +96,12 @@ * CHANGED: use pkg-config to find spatialite & geos and remove our cmake modules; upgraded conan's boost to 1.83.0 in the process [#4253](https://github.com/valhalla/valhalla/pull/4253) * ADDED: Added aggregation logic to filter stage of tile building [#4512](https://github.com/valhalla/valhalla/pull/4512) * UPDATED: tz to 2023d [#4519](https://github.com/valhalla/valhalla/pull/4519) - * FIXED: Fix segfault in costmatrix (date_time and time zone always added). [#4530](https://github.com/valhalla/valhalla/pull/4530) * CHANGED: libvalhalla.pc generation to have finer controls; install third_party public headers; overhaul lots of CMake; remove conan support [#4516](https://github.com/valhalla/valhalla/pull/4516) * CHANGED: refactored matrix code to include a base class for all matrix algorithms to prepare for second passes on matrix [#4535](https://github.com/valhalla/valhalla/pull/4535) * ADDED: matrix second pass for connections not found in the first pass, analogous to /route [#4536](https://github.com/valhalla/valhalla/pull/4536) * UPDATED: cxxopts to 3.1.1 [#4541](https://github.com/valhalla/valhalla/pull/4541) * CHANGED: make use of vendored libraries optional (other than libraries which are not commonly in package managers or only used for testing) [#4544](https://github.com/valhalla/valhalla/pull/4544) * ADDED: Improved instructions for blind users [#3694](https://github.com/valhalla/valhalla/pull/3694) - * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) * ADDED: isochrone proper polygon support & pbf output for isochrone [#4575](https://github.com/valhalla/valhalla/pull/4575) * ADDED: return isotile grid as geotiff [#4594](https://github.com/valhalla/valhalla/pull/4594) * ADDED: `ignore_non_vehicular_restrictions` parameter for truck costing [#4606](https://github.com/valhalla/valhalla/pull/4606) @@ -109,6 +109,7 @@ * ADDED: `hgv_no_penalty` costing option to allow penalized truck access to `hgv=no` edges [#4650](https://github.com/valhalla/valhalla/pull/4650) * CHANGED: Significantly improve performance of graphbuilder [#4669](https://github.com/valhalla/valhalla/pull/4669) * UPDATED: Improved turn by turn api reference documentation [#4675](https://github.com/valhalla/valhalla/pull/4675) + * CHANGED: contract nodes if connecting edges have different names or speed or non-conditional access restrictions [#4613](https://github.com/valhalla/valhalla/pull/4613) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/mjolnir/restrictionbuilder.cc b/src/mjolnir/restrictionbuilder.cc index da322d954e..da77858a16 100644 --- a/src/mjolnir/restrictionbuilder.cc +++ b/src/mjolnir/restrictionbuilder.cc @@ -270,8 +270,8 @@ ComplexRestrictionBuilder CreateComplexRestriction(const OSMRestriction& restric }; struct Result { - uint32_t forward_restrictions_count; - uint32_t reverse_restrictions_count; + uint32_t forward_restrictions_count = 0; + uint32_t reverse_restrictions_count = 0; std::vector restrictions; std::unordered_set part_of_restriction; }; diff --git a/src/mjolnir/shortcutbuilder.cc b/src/mjolnir/shortcutbuilder.cc index d9d36fbec0..7f9a9edaa3 100644 --- a/src/mjolnir/shortcutbuilder.cc +++ b/src/mjolnir/shortcutbuilder.cc @@ -29,6 +29,50 @@ using namespace valhalla::mjolnir; namespace { +struct ShortcutAccessRestriction { + std::unordered_map all_restrictions; + // important to set the edge's attribute + uint64_t modes = 0; + + ShortcutAccessRestriction(const std::vector&& restrictions) { + for (const auto& res : restrictions) { + modes |= res.modes(); + all_restrictions.emplace(res.type(), std::move(res)); + } + }; + + // updates non-conditional restrictions if their value is lower than the current value + // TODO(nils): we could also contract over conditional restrictions with a bit more work: + // kTimeDenied is fine to just append all restrictions of the base edges, but kTimeAllowed + // will be harder, there we'll have to merge overlapping time periods + void update_nonconditional(const std::vector&& other_restrictions) { + for (const auto& new_ar : other_restrictions) { + // update the modes for the edge attribute regardless + if (new_ar.type() == AccessType::kTimedAllowed || new_ar.type() == AccessType::kTimedDenied || + new_ar.type() == AccessType::kDestinationAllowed) { + continue; + } + modes |= new_ar.modes(); + auto ar_inserted = all_restrictions.emplace(new_ar.type(), new_ar); + if (!ar_inserted.second && new_ar.value() < ar_inserted.first->second.value()) { + ar_inserted.first->second = std::move(new_ar); + } + } + } +}; + +// only keeps access restrictions which can fail contraction +void remove_nonconditional_restrictions(std::vector& access_restrictions) { + access_restrictions.erase(std::remove_if(std::begin(access_restrictions), + std::end(access_restrictions), + [](const AccessRestriction& elem) { + return elem.type() != AccessType::kDestinationAllowed && + elem.type() != AccessType::kTimedAllowed && + elem.type() != AccessType::kTimedDenied; + }), + std::end(access_restrictions)); +} + // Simple structure to hold the 2 pair of directed edges at a node. // First edge in the pair is incoming and second is outgoing struct EdgePairs { @@ -59,9 +103,9 @@ bool EdgesMatch(const graph_tile_ptr& tile, const DirectedEdge* edge1, const Dir return false; } - // Neither edge can be part of a complex turn restriction or differ on access restrictions + // Neither edge can be part of a complex turn restriction if (edge1->start_restriction() || edge1->end_restriction() || edge2->start_restriction() || - edge2->end_restriction() || edge1->access_restriction() != edge2->access_restriction()) { + edge2->end_restriction()) { return false; } @@ -77,20 +121,12 @@ bool EdgesMatch(const graph_tile_ptr& tile, const DirectedEdge* edge1, const Dir return false; } - // Names must match - // TODO - this allows matches in any order. Do we need to maintain order? - // TODO - should allow near matches? - std::vector edge1names = tile->GetNames(edge1); - std::vector edge2names = tile->GetNames(edge2); - std::sort(edge1names.begin(), edge1names.end()); - std::sort(edge2names.begin(), edge2names.end()); - if (edge1names != edge2names) - return false; - - // if they have access restrictions those must match (for modes that use shortcuts) - if (edge1->access_restriction()) { + // if there's conditional access restrictions, they must match; others we can safely contract over + if (edge1->access_restriction() || edge2->access_restriction()) { auto res1 = tile->GetAccessRestrictions(edge1 - tile->directededge(0), kVehicularAccess); + remove_nonconditional_restrictions(res1); auto res2 = tile->GetAccessRestrictions(edge2 - tile->directededge(0), kVehicularAccess); + remove_nonconditional_restrictions(res2); if (res1.size() != res2.size()) return false; for (size_t i = 0; i < res1.size(); ++i) { @@ -231,11 +267,6 @@ bool CanContract(GraphReader& reader, return false; } - // Can not have different speeds in the same direction - if ((oppdiredge1->speed() != edge2->speed()) || (oppdiredge2->speed() != edge1->speed())) { - return false; - } - // ISO country codes at the end nodes must equal this node std::string iso = tile->admininfo(nodeinfo->admin_index()).country_iso(); std::string e1_iso = EndNodeIso(edge1, reader); @@ -280,7 +311,8 @@ void ConnectEdges(GraphReader& reader, uint32_t& restrictions, float& average_density, float& total_duration, - float& total_truck_duration) { + float& total_truck_duration, + ShortcutAccessRestriction& access_restrictions) { // Get the tile and directed edge. auto tile = reader.GetGraphTile(startnode); const DirectedEdge* directededge = tile->directededge(edgeid); @@ -324,6 +356,9 @@ void ConnectEdges(GraphReader& reader, // Add to the weighted average average_density += directededge->length() * directededge->density(); + // Preserve the most restrictive access restrictions + access_restrictions.update_nonconditional(tile->GetAccessRestrictions(edgeid.id(), kAllAccess)); + // Update the end node endnode = directededge->endnode(); } @@ -403,22 +438,10 @@ std::pair AddShortcutEdges(GraphReader& reader, std::reverse(shape.begin(), shape.end()); } - // Get names - they apply over all edges of the shortcut - auto names = edgeinfo.GetNames(); - auto tagged_values = edgeinfo.GetTaggedValues(); - auto linguistics = edgeinfo.GetLinguisticTaggedValues(); - - auto types = edgeinfo.GetTypes(); - - // Add any access restriction records. We don't contract if they differ, so if - // there's any, they're the same for all involved edges - if (newedge.access_restriction()) { - auto restrictions = tile->GetAccessRestrictions(edge_id.id(), kAllAccess); - for (const auto& res : restrictions) { - tilebuilder.AddAccessRestriction(AccessRestriction(tilebuilder.directededges().size(), - res.type(), res.modes(), res.value())); - } - } + // store all access_restrictions of the base edge: non-conditional ones will be updated while + // contracting, conditional ones are breaking contraction and are safe to simply copy + ShortcutAccessRestriction access_restrictions{ + tile->GetAccessRestrictions(edge_id.id(), kAllAccess)}; // Connect edges to the shortcut while the end node is marked as // contracted (contains edge pairs in the shortcut info). @@ -454,13 +477,10 @@ std::pair AddShortcutEdges(GraphReader& reader, // on the connected shortcut - need to set that so turn restrictions // off of shortcuts work properly ConnectEdges(reader, end_node, next_edge_id, shape, end_node, opp_local_idx, rst, - average_density, total_duration, total_truck_duration); + average_density, total_duration, total_truck_duration, access_restrictions); total_edge_count++; } - // Names can be different in the forward and backward direction - bool diff_names = tilebuilder.OpposingEdgeInfoDiffers(tile, directededge); - // Get the length from the shape. This prevents roundoff issues when forming // elevation. uint32_t length = valhalla::midgard::length(shape); @@ -469,13 +489,14 @@ std::pair AddShortcutEdges(GraphReader& reader, // edge in case multiple shortcut edges exist between the 2 nodes. // Test whether this shape is forward or reverse (in case an existing // edge exists). Shortcuts use way Id = 0.Set mean elevation to 0 as a placeholder, - // set it later if adding elevation to this dataset. + // set it later if adding elevation to this dataset. No need for names etc, shortcuts + // aren't used in guidance bool forward = true; uint32_t idx = ((length & 0xfffff) | ((shape.size() & 0xfff) << 20)); uint32_t edge_info_offset = tilebuilder.AddEdgeInfo(idx, start_node, end_node, 0, 0, edgeinfo.bike_network(), - edgeinfo.speed_limit(), shape, names, tagged_values, linguistics, - types, forward, diff_names); + edgeinfo.speed_limit(), shape, {}, {}, {}, 0, forward, false); + ; newedge.set_edgeinfo_offset(edge_info_offset); @@ -489,6 +510,18 @@ std::pair AddShortcutEdges(GraphReader& reader, newedge.set_opp_local_idx(opp_local_idx); newedge.set_restrictions(rst); + // add new access restrictions if any and set the mask on the edge + if (access_restrictions.all_restrictions.size()) { + newedge.set_access_restriction(access_restrictions.modes); + for (const auto& res : access_restrictions.all_restrictions) { + tilebuilder.AddAccessRestriction(AccessRestriction(tilebuilder.directededges().size(), + res.second.type(), res.second.modes(), + res.second.value())); + } + } + + // set new access mask + // Update the length, curvature, and end node newedge.set_length(length); newedge.set_curvature(compute_curvature(shape)); diff --git a/test/astar.cc b/test/astar.cc index f2868007fc..ba1f6a2097 100644 --- a/test/astar.cc +++ b/test/astar.cc @@ -1571,7 +1571,7 @@ TEST(BiDiAstar, test_recost_path) { )"; const gurka::ways ways = { // make ABC to be a shortcut - {"ABC", {{"highway", "primary"}, {"maxspeed", "80"}}}, + {"ABC", {{"highway", "motorway"}, {"maxspeed", "80"}}}, // make CDE to be a shortcut {"CDE", {{"highway", "primary"}, {"maxspeed", "80"}}}, {"1A", {{"highway", "secondary"}}}, @@ -1580,8 +1580,8 @@ TEST(BiDiAstar, test_recost_path) { {"D5", {{"highway", "secondary"}}}, // set speeds less than on ABCDE path to force the algorithm // to go through ABCDE nodes instead of AXY - {"AX", {{"highway", "primary"}, {"maxspeed", "70"}}}, - {"XY", {{"highway", "primary"}, {"maxspeed", "70"}}}, + {"AX", {{"highway", "motorway"}, {"maxspeed", "70"}}}, + {"XY", {{"highway", "trunk"}, {"maxspeed", "70"}}}, {"YE", {{"highway", "primary"}, {"maxspeed", "80"}}}, {"E2", {{"highway", "secondary"}}}, }; @@ -1679,6 +1679,81 @@ TEST(BiDiAstar, test_recost_path) { } } +// TODO(nils): this test fails currently, because bidir A* has a problem with 2 shortcuts between the +// same nodes: https://github.com/valhalla/valhalla/issues/4609 +TEST(BiDiAstar, DISABLED_test_recost_path_failing) { + const std::string ascii_map = R"( + X-----------Y + / \ + 1----A E---2 + \ / + B--C--------D + )"; + const gurka::ways ways = { + // make ABCDE to be a shortcut + {"ABC", {{"highway", "primary"}, {"maxspeed", "80"}}}, + {"CDE", {{"highway", "primary"}, {"maxspeed", "80"}}}, + {"1A", {{"highway", "secondary"}}}, + // set speeds less than on ABCDE path to force the algorithm + // to go through ABCDE nodes instead of AXY + {"AX", {{"highway", "primary"}, {"maxspeed", "70"}}}, + {"XY", {{"highway", "primary"}, {"maxspeed", "70"}}}, + {"YE", {{"highway", "primary"}, {"maxspeed", "80"}}}, + {"E2", {{"highway", "secondary"}}}, + }; + + auto nodes = gurka::detail::map_to_coordinates(ascii_map, 500); + + const std::string test_dir = "test/data/astar_shortcuts_recosting"; + const auto map = gurka::buildtiles(nodes, ways, {}, {}, test_dir); + + vb::GraphReader graphreader(map.config.get_child("mjolnir")); + + // before continue check that ABC is actually a shortcut + const auto ABCDE = gurka::findEdgeByNodes(graphreader, nodes, "A", "E"); + ASSERT_TRUE(std::get<1>(ABCDE)->is_shortcut()) << "Expected ABCDE to be a shortcut"; + + Options options; + create_costing_options(options, Costing::auto_); + vs::TravelMode travel_mode = vs::TravelMode::kDrive; + const auto mode_costing = vs::CostFactory().CreateModeCosting(options, travel_mode); + + std::vector locations; + // set origin location + locations.push_back({nodes["1"]}); + // set destination location + locations.push_back({nodes["2"]}); + auto pbf_locations = ToPBFLocations(locations, graphreader, mode_costing[int(travel_mode)]); + + vt::BidirectionalAStar astar; + + // hack hierarchy limits to allow to go through the shortcut + { + auto& hierarchy_limits = + mode_costing[int(travel_mode)]->GetHierarchyLimits(); // access mutable limits + for (auto& hierarchy : hierarchy_limits) { + hierarchy.Relax(0.f, 0.f); + } + } + const auto path = + astar.GetBestPath(pbf_locations[0], pbf_locations[1], graphreader, mode_costing, travel_mode) + .front(); + + // collect names of base edges + std::vector expected_names = {"1A", "AB", "BC", "CD", "DE", "E2"}; + std::vector actual_names; + for (const auto& info : path) { + const auto* edge = graphreader.directededge(info.edgeid); + ASSERT_FALSE(edge->is_shortcut()) << "Final path shouldn't contain shortcuts"; + + const auto name = graphreader.edgeinfo(info.edgeid).GetNames()[0]; + actual_names.emplace_back(name); + } + // TODO(nils): it gets the wrong path! bidir A* has a problem with 2 shortcuts between the same + // nodes + EXPECT_EQ(actual_names, expected_names); +} + class BiAstarTest : public thor::BidirectionalAStar { public: explicit BiAstarTest(const boost::property_tree::ptree& config = {}) : BidirectionalAStar(config) { diff --git a/test/gurka/gurka.cc b/test/gurka/gurka.cc index 6fece2c239..ca0f22bed6 100644 --- a/test/gurka/gurka.cc +++ b/test/gurka/gurka.cc @@ -488,6 +488,7 @@ map buildtiles(const nodelayout& layout, * @param end_node the node that should be the target of the directed edge you want * @param tile_id optional tile_id to limit the search to * @param way_id optional way_id to limit the search to + * @param is_shortcut whether we want a shortcut returned * @return the directed edge that matches, or nullptr if there was no match */ std::tuple osm_way_ids) { // check all edges have correct edge info for (const auto& tile_id : reader.GetTileSet()) { auto tile = reader.GetGraphTile(tile_id); - for (auto edge = tile_id; edge.id() < tile->header()->directededgecount(); ++edge) { - // we should find every way id in the tile set - auto info = tile->edgeinfo(tile->directededge(edge)); + for (auto edge_id = tile_id; edge_id.id() < tile->header()->directededgecount(); ++edge_id) { + // we should find every way id in the tile set, unless it's a shortcut, no way id there + auto* edge = tile->directededge(edge_id); + if (edge->is_shortcut()) { + continue; + } + auto info = tile->edgeinfo(edge); auto id = info.wayid(); auto found = osm_way_ids.find(id); if (found == osm_way_ids.cend()) { - EXPECT_FALSE(found == osm_way_ids.cend()) << " couldnt find " << info.wayid(); + ASSERT_FALSE(found == osm_way_ids.cend()) << " couldnt find " << info.wayid(); return; } osm_way_ids.erase(found); diff --git a/test/gurka/test_gtfs.cc b/test/gurka/test_gtfs.cc index aa43999789..7ad51bac8c 100644 --- a/test/gurka/test_gtfs.cc +++ b/test/gurka/test_gtfs.cc @@ -847,7 +847,7 @@ TEST(GtfsExample, MakeTile) { } EXPECT_EQ(transit_nodes, 15); - EXPECT_EQ(uses[Use::kRoad], 12); + EXPECT_EQ(uses[Use::kRoad], 14); // + 2 shortcuts EXPECT_EQ(uses[Use::kTransitConnection], 20); EXPECT_EQ(uses[Use::kPlatformConnection], 10); EXPECT_EQ(uses[Use::kEgressConnection], 10); diff --git a/test/gurka/test_landmarks.cc b/test/gurka/test_landmarks.cc index b449bed419..b565b29cc1 100644 --- a/test/gurka/test_landmarks.cc +++ b/test/gurka/test_landmarks.cc @@ -143,6 +143,9 @@ void CheckLandmarksInTiles(GraphReader& reader, const GraphId& graphid) { auto tile = reader.GetGraphTile(graphid); for (const auto& e : tile->GetDirectedEdges()) { + if (e.is_shortcut()) { + continue; + } auto ei = tile->edgeinfo(&e); auto tagged_values = ei.GetTags(); diff --git a/test/gurka/test_shortcut.cc b/test/gurka/test_shortcut.cc index b16f357fc9..028fb6e1b5 100644 --- a/test/gurka/test_shortcut.cc +++ b/test/gurka/test_shortcut.cc @@ -67,38 +67,6 @@ TEST(Shortcuts, LoopWithoutShortcut) { EXPECT_FALSE(shortcut.Is_Valid()) << "Shortcuts found. Check the map."; } -// Here no shortcuts are created. There could be one from A to C with speed 80 but in the opposite -// direction speeds differ which blocks CA creation. -TEST(Shortcuts, CreateInvalid) { - constexpr double gridsize = 50; - - const std::string ascii_map = R"( - A--B--C - )"; - - const gurka::ways ways = { - {"AB", - {{"highway", "primary"}, - {"name", "Independence Avenue"}, - {"maxspeed:forward", "80"}, - {"maxspeed:backward", "80"}}}, - {"BC", - {{"highway", "primary"}, - {"name", "Independence Avenue"}, - {"maxspeed:forward", "80"}, - {"maxspeed:backward", "90"}}}, - }; - - const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); - auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_openlrjoiner_shortcut_speed"); - - baldr::GraphReader graph_reader(map.config.get_child("mjolnir")); - - // check that there are no shortcut edges - EXPECT_ANY_THROW(gurka::findEdgeByNodes(graph_reader, layout, "A", "C")); - EXPECT_ANY_THROW(gurka::findEdgeByNodes(graph_reader, layout, "C", "A")); -} - TEST(Shortcuts, ShortcutSpeed) { // At C node turn duration is present. As a result the speed for AE shortcut is decreased // from 100 kph to 93 kph and for EA shortcut - from 100 kph to 98 kph in the test case below. @@ -363,60 +331,95 @@ TEST(Shortcuts, ShortcutsInBins) { } TEST(Shortcuts, ShortcutRestrictions) { + using node_pairs = std::vector>; + + // the first line should produce only one HUGE shortcut, the second line one small one const std::string ascii_map = R"( A--B--C--D--E--F G--H--I--J--K--L )"; - // the first line should produce only one shortcut, the second two + std::map high_access_res = {{"highway", "motorway"}, {"hazmat", "yes"}, + {"maxweight", "30"}, {"maxheight", "6"}, + {"maxlength", "10"}, {"maxaxles", "10"}}; + std::map low_access_res = {{"highway", "motorway"}, {"hazmat", "no"}, + {"maxweight", "3"}, {"maxheight", "3"}, + {"maxlength", "4"}, {"maxaxles", "4"}}; + gurka::ways ways = { - {"AB", {{"highway", "motorway"}, {"name", "highway"}}}, - {"BC", {{"highway", "motorway"}, {"name", "highway"}}}, - {"CD", {{"highway", "motorway"}, {"name", "highway"}, {"maxweight", "3.5"}}}, - {"DE", {{"highway", "motorway"}, {"name", "highway"}, {"maxweight", "8"}}}, - {"EF", {{"highway", "motorway"}, {"name", "highway"}}}, - - {"GH", {{"highway", "motorway"}, {"name", "highway"}}}, - {"HI", {{"highway", "motorway"}, {"name", "highway"}, {"hazmat", "yes"}}}, - {"IJ", {{"highway", "motorway"}, {"name", "highway"}, {"hazmat", "yes"}}}, - {"JK", {{"highway", "motorway"}, {"name", "highway"}}}, - {"KL", {{"highway", "motorway"}, {"name", "highway"}}}, + {"AB", {{"highway", "motorway"}}}, + {"BC", {{"highway", "motorway"}}}, + {"CD", high_access_res}, + {"DE", low_access_res}, + {"EF", {{"highway", "motorway"}}}, + + {"GH", {{"highway", "motorway"}}}, + {"HI", {{"highway", "motorway"}, {"motorcar:conditional", "yes @ 00:00-07:00"}}}, + {"IJ", {{"highway", "motorway"}, {"motorcar:conditional", "no @ 00:00-07:00"}}}, + {"JK", {{"highway", "motorway"}}}, + {"KL", {{"highway", "motorway"}}}, }; const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); auto map = gurka::buildtiles(layout, ways, {}, {}, VALHALLA_BUILD_DIR "test/data/gurka_shortcut_restrictions"); baldr::GraphReader reader(map.config.get_child("mjolnir")); - // test we got the right shortcuts edges, implicitly means they were broken properly - for (const auto& end_node : {"C", "J", "L"}) { - const auto shortcut = - gurka::findEdge(reader, layout, "highway", end_node, baldr::GraphId{}, 0, true); + // test we got the right shortcuts edges for the second line of the map + // implicitly means they were broken properly + for (const auto& pair : node_pairs{{"A", "F"}, {"F", "A"}, {"J", "L"}, {"L", "J"}}) { + const auto shortcut = gurka::findEdgeByNodes(reader, layout, pair.first, pair.second); EXPECT_TRUE(std::get<1>(shortcut)->is_shortcut()); - EXPECT_TRUE(std::get<3>(shortcut)->is_shortcut()); - EXPECT_NEAR(std::get<1>(shortcut)->length(), 3000, 1); - EXPECT_NEAR(std::get<3>(shortcut)->length(), 3000, 1); + } + + // test that the long shortcut has the strictest non-conditional access restrictions + const auto AF = gurka::findEdgeByNodes(reader, layout, "A", "F"); + const auto AF_res = + reader.GetGraphTile(std::get<0>(AF))->GetAccessRestrictions(std::get<0>(AF).id(), kAllAccess); + EXPECT_EQ(AF_res.size(), 5); + for (const auto& res : AF_res) { + uint64_t expected_value = 0; + switch (res.type()) { + case AccessType::kHazmat: + // should be false/0 + break; + case AccessType::kMaxWeight: + expected_value = strtoull(low_access_res["maxweight"].c_str(), nullptr, 10) * 100; + break; + case AccessType::kMaxHeight: + expected_value = strtoull(low_access_res["maxheight"].c_str(), nullptr, 10) * 100; + break; + case AccessType::kMaxLength: + expected_value = strtoull(low_access_res["maxlength"].c_str(), nullptr, 10) * 100; + break; + case AccessType::kMaxAxles: + expected_value = strtoull(low_access_res["maxaxles"].c_str(), nullptr, 10); + break; + default: + break; + } + EXPECT_EQ(res.value(), expected_value); } // test the right edges are really superseded by a shortcut // forward - for (const auto& end_node : {"B", "I", "K"}) { - const auto edge = gurka::findEdge(reader, layout, "highway", end_node); + for (const auto& pair : node_pairs{{"A", "B"}, {"J", "K"}}) { + const auto edge = gurka::findEdgeByNodes(reader, layout, pair.first, pair.second); EXPECT_NE(std::get<1>(edge)->superseded(), 0); } // reverse - for (const auto& end_node : {"C", "J", "L"}) { - const auto edge = gurka::findEdge(reader, layout, "highway", end_node); - EXPECT_NE(std::get<3>(edge)->superseded(), 0); + for (const auto& pair : node_pairs{{"F", "E"}, {"L", "K"}}) { + const auto edge = gurka::findEdgeByNodes(reader, layout, pair.first, pair.second); + EXPECT_NE(std::get<1>(edge)->superseded(), 0); } // test that without those restrictions we're still building all shortcuts // remove those access restrictions - ways["CD"].erase("maxweight"); - ways["DE"].erase("maxweight"); - ways["HI"].erase("hazmat"); - ways["IJ"].erase("hazmat"); + ways["CD"] = {{"highway", "motorway"}}; + ways["DE"] = {{"highway", "motorway"}}; + ways["HI"] = {{"highway", "motorway"}}; + ways["IJ"] = {{"highway", "motorway"}}; auto map2 = gurka::buildtiles(layout, ways, {}, {}, VALHALLA_BUILD_DIR "test/data/gurka_shortcut_without_restrictions"); baldr::GraphReader reader2(map2.config.get_child("mjolnir")); @@ -430,12 +433,9 @@ TEST(Shortcuts, ShortcutRestrictions) { } // we did build the long shorcuts across all edges - for (const auto& end_node : {"F", "L"}) { - const auto shortcut = - gurka::findEdge(reader2, layout, "highway", end_node, baldr::GraphId{}, 0, true); + for (const auto& pair : node_pairs{{"A", "F"}, {"F", "A"}, {"G", "L"}, {"L", "G"}}) { + const auto shortcut = gurka::findEdgeByNodes(reader2, layout, pair.first, pair.second); EXPECT_TRUE(std::get<1>(shortcut)->is_shortcut()); - EXPECT_TRUE(std::get<3>(shortcut)->is_shortcut()); EXPECT_NEAR(std::get<1>(shortcut)->length(), 7500, 1); - EXPECT_NEAR(std::get<3>(shortcut)->length(), 7500, 1); } } diff --git a/test/scripts/test_valhalla_build_extract.py b/test/scripts/test_valhalla_build_extract.py index a671a10199..ae62f3d903 100644 --- a/test/scripts/test_valhalla_build_extract.py +++ b/test/scripts/test_valhalla_build_extract.py @@ -151,15 +151,15 @@ def test_create_extracts(self): tile_count = len(tile_resolver.matched_paths) # test that the index has the right offsets/sizes - exp_tuples = ((2560, 25568, 302472), (306688, 410441, 676208), (984576, 6549282, 6137768)) + exp_tuples = ((2560, 25568, 296768), (301056, 410441, 665624), (968704, 6549282, 6137080)) self.check_tar(EXTRACT_PATH, exp_tuples, tile_count * INDEX_BIN_SIZE) # same for traffic.tar - exp_tuples = ((1536, 25568, 26416), (28672, 410441, 65312), (94720, 6549282, 604608)) + exp_tuples = ((1536, 25568, 25856), (28160, 410441, 64400), (93184, 6549282, 604608)) self.check_tar(TRAFFIC_PATH, exp_tuples, tile_count * INDEX_BIN_SIZE) # tests the implementation using the tile_dir new_tile_extract = TILE_PATH.joinpath("tiles2.tar") - exp_tuples = ((2560, 25568, 302472), (306688, 410441, 676208), (984576, 6549282, 6137768)) + exp_tuples = ((2560, 25568, 296768), (301056, 410441, 665624), (968704, 6549282, 6137080)) tile_resolver = TileResolver(EXTRACT_PATH) tile_resolver.matched_paths = tile_resolver.normalized_tile_paths valhalla_build_extract.create_extracts(config, True, tile_resolver, new_tile_extract) diff --git a/valhalla/baldr/accessrestriction.h b/valhalla/baldr/accessrestriction.h index 624a0b03f8..672bfe77e3 100644 --- a/valhalla/baldr/accessrestriction.h +++ b/valhalla/baldr/accessrestriction.h @@ -39,7 +39,7 @@ class AccessRestriction { /** * Get the modes impacted by access restriction. - * @return Returns a bit field of affected modes. + * @return Returns a bit mask of affected modes. */ uint32_t modes() const; From 1b864c9b0da39550d3137949477806b8539d40ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Hrab=C3=A1nek?= Date: Wed, 10 Apr 2024 18:46:42 +0200 Subject: [PATCH 065/198] Add vcpkg overlay port for xz package to fix windows CI workflow (#4679) --- .../liblzma/add_support_ios.patch | 20 +++++ overlay-ports-vcpkg/liblzma/build-tools.patch | 20 +++++ .../liblzma/fix_config_include.patch | 12 +++ overlay-ports-vcpkg/liblzma/portfile.cmake | 86 +++++++++++++++++++ overlay-ports-vcpkg/liblzma/usage | 9 ++ .../liblzma/vcpkg-cmake-wrapper.cmake | 64 ++++++++++++++ overlay-ports-vcpkg/liblzma/vcpkg.json | 23 +++++ .../liblzma/win_output_name.patch | 17 ++++ vcpkg-configuration.json | 5 ++ 9 files changed, 256 insertions(+) create mode 100644 overlay-ports-vcpkg/liblzma/add_support_ios.patch create mode 100644 overlay-ports-vcpkg/liblzma/build-tools.patch create mode 100644 overlay-ports-vcpkg/liblzma/fix_config_include.patch create mode 100644 overlay-ports-vcpkg/liblzma/portfile.cmake create mode 100644 overlay-ports-vcpkg/liblzma/usage create mode 100644 overlay-ports-vcpkg/liblzma/vcpkg-cmake-wrapper.cmake create mode 100644 overlay-ports-vcpkg/liblzma/vcpkg.json create mode 100644 overlay-ports-vcpkg/liblzma/win_output_name.patch create mode 100644 vcpkg-configuration.json diff --git a/overlay-ports-vcpkg/liblzma/add_support_ios.patch b/overlay-ports-vcpkg/liblzma/add_support_ios.patch new file mode 100644 index 0000000000..79741639b6 --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/add_support_ios.patch @@ -0,0 +1,20 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 52439b3..0b5e371 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -574,6 +574,7 @@ if(HAVE_GETOPT_LONG) + + install(TARGETS xzdec + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ++ BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}" + COMPONENT xzdec) + + if(UNIX) +@@ -701,6 +702,7 @@ if(NOT MSVC AND HAVE_GETOPT_LONG) + + install(TARGETS xz + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ++ BUNDLE DESTINATION "${CMAKE_INSTALL_BINDIR}" + COMPONENT xz) + + if(UNIX) diff --git a/overlay-ports-vcpkg/liblzma/build-tools.patch b/overlay-ports-vcpkg/liblzma/build-tools.patch new file mode 100644 index 0000000000..759345ef23 --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/build-tools.patch @@ -0,0 +1,20 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 03b8301..820d08e 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -584,6 +584,7 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/liblzma-config.cmake" + COMPONENT liblzma_Development) + + ++if(BUILD_TOOLS) + ############################################################################# + # getopt_long + ############################################################################# +@@ -793,6 +794,7 @@ if(NOT MSVC AND HAVE_GETOPT_LONG) + endforeach() + endif() + endif() ++endif() + + + ############################################################################# diff --git a/overlay-ports-vcpkg/liblzma/fix_config_include.patch b/overlay-ports-vcpkg/liblzma/fix_config_include.patch new file mode 100644 index 0000000000..91dc4c13ba --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/fix_config_include.patch @@ -0,0 +1,12 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 34c6aca00..7b3708ab2 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -413,6 +413,7 @@ if(WIN32) + if(BUILD_SHARED_LIBS) + # Add the Windows resource file for liblzma.dll. + target_sources(liblzma PRIVATE src/liblzma/liblzma_w32res.rc) ++ target_include_directories(liblzma PRIVATE windows/vs2019) + + set_target_properties(liblzma PROPERTIES + LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/common/common_w32res.rc" diff --git a/overlay-ports-vcpkg/liblzma/portfile.cmake b/overlay-ports-vcpkg/liblzma/portfile.cmake new file mode 100644 index 0000000000..c551658fe0 --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/portfile.cmake @@ -0,0 +1,86 @@ +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO xz-mirror/xz + REF "v${VERSION}" + SHA512 c28461123562564e030f3f733f078bc4c840e87598d9f4b718d4bca639120d8133f969c45d7bdc62f33f081d789ec0f14a1791fb7da18515682bfe3c0c7362e0 + HEAD_REF master + PATCHES + fix_config_include.patch + win_output_name.patch # Fix output name on Windows. Autotool build does not generate lib prefixed libraries on windows. + add_support_ios.patch # add install bundle info for support ios + build-tools.patch +) + +vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS + FEATURES + tools BUILD_TOOLS +) + +if(VCPKG_TARGET_ARCHITECTURE STREQUAL "wasm32") + set(WASM_OPTIONS -DCMAKE_C_BYTE_ORDER=LITTLE_ENDIAN -DCMAKE_CXX_BYTE_ORDER=LITTLE_ENDIAN) +endif() + +vcpkg_cmake_configure( + SOURCE_PATH "${SOURCE_PATH}" + OPTIONS + ${FEATURE_OPTIONS} + ${WASM_OPTIONS} + -DBUILD_TESTING=OFF + -DCREATE_XZ_SYMLINKS=OFF + -DCREATE_LZMA_SYMLINKS=OFF + -DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT= # using flags from (vcpkg) toolchain + MAYBE_UNUSED_VARIABLES + CMAKE_MSVC_DEBUG_INFORMATION_FORMAT + CREATE_XZ_SYMLINKS + CREATE_LZMA_SYMLINKS +) +vcpkg_cmake_install() +vcpkg_copy_pdbs() + +set(exec_prefix "\${prefix}") +set(libdir "\${prefix}/lib") +set(includedir "\${prefix}/include") +set(PACKAGE_URL https://tukaani.org/xz/) +set(PACKAGE_VERSION 5.4.3) +if(NOT VCPKG_TARGET_IS_WINDOWS) + set(PTHREAD_CFLAGS -pthread) +endif() +set(prefix "${CURRENT_INSTALLED_DIR}") +configure_file("${SOURCE_PATH}/src/liblzma/liblzma.pc.in" "${CURRENT_PACKAGES_DIR}/lib/pkgconfig/liblzma.pc" @ONLY) +if (NOT VCPKG_BUILD_TYPE) + set(prefix "${CURRENT_INSTALLED_DIR}/debug") + configure_file("${SOURCE_PATH}/src/liblzma/liblzma.pc.in" "${CURRENT_PACKAGES_DIR}/debug/lib/pkgconfig/liblzma.pc" @ONLY) +endif() +vcpkg_fixup_pkgconfig() + +vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/liblzma) + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/lzma.h" "defined(LZMA_API_STATIC)" "1") +else() + vcpkg_replace_string("${CURRENT_PACKAGES_DIR}/include/lzma.h" "defined(LZMA_API_STATIC)" "0") +endif() + +file(REMOVE_RECURSE + "${CURRENT_PACKAGES_DIR}/debug/include" + "${CURRENT_PACKAGES_DIR}/debug/share" + "${CURRENT_PACKAGES_DIR}/share/man" +) + +set(TOOLS xz xzdec) +foreach(_tool IN LISTS TOOLS) + if(NOT EXISTS "${CURRENT_PACKAGES_DIR}/bin/${_tool}${VCPKG_TARGET_EXECUTABLE_SUFFIX}") + list(REMOVE_ITEM TOOLS ${_tool}) + endif() +endforeach() +if(TOOLS) + vcpkg_copy_tools(TOOL_NAMES ${TOOLS} AUTO_CLEAN) +endif() + +if(VCPKG_LIBRARY_LINKAGE STREQUAL "static") + file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/bin" "${CURRENT_PACKAGES_DIR}/debug/bin") +endif() + +file(COPY "${CMAKE_CURRENT_LIST_DIR}/vcpkg-cmake-wrapper.cmake" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}") +vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/COPYING") diff --git a/overlay-ports-vcpkg/liblzma/usage b/overlay-ports-vcpkg/liblzma/usage new file mode 100644 index 0000000000..b8a87394ec --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/usage @@ -0,0 +1,9 @@ +liblzma is compatible with built-in CMake targets: + + find_package(LibLZMA REQUIRED) + target_link_libraries(main PRIVATE LibLZMA::LibLZMA) + +liblzma provides CMake targets: + + find_package(liblzma CONFIG REQUIRED) + target_link_libraries(main PRIVATE liblzma::liblzma) diff --git a/overlay-ports-vcpkg/liblzma/vcpkg-cmake-wrapper.cmake b/overlay-ports-vcpkg/liblzma/vcpkg-cmake-wrapper.cmake new file mode 100644 index 0000000000..826cdba065 --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/vcpkg-cmake-wrapper.cmake @@ -0,0 +1,64 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0012 NEW) +cmake_policy(SET CMP0057 NEW) +set(z_vcpkg_liblzma_fixup_needed 0) +if(NOT "CONFIG" IN_LIST ARGS AND NOT "NO_MODULE" IN_LIST ARGS AND NOT CMAKE_DISABLE_FIND_PACKAGE_LibLZMA) + get_filename_component(z_vcpkg_liblzma_prefix "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY) + get_filename_component(z_vcpkg_liblzma_prefix "${z_vcpkg_liblzma_prefix}" DIRECTORY) + find_path(LIBLZMA_INCLUDE_DIR NAMES lzma.h PATHS "${z_vcpkg_liblzma_prefix}/include" NO_DEFAULT_PATH) + # liblzma doesn't use a debug postfix, but FindLibLZMA.cmake expects it + find_library(LIBLZMA_LIBRARY_RELEASE NAMES lzma PATHS "${z_vcpkg_liblzma_prefix}/lib" NO_DEFAULT_PATH) + find_library(LIBLZMA_LIBRARY_DEBUG NAMES lzma PATHS "${z_vcpkg_liblzma_prefix}/debug/lib" NO_DEFAULT_PATH) + unset(z_vcpkg_liblzma_prefix) + if(CMAKE_VERSION VERSION_LESS 3.16) + # Older versions of FindLibLZMA.cmake need a single lib in LIBLZMA_LIBRARY. + set(z_vcpkg_liblzma_fixup_needed 1) + set(LIBLZMA_LIBRARY "${LIBLZMA_LIBRARY_RELEASE}" CACHE INTERNAL "") + elseif(NOT TARGET LibLZMA::LibLZMA) + set(z_vcpkg_liblzma_fixup_needed 1) + endif() + # Known values, and required. Skip expensive tests. + set(LIBLZMA_HAS_AUTO_DECODER 1 CACHE INTERNAL "") + set(LIBLZMA_HAS_EASY_ENCODER 1 CACHE INTERNAL "") + set(LIBLZMA_HAS_LZMA_PRESET 1 CACHE INTERNAL "") +endif() + +_find_package(${ARGS}) + +if(z_vcpkg_liblzma_fixup_needed) + include(SelectLibraryConfigurations) + select_library_configurations(LIBLZMA) + if(NOT TARGET LibLZMA::LibLZMA) + # Backfill LibLZMA::LibLZMA to versions of cmake before 3.14 + add_library(LibLZMA::LibLZMA UNKNOWN IMPORTED) + if(DEFINED LIBLZMA_INCLUDE_DIRS) + set_target_properties(LibLZMA::LibLZMA PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${LIBLZMA_INCLUDE_DIRS}") + endif() + set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(LibLZMA::LibLZMA PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" + IMPORTED_LOCATION_RELEASE "${LIBLZMA_LIBRARY_RELEASE}") + if(EXISTS "${LIBLZMA_LIBRARY}") + set_target_properties(LibLZMA::LibLZMA PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${LIBLZMA_LIBRARY}") + endif() + endif() + if(LIBLZMA_LIBRARY_DEBUG) + # Backfill debug variant to versions of cmake before 3.16 + set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(LibLZMA::LibLZMA PROPERTIES IMPORTED_LOCATION_DEBUG "${LIBLZMA_LIBRARY_DEBUG}") + endif() +endif() +if(LIBLZMA_LIBRARIES AND NOT "Threads::Threads" IN_LIST LIBLZMA_LIBRARIES) + set(THREADS_PREFER_PTHREAD_FLAG TRUE) + find_package(Threads) + list(APPEND LIBLZMA_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + if(TARGET LibLZMA::LibLZMA) + set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads) + endif() +endif() +unset(z_vcpkg_liblzma_fixup_needed) +cmake_policy(POP) diff --git a/overlay-ports-vcpkg/liblzma/vcpkg.json b/overlay-ports-vcpkg/liblzma/vcpkg.json new file mode 100644 index 0000000000..e9168357cc --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/vcpkg.json @@ -0,0 +1,23 @@ +{ + "name": "liblzma", + "version": "5.4.4", + "description": "Compression library with an API similar to that of zlib.", + "homepage": "https://tukaani.org/xz/", + "license": null, + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ], + "features": { + "tools": { + "description": "Build tools", + "supports": "!windows, mingw" + } + } +} diff --git a/overlay-ports-vcpkg/liblzma/win_output_name.patch b/overlay-ports-vcpkg/liblzma/win_output_name.patch new file mode 100644 index 0000000000..9a845bdbae --- /dev/null +++ b/overlay-ports-vcpkg/liblzma/win_output_name.patch @@ -0,0 +1,17 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 0c6d4b7..62a824a 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -868,8 +868,11 @@ set_target_properties(liblzma PROPERTIES + + # It's liblzma.so or liblzma.dll, not libliblzma.so or lzma.dll. + # Avoid the name lzma.dll because it would conflict with LZMA SDK. +- PREFIX "" ++ OUTPUT_NAME lzma + ) ++if(WIN32 AND NOT MINGW) ++ set_target_properties(liblzma PROPERTIES RUNTIME_OUTPUT_NAME liblzma) ++endif() + + # Create liblzma-config-version.cmake. + # diff --git a/vcpkg-configuration.json b/vcpkg-configuration.json new file mode 100644 index 0000000000..391ce615db --- /dev/null +++ b/vcpkg-configuration.json @@ -0,0 +1,5 @@ +{ + "overlay-ports": [ + "./overlay-ports-vcpkg" + ] +} From afd01613cd430bdc9550cfd7352dea7552004be5 Mon Sep 17 00:00:00 2001 From: Nils Date: Fri, 12 Apr 2024 20:41:36 +0200 Subject: [PATCH 066/198] astar costmatrix (#4671) --- CHANGELOG.md | 1 + src/thor/costmatrix.cc | 136 +++++++++++++++++++++++++---------- src/tyr/matrix_serializer.cc | 4 +- test/matrix.cc | 2 +- valhalla/thor/costmatrix.h | 17 +++++ 5 files changed, 120 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 279e22fbc4..9beac3379d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -110,6 +110,7 @@ * CHANGED: Significantly improve performance of graphbuilder [#4669](https://github.com/valhalla/valhalla/pull/4669) * UPDATED: Improved turn by turn api reference documentation [#4675](https://github.com/valhalla/valhalla/pull/4675) * CHANGED: contract nodes if connecting edges have different names or speed or non-conditional access restrictions [#4613](https://github.com/valhalla/valhalla/pull/4613) + * CHANGED: CostMatrix switched from Dijkstra to A* [#4650](https://github.com/valhalla/valhalla/pull/4650) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 21e440c93d..576967404e 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -95,31 +95,34 @@ void CostMatrix::Clear() { // Resize and shrink_to_fit so all capacity is reduced. auto label_reservation = clear_reserved_memory_ ? 0 : max_reserved_labels_count_; auto locs_reservation = clear_reserved_memory_ ? 0 : max_reserved_locations_count_; - for (const auto exp_dir : {MATRIX_FORW, MATRIX_REV}) { + for (const auto is_fwd : {MATRIX_FORW, MATRIX_REV}) { // resize all relevant structures down to configured amount of locations (25 default) - if (locs_count_[exp_dir] > locs_reservation) { - edgelabel_[exp_dir].resize(locs_reservation); - edgelabel_[exp_dir].shrink_to_fit(); - adjacency_[exp_dir].resize(locs_reservation); - adjacency_[exp_dir].shrink_to_fit(); - edgestatus_[exp_dir].resize(locs_reservation); - edgestatus_[exp_dir].shrink_to_fit(); + if (locs_count_[is_fwd] > locs_reservation) { + edgelabel_[is_fwd].resize(locs_reservation); + edgelabel_[is_fwd].shrink_to_fit(); + adjacency_[is_fwd].resize(locs_reservation); + adjacency_[is_fwd].shrink_to_fit(); + edgestatus_[is_fwd].resize(locs_reservation); + edgestatus_[is_fwd].shrink_to_fit(); + astar_heuristics_[is_fwd].resize(locs_reservation); + astar_heuristics_[is_fwd].shrink_to_fit(); } - for (auto& iter : edgelabel_[exp_dir]) { + for (auto& iter : edgelabel_[is_fwd]) { if (iter.size() > label_reservation) { iter.resize(label_reservation); iter.shrink_to_fit(); } iter.clear(); } - for (auto& iter : edgestatus_[exp_dir]) { + for (auto& iter : edgestatus_[is_fwd]) { iter.clear(); } - for (auto& iter : adjacency_[exp_dir]) { + for (auto& iter : adjacency_[is_fwd]) { iter.clear(); } - hierarchy_limits_[exp_dir].clear(); - locs_status_[exp_dir].clear(); + hierarchy_limits_[is_fwd].clear(); + locs_status_[is_fwd].clear(); + astar_heuristics_[is_fwd].clear(); } best_connection_.clear(); ignore_hierarchy_limits_ = false; @@ -190,7 +193,7 @@ bool CostMatrix::SourceToTarget(Api& request, if (targets.empty() && locs_status_[MATRIX_FORW][source].threshold > 0) { // TODO(nils): shouldn't we extend the search here similar to bidir A* // i.e. if pruning was disabled we extend the search in the other direction - locs_status_[MATRIX_FORW][i].threshold = -1; + locs_status_[MATRIX_FORW][source].threshold = -1; if (locs_remaining_[MATRIX_FORW] > 0) { locs_remaining_[MATRIX_FORW]--; } @@ -225,7 +228,7 @@ bool CostMatrix::SourceToTarget(Api& request, if (sources.empty() && locs_status_[MATRIX_REV][target].threshold > 0) { // TODO(nils): shouldn't we extend the search here similar to bidir A* // i.e. if pruning was disabled we extend the search in the other direction - locs_status_[MATRIX_REV][i].threshold = -1; + locs_status_[MATRIX_REV][target].threshold = -1; if (locs_remaining_[MATRIX_REV] > 0) { locs_remaining_[MATRIX_REV]--; } @@ -318,6 +321,8 @@ void CostMatrix::Initialize( locs_count_[MATRIX_FORW] = source_locations.size(); locs_count_[MATRIX_REV] = target_locations.size(); + astar_heuristics_[MATRIX_FORW].resize(target_locations.size()); + astar_heuristics_[MATRIX_REV].resize(source_locations.size()); const auto& hlimits = costing_->GetHierarchyLimits(); ignore_hierarchy_limits_ = @@ -326,22 +331,40 @@ void CostMatrix::Initialize( return limits.max_up_transitions == kUnlimitedTransitions; }); - // Add initial sources status - for (const auto exp_dir : {MATRIX_FORW, MATRIX_REV}) { - const auto count = locs_count_[exp_dir]; - locs_status_[exp_dir].reserve(count); - hierarchy_limits_[exp_dir].resize(count); - adjacency_[exp_dir].resize(count); - edgestatus_[exp_dir].resize(count); - edgelabel_[exp_dir].resize(count); + const uint32_t bucketsize = costing_->UnitSize(); + const float range = kBucketCount * bucketsize; + + // Add initial sources & targets properties + for (const auto is_fwd : {MATRIX_FORW, MATRIX_REV}) { + const auto count = locs_count_[is_fwd]; + const auto other_count = locs_count_[!is_fwd]; + + const auto& locations = is_fwd ? source_locations : target_locations; + const auto& other_locations = is_fwd ? target_locations : source_locations; + + locs_status_[is_fwd].reserve(count); + hierarchy_limits_[is_fwd].resize(count); + adjacency_[is_fwd].resize(count); + edgestatus_[is_fwd].resize(count); + edgelabel_[is_fwd].resize(count); for (uint32_t i = 0; i < count; i++) { // Allocate the adjacency list and hierarchy limits for this source. // Use the cost threshold to size the adjacency list. - edgelabel_[exp_dir][i].reserve(max_reserved_labels_count_); - adjacency_[exp_dir][i].reuse(0, current_cost_threshold_, costing_->UnitSize(), - &edgelabel_[exp_dir][i]); - locs_status_[exp_dir].emplace_back(kMaxThreshold); - hierarchy_limits_[exp_dir][i] = hlimits; + edgelabel_[is_fwd][i].reserve(max_reserved_labels_count_); + locs_status_[is_fwd].emplace_back(kMaxThreshold); + hierarchy_limits_[is_fwd][i] = hlimits; + // for each source/target init the other direction's astar heuristic + auto& ll = locations[i].ll(); + astar_heuristics_[!is_fwd][i].Init({ll.lng(), ll.lat()}, costing_->AStarCostFactor()); + + // get the min heuristic to all targets/sources for this source's/target's adjacency list + float min_heuristic = std::numeric_limits::max(); + for (uint32_t j = 0; j < other_count; j++) { + auto& other_ll = other_locations[j].ll(); + auto heuristic = astar_heuristics_[!is_fwd][i].Get({other_ll.lng(), other_ll.lat()}); + min_heuristic = std::min(min_heuristic, heuristic); + } + adjacency_[is_fwd][i].reuse(min_heuristic, range, bucketsize, &edgelabel_[is_fwd][i]); } } @@ -442,6 +465,8 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, return true; } + // TODO(nils): refactor this whole thing to only have a single if (FORWARD) {} + const baldr::DirectedEdge* opp_edge = nullptr; if (!FORWARD) { // Check the access mode and skip this edge if access is not allowed in the reverse @@ -495,12 +520,14 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, const auto pred_dist = pred.path_distance() + meta.edge->length(); auto& adj = adjacency_[FORWARD][index]; // Check if edge is temporarily labeled and this path has less cost. If - // less cost the predecessor is updated along with new cost and distance. + // less cost the predecessor is updated and the sort cost is decremented + // by the difference in real cost (A* heuristic doesn't change) if (meta.edge_status->set() == EdgeSet::kTemporary) { BDEdgeLabel& lab = edgelabel_[FORWARD][index][meta.edge_status->index()]; if (newcost.cost < lab.cost().cost) { - adj.decrease(meta.edge_status->index(), newcost.cost); - lab.Update(pred_idx, newcost, newcost.cost, tc, pred_dist, restriction_idx); + float newsortcost = lab.sortcost() - (lab.cost().cost - newcost.cost); + adj.decrease(meta.edge_status->index(), newsortcost); + lab.Update(pred_idx, newcost, newsortcost, tc, pred_dist, restriction_idx); } // Returning true since this means we approved the edge return true; @@ -516,6 +543,11 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, bool not_thru_pruning = not_thru_pruning_ ? (pred.not_thru_pruning() || !meta.edge->not_thru()) : false; + // TODO(nils): we could use the distance to the source/target to disable hierarchy limits + // , just as bidir a* /route does; that would make for more optimal paths in some edge cases but + // we'd pay a severe performance penalty, e.g. a request with distance checks (i.e. more expansion + // on lower levels) takes 100 secs, while without it takes 60 secs. + // Add edge label, add to the adjacency list and set edge status uint32_t idx = edgelabels.size(); *meta.edge_status = {EdgeSet::kTemporary, idx}; @@ -540,7 +572,11 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, opp_edge->destonly() || (costing_->is_hgv() && opp_edge->destonly_hgv()), opp_edge->forwardaccess() & kTruckAccess); } + auto newsortcost = + GetAstarHeuristic(index, t2->get_node_ll(meta.edge->endnode())); + edgelabels.back().SetSortCost(newcost.cost + newsortcost); adj.add(idx); + // mark the edge as settled for the connection check if (!FORWARD) { (*targets_)[meta.edge_id].push_back(index); @@ -1006,8 +1042,9 @@ void CostMatrix::SetSources(GraphReader& graphreader, // Get the directed edge and the opposing edge Id graph_tile_ptr tile = graphreader.GetGraphTile(edgeid); + graph_tile_ptr opp_tile = tile; const DirectedEdge* directededge = tile->directededge(edgeid); - GraphId oppedge = graphreader.GetOpposingEdgeId(edgeid); + GraphId oppedgeid = graphreader.GetOpposingEdgeId(edgeid, opp_tile); // Get cost. Get distance along the remainder of this edge. uint8_t flow_sources; @@ -1020,13 +1057,11 @@ void CostMatrix::SetSources(GraphReader& graphreader, // TODO: assumes 1m/s which is a maximum penalty this could vary per costing model cost.cost += edge.distance(); - // Set the initial not_thru flag to false. There is an issue with not_thru - // flags on small loops. Set this to false here to override this for now. // 2 adjustments related only to properly handle trivial routes: // - "transition_cost" is used to store the traversed secs & length // - "path_id" is used to store whether the edge is even allowed (e.g. no oneway) Cost ec(std::round(edgecost.secs), static_cast(directededge->length())); - BDEdgeLabel edge_label(kInvalidLabel, edgeid, oppedge, directededge, cost, mode_, ec, d, + BDEdgeLabel edge_label(kInvalidLabel, edgeid, oppedgeid, directededge, cost, mode_, ec, d, !directededge->not_thru(), !(costing_->IsClosed(directededge, tile)), static_cast(flow_sources & kDefaultFlowMask), InternalTurn::kNoTurn, kInvalidRestriction, @@ -1034,6 +1069,13 @@ void CostMatrix::SetSources(GraphReader& graphreader, directededge->destonly() || (costing_->is_hgv() && directededge->destonly_hgv()), directededge->forwardaccess() & kTruckAccess); + auto newsortcost = + GetAstarHeuristic(index, opp_tile->get_node_ll( + directededge->endnode())); + edge_label.SetSortCost(cost.cost + newsortcost); + + // Set the initial not_thru flag to false. There is an issue with not_thru + // flags on small loops. Set this to false here to override this for now. edge_label.set_not_thru(false); // Add EdgeLabel to the adjacency list (but do not set its status). @@ -1105,8 +1147,6 @@ void CostMatrix::SetTargets(baldr::GraphReader& graphreader, // TODO: assumes 1m/s which is a maximum penalty this could vary per costing model cost.cost += edge.distance(); - // Set the initial not_thru flag to false. There is an issue with not_thru - // flags on small loops. Set this to false here to override this for now. // 2 adjustments related only to properly handle trivial routes: // - "transition_cost" is used to store the traversed secs & length // - "path_id" is used to store whether the opp edge is even allowed (e.g. no oneway) @@ -1119,6 +1159,13 @@ void CostMatrix::SetTargets(baldr::GraphReader& graphreader, directededge->destonly() || (costing_->is_hgv() && directededge->destonly_hgv()), directededge->forwardaccess() & kTruckAccess); + + auto newsortcost = + GetAstarHeuristic(index, + tile->get_node_ll(opp_dir_edge->endnode())); + edge_label.SetSortCost(cost.cost + newsortcost); + // Set the initial not_thru flag to false. There is an issue with not_thru + // flags on small loops. Set this to false here to override this for now. edge_label.set_not_thru(false); // Add EdgeLabel to the adjacency list (but do not set its status). @@ -1285,5 +1332,20 @@ std::string CostMatrix::RecostFormPath(GraphReader& graphreader, return encode(points, shape_format != polyline5 ? 1e6 : 1e5); } +template +float CostMatrix::GetAstarHeuristic(const uint32_t loc_idx, const PointLL& ll) const { + if (locs_status_[FORWARD][loc_idx].unfound_connections.empty()) { + return 0.f; + } + + auto min_cost = std::numeric_limits::max(); + for (const auto other_idx : locs_status_[FORWARD][loc_idx].unfound_connections) { + const auto cost = astar_heuristics_[FORWARD][other_idx].Get(ll); + min_cost = std::min(cost, min_cost); + } + + return min_cost; +}; + } // namespace thor } // namespace valhalla diff --git a/src/tyr/matrix_serializer.cc b/src/tyr/matrix_serializer.cc index 14a7348c10..fa58ada1be 100644 --- a/src/tyr/matrix_serializer.cc +++ b/src/tyr/matrix_serializer.cc @@ -50,7 +50,7 @@ json::ArrayPtr serialize_shape(const valhalla::Matrix& matrix, const ShapeFormat shape_format) { // TODO(nils): shapes aren't implemented yet in TDMatrix auto shapes = json::array({}); - if (shape_format == no_shape || matrix.algorithm() != Matrix::CostMatrix) + if (shape_format == no_shape || (matrix.algorithm() != Matrix::CostMatrix)) return shapes; for (size_t i = start_td; i < start_td + td_count; ++i) { @@ -213,7 +213,7 @@ std::string serialize(const Api& request, double distance_scale) { } matrix->emplace("distances", distance); matrix->emplace("durations", time); - if (!(options.shape_format() == no_shape) && request.matrix().algorithm() == Matrix::CostMatrix) + if (!(options.shape_format() == no_shape) && (request.matrix().algorithm() == Matrix::CostMatrix)) matrix->emplace("shapes", shapes); json->emplace("sources_to_targets", matrix); diff --git a/test/matrix.cc b/test/matrix.cc index a362be740b..93e5646fa5 100644 --- a/test/matrix.cc +++ b/test/matrix.cc @@ -254,7 +254,7 @@ TEST(Matrix, test_matrix) { ++found; } } - EXPECT_EQ(found, 10) << " not the number of results as expected"; + EXPECT_EQ(found, 11) << " not the number of results as expected"; request.clear_matrix(); TimeDistanceMatrix timedist_matrix; diff --git a/valhalla/thor/costmatrix.h b/valhalla/thor/costmatrix.h index 40697f4c59..49dfcad068 100644 --- a/valhalla/thor/costmatrix.h +++ b/valhalla/thor/costmatrix.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -151,6 +152,9 @@ class CostMatrix : public MatrixAlgorithm { std::array>, 2> edgelabel_; std::array, 2> edgestatus_; + // A* heuristics for both trees and each location + std::array, 2> astar_heuristics_; + // List of best connections found so far std::vector best_connection_; @@ -345,6 +349,19 @@ class CostMatrix : public MatrixAlgorithm { } }; + /** + * Get the minimum AStar heuristic for a given source/target, i.e. for a source we get + * the minimum heuristic of all targets for the forward expansion, so that we direct + * the search towards the closest target/source. + * + * @param loc_idx either the source or target index + * @param node_ll the current edge's end node's lat/lon + * @returns The heuristic for the closest target/source of the passed node + */ + template + float GetAstarHeuristic(const uint32_t loc_idx, const PointLL& node_ll) const; + private: class ReachedMap; From 13c6580a30e53a2e0a122be9fa11fb6e02a92e75 Mon Sep 17 00:00:00 2001 From: Trietes <33934221+Trietes@users.noreply.github.com> Date: Mon, 15 Apr 2024 09:10:44 +0200 Subject: [PATCH 067/198] Add missing docs (#4687) --- CHANGELOG.md | 1 + docs/docs/api/isochrone/api-reference.md | 18 +++--------------- docs/docs/api/turn-by-turn/api-reference.md | 15 ++++++++++++++- valhalla/sif/dynamiccost.h | 3 ++- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9beac3379d..c3436325c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -111,6 +111,7 @@ * UPDATED: Improved turn by turn api reference documentation [#4675](https://github.com/valhalla/valhalla/pull/4675) * CHANGED: contract nodes if connecting edges have different names or speed or non-conditional access restrictions [#4613](https://github.com/valhalla/valhalla/pull/4613) * CHANGED: CostMatrix switched from Dijkstra to A* [#4650](https://github.com/valhalla/valhalla/pull/4650) + * ADDED: some missing documentation about request parameters [#4687](https://github.com/valhalla/valhalla/pull/4687) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/docs/docs/api/isochrone/api-reference.md b/docs/docs/api/isochrone/api-reference.md index 27d82a047b..cdedab9ad8 100644 --- a/docs/docs/api/isochrone/api-reference.md +++ b/docs/docs/api/isochrone/api-reference.md @@ -47,7 +47,9 @@ The isochrone service uses the `auto`, `bicycle`, `pedestrian`, and `multimodal` | `polygons` | A Boolean value to determine whether to return geojson polygons or linestrings as the contours. The default is `false`, which returns lines; when `true`, polygons are returned. Note: When `polygons` is `true`, a feature's geometry type can be either `Polygon` or `MultiPolygon`, depending on the number of exterior rings formed for a given interval. | | `denoise` | A floating point value from `0` to `1` (default of `1`) which can be used to remove smaller contours. A value of `1` will only return the largest contour for a given time value. A value of `0.5` drops any contours that are less than half the area of the largest contour in the set of contours for that same time value. | | `generalize` | A floating point value in meters used as the tolerance for [Douglas-Peucker](https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm) generalization. Note: Generalization of contours can lead to self-intersections, as well as intersections of adjacent contours. | -| `show_locations` | A boolean indicating whether the input locations should be returned as MultiPoint features: one feature for the exact input coordinates and one feature for the coordinates of the network node it snapped to. Default false. +| `show_locations` | A boolean indicating whether the input locations should be returned as MultiPoint features: one feature for the exact input coordinates and one feature for the coordinates of the network node it snapped to. Default false. | +| `reverse` | A boolean which can be set to do inverse expansion of the isochrone. The reverse isochrone will show from which area the given location can be reached within the given time. + ## Outputs of the Isochrone service @@ -65,20 +67,6 @@ When making a map, drawing the isochrone contours as lines is more straightforwa (TODO: write something about rendering the GeoTIFF output.) -## Future work on the isochrone service - -The Isochrone service is in active development. To report software issues or suggest enhancements, open an issue in the [Valhalla GitHub repository](https://github.com/valhalla/valhalla/issues). - -Several other options are being considered as future service enhancements. These include: - -* ~~Using distance rather than time for each unit.~~ -* ~~Generating outer contours or contours with interior holes for regions that cannot be accessed within the specified time.~~ -* ~~Options to control the minimum size of interior holes.~~ -* ~~Removing self intersections from polygonal contours.~~ -* ~~Allowing multiple locations to compute the region reachable from any of the locations within a specified time.~~ -* ~~Generating contours with reverse access logic to see the region that can reach a specific location within the specified time.~~ -* ~~Returning raster data for potential animation using OpenGL shaders. This also has analysis use for being able to query thousands of locations to determine the time to each location, including improvements with one-to-many requests to the Valhalla Time-Distance Matrix service.~~ - ## Data credits The image includes data from [OpenStreetMap](http://www.openstreetmap.org/). diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 304405b3a9..656e62123e 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -93,6 +93,12 @@ A special costing option is `shortest`, which, when `true`, will solely use dist Another special case is `disable_hierarchy_pruning` costing option. As the name indicates, `disable_hierarchy_pruning = true` will disable hierarchies in routing algorithms, which allows us to find the actual optimal route even in edge cases. For example, together with `shortest = true` they can find the actual shortest route. When `disable_hierarchy_pruning` is `true` and arc distances between source and target are not above the max limit, the actual optimal route will be calculated at the expense of performance. Note that if arc distances between locations exceed the max limit, `disable_hierarchy_pruning` is `true` will not be applied. This costing option is available for all motorized costing models, i.e `auto`, `motorcycle`, `motor_scooter`, `bus`, `truck` & `taxi`. For `bicycle` and `pedestrian` hierarchies are always disabled by default. +Additionally to the main costing option, the `recostings` option can be used to calculate the travelling time of the found route based on different costing options. By e.g. adding +```json +"recostings":[{"costing":"auto","fixed_speed":20,"name":"auto_20","costing":"auto","fixed_speed":50,"name":"auto_50"}] +``` +to the route request, the values `time_auto_20` and `time_auto_50` will be added to summaries to show how much time the route would cost with these given costing options. Passing a recosting which make the route impossible to follow (e.g. the main rout is by car over a motorway and recosting with pedestrian costing) leads to a `none` result of this recosting. + ##### Automobile and bus costing options These options are available for `auto`, `bus`, and `truck` costing methods. @@ -103,6 +109,7 @@ These options are available for `auto`, `bus`, and `truck` costing methods. | `gate_cost` | A cost applied when a [gate](http://wiki.openstreetmap.org/wiki/Tag:barrier%3Dgate) with undefined or private access is encountered. This cost is added to the estimated time / elapsed time. The default gate cost is 30 seconds. | | `gate_penalty` | A penalty applied when a [gate](https://wiki.openstreetmap.org/wiki/Tag:barrier%3Dgate) with no access information is on the road. The default gate penalty is 300 seconds. | | `private_access_penalty` | A penalty applied when a [gate](https://wiki.openstreetmap.org/wiki/Tag:barrier%3Dgate) or [bollard](https://wiki.openstreetmap.org/wiki/Tag:barrier%3Dbollard) with `access=private` is encountered. The default private access penalty is 450 seconds. | +| `destination_only_penalty` | A penalty applied when entering an road which is only allowed to enter if necessary to reach the [destination](https://wiki.openstreetmap.org/wiki/Tag:vehicle%3Ddestination). | | `toll_booth_cost` | A cost applied when a [toll booth](http://wiki.openstreetmap.org/wiki/Tag:barrier%3Dtoll_booth) is encountered. This cost is added to the estimated and elapsed times. The default cost is 15 seconds. | | `toll_booth_penalty` | A penalty applied to the cost when a [toll booth](http://wiki.openstreetmap.org/wiki/Tag:barrier%3Dtoll_booth) is encountered. This penalty can be used to create paths that avoid toll roads. The default toll booth penalty is 0. | | `ferry_cost` | A cost applied when entering a ferry. This cost is added to the estimated and elapsed times. The default cost is 300 seconds (5 minutes). | @@ -116,6 +123,7 @@ These options are available for `auto`, `bus`, and `truck` costing methods. | `country_crossing_cost` | A cost applied when encountering an international border. This cost is added to the estimated and elapsed times. The default cost is 600 seconds. | | `country_crossing_penalty` | A penalty applied for a country crossing. This penalty can be used to create paths that avoid spanning country boundaries. The default penalty is 0. | | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | +| `use_distance` | A factor that allows controlling the contribution of distance and time to the route costs. The value is in range between 0 and 1, where 0 only takes time into account (default) and 1 only distance. A factor of 0.5 will weight them roughly equally. **Note:** this costing is currently only available for auto costing. | | `disable_hierarchy_pruning` | Disable hierarchies to calculate the actual optimal route. The default is `false`. **Note:** This could be quite a performance drainer so there is a upper limit of distance. If the upper limit is exceeded, this option will always be `false`. | | `top_speed` | Top speed the vehicle can go. Also used to avoid roads with higher speeds than this value. `top_speed` must be between 10 and 252 KPH. The default value is 90 KPH for `truck` and 140 KPH for `auto` and `bus`. | | `fixed_speed` | Fixed speed the vehicle can go. Used to override the calculated speed. Can be useful if speed of vehicle is known. `fixed_speed` must be between 1 and 252 KPH. The default value is 0 KPH which disables fixed speed and falls back to the standard calculated speed based on the road attribution. | @@ -149,6 +157,8 @@ The following options are available for `truck` costing. | `axle_count` | The axle count of the truck. Default 5. | | `hazmat` | A value indicating if the truck is carrying hazardous materials. Default false. | | `hgv_no_access_penalty` | A penalty applied to roads with no HGV/truck access. If set to a value less than 43200 seconds, HGV will be allowed on these roads and the penalty applies. Default 43200, i.e. trucks are not allowed. | +| `low_class_penalty` | A penalty (in seconds) which is applied when going to residential or service roads. Default is 30 seconds. | + ##### Bicycle costing options The default bicycle costing is tuned toward road bicycles with a slight preference for using [cycleways](http://wiki.openstreetmap.org/wiki/Key:cycleway) or roads with bicycle lanes. Bicycle routes use regular roads where needed or where no direct bicycle lane options exist, but avoid roads without bicycle access. The costing model recognizes several factors unique to bicycle travel and offers several options for tuning bicycle routes. Several factors unique to travel by bicycle influence the resulting route. @@ -158,7 +168,7 @@ The default bicycle costing is tuned toward road bicycles with a slight preferen * Bicyclists vary in their tolerance for riding on roads. Most novice bicyclists, and even other bicyclists, prefer cycleways and dedicated cycling paths and would rather avoid all but the quietest neighborhood roads. Other cyclists may be experienced riding on roads and prefer to take roadways because they often provide the fastest way to get between two places. The bicycle costing model accounts for this with a `use_roads` factor to indicate a cyclist's tolerance for riding on roads. * Bicyclists vary in their fitness level and experience level, and many want to avoid hilly roads, and especially roads with very steep uphill or even downhill sections. Even if the fastest path is over a mountain, many cyclists prefer a flatter path that avoids the climb and descent up and over the mountain. -The following options described above for autos also apply to bicycle costing methods: `maneuver_penalty`, `gate_cost`, `gate_penalty`, `country_crossing_cost`, `country_costing_penalty`, and `service_penalty`. +The following options described above for autos also apply to bicycle costing methods: `maneuver_penalty`, `gate_cost`, `gate_penalty`, `destination_only_penalty` , `country_crossing_cost`, `country_costing_penalty`, and `service_penalty`. These additional options are available for bicycle costing methods. @@ -220,10 +230,12 @@ These options are available for pedestrian costing methods. | `use_lit` | This value is a range of values from 0 to 1, where 0 indicates indifference towards lit streets, and 1 indicates that unlit streets should be avoided. Note that even with values near 1, there is no guarantee the returned route will include lit segments. The default value is 0. | | `service_penalty` | A penalty applied for transition to generic service road. The default penalty is 0. | | `service_factor` | A factor that modifies (multiplies) the cost when generic service roads are encountered. The default `service_factor` is 1. | +| `destination_only_penalty` | A penalty applied when entering an road which is only allowed to enter if necessary to reach the [destination](https://wiki.openstreetmap.org/wiki/Tag:vehicle%3Ddestination) | | `max_hiking_difficulty` | This value indicates the maximum difficulty of hiking trails that is allowed. Values between 0 and 6 are allowed. The values correspond to *sac_scale* values within OpenStreetMap, see reference [here](https://wiki.openstreetmap.org/wiki/Key:sac_scale). The default value is 1 which means that well cleared trails that are mostly flat or slightly sloped are allowed. Higher difficulty trails can be allowed by specifying a higher value for max_hiking_difficulty. |`bss_rent_cost`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to give the time will be used to rent a bike from a bike share station. This value will be displayed in the final directions and used to calculate the whole duration. The default value is 120 seconds.| |`bss_rent_penalty`| This value is useful when `bikeshare` is chosen as travel mode. It is meant to describe the potential effort to rent a bike from a bike share station. This value won't be displayed and used only inside of the algorithm.| | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | +| `max_distance` | Sets the maximum total walking distance of a route. Default is 100 km (~62 miles). | | `transit_start_end_max_distance` | A pedestrian option that can be added to the request to extend the defaults (2145 meters or approximately 1.5 miles). This is the maximum walking distance at the beginning or end of a route.| | `transit_transfer_max_distance` | A pedestrian option that can be added to the request to extend the defaults (800 meters or 0.5 miles). This is the maximum walking distance between transfers.| | `type` | If set to `blind`, enables additional route instructions, especially useful for blind users: Announcing crossed streets, the stairs, bridges, tunnels, gates and bollards, which need to be passed on route; information about traffic signals on crosswalks; route numbers not announced for named routes. Default `foot` | @@ -276,6 +288,7 @@ Directions options should be specified at the top level of the JSON object. | `language` | The language of the narration instructions based on the [IETF BCP 47](https://tools.ietf.org/html/bcp47) language tag string. If no language is specified or the specified language is unsupported, United States-based English (en-US) is used. [Currently supported language list](#supported-language-tags) | | `directions_type` | An enum with 3 values.
  • `none` indicating no maneuvers or instructions should be returned.
  • `maneuvers` indicating that only maneuvers be returned.
  • `instructions` indicating that maneuvers with instructions should be returned (this is the default if not specified).
| | `format` | Four options are available:
  • `json` is default valhalla routing directions JSON format
  • `gpx` returns the route as a GPX (GPS exchange format) XML track
  • `osrm` creates a OSRM compatible route directions JSON
  • `pbf` formats the result using protocol buffers
| +| `shape_format` | If `"format" : "osrm"` is set: Specifies the optional format for the path shape of each connection. One of `polyline6` (default), `polyline5`, `geojson` or `no_shape`. | | `banner_instructions` | If the format is `osrm`, this boolean indicates if each step should have the additional `bannerInstructions` attribute, which can be displayed in some navigation system SDKs. | | `voice_instructions` | If the format is `osrm`, this boolean indicates if each step should have the additional `voiceInstructions` attribute, which can be heard in some navigation system SDKs. | | `alternates` | A number denoting how many alternate routes should be provided. There may be no alternates or less alternates than the user specifies. Alternates are not yet supported on multipoint routes (that is, routes with more than 2 locations). They are also not supported on time dependent routes. | diff --git a/valhalla/sif/dynamiccost.h b/valhalla/sif/dynamiccost.h index 02efa7e056..bf89d9a3de 100644 --- a/valhalla/sif/dynamiccost.h +++ b/valhalla/sif/dynamiccost.h @@ -1017,7 +1017,8 @@ class DynamicCost { // Penalties that all costing methods support float maneuver_penalty_; // Penalty (seconds) when inconsistent names float alley_penalty_; // Penalty (seconds) to use a alley - float destination_only_penalty_; // Penalty (seconds) using private road, driveway, or parking aisle + float destination_only_penalty_; // Penalty (seconds) using private road, driveway, parking aisle or + // destination only road float living_street_penalty_; // Penalty (seconds) to use a living street float track_penalty_; // Penalty (seconds) to use tracks float service_penalty_; // Penalty (seconds) to use a generic service road From 169b88e0dbd1d65297f14e1f7f1fc53dcd608627 Mon Sep 17 00:00:00 2001 From: Nils Date: Tue, 16 Apr 2024 08:33:17 +0200 Subject: [PATCH 068/198] last missing docs (hopefully) (#4690) --- docs/docs/api/turn-by-turn/api-reference.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 656e62123e..b50f85cb91 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -95,7 +95,10 @@ Another special case is `disable_hierarchy_pruning` costing option. As the name Additionally to the main costing option, the `recostings` option can be used to calculate the travelling time of the found route based on different costing options. By e.g. adding ```json -"recostings":[{"costing":"auto","fixed_speed":20,"name":"auto_20","costing":"auto","fixed_speed":50,"name":"auto_50"}] +"recostings":[ + {"costing":"auto","fixed_speed":20,"name":"auto_20"}, + {"costing":"auto","fixed_speed":50,"name":"auto_50"} +] ``` to the route request, the values `time_auto_20` and `time_auto_50` will be added to summaries to show how much time the route would cost with these given costing options. Passing a recosting which make the route impossible to follow (e.g. the main rout is by car over a motorway and recosting with pedestrian costing) leads to a `none` result of this recosting. @@ -133,6 +136,7 @@ These options are available for `auto`, `bus`, and `truck` costing methods. | `ignore_non_vehicular_restrictions` | Similar to `ignore_restrictions`, but will respect restrictions that impact vehicle safety, such as weight and size restrictions. | | `ignore_access` | Will ignore mode-specific access tags. Especially useful for matching GPS traces to the road network regardless of restrictions. Default is `false`. | | `ignore_closures` | Will ignore traffic closures. Default is `false`. | +| `speed_types` | Will determine which speed sources are used, if available. A list of strings with the following possible values:
  • freeflow
  • constrained
  • predicted
  • current
Default is all sources (again, only if available). | ###### Other costing options The following options are available for `auto`, `bus`, `taxi`, and `truck` costing methods. @@ -239,6 +243,7 @@ These options are available for pedestrian costing methods. | `transit_start_end_max_distance` | A pedestrian option that can be added to the request to extend the defaults (2145 meters or approximately 1.5 miles). This is the maximum walking distance at the beginning or end of a route.| | `transit_transfer_max_distance` | A pedestrian option that can be added to the request to extend the defaults (800 meters or 0.5 miles). This is the maximum walking distance between transfers.| | `type` | If set to `blind`, enables additional route instructions, especially useful for blind users: Announcing crossed streets, the stairs, bridges, tunnels, gates and bollards, which need to be passed on route; information about traffic signals on crosswalks; route numbers not announced for named routes. Default `foot` | +| `mode_factor` | A factor which the cost of a pedestrian edge will be multiplied with on multimodal request, e.g. `bss` or `multimodal/transit`. Default is a factor of 1.5, i.e. avoiding walking. ##### Transit costing options From e73131c6e20a7c06538f52c239f62f3542876fcf Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Tue, 16 Apr 2024 10:43:41 +0200 Subject: [PATCH 069/198] Consider more forward & backward tags (#4686) --- CHANGELOG.md | 1 + lua/graph.lua | 18 ++- src/baldr/graphtile.cc | 2 +- src/mjolnir/graphbuilder.cc | 28 ++++- src/mjolnir/osmaccessrestriction.cc | 8 ++ src/mjolnir/osmway.cc | 18 +++ src/mjolnir/pbfgraphparser.cc | 104 +++++++++++++++ src/sif/autocost.cc | 12 +- src/sif/bicyclecost.cc | 4 +- src/sif/motorcyclecost.cc | 4 +- src/sif/motorscootercost.cc | 4 +- src/sif/pedestriancost.cc | 4 +- src/sif/truckcost.cc | 4 +- taginfo.json | 70 +++++++++++ ...st_truck_restrictions.cc => test_truck.cc} | 119 ++++++++++++++++++ test/lua.cc | 28 +++++ valhalla/mjolnir/osmaccessrestriction.h | 17 ++- valhalla/mjolnir/osmway.h | 30 +++++ 18 files changed, 450 insertions(+), 25 deletions(-) rename test/gurka/{test_truck_restrictions.cc => test_truck.cc} (58%) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3436325c3..03514148c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -112,6 +112,7 @@ * CHANGED: contract nodes if connecting edges have different names or speed or non-conditional access restrictions [#4613](https://github.com/valhalla/valhalla/pull/4613) * CHANGED: CostMatrix switched from Dijkstra to A* [#4650](https://github.com/valhalla/valhalla/pull/4650) * ADDED: some missing documentation about request parameters [#4687](https://github.com/valhalla/valhalla/pull/4687) + * ADDED: Consider more forward/backward tags for access restrictions and speeds [#4686](https://github.com/valhalla/valhalla/pull/4686) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/lua/graph.lua b/lua/graph.lua index 0067cf1c25..1378d4e1df 100644 --- a/lua/graph.lua +++ b/lua/graph.lua @@ -1827,15 +1827,31 @@ function filter_tags_generic(kv) kv["maxheight"] = normalize_measurement(kv["maxheight"]) or normalize_measurement(kv["maxheight:physical"]) kv["maxwidth"] = normalize_measurement(kv["maxwidth"]) or normalize_measurement(kv["maxwidth:physical"]) kv["maxlength"] = normalize_measurement(kv["maxlength"]) - kv["maxweight"] = normalize_weight(kv["maxweight"]) kv["maxaxleload"] = normalize_weight(kv["maxaxleload"]) kv["maxaxles"] = tonumber(kv["maxaxles"]) + --forward/backward only tags + kv["maxheight_forward"] = normalize_measurement(kv["maxheight:forward"]) + kv["maxheight_backward"] = normalize_measurement(kv["maxheight:backward"]) + kv["maxlength_forward"] = normalize_measurement(kv["maxlength:forward"]) + kv["maxlength_backward"] = normalize_measurement(kv["maxlength:backward"]) + kv["maxweight_forward"] = normalize_weight(kv["maxweight:forward"]) + kv["maxweight_backward"] = normalize_weight(kv["maxweight:backward"]) + kv["maxwidth_forward"] = normalize_measurement(kv["maxwidth:forward"]) + kv["maxwidth_backward"] = normalize_measurement(kv["maxwidth:backward"]) + --TODO: hazmat really should have subcategories kv["hazmat"] = hazmat[kv["hazmat"]] or hazmat[kv["hazmat:water"]] or hazmat[kv["hazmat:A"]] or hazmat[kv["hazmat:B"]] or hazmat[kv["hazmat:C"]] or hazmat[kv["hazmat:D"]] or hazmat[kv["hazmat:E"]] + kv["hazmat_forward"] = hazmat[kv["hazmat:forward"]] or hazmat[kv["hazmat:water:forward"]] or hazmat[kv["hazmat:A:forward"]] or hazmat[kv["hazmat:B:forward"]] or + hazmat[kv["hazmat:C:forward"]] or hazmat[kv["hazmat:D:forward"]] or hazmat[kv["hazmat:E:forward"]] + kv["hazmat_backward"] = hazmat[kv["hazmat:backward"]] or hazmat[kv["hazmat:water:backward"]] or hazmat[kv["hazmat:A:backward"]] or hazmat[kv["hazmat:B:backward"]] or + hazmat[kv["hazmat:C:backward"]] or hazmat[kv["hazmat:D:backward"]] or hazmat[kv["hazmat:E:backward"]] + kv["maxspeed:hgv"] = normalize_speed(kv["maxspeed:hgv"]) + kv["maxspeed:hgv:forward"] = normalize_speed(kv["maxspeed:hgv:forward"]) + kv["maxspeed:hgv:backward"] = normalize_speed(kv["maxspeed:hgv:backward"]) if (kv["hgv:national_network"] or kv["hgv:state_network"] or kv["hgv"] == "local" or kv["hgv"] == "designated") then kv["truck_route"] = "true" diff --git a/src/baldr/graphtile.cc b/src/baldr/graphtile.cc index b44098f941..771a5e216e 100644 --- a/src/baldr/graphtile.cc +++ b/src/baldr/graphtile.cc @@ -1196,7 +1196,7 @@ std::vector GraphTile::GetAccessRestrictions(const uint32_t i } // Access restriction are sorted by edge Id. - // Binary search to find a access restriction with matching edge Id. + // Binary search to find an access restriction with matching edge Id. int32_t low = 0; int32_t high = count - 1; int32_t mid; diff --git a/src/mjolnir/graphbuilder.cc b/src/mjolnir/graphbuilder.cc index 62ff3a7923..47e9fcf0c9 100644 --- a/src/mjolnir/graphbuilder.cc +++ b/src/mjolnir/graphbuilder.cc @@ -374,6 +374,7 @@ uint32_t CreateSimpleTurnRestriction(const uint64_t wayid, uint32_t AddAccessRestrictions(const uint32_t edgeid, const uint64_t wayid, const OSMData& osmdata, + const bool forward, GraphTileBuilder& graphtile) { auto res = osmdata.access_restrictions.equal_range(wayid); if (res.first == osmdata.access_restrictions.end()) { @@ -382,10 +383,16 @@ uint32_t AddAccessRestrictions(const uint32_t edgeid, uint32_t modes = 0; for (auto r = res.first; r != res.second; ++r) { - AccessRestriction access_restriction(edgeid, r->second.type(), r->second.modes(), - r->second.value()); - graphtile.AddAccessRestriction(access_restriction); - modes |= r->second.modes(); + auto direction = r->second.direction(); + + if ((direction == AccessRestrictionDirection::kBoth) || + (forward && direction == AccessRestrictionDirection::kForward) || + (!forward && direction == AccessRestrictionDirection::kBackward)) { + AccessRestriction access_restriction(edgeid, r->second.type(), r->second.modes(), + r->second.value()); + graphtile.AddAccessRestriction(access_restriction); + modes |= r->second.modes(); + } } return modes; } @@ -608,7 +615,15 @@ void BuildTileSet(const std::string& ways_file, speed_limit = kMaxAssumedSpeed; } - uint32_t truck_speed = w.truck_speed(); + const uint8_t directed_truck_speed = + forward ? w.truck_speed_forward() : w.truck_speed_backward(); + + // if there's a general truck speed AND a directed one, apply the stricter one + // otherwise just pick whichever is set + uint32_t truck_speed = w.truck_speed() && directed_truck_speed + ? std::min(w.truck_speed(), directed_truck_speed) + : std::max(w.truck_speed(), directed_truck_speed); + if (truck_speed > kMaxAssumedSpeed) { LOG_WARN("Truck Speed = " + std::to_string(truck_speed) + " wayId= " + std::to_string(w.way_id())); @@ -1008,7 +1023,8 @@ void BuildTileSet(const std::string& ways_file, // Add restrictions..For now only storing access restrictions for trucks // TODO - support more than one mode if (directededge.forwardaccess()) { - uint32_t ar_modes = AddAccessRestrictions(idx, w.way_id(), osmdata, graphtile); + uint32_t ar_modes = + AddAccessRestrictions(idx, w.way_id(), osmdata, directededge.forward(), graphtile); if (ar_modes) { directededge.set_access_restriction(ar_modes); } diff --git a/src/mjolnir/osmaccessrestriction.cc b/src/mjolnir/osmaccessrestriction.cc index 61cf4c1e4f..2a772cac4a 100644 --- a/src/mjolnir/osmaccessrestriction.cc +++ b/src/mjolnir/osmaccessrestriction.cc @@ -44,5 +44,13 @@ uint16_t OSMAccessRestriction::modes() const { return attributes_.modes_; } +AccessRestrictionDirection OSMAccessRestriction::direction() const { + return static_cast(direction_); +} + +void OSMAccessRestriction::set_direction(AccessRestrictionDirection direction) { + direction_ = direction; +}; + } // namespace mjolnir } // namespace valhalla diff --git a/src/mjolnir/osmway.cc b/src/mjolnir/osmway.cc index 4d54cbdc52..4260fe2b28 100644 --- a/src/mjolnir/osmway.cc +++ b/src/mjolnir/osmway.cc @@ -81,6 +81,24 @@ void OSMWay::set_truck_speed(const float speed) { } } +void OSMWay::set_truck_speed_forward(const float truck_speed_forward) { + if (truck_speed_forward > kMaxOSMSpeed) { + LOG_WARN("Exceeded max forward truck speed for way id: " + std::to_string(osmwayid_)); + truck_speed_forward_ = kMaxOSMSpeed; + } else { + truck_speed_forward_ = static_cast(truck_speed_forward + 0.5f); + } +} + +void OSMWay::set_truck_speed_backward(const float truck_speed_backward) { + if (truck_speed_backward > kMaxOSMSpeed) { + LOG_WARN("Exceeded max backward truck speed for way id: " + std::to_string(osmwayid_)); + truck_speed_backward_ = kMaxOSMSpeed; + } else { + truck_speed_backward_ = static_cast(truck_speed_backward + 0.5f); + } +} + // Sets the number of lanes void OSMWay::set_lanes(const uint32_t lanes) { lanes_ = (lanes > kMaxLaneCount) ? kMaxLaneCount : lanes; diff --git a/src/mjolnir/pbfgraphparser.cc b/src/mjolnir/pbfgraphparser.cc index 3312c05eff..b9106f1e3b 100644 --- a/src/mjolnir/pbfgraphparser.cc +++ b/src/mjolnir/pbfgraphparser.cc @@ -934,6 +934,20 @@ struct graph_callback : public OSMPBF::Callback { LOG_INFO("out_of_range thrown for way id: " + std::to_string(osmid_)); } }; + tag_handlers_["maxspeed:hgv:forward"] = [this]() { + try { + way_.set_truck_speed_forward(std::stof(tag_.second)); + } catch (const std::out_of_range& oor) { + LOG_INFO("out_of_range thrown for way id: " + std::to_string(osmid_)); + } + }; + tag_handlers_["maxspeed:hgv:backward"] = [this]() { + try { + way_.set_truck_speed_backward(std::stof(tag_.second)); + } catch (const std::out_of_range& oor) { + LOG_INFO("out_of_range thrown for way id: " + std::to_string(osmid_)); + } + }; tag_handlers_["truck_route"] = [this]() { way_.set_truck_route(tag_.second == "true" ? true : false); }; @@ -945,6 +959,24 @@ struct graph_callback : public OSMPBF::Callback { osmdata_.access_restrictions.insert( AccessRestrictionsMultiMap::value_type(osmid_, restriction)); }; + tag_handlers_["hazmat_forward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kHazmat); + restriction.set_value(tag_.second == "true" ? true : false); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kForward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; + tag_handlers_["hazmat_backward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kHazmat); + restriction.set_value(tag_.second == "true" ? true : false); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kBackward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; tag_handlers_["maxheight"] = [this]() { OSMAccessRestriction restriction; restriction.set_type(AccessType::kMaxHeight); @@ -953,6 +985,24 @@ struct graph_callback : public OSMPBF::Callback { osmdata_.access_restrictions.insert( AccessRestrictionsMultiMap::value_type(osmid_, restriction)); }; + tag_handlers_["maxheight_forward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxHeight); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess | kAutoAccess | kHOVAccess | kTaxiAccess | kBusAccess); + restriction.set_direction(AccessRestrictionDirection::kForward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; + tag_handlers_["maxheight_backward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxHeight); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess | kAutoAccess | kHOVAccess | kTaxiAccess | kBusAccess); + restriction.set_direction(AccessRestrictionDirection::kBackward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; tag_handlers_["maxwidth"] = [this]() { OSMAccessRestriction restriction; restriction.set_type(AccessType::kMaxWidth); @@ -961,6 +1011,24 @@ struct graph_callback : public OSMPBF::Callback { osmdata_.access_restrictions.insert( AccessRestrictionsMultiMap::value_type(osmid_, restriction)); }; + tag_handlers_["maxwidth_forward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxWidth); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess | kAutoAccess | kHOVAccess | kTaxiAccess | kBusAccess); + restriction.set_direction(AccessRestrictionDirection::kForward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; + tag_handlers_["maxwidth_backward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxWidth); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess | kAutoAccess | kHOVAccess | kTaxiAccess | kBusAccess); + restriction.set_direction(AccessRestrictionDirection::kBackward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; tag_handlers_["maxlength"] = [this]() { OSMAccessRestriction restriction; restriction.set_type(AccessType::kMaxLength); @@ -969,6 +1037,24 @@ struct graph_callback : public OSMPBF::Callback { osmdata_.access_restrictions.insert( AccessRestrictionsMultiMap::value_type(osmid_, restriction)); }; + tag_handlers_["maxlength_forward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxLength); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kForward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; + tag_handlers_["maxlength_backward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxLength); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kBackward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; tag_handlers_["maxweight"] = [this]() { OSMAccessRestriction restriction; restriction.set_type(AccessType::kMaxWeight); @@ -977,6 +1063,24 @@ struct graph_callback : public OSMPBF::Callback { osmdata_.access_restrictions.insert( AccessRestrictionsMultiMap::value_type(osmid_, restriction)); }; + tag_handlers_["maxweight_forward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxWeight); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kForward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; + tag_handlers_["maxweight_backward"] = [this]() { + OSMAccessRestriction restriction; + restriction.set_type(AccessType::kMaxWeight); + restriction.set_value(std::stof(tag_.second) * 100); + restriction.set_modes(kTruckAccess); + restriction.set_direction(AccessRestrictionDirection::kBackward); + osmdata_.access_restrictions.insert( + AccessRestrictionsMultiMap::value_type(osmid_, restriction)); + }; tag_handlers_["maxaxleload"] = [this]() { OSMAccessRestriction restriction; restriction.set_type(AccessType::kMaxAxleLoad); diff --git a/src/sif/autocost.cc b/src/sif/autocost.cc index 93a3dbd648..1e8fddc3f6 100644 --- a/src/sif/autocost.cc +++ b/src/sif/autocost.cc @@ -454,8 +454,8 @@ bool AutoCost::AllowedReverse(const baldr::DirectedEdge* edge, return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } bool AutoCost::ModeSpecificAllowed(const baldr::AccessRestriction& restriction) const { @@ -816,8 +816,8 @@ bool BusCost::AllowedReverse(const baldr::DirectedEdge* edge, return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } void ParseBusCostOptions(const rapidjson::Document& doc, @@ -994,8 +994,8 @@ bool TaxiCost::AllowedReverse(const baldr::DirectedEdge* edge, (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved())) { return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } void ParseTaxiCostOptions(const rapidjson::Document& doc, diff --git a/src/sif/bicyclecost.cc b/src/sif/bicyclecost.cc index 46a07b54d6..720c159547 100644 --- a/src/sif/bicyclecost.cc +++ b/src/sif/bicyclecost.cc @@ -591,8 +591,8 @@ bool BicycleCost::AllowedReverse(const baldr::DirectedEdge* edge, if (edge->surface() > worst_allowed_surface_) { return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } // Returns the cost to traverse the edge and an estimate of the actual time diff --git a/src/sif/motorcyclecost.cc b/src/sif/motorcyclecost.cc index 1f716bd499..5a24b8c495 100644 --- a/src/sif/motorcyclecost.cc +++ b/src/sif/motorcyclecost.cc @@ -386,8 +386,8 @@ bool MotorcycleCost::AllowedReverse(const baldr::DirectedEdge* edge, return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } Cost MotorcycleCost::EdgeCost(const baldr::DirectedEdge* edge, diff --git a/src/sif/motorscootercost.cc b/src/sif/motorscootercost.cc index 7fe8270f05..a82a2e4af1 100644 --- a/src/sif/motorscootercost.cc +++ b/src/sif/motorscootercost.cc @@ -407,8 +407,8 @@ bool MotorScooterCost::AllowedReverse(const baldr::DirectedEdge* edge, return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } Cost MotorScooterCost::EdgeCost(const baldr::DirectedEdge* edge, diff --git a/src/sif/pedestriancost.cc b/src/sif/pedestriancost.cc index 3d71a925fa..1833b68851 100644 --- a/src/sif/pedestriancost.cc +++ b/src/sif/pedestriancost.cc @@ -687,8 +687,8 @@ bool PedestrianCost::AllowedReverse(const baldr::DirectedEdge* edge, return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } // Returns the cost to traverse the edge and an estimate of the actual time diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index de5c415939..0f3017e4de 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -481,8 +481,8 @@ bool TruckCost::AllowedReverse(const baldr::DirectedEdge* edge, return false; } - return DynamicCost::EvaluateRestrictions(access_mask_, edge, false, tile, opp_edgeid, current_time, - tz_index, restriction_idx); + return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, + current_time, tz_index, restriction_idx); } // Get the cost to traverse the edge in seconds diff --git a/taginfo.json b/taginfo.json index a5fd649579..8d91ce36bc 100644 --- a/taginfo.json +++ b/taginfo.json @@ -3795,6 +3795,20 @@ ], "description": "Maxheight for hgv." }, + { + "key": "maxheight:forward", + "object_types": [ + "way" + ], + "description": "Maxheight for hgv in forward direction." + }, + { + "key": "maxheight:backward", + "object_types": [ + "way" + ], + "description": "Maxheight for hgv in backward direction." + }, { "key": "maxheight:physical", "object_types": [ @@ -3809,6 +3823,20 @@ ], "description": "Maxlength for hgv." }, + { + "key": "maxlength:forward", + "object_types": [ + "way" + ], + "description": "Maxlength for hgv in forward direction." + }, + { + "key": "maxlength:backward", + "object_types": [ + "way" + ], + "description": "Maxlength for hgv in backward direction." + }, { "key": "maxspeed", "description": "Speed Limit (assume kph except where mph given)", @@ -3844,6 +3872,20 @@ ], "description": "Maxspeed for hgv." }, + { + "key": "maxspeed:hgv:forward", + "object_types": [ + "way" + ], + "description": "Maxspeed for hgv in forward direction." + }, + { + "key": "maxspeed:hgv:backward", + "object_types": [ + "way" + ], + "description": "Maxspeed for hgv in backward direction." + }, { "key": "maxspeed:practical", "description": "Considered the average speed on a road (assume kph except where mph given)", @@ -3858,6 +3900,20 @@ ], "description": "Maxweight for hgv." }, + { + "key": "maxweight:forward", + "object_types": [ + "way" + ], + "description": "Maxweight for hgv in forward direction." + }, + { + "key": "maxweight:backward", + "object_types": [ + "way" + ], + "description": "Maxweight for hgv in backward direction." + }, { "key": "maxwidth", "object_types": [ @@ -3865,6 +3921,20 @@ ], "description": "Maxwidth for hgv." }, + { + "key": "maxwidth:forward", + "object_types": [ + "way" + ], + "description": "Maxwidth for hgv in forward direction." + }, + { + "key": "maxwidth:backward", + "object_types": [ + "way" + ], + "description": "Maxwidth for hgv in backward direction." + }, { "key": "maxwidth:physical", "object_types": [ diff --git a/test/gurka/test_truck_restrictions.cc b/test/gurka/test_truck.cc similarity index 58% rename from test/gurka/test_truck_restrictions.cc rename to test/gurka/test_truck.cc index 9c11d099f4..0ded54181f 100644 --- a/test/gurka/test_truck_restrictions.cc +++ b/test/gurka/test_truck.cc @@ -166,3 +166,122 @@ TEST(TruckSpeed, MaxTruckSpeed) { // expect lower traffic speeds (< kMaxAssumedTruckSpeed ) to lead to a lower duration ASSERT_LT(traffic_time, traffic_low_speed_time); } + +// tag name, tag value, costing opt name, costing opt value, forward +using asymmetric_restriction_params_t = + std::tuple; + +class TruckAsymmetricAccessRestrictions + : public ::testing::TestWithParam { + +protected: + static std::string ascii_map; + + static void SetUpTestSuite() { + + ascii_map = R"( + A---B---C---D + )"; + } +}; +std::string TruckAsymmetricAccessRestrictions::ascii_map = {}; + +// test forward/backward only access restrictions +TEST_P(TruckAsymmetricAccessRestrictions, ForwardBackward) { + std::string tag_name; + std::string tag_value; + std::string co_opt; + std::string co_value; + bool forward; + std::tie(tag_name, tag_value, co_opt, co_value, forward) = GetParam(); + + std::string tag_name_full = tag_name + (forward ? ":forward" : ":backward"); + + // middle edge has a forward/backward only access restriction + const gurka::ways ways = { + {"AB", {{"highway", "residential"}}}, + {"BC", {{"highway", "residential"}, {tag_name_full, tag_value}}}, + {"CD", {{"highway", "residential"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 10); + gurka::map map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/truck_directed_access_restrictions"); + + // we test a route in the direction of the access restriction, should fail... + std::vector locations_fail = {"A", "D"}; + // ...but should work in the other direction... + std::vector locations_success = {"D", "A"}; + // ...and this should be the succeeding route's path + std::vector expected_path_success = {"CD", "BC", "AB"}; + + // reverse case if backward + if (!forward) { + locations_fail = {"D", "A"}; + locations_success = {"A", "D"}; + expected_path_success = {"AB", "BC", "CD"}; + } + + const std::unordered_map costing_opts = {{co_opt, co_value}}; + try { + std::vector> options; + gurka::do_action(Options::route, map, locations_fail, "truck", costing_opts); + FAIL() << "Expected no path to be found"; + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 442); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; + + // other direction should work + const auto success_route = + gurka::do_action(Options::route, map, locations_success, "truck", costing_opts); + gurka::assert::raw::expect_path(success_route, expected_path_success); +} + +std::vector buildParams() { + std::vector params; + + params.push_back(std::make_tuple("maxheight", "4", "/costing_options/truck/height", "7", true)); + params.push_back(std::make_tuple("maxheight", "4", "/costing_options/truck/height", "7", false)); + params.push_back(std::make_tuple("maxlength", "4", "/costing_options/truck/length", "5", true)); + params.push_back(std::make_tuple("maxlength", "4", "/costing_options/truck/length", "5", false)); + params.push_back(std::make_tuple("maxwidth", "2", "/costing_options/truck/length", "3", true)); + params.push_back(std::make_tuple("maxwidth", "2", "/costing_options/truck/length", "3", false)); + params.push_back(std::make_tuple("hazmat", "no", "/costing_options/truck/hazmat", "1", true)); + + return params; +}; + +INSTANTIATE_TEST_SUITE_P(TruckAsymmetricAccessRestrictionsTest, + TruckAsymmetricAccessRestrictions, + ::testing::ValuesIn(buildParams())); + +TEST(Standalone, TruckSpeeds) { + constexpr double gridsize = 500; + + const std::string ascii_map = R"( + A---B---C---D---E + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "residential"}, {"maxspeed:hgv:forward", "15"}}}, + {"BC", {{"highway", "residential"}, {"maxspeed:hgv:backward", "15"}}}, + // truck_speed > truck_speed_forward + {"CD", {{"highway", "residential"}, {"maxspeed:hgv", "20"}, {"maxspeed:hgv:forward", "15"}}}, + // truck_speed < truck_speed_forward + {"DE", {{"highway", "residential"}, {"maxspeed:hgv", "15"}, {"maxspeed:hgv:forward", "20"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/truck_speed"); + + baldr::GraphReader reader(map.config.get_child("mjolnir")); + const DirectedEdge *de_fwd, *de_rev, *de_both_1, *de_both_2; + std::tie(std::ignore, de_fwd) = gurka::findEdgeByNodes(reader, layout, "A", "B"); + std::tie(std::ignore, de_rev) = gurka::findEdgeByNodes(reader, layout, "C", "B"); + std::tie(std::ignore, de_both_1) = gurka::findEdgeByNodes(reader, layout, "C", "D"); + std::tie(std::ignore, de_both_2) = gurka::findEdgeByNodes(reader, layout, "D", "E"); + EXPECT_EQ(de_fwd->truck_speed(), 15); + EXPECT_EQ(de_rev->truck_speed(), 15); + EXPECT_EQ(de_both_1->truck_speed(), 15); + EXPECT_EQ(de_both_2->truck_speed(), 15); +} diff --git a/test/lua.cc b/test/lua.cc index 669e76cee3..5110a54185 100644 --- a/test/lua.cc +++ b/test/lua.cc @@ -103,6 +103,34 @@ TEST(Lua, NumberDoublePeriod) { // ... but that the results aren't completely empty ASSERT_TRUE(results.size() > 0); } + +TEST(Lua, TestForwardBackward) { + // Way 25494427 version 14 has a "maxheight" tag value of 3..35 with the two + // dots. This probably shouldn't be parsed? + mjolnir::LuaTagTransform lua(std::string(lua_graph_lua, lua_graph_lua + lua_graph_lua_len)); + + mjolnir::Tags tags; + tags.insert({"highway", "tertiary"}); + tags.insert({"maxheight:forward", "1"}); + tags.insert({"maxheight:backward", "1"}); + tags.insert({"maxlength:forward", "1"}); + tags.insert({"maxlength:backward", "1"}); + tags.insert({"maxwidth:forward", "1"}); + tags.insert({"maxwidth:backward", "1"}); + tags.insert({"maxweight:forward", "1"}); + tags.insert({"maxweight:backward", "1"}); + auto results = lua.Transform(mjolnir::OSMType::kWay, 1, tags); + + // check that the maxheight is present... + ASSERT_TRUE(results.count("maxheight_forward") == 1); + ASSERT_TRUE(results.count("maxheight_backward") == 1); + ASSERT_TRUE(results.count("maxlength_forward") == 1); + ASSERT_TRUE(results.count("maxlength_backward") == 1); + ASSERT_TRUE(results.count("maxwidth_forward") == 1); + ASSERT_TRUE(results.count("maxwidth_backward") == 1); + ASSERT_TRUE(results.count("maxweight_forward") == 1); + ASSERT_TRUE(results.count("maxweight_backward") == 1); +} } // namespace // TODO: sweet jesus add more tests of this class! diff --git a/valhalla/mjolnir/osmaccessrestriction.h b/valhalla/mjolnir/osmaccessrestriction.h index da9f31ed81..9f7826c7b8 100644 --- a/valhalla/mjolnir/osmaccessrestriction.h +++ b/valhalla/mjolnir/osmaccessrestriction.h @@ -8,6 +8,9 @@ namespace valhalla { namespace mjolnir { +// Used for access restrictions. Conveys the direction in which the access restriction applies +enum class AccessRestrictionDirection : uint8_t { kBoth = 0, kForward = 1, kBackward = 2 }; + /** * OSM Access restriction information. Access Restrictions are stored in a * multimap keyed by the Id of the "from" way of the restriction. @@ -55,6 +58,16 @@ class OSMAccessRestriction { */ void set_modes(uint16_t modes); + /** + * Get the direction the access restriction applies to. + */ + AccessRestrictionDirection direction() const; + + /** + * Set the direction the access restriction applies to. + */ + void set_direction(AccessRestrictionDirection direction); + protected: uint64_t value_; @@ -63,7 +76,9 @@ class OSMAccessRestriction { uint16_t modes_ : 12; }; Attributes attributes_; - uint16_t spare_[3]; + AccessRestrictionDirection direction_; + uint16_t spare_[2]; + uint8_t spare2_; }; } // namespace mjolnir diff --git a/valhalla/mjolnir/osmway.h b/valhalla/mjolnir/osmway.h index ce85c3cd85..a34d9a93b7 100644 --- a/valhalla/mjolnir/osmway.h +++ b/valhalla/mjolnir/osmway.h @@ -123,6 +123,34 @@ struct OSMWay { return truck_speed_; } + /** + * Sets the forward truck speed + * @param truck_speed_forward Forward truck speed in KPH. + */ + void set_truck_speed_forward(const float truck_speed_forward); + + /** + * Gets the forward truck speed in KPH. + * @return Returns forward truck speed. + */ + uint8_t truck_speed_forward() const { + return truck_speed_forward_; + } + + /** + * Sets the backward truck speed + * @param truck_speed_backward Backward truck speed in KPH. + */ + void set_truck_speed_backward(const float truck_speed_backward); + + /** + * Gets the backward truck speed in KPH. + * @return Returns backward truck speed. + */ + uint8_t truck_speed_backward() const { + return truck_speed_backward_; + } + /** * Sets the index for the ref * @param idx Index for the reference. @@ -2700,6 +2728,8 @@ struct OSMWay { // Truck speed in kilometers per hour uint8_t truck_speed_; + uint8_t truck_speed_forward_; + uint8_t truck_speed_backward_; // layer index(Z-level) of the way relatively to other levels int8_t layer_; From 58241a287d2096cd0520ec7ac30ff4b13872000c Mon Sep 17 00:00:00 2001 From: Nils Date: Wed, 17 Apr 2024 12:37:57 +0200 Subject: [PATCH 070/198] change costmatrix max_distance threshold to a distance threshold instead of duration (#4674) --- CHANGELOG.md | 1 + src/thor/costmatrix.cc | 29 ++++++----------------------- test/matrix.cc | 6 +++--- valhalla/thor/costmatrix.h | 11 ++--------- 4 files changed, 12 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03514148c3..915ae027b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -113,6 +113,7 @@ * CHANGED: CostMatrix switched from Dijkstra to A* [#4650](https://github.com/valhalla/valhalla/pull/4650) * ADDED: some missing documentation about request parameters [#4687](https://github.com/valhalla/valhalla/pull/4687) * ADDED: Consider more forward/backward tags for access restrictions and speeds [#4686](https://github.com/valhalla/valhalla/pull/4686) + * CHANGED: change costmatrix max_distance threshold to a distance threshold instead of duration [#4672](https://github.com/valhalla/valhalla/pull/4672) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 576967404e..3218240e13 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -57,32 +57,12 @@ CostMatrix::CostMatrix(const boost::property_tree::ptree& config) check_reverse_connections_(config.get("costmatrix_check_reverse_connection", false)), access_mode_(kAutoAccess), mode_(travel_mode_t::kDrive), locs_count_{0, 0}, locs_remaining_{0, 0}, - current_cost_threshold_(0), targets_{new ReachedMap}, sources_{new ReachedMap} { + current_pathdist_threshold_(0), targets_{new ReachedMap}, sources_{new ReachedMap} { } CostMatrix::~CostMatrix() { } -float CostMatrix::GetCostThreshold(const float max_matrix_distance) { - float cost_threshold; - switch (mode_) { - case travel_mode_t::kBicycle: - cost_threshold = max_matrix_distance / kCostThresholdBicycleDivisor; - break; - case travel_mode_t::kPedestrian: - case travel_mode_t::kPublicTransit: - cost_threshold = max_matrix_distance / kCostThresholdPedestrianDivisor; - break; - case travel_mode_t::kDrive: - default: - cost_threshold = max_matrix_distance / kCostThresholdAutoDivisor; - } - - // Increase the cost threshold to make sure requests near the max distance succeed. - // Some costing models and locations require higher thresholds to succeed. - return cost_threshold * 2.0f; -} - // Clear the temporary information generated during time + distance matrix // construction. void CostMatrix::Clear() { @@ -147,7 +127,7 @@ bool CostMatrix::SourceToTarget(Api& request, auto& source_location_list = *request.mutable_options()->mutable_sources(); auto& target_location_list = *request.mutable_options()->mutable_targets(); - current_cost_threshold_ = GetCostThreshold(max_matrix_distance); + current_pathdist_threshold_ = max_matrix_distance / 2; auto time_infos = SetOriginTimes(source_location_list, graphreader); @@ -364,6 +344,9 @@ void CostMatrix::Initialize( auto heuristic = astar_heuristics_[!is_fwd][i].Get({other_ll.lng(), other_ll.lat()}); min_heuristic = std::min(min_heuristic, heuristic); } + // TODO(nils): previously we'd estimate the bucket range by the max matrix distance, + // which would lead to tons of RAM if a high value was chosen in the config; ideally + // this would be chosen based on the request (e.g. some factor to the A* distance) adjacency_[is_fwd][i].reuse(min_heuristic, range, bucketsize, &edgelabel_[is_fwd][i]); } } @@ -619,7 +602,7 @@ bool CostMatrix::Expand(const uint32_t index, // Get edge label and check cost threshold auto pred = edgelabels[pred_idx]; - if (pred.cost().secs > current_cost_threshold_) { + if (pred.path_distance() > current_pathdist_threshold_) { locs_status_[FORWARD][index].threshold = 0; return false; } diff --git a/test/matrix.cc b/test/matrix.cc index 93e5646fa5..109f9c392f 100644 --- a/test/matrix.cc +++ b/test/matrix.cc @@ -231,7 +231,7 @@ TEST(Matrix, test_matrix) { CostMatrix cost_matrix_abort_source; cost_matrix_abort_source.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, - 90000.0); + 7000.0); matrix = request.matrix(); uint32_t found = 0; @@ -245,7 +245,7 @@ TEST(Matrix, test_matrix) { CostMatrix cost_matrix_abort_target; cost_matrix_abort_target.SourceToTarget(request, reader, mode_costing, sif::TravelMode::kDrive, - 50000.0); + 5000.0); matrix = request.matrix(); found = 0; @@ -254,7 +254,7 @@ TEST(Matrix, test_matrix) { ++found; } } - EXPECT_EQ(found, 11) << " not the number of results as expected"; + EXPECT_EQ(found, 13) << " not the number of results as expected"; request.clear_matrix(); TimeDistanceMatrix timedist_matrix; diff --git a/valhalla/thor/costmatrix.h b/valhalla/thor/costmatrix.h index 49dfcad068..1c9e89b0ac 100644 --- a/valhalla/thor/costmatrix.h +++ b/valhalla/thor/costmatrix.h @@ -140,8 +140,8 @@ class CostMatrix : public MatrixAlgorithm { std::array locs_count_; std::array locs_remaining_; - // The cost threshold being used for the currently executing query - float current_cost_threshold_; + // The path distance threshold being used for the currently executing query + float current_pathdist_threshold_; // Status std::array, 2> locs_status_; @@ -163,13 +163,6 @@ class CostMatrix : public MatrixAlgorithm { // when doing timezone differencing a timezone cache speeds up the computation baldr::DateTime::tz_sys_info_cache_t tz_cache_; - /** - * Get the cost threshold based on the current mode and the max arc-length distance - * for that mode. - * @param max_matrix_distance Maximum arc-length distance for current mode. - */ - float GetCostThreshold(const float max_matrix_distance); - /** * Form the initial time distance matrix given the sources * and destinations. From c835e2c94336ec56525e048e75595665fde6ca44 Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Fri, 19 Apr 2024 09:39:25 -0400 Subject: [PATCH 071/198] typo in docs --- docs/docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/building.md b/docs/docs/building.md index 7296b815bc..d3c65d66c4 100644 --- a/docs/docs/building.md +++ b/docs/docs/building.md @@ -116,7 +116,7 @@ git -C C:\path\to\vcpkg checkout f330a32 # only build release versions for vcpkg packages echo.set(VCPKG_BUILD_TYPE release)>> path\to\vcpkg\triplets\x64-windows.cmake cd C:\path\to\valhalla -C:\path\to\vcpkg.exe install --triple x64-windows +C:\path\to\vcpkg.exe install --triplet x64-windows ``` 2. Let CMake configure the build with the required modules enabled. The final command for `x64` could look like ``` From c8f47736068e948c7376c981b22495f89f1ccabf Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:02:40 +0200 Subject: [PATCH 072/198] PBF support for `/expansion` (#4614) --- CHANGELOG.md | 1 + proto/CMakeLists.txt | 3 +- proto/api.proto | 9 ++- proto/expansion.proto | 26 ++++++++ proto/options.proto | 2 +- src/proto_conversions.cc | 10 +++ src/thor/bidirectional_astar.cc | 28 +++++--- src/thor/costmatrix.cc | 19 +++--- src/thor/dijkstras.cc | 5 +- src/thor/expansion_action.cc | 115 +++++++++++++++----------------- src/tyr/CMakeLists.txt | 3 +- src/tyr/expansion_serializer.cc | 86 ++++++++++++++++++++++++ src/tyr/matrix_serializer.cc | 5 ++ src/tyr/serializers.cc | 5 ++ src/worker.cc | 3 +- test/gurka/test_expansion.cc | 105 +++++++++++++++++++++++------ valhalla/proto_conversions.h | 1 + valhalla/thor/dijkstras.h | 2 +- valhalla/thor/matrixalgorithm.h | 2 +- valhalla/thor/pathalgorithm.h | 2 +- valhalla/tyr/serializers.h | 6 ++ 21 files changed, 323 insertions(+), 115 deletions(-) create mode 100644 proto/expansion.proto create mode 100644 src/tyr/expansion_serializer.cc diff --git a/CHANGELOG.md b/CHANGELOG.md index 915ae027b8..ddc3a26b56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -114,6 +114,7 @@ * ADDED: some missing documentation about request parameters [#4687](https://github.com/valhalla/valhalla/pull/4687) * ADDED: Consider more forward/backward tags for access restrictions and speeds [#4686](https://github.com/valhalla/valhalla/pull/4686) * CHANGED: change costmatrix max_distance threshold to a distance threshold instead of duration [#4672](https://github.com/valhalla/valhalla/pull/4672) + * ADDED: PBF support for expansion [#4614](https://github.com/valhalla/valhalla/pull/4614/) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/proto/CMakeLists.txt b/proto/CMakeLists.txt index f58a7c76a9..f7cfb1c898 100644 --- a/proto/CMakeLists.txt +++ b/proto/CMakeLists.txt @@ -12,7 +12,8 @@ set(protobuf_descriptors incidents.proto status.proto matrix.proto - isochrone.proto) + isochrone.proto + expansion.proto) if(ENABLE_DATA_TOOLS) # Only mjolnir needs the OSM PBF descriptors diff --git a/proto/api.proto b/proto/api.proto index 448713c309..19b1b72e94 100644 --- a/proto/api.proto +++ b/proto/api.proto @@ -9,6 +9,7 @@ import public "info.proto"; // statistics about the request, filled out by import public "status.proto"; // info for status endpoint import public "matrix.proto"; // the matrix results import public "isochrone.proto"; // the isochrone results +import public "expansion.proto"; // the expansion results message Api { // this is the request to the api @@ -20,11 +21,9 @@ message Api { Status status = 4; // status Matrix matrix = 5; // sources_to_targets Isochrone isochrone = 6; // isochrone - //TODO: isochrone - //TODO: matrix - //TODO: locate - //TODO: height - //TODO: expansion + Expansion expansion = 7; // expansion + //TODO: locate; + //TODO: height; // here we store a bit of info about what happened during request processing (stats/errors/warnings) Info info = 20; diff --git a/proto/expansion.proto b/proto/expansion.proto new file mode 100644 index 0000000000..e193e70bf0 --- /dev/null +++ b/proto/expansion.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +option optimize_for = LITE_RUNTIME; +package valhalla; + +message Expansion { + + message Geometry { + repeated sint32 coords = 1 [packed=true]; + } + + enum EdgeStatus { + connected = 0; + settled = 1; + reached = 2; + } + + repeated uint32 costs = 1 [packed=true]; + repeated uint32 durations = 2 [packed=true]; + repeated uint32 distances = 3 [packed=true]; + repeated EdgeStatus edge_status = 4; + repeated uint32 edge_id = 5 [packed=true]; + repeated uint32 pred_edge_id = 6 [packed=true]; + + repeated Geometry geometries = 7; +} \ No newline at end of file diff --git a/proto/options.proto b/proto/options.proto index afb3866ccc..e969ca51c1 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -53,10 +53,10 @@ message PbfFieldSelector { bool status = 4; // /status bool matrix = 5; // sources_to_targets bool isochrone = 6; + bool expansion = 9; // TODO: enable these once we have objects for them // bool locate = 7; // bool height = 8; - // bool expansion = 9; } message AvoidEdge { diff --git a/src/proto_conversions.cc b/src/proto_conversions.cc index f4172b5b0c..93ebf573af 100644 --- a/src/proto_conversions.cc +++ b/src/proto_conversions.cc @@ -436,4 +436,14 @@ travel_mode_type(const valhalla::DirectionsLeg_Maneuver& maneuver) { throw std::logic_error("Unknown travel mode"); } } + +const std::string& Expansion_EdgeStatus_Enum_Name(const Expansion_EdgeStatus status) { + static const std::unordered_map statuses{ + {Expansion_EdgeStatus_reached, "r"}, + {Expansion_EdgeStatus_settled, "s"}, + {Expansion_EdgeStatus_connected, "c"}, + }; + auto i = statuses.find(status); + return i == statuses.cend() ? empty_str : i->second; +} } // namespace valhalla diff --git a/src/thor/bidirectional_astar.cc b/src/thor/bidirectional_astar.cc index e6907a2e22..f2ff21e711 100644 --- a/src/thor/bidirectional_astar.cc +++ b/src/thor/bidirectional_astar.cc @@ -361,7 +361,7 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, ? GraphId{} : (FORWARD ? edgelabels_forward_ : edgelabels_reverse_)[pred.predecessor()].edgeid(); expansion_callback_(graphreader, FORWARD ? meta.edge_id : opp_edge_id, prev_pred, - "bidirectional_astar", "r", newcost.secs, + "bidirectional_astar", Expansion_EdgeStatus_reached, newcost.secs, pred.path_distance() + meta.edge->length(), newcost.cost); } @@ -707,8 +707,9 @@ BidirectionalAStar::GetBestPath(valhalla::Location& origin, const auto prev_pred = fwd_pred.predecessor() == kInvalidLabel ? GraphId{} : edgelabels_forward_[fwd_pred.predecessor()].edgeid(); - expansion_callback_(graphreader, fwd_pred.edgeid(), prev_pred, "bidirectional_astar", "s", - fwd_pred.cost().secs, fwd_pred.path_distance(), fwd_pred.cost().cost); + expansion_callback_(graphreader, fwd_pred.edgeid(), prev_pred, "bidirectional_astar", + Expansion_EdgeStatus_settled, fwd_pred.cost().secs, + fwd_pred.path_distance(), fwd_pred.cost().cost); } // Prune path if predecessor is not a through edge or if the maximum @@ -754,8 +755,9 @@ BidirectionalAStar::GetBestPath(valhalla::Location& origin, const auto prev_pred = rev_pred.predecessor() == kInvalidLabel ? GraphId{} : edgelabels_reverse_[rev_pred.predecessor()].edgeid(); - expansion_callback_(graphreader, rev_pred.opp_edgeid(), prev_pred, "bidirectional_astar", "s", - rev_pred.cost().secs, rev_pred.path_distance(), rev_pred.cost().cost); + expansion_callback_(graphreader, rev_pred.opp_edgeid(), prev_pred, "bidirectional_astar", + Expansion_EdgeStatus_settled, rev_pred.cost().secs, + rev_pred.path_distance(), rev_pred.cost().cost); } // Prune path if predecessor is not a through edge @@ -869,8 +871,9 @@ bool BidirectionalAStar::SetForwardConnection(GraphReader& graphreader, const BD const auto prev_pred = pred.predecessor() == kInvalidLabel ? GraphId{} : edgelabels_forward_[pred.predecessor()].edgeid(); - expansion_callback_(graphreader, pred.edgeid(), prev_pred, "bidirectional_astar", "c", - pred.cost().secs, pred.path_distance(), pred.cost().cost); + expansion_callback_(graphreader, pred.edgeid(), prev_pred, "bidirectional_astar", + Expansion_EdgeStatus_connected, pred.cost().secs, pred.path_distance(), + pred.cost().cost); } return true; @@ -941,8 +944,9 @@ bool BidirectionalAStar::SetReverseConnection(GraphReader& graphreader, const BD const auto prev_pred = fwd_pred.predecessor() == kInvalidLabel ? GraphId{} : edgelabels_forward_[fwd_pred.predecessor()].edgeid(); - expansion_callback_(graphreader, fwd_edge_id, prev_pred, "bidirectional_astar", "c", - fwd_pred.cost().secs, fwd_pred.path_distance(), fwd_pred.cost().cost); + expansion_callback_(graphreader, fwd_edge_id, prev_pred, "bidirectional_astar", + Expansion_EdgeStatus_connected, fwd_pred.cost().secs, + fwd_pred.path_distance(), fwd_pred.cost().cost); } return true; @@ -1025,7 +1029,8 @@ void BidirectionalAStar::SetOrigin(GraphReader& graphreader, // setting this edge as reached if (expansion_callback_) { - expansion_callback_(graphreader, edgeid, GraphId{}, "bidirectional_astar", "r", cost.secs, + expansion_callback_(graphreader, edgeid, GraphId{}, "bidirectional_astar", + Expansion_EdgeStatus_reached, cost.secs, static_cast(edge.distance() + 0.5), cost.cost); } @@ -1122,7 +1127,8 @@ void BidirectionalAStar::SetDestination(GraphReader& graphreader, // setting this edge as reached, sending the opposing because this is the reverse tree if (expansion_callback_) { - expansion_callback_(graphreader, edgeid, GraphId{}, "bidirectional_astar", "r", cost.secs, + expansion_callback_(graphreader, edgeid, GraphId{}, "bidirectional_astar", + Expansion_EdgeStatus_reached, cost.secs, static_cast(edge.distance() + 0.5), cost.cost); } diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 3218240e13..15c1ffdb12 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -569,8 +569,8 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, // setting this edge as reached if (expansion_callback_) { - expansion_callback_(graphreader, meta.edge_id, pred.edgeid(), "costmatrix", "r", newcost.secs, - pred_dist, newcost.cost); + expansion_callback_(graphreader, meta.edge_id, pred.edgeid(), "costmatrix", + Expansion_EdgeStatus_reached, newcost.secs, pred_dist, newcost.cost); } return !(pred.not_thru_pruning() && meta.edge->not_thru()); @@ -613,8 +613,9 @@ bool CostMatrix::Expand(const uint32_t index, if (expansion_callback_) { auto prev_pred = pred.predecessor() == kInvalidLabel ? GraphId{} : edgelabels[pred.predecessor()].edgeid(); - expansion_callback_(graphreader, pred.edgeid(), prev_pred, "costmatrix", "s", pred.cost().secs, - pred.path_distance(), pred.cost().cost); + expansion_callback_(graphreader, pred.edgeid(), prev_pred, "costmatrix", + Expansion_EdgeStatus_settled, pred.cost().secs, pred.path_distance(), + pred.cost().cost); } if (FORWARD) { @@ -847,8 +848,9 @@ void CostMatrix::CheckForwardConnections(const uint32_t source, auto prev_pred = fwd_pred.predecessor() == kInvalidLabel ? GraphId{} : edgelabel_[MATRIX_FORW][source][fwd_pred.predecessor()].edgeid(); - expansion_callback_(graphreader, fwd_pred.edgeid(), prev_pred, "costmatrix", "c", - fwd_pred.cost().secs, fwd_pred.path_distance(), fwd_pred.cost().cost); + expansion_callback_(graphreader, fwd_pred.edgeid(), prev_pred, "costmatrix", + Expansion_EdgeStatus_connected, fwd_pred.cost().secs, + fwd_pred.path_distance(), fwd_pred.cost().cost); } } @@ -954,8 +956,9 @@ void CostMatrix::CheckReverseConnections(const uint32_t target, auto prev_pred = rev_pred.predecessor() == kInvalidLabel ? GraphId{} : edgelabel_[MATRIX_REV][source][rev_pred.predecessor()].edgeid(); - expansion_callback_(graphreader, rev_pred.edgeid(), prev_pred, "costmatrix", "c", - rev_pred.cost().secs, rev_pred.path_distance(), rev_pred.cost().cost); + expansion_callback_(graphreader, rev_pred.edgeid(), prev_pred, "costmatrix", + Expansion_EdgeStatus_connected, rev_pred.cost().secs, + rev_pred.path_distance(), rev_pred.cost().cost); } } } diff --git a/src/thor/dijkstras.cc b/src/thor/dijkstras.cc index 1e4099b067..9acc7246fa 100644 --- a/src/thor/dijkstras.cc +++ b/src/thor/dijkstras.cc @@ -386,8 +386,9 @@ void Dijkstras::Compute(google::protobuf::RepeatedPtrField& const auto prev_pred = pred.predecessor() == kInvalidLabel ? GraphId{} : bdedgelabels_[pred.predecessor()].edgeid(); - expansion_callback_(graphreader, pred.edgeid(), prev_pred, "dijkstras", "s", pred.cost().secs, - pred.path_distance(), pred.cost().cost); + expansion_callback_(graphreader, pred.edgeid(), prev_pred, "dijkstras", + Expansion_EdgeStatus_settled, pred.cost().secs, pred.path_distance(), + pred.cost().cost); } } } diff --git a/src/thor/expansion_action.cc b/src/thor/expansion_action.cc index 476e4cc760..6d399fb7f0 100644 --- a/src/thor/expansion_action.cc +++ b/src/thor/expansion_action.cc @@ -6,9 +6,53 @@ #include "midgard/logging.h" #include "midgard/polyline2.h" #include "midgard/util.h" +#include "tyr/serializers.h" using namespace rapidjson; using namespace valhalla::midgard; +using namespace valhalla::tyr; + +namespace { + +using namespace valhalla; + +void writeExpansionProgress(Expansion* expansion, + const baldr::GraphId& edgeid, + const baldr::GraphId& prev_edgeid, + const std::vector& shape, + const std::unordered_set& exp_props, + const Expansion_EdgeStatus& status, + const float& duration, + const uint32_t& distance, + const float& cost) { + + auto* geom = expansion->add_geometries(); + // make the geom + for (const auto& p : shape) { + geom->add_coords(round(p.lng() * 1e6)); + geom->add_coords(round(p.lat() * 1e6)); + } + + // no properties asked for, don't collect any + if (!exp_props.size()) { + return; + } + + // make the properties + if (exp_props.count(Options_ExpansionProperties_duration)) + expansion->add_durations(static_cast(duration)); + if (exp_props.count(Options_ExpansionProperties_distance)) + expansion->add_distances(static_cast(distance)); + if (exp_props.count(Options_ExpansionProperties_cost)) + expansion->add_costs(static_cast(cost)); + if (exp_props.count(Options_ExpansionProperties_edge_status)) + expansion->add_edge_status(status); + if (exp_props.count(Options_ExpansionProperties_edge_id)) + expansion->add_edge_id(static_cast(edgeid)); + if (exp_props.count(Options_ExpansionProperties_pred_edge_id)) + expansion->add_pred_edge_id(static_cast(prev_edgeid)); +} +} // namespace namespace valhalla { namespace thor { @@ -30,21 +74,16 @@ std::string thor_worker_t::expansion(Api& request) { exp_props.insert(static_cast(prop)); } - // default the expansion geojson so its easy to add to as we go - writer_wrapper_t writer(1024 * 1024); - writer.start_object(); - writer("type", "FeatureCollection"); - writer.start_array("features"); - writer.set_precision(6); - + auto* expansion = request.mutable_expansion(); // a lambda that the path algorithm can call to add stuff to the dom // route and isochrone produce different GeoJSON properties std::string algo = ""; - auto track_expansion = [&writer, &opp_edges, &gen_factor, &skip_opps, &exp_props, + auto track_expansion = [&expansion, &opp_edges, &gen_factor, &skip_opps, &exp_props, &algo](baldr::GraphReader& reader, baldr::GraphId edgeid, baldr::GraphId prev_edgeid, const char* algorithm = nullptr, - std::string status = nullptr, const float duration = 0.f, - const uint32_t distance = 0, const float cost = 0.f) { + const Expansion_EdgeStatus status = Expansion_EdgeStatus_reached, + const float duration = 0.f, const uint32_t distance = 0, + const float cost = 0.f) { algo = algorithm; auto tile = reader.GetGraphTile(edgeid); @@ -73,51 +112,8 @@ std::string thor_worker_t::expansion(Api& request) { std::reverse(shape.begin(), shape.end()); Polyline2::Generalize(shape, gen_factor, {}, false); - writer.start_object(); // feature object - writer("type", "Feature"); - - writer.start_object("geometry"); - writer("type", "LineString"); - writer.start_array("coordinates"); - - // make the geom - for (const auto& p : shape) { - writer.start_array(); - writer(p.lng()); - writer(p.lat()); - writer.end_array(); - } - - writer.end_array(); // coordinates - writer.end_object(); // geometry - - writer.start_object("properties"); - // no properties asked for, don't collect any - if (!exp_props.size()) { - writer.end_object(); // properties - writer.end_object(); // feature - return; - } - - // make the properties - if (exp_props.count(Options_ExpansionProperties_duration)) { - writer("duration", static_cast(duration)); - } - if (exp_props.count(Options_ExpansionProperties_distance)) { - writer("distance", static_cast(distance)); - } - if (exp_props.count(Options_ExpansionProperties_cost)) { - writer("cost", static_cast(cost)); - } - if (exp_props.count(Options_ExpansionProperties_edge_status)) - writer("edge_status", status); - if (exp_props.count(Options_ExpansionProperties_edge_id)) - writer("edge_id", static_cast(edgeid)); - if (exp_props.count(Options_ExpansionProperties_pred_edge_id)) - writer("pred_edge_id", static_cast(prev_edgeid)); - - writer.end_object(); // properties - writer.end_object(); // feature + writeExpansionProgress(expansion, edgeid, prev_edgeid, shape, exp_props, status, duration, + distance, cost); }; // tell all the algorithms how to track expansion @@ -150,13 +146,6 @@ std::string thor_worker_t::expansion(Api& request) { // anyway } - // close the GeoJSON - writer.end_array(); // features - writer.start_object("properties"); - writer("algorithm", algo); - writer.end_object(); - writer.end_object(); // object - // tell all the algorithms to stop tracking the expansion for (auto* alg : std::vector{&multi_modal_astar, &timedep_forward, &timedep_reverse, &bidir_astar, &bss_astar}) { @@ -166,7 +155,7 @@ std::string thor_worker_t::expansion(Api& request) { isochrone_gen.SetInnerExpansionCallback(nullptr); // serialize it - return writer.get_buffer(); + return tyr::serializeExpansion(request, algo); } } // namespace thor diff --git a/src/tyr/CMakeLists.txt b/src/tyr/CMakeLists.txt index 571d2b0de1..38c669f996 100644 --- a/src/tyr/CMakeLists.txt +++ b/src/tyr/CMakeLists.txt @@ -8,7 +8,8 @@ set(sources route_serializer_osrm.cc route_summary_cache.cc serializers.cc - transit_available_serializer.cc) + transit_available_serializer.cc + expansion_serializer.cc) set(sources_with_warnings locate_serializer.cc diff --git a/src/tyr/expansion_serializer.cc b/src/tyr/expansion_serializer.cc new file mode 100644 index 0000000000..0401bbc522 --- /dev/null +++ b/src/tyr/expansion_serializer.cc @@ -0,0 +1,86 @@ + +#include "baldr/rapidjson_utils.h" +#include "tyr/serializers.h" + +using namespace valhalla; +using namespace rapidjson; + +namespace valhalla { +namespace tyr { +std::string serializeExpansion(Api& request, const std::string& algo) { + if (request.options().format() == Options::Format::Options_Format_pbf) + return serializePbf(request); + + // form GeoJSON + writer_wrapper_t writer(1024 * 1024); + writer.start_object(); + writer("type", "FeatureCollection"); + writer.start_array("features"); + writer.set_precision(6); + + std::unordered_set exp_props; + for (const auto& prop : request.options().expansion_properties()) { + exp_props.insert(static_cast(prop)); + } + auto expansion = request.expansion(); + for (int i = 0; i < expansion.geometries().size(); ++i) { + // create features + writer.start_object(); // feature object + writer("type", "Feature"); + + writer.start_object("geometry"); + writer("type", "LineString"); + writer.start_array("coordinates"); + + // make the geom + const auto& geom = expansion.geometries(i); + for (int j = 0; j < geom.coords().size() - 1; j += 2) { + writer.start_array(); + writer(static_cast((geom.coords(j) / 1e6))); + writer(static_cast((geom.coords(j + 1) / 1e6))); + writer.end_array(); + } + + writer.end_array(); // coordinates + writer.end_object(); // geometry + + writer.start_object("properties"); + // no properties asked for, don't collect any + if (!exp_props.size()) { + writer.end_object(); // properties + writer.end_object(); // feature + continue; + } + + // make the properties + if (exp_props.count(Options_ExpansionProperties_duration)) { + writer("duration", static_cast(expansion.durations(i))); + } + if (exp_props.count(Options_ExpansionProperties_distance)) { + writer("distance", static_cast(expansion.distances(i))); + } + if (exp_props.count(Options_ExpansionProperties_cost)) { + writer("cost", static_cast(expansion.costs(i))); + } + if (exp_props.count(Options_ExpansionProperties_edge_status)) + writer("edge_status", Expansion_EdgeStatus_Enum_Name(expansion.edge_status(i))); + if (exp_props.count(Options_ExpansionProperties_edge_id)) + writer("edge_id", static_cast(expansion.edge_id(i))); + if (exp_props.count(Options_ExpansionProperties_pred_edge_id)) + writer("pred_edge_id", static_cast(expansion.pred_edge_id(i))); + + writer.end_object(); // properties + writer.end_object(); // feature + } + + // close the GeoJSON + writer.end_array(); // features + writer.start_object("properties"); + writer("algorithm", algo); + writer.end_object(); + writer.end_object(); // object + + return writer.get_buffer(); +} +} // namespace tyr +} // namespace valhalla \ No newline at end of file diff --git a/src/tyr/matrix_serializer.cc b/src/tyr/matrix_serializer.cc index fa58ada1be..e9009e8a52 100644 --- a/src/tyr/matrix_serializer.cc +++ b/src/tyr/matrix_serializer.cc @@ -242,6 +242,11 @@ namespace tyr { std::string serializeMatrix(Api& request) { double distance_scale = (request.options().units() == Options::miles) ? kMilePerMeter : kKmPerMeter; + + // dont bother serializing in case of /expansion request + if (request.options().action() == Options_Action_expansion) + return ""; + switch (request.options().format()) { case Options_Format_osrm: return osrm_serializers::serialize(request); diff --git a/src/tyr/serializers.cc b/src/tyr/serializers.cc index d70d0a08ec..f9fa77931d 100644 --- a/src/tyr/serializers.cc +++ b/src/tyr/serializers.cc @@ -216,6 +216,9 @@ std::string serializePbf(Api& request) { case Options::isochrone: selection.set_isochrone(true); break; + case Options::expansion: + selection.set_expansion(true); + break; // should never get here, actions which dont have pbf yet return json default: throw std::logic_error("Requested action is not yet serializable as pbf"); @@ -243,6 +246,8 @@ std::string serializePbf(Api& request) { request.clear_matrix(); if (!selection.isochrone()) request.clear_isochrone(); + if (!selection.expansion()) + request.clear_expansion(); // serialize the bytes auto bytes = request.SerializeAsString(); diff --git a/src/worker.cc b/src/worker.cc index fed19befb2..b914dfa1c0 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -688,7 +688,8 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { Options::trace_attributes, Options::status, Options::sources_to_targets, - Options::isochrone}; + Options::isochrone, + Options::expansion}; // if its not a pbf supported action we reset to json if (pbf_actions.count(options.action()) == 0) { options.set_format(Options::json); diff --git a/test/gurka/test_expansion.cc b/test/gurka/test_expansion.cc index 5a42ea993a..864f4490ac 100644 --- a/test/gurka/test_expansion.cc +++ b/test/gurka/test_expansion.cc @@ -31,15 +31,16 @@ class ExpansionTest : public ::testing::TestWithParam> expansion_map = gurka::buildtiles(layout, ways, {}, {}, "test/data/expansion"); } - void check_result(const std::string& action, - const std::vector& waypoints, - bool skip_opps, - unsigned exp_feats, - const std::vector& props = {}) { - + valhalla::Api do_expansion_action(std::string* res, + bool skip_opps, + std::string action, + const std::vector& props, + const std::vector& waypoints, + const bool pbf) { std::unordered_map options = {{"/skip_opposites", skip_opps ? "1" : "0"}, - {"/action", action}}; + {"/action", action}, + {"/format", pbf ? "pbf" : "json"}}; for (uint8_t i = 0; i < props.size(); i++) { options.insert({{"/expansion_properties/" + std::to_string(i), props[i]}}); } @@ -48,16 +49,82 @@ class ExpansionTest : public ::testing::TestWithParam> } // get the response - std::string res; valhalla::Api api; if (action == "sources_to_targets") { api = gurka::do_action(Options::expansion, expansion_map, {waypoints[0]}, {waypoints[1]}, - "auto", options, {}, &res); + "auto", options, {}, res); } else { - api = gurka::do_action(Options::expansion, expansion_map, waypoints, "auto", options, {}, &res); + api = gurka::do_action(Options::expansion, expansion_map, waypoints, "auto", options, {}, res); + } + + return api; + } + void check_results(const std::string& action, + const std::vector& waypoints, + bool skip_opps, + unsigned exp_feats, + const std::vector& props = {}) { + check_result_json(action, waypoints, skip_opps, exp_feats, props); + check_result_pbf(action, waypoints, skip_opps, exp_feats, props); + } + void check_result_pbf(const std::string& action, + const std::vector& waypoints, + bool skip_opps, + unsigned exp_feats, + const std::vector& props) { + std::string res; + auto api = do_expansion_action(&res, skip_opps, action, props, waypoints, true); + + Api parsed_api; + parsed_api.ParseFromString(res); + + ASSERT_EQ(parsed_api.expansion().geometries_size(), exp_feats); + + bool edge_status = false; + bool distance = false; + bool duration = false; + bool pred_edge_id = false; + bool edge_id = false; + bool cost = false; + + const std::unordered_map prop_count; + for (const auto& prop : props) { + if (prop == "edge_status") { + edge_status = true; + } + if (prop == "distance") { + distance = true; + } + if (prop == "duration") { + duration = true; + } + if (prop == "pred_edge_id") { + pred_edge_id = true; + } + if (prop == "edge_id") { + edge_id = true; + } + if (prop == "cost") { + cost = true; + } } + ASSERT_EQ(parsed_api.expansion().geometries_size(), exp_feats); + ASSERT_EQ(parsed_api.expansion().edge_status_size(), edge_status ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().distances_size(), distance ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().durations_size(), duration ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().pred_edge_id_size(), pred_edge_id ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().edge_id_size(), edge_id ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().costs_size(), cost ? exp_feats : 0); + } + void check_result_json(const std::string& action, + const std::vector& waypoints, + bool skip_opps, + unsigned exp_feats, + const std::vector& props) { + std::string res; + auto api = do_expansion_action(&res, skip_opps, action, props, waypoints, false); // get the MultiLineString feature rapidjson::Document res_doc; res_doc.Parse(res.c_str()); @@ -80,35 +147,35 @@ gurka::map ExpansionTest::expansion_map = {}; TEST_P(ExpansionTest, Isochrone) { // test Dijkstra expansion // 11 because there's a one-way - check_result("isochrone", {"A"}, false, 11, GetParam()); + check_results("isochrone", {"A"}, false, 11, GetParam()); } TEST_P(ExpansionTest, IsochroneNoOpposites) { // test Dijkstra expansion and skip collecting more expensive opposite edges - check_result("isochrone", {"A"}, true, 6, GetParam()); + check_results("isochrone", {"A"}, true, 6, GetParam()); } TEST_P(ExpansionTest, Routing) { // test AStar expansion - check_result("route", {"E", "H"}, false, 23, GetParam()); + check_results("route", {"E", "H"}, false, 23, GetParam()); } TEST_P(ExpansionTest, RoutingNoOpposites) { // test AStar expansion and no opposite edges - check_result("route", {"E", "H"}, true, 16, GetParam()); + check_results("route", {"E", "H"}, true, 16, GetParam()); } TEST_P(ExpansionTest, Matrix) { - check_result("sources_to_targets", {"E", "H"}, false, 48, GetParam()); + check_results("sources_to_targets", {"E", "H"}, false, 48, GetParam()); } TEST_P(ExpansionTest, MatrixNoOpposites) { - check_result("sources_to_targets", {"E", "H"}, true, 23, GetParam()); + check_results("sources_to_targets", {"E", "H"}, true, 23, GetParam()); } TEST_F(ExpansionTest, UnsupportedAction) { try { - check_result("status", {"E", "H"}, true, 16); + check_results("status", {"E", "H"}, true, 16); } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 144); } catch (...) { FAIL() << "Expected valhalla_exception_t."; }; @@ -116,7 +183,7 @@ TEST_F(ExpansionTest, UnsupportedAction) { TEST_F(ExpansionTest, UnsupportedPropType) { try { - check_result("route", {"E", "H"}, true, 16, {"foo", "bar"}); + check_results("route", {"E", "H"}, true, 16, {"foo", "bar"}); } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 168); } catch (...) { FAIL() << "Expected valhalla_exception_t."; }; @@ -141,4 +208,4 @@ INSTANTIATE_TEST_SUITE_P(ExpandPropsTest, std::vector{"distance", "duration", "pred_edge_id"}, std::vector{"edge_id", "cost"}, - std::vector{})); + std::vector{})); \ No newline at end of file diff --git a/valhalla/proto_conversions.h b/valhalla/proto_conversions.h index 7dda741eb9..768bdc5d14 100644 --- a/valhalla/proto_conversions.h +++ b/valhalla/proto_conversions.h @@ -546,6 +546,7 @@ const std::string& Location_Type_Enum_Name(const Location::Type t); const std::string& Location_SideOfStreet_Enum_Name(const Location::SideOfStreet s); bool Options_ExpansionProperties_Enum_Parse(const std::string& prop, Options::ExpansionProperties* a); bool Options_ExpansionAction_Enum_Parse(const std::string& action, Options::Action* a); +const std::string& Expansion_EdgeStatus_Enum_Name(const Expansion_EdgeStatus status); std::pair travel_mode_type(const valhalla::DirectionsLeg_Maneuver& maneuver); diff --git a/valhalla/thor/dijkstras.h b/valhalla/thor/dijkstras.h index 4fe12eea49..dd8cdbb0e0 100644 --- a/valhalla/thor/dijkstras.h +++ b/valhalla/thor/dijkstras.h @@ -74,7 +74,7 @@ class Dijkstras { const baldr::GraphId, const baldr::GraphId, const char*, - const char*, + const Expansion::EdgeStatus, float, uint32_t, float)>; diff --git a/valhalla/thor/matrixalgorithm.h b/valhalla/thor/matrixalgorithm.h index a697dcb7cf..f2f62b7ace 100644 --- a/valhalla/thor/matrixalgorithm.h +++ b/valhalla/thor/matrixalgorithm.h @@ -124,7 +124,7 @@ class MatrixAlgorithm { const baldr::GraphId, const baldr::GraphId, const char*, - const char*, + const Expansion_EdgeStatus, float, uint32_t, float)>; diff --git a/valhalla/thor/pathalgorithm.h b/valhalla/thor/pathalgorithm.h index 0c18bcf914..5b7d12bb4c 100644 --- a/valhalla/thor/pathalgorithm.h +++ b/valhalla/thor/pathalgorithm.h @@ -130,7 +130,7 @@ class PathAlgorithm { const baldr::GraphId, const baldr::GraphId, const char*, - const char*, + const Expansion_EdgeStatus, float, uint32_t, float)>; diff --git a/valhalla/tyr/serializers.h b/valhalla/tyr/serializers.h index 062d9dc985..f494e802ce 100644 --- a/valhalla/tyr/serializers.h +++ b/valhalla/tyr/serializers.h @@ -17,6 +17,7 @@ #include #include #include +#include #include namespace valhalla { @@ -41,6 +42,11 @@ std::string serializeMatrix(Api& request); std::string serializeIsochrones(Api& request, std::vector::contour_interval_t>& intervals, const std::shared_ptr>& isogrid); +/** + * Write GeoJSON from expansion pbf + */ +std::string serializeExpansion(Api& request, const std::string& algo); + /** * Turn heights and ranges into a height response * From 56785d0a7f5ecb1a38b2fea64dd38143062360b3 Mon Sep 17 00:00:00 2001 From: Nils Date: Tue, 23 Apr 2024 16:35:21 +0200 Subject: [PATCH 073/198] label third_party includes as -isystem in test/CMakeLists.txt (#4698) Co-authored-by: Michael Kirk --- test/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2f56325ff9..de1b8c0cd0 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,11 +17,12 @@ add_library(valhalla_test ) target_include_directories(valhalla_test PUBLIC ${VALHALLA_SOURCE_DIR}/test - ${VALHALLA_SOURCE_DIR}/test/gurka - ${VALHALLA_SOURCE_DIR}/third_party/protozero/include + ${VALHALLA_SOURCE_DIR}/test/gurka) +target_include_directories(valhalla_test SYSTEM PUBLIC + ${robinhoodhashing_include_dir} ${VALHALLA_SOURCE_DIR}/third_party/just_gtfs/include + ${VALHALLA_SOURCE_DIR}/third_party/protozero/include ${VALHALLA_SOURCE_DIR}/third_party/libosmium/include - ${robinhoodhashing_include_dir} ${VALHALLA_SOURCE_DIR}/third_party/microtar/src) target_link_libraries(valhalla_test valhalla gtest gtest_main gmock pthread PkgConfig::LZ4) From 812383f0f87a29f3bf6e446adae4db9ded13cc3b Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 24 Apr 2024 07:05:48 -0700 Subject: [PATCH 074/198] Address some "unused code" errors (#4701) --- src/thor/astar_bss.cc | 2 +- test/gurka/test_landmarks.cc | 2 -- test/logging.cc | 6 +----- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/thor/astar_bss.cc b/src/thor/astar_bss.cc index c6cf719117..74a44cdce3 100644 --- a/src/thor/astar_bss.cc +++ b/src/thor/astar_bss.cc @@ -513,7 +513,7 @@ std::vector AStarBSSAlgorithm::FormPath(baldr::GraphReader& graphreade // Work backwards from the destination std::vector path; travel_mode_t old = travel_mode_t::kPedestrian; - int mode_change_count = 0; + [[maybe_unused]] int mode_change_count = 0; for (auto edgelabel_index = dest; edgelabel_index != kInvalidLabel; edgelabel_index = edgelabels_[edgelabel_index].predecessor()) { diff --git a/test/gurka/test_landmarks.cc b/test/gurka/test_landmarks.cc index b565b29cc1..eecefd512c 100644 --- a/test/gurka/test_landmarks.cc +++ b/test/gurka/test_landmarks.cc @@ -180,11 +180,9 @@ void DisplayLandmarksInTiles(GraphReader& reader, const GraphId& graphid) { LOG_INFO("edge endnode: " + std::to_string(e.endnode().id()) + ", length: " + std::to_string(e.length())); - int count_landmarks = 0; for (const auto& value : tagged_values) { if (value.first != baldr::TaggedValue::kLandmark) continue; - count_landmarks++; DisplayLandmark(Landmark(value.second)); } } diff --git a/test/logging.cc b/test/logging.cc index 4aa5c9ee0e..6a0abc4ad9 100644 --- a/test/logging.cc +++ b/test/logging.cc @@ -57,12 +57,8 @@ TEST(Logging, FileLoggerTest) { results.emplace_back(std::async(std::launch::async, work)); } - // dont really care about the results but we can pretend - int exit_code = 0; for (auto& result : results) { - try { - result.get(); - } catch (std::exception& e) { exit_code++; } + ASSERT_NO_THROW(result.get()); } // wait for logger to close and reopen the file From 9888d8e5de6f26193a787b442b1e0acf34c38876 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 24 Apr 2024 09:45:14 -0700 Subject: [PATCH 075/198] rm unused import fixes building tests without prime_server (#4705) --- test/gurka/test_elevation.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/gurka/test_elevation.cc b/test/gurka/test_elevation.cc index 1c9656ea7f..d97fc1e274 100644 --- a/test/gurka/test_elevation.cc +++ b/test/gurka/test_elevation.cc @@ -8,12 +8,9 @@ #include #include -#include -#include using namespace valhalla; using namespace valhalla::gurka; -using namespace prime_server; const std::string workdir = "test/data/gurka_elevation"; From 16fecc14df63b6bbb0f53f37c6b31e44589d44cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Hrab=C3=A1nek?= Date: Thu, 25 Apr 2024 11:12:40 +0200 Subject: [PATCH 076/198] Correctly set has_highway, has_ferry and has_toll attributes when directions_type is set to none (#4706) --- CHANGELOG.md | 1 + proto/common.proto | 10 ++++ proto/directions.proto | 9 ---- proto/trip.proto | 1 + src/odin/directionsbuilder.cc | 12 ++--- src/thor/triplegbuilder.cc | 18 +++++++ test/gurka/test_route_summary.cc | 80 ++++++++++++++++++++++++++++++++ valhalla/odin/enhancedtrippath.h | 4 ++ 8 files changed, 117 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ddc3a26b56..edc038784e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ * FIXED: Fix segfault in OSRM serializer with bannerInstructions when destination is on roundabout [#4480](https://github.com/valhalla/valhalla/pull/4481) * FIXED: Fix segfault in costmatrix (date_time and time zone always added). [#4530](https://github.com/valhalla/valhalla/pull/4530) * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) + * FIXED: Fix for assigning attributes has_(highway, ferry, toll) if directions_type is none [#4465](https://github.com/valhalla/valhalla/issues/4465) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/proto/common.proto b/proto/common.proto index 1b1c7b58db..649d8a8c74 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -391,3 +391,13 @@ enum TransitType { kGondola = 6; kFunicular = 7; } + +message Summary { + float length = 1; // kilometers or miles based on units + double time = 2; // seconds + BoundingBox bbox = 3; // Bounding box of the shape + bool has_time_restrictions = 4; // Does the route contain any time restrictions? + bool has_toll = 5; + bool has_ferry = 6; + bool has_highway = 7; +} diff --git a/proto/directions.proto b/proto/directions.proto index 226cbeb186..ada56686d7 100644 --- a/proto/directions.proto +++ b/proto/directions.proto @@ -6,15 +6,6 @@ import public "sign.proto"; message DirectionsLeg { - message Summary { - float length = 1; // kilometers or miles based on units - double time = 2; // seconds - BoundingBox bbox = 3; // Bounding box of the shape - bool has_time_restrictions = 4; // Does the route contain any time restrictions? - bool has_toll = 5; - bool has_ferry = 6; - bool has_highway = 7; - } message GuidanceView { enum Type{ diff --git a/proto/trip.proto b/proto/trip.proto index b49af1d981..ee2dddf811 100644 --- a/proto/trip.proto +++ b/proto/trip.proto @@ -286,6 +286,7 @@ message TripLeg { repeated Incident incidents = 11; repeated string algorithms = 12; repeated Closure closures = 13; + Summary summary = 14; } message TripRoute { diff --git a/src/odin/directionsbuilder.cc b/src/odin/directionsbuilder.cc index 562cb62172..2f0515f80a 100644 --- a/src/odin/directionsbuilder.cc +++ b/src/odin/directionsbuilder.cc @@ -95,9 +95,6 @@ void DirectionsBuilder::PopulateDirectionsLeg(const Options& options, EnhancedTripLeg* etp, std::list& maneuvers, DirectionsLeg& trip_directions) { - bool has_toll = false; - bool has_highway = false; - bool has_ferry = false; // Populate trip and leg IDs trip_directions.set_trip_id(etp->trip_id()); trip_directions.set_leg_id(etp->leg_id()); @@ -147,15 +144,12 @@ void DirectionsBuilder::PopulateDirectionsLeg(const Options& options, trip_maneuver->set_end_shape_index(maneuver.end_shape_index()); if (maneuver.portions_toll()) { trip_maneuver->set_portions_toll(maneuver.portions_toll()); - has_toll = true; } if (maneuver.portions_highway()) { trip_maneuver->set_portions_highway(maneuver.portions_highway()); - has_highway = true; } if (maneuver.ferry()) { trip_maneuver->set_portions_ferry(maneuver.ferry()); - has_ferry = true; } trip_maneuver->set_has_time_restrictions(maneuver.has_time_restrictions()); @@ -439,9 +433,9 @@ void DirectionsBuilder::PopulateDirectionsLeg(const Options& options, trip_directions.mutable_summary()->set_has_time_restrictions(has_time_restrictions); // Populate toll, highway, ferry tags - trip_directions.mutable_summary()->set_has_toll(has_toll); - trip_directions.mutable_summary()->set_has_highway(has_highway); - trip_directions.mutable_summary()->set_has_ferry(has_ferry); + trip_directions.mutable_summary()->set_has_toll(etp->summary().has_toll()); + trip_directions.mutable_summary()->set_has_highway(etp->summary().has_highway()); + trip_directions.mutable_summary()->set_has_ferry(etp->summary().has_ferry()); } } // namespace odin diff --git a/src/thor/triplegbuilder.cc b/src/thor/triplegbuilder.cc index 6fd95ae072..7ae3e64b87 100644 --- a/src/thor/triplegbuilder.cc +++ b/src/thor/triplegbuilder.cc @@ -1754,6 +1754,9 @@ void TripLegBuilder::Build( // from the edge index that we assigned to them earlier in route_action auto intermediate_itr = trip_path.mutable_location()->begin() + 1; double total_distance = 0; + bool has_toll = false; + bool has_ferry = false; + bool has_highway = false; // loop over the edges to build the trip leg for (auto edge_itr = path_begin; edge_itr != path_end; ++edge_itr, ++edge_index) { @@ -1767,6 +1770,16 @@ void TripLegBuilder::Build( const uint8_t travel_type = travel_types[static_cast(mode)]; const auto& costing = mode_costing[static_cast(mode)]; + if (directededge->toll()) { + has_toll = true; + } + if (directededge->use() == Use::kFerry) { + has_ferry = true; + } + if (directededge->classification() == baldr::RoadClass::kMotorway) { + has_highway = true; + } + // Set node attributes - only set if they are true since they are optional graph_tile_ptr start_tile = graphtile; graphreader.GetGraphTile(startnode, start_tile); @@ -2077,6 +2090,11 @@ void TripLegBuilder::Build( trip_path.set_osm_changeset(osmchangeset); } + Summary* summary = trip_path.mutable_summary(); + summary->set_has_toll(has_toll); + summary->set_has_ferry(has_ferry); + summary->set_has_highway(has_highway); + // Add that extra costing information if requested AccumulateRecostingInfoForward(options, start_pct, end_pct, forward_time_info, invariant, graphreader, trip_path); diff --git a/test/gurka/test_route_summary.cc b/test/gurka/test_route_summary.cc index 0ad3c1f8e5..346f7e3a0e 100644 --- a/test/gurka/test_route_summary.cc +++ b/test/gurka/test_route_summary.cc @@ -205,3 +205,83 @@ TEST(TestRouteSummary, DupSummaryFix) { std::filesystem::remove_all(workdir); } + +TEST(Standalone, TripLegSummary) { + const std::string ascii_map = R"( + A---B---C---D + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}, + {"BC", {{"highway", "motorway"}, {"toll", "yes"}}}, + {"CD", {{"route", "ferry"}}}}; + const double gridsize = 100; + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + const std::string workdir = "test/data/gurka_test_route_summary"; + + std::string result_json; + rapidjson::Document result; + + valhalla::gurka::map map = gurka::buildtiles(layout, ways, {}, {}, workdir); + + valhalla::Api result0 = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "auto", + {{"/directions_type", "none"}}, {}, &result_json); + EXPECT_TRUE(result0.trip().routes(0).legs(0).summary().has_highway()); + EXPECT_FALSE(result0.trip().routes(0).legs(0).summary().has_toll()); + EXPECT_FALSE(result0.trip().routes(0).legs(0).summary().has_ferry()); + + result.Parse(result_json.c_str()); + auto trip = result["trip"].GetObject(); + auto summary = trip["summary"].GetObject(); + + EXPECT_TRUE(summary["has_highway"].GetBool()); + EXPECT_FALSE(summary["has_toll"].GetBool()); + EXPECT_FALSE(summary["has_ferry"].GetBool()); + result_json.erase(); + + valhalla::Api result1 = gurka::do_action(valhalla::Options::route, map, {"B", "C"}, "auto", + {{"/directions_type", "none"}}); + EXPECT_TRUE(result1.trip().routes(0).legs(0).summary().has_highway()); + EXPECT_TRUE(result1.trip().routes(0).legs(0).summary().has_toll()); + EXPECT_FALSE(result1.trip().routes(0).legs(0).summary().has_ferry()); + + valhalla::Api result2 = gurka::do_action(valhalla::Options::route, map, {"C", "D"}, "auto", + {{"/directions_type", "none"}}); + EXPECT_FALSE(result2.trip().routes(0).legs(0).summary().has_highway()); + EXPECT_FALSE(result2.trip().routes(0).legs(0).summary().has_toll()); + EXPECT_TRUE(result2.trip().routes(0).legs(0).summary().has_ferry()); + + // Validate that the presence of highway, toll, and ferry tags in route summaries is consistent + // and does not depend on the `directions_type` value + + valhalla::Api result3 = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "auto", + {{"/directions_type", "none"}}, {}, &result_json); + + EXPECT_TRUE(result3.trip().routes(0).legs(0).summary().has_highway()); + EXPECT_TRUE(result3.trip().routes(0).legs(0).summary().has_toll()); + EXPECT_TRUE(result3.trip().routes(0).legs(0).summary().has_ferry()); + + result.Parse(result_json.c_str()); + trip = result["trip"].GetObject(); + summary = trip["summary"].GetObject(); + + EXPECT_TRUE(summary["has_highway"].GetBool()); + EXPECT_TRUE(summary["has_toll"].GetBool()); + EXPECT_TRUE(summary["has_ferry"].GetBool()); + result_json.erase(); + + valhalla::Api result4 = gurka::do_action(valhalla::Options::route, map, {"A", "D"}, "auto", + {{"/directions_type", "maneuvers"}}, {}, &result_json); + EXPECT_TRUE(result4.trip().routes(0).legs(0).summary().has_highway()); + EXPECT_TRUE(result4.trip().routes(0).legs(0).summary().has_toll()); + EXPECT_TRUE(result4.trip().routes(0).legs(0).summary().has_ferry()); + + result.Parse(result_json.c_str()); + trip = result["trip"].GetObject(); + summary = trip["summary"].GetObject(); + + EXPECT_TRUE(summary["has_highway"].GetBool()); + EXPECT_TRUE(summary["has_toll"].GetBool()); + EXPECT_TRUE(summary["has_ferry"].GetBool()); + + std::filesystem::remove_all(workdir); +} diff --git a/valhalla/odin/enhancedtrippath.h b/valhalla/odin/enhancedtrippath.h index b28ecee143..343c015a1f 100644 --- a/valhalla/odin/enhancedtrippath.h +++ b/valhalla/odin/enhancedtrippath.h @@ -88,6 +88,10 @@ class EnhancedTripLeg { return trip_path_.bbox(); } + const ::valhalla::Summary& summary() const { + return trip_path_.summary(); + } + std::unique_ptr GetEnhancedNode(const int node_index); std::unique_ptr GetPrevEdge(const int node_index, int delta = 1); From 0636e379875293a98e7b0c1b006f8765502b73b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norbert=20=C5=BDof=C3=A1k?= Date: Thu, 2 May 2024 17:57:06 +0200 Subject: [PATCH 077/198] Added elapsed_cost to map matching json output (#4709) --- CHANGELOG.md | 3 ++- src/tyr/trace_serializer.cc | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edc038784e..1c90dd9fb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,7 +115,8 @@ * ADDED: some missing documentation about request parameters [#4687](https://github.com/valhalla/valhalla/pull/4687) * ADDED: Consider more forward/backward tags for access restrictions and speeds [#4686](https://github.com/valhalla/valhalla/pull/4686) * CHANGED: change costmatrix max_distance threshold to a distance threshold instead of duration [#4672](https://github.com/valhalla/valhalla/pull/4672) - * ADDED: PBF support for expansion [#4614](https://github.com/valhalla/valhalla/pull/4614/) + * ADDED: PBF support for expansion [#4614](https://github.com/valhalla/valhalla/pull/4614/) + * ADDED: elapsed_cost field to map matching json response [#4709](https://github.com/valhalla/valhalla/pull/4709) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/tyr/trace_serializer.cc b/src/tyr/trace_serializer.cc index 662ce9d3e2..dfed9514f6 100644 --- a/src/tyr/trace_serializer.cc +++ b/src/tyr/trace_serializer.cc @@ -308,6 +308,7 @@ void serialize_edges(const AttributesController& controller, if (controller(kNodeElapsedTime)) { writer.set_precision(3); writer("elapsed_time", node.cost().elapsed_cost().seconds()); + writer("elapsed_cost", node.cost().elapsed_cost().cost()); } if (controller(kNodeAdminIndex)) { writer("admin_index", static_cast(node.admin_index())); From 651a515555971153772c655f15a9f65f41c8fea7 Mon Sep 17 00:00:00 2001 From: Nils Date: Thu, 2 May 2024 19:52:17 +0200 Subject: [PATCH 078/198] test-add workflow to auto-update the fossgis instances (#4716) --- .github/workflows/update_public_servers.yml | 32 +++++++++++++++++++ scripts/update_public_server.sh | 35 +++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 .github/workflows/update_public_servers.yml create mode 100644 scripts/update_public_server.sh diff --git a/.github/workflows/update_public_servers.yml b/.github/workflows/update_public_servers.yml new file mode 100644 index 0000000000..8bf122c66e --- /dev/null +++ b/.github/workflows/update_public_servers.yml @@ -0,0 +1,32 @@ +name: Update public FOSSGIS servers + +on: + push: + branches: + - nn-update-fossgis-gha + paths: + - .github/workflows + - 'src' + - 'valhalla' + - 'proto' + - 'scripts' + - 'lua' + workflow_dispatch: + +jobs: + update_servers: + name: Update servers + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run script + run: | + eval $(ssh-agent -s) + ssh-add - <<< "${{ secrets.SSH_SECRET }}" + # first the build server + ssh -p 23432 -o StrictHostKeyChecking=no valhalla@162.55.2.221 'sudo bash -s builder' < scripts/update_public_server.sh + # then the services + ssh -p 23432 -o StrictHostKeyChecking=no valhalla@162.55.2.221 'sudo bash -s service' < scripts/update_public_server.sh diff --git a/scripts/update_public_server.sh b/scripts/update_public_server.sh new file mode 100644 index 0000000000..08b43bcd2e --- /dev/null +++ b/scripts/update_public_server.sh @@ -0,0 +1,35 @@ +# updates Valhalla on the FOSSGIS servers + +set -e + +server=$1 +src_dir="/src/valhalla" + +git -C "${src_dir}" checkout master +git -C "${src_dir}" pull + +if [[ $server == "builder" ]]; then + cmake -S "${src_dir}" -B "${src_dir}/build" \ + -DENABLE_TOOLS=OFF \ + -DENABLE_SERVICES=OFF \ + -DENABLE_HTTP=OFF \ + -DENABLE_PYTHON_BINDINGS=OFF \ + -DENABLE_TESTS=OFF \ + -DENABLE_SINGLE_FILES_WERROR=OFF \ + -DENABLE_GDAL=OFF + + sudo make -C "${src_dir}/build" -j$(nproc) install + # config is updated by +else + cmake -S "${src_dir}" -B "${src_dir}/build" \ + -DENABLE_DATA_TOOLS=OFF \ + -DENABLE_SERVICES=ON \ + -DENABLE_HTTP=ON \ + -DENABLE_PYTHON_BINDINGS=OFF \ + -DENABLE_TESTS=OFF \ + -DENABLE_SINGLE_FILES_WERROR=OFF + + sudo make -C "${src_dir}/build" -j$(nproc) install + # Update the configs + /opt/valhalla/runner_build_config.sh 8000 && /opt/valhalla/runner_build_config.sh 8001 +fi From 629f0dd3ab99c862d407f9b3ff166f86d563cd6d Mon Sep 17 00:00:00 2001 From: Nils Date: Sun, 5 May 2024 18:34:38 +0200 Subject: [PATCH 079/198] always save vcpkg cache (#4719) --- .github/workflows/win.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index e3755c8df1..8de02d329c 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -109,8 +109,7 @@ jobs: -DPREFER_EXTERNAL_DEPS=ON \ -DENABLE_GDAL=ON - - if: ${{ steps.vcpkg-restore.outputs.cache-hit != 'true' }} - name: Save vcpkg packages (if cache miss) + - name: Save vcpkg packages (always, to avoid redundant rebuilds) uses: actions/cache/save@v3 with: key: ${{ steps.vcpkg-restore.outputs.cache-primary-key }} From 1594fe9d386199439343cd6b3db519d043a76f80 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Tue, 7 May 2024 21:50:35 +0200 Subject: [PATCH 080/198] Remove tile_extract from config when creating stats (#4722) --- CHANGELOG.md | 1 + src/mjolnir/valhalla_add_predicted_traffic.cc | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c90dd9fb9..fecd90b25b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ * FIXED: Fix segfault in costmatrix (date_time and time zone always added). [#4530](https://github.com/valhalla/valhalla/pull/4530) * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) * FIXED: Fix for assigning attributes has_(highway, ferry, toll) if directions_type is none [#4465](https://github.com/valhalla/valhalla/issues/4465) + * FIXED: Have the `valhalla_add_predicted_speeds` summary always be created from `mjolnir.tile_dir` [#4722](https://github.com/valhalla/valhalla/pull/4722) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/mjolnir/valhalla_add_predicted_traffic.cc b/src/mjolnir/valhalla_add_predicted_traffic.cc index 0df2e1b106..f415c0aed2 100644 --- a/src/mjolnir/valhalla_add_predicted_traffic.cc +++ b/src/mjolnir/valhalla_add_predicted_traffic.cc @@ -351,6 +351,9 @@ int main(int argc, char** argv) { if (!summary) return EXIT_SUCCESS; + // don't use the .tar for stats + config.get_child("mjolnir").erase("tile_extract"); + GraphReader reader(config.get_child("mjolnir")); // Iterate through the tiles int shortcuts_with_speed = 0; From 76f316c080266ed8f32ac123f717cbd315a07261 Mon Sep 17 00:00:00 2001 From: Nils Date: Wed, 8 May 2024 16:30:11 +0200 Subject: [PATCH 081/198] add error when we fail to find any connection in matrix (#4718) --- CHANGELOG.md | 1 + src/tyr/matrix_serializer.cc | 7 ++++++- test/gurka/test_matrix.cc | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fecd90b25b..b4d62e9cca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -118,6 +118,7 @@ * CHANGED: change costmatrix max_distance threshold to a distance threshold instead of duration [#4672](https://github.com/valhalla/valhalla/pull/4672) * ADDED: PBF support for expansion [#4614](https://github.com/valhalla/valhalla/pull/4614/) * ADDED: elapsed_cost field to map matching json response [#4709](https://github.com/valhalla/valhalla/pull/4709) + * ADDED: error if we fail to find any matrix connection [#4718](https://github.com/valhalla/valhalla/pull/4718) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/tyr/matrix_serializer.cc b/src/tyr/matrix_serializer.cc index e9009e8a52..73cffd2465 100644 --- a/src/tyr/matrix_serializer.cc +++ b/src/tyr/matrix_serializer.cc @@ -243,9 +243,14 @@ namespace tyr { std::string serializeMatrix(Api& request) { double distance_scale = (request.options().units() == Options::miles) ? kMilePerMeter : kKmPerMeter; + // error if we failed finding any connection // dont bother serializing in case of /expansion request - if (request.options().action() == Options_Action_expansion) + if (std::all_of(request.matrix().times().begin(), request.matrix().times().end(), + [](const float& time) { return time == kMaxCost; })) { + throw valhalla_exception_t(442); + } else if (request.options().action() == Options_Action_expansion) { return ""; + } switch (request.options().format()) { case Options_Format_osrm: diff --git a/test/gurka/test_matrix.cc b/test/gurka/test_matrix.cc index 960841ebdf..086c66e460 100644 --- a/test/gurka/test_matrix.cc +++ b/test/gurka/test_matrix.cc @@ -399,6 +399,12 @@ TEST(StandAlone, CostMatrixDeadends) { res_doc.Parse(res.c_str()); check_matrix(res_doc, {0.8f}, false, Matrix::CostMatrix); } + + // throw if no connection can be found at all + try { + auto result = gurka::do_action(valhalla::Options::sources_to_targets, map, {"C"}, {"A"}, "auto"); + FAIL() << "No connection found should have thrown"; + } catch (const valhalla_exception_t& e) { EXPECT_EQ(e.code, 442); } } TEST(StandAlone, CostMatrixShapes) { From 6fbe96ef28fdea1118972282c5eea9d8a777b281 Mon Sep 17 00:00:00 2001 From: Nils Date: Wed, 8 May 2024 18:41:37 +0200 Subject: [PATCH 082/198] auto-update the fossgis instances (#4717) Co-authored-by: Christian <58629404+chrstnbwnkl@users.noreply.github.com> --- .github/workflows/update_public_servers.yml | 6 +++--- scripts/update_public_server.sh | 9 ++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/update_public_servers.yml b/.github/workflows/update_public_servers.yml index 8bf122c66e..c17eb27c61 100644 --- a/.github/workflows/update_public_servers.yml +++ b/.github/workflows/update_public_servers.yml @@ -1,9 +1,9 @@ -name: Update public FOSSGIS servers +name: Update public FOSSGIS servers on: push: branches: - - nn-update-fossgis-gha + - master paths: - .github/workflows - 'src' @@ -27,6 +27,6 @@ jobs: eval $(ssh-agent -s) ssh-add - <<< "${{ secrets.SSH_SECRET }}" # first the build server - ssh -p 23432 -o StrictHostKeyChecking=no valhalla@162.55.2.221 'sudo bash -s builder' < scripts/update_public_server.sh + ssh -p 23432 -o StrictHostKeyChecking=no valhalla@162.55.103.19 'sudo bash -s builder' < scripts/update_public_server.sh # then the services ssh -p 23432 -o StrictHostKeyChecking=no valhalla@162.55.2.221 'sudo bash -s service' < scripts/update_public_server.sh diff --git a/scripts/update_public_server.sh b/scripts/update_public_server.sh index 08b43bcd2e..9777434f46 100644 --- a/scripts/update_public_server.sh +++ b/scripts/update_public_server.sh @@ -1,12 +1,19 @@ # updates Valhalla on the FOSSGIS servers +# accepts one argument, either "builder" or "service" set -e server=$1 src_dir="/src/valhalla" +git config --global --add safe.directory /src/valhalla + git -C "${src_dir}" checkout master git -C "${src_dir}" pull +git submodule update --init --recursive + +# remove the build folder first +rm -r "${src_dir}"/build if [[ $server == "builder" ]]; then cmake -S "${src_dir}" -B "${src_dir}/build" \ @@ -19,7 +26,7 @@ if [[ $server == "builder" ]]; then -DENABLE_GDAL=OFF sudo make -C "${src_dir}/build" -j$(nproc) install - # config is updated by + # config is updated by the build script on the server else cmake -S "${src_dir}" -B "${src_dir}/build" \ -DENABLE_DATA_TOOLS=OFF \ From 26cfae4afb42814fc0cbd9c3cb83c4ff1910cb25 Mon Sep 17 00:00:00 2001 From: Nils Date: Fri, 10 May 2024 11:06:49 +0200 Subject: [PATCH 083/198] add some error communication & handling to `valhalla_ingest_transit` (#4710) Co-authored-by: Christian <58629404+chrstnbwnkl@users.noreply.github.com> --- CHANGELOG.md | 1 + scripts/valhalla_build_config | 2 +- src/mjolnir/ingest_transit.cc | 14 +++++++++++++- src/mjolnir/valhalla_ingest_transit.cc | 19 ++++++++++++------- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4d62e9cca..a18289b459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -119,6 +119,7 @@ * ADDED: PBF support for expansion [#4614](https://github.com/valhalla/valhalla/pull/4614/) * ADDED: elapsed_cost field to map matching json response [#4709](https://github.com/valhalla/valhalla/pull/4709) * ADDED: error if we fail to find any matrix connection [#4718](https://github.com/valhalla/valhalla/pull/4718) + * ADDED: Fail early in valhalla_ingest_transit if there's no valid GTFS feeds [#4710](https://github.com/valhalla/valhalla/pull/4710/) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/scripts/valhalla_build_config b/scripts/valhalla_build_config index 651e0d2a24..20366ecea4 100755 --- a/scripts/valhalla_build_config +++ b/scripts/valhalla_build_config @@ -304,7 +304,7 @@ help_text = { 'landmarks': 'Location of sqlite file holding landmark POI created with valhalla_build_landmarks', 'timezone': 'Location of sqlite file holding timezone information created with valhalla_build_timezones', 'transit_dir': 'Location of intermediate transit tiles created with valhalla_build_transit', - 'transit_feeds_dir': 'Location of GTFS transit feeds', + 'transit_feeds_dir': 'Location of all GTFS transit feeds, needs to contain one subdirectory per feed', 'transit_bounding_box': 'Add comma separated bounding box values to only download transit data inside the given bounding box', 'transit_pbf_limit': 'Limit individual PBF files to this many trips (needed for PBF\'s stupid size limit)', 'hierarchy': 'bool indicating whether road hierarchy is to be built - default to True', diff --git a/src/mjolnir/ingest_transit.cc b/src/mjolnir/ingest_transit.cc index db6000f307..c03d0e20e4 100644 --- a/src/mjolnir/ingest_transit.cc +++ b/src/mjolnir/ingest_transit.cc @@ -169,13 +169,22 @@ std::priority_queue select_transit_tiles(const std::string& filesystem::recursive_directory_iterator end_file_itr; for (; gtfs_feed_itr != end_file_itr; ++gtfs_feed_itr) { const auto& feed_path = gtfs_feed_itr->path(); + if (gtfs_feed_itr->is_directory() && filesystem::is_empty(feed_path)) { + LOG_ERROR("Feed directory " + feed_path.string() + " is empty"); + continue; + } if (filesystem::is_directory(feed_path)) { // feed_path has a trailing separator const auto feed_name = feed_path.filename().string(); LOG_INFO("Loading " + feed_name); gtfs::Feed feed(feed_path.string()); - feed.read_feed(); + auto read_result = feed.read_feed(); + if (read_result.code != gtfs::ResultCode::OK) { + LOG_ERROR("Couldn't find a required file for feed " + feed_path.filename().string() + ": " + + read_result.message); + continue; + } LOG_INFO("Done loading, now parsing " + feed_name); const auto& stops = feed.get_stops(); @@ -874,6 +883,9 @@ std::list ingest_transit(const boost::property_tree::ptree& pt) { // go get information about what transit tiles we should be fetching LOG_INFO("Tiling GTFS Feeds"); auto tiles = select_transit_tiles(gtfs_dir); + if (tiles.empty()) { + throw std::runtime_error("Couldn't find any usable GTFS feeds."); + } LOG_INFO("Writing " + std::to_string(tiles.size()) + " transit pbf tiles with " + std::to_string(thread_count) + " threads..."); diff --git a/src/mjolnir/valhalla_ingest_transit.cc b/src/mjolnir/valhalla_ingest_transit.cc index bc6ee033e2..9180d25df0 100644 --- a/src/mjolnir/valhalla_ingest_transit.cc +++ b/src/mjolnir/valhalla_ingest_transit.cc @@ -37,12 +37,17 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - // spawn threads to download all the tiles returning a list of - // tiles that ended up having dangling stop pairs - auto dangling_tiles = valhalla::mjolnir::ingest_transit(config); - - // spawn threads to connect dangling stop pairs to adjacent tiles' stops - valhalla::mjolnir::stitch_transit(config, dangling_tiles); + try { + // spawn threads to download all the tiles returning a list of + // tiles that ended up having dangling stop pairs + auto dangling_tiles = valhalla::mjolnir::ingest_transit(config); + + // spawn threads to connect dangling stop pairs to adjacent tiles' stops + valhalla::mjolnir::stitch_transit(config, dangling_tiles); + } catch (const std::runtime_error& e) { + LOG_ERROR(e.what()); + return EXIT_FAILURE; + } - return 0; + return EXIT_SUCCESS; } From 3463f1a936fb5e2c7239087cd5418082a87dbf2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Hrab=C3=A1nek?= Date: Mon, 13 May 2024 15:26:02 +0200 Subject: [PATCH 084/198] Fix inconsistency in graph.lua for motor_vehicle_node (#4724) Co-authored-by: Christian <58629404+chrstnbwnkl@users.noreply.github.com> --- CHANGELOG.md | 1 + lua/graph.lua | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a18289b459..cd6bb143c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ * FIXED: Fixed roundoff issue in Tiles Row and Col methods [#4585](https://github.com/valhalla/valhalla/pull/4585) * FIXED: Fix for assigning attributes has_(highway, ferry, toll) if directions_type is none [#4465](https://github.com/valhalla/valhalla/issues/4465) * FIXED: Have the `valhalla_add_predicted_speeds` summary always be created from `mjolnir.tile_dir` [#4722](https://github.com/valhalla/valhalla/pull/4722) + * FIXED: Fix inconsistency in graph.lua for motor_vehicle_node [#4723](https://github.com/valhalla/valhalla/issues/4723) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/lua/graph.lua b/lua/graph.lua index 1378d4e1df..886988c59b 100644 --- a/lua/graph.lua +++ b/lua/graph.lua @@ -435,7 +435,7 @@ motor_vehicle_node = { ["forestry"] = 0, ["destination"] = 1, ["customers"] = 1, -["official"] = 0, +["official"] = 1, ["public"] = 1, ["restricted"] = 1, ["allowed"] = 1, From c14d3ac8e4745a17e5e99a50dac32c838fc3475b Mon Sep 17 00:00:00 2001 From: Martin Schlossarek <33715215+martin-schlossarek@users.noreply.github.com> Date: Tue, 14 May 2024 15:40:11 +0200 Subject: [PATCH 085/198] fix echo statement for linux/macos in building.md (#4730) Co-authored-by: Martin Schlossarek --- docs/docs/building.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/building.md b/docs/docs/building.md index d3c65d66c4..48879841f6 100644 --- a/docs/docs/building.md +++ b/docs/docs/building.md @@ -42,9 +42,9 @@ git clone https://github.com/microsoft/vcpkg && git -C vcpkg checkout ./vcpkg/bootstrap-vcpkg.sh # windows: cmd.exe /c bootstrap-vcpkg.bat # only build Release versions of dependencies, not Debug -echo "VCPKG_BUILD_TYPE release" >> vcpkg/triplets/x64-linux.cmake +echo "set(VCPKG_BUILD_TYPE release)" >> vcpkg/triplets/x64-linux.cmake # windows: echo.set(VCPKG_BUILD_TYPE release)>> .\vcpkg\triplets\x64-windows.cmake -# osx: echo "VCPKG_BUILD_TYPE release" >> vcpkg/triplets/arm64-osx.cmake +# osx: echo "set(VCPKG_BUILD_TYPE release)" >> vcpkg/triplets/arm64-osx.cmake # vcpkg will install everything during cmake configuration # if you want to ENABLE_SERVICES=ON, install https://github.com/kevinkreiser/prime_server#build-and-install (no Windows) From 78c170157903da192d20a88c0a8392e4ac2a55bd Mon Sep 17 00:00:00 2001 From: Trietes <33934221+Trietes@users.noreply.github.com> Date: Tue, 4 Jun 2024 16:22:21 +0200 Subject: [PATCH 086/198] Removed voice & banner instructions from last step and added ssmlAnnouncements (#4644) Co-authored-by: Christian <58629404+chrstnbwnkl@users.noreply.github.com> --- CHANGELOG.md | 1 + src/tyr/route_serializer_osrm.cc | 23 +++++++++++++++----- test/gurka/test_osrm_serializer.cc | 34 ++++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd6bb143c4..e75845b98b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,6 +121,7 @@ * ADDED: elapsed_cost field to map matching json response [#4709](https://github.com/valhalla/valhalla/pull/4709) * ADDED: error if we fail to find any matrix connection [#4718](https://github.com/valhalla/valhalla/pull/4718) * ADDED: Fail early in valhalla_ingest_transit if there's no valid GTFS feeds [#4710](https://github.com/valhalla/valhalla/pull/4710/) + * ADDED: Added ssmlAnnouncements for voice instructions and removed voice and banner instructions from last step. [#4644](https://github.com/valhalla/valhalla/pull/4644) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/tyr/route_serializer_osrm.cc b/src/tyr/route_serializer_osrm.cc index dcef6ca96c..94e00b6bf7 100644 --- a/src/tyr/route_serializer_osrm.cc +++ b/src/tyr/route_serializer_osrm.cc @@ -1513,6 +1513,10 @@ json::ArrayPtr voice_instructions(const valhalla::DirectionsLeg::Maneuver* prev_ voice_instruction_start->emplace("distanceAlongGeometry", json::fixed_t{distance, 1}); voice_instruction_start->emplace("announcement", prev_maneuver->verbal_pre_transition_instruction()); + voice_instruction_start->emplace("ssmlAnnouncement", + "" + + prev_maneuver->verbal_pre_transition_instruction() + + ""); voice_instructions_array->emplace_back(std::move(voice_instruction_start)); } else if (distance > distance_before_verbal_transition_alert_instruction + APPROXIMATE_VERBAL_POSTRANSITION_LENGTH && @@ -1527,6 +1531,10 @@ json::ArrayPtr voice_instructions(const valhalla::DirectionsLeg::Maneuver* prev_ voice_instruction_beginning->emplace("distanceAlongGeometry", json::fixed_t{distance - 10, 1}); voice_instruction_beginning->emplace("announcement", prev_maneuver->verbal_post_transition_instruction()); + voice_instruction_beginning->emplace("ssmlAnnouncement", + "" + + prev_maneuver->verbal_post_transition_instruction() + + ""); voice_instructions_array->emplace_back(std::move(voice_instruction_beginning)); } } @@ -1545,6 +1553,9 @@ json::ArrayPtr voice_instructions(const valhalla::DirectionsLeg::Maneuver* prev_ json::fixed_t{distance_before_verbal_transition_alert_instruction, 1}); } voice_instruction_end->emplace("announcement", maneuver.verbal_transition_alert_instruction()); + voice_instruction_end->emplace("ssmlAnnouncement", + "" + maneuver.verbal_transition_alert_instruction() + + ""); voice_instructions_array->emplace_back(std::move(voice_instruction_end)); } @@ -1563,6 +1574,9 @@ json::ArrayPtr voice_instructions(const valhalla::DirectionsLeg::Maneuver* prev_ 1}); } voice_instruction_end->emplace("announcement", maneuver.verbal_pre_transition_instruction()); + voice_instruction_end->emplace("ssmlAnnouncement", + "" + maneuver.verbal_pre_transition_instruction() + + ""); voice_instructions_array->emplace_back(std::move(voice_instruction_end)); } @@ -1839,9 +1853,8 @@ json::ArrayPtr serialize_legs(const google::protobuf::RepeatedPtrFieldemplace("bannerInstructions", - banner_instructions(name, dest, ref, prev_maneuver, maneuver, arrive_maneuver, - &etp, mnvr_type, modifier, ex, distance, drive_side)); + // just add empty array for arrival maneuver + step->emplace("bannerInstructions", json::array({})); } } @@ -1853,8 +1866,8 @@ json::ArrayPtr serialize_legs(const google::protobuf::RepeatedPtrFieldemplace("voiceInstructions", - voice_instructions(prev_maneuver, maneuver, distance, maneuver_index, &etp)); + // just add empty array for arrival maneuver + step->emplace("voiceInstructions", json::array({})); } } diff --git a/test/gurka/test_osrm_serializer.cc b/test/gurka/test_osrm_serializer.cc index e7b3461a46..eb61f06a64 100644 --- a/test/gurka/test_osrm_serializer.cc +++ b/test/gurka/test_osrm_serializer.cc @@ -571,17 +571,24 @@ gurka::map VoiceInstructions::map = {}; TEST_F(VoiceInstructions, VoiceInstructionsPresent) { auto json = json_request("A", "F"); auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); - // Validate that each step has voiceInstructions with announcement and distanceAlongGeometry - for (int step = 0; step < steps.Size(); ++step) { + // Validate that each step (except the last one) has voiceInstructions with announcement, + // ssmlAnnouncement and distanceAlongGeometry + for (int step = 0; step < steps.Size() - 1; ++step) { ASSERT_TRUE(steps[step].HasMember("voiceInstructions")); ASSERT_TRUE(steps[step]["voiceInstructions"].IsArray()); EXPECT_GT(steps[step]["voiceInstructions"].Size(), 0); for (int instr = 0; instr < steps[step]["voiceInstructions"].GetArray().Size(); ++instr) { ASSERT_TRUE(steps[step]["voiceInstructions"][instr].HasMember("announcement")); + ASSERT_TRUE(steps[step]["voiceInstructions"][instr].HasMember("ssmlAnnouncement")); ASSERT_TRUE(steps[step]["voiceInstructions"][instr].HasMember("distanceAlongGeometry")); } } + + // Validate the last step as empty voiceInstructions + ASSERT_TRUE(steps[steps.Size() - 1].HasMember("voiceInstructions")); + ASSERT_TRUE(steps[steps.Size() - 1]["voiceInstructions"].IsArray()); + EXPECT_EQ(steps[steps.Size() - 1]["voiceInstructions"].Size(), 0); } // depart_instruction @@ -695,9 +702,8 @@ TEST_F(VoiceInstructions, AllVoiceInstructions) { EXPECT_GT(final_arrive_instruction["distanceAlongGeometry"].GetFloat(), 41); EXPECT_LT(final_arrive_instruction["distanceAlongGeometry"].GetFloat(), 43); - auto last_instruction = steps[2]["voiceInstructions"][0].GetObject(); - EXPECT_STREQ(last_instruction["announcement"].GetString(), "You will arrive at your destination."); - EXPECT_EQ(last_instruction["distanceAlongGeometry"].GetFloat(), 0.0); + auto last_instruction = steps[2]["voiceInstructions"].GetArray(); + EXPECT_EQ(last_instruction.Size(), 0); } TEST(Standalone, BannerInstructions) { @@ -749,8 +755,8 @@ TEST(Standalone, BannerInstructions) { auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); - // Validate that each step has bannerInstructions with primary - for (int step = 0; step < steps.Size(); ++step) { + // Validate that each step (except the last one) has bannerInstructions with primary + for (int step = 0; step < steps.Size() - 1; ++step) { ASSERT_TRUE(steps[step].HasMember("bannerInstructions")); ASSERT_TRUE(steps[step]["bannerInstructions"].IsArray()); EXPECT_GT(steps[step]["bannerInstructions"].GetArray().Size(), 0); @@ -762,6 +768,11 @@ TEST(Standalone, BannerInstructions) { ASSERT_TRUE(steps[step]["bannerInstructions"][instr]["primary"].HasMember("components")); ASSERT_TRUE(steps[step]["bannerInstructions"][instr]["primary"]["components"].IsArray()); } + + // Validate the last step has empty bannerInstructions + ASSERT_TRUE(steps[steps.Size() - 1].HasMember("bannerInstructions")); + ASSERT_TRUE(steps[steps.Size() - 1]["bannerInstructions"].IsArray()); + EXPECT_EQ(steps[steps.Size() - 1]["bannerInstructions"].GetArray().Size(), 0); } // validate first step has two bannerInstruction @@ -957,8 +968,8 @@ TEST(Standalone, BannerInstructionsRoundabout) { auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); - // Validate that each step has bannerInstructions with primary - for (int step = 0; step < steps.Size(); ++step) { + // Validate that each step (except the last one) has bannerInstructions with primary + for (int step = 0; step < steps.Size() - 1; ++step) { ASSERT_TRUE(steps[step].HasMember("bannerInstructions")); ASSERT_TRUE(steps[step]["bannerInstructions"].IsArray()); EXPECT_GT(steps[step]["bannerInstructions"].GetArray().Size(), 0); @@ -972,6 +983,11 @@ TEST(Standalone, BannerInstructionsRoundabout) { } } + // Validate the last step has empty bannerInstructions + ASSERT_TRUE(steps[steps.Size() - 1].HasMember("bannerInstructions")); + ASSERT_TRUE(steps[steps.Size() - 1]["bannerInstructions"].IsArray()); + EXPECT_EQ(steps[steps.Size() - 1]["bannerInstructions"].GetArray().Size(), 0); + // validate first step's primary instructions auto primary_0 = steps[0]["bannerInstructions"][0]["primary"].GetObject(); EXPECT_STREQ(primary_0["type"].GetString(), "rotary"); From 4f6ef0595dadf530bc88ae7721af2d010e3de34e Mon Sep 17 00:00:00 2001 From: Eike Send Date: Wed, 5 Jun 2024 14:49:29 +0200 Subject: [PATCH 087/198] Adds documentation for `ignore_oneways` option (#4743) --- docs/docs/api/turn-by-turn/api-reference.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index b50f85cb91..2c1c1199fc 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -133,6 +133,7 @@ These options are available for `auto`, `bus`, and `truck` costing methods. | `ignore_closures` | If set to `true`, ignores all closures, marked due to live traffic closures, during routing. **Note:** This option cannot be set if `location.search_filter.exclude_closures` is also specified in the request and will return an error if it is | | `closure_factor` | A factor that penalizes the cost when traversing a closed edge (eg: if `search_filter.exclude_closures` is `false` for origin and/or destination location and the route starts/ends on closed edges). Its value can range from `1.0` - don't penalize closed edges, to `10.0` - apply high cost penalty to closed edges. Default value is `9.0`. **Note:** This factor is applicable only for motorized modes of transport, i.e `auto`, `motorcycle`, `motor_scooter`, `bus`, `truck` & `taxi`. | | `ignore_restrictions` | If set to `true`, ignores any restrictions (e.g. turn/dimensional/conditional restrictions). Especially useful for matching GPS traces to the road network regardless of restrictions. Default is `false`. | +| `ignore_oneways` | If set to `true`, ignores one-way restrictions. Especially useful for matching GPS traces to the road network ignoring uni-directional traffic rules. Not included in `ignore_restrictions` option. Default is `false`. | | `ignore_non_vehicular_restrictions` | Similar to `ignore_restrictions`, but will respect restrictions that impact vehicle safety, such as weight and size restrictions. | | `ignore_access` | Will ignore mode-specific access tags. Especially useful for matching GPS traces to the road network regardless of restrictions. Default is `false`. | | `ignore_closures` | Will ignore traffic closures. Default is `false`. | From 48b339bacc18a221471307bf7ec631ac0ac9714a Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Wed, 5 Jun 2024 18:20:23 +0200 Subject: [PATCH 088/198] add deadend to directededge json (#4751) --- CHANGELOG.md | 1 + src/baldr/directededge.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e75845b98b..cdbc55ba8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -122,6 +122,7 @@ * ADDED: error if we fail to find any matrix connection [#4718](https://github.com/valhalla/valhalla/pull/4718) * ADDED: Fail early in valhalla_ingest_transit if there's no valid GTFS feeds [#4710](https://github.com/valhalla/valhalla/pull/4710/) * ADDED: Added ssmlAnnouncements for voice instructions and removed voice and banner instructions from last step. [#4644](https://github.com/valhalla/valhalla/pull/4644) + * ADDED: deadend information in directed edge JSON for `/locate` [#4751](https://github.com/valhalla/valhalla/pull/4751) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/baldr/directededge.cc b/src/baldr/directededge.cc index cef1ed3e04..6c7f28f2ff 100644 --- a/src/baldr/directededge.cc +++ b/src/baldr/directededge.cc @@ -626,6 +626,7 @@ json::MapPtr DirectedEdge::json() const { {"sidewalk_left", static_cast(sidewalk_left_)}, {"sidewalk_right", static_cast(sidewalk_right_)}, {"sac_scale", to_string(static_cast(sac_scale_))}, + {"deadend", static_cast(deadend_)}, {"geo_attributes", json::map({ {"length", static_cast(length_)}, From 950b4c48f3320ea9f6d59cda54fed780aa5b9945 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:12:55 +0200 Subject: [PATCH 089/198] Add missing include in `baldr/admin.h` (#4766) --- CHANGELOG.md | 1 + src/baldr/admin.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cdbc55ba8c..98e42387ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ * FIXED: Fix for assigning attributes has_(highway, ferry, toll) if directions_type is none [#4465](https://github.com/valhalla/valhalla/issues/4465) * FIXED: Have the `valhalla_add_predicted_speeds` summary always be created from `mjolnir.tile_dir` [#4722](https://github.com/valhalla/valhalla/pull/4722) * FIXED: Fix inconsistency in graph.lua for motor_vehicle_node [#4723](https://github.com/valhalla/valhalla/issues/4723) + * FIXED: Missing algorithm include in `baldr/admin.h` [#4766](https://github.com/valhalla/valhalla/pull/4766) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/src/baldr/admin.cc b/src/baldr/admin.cc index 704193e9b8..c301a414d8 100644 --- a/src/baldr/admin.cc +++ b/src/baldr/admin.cc @@ -1,4 +1,5 @@ #include "baldr/admin.h" +#include namespace { From 2985c08c0e14017febeb50621afb0214fe79317a Mon Sep 17 00:00:00 2001 From: David Nesbitt Date: Wed, 26 Jun 2024 14:31:40 -0400 Subject: [PATCH 090/198] Fix old code that allows bicycle access on footway due to sac_scale=hiking (#4781) --- CHANGELOG.md | 1 + lua/graph.lua | 9 --------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98e42387ed..0f32cdbad3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -124,6 +124,7 @@ * ADDED: Fail early in valhalla_ingest_transit if there's no valid GTFS feeds [#4710](https://github.com/valhalla/valhalla/pull/4710/) * ADDED: Added ssmlAnnouncements for voice instructions and removed voice and banner instructions from last step. [#4644](https://github.com/valhalla/valhalla/pull/4644) * ADDED: deadend information in directed edge JSON for `/locate` [#4751](https://github.com/valhalla/valhalla/pull/4751) + * FIXED: remove old code that allows bicycle access on hiking trails. [#4781](https://github.com/valhalla/valhalla/pull/4781) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/lua/graph.lua b/lua/graph.lua index 886988c59b..1587725c5b 100644 --- a/lua/graph.lua +++ b/lua/graph.lua @@ -976,15 +976,6 @@ function filter_tags_generic(kv) kv["motorcycle_forward"] = motor_vehicle[kv["motorcycle"]] or motor_vehicle[kv["motor_vehicle"]] or kv["motorcycle_forward"] kv["motorcycle_tag"] = motor_vehicle[kv["motorcycle"]] or motor_vehicle[kv["motor_vehicle"]] or nil - if kv["bike_tag"] == nil then - if kv["sac_scale"] == "hiking" then - kv["bike_forward"] = "true" - kv["bike_tag"] = "true" - elseif kv["sac_scale"] then - kv["bike_forward"] = "false" - end - end - if kv["access"] == "psv" then kv["taxi_forward"] = "true" kv["taxi_tag"] = "true" From 7be6e23712ae06cb8fc7a41462529f10e662d7b4 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Wed, 26 Jun 2024 22:54:46 +0200 Subject: [PATCH 091/198] log warning in case of attempted multi-file graph build (#4782) --- src/mjolnir/util.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/mjolnir/util.cc b/src/mjolnir/util.cc index e1e250a948..2960778d47 100644 --- a/src/mjolnir/util.cc +++ b/src/mjolnir/util.cc @@ -230,6 +230,11 @@ bool build_tile_set(const boost::property_tree::ptree& original_config, } }; + if (input_files.size() > 1) { + LOG_WARN( + "Tile building using more than one osm.pbf extract is discouraged. Consider merging the extracts into one file. See this issue for more info: https://github.com/valhalla/valhalla/issues/3925 "); + } + // Take out tile_extract and tile_url from property tree as tiles must only use the tile_dir auto config = original_config; config.get_child("mjolnir").erase("tile_extract"); From 7725f8a4c41c18bcf98a1131241c2174ea5e4e47 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Fri, 28 Jun 2024 21:29:14 +0900 Subject: [PATCH 092/198] Fix misleading map matching documentation (#4785) --- docs/docs/api/map-matching/api-reference.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/docs/api/map-matching/api-reference.md b/docs/docs/api/map-matching/api-reference.md index 1346231498..c0d9c895e3 100644 --- a/docs/docs/api/map-matching/api-reference.md +++ b/docs/docs/api/map-matching/api-reference.md @@ -39,9 +39,7 @@ Note that the attributes that are returned are Valhalla routing attributes, not ### Costing models and other options -Valhalla Map Matching uses the `auto`, `auto_shorter`, `bicycle`, `bus`, and `pedestrian` costing models available in the Valhalla route service. Refer to the [route costing models](../turn-by-turn/api-reference.md#costing-models) and [costing options](../turn-by-turn/api-reference.md#costing-options) documentation for more on how to specify this input. - -Costing for `multimodal` is not supported for map matching because it would be difficult to get favorable GPS traces. +Valhalla Map Matching can use any costing model available in the Valhalla route service except for `multimodal` (it would be difficult to get a GPS trace and detect certain mode changes). Refer to the [route costing models](../turn-by-turn/api-reference.md#costing-models) and [costing options](../turn-by-turn/api-reference.md#costing-options) documentation for more on how to specify this input. You can also set `directions_options` to specify output units, language, and whether or not to return directions in a narrative form. Refer to the [route options](../turn-by-turn/api-reference.md#directions-options) documentation for examples. From f25bbf49e00f0e45d04a35f7d40ba3f85b584960 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:26:22 +0200 Subject: [PATCH 093/198] fix build_elevation script help string (#4786) --- scripts/valhalla_build_elevation | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/valhalla_build_elevation b/scripts/valhalla_build_elevation index 53918d0f80..2b54178295 100755 --- a/scripts/valhalla_build_elevation +++ b/scripts/valhalla_build_elevation @@ -116,7 +116,7 @@ group.add_argument( "-z", "--lz4", help="If set, downloaded files will be recompressed with LZ4. Requires lz4. " - "Requires ~12% more disk space (216GB total) vs gzip. While you pay upfront in extra CPU and space on the " + "Requires ~12%% more disk space (216GB total) vs gzip. While you pay upfront in extra CPU and space on the " "initial download, tiles will be several times faster to decompress vz gzip.", action="store_true", ) From cbb4e984ff287ff559151939df7bf4a277ce430f Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Mon, 1 Jul 2024 19:35:59 +0200 Subject: [PATCH 094/198] Add `--inline-config` arg to `valhalla_build_elevation` (#4787) --- CHANGELOG.md | 1 + scripts/valhalla_build_elevation | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f32cdbad3..fee8943d1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -125,6 +125,7 @@ * ADDED: Added ssmlAnnouncements for voice instructions and removed voice and banner instructions from last step. [#4644](https://github.com/valhalla/valhalla/pull/4644) * ADDED: deadend information in directed edge JSON for `/locate` [#4751](https://github.com/valhalla/valhalla/pull/4751) * FIXED: remove old code that allows bicycle access on hiking trails. [#4781](https://github.com/valhalla/valhalla/pull/4781) + * ADDED: inline config arg for `valhalla_build_elevation` script [#4787](https://github.com/valhalla/valhalla/pull/4787) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/scripts/valhalla_build_elevation b/scripts/valhalla_build_elevation index 2b54178295..5f219e0001 100755 --- a/scripts/valhalla_build_elevation +++ b/scripts/valhalla_build_elevation @@ -88,6 +88,13 @@ parser.add_argument( "If present, can be used for download location and bbox.", type=Path, ) +parser.add_argument( + "-i", + "--inline-config", + help="Inline JSON config, will override --config JSON if present", + type=str, + default='{}', +) parser.add_argument( "-o", "--outdir", @@ -322,14 +329,17 @@ if __name__ == "__main__": elif args.verbosity >= 2: LOGGER.setLevel(logging.DEBUG) - config = None + config = dict() if args.config: with open(args.config) as f: config = json.load(f) + if args.inline_config: + config.update(**json.loads(args.inline_config)) + if args.outdir: elevation_fp = args.outdir - elif config is not None: + elif config: elevation_fp = Path(config["additional_data"]["elevation"] or "elevation") else: LOGGER.critical("Either config or outdir is required.") @@ -340,7 +350,7 @@ if __name__ == "__main__": elif args.from_bbox: tiles = get_tiles_with_bbox(args.from_bbox) elif args.from_tiles: - if config is None: + if not config: LOGGER.critical("--from-tiles requires a config to be specified.") sys.exit(1) tiles = get_tiles_with_graph(Path(config["mjolnir"]["tile_dir"])) From 45bd34ac362937e45e9f77d4d213fddbc06bf340 Mon Sep 17 00:00:00 2001 From: Eike Send Date: Wed, 10 Jul 2024 14:31:03 +0200 Subject: [PATCH 095/198] OSRM serializer: Add voiceLocale to route if voice_instructions option is set (#4742) --- CHANGELOG.md | 1 + src/tyr/route_serializer_osrm.cc | 5 +++++ test/gurka/test_osrm_serializer.cc | 29 +++++++++++++++++++++++++---- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fee8943d1d..21511a652f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -122,6 +122,7 @@ * ADDED: elapsed_cost field to map matching json response [#4709](https://github.com/valhalla/valhalla/pull/4709) * ADDED: error if we fail to find any matrix connection [#4718](https://github.com/valhalla/valhalla/pull/4718) * ADDED: Fail early in valhalla_ingest_transit if there's no valid GTFS feeds [#4710](https://github.com/valhalla/valhalla/pull/4710/) + * ADDED: Support for `voiceLocale` attribute in OSRM serializer via `voice_instructions` request parameter [#4677](https://github.com/valhalla/valhalla/pull/4742) * ADDED: Added ssmlAnnouncements for voice instructions and removed voice and banner instructions from last step. [#4644](https://github.com/valhalla/valhalla/pull/4644) * ADDED: deadend information in directed edge JSON for `/locate` [#4751](https://github.com/valhalla/valhalla/pull/4751) * FIXED: remove old code that allows bicycle access on hiking trails. [#4781](https://github.com/valhalla/valhalla/pull/4781) diff --git a/src/tyr/route_serializer_osrm.cc b/src/tyr/route_serializer_osrm.cc index 94e00b6bf7..62f4abd7b5 100644 --- a/src/tyr/route_serializer_osrm.cc +++ b/src/tyr/route_serializer_osrm.cc @@ -2126,6 +2126,11 @@ std::string serialize(valhalla::Api& api) { *api.mutable_trip()->mutable_routes(i)->mutable_legs(), imperial, options, controller)); + // Add voice instructions if the user requested them + if (options.voice_instructions()) { + route->emplace("voiceLocale", options.language()); + } + routes->emplace_back(std::move(route)); } diff --git a/test/gurka/test_osrm_serializer.cc b/test/gurka/test_osrm_serializer.cc index eb61f06a64..c1c6822eb4 100644 --- a/test/gurka/test_osrm_serializer.cc +++ b/test/gurka/test_osrm_serializer.cc @@ -553,13 +553,14 @@ class VoiceInstructions : public ::testing::Test { map = gurka::buildtiles(layout, ways, {}, {}, "test/data/osrm_serializer_voice", build_config); } - - rapidjson::Document json_request(const std::string& from, const std::string& to) { + rapidjson::Document json_request(const std::string& from, + const std::string& to, + const std::string& language = "en-US") { const std::string& request = (boost::format( - R"({"locations":[{"lat":%s,"lon":%s},{"lat":%s,"lon":%s}],"costing":"auto","voice_instructions":true})") % + R"({"locations":[{"lat":%s,"lon":%s},{"lat":%s,"lon":%s}],"costing":"auto","voice_instructions":true,"language":"%s"})") % std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % - std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng()) % language) .str(); auto result = gurka::do_action(valhalla::Options::route, map, request); return gurka::convert_to_json(result, Options::Format::Options_Format_osrm); @@ -706,6 +707,26 @@ TEST_F(VoiceInstructions, AllVoiceInstructions) { EXPECT_EQ(last_instruction.Size(), 0); } +TEST_F(VoiceInstructions, DefaultVoiceLocalePresent) { + auto json = json_request("A", "F"); + auto routes = json["routes"].GetArray(); + // Validate that each route has the default voiceLocale + for (int route = 0; route < routes.Size(); ++route) { + ASSERT_TRUE(routes[route].HasMember("voiceLocale")); + EXPECT_STREQ(routes[route]["voiceLocale"].GetString(), "en-US"); + } +} + +TEST_F(VoiceInstructions, VoiceLocalePresent) { + auto json = json_request("A", "F", "de-DE"); + auto routes = json["routes"].GetArray(); + // Validate that each route has the voiceLocale from the options + for (int route = 0; route < routes.Size(); ++route) { + ASSERT_TRUE(routes[route].HasMember("voiceLocale")); + EXPECT_STREQ(routes[route]["voiceLocale"].GetString(), "de-DE"); + } +} + TEST(Standalone, BannerInstructions) { const std::string ascii_map = R"( A-------------1-B---X From bfc346081ad82eb81c0284557617a0be322f37c7 Mon Sep 17 00:00:00 2001 From: Andrew Smyth Date: Fri, 12 Jul 2024 13:13:13 -0700 Subject: [PATCH 096/198] =?UTF-8?q?Handle=20list=20arguments=20with=20narg?= =?UTF-8?q?s=20instead=20of=20list=20type=20when=20overriding=E2=80=A6=20(?= =?UTF-8?q?#4799)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + scripts/valhalla_build_config | 5 ++++- test/scripts/test_valhalla_build_config.py | 15 ++++++++++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21511a652f..ee88552518 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ * FIXED: Have the `valhalla_add_predicted_speeds` summary always be created from `mjolnir.tile_dir` [#4722](https://github.com/valhalla/valhalla/pull/4722) * FIXED: Fix inconsistency in graph.lua for motor_vehicle_node [#4723](https://github.com/valhalla/valhalla/issues/4723) * FIXED: Missing algorithm include in `baldr/admin.h` [#4766](https://github.com/valhalla/valhalla/pull/4766) + * FIXED: Handle list type arguments correctly when overriding config with valhalla_build_config [#4799](https://github.com/valhalla/valhalla/pull/4799) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/scripts/valhalla_build_config b/scripts/valhalla_build_config index 20366ecea4..caea2e863e 100755 --- a/scripts/valhalla_build_config +++ b/scripts/valhalla_build_config @@ -596,7 +596,10 @@ def add_leaf_args( t = type(tree) arg = '--' + path.replace('_', '-').replace('\0', '-') - parser_.add_argument(arg, type=t, help=h, default=tree) + if t == list: + parser_.add_argument(arg, nargs="+", help=h, default=tree) + else: + parser_.add_argument(arg, type=t, help=h, default=tree) leaves_.append(path) diff --git a/test/scripts/test_valhalla_build_config.py b/test/scripts/test_valhalla_build_config.py index 2cc04a98f0..8c2d422227 100644 --- a/test/scripts/test_valhalla_build_config.py +++ b/test/scripts/test_valhalla_build_config.py @@ -45,9 +45,14 @@ def test_add_leaf_types(self): # make sure the parser.add_argument was called with the right stuff while parsing the config for name, default in config.items(): - mock_parser.add_argument.assert_any_call( - f'--{name.replace("_", "-")}', type=value_types[name], help=help_text[name], default=default - ) + if "list" in name: + mock_parser.add_argument.assert_any_call( + f'--{name.replace("_", "-")}', nargs="+", help=help_text[name], default=default + ) + else: + mock_parser.add_argument.assert_any_call( + f'--{name.replace("_", "-")}', type=value_types[name], help=help_text[name], default=default + ) def test_nested_config(self): """tests if we get the values of nested dicts""" @@ -74,12 +79,12 @@ def test_override_config(self): valhalla_build_config.add_leaf_args('', config, leaves, mock_parser, help_text) # create the args mock - args = {'0_bool': True, "0_opt_str": valhalla_build_config.Optional(str), "0_1_list": [3, 4, 5]} + args = {'0_bool': True, "0_opt_str": valhalla_build_config.Optional(str), "0_1_list": ["item1", "item2", "item3"]} valhalla_build_config.override_config(args, leaves, config) # the Optional arg should be removed self.assertEqual(len(config["0"]), 2) - self.assertEqual(config, {"0": {"bool": True, "1": {"list": [3, 4, 5]}}}) + self.assertEqual(config, {"0": {"bool": True, "1": {"list": ["item1", "item2", "item3"]}}}) if __name__ == '__main__': From 38fc9ef12d92ce385488ecfdbba801995123335c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Hrab=C3=A1nek?= Date: Mon, 15 Jul 2024 14:54:13 +0200 Subject: [PATCH 097/198] Improvements to /expansion (#4728) --- CHANGELOG.md | 1 + docs/docs/api/expansion/api-reference.md | 5 +- proto/options.proto | 2 + src/thor/expansion_action.cc | 81 ++++++++++++++++++------ src/worker.cc | 3 + test/gurka/test_expansion.cc | 59 +++++++++++++++-- 6 files changed, 124 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee88552518..ecc0991251 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -126,6 +126,7 @@ * ADDED: Support for `voiceLocale` attribute in OSRM serializer via `voice_instructions` request parameter [#4677](https://github.com/valhalla/valhalla/pull/4742) * ADDED: Added ssmlAnnouncements for voice instructions and removed voice and banner instructions from last step. [#4644](https://github.com/valhalla/valhalla/pull/4644) * ADDED: deadend information in directed edge JSON for `/locate` [#4751](https://github.com/valhalla/valhalla/pull/4751) + * ADDED: Dedupe option for expansion, significantly reducing the response size. [#4601](https://github.com/valhalla/valhalla/issues/4601) * FIXED: remove old code that allows bicycle access on hiking trails. [#4781](https://github.com/valhalla/valhalla/pull/4781) * ADDED: inline config arg for `valhalla_build_elevation` script [#4787](https://github.com/valhalla/valhalla/pull/4787) diff --git a/docs/docs/api/expansion/api-reference.md b/docs/docs/api/expansion/api-reference.md index da9a682eab..0e3351ef61 100644 --- a/docs/docs/api/expansion/api-reference.md +++ b/docs/docs/api/expansion/api-reference.md @@ -14,8 +14,9 @@ Since this service wraps other services, the request format mostly follows the o | Parameter | Description | |:----------------------------------| :------------------------------------ | -| `action` (required) | The service whose expansion should be tracked. Currently one of `route`, `isochrone` or `sources_to_targets`. | -| `skip_opposites` (optional) | If set to `true` the output won't contain an edge's opposing edge. Opposing edges can be thought of as both directions of one road segment. Of the two, we discard the directional edge with higher cost and keep the one with less cost. Default false. | +| `action` (required) | The service whose expansion should be tracked. Currently one of `route`, `isochrone` or `sources_to_targets`. | +| `skip_opposites` (optional) | If set to `true` the output won't contain an edge's opposing edge. Opposing edges can be thought of as both directions of one road segment. Of the two, we discard the directional edge with higher cost and keep the one with less cost. Default `false`. | +| `dedupe` (optional) | If set to `true`, the output will contain each edge only once, significantly reducing the response size. The expansion will keep track of already visited edges and override their properties, ensuring that only the one with higher edge state is returned. Default `false`. | | `expansion_properties` (optional) | A JSON array of strings of the GeoJSON property keys you'd like to have in the response. One or multiple of "duration", "distance", "cost", "edge_id", "pred_edge_id" or "edge_status". **Note**, that each additional property will increase the output size by minimum ~ 10%. By default an empty `properties` object is returned. | The `expansion_properties` choices are as follows: diff --git a/proto/options.proto b/proto/options.proto index e969ca51c1..9759399f92 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -494,4 +494,6 @@ message Options { bool banner_instructions = 55; // Whether to return bannerInstructions in the OSRM serializer response float elevation_interval = 56; // Interval for sampling elevation along the route path. [default = 0.0]; bool voice_instructions = 57; // Whether to return voiceInstructions in the OSRM serializer response + bool dedupe = 58; // Keep track of edges and override their properties during expansion, + // ensuring that each edge appears in the output only once. [default = false] } diff --git a/src/thor/expansion_action.cc b/src/thor/expansion_action.cc index 6d399fb7f0..11b08abb96 100644 --- a/src/thor/expansion_action.cc +++ b/src/thor/expansion_action.cc @@ -1,12 +1,10 @@ -#include "thor/worker.h" - -#include "baldr/json.h" -#include "baldr/rapidjson_utils.h" #include "midgard/constants.h" #include "midgard/logging.h" #include "midgard/polyline2.h" #include "midgard/util.h" +#include "thor/worker.h" #include "tyr/serializers.h" +#include using namespace rapidjson; using namespace valhalla::midgard; @@ -52,6 +50,31 @@ void writeExpansionProgress(Expansion* expansion, if (exp_props.count(Options_ExpansionProperties_pred_edge_id)) expansion->add_pred_edge_id(static_cast(prev_edgeid)); } + +struct expansion_properties_t { + baldr::GraphId prev_edgeid; + // highest status the edge has seen + Expansion_EdgeStatus status; + float duration; + std::vector shape; + uint32_t distance; + float cost; + + expansion_properties_t() = default; + expansion_properties_t(baldr::GraphId prev_edgeid, + Expansion_EdgeStatus status, + float duration, + uint32_t distance, + std::vector&& shape, + float cost) + : prev_edgeid(prev_edgeid), status(status), duration(duration), distance(distance), + shape(std::move(shape)), cost(cost){}; + + // check if status is higher or same – as we will keep track of the latest one + static bool is_latest_status(Expansion_EdgeStatus current, Expansion_EdgeStatus candidate) { + return candidate <= current; + } +}; } // namespace namespace valhalla { @@ -65,8 +88,11 @@ std::string thor_worker_t::expansion(Api& request) { auto options = request.options(); auto exp_action = options.expansion_action(); bool skip_opps = options.skip_opposites(); + bool dedupe = options.dedupe(); std::unordered_set opp_edges; std::unordered_set exp_props; + typedef robin_hood::unordered_map edge_state_t; + edge_state_t edge_state; // default generalization to ~ zoom level 15 float gen_factor = options.has_generalize_case() ? options.generalize() : 10.f; @@ -78,12 +104,11 @@ std::string thor_worker_t::expansion(Api& request) { // a lambda that the path algorithm can call to add stuff to the dom // route and isochrone produce different GeoJSON properties std::string algo = ""; - auto track_expansion = [&expansion, &opp_edges, &gen_factor, &skip_opps, &exp_props, - &algo](baldr::GraphReader& reader, baldr::GraphId edgeid, - baldr::GraphId prev_edgeid, const char* algorithm = nullptr, - const Expansion_EdgeStatus status = Expansion_EdgeStatus_reached, - const float duration = 0.f, const uint32_t distance = 0, - const float cost = 0.f) { + auto track_expansion = [&](baldr::GraphReader& reader, baldr::GraphId edgeid, + baldr::GraphId prev_edgeid, const char* algorithm = nullptr, + const Expansion_EdgeStatus status = Expansion_EdgeStatus_reached, + const float duration = 0.f, const uint32_t distance = 0, + const float cost = 0.f) { algo = algorithm; auto tile = reader.GetGraphTile(edgeid); @@ -92,28 +117,36 @@ std::string thor_worker_t::expansion(Api& request) { std::to_string(edgeid.Tile_Base())); return; } - const auto* edge = tile->directededge(edgeid); - // unfortunately we have to call this before checking if we can skip - // else the tile could change underneath us when we get the opposing - auto shape = tile->edgeinfo(edge).shape(); - auto names = tile->edgeinfo(edge).GetNames(); - auto is_forward = edge->forward(); // if requested, skip this edge in case its opposite edge has been added // before (i.e. lower cost) else add this edge's id to the lookup container if (skip_opps) { - auto opp_edgeid = reader.GetOpposingEdgeId(edgeid, tile); + auto opp_tile = tile; + auto opp_edgeid = reader.GetOpposingEdgeId(edgeid, opp_tile); if (opp_edgeid && opp_edges.count(opp_edgeid)) return; opp_edges.insert(edgeid); } + const auto* edge = tile->directededge(edgeid); + auto shape = tile->edgeinfo(edge).shape(); + if (!edge->forward()) std::reverse(shape.begin(), shape.end()); Polyline2::Generalize(shape, gen_factor, {}, false); - - writeExpansionProgress(expansion, edgeid, prev_edgeid, shape, exp_props, status, duration, - distance, cost); + if (dedupe) { + if (edge_state.contains(edgeid)) { + // Keep only properties of last/highest status of edge + if (!expansion_properties_t::is_latest_status(edge_state.at(edgeid).status, status)) { + return; + } + } + edge_state[edgeid] = + expansion_properties_t(prev_edgeid, status, duration, distance, std::move(shape), cost); + } else { + writeExpansionProgress(expansion, edgeid, prev_edgeid, shape, exp_props, status, duration, + distance, cost); + } }; // tell all the algorithms how to track expansion @@ -146,6 +179,14 @@ std::string thor_worker_t::expansion(Api& request) { // anyway } + // assemble the properties from latest/highest stages it went through + if (dedupe) { + for (const auto& e : edge_state) { + writeExpansionProgress(expansion, e.first, e.second.prev_edgeid, e.second.shape, exp_props, + e.second.status, e.second.duration, e.second.distance, e.second.cost); + } + } + // tell all the algorithms to stop tracking the expansion for (auto* alg : std::vector{&multi_modal_astar, &timedep_forward, &timedep_reverse, &bidir_astar, &bss_astar}) { diff --git a/src/worker.cc b/src/worker.cc index b914dfa1c0..04254e8a66 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -1118,6 +1118,9 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { // should the expansion track opposites? options.set_skip_opposites(rapidjson::get(doc, "/skip_opposites", options.skip_opposites())); + // should the expansion be less verbose, printing each edge only once, default false + options.set_dedupe(rapidjson::get(doc, "/dedupe", options.dedupe())); + // get the contours in there parse_contours(doc, options.mutable_contours()); diff --git a/test/gurka/test_expansion.cc b/test/gurka/test_expansion.cc index 864f4490ac..eeeee4dc8e 100644 --- a/test/gurka/test_expansion.cc +++ b/test/gurka/test_expansion.cc @@ -33,6 +33,7 @@ class ExpansionTest : public ::testing::TestWithParam> valhalla::Api do_expansion_action(std::string* res, bool skip_opps, + bool dedupe, std::string action, const std::vector& props, const std::vector& waypoints, @@ -40,6 +41,7 @@ class ExpansionTest : public ::testing::TestWithParam> std::unordered_map options = {{"/skip_opposites", skip_opps ? "1" : "0"}, {"/action", action}, + {"/dedupe", dedupe ? "1" : "0"}, {"/format", pbf ? "pbf" : "json"}}; for (uint8_t i = 0; i < props.size(); i++) { options.insert({{"/expansion_properties/" + std::to_string(i), props[i]}}); @@ -64,17 +66,19 @@ class ExpansionTest : public ::testing::TestWithParam> const std::vector& waypoints, bool skip_opps, unsigned exp_feats, - const std::vector& props = {}) { - check_result_json(action, waypoints, skip_opps, exp_feats, props); - check_result_pbf(action, waypoints, skip_opps, exp_feats, props); + const std::vector& props = {}, + bool dedupe = false) { + check_result_json(action, waypoints, skip_opps, dedupe, exp_feats, props); + check_result_pbf(action, waypoints, skip_opps, dedupe, exp_feats, props); } void check_result_pbf(const std::string& action, const std::vector& waypoints, bool skip_opps, + bool dedupe, unsigned exp_feats, const std::vector& props) { std::string res; - auto api = do_expansion_action(&res, skip_opps, action, props, waypoints, true); + auto api = do_expansion_action(&res, skip_opps, dedupe, action, props, waypoints, true); Api parsed_api; parsed_api.ParseFromString(res); @@ -120,11 +124,12 @@ class ExpansionTest : public ::testing::TestWithParam> void check_result_json(const std::string& action, const std::vector& waypoints, bool skip_opps, + bool dedupe, unsigned exp_feats, const std::vector& props) { std::string res; - auto api = do_expansion_action(&res, skip_opps, action, props, waypoints, false); + auto api = do_expansion_action(&res, skip_opps, dedupe, action, props, waypoints, false); // get the MultiLineString feature rapidjson::Document res_doc; res_doc.Parse(res.c_str()); @@ -173,6 +178,34 @@ TEST_P(ExpansionTest, MatrixNoOpposites) { check_results("sources_to_targets", {"E", "H"}, true, 23, GetParam()); } +TEST_P(ExpansionTest, IsochroneDedupe) { + // test Dijkstra expansion + // 11 because there's a one-way + check_results("isochrone", {"A"}, false, 11, GetParam(), true); +} +TEST_P(ExpansionTest, IsochroneNoOppositesDedupe) { + // test Dijkstra expansion and skip collecting more expensive opposite edges + check_results("isochrone", {"A"}, true, 6, GetParam(), true); +} + +TEST_P(ExpansionTest, RoutingDedupe) { + // test AStar expansion + check_results("route", {"E", "H"}, false, 7, GetParam(), true); +} + +TEST_P(ExpansionTest, RoutingNoOppositesDedupe) { + // test AStar expansion and no opposite edges + check_results("route", {"E", "H"}, true, 5, GetParam(), true); +} + +TEST_P(ExpansionTest, MatrixDedupe) { + check_results("sources_to_targets", {"E", "H"}, false, 11, GetParam(), true); +} + +TEST_P(ExpansionTest, MatrixNoOppositesDedupe) { + check_results("sources_to_targets", {"E", "H"}, true, 6, GetParam(), true); +} + TEST_F(ExpansionTest, UnsupportedAction) { try { check_results("status", {"E", "H"}, true, 16); @@ -202,6 +235,22 @@ TEST_F(ExpansionTest, NoAction) { }; } +TEST_F(ExpansionTest, UnsupportedActionDedupe) { + try { + check_results("status", {"E", "H"}, true, 5, {}, true); + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 144); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; +} + +TEST_F(ExpansionTest, UnsupportedPropTypeDedupe) { + try { + check_results("route", {"E", "H"}, true, 5, {"foo", "bar"}, true); + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 168); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; +} + INSTANTIATE_TEST_SUITE_P(ExpandPropsTest, ExpansionTest, ::testing::Values(std::vector{"edge_status"}, From 9725261d1c33d6fa361d3d0eec12404f79ca150d Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Mon, 15 Jul 2024 15:49:43 +0200 Subject: [PATCH 098/198] Restructure `top_speed` for truck (#4793) --- CHANGELOG.md | 2 + docs/docs/api/turn-by-turn/api-reference.md | 2 +- src/mjolnir/shortcutbuilder.cc | 5 +-- src/sif/autocost.cc | 3 ++ src/sif/dynamiccost.cc | 5 --- src/sif/motorcyclecost.cc | 3 ++ src/sif/truckcost.cc | 6 +-- test/gurka/test_shortcut.cc | 7 ++-- test/gurka/test_top_speed.cc | 6 +-- test/gurka/test_truck.cc | 43 +++++++++++++++++---- valhalla/baldr/graphconstants.h | 2 +- 11 files changed, 55 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecc0991251..32975efa6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,8 @@ * FIXED: Fix inconsistency in graph.lua for motor_vehicle_node [#4723](https://github.com/valhalla/valhalla/issues/4723) * FIXED: Missing algorithm include in `baldr/admin.h` [#4766](https://github.com/valhalla/valhalla/pull/4766) * FIXED: Handle list type arguments correctly when overriding config with valhalla_build_config [#4799](https://github.com/valhalla/valhalla/pull/4799) + * FIXED: `top_speed` range not fully allowed for trucks [#4793](https://github.com/valhalla/valhalla/pull/4793) + * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 2c1c1199fc..b076861371 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -128,7 +128,7 @@ These options are available for `auto`, `bus`, and `truck` costing methods. | `shortest` | Changes the metric to quasi-shortest, i.e. purely distance-based costing. Note, this will disable all other costings & penalties. Also note, `shortest` will not disable hierarchy pruning, leading to potentially sub-optimal routes for some costing models. The default is `false`. | | `use_distance` | A factor that allows controlling the contribution of distance and time to the route costs. The value is in range between 0 and 1, where 0 only takes time into account (default) and 1 only distance. A factor of 0.5 will weight them roughly equally. **Note:** this costing is currently only available for auto costing. | | `disable_hierarchy_pruning` | Disable hierarchies to calculate the actual optimal route. The default is `false`. **Note:** This could be quite a performance drainer so there is a upper limit of distance. If the upper limit is exceeded, this option will always be `false`. | -| `top_speed` | Top speed the vehicle can go. Also used to avoid roads with higher speeds than this value. `top_speed` must be between 10 and 252 KPH. The default value is 90 KPH for `truck` and 140 KPH for `auto` and `bus`. | +| `top_speed` | Top speed the vehicle can go. Also used to avoid roads with higher speeds than this value. `top_speed` must be between 10 and 252 KPH. The default value is 120 KPH for `truck` and 140 KPH for `auto` and `bus`. | | `fixed_speed` | Fixed speed the vehicle can go. Used to override the calculated speed. Can be useful if speed of vehicle is known. `fixed_speed` must be between 1 and 252 KPH. The default value is 0 KPH which disables fixed speed and falls back to the standard calculated speed based on the road attribution. | | `ignore_closures` | If set to `true`, ignores all closures, marked due to live traffic closures, during routing. **Note:** This option cannot be set if `location.search_filter.exclude_closures` is also specified in the request and will return an error if it is | | `closure_factor` | A factor that penalizes the cost when traversing a closed edge (eg: if `search_filter.exclude_closures` is `false` for origin and/or destination location and the route starts/ends on closed edges). Its value can range from `1.0` - don't penalize closed edges, to `10.0` - apply high cost penalty to closed edges. Default value is `9.0`. **Note:** This factor is applicable only for motorized modes of transport, i.e `auto`, `motorcycle`, `motor_scooter`, `bus`, `truck` & `taxi`. | diff --git a/src/mjolnir/shortcutbuilder.cc b/src/mjolnir/shortcutbuilder.cc index 7f9a9edaa3..a9140fb599 100644 --- a/src/mjolnir/shortcutbuilder.cc +++ b/src/mjolnir/shortcutbuilder.cc @@ -328,10 +328,7 @@ void ConnectEdges(GraphReader& reader, // Add edge and turn duration for truck total_truck_duration += turn_duration; - auto const truck_speed = - std::min(tile->GetSpeed(directededge, kNoFlowMask, kInvalidSecondsOfWeek, true), - directededge->truck_speed() ? directededge->truck_speed() : kMaxAssumedTruckSpeed); - + auto const truck_speed = tile->GetSpeed(directededge, kNoFlowMask, kInvalidSecondsOfWeek, true); assert(truck_speed != 0); auto const edge_duration_truck = directededge->length() / (truck_speed * kKPHtoMetersPerSec); total_truck_duration += edge_duration_truck; diff --git a/src/sif/autocost.cc b/src/sif/autocost.cc index 1e8fddc3f6..ea3c14d7ef 100644 --- a/src/sif/autocost.cc +++ b/src/sif/autocost.cc @@ -79,6 +79,8 @@ constexpr ranged_default_t kUseDistanceRange{0, kDefaultUseDistance, 1.0f constexpr ranged_default_t kAutoHeightRange{0, kDefaultAutoHeight, 10.0f}; constexpr ranged_default_t kAutoWidthRange{0, kDefaultAutoWidth, 10.0f}; constexpr ranged_default_t kProbabilityRange{0, kDefaultRestrictionProbability, 100}; +constexpr ranged_default_t kVehicleSpeedRange{10, baldr::kMaxAssumedSpeed, + baldr::kMaxSpeedKph}; constexpr float kHighwayFactor[] = { 1.0f, // Motorway @@ -694,6 +696,7 @@ void ParseAutoCostOptions(const rapidjson::Document& doc, JSON_PBF_DEFAULT(co, false, json, "/include_hot", include_hot); JSON_PBF_DEFAULT(co, false, json, "/include_hov2", include_hov2); JSON_PBF_DEFAULT(co, false, json, "/include_hov3", include_hov3); + JSON_PBF_RANGED_DEFAULT(co, kVehicleSpeedRange, json, "/top_speed", top_speed); } cost_ptr_t CreateAutoCost(const Costing& costing_options) { diff --git a/src/sif/dynamiccost.cc b/src/sif/dynamiccost.cc index c92ec6e33a..e26dcc0b09 100644 --- a/src/sif/dynamiccost.cc +++ b/src/sif/dynamiccost.cc @@ -108,8 +108,6 @@ constexpr float kDefaultClosureFactor = 9.0f; // non-closure end constexpr ranged_default_t kClosureFactorRange{1.0f, kDefaultClosureFactor, 10.0f}; -constexpr ranged_default_t kVehicleSpeedRange{10, baldr::kMaxAssumedSpeed, - baldr::kMaxSpeedKph}; constexpr ranged_default_t kFixedSpeedRange{0, baldr::kDisableFixedSpeed, baldr::kMaxSpeedKph}; } // namespace @@ -402,9 +400,6 @@ void ParseBaseCostOptions(const rapidjson::Value& json, co->set_disable_hierarchy_pruning( rapidjson::get(json, "/disable_hierarchy_pruning", co->disable_hierarchy_pruning())); - // top speed - JSON_PBF_RANGED_DEFAULT(co, kVehicleSpeedRange, json, "/top_speed", top_speed); - // destination only penalty JSON_PBF_RANGED_DEFAULT(co, cfg.dest_only_penalty_, json, "/destination_only_penalty", destination_only_penalty); diff --git a/src/sif/motorcyclecost.cc b/src/sif/motorcyclecost.cc index 5a24b8c495..1dcaf9805e 100644 --- a/src/sif/motorcyclecost.cc +++ b/src/sif/motorcyclecost.cc @@ -54,6 +54,8 @@ constexpr float kLeftSideTurnCosts[] = {kTCStraight, kTCSlight, kTCUnfa constexpr ranged_default_t kUseHighwaysRange{0, kDefaultUseHighways, 1.0f}; constexpr ranged_default_t kUseTollsRange{0, kDefaultUseTolls, 1.0f}; constexpr ranged_default_t kUseTrailsRange{0, kDefaultUseTrails, 1.0f}; +constexpr ranged_default_t kMotorcycleSpeedRange{10, baldr::kMaxAssumedSpeed, + baldr::kMaxSpeedKph}; constexpr float kHighwayFactor[] = { 1.0f, // Motorway @@ -578,6 +580,7 @@ void ParseMotorcycleCostOptions(const rapidjson::Document& doc, JSON_PBF_RANGED_DEFAULT(co, kUseHighwaysRange, json, "/use_highways", use_highways); JSON_PBF_RANGED_DEFAULT(co, kUseTollsRange, json, "/use_tolls", use_tolls); JSON_PBF_RANGED_DEFAULT(co, kUseTrailsRange, json, "/use_trails", use_trails); + JSON_PBF_RANGED_DEFAULT(co, kMotorcycleSpeedRange, json, "/top_speed", top_speed); } cost_ptr_t CreateMotorcycleCost(const Costing& costing_options) { diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index 0f3017e4de..1868622e36 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -495,9 +495,9 @@ Cost TruckCost::EdgeCost(const baldr::DirectedEdge* edge, &flow_sources, time_info.seconds_from_now) : fixed_speed_; - auto final_speed = std::min(std::min(edge_speed, edge->truck_speed() ? edge->truck_speed() - : kMaxAssumedTruckSpeed), - top_speed_); + auto final_speed = + std::min(edge_speed, + edge->truck_speed() ? std::min(edge->truck_speed(), top_speed_) : top_speed_); float sec = edge->length() * speedfactor_[final_speed]; diff --git a/test/gurka/test_shortcut.cc b/test/gurka/test_shortcut.cc index 028fb6e1b5..a0939881c3 100644 --- a/test/gurka/test_shortcut.cc +++ b/test/gurka/test_shortcut.cc @@ -158,8 +158,7 @@ TEST(Shortcuts, ShortcutSpeed) { } TEST(Shortcuts, TruckSpeedNotSet) { - // When truck speed is not set normal speed is used to calculate shortcut truck speed, which - // is clamped at 90 KPH. + // When truck speed is not set normal speed is used to calculate shortcut truck speed // As a result it should be equal to normal shortcut speed. const std::string ascii_map = R"(A-----B\ \C @@ -199,8 +198,8 @@ TEST(Shortcuts, TruckSpeedNotSet) { if (!edge->is_shortcut() || !(edge->forwardaccess() & baldr::kAutoAccess)) continue; - // truck speed should be smaller than the edge speed - EXPECT_GT(edge->speed(), edge->truck_speed()); + // truck speed should be equal to edge speed by default + EXPECT_EQ(edge->speed(), edge->truck_speed()); found_shortcut = true; } } diff --git a/test/gurka/test_top_speed.cc b/test/gurka/test_top_speed.cc index 32ed3bff20..f9f9945b45 100644 --- a/test/gurka/test_top_speed.cc +++ b/test/gurka/test_top_speed.cc @@ -80,11 +80,11 @@ TEST_F(TopSpeedTest, TaxiTopSpeed) { TEST_F(TopSpeedTest, ClampMaxSpeed) { Options options; rapidjson::Document dom; - rapidjson::SetValueByPointer(dom, "/top_speed", 500); + rapidjson::SetValueByPointer(dom, "/auto/top_speed", 500); + Costing co; options.set_costing_type(Costing::auto_); - auto& co = (*options.mutable_costings())[Costing::auto_]; - sif::ParseBaseCostOptions(*rapidjson::GetValueByPointer(dom, ""), &co, {}); + sif::ParseAutoCostOptions(dom, "/auto", &co); ASSERT_EQ(co.options().top_speed(), baldr::kMaxAssumedSpeed); } diff --git a/test/gurka/test_truck.cc b/test/gurka/test_truck.cc index 0ded54181f..1dcb8e6c8c 100644 --- a/test/gurka/test_truck.cc +++ b/test/gurka/test_truck.cc @@ -89,14 +89,13 @@ TEST(TruckSpeed, MaxTruckSpeed) { valhalla::Api default_route = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", {}); - // should be clamped to kMaxAssumedTruckSpeed + // should be clamped to edge speed valhalla::Api clamped_top_speed_route = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", - {{"/costing_options/truck/top_speed", "100"}, + {{"/costing_options/truck/top_speed", "115"}, {"/date_time/type", "0"}, {"/date_time/value", "current"}}); - // just below kMaxAssumedTruckSpeed valhalla::Api low_top_speed_route = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", {{"/costing_options/truck/top_speed", "70"}, @@ -153,20 +152,48 @@ TEST(TruckSpeed, MaxTruckSpeed) { auto traffic_time = getDuration(modified_traffic_route); auto traffic_low_speed_time = getDuration(modified_traffic_low_speed_route); - // default and clamped durations should be the same in this case + // both default and set top_speeds exceeds edge speed, so use edge speed in both cases ASSERT_EQ(default_time, clamped_top_speed_time); // expect a trip to take longer when a low top speed is set ASSERT_LT(default_time, low_top_speed_time); - // expect duration to be equal to default if traffic speed is higher than kMaxAssumedTruckCost - // and no truck specific speed tag is set on the way - ASSERT_EQ(default_time, traffic_time); + // was clamped to 120 KPH, traffic speed was set to 140 + ASSERT_EQ(5500 / (120 / 3.6), traffic_time); - // expect lower traffic speeds (< kMaxAssumedTruckSpeed ) to lead to a lower duration + // expect lower traffic speeds to lead to a lower duration ASSERT_LT(traffic_time, traffic_low_speed_time); } +TEST(TruckSpeed, TopSpeed) { + constexpr double gridsize = 500; + + const std::string ascii_map = R"( + A----B + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}, {"maxspeed", "120"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/truckspeed"); + + valhalla::Api default_route = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck"); + + valhalla::Api top_speed_route = gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/costing_options/truck/top_speed", "110"}}); + + valhalla::Api default_top_speed_route = + gurka::do_action(valhalla::Options::route, map, {"A", "B"}, "truck", + {{"/costing_options/truck/top_speed", "120"}}); + + auto default_dur = getDuration(default_route); + auto top_speed_dur = getDuration(top_speed_route); + auto default_top_speed_dur = getDuration(default_top_speed_route); + + ASSERT_LT(default_dur, top_speed_dur); + ASSERT_EQ(default_dur, default_top_speed_dur); +} + // tag name, tag value, costing opt name, costing opt value, forward using asymmetric_restriction_params_t = std::tuple; diff --git a/valhalla/baldr/graphconstants.h b/valhalla/baldr/graphconstants.h index ca950dbc71..f5fb6bf499 100644 --- a/valhalla/baldr/graphconstants.h +++ b/valhalla/baldr/graphconstants.h @@ -99,7 +99,7 @@ constexpr uint8_t kMaxTrafficSpeed = 252; // ~157 MPH // clamped to this maximum value. constexpr uint32_t kMaxSpeedKph = std::max(kMaxTrafficSpeed, kMaxAssumedSpeed); -constexpr uint32_t kMaxAssumedTruckSpeed = 90; // ~ 56 MPH +constexpr uint32_t kMaxAssumedTruckSpeed = 120; // ~75 MPH // Minimum speed. This is a stop gap for dubious traffic data. While its possible // to measure a probe going this slow via stop and go traffic over a long enough From 66bb9a752eda6e7731c1886118b514290a0bfcac Mon Sep 17 00:00:00 2001 From: Nils Date: Mon, 22 Jul 2024 15:36:02 +0200 Subject: [PATCH 099/198] fix trivial route for CostMatrix (#4634) Co-authored-by: Christian Beiwinkel --- CHANGELOG.md | 1 + src/thor/costmatrix.cc | 50 ++++++++++++++++------------ test/gurka/test_matrix.cc | 23 ++++++++++++- valhalla/baldr/double_bucket_queue.h | 2 +- valhalla/sif/hierarchylimits.h | 3 +- valhalla/thor/costmatrix.h | 11 ++++-- 6 files changed, 62 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32975efa6e..42727e68bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ * FIXED: Missing algorithm include in `baldr/admin.h` [#4766](https://github.com/valhalla/valhalla/pull/4766) * FIXED: Handle list type arguments correctly when overriding config with valhalla_build_config [#4799](https://github.com/valhalla/valhalla/pull/4799) * FIXED: `top_speed` range not fully allowed for trucks [#4793](https://github.com/valhalla/valhalla/pull/4793) + * FIXED: Trivial routes for CostMatrix [#4634](https://github.com/valhalla/valhalla/pull/4634) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 15c1ffdb12..9e80f4ce02 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -157,7 +157,7 @@ bool CostMatrix::SourceToTarget(Api& request, for (uint32_t i = 0; i < locs_count_[MATRIX_REV]; i++) { if (locs_status_[MATRIX_REV][i].threshold > 0) { locs_status_[MATRIX_REV][i].threshold--; - Expand(i, n, graphreader); + Expand(i, n, graphreader, request.options()); // if we exhausted this search if (locs_status_[MATRIX_REV][i].threshold == 0) { for (uint32_t source = 0; source < locs_count_[MATRIX_FORW]; source++) { @@ -192,7 +192,8 @@ bool CostMatrix::SourceToTarget(Api& request, for (uint32_t i = 0; i < locs_count_[MATRIX_FORW]; i++) { if (locs_status_[MATRIX_FORW][i].threshold > 0) { locs_status_[MATRIX_FORW][i].threshold--; - Expand(i, n, graphreader, time_infos[i], invariant); + Expand(i, n, graphreader, request.options(), time_infos[i], + invariant); // if we exhausted this search if (locs_status_[MATRIX_FORW][i].threshold == 0) { for (uint32_t target = 0; target < locs_count_[MATRIX_REV]; target++) { @@ -580,6 +581,7 @@ template bool CostMatrix::Expand(const uint32_t index, const uint32_t n, baldr::GraphReader& graphreader, + const valhalla::Options& options, const baldr::TimeInfo& time_info, const bool invariant) { @@ -619,9 +621,9 @@ bool CostMatrix::Expand(const uint32_t index, } if (FORWARD) { - CheckForwardConnections(index, pred, n, graphreader); + CheckForwardConnections(index, pred, n, graphreader, options); } else if (check_reverse_connections_) { - CheckReverseConnections(index, pred, n, graphreader); + CheckReverseConnections(index, pred, n, graphreader, options); } GraphId node = pred.endnode(); @@ -752,7 +754,8 @@ bool CostMatrix::Expand(const uint32_t index, void CostMatrix::CheckForwardConnections(const uint32_t source, const BDEdgeLabel& fwd_pred, const uint32_t n, - GraphReader& graphreader) { + GraphReader& graphreader, + const valhalla::Options& options) { // Disallow connections that are part of an uturn on an internal edge if (fwd_pred.internal_turn() != InternalTurn::kNoTurn) { @@ -798,8 +801,15 @@ void CostMatrix::CheckForwardConnections(const uint32_t source, // Special case - common edge for source and target are both initial edges if (fwd_pred.predecessor() == kInvalidLabel && rev_predidx == kInvalidLabel) { - // bail if either edge wasn't allowed (see notes in SetSources/Targets) - if (!fwd_pred.path_id() || !rev_label.path_id()) { + // bail if forward edge wasn't allowed (see notes in SetSources/Targets) + if (!fwd_pred.path_id()) { + return; + } + + // if source percent along edge is larger than target percent along, + // can't connect on this edge + if (find_correlated_edge(options.sources(source), fwd_pred.edgeid()).percent_along() > + find_correlated_edge(options.targets(target), fwd_pred.edgeid()).percent_along()) { return; } @@ -860,7 +870,8 @@ void CostMatrix::CheckForwardConnections(const uint32_t source, void CostMatrix::CheckReverseConnections(const uint32_t target, const BDEdgeLabel& rev_pred, const uint32_t n, - GraphReader& graphreader) { + GraphReader& graphreader, + const valhalla::Options& options) { // Disallow connections that are part of an uturn on an internal edge if (rev_pred.internal_turn() != InternalTurn::kNoTurn) { @@ -905,8 +916,13 @@ void CostMatrix::CheckReverseConnections(const uint32_t target, // Special case - common edge for source and target are both initial edges if (rev_pred.predecessor() == kInvalidLabel && fwd_predidx == kInvalidLabel) { - // bail if either edge wasn't allowed - if (!rev_pred.path_id() || !fwd_label.path_id()) { + // bail if the edge wasn't allowed + if (!rev_pred.path_id()) { + return; + } + + if (find_correlated_edge(options.sources(source), fwd_label.edgeid()).percent_along() > + find_correlated_edge(options.targets(target), fwd_label.edgeid()).percent_along()) { return; } @@ -1070,7 +1086,7 @@ void CostMatrix::SetSources(GraphReader& graphreader, uint32_t idx = edgelabel_[MATRIX_FORW][index].size(); edgelabel_[MATRIX_FORW][index].push_back(std::move(edge_label)); adjacency_[MATRIX_FORW][index].add(idx); - edgestatus_[MATRIX_FORW][index].Set(edgeid, EdgeSet::kUnreachedOrReset, idx, tile); + edgestatus_[MATRIX_FORW][index].Set(edgeid, EdgeSet::kTemporary, idx, tile); if (check_reverse_connections_) (*sources_)[edgeid].push_back(index); } @@ -1160,7 +1176,7 @@ void CostMatrix::SetTargets(baldr::GraphReader& graphreader, uint32_t idx = edgelabel_[MATRIX_REV][index].size(); edgelabel_[MATRIX_REV][index].push_back(std::move(edge_label)); adjacency_[MATRIX_REV][index].add(idx); - edgestatus_[MATRIX_REV][index].Set(opp_edge_id, EdgeSet::kUnreachedOrReset, idx, opp_tile); + edgestatus_[MATRIX_REV][index].Set(opp_edge_id, EdgeSet::kTemporary, idx, opp_tile); (*targets_)[opp_edge_id].push_back(index); } index++; @@ -1242,16 +1258,6 @@ std::string CostMatrix::RecostFormPath(GraphReader& graphreader, float source_pct = static_cast(source_edge.percent_along()); float target_pct = static_cast(target_edge.percent_along()); - // TODO(nils): bug with trivial routes https://github.com/valhalla/valhalla/issues/4433 - // remove this whole block below once that's fixed - if (path_edges.size() == 1 && source_pct > target_pct) { - // it found the wrong direction, so let's turn that around - auto opp_id = graphreader.GetOpposingEdgeId(path_edges[0]); - path_edges.clear(); - path_edges.emplace_back(opp_id); - source_pct = 1.f - source_pct; - target_pct = 1.f - target_pct; - } // recost the path if this was a time-dependent expansion if (has_time_) { auto edge_itr = path_edges.begin(); diff --git a/test/gurka/test_matrix.cc b/test/gurka/test_matrix.cc index 086c66e460..5af0406d6c 100644 --- a/test/gurka/test_matrix.cc +++ b/test/gurka/test_matrix.cc @@ -788,6 +788,7 @@ TEST(StandAlone, CostMatrixTrivialRoutes) { const std::string ascii_map = R"( A---B--2->-1--C---D | | + 6 5 | | E--3---4--F )"; @@ -806,7 +807,7 @@ TEST(StandAlone, CostMatrixTrivialRoutes) { { auto matrix = gurka::do_action(valhalla::Options::sources_to_targets, map, {"1"}, {"2"}, "auto", options); - EXPECT_EQ(matrix.matrix().distances(0), 2200); + EXPECT_EQ(matrix.matrix().distances(0), 2400); std::vector oneway_vertices; for (auto& node : {"1", "C", "F", "E", "B", "2"}) { @@ -816,6 +817,16 @@ TEST(StandAlone, CostMatrixTrivialRoutes) { EXPECT_EQ(matrix.matrix().shapes(0), encoded); } + // test the oneway case + { + auto matrix = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"2"}, {"1"}, "auto", options); + EXPECT_EQ(matrix.matrix().distances(0), 400); + + auto encoded = encode>({layout["2"], layout["1"]}, 1e6); + EXPECT_EQ(matrix.matrix().shapes(0), encoded); + } + // test the normal trivial case { auto matrix = @@ -825,6 +836,16 @@ TEST(StandAlone, CostMatrixTrivialRoutes) { auto encoded = encode>({layout["3"], layout["4"]}, 1e6); EXPECT_EQ(matrix.matrix().shapes(0), encoded); } + + // test trivial case via connecting edge + { + auto matrix = + gurka::do_action(valhalla::Options::sources_to_targets, map, {"4"}, {"5"}, "auto", options); + EXPECT_EQ(matrix.matrix().distances(0), 500); + + auto encoded = encode>({layout["4"], layout["F"], layout["5"]}, 1e6); + EXPECT_EQ(matrix.matrix().shapes(0), encoded); + } } TEST(StandAlone, HGVNoAccessPenalty) { diff --git a/valhalla/baldr/double_bucket_queue.h b/valhalla/baldr/double_bucket_queue.h index 8975430cb9..d307c418b9 100644 --- a/valhalla/baldr/double_bucket_queue.h +++ b/valhalla/baldr/double_bucket_queue.h @@ -98,7 +98,7 @@ template class DoubleBucketQueue final { } /** - * Clear all labels from the low-level buckets and the overflow buckets and deallocates buckets + * Clear all labels from the low-level buckets and the overflow bucket and deallocate the buckets' * memory. */ void clear() { diff --git a/valhalla/sif/hierarchylimits.h b/valhalla/sif/hierarchylimits.h index 006f6683b4..993096af58 100644 --- a/valhalla/sif/hierarchylimits.h +++ b/valhalla/sif/hierarchylimits.h @@ -46,8 +46,7 @@ struct HierarchyLimits { // always allowed. Used for A*. /** - * Set hierarchy limits for the specified level using a property tree. - * @param pt Property tree + * Set hierarchy limits for the specified level. * @param level Hierarchy level */ HierarchyLimits(const uint32_t level) : up_transition_count(0) { diff --git a/valhalla/thor/costmatrix.h b/valhalla/thor/costmatrix.h index 1c9e89b0ac..157ad98441 100644 --- a/valhalla/thor/costmatrix.h +++ b/valhalla/thor/costmatrix.h @@ -194,17 +194,21 @@ class CostMatrix : public MatrixAlgorithm { * @param pred Edge label of the predecessor. * @param n Iteration counter. * @param graphreader the graph reader instance + * @param options the request options to check for the position along origin and destination + * edges */ void CheckForwardConnections(const uint32_t source, const sif::BDEdgeLabel& pred, const uint32_t n, - baldr::GraphReader& graphreader); + baldr::GraphReader& graphreader, + const valhalla::Options& options); template bool Expand(const uint32_t index, const uint32_t n, baldr::GraphReader& graphreader, + const valhalla::Options& options, const baldr::TimeInfo& time_info = baldr::TimeInfo::invalid(), const bool invariant = false); @@ -228,11 +232,14 @@ class CostMatrix : public MatrixAlgorithm { * @param pred Edge label of the predecessor. * @param n Iteration counter. * @param graphreader the graph reader instance + * @param options the request options to check for the position along origin and destination + * edges */ void CheckReverseConnections(const uint32_t target, const sif::BDEdgeLabel& pred, const uint32_t n, - baldr::GraphReader& graphreader); + baldr::GraphReader& graphreader, + const valhalla::Options& options); /** * Update status when a connection is found. From e5e695f5209440aabea02004fa3999f49ca154de Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Mon, 22 Jul 2024 17:03:40 +0200 Subject: [PATCH 100/198] Add `expansion_type` property to `/expansion` (#4784) --- CHANGELOG.md | 1 + docs/docs/api/expansion/api-reference.md | 10 ++- proto/expansion.proto | 6 ++ proto/options.proto | 1 + src/proto_conversions.cc | 3 +- src/thor/bidirectional_astar.cc | 20 +++-- src/thor/costmatrix.cc | 11 ++- src/thor/dijkstras.cc | 3 +- src/thor/expansion_action.cc | 98 +++++++++++++----------- src/tyr/expansion_serializer.cc | 2 + test/gurka/test_expansion.cc | 9 ++- valhalla/thor/dijkstras.h | 3 +- valhalla/thor/matrixalgorithm.h | 3 +- valhalla/thor/pathalgorithm.h | 3 +- 14 files changed, 106 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42727e68bc..32b02add5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -131,6 +131,7 @@ * ADDED: deadend information in directed edge JSON for `/locate` [#4751](https://github.com/valhalla/valhalla/pull/4751) * ADDED: Dedupe option for expansion, significantly reducing the response size. [#4601](https://github.com/valhalla/valhalla/issues/4601) * FIXED: remove old code that allows bicycle access on hiking trails. [#4781](https://github.com/valhalla/valhalla/pull/4781) + * ADDED: `expansion_type` property to `/expansion` [#4784](https://github.com/valhalla/valhalla/pull/4784) * ADDED: inline config arg for `valhalla_build_elevation` script [#4787](https://github.com/valhalla/valhalla/pull/4787) ## Release Date: 2023-05-11 Valhalla 3.4.0 diff --git a/docs/docs/api/expansion/api-reference.md b/docs/docs/api/expansion/api-reference.md index 0e3351ef61..3829a52f44 100644 --- a/docs/docs/api/expansion/api-reference.md +++ b/docs/docs/api/expansion/api-reference.md @@ -14,10 +14,10 @@ Since this service wraps other services, the request format mostly follows the o | Parameter | Description | |:----------------------------------| :------------------------------------ | -| `action` (required) | The service whose expansion should be tracked. Currently one of `route`, `isochrone` or `sources_to_targets`. | -| `skip_opposites` (optional) | If set to `true` the output won't contain an edge's opposing edge. Opposing edges can be thought of as both directions of one road segment. Of the two, we discard the directional edge with higher cost and keep the one with less cost. Default `false`. | +| `action` (required) | The service whose expansion should be tracked. Currently one of `route`, `isochrone` or `sources_to_targets`. | +| `skip_opposites` (optional) | If set to `true` the output won't contain an edge's opposing edge. Opposing edges can be thought of as both directions of one road segment. Of the two, we discard the directional edge with higher cost and keep the one with less cost. Default false. | | `dedupe` (optional) | If set to `true`, the output will contain each edge only once, significantly reducing the response size. The expansion will keep track of already visited edges and override their properties, ensuring that only the one with higher edge state is returned. Default `false`. | -| `expansion_properties` (optional) | A JSON array of strings of the GeoJSON property keys you'd like to have in the response. One or multiple of "duration", "distance", "cost", "edge_id", "pred_edge_id" or "edge_status". **Note**, that each additional property will increase the output size by minimum ~ 10%. By default an empty `properties` object is returned. | +| `expansion_properties` (optional) | A JSON array of strings of the GeoJSON property keys you'd like to have in the response. One or multiple of "duration", "distance", "cost", "edge_id", "pred_edge_id", "edge_status" or "expansion_type". **Note**, that each additional property will increase the output size by minimum ~ 10%. By default an empty `properties` object is returned. | The `expansion_properties` choices are as follows: @@ -29,6 +29,7 @@ The `expansion_properties` choices are as follows: | `edge_id` | Returns the internal edge IDs for each edge in order of graph traversal. Mostly interesting for debugging. | | `pred_edge_id` | Returns the internal edge IDs of the predecessor for each edge in order of graph traversal. Mostly interesting for debugging. | | `edge_status` | Returns the edge states for each edge in order of graph traversal. Mostly interesting for debugging. Can be one of "r" (reached), "s" (settled), "c" (connected). | +| `expansion_type` | Returns the expansion direction from which the edge was encountered. 0 for forward, 1 for reverse. | An example request is: @@ -54,7 +55,8 @@ An example request is: "edge_id", "pred_edge_id", "edge_status", - "cost" + "cost", + "expansion_type" ] } ``` diff --git a/proto/expansion.proto b/proto/expansion.proto index e193e70bf0..60454202a3 100644 --- a/proto/expansion.proto +++ b/proto/expansion.proto @@ -15,12 +15,18 @@ message Expansion { reached = 2; } + enum ExpansionType { + forward = 0; + reverse = 1; + } + repeated uint32 costs = 1 [packed=true]; repeated uint32 durations = 2 [packed=true]; repeated uint32 distances = 3 [packed=true]; repeated EdgeStatus edge_status = 4; repeated uint32 edge_id = 5 [packed=true]; repeated uint32 pred_edge_id = 6 [packed=true]; + repeated ExpansionType expansion_type = 8; repeated Geometry geometries = 7; } \ No newline at end of file diff --git a/proto/options.proto b/proto/options.proto index 9759399f92..936d9ccc04 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -378,6 +378,7 @@ message Options { edge_status = 3; edge_id = 4; pred_edge_id = 5; + expansion_type = 6; } Units units = 1; // kilometers or miles diff --git a/src/proto_conversions.cc b/src/proto_conversions.cc index 93ebf573af..9f01d1d89b 100644 --- a/src/proto_conversions.cc +++ b/src/proto_conversions.cc @@ -369,7 +369,8 @@ bool Options_ExpansionProperties_Enum_Parse(const std::string& prop, {"distance", Options_ExpansionProperties_distance}, {"edge_status", Options_ExpansionProperties_edge_status}, {"edge_id", Options::ExpansionProperties::Options_ExpansionProperties_edge_id}, - {"pred_edge_id", Options_ExpansionProperties_pred_edge_id}}; + {"pred_edge_id", Options_ExpansionProperties_pred_edge_id}, + {"expansion_type", Options_ExpansionProperties_expansion_type}}; auto i = actions.find(prop); if (i == actions.cend()) return false; diff --git a/src/thor/bidirectional_astar.cc b/src/thor/bidirectional_astar.cc index f2ff21e711..faae829ae6 100644 --- a/src/thor/bidirectional_astar.cc +++ b/src/thor/bidirectional_astar.cc @@ -362,7 +362,8 @@ inline bool BidirectionalAStar::ExpandInner(baldr::GraphReader& graphreader, : (FORWARD ? edgelabels_forward_ : edgelabels_reverse_)[pred.predecessor()].edgeid(); expansion_callback_(graphreader, FORWARD ? meta.edge_id : opp_edge_id, prev_pred, "bidirectional_astar", Expansion_EdgeStatus_reached, newcost.secs, - pred.path_distance() + meta.edge->length(), newcost.cost); + pred.path_distance() + meta.edge->length(), newcost.cost, + static_cast(expansion_direction)); } // we've just added this edge to the queue, but we won't expand from it if it's a not-thru edge that @@ -709,7 +710,8 @@ BidirectionalAStar::GetBestPath(valhalla::Location& origin, : edgelabels_forward_[fwd_pred.predecessor()].edgeid(); expansion_callback_(graphreader, fwd_pred.edgeid(), prev_pred, "bidirectional_astar", Expansion_EdgeStatus_settled, fwd_pred.cost().secs, - fwd_pred.path_distance(), fwd_pred.cost().cost); + fwd_pred.path_distance(), fwd_pred.cost().cost, + Expansion_ExpansionType_forward); } // Prune path if predecessor is not a through edge or if the maximum @@ -757,7 +759,8 @@ BidirectionalAStar::GetBestPath(valhalla::Location& origin, : edgelabels_reverse_[rev_pred.predecessor()].edgeid(); expansion_callback_(graphreader, rev_pred.opp_edgeid(), prev_pred, "bidirectional_astar", Expansion_EdgeStatus_settled, rev_pred.cost().secs, - rev_pred.path_distance(), rev_pred.cost().cost); + rev_pred.path_distance(), rev_pred.cost().cost, + Expansion_ExpansionType_reverse); } // Prune path if predecessor is not a through edge @@ -873,7 +876,7 @@ bool BidirectionalAStar::SetForwardConnection(GraphReader& graphreader, const BD : edgelabels_forward_[pred.predecessor()].edgeid(); expansion_callback_(graphreader, pred.edgeid(), prev_pred, "bidirectional_astar", Expansion_EdgeStatus_connected, pred.cost().secs, pred.path_distance(), - pred.cost().cost); + pred.cost().cost, Expansion_ExpansionType_forward); } return true; @@ -946,7 +949,8 @@ bool BidirectionalAStar::SetReverseConnection(GraphReader& graphreader, const BD : edgelabels_forward_[fwd_pred.predecessor()].edgeid(); expansion_callback_(graphreader, fwd_edge_id, prev_pred, "bidirectional_astar", Expansion_EdgeStatus_connected, fwd_pred.cost().secs, - fwd_pred.path_distance(), fwd_pred.cost().cost); + fwd_pred.path_distance(), fwd_pred.cost().cost, + Expansion_ExpansionType_reverse); } return true; @@ -1031,7 +1035,8 @@ void BidirectionalAStar::SetOrigin(GraphReader& graphreader, if (expansion_callback_) { expansion_callback_(graphreader, edgeid, GraphId{}, "bidirectional_astar", Expansion_EdgeStatus_reached, cost.secs, - static_cast(edge.distance() + 0.5), cost.cost); + static_cast(edge.distance() + 0.5), cost.cost, + Expansion_ExpansionType_forward); } // Set the initial not_thru flag to false. There is an issue with not_thru @@ -1129,7 +1134,8 @@ void BidirectionalAStar::SetDestination(GraphReader& graphreader, if (expansion_callback_) { expansion_callback_(graphreader, edgeid, GraphId{}, "bidirectional_astar", Expansion_EdgeStatus_reached, cost.secs, - static_cast(edge.distance() + 0.5), cost.cost); + static_cast(edge.distance() + 0.5), cost.cost, + Expansion_ExpansionType_reverse); } // Set the initial not_thru flag to false. There is an issue with not_thru diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 9e80f4ce02..df4f7cfef9 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -571,7 +571,8 @@ bool CostMatrix::ExpandInner(baldr::GraphReader& graphreader, // setting this edge as reached if (expansion_callback_) { expansion_callback_(graphreader, meta.edge_id, pred.edgeid(), "costmatrix", - Expansion_EdgeStatus_reached, newcost.secs, pred_dist, newcost.cost); + Expansion_EdgeStatus_reached, newcost.secs, pred_dist, newcost.cost, + static_cast(expansion_direction)); } return !(pred.not_thru_pruning() && meta.edge->not_thru()); @@ -617,7 +618,7 @@ bool CostMatrix::Expand(const uint32_t index, pred.predecessor() == kInvalidLabel ? GraphId{} : edgelabels[pred.predecessor()].edgeid(); expansion_callback_(graphreader, pred.edgeid(), prev_pred, "costmatrix", Expansion_EdgeStatus_settled, pred.cost().secs, pred.path_distance(), - pred.cost().cost); + pred.cost().cost, static_cast(expansion_direction)); } if (FORWARD) { @@ -860,7 +861,8 @@ void CostMatrix::CheckForwardConnections(const uint32_t source, : edgelabel_[MATRIX_FORW][source][fwd_pred.predecessor()].edgeid(); expansion_callback_(graphreader, fwd_pred.edgeid(), prev_pred, "costmatrix", Expansion_EdgeStatus_connected, fwd_pred.cost().secs, - fwd_pred.path_distance(), fwd_pred.cost().cost); + fwd_pred.path_distance(), fwd_pred.cost().cost, + Expansion_ExpansionType_forward); } } @@ -974,7 +976,8 @@ void CostMatrix::CheckReverseConnections(const uint32_t target, : edgelabel_[MATRIX_REV][source][rev_pred.predecessor()].edgeid(); expansion_callback_(graphreader, rev_pred.edgeid(), prev_pred, "costmatrix", Expansion_EdgeStatus_connected, rev_pred.cost().secs, - rev_pred.path_distance(), rev_pred.cost().cost); + rev_pred.path_distance(), rev_pred.cost().cost, + Expansion_ExpansionType_reverse); } } } diff --git a/src/thor/dijkstras.cc b/src/thor/dijkstras.cc index 9acc7246fa..249e9f7a91 100644 --- a/src/thor/dijkstras.cc +++ b/src/thor/dijkstras.cc @@ -388,7 +388,8 @@ void Dijkstras::Compute(google::protobuf::RepeatedPtrField& : bdedgelabels_[pred.predecessor()].edgeid(); expansion_callback_(graphreader, pred.edgeid(), prev_pred, "dijkstras", Expansion_EdgeStatus_settled, pred.cost().secs, pred.path_distance(), - pred.cost().cost); + pred.cost().cost, + static_cast(expansion_direction)); } } } diff --git a/src/thor/expansion_action.cc b/src/thor/expansion_action.cc index 11b08abb96..326e35d4a0 100644 --- a/src/thor/expansion_action.cc +++ b/src/thor/expansion_action.cc @@ -2,6 +2,7 @@ #include "midgard/logging.h" #include "midgard/polyline2.h" #include "midgard/util.h" +#include "thor/pathalgorithm.h" #include "thor/worker.h" #include "tyr/serializers.h" #include @@ -22,7 +23,8 @@ void writeExpansionProgress(Expansion* expansion, const Expansion_EdgeStatus& status, const float& duration, const uint32_t& distance, - const float& cost) { + const float& cost, + const Expansion_ExpansionType expansion_type) { auto* geom = expansion->add_geometries(); // make the geom @@ -49,6 +51,8 @@ void writeExpansionProgress(Expansion* expansion, expansion->add_edge_id(static_cast(edgeid)); if (exp_props.count(Options_ExpansionProperties_pred_edge_id)) expansion->add_pred_edge_id(static_cast(prev_edgeid)); + if (exp_props.count(Options_ExpansionProperties_expansion_type)) + expansion->add_expansion_type(expansion_type); } struct expansion_properties_t { @@ -59,6 +63,7 @@ struct expansion_properties_t { std::vector shape; uint32_t distance; float cost; + Expansion_ExpansionType expansion_type; expansion_properties_t() = default; expansion_properties_t(baldr::GraphId prev_edgeid, @@ -66,9 +71,10 @@ struct expansion_properties_t { float duration, uint32_t distance, std::vector&& shape, - float cost) + float cost, + Expansion_ExpansionType expansion_type) : prev_edgeid(prev_edgeid), status(status), duration(duration), distance(distance), - shape(std::move(shape)), cost(cost){}; + shape(std::move(shape)), cost(cost), expansion_type(expansion_type){}; // check if status is higher or same – as we will keep track of the latest one static bool is_latest_status(Expansion_EdgeStatus current, Expansion_EdgeStatus candidate) { @@ -104,50 +110,51 @@ std::string thor_worker_t::expansion(Api& request) { // a lambda that the path algorithm can call to add stuff to the dom // route and isochrone produce different GeoJSON properties std::string algo = ""; - auto track_expansion = [&](baldr::GraphReader& reader, baldr::GraphId edgeid, - baldr::GraphId prev_edgeid, const char* algorithm = nullptr, - const Expansion_EdgeStatus status = Expansion_EdgeStatus_reached, - const float duration = 0.f, const uint32_t distance = 0, - const float cost = 0.f) { - algo = algorithm; - - auto tile = reader.GetGraphTile(edgeid); - if (tile == nullptr) { - LOG_ERROR("thor_worker_t::expansion error, tile no longer available" + - std::to_string(edgeid.Tile_Base())); - return; - } - - // if requested, skip this edge in case its opposite edge has been added - // before (i.e. lower cost) else add this edge's id to the lookup container - if (skip_opps) { - auto opp_tile = tile; - auto opp_edgeid = reader.GetOpposingEdgeId(edgeid, opp_tile); - if (opp_edgeid && opp_edges.count(opp_edgeid)) - return; - opp_edges.insert(edgeid); - } + auto track_expansion = + [&](baldr::GraphReader& reader, baldr::GraphId edgeid, baldr::GraphId prev_edgeid, + const char* algorithm = nullptr, + const Expansion_EdgeStatus status = Expansion_EdgeStatus_reached, + const float duration = 0.f, const uint32_t distance = 0, const float cost = 0.f, + const Expansion_ExpansionType expansion_type = Expansion_ExpansionType_forward) { + algo = algorithm; + + auto tile = reader.GetGraphTile(edgeid); + if (tile == nullptr) { + LOG_ERROR("thor_worker_t::expansion error, tile no longer available" + + std::to_string(edgeid.Tile_Base())); + return; + } - const auto* edge = tile->directededge(edgeid); - auto shape = tile->edgeinfo(edge).shape(); + // if requested, skip this edge in case its opposite edge has been added + // before (i.e. lower cost) else add this edge's id to the lookup container + if (skip_opps) { + auto opp_tile = tile; + auto opp_edgeid = reader.GetOpposingEdgeId(edgeid, opp_tile); + if (opp_edgeid && opp_edges.count(opp_edgeid)) + return; + opp_edges.insert(edgeid); + } - if (!edge->forward()) - std::reverse(shape.begin(), shape.end()); - Polyline2::Generalize(shape, gen_factor, {}, false); - if (dedupe) { - if (edge_state.contains(edgeid)) { - // Keep only properties of last/highest status of edge - if (!expansion_properties_t::is_latest_status(edge_state.at(edgeid).status, status)) { - return; + const auto* edge = tile->directededge(edgeid); + auto shape = tile->edgeinfo(edge).shape(); + + if (!edge->forward()) + std::reverse(shape.begin(), shape.end()); + Polyline2::Generalize(shape, gen_factor, {}, false); + if (dedupe) { + if (edge_state.contains(edgeid)) { + // Keep only properties of last/highest status of edge + if (!expansion_properties_t::is_latest_status(edge_state.at(edgeid).status, status)) { + return; + } + } + edge_state[edgeid] = expansion_properties_t(prev_edgeid, status, duration, distance, + std::move(shape), cost, expansion_type); + } else { + writeExpansionProgress(expansion, edgeid, prev_edgeid, shape, exp_props, status, duration, + distance, cost, expansion_type); } - } - edge_state[edgeid] = - expansion_properties_t(prev_edgeid, status, duration, distance, std::move(shape), cost); - } else { - writeExpansionProgress(expansion, edgeid, prev_edgeid, shape, exp_props, status, duration, - distance, cost); - } - }; + }; // tell all the algorithms how to track expansion for (auto* alg : std::vector{ @@ -183,7 +190,8 @@ std::string thor_worker_t::expansion(Api& request) { if (dedupe) { for (const auto& e : edge_state) { writeExpansionProgress(expansion, e.first, e.second.prev_edgeid, e.second.shape, exp_props, - e.second.status, e.second.duration, e.second.distance, e.second.cost); + e.second.status, e.second.duration, e.second.distance, e.second.cost, + e.second.expansion_type); } } diff --git a/src/tyr/expansion_serializer.cc b/src/tyr/expansion_serializer.cc index 0401bbc522..ee6032d497 100644 --- a/src/tyr/expansion_serializer.cc +++ b/src/tyr/expansion_serializer.cc @@ -68,6 +68,8 @@ std::string serializeExpansion(Api& request, const std::string& algo) { writer("edge_id", static_cast(expansion.edge_id(i))); if (exp_props.count(Options_ExpansionProperties_pred_edge_id)) writer("pred_edge_id", static_cast(expansion.pred_edge_id(i))); + if (exp_props.count(Options_ExpansionProperties_expansion_type)) + writer("expansion_type", static_cast(expansion.expansion_type(i))); writer.end_object(); // properties writer.end_object(); // feature diff --git a/test/gurka/test_expansion.cc b/test/gurka/test_expansion.cc index eeeee4dc8e..bd285ea807 100644 --- a/test/gurka/test_expansion.cc +++ b/test/gurka/test_expansion.cc @@ -91,6 +91,7 @@ class ExpansionTest : public ::testing::TestWithParam> bool pred_edge_id = false; bool edge_id = false; bool cost = false; + bool expansion_type = false; const std::unordered_map prop_count; for (const auto& prop : props) { @@ -112,6 +113,9 @@ class ExpansionTest : public ::testing::TestWithParam> if (prop == "cost") { cost = true; } + if (prop == "expansion_type") { + expansion_type = true; + } } ASSERT_EQ(parsed_api.expansion().geometries_size(), exp_feats); ASSERT_EQ(parsed_api.expansion().edge_status_size(), edge_status ? exp_feats : 0); @@ -120,6 +124,7 @@ class ExpansionTest : public ::testing::TestWithParam> ASSERT_EQ(parsed_api.expansion().pred_edge_id_size(), pred_edge_id ? exp_feats : 0); ASSERT_EQ(parsed_api.expansion().edge_id_size(), edge_id ? exp_feats : 0); ASSERT_EQ(parsed_api.expansion().costs_size(), cost ? exp_feats : 0); + ASSERT_EQ(parsed_api.expansion().expansion_type_size(), expansion_type ? exp_feats : 0); } void check_result_json(const std::string& action, const std::vector& waypoints, @@ -255,6 +260,6 @@ INSTANTIATE_TEST_SUITE_P(ExpandPropsTest, ExpansionTest, ::testing::Values(std::vector{"edge_status"}, std::vector{"distance", "duration", - "pred_edge_id"}, + "pred_edge_id", "expansion_type"}, std::vector{"edge_id", "cost"}, - std::vector{})); \ No newline at end of file + std::vector{})); diff --git a/valhalla/thor/dijkstras.h b/valhalla/thor/dijkstras.h index dd8cdbb0e0..d8050f388e 100644 --- a/valhalla/thor/dijkstras.h +++ b/valhalla/thor/dijkstras.h @@ -77,7 +77,8 @@ class Dijkstras { const Expansion::EdgeStatus, float, uint32_t, - float)>; + float, + const Expansion_ExpansionType)>; void set_track_expansion(const expansion_callback_t& expansion_callback) { expansion_callback_ = expansion_callback; } diff --git a/valhalla/thor/matrixalgorithm.h b/valhalla/thor/matrixalgorithm.h index f2f62b7ace..3e0fb34a91 100644 --- a/valhalla/thor/matrixalgorithm.h +++ b/valhalla/thor/matrixalgorithm.h @@ -127,7 +127,8 @@ class MatrixAlgorithm { const Expansion_EdgeStatus, float, uint32_t, - float)>; + float, + const Expansion_ExpansionType)>; void set_track_expansion(const expansion_callback_t& expansion_callback) { expansion_callback_ = expansion_callback; } diff --git a/valhalla/thor/pathalgorithm.h b/valhalla/thor/pathalgorithm.h index 5b7d12bb4c..a35da55460 100644 --- a/valhalla/thor/pathalgorithm.h +++ b/valhalla/thor/pathalgorithm.h @@ -133,7 +133,8 @@ class PathAlgorithm { const Expansion_EdgeStatus, float, uint32_t, - float)>; + float, + const Expansion_ExpansionType)>; void set_track_expansion(const expansion_callback_t& expansion_callback) { expansion_callback_ = expansion_callback; } From 1b5d6f0d5765a8558458c70443d4261a77005e00 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 25 Jul 2024 06:42:45 +0200 Subject: [PATCH 101/198] `use_truck_route` parameter (#4809) --- CHANGELOG.md | 1 + docs/docs/api/turn-by-turn/api-reference.md | 1 + proto/options.proto | 1 + src/sif/truckcost.cc | 27 +++++++++++----- test/gurka/test_truck.cc | 35 +++++++++++++++++++++ 5 files changed, 57 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32b02add5e..2d8fbceae1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -133,6 +133,7 @@ * FIXED: remove old code that allows bicycle access on hiking trails. [#4781](https://github.com/valhalla/valhalla/pull/4781) * ADDED: `expansion_type` property to `/expansion` [#4784](https://github.com/valhalla/valhalla/pull/4784) * ADDED: inline config arg for `valhalla_build_elevation` script [#4787](https://github.com/valhalla/valhalla/pull/4787) + * ADDED: `use_truck_route` [#4809](https://github.com/valhalla/valhalla/pull/4809) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index b076861371..c00e646f67 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -163,6 +163,7 @@ The following options are available for `truck` costing. | `hazmat` | A value indicating if the truck is carrying hazardous materials. Default false. | | `hgv_no_access_penalty` | A penalty applied to roads with no HGV/truck access. If set to a value less than 43200 seconds, HGV will be allowed on these roads and the penalty applies. Default 43200, i.e. trucks are not allowed. | | `low_class_penalty` | A penalty (in seconds) which is applied when going to residential or service roads. Default is 30 seconds. | +| `use_truck_route` | This value is a range of values from 0 to 1, where 0 indicates a light preference for streets marked as truck routes, and 1 indicates that streets not marked as truck routes should be avoided. This information is derived from the `hgv=designated` tag. Note that even with values near 1, there is no guarantee the returned route will include streets marked as truck routes. The default value is 0. | ##### Bicycle costing options diff --git a/proto/options.proto b/proto/options.proto index 936d9ccc04..3f3795e8fd 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -316,6 +316,7 @@ message Costing { oneof has_hgv_no_access_penalty { float hgv_no_access_penalty = 85; } + float use_truck_route = 86; } oneof has_options { diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index 1868622e36..6ef4980270 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -65,6 +65,8 @@ constexpr float kLeftSideTurnCosts[] = {kTCStraight, kTCSlight, kTCUnfa // How much to favor truck routes. constexpr float kTruckRouteFactor = 0.85f; +constexpr float kDefaultUseTruckRoute = 0.0f; +constexpr float kMinNonTruckRouteFactor = 1.0f; constexpr float kHighwayFactor[] = { 1.0f, // Motorway @@ -99,6 +101,7 @@ constexpr ranged_default_t kAxleCountRange{2, kDefaultAxleCount, 20}; constexpr ranged_default_t kUseHighwaysRange{0.f, kDefaultUseHighways, 1.0f}; constexpr ranged_default_t kTopSpeedRange{10.f, kMaxAssumedTruckSpeed, kMaxSpeedKph}; constexpr ranged_default_t kHGVNoAccessRange{0.f, kMaxPenalty, kMaxPenalty}; +constexpr ranged_default_t kUseTruckRouteRange{0.f, kDefaultUseTruckRoute, 1.0f}; BaseCostingOptionsConfig GetBaseCostOptsConfig() { BaseCostingOptionsConfig cfg{}; @@ -301,14 +304,15 @@ class TruckCost : public DynamicCost { float low_class_penalty_; // Penalty (seconds) to go to residential or service road // Vehicle attributes (used for special restrictions and costing) - bool hazmat_; // Carrying hazardous materials - float weight_; // Vehicle weight in metric tons - float axle_load_; // Axle load weight in metric tons - float height_; // Vehicle height in meters - float width_; // Vehicle width in meters - float length_; // Vehicle length in meters - float highway_factor_; // Factor applied when road is a motorway or trunk - uint8_t axle_count_; // Vehicle axle count + bool hazmat_; // Carrying hazardous materials + float weight_; // Vehicle weight in metric tons + float axle_load_; // Axle load weight in metric tons + float height_; // Vehicle height in meters + float width_; // Vehicle width in meters + float length_; // Vehicle length in meters + float highway_factor_; // Factor applied when road is a motorway or trunk + float non_truck_route_factor_; // Factor applied when road is not part of a designated truck route + uint8_t axle_count_; // Vehicle axle count // Density factor used in edge transition costing std::vector trans_density_factor_; @@ -330,6 +334,10 @@ TruckCost::TruckCost(const Costing& costing) get_base_costs(costing); low_class_penalty_ = costing_options.low_class_penalty(); + non_truck_route_factor_ = + costing_options.use_truck_route() < 0.5f + ? kMinNonTruckRouteFactor + 2.f * costing_options.use_truck_route() + : ((kMinNonTruckRouteFactor - 5.f) + 12.f * costing_options.use_truck_route()); // Get the vehicle attributes hazmat_ = costing_options.hazmat(); @@ -523,6 +531,8 @@ Cost TruckCost::EdgeCost(const baldr::DirectedEdge* edge, if (edge->truck_route() > 0) { factor *= kTruckRouteFactor; + } else { + factor *= non_truck_route_factor_; } if (edge->toll()) { @@ -733,6 +743,7 @@ void ParseTruckCostOptions(const rapidjson::Document& doc, JSON_PBF_RANGED_DEFAULT(co, kTopSpeedRange, json, "/top_speed", top_speed); JSON_PBF_RANGED_DEFAULT(co, kHGVNoAccessRange, json, "/hgv_no_access_penalty", hgv_no_access_penalty); + JSON_PBF_RANGED_DEFAULT_V2(co, kUseTruckRouteRange, json, "/use_truck_route", use_truck_route); } cost_ptr_t CreateTruckCost(const Costing& costing_options) { diff --git a/test/gurka/test_truck.cc b/test/gurka/test_truck.cc index 1dcb8e6c8c..132dce2238 100644 --- a/test/gurka/test_truck.cc +++ b/test/gurka/test_truck.cc @@ -312,3 +312,38 @@ TEST(Standalone, TruckSpeeds) { EXPECT_EQ(de_both_1->truck_speed(), 15); EXPECT_EQ(de_both_2->truck_speed(), 15); } + +TEST(Standalone, UseTruckRoute) { + constexpr double gridsize = 500; + + const std::string ascii_map = R"( + A------B + | | + | | + | | + | | + | C + | | + E------D + )"; + + const gurka::ways ways = {{"AB", {{"highway", "residential"}}}, + {"AE", {{"highway", "residential"}, {"hgv", "designated"}}}, + {"BC", {{"highway", "residential"}}}, + {"CD", {{"highway", "residential"}}}, + {"ED", {{"highway", "residential"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); + gurka::map map = gurka::buildtiles(layout, ways, {}, {}, "test/data/use_truck_route"); + + std::unordered_map options = { + {"/costing_options/truck/use_truck_route", "1"}}; + + valhalla::Api default_route = gurka::do_action(valhalla::Options::route, map, {"A", "C"}, "truck"); + + valhalla::Api use_truck_route = + gurka::do_action(valhalla::Options::route, map, {"A", "C"}, "truck", options); + + gurka::assert::raw::expect_path(default_route, {"AB", "BC"}); + gurka::assert::raw::expect_path(use_truck_route, {"AE", "ED", "CD"}); +} From 270163cf58e6447b469ce2dfc86bf67e89a8b566 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Sat, 27 Jul 2024 03:40:40 +0200 Subject: [PATCH 102/198] costmatrix cleanup was not resetting properly (#4817) --- CHANGELOG.md | 1 + src/thor/costmatrix.cc | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d8fbceae1..4a287451f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ * FIXED: Handle list type arguments correctly when overriding config with valhalla_build_config [#4799](https://github.com/valhalla/valhalla/pull/4799) * FIXED: `top_speed` range not fully allowed for trucks [#4793](https://github.com/valhalla/valhalla/pull/4793) * FIXED: Trivial routes for CostMatrix [#4634](https://github.com/valhalla/valhalla/pull/4634) + * FIXED: Reset `not_thru_pruning` in CostMatrix after second pass was used [#4817](https://github.com/valhalla/valhalla/pull/4817) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index df4f7cfef9..72ad0b1810 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -71,7 +71,7 @@ void CostMatrix::Clear() { if (check_reverse_connections_) sources_->clear(); - // Clear all source adjacency lists, edge labels, and edge status + // Clear all adjacency lists, edge labels, and edge status // Resize and shrink_to_fit so all capacity is reduced. auto label_reservation = clear_reserved_memory_ ? 0 : max_reserved_labels_count_; auto locs_reservation = clear_reserved_memory_ ? 0 : max_reserved_locations_count_; @@ -105,6 +105,7 @@ void CostMatrix::Clear() { astar_heuristics_[is_fwd].clear(); } best_connection_.clear(); + set_not_thru_pruning(true); ignore_hierarchy_limits_ = false; } From 5c1eb841a29ed4344a71ac257fa1412158a812f9 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Tue, 30 Jul 2024 15:07:39 +0200 Subject: [PATCH 103/198] Fix expansion callback (#4821) --- CHANGELOG.md | 1 + src/thor/costmatrix.cc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a287451f5..4ab15a9da1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,6 +57,7 @@ * FIXED: `top_speed` range not fully allowed for trucks [#4793](https://github.com/valhalla/valhalla/pull/4793) * FIXED: Trivial routes for CostMatrix [#4634](https://github.com/valhalla/valhalla/pull/4634) * FIXED: Reset `not_thru_pruning` in CostMatrix after second pass was used [#4817](https://github.com/valhalla/valhalla/pull/4817) + * FIXED: wrong index used in CostMatrix expansion callback inside reverse connection check [#4821](https://github.com/valhalla/valhalla/pull/4821) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 72ad0b1810..4c1cf16a5c 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -974,7 +974,7 @@ void CostMatrix::CheckReverseConnections(const uint32_t target, if (expansion_callback_) { auto prev_pred = rev_pred.predecessor() == kInvalidLabel ? GraphId{} - : edgelabel_[MATRIX_REV][source][rev_pred.predecessor()].edgeid(); + : edgelabel_[MATRIX_REV][target][rev_pred.predecessor()].edgeid(); expansion_callback_(graphreader, rev_pred.edgeid(), prev_pred, "costmatrix", Expansion_EdgeStatus_connected, rev_pred.cost().secs, rev_pred.path_distance(), rev_pred.cost().cost, From 2272caf98546c5a64622bd15b977c67e6fca19f1 Mon Sep 17 00:00:00 2001 From: Yota Toyama Date: Wed, 31 Jul 2024 20:19:39 +0900 Subject: [PATCH 104/198] Remove manual timeouts of Loki and Skadi service tests (#4823) --- test/loki_service.cc | 3 --- test/skadi_service.cc | 3 --- 2 files changed, 6 deletions(-) diff --git a/test/loki_service.cc b/test/loki_service.cc index 4225d79cfd..dbb3f1072d 100644 --- a/test/loki_service.cc +++ b/test/loki_service.cc @@ -607,9 +607,6 @@ class LokiServiceEnv : public ::testing::Environment { // Elevation service int main(int argc, char* argv[]) { - // make this whole thing bail if it doesnt finish fast - alarm(180); - testing::AddGlobalTestEnvironment(new LokiServiceEnv); testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/test/skadi_service.cc b/test/skadi_service.cc index 5938e46a62..94d6c90bde 100644 --- a/test/skadi_service.cc +++ b/test/skadi_service.cc @@ -241,9 +241,6 @@ TEST(SkadiService, test_requests) { }, 1); - // make this whole thing bail if it doesnt finish fast - alarm(120); - // request and receive client.batch(); } From 94745de62cd9d6f6d76a0b364046e5ac8759f4b7 Mon Sep 17 00:00:00 2001 From: Johannes Nonnenmacher <146193501+johannes-no@users.noreply.github.com> Date: Thu, 1 Aug 2024 19:19:43 +0200 Subject: [PATCH 105/198] Enable edge.country_crossing to be traced (#4825) --- CHANGELOG.md | 1 + docs/docs/api/map-matching/api-reference.md | 5 ++++- proto/trip.proto | 1 + src/baldr/attributes_controller.cc | 1 + src/thor/triplegbuilder.cc | 5 +++++ src/tyr/trace_serializer.cc | 3 +++ valhalla/baldr/attributes_controller.h | 1 + 7 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ab15a9da1..ec54734be8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -136,6 +136,7 @@ * ADDED: `expansion_type` property to `/expansion` [#4784](https://github.com/valhalla/valhalla/pull/4784) * ADDED: inline config arg for `valhalla_build_elevation` script [#4787](https://github.com/valhalla/valhalla/pull/4787) * ADDED: `use_truck_route` [#4809](https://github.com/valhalla/valhalla/pull/4809) + * ADDED: Add option `edge.country_crossing` to trace attributes[4825](https://github.com/valhalla/valhalla/pull/4825) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/docs/docs/api/map-matching/api-reference.md b/docs/docs/api/map-matching/api-reference.md index c0d9c895e3..1385dbd62d 100644 --- a/docs/docs/api/map-matching/api-reference.md +++ b/docs/docs/api/map-matching/api-reference.md @@ -113,6 +113,7 @@ edge.density edge.speed_limit edge.truck_speed edge.truck_route +edge.country_crossing // Node filter keys node.intersecting_edge.begin_heading @@ -216,7 +217,9 @@ Each `edge` may include: | `truck_speed` | Edge truck speed in the units specified. The default is kilometers per hour. | | `truck_route` | True if edge is part of a truck network/route. | | `end_node` | The node at the end of this edge. See the list of [end node items](#end-node-items) for details. | -| `landmarks` | List of landmarks along the edge. They are used as direction support in navigation. | +| `landmarks` | List of landmarks along the edge. They are used as direction support in navigation. | | +| `country_crossing` | True if the edge is a country crossing. | + #### Sign items diff --git a/proto/trip.proto b/proto/trip.proto index ee2dddf811..493a5031f3 100644 --- a/proto/trip.proto +++ b/proto/trip.proto @@ -180,6 +180,7 @@ message TripLeg { repeated StreetName tunnel_name = 55; float elevation_sampling_interval = 56; repeated float elevation = 57; + bool country_crossing = 58; } message IntersectingEdge { diff --git a/src/baldr/attributes_controller.cc b/src/baldr/attributes_controller.cc index 98ba2065e7..2f972dd841 100644 --- a/src/baldr/attributes_controller.cc +++ b/src/baldr/attributes_controller.cc @@ -81,6 +81,7 @@ const std::unordered_map AttributesController::kDefaultAttrib {kEdgeTaggedValues, true}, {kEdgeIndoor, true}, {kEdgeLandmarks, true}, + {kEdgeCountryCrossing, true}, // Node keys {kIncidents, false}, diff --git a/src/thor/triplegbuilder.cc b/src/thor/triplegbuilder.cc index 7ae3e64b87..2b1e5e1fff 100644 --- a/src/thor/triplegbuilder.cc +++ b/src/thor/triplegbuilder.cc @@ -1203,6 +1203,11 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, trip_edge->set_speed(speed); } + // Set country crossing if requested + if (controller(kEdgeCountryCrossing)) { + trip_edge->set_country_crossing(directededge->ctry_crossing()); + } + uint8_t kAccess = 0; if (mode == sif::TravelMode::kBicycle) { kAccess = kBicycleAccess; diff --git a/src/tyr/trace_serializer.cc b/src/tyr/trace_serializer.cc index dfed9514f6..a883bc3299 100644 --- a/src/tyr/trace_serializer.cc +++ b/src/tyr/trace_serializer.cc @@ -189,6 +189,9 @@ void serialize_edges(const AttributesController& controller, if (controller(kEdgeSpeed)) { writer("speed", static_cast(std::round(edge.speed() * scale))); } + if (controller(kEdgeCountryCrossing)) { + writer("country_crossing", static_cast(edge.country_crossing())); + } if (controller(kEdgeLength)) { writer.set_precision(3); writer("length", edge.length_km() * scale); diff --git a/valhalla/baldr/attributes_controller.h b/valhalla/baldr/attributes_controller.h index 51cd6bcda0..4b93d5789e 100644 --- a/valhalla/baldr/attributes_controller.h +++ b/valhalla/baldr/attributes_controller.h @@ -77,6 +77,7 @@ const std::string kEdgeIsUrban = "edge.is_urban"; const std::string kEdgeTaggedValues = "edge.tagged_values"; const std::string kEdgeIndoor = "edge.indoor"; const std::string kEdgeLandmarks = "edge.landmarks"; +const std::string kEdgeCountryCrossing = "edge.country_crossing"; // Node keys const std::string kNodeIntersectingEdgeBeginHeading = "node.intersecting_edge.begin_heading"; From f0d77e02ed59c20c076c3eadd3c2e36143015337 Mon Sep 17 00:00:00 2001 From: Johannes Nonnenmacher <146193501+johannes-no@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:32:08 +0200 Subject: [PATCH 106/198] Unification of turn costs for ramps and roundabouts (#4827) --- CHANGELOG.md | 1 + src/sif/autocost.cc | 10 ++++++---- src/sif/motorcyclecost.cc | 10 ++++++---- src/sif/motorscootercost.cc | 10 ++++++---- src/sif/truckcost.cc | 10 ++++++---- 5 files changed, 25 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec54734be8..8f5bcffac1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -137,6 +137,7 @@ * ADDED: inline config arg for `valhalla_build_elevation` script [#4787](https://github.com/valhalla/valhalla/pull/4787) * ADDED: `use_truck_route` [#4809](https://github.com/valhalla/valhalla/pull/4809) * ADDED: Add option `edge.country_crossing` to trace attributes[4825](https://github.com/valhalla/valhalla/pull/4825) + * CHANGED: Unification of turn costs for ramps and roundabouts[4827](https://github.com/valhalla/valhalla/pull/4827) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/src/sif/autocost.cc b/src/sif/autocost.cc index ea3c14d7ef..e280e52dee 100644 --- a/src/sif/autocost.cc +++ b/src/sif/autocost.cc @@ -46,6 +46,8 @@ constexpr float kTCCrossing = 2.0f; constexpr float kTCUnfavorable = 2.5f; constexpr float kTCUnfavorableSharp = 3.5f; constexpr float kTCReverse = 9.5f; +constexpr float kTCRamp = 1.5f; +constexpr float kTCRoundabout = 0.5f; // How much to favor taxi roads. constexpr float kTaxiFactor = 0.85f; @@ -567,9 +569,9 @@ Cost AutoCost::TransitionCost(const baldr::DirectedEdge* edge, if ((edge->use() != Use::kRamp && pred.use() == Use::kRamp) || (edge->use() == Use::kRamp && pred.use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -635,9 +637,9 @@ Cost AutoCost::TransitionCostReverse(const uint32_t idx, if ((edge->use() != Use::kRamp && pred->use() == Use::kRamp) || (edge->use() == Use::kRamp && pred->use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; diff --git a/src/sif/motorcyclecost.cc b/src/sif/motorcyclecost.cc index 1dcaf9805e..040fede3ee 100644 --- a/src/sif/motorcyclecost.cc +++ b/src/sif/motorcyclecost.cc @@ -41,6 +41,8 @@ constexpr float kTCCrossing = 2.0f; constexpr float kTCUnfavorable = 2.5f; constexpr float kTCUnfavorableSharp = 3.5f; constexpr float kTCReverse = 9.5f; +constexpr float kTCRamp = 1.5f; +constexpr float kTCRoundabout = 0.5f; // Turn costs based on side of street driving constexpr float kRightSideTurnCosts[] = {kTCStraight, kTCSlight, kTCFavorable, @@ -461,9 +463,9 @@ Cost MotorcycleCost::TransitionCost(const baldr::DirectedEdge* edge, if ((edge->use() != Use::kRamp && pred.use() == Use::kRamp) || (edge->use() == Use::kRamp && pred.use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -530,9 +532,9 @@ Cost MotorcycleCost::TransitionCostReverse(const uint32_t idx, if ((edge->use() != Use::kRamp && pred->use() == Use::kRamp) || (edge->use() == Use::kRamp && pred->use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; diff --git a/src/sif/motorscootercost.cc b/src/sif/motorscootercost.cc index a82a2e4af1..9923acb54a 100644 --- a/src/sif/motorscootercost.cc +++ b/src/sif/motorscootercost.cc @@ -46,6 +46,8 @@ constexpr float kTCCrossing = 2.0f; constexpr float kTCUnfavorable = 2.5f; constexpr float kTCUnfavorableSharp = 3.5f; constexpr float kTCReverse = 9.5f; +constexpr float kTCRamp = 1.5f; +constexpr float kTCRoundabout = 0.5f; // Turn costs based on side of street driving constexpr float kRightSideTurnCosts[] = {kTCStraight, kTCSlight, kTCFavorable, @@ -486,9 +488,9 @@ Cost MotorScooterCost::TransitionCost(const baldr::DirectedEdge* edge, if ((edge->use() != Use::kRamp && pred.use() == Use::kRamp) || (edge->use() == Use::kRamp && pred.use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -555,9 +557,9 @@ Cost MotorScooterCost::TransitionCostReverse(const uint32_t idx, if ((edge->use() != Use::kRamp && pred->use() == Use::kRamp) || (edge->use() == Use::kRamp && pred->use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index 6ef4980270..de4a8152c7 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -46,6 +46,8 @@ constexpr float kTCCrossing = 2.0f; constexpr float kTCUnfavorable = 2.5f; constexpr float kTCUnfavorableSharp = 3.5f; constexpr float kTCReverse = 9.5f; +constexpr float kTCRamp = 1.5f; +constexpr float kTCRoundabout = 0.5f; // Default truck attributes constexpr float kDefaultTruckWeight = 21.77f; // Metric Tons (48,000 lbs) @@ -588,9 +590,9 @@ Cost TruckCost::TransitionCost(const baldr::DirectedEdge* edge, if ((edge->use() != Use::kRamp && pred.use() == Use::kRamp) || (edge->use() == Use::kRamp && pred.use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; @@ -665,9 +667,9 @@ Cost TruckCost::TransitionCostReverse(const uint32_t idx, if ((edge->use() != Use::kRamp && pred->use() == Use::kRamp) || (edge->use() == Use::kRamp && pred->use() != Use::kRamp)) { - turn_cost += 1.5f; + turn_cost += kTCRamp; if (edge->roundabout()) - turn_cost += 0.5f; + turn_cost += kTCRoundabout; } float seconds = turn_cost; From f580be3c5aebc5d38ce63dfcad6cded5cabf68fd Mon Sep 17 00:00:00 2001 From: Stepan Kizim <10885920+kinkard@users.noreply.github.com> Date: Sun, 4 Aug 2024 15:10:26 +0200 Subject: [PATCH 107/198] Add `boost` to the building from source for macos doc instruction (#4830) --- docs/docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/building.md b/docs/docs/building.md index 48879841f6..50cb7dac78 100644 --- a/docs/docs/building.md +++ b/docs/docs/building.md @@ -79,7 +79,7 @@ To install Valhalla on macOS, you need to install its dependencies with [Homebre ```bash # install dependencies (automake & czmq are required by prime_server) -brew install automake cmake libtool protobuf-c libspatialite pkg-config sqlite3 jq curl wget czmq lz4 spatialite-tools unzip luajit +brew install automake cmake libtool protobuf-c libspatialite pkg-config sqlite3 jq curl wget czmq lz4 spatialite-tools unzip luajit boost # following packages are needed for running Linux compatible scripts brew install bash coreutils binutils # Update your PATH env variable to include /usr/local/opt/binutils/bin:/usr/local/opt/coreutils/libexec/gnubin From 58a289de51638c55f479101bf5352160902e0182 Mon Sep 17 00:00:00 2001 From: Stepan Kizim <10885920+kinkard@users.noreply.github.com> Date: Sun, 4 Aug 2024 20:30:36 +0200 Subject: [PATCH 108/198] Remove unused headers (#4829) --- CHANGELOG.md | 1 + src/baldr/connectivity_map.cc | 1 - src/baldr/datetime.cc | 3 --- src/baldr/graphid.cc | 2 -- src/baldr/graphreader.cc | 2 -- src/baldr/graphtile.cc | 2 -- src/baldr/location.cc | 2 -- src/baldr/streetname.cc | 1 - src/baldr/streetname_us.cc | 4 +--- src/baldr/streetnames.cc | 1 - src/baldr/streetnames_factory.cc | 1 - src/baldr/streetnames_us.cc | 1 - src/baldr/verbal_text_formatter_us.cc | 5 +---- src/baldr/verbal_text_formatter_us_co.cc | 5 +---- src/baldr/verbal_text_formatter_us_tx.cc | 5 +---- src/bindings/python/python.cc | 1 - src/loki/search.cc | 1 - src/loki/worker.cc | 2 -- src/meili/map_matcher_factory.cc | 2 -- src/midgard/aabb2.cc | 1 - src/midgard/logging.cc | 1 - src/midgard/point_tile_index.cc | 2 ++ src/midgard/util.cc | 2 +- src/mjolnir/bssbuilder.cc | 1 - src/mjolnir/complexrestrictionbuilder.cc | 1 - src/mjolnir/convert_transit.cc | 6 ------ src/mjolnir/edgeinfobuilder.cc | 1 - src/mjolnir/elevationbuilder.cc | 3 +-- src/mjolnir/graphbuilder.cc | 1 - src/mjolnir/graphenhancer.cc | 1 + src/mjolnir/graphvalidator.cc | 6 +----- src/mjolnir/hierarchybuilder.cc | 5 ----- src/mjolnir/idtable.h | 1 - src/mjolnir/ingest_transit.cc | 4 ---- src/mjolnir/linkclassification.cc | 1 - src/mjolnir/osmdata.cc | 1 - src/mjolnir/osmpbfparser.cc | 1 - src/mjolnir/osmway.cc | 1 - src/mjolnir/pbfgraphparser.cc | 2 -- src/mjolnir/restrictionbuilder.cc | 2 +- src/mjolnir/shortcutbuilder.cc | 5 ----- src/mjolnir/statistics.cc | 5 ----- src/mjolnir/statistics.h | 1 - src/mjolnir/statistics_database.cc | 2 -- src/mjolnir/timeparsing.cc | 1 - src/mjolnir/transitbuilder.cc | 2 -- src/mjolnir/valhalla_add_predicted_traffic.cc | 5 +---- src/mjolnir/valhalla_assign_speeds.cc | 1 + src/mjolnir/valhalla_benchmark_admins.cc | 8 -------- src/mjolnir/valhalla_build_connectivity.cc | 1 - src/mjolnir/valhalla_build_statistics.cc | 5 +---- src/mjolnir/valhalla_query_transit.cc | 3 --- src/mjolnir/valhalla_validate_transit.cc | 1 - src/mjolnir/valhalla_ways_to_edges.cc | 1 - src/mjolnir/validatetransit.cc | 1 - src/odin/directionsbuilder.cc | 5 +---- src/odin/enhancedtrippath.cc | 1 - src/odin/maneuver.cc | 1 - src/odin/maneuversbuilder.cc | 3 --- src/odin/markup_formatter.cc | 1 - src/odin/narrative_dictionary.cc | 2 -- src/odin/narrativebuilder.cc | 2 -- src/odin/signs.cc | 1 - src/odin/worker.cc | 5 ----- src/sif/dynamiccost.cc | 1 - src/sif/nocost.cc | 1 - src/skadi/sample.cc | 5 +---- src/thor/alternates.cc | 1 - src/thor/astar_bss.cc | 2 -- src/thor/dijkstras.cc | 1 - src/thor/isochrone.cc | 2 -- src/thor/map_matcher.cc | 1 - src/thor/multimodal.cc | 1 - src/thor/trace_attributes_action.cc | 1 - src/thor/trace_route_action.cc | 1 - src/thor/triplegbuilder.cc | 2 -- src/thor/worker.cc | 3 --- src/tile_server.cc | 1 - src/tyr/route_serializer.cc | 1 - src/tyr/route_serializer_valhalla.cc | 1 - src/tyr/serializers.cc | 4 ---- src/valhalla_export_edges.cc | 1 - src/valhalla_run_matrix.cc | 1 - src/valhalla_run_route.cc | 2 -- src/valhalla_service.cc | 6 ------ src/worker.cc | 1 - test/access_restriction.cc | 2 -- test/actor.cc | 2 -- test/admin.cc | 2 -- test/alternates.cc | 2 -- test/astar.cc | 2 -- test/double_bucket_queue.cc | 1 - test/edgeinfobuilder.cc | 1 - test/graphbuilder.cc | 1 - test/grid_traversal.cc | 2 -- test/gurka/gurka.h | 3 --- test/gurka/test_admin.cc | 1 - test/gurka/test_bidir_search.cc | 1 - test/gurka/test_costing_with_traffic.cc | 1 + test/gurka/test_expansion.cc | 1 - test/gurka/test_fixed_speed.cc | 1 - test/gurka/test_hov.cc | 1 + test/gurka/test_indoor.cc | 1 - test/gurka/test_instructions_long_street_names.cc | 1 - test/gurka/test_instructions_roundabout_multicue.cc | 1 - test/gurka/test_multi_level_loki.cc | 1 - test/gurka/test_node_type.cc | 1 - test/gurka/test_osrm_serializer.cc | 1 + test/gurka/test_pathlocation_serialization.cc | 2 -- test/gurka/test_precision.cc | 1 - test/gurka/test_ramps_tc.cc | 1 - test/gurka/test_reach.cc | 1 - test/gurka/test_route.cc | 3 ++- test/gurka/test_route_summary.cc | 2 -- test/gurka/test_route_with_narrative_languages.cc | 1 - test/gurka/test_shortest.cc | 1 - test/gurka/test_tagged_values.cc | 1 - test/gurka/test_top_speed.cc | 1 - test/gurka/test_traffic.cc | 3 --- test/gurka/test_traffic_smoothing.cc | 1 + test/gurka/test_use.cc | 1 - test/gurka/test_use_lit.cc | 1 - test/gurka/test_via_waypoints.cc | 1 - test/gurka/test_warnings.cc | 1 - test/http_tiles.cc | 1 - test/instructions.cc | 2 -- test/logging.cc | 1 - test/matrix.cc | 1 - test/minbb.cc | 1 - test/multipoint_routes.cc | 2 -- test/names.cc | 1 - test/narrativebuilder.cc | 1 - test/node_search.cc | 1 - test/predictive_traffic.cc | 1 - test/reach.cc | 1 - test/recover_shortcut.cc | 2 -- test/search.cc | 1 - test/shape_attributes.cc | 3 --- test/skadi_service.cc | 1 - test/streetname.cc | 2 -- test/streetname_us.cc | 2 -- test/streetnames_us.cc | 1 - test/summary.cc | 3 --- test/tar_index.cc | 1 - test/test.cc | 1 - test/test.h | 4 ---- test/thor_worker.cc | 1 - test/tilehierarchy.cc | 2 -- test/timedep_paths.cc | 2 -- test/timeparsing.cc | 1 - test/trivial_paths.cc | 2 -- test/turn.cc | 4 ---- test/turnlanes.cc | 2 -- test/urban.cc | 1 - test/util_mjolnir.cc | 2 -- test/utrecht.cc | 1 - valhalla/baldr/admininfo.h | 1 - valhalla/baldr/complexrestriction.h | 4 ---- valhalla/baldr/curler.h | 1 - valhalla/baldr/datetime.h | 2 -- valhalla/baldr/graphid.h | 2 -- valhalla/baldr/graphreader.h | 1 - valhalla/baldr/json.h | 2 -- valhalla/baldr/landmark.h | 2 -- valhalla/baldr/pathlocation.h | 2 -- valhalla/baldr/streetname.h | 1 - valhalla/baldr/streetname_us.h | 1 - valhalla/baldr/streetnames_us.h | 1 - valhalla/baldr/tilehierarchy.h | 1 - valhalla/baldr/timedomain.h | 1 - valhalla/baldr/traffictile.h | 1 - valhalla/baldr/verbal_text_formatter_us_co.h | 2 -- valhalla/baldr/verbal_text_formatter_us_tx.h | 2 -- valhalla/config.h | 1 - valhalla/loki/node_search.h | 1 - valhalla/loki/search.h | 3 --- valhalla/loki/worker.h | 1 - valhalla/meili/candidate_search.h | 3 --- valhalla/meili/map_matcher.h | 1 - valhalla/meili/map_matcher_factory.h | 3 --- valhalla/meili/stateid.h | 1 - valhalla/meili/traffic_segment_matcher.h | 1 - valhalla/meili/transition_cost_model.h | 2 -- valhalla/meili/viterbi_search.h | 1 - valhalla/midgard/elevation_encoding.h | 1 - valhalla/midgard/point2.h | 3 --- valhalla/midgard/polyline2.h | 1 - valhalla/midgard/sequence.h | 3 --- valhalla/midgard/tiles.h | 2 -- valhalla/midgard/util.h | 4 +--- valhalla/mjolnir/adminbuilder.h | 3 --- valhalla/mjolnir/bssbuilder.h | 1 - valhalla/mjolnir/complexrestrictionbuilder.h | 3 --- valhalla/mjolnir/edgeinfobuilder.h | 2 -- valhalla/mjolnir/ferry_connections.h | 2 -- valhalla/mjolnir/graphenhancer.h | 1 - valhalla/mjolnir/graphfilter.h | 1 - valhalla/mjolnir/graphtilebuilder.h | 3 --- valhalla/mjolnir/graphvalidator.h | 1 - valhalla/mjolnir/hierarchybuilder.h | 1 - valhalla/mjolnir/ingest_transit.h | 4 ---- valhalla/mjolnir/landmarks.h | 1 - valhalla/mjolnir/linkclassification.h | 2 -- valhalla/mjolnir/node_expander.h | 2 -- valhalla/mjolnir/osmaccess.h | 2 -- valhalla/mjolnir/osmadmindata.h | 2 -- valhalla/mjolnir/osmdata.h | 3 --- valhalla/mjolnir/osmlinguistic.h | 3 --- valhalla/mjolnir/osmnode.h | 2 -- valhalla/mjolnir/pbfadminparser.h | 1 - valhalla/mjolnir/pbfgraphparser.h | 1 - valhalla/mjolnir/restrictionbuilder.h | 3 --- valhalla/mjolnir/servicedays.h | 4 ---- valhalla/mjolnir/shortcutbuilder.h | 1 - valhalla/mjolnir/transitbuilder.h | 1 - valhalla/mjolnir/validatetransit.h | 1 - valhalla/odin/maneuver.h | 1 - valhalla/odin/narrative_builder_factory.h | 1 - valhalla/odin/util.h | 3 --- valhalla/sif/autocost.h | 3 --- valhalla/sif/bicyclecost.h | 1 - valhalla/sif/costfactory.h | 1 - valhalla/sif/edgelabel.h | 1 - valhalla/sif/hierarchylimits.h | 2 +- valhalla/sif/motorcyclecost.h | 3 --- valhalla/sif/motorscootercost.h | 3 --- valhalla/sif/nocost.h | 3 --- valhalla/sif/pedestriancost.h | 1 - valhalla/sif/transitcost.h | 1 - valhalla/sif/truckcost.h | 1 - valhalla/skadi/sample.h | 1 - valhalla/skadi/util.h | 1 - valhalla/thor/astar_bss.h | 1 - valhalla/thor/bidirectional_astar.h | 3 --- valhalla/thor/centroid.h | 3 --- valhalla/thor/costmatrix.h | 2 -- valhalla/thor/dijkstras.h | 2 -- valhalla/thor/isochrone.h | 4 ---- valhalla/thor/map_matcher.h | 4 ---- valhalla/thor/multimodal.h | 1 - valhalla/thor/pathalgorithm.h | 4 ---- valhalla/thor/pathinfo.h | 1 - valhalla/thor/route_matcher.h | 5 ----- valhalla/thor/timedistancebssmatrix.h | 2 -- valhalla/thor/timedistancematrix.h | 2 -- valhalla/thor/triplegbuilder.h | 2 -- valhalla/thor/unidirectional_astar.h | 1 - valhalla/thor/worker.h | 1 - valhalla/tyr/actor.h | 1 - valhalla/tyr/serializers.h | 2 -- 250 files changed, 25 insertions(+), 446 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f5bcffac1..3a7a8242da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * REMOVED: needs_ci_run script [#4423](https://github.com/valhalla/valhalla/pull/4423) * REMOVED: unused vehicle types in AutoCost and segway; renamed kTruck to "truck" instead of "tractor_trailer" [#4430](https://github.com/valhalla/valhalla/pull/4430) * REMOVED: ./bench and related files/code [#4560](https://github.com/valhalla/valhalla/pull/4560) + * REMOVED: unused headers [#4829](https://github.com/valhalla/valhalla/pull/4829) * **Bug Fix** * FIXED: gcc13 was missing some std header includes [#4154](https://github.com/valhalla/valhalla/pull/4154) * FIXED: when reclassifying ferry edges, remove destonly from ways only if the connecting way was destonly [#4118](https://github.com/valhalla/valhalla/pull/4118) diff --git a/src/baldr/connectivity_map.cc b/src/baldr/connectivity_map.cc index 9dfdfb02d7..8dd90785c7 100644 --- a/src/baldr/connectivity_map.cc +++ b/src/baldr/connectivity_map.cc @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/src/baldr/datetime.cc b/src/baldr/datetime.cc index f29d9cce06..86b436dcf6 100644 --- a/src/baldr/datetime.cc +++ b/src/baldr/datetime.cc @@ -1,7 +1,4 @@ #include -#include -#include -#include #include #include diff --git a/src/baldr/graphid.cc b/src/baldr/graphid.cc index a9c41106f3..35c6685884 100644 --- a/src/baldr/graphid.cc +++ b/src/baldr/graphid.cc @@ -1,5 +1,3 @@ -#include - #include "baldr/graphid.h" namespace valhalla { diff --git a/src/baldr/graphreader.cc b/src/baldr/graphreader.cc index 16724d4cc7..b4086c73a7 100644 --- a/src/baldr/graphreader.cc +++ b/src/baldr/graphreader.cc @@ -1,5 +1,3 @@ -#include -#include #include #include #include diff --git a/src/baldr/graphtile.cc b/src/baldr/graphtile.cc index 771a5e216e..265ee200c9 100644 --- a/src/baldr/graphtile.cc +++ b/src/baldr/graphtile.cc @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/src/baldr/location.cc b/src/baldr/location.cc index 93d3f16dd9..bd9b9e4066 100644 --- a/src/baldr/location.cc +++ b/src/baldr/location.cc @@ -1,5 +1,3 @@ -#include - #include "baldr/location.h" #include "baldr/rapidjson_utils.h" #include "midgard/logging.h" diff --git a/src/baldr/streetname.cc b/src/baldr/streetname.cc index ec764eb644..a071bc1341 100644 --- a/src/baldr/streetname.cc +++ b/src/baldr/streetname.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/baldr/streetname_us.cc b/src/baldr/streetname_us.cc index fc992e0b96..b23f6253e9 100644 --- a/src/baldr/streetname_us.cc +++ b/src/baldr/streetname_us.cc @@ -1,7 +1,5 @@ -#include - -#include "baldr/streetname.h" #include "baldr/streetname_us.h" +#include "baldr/streetname.h" namespace valhalla { namespace baldr { diff --git a/src/baldr/streetnames.cc b/src/baldr/streetnames.cc index 526fc34dbf..8c1bdacce7 100644 --- a/src/baldr/streetnames.cc +++ b/src/baldr/streetnames.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/baldr/streetnames_factory.cc b/src/baldr/streetnames_factory.cc index 35a0708698..37b14a07fe 100644 --- a/src/baldr/streetnames_factory.cc +++ b/src/baldr/streetnames_factory.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/baldr/streetnames_us.cc b/src/baldr/streetnames_us.cc index aeb0725d57..79eaa54ab7 100644 --- a/src/baldr/streetnames_us.cc +++ b/src/baldr/streetnames_us.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/baldr/verbal_text_formatter_us.cc b/src/baldr/verbal_text_formatter_us.cc index 1b4dfc45d0..cf49e912d9 100644 --- a/src/baldr/verbal_text_formatter_us.cc +++ b/src/baldr/verbal_text_formatter_us.cc @@ -1,8 +1,5 @@ -#include -#include - -#include "baldr/verbal_text_formatter.h" #include "baldr/verbal_text_formatter_us.h" +#include "baldr/verbal_text_formatter.h" #include "midgard/util.h" namespace valhalla { diff --git a/src/baldr/verbal_text_formatter_us_co.cc b/src/baldr/verbal_text_formatter_us_co.cc index 2c7dcf10ff..5fcd3158e5 100644 --- a/src/baldr/verbal_text_formatter_us_co.cc +++ b/src/baldr/verbal_text_formatter_us_co.cc @@ -1,9 +1,6 @@ -#include -#include - +#include "baldr/verbal_text_formatter_us_co.h" #include "baldr/verbal_text_formatter.h" #include "baldr/verbal_text_formatter_us.h" -#include "baldr/verbal_text_formatter_us_co.h" #include "midgard/util.h" namespace valhalla { diff --git a/src/baldr/verbal_text_formatter_us_tx.cc b/src/baldr/verbal_text_formatter_us_tx.cc index c6cb11638a..7000fa5d2d 100644 --- a/src/baldr/verbal_text_formatter_us_tx.cc +++ b/src/baldr/verbal_text_formatter_us_tx.cc @@ -1,9 +1,6 @@ -#include -#include - +#include "baldr/verbal_text_formatter_us_tx.h" #include "baldr/verbal_text_formatter.h" #include "baldr/verbal_text_formatter_us.h" -#include "baldr/verbal_text_formatter_us_tx.h" #include "midgard/util.h" namespace valhalla { diff --git a/src/bindings/python/python.cc b/src/bindings/python/python.cc index 3701cf6d73..63dda05ff0 100644 --- a/src/bindings/python/python.cc +++ b/src/bindings/python/python.cc @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "baldr/rapidjson_utils.h" diff --git a/src/loki/search.cc b/src/loki/search.cc index 045d18f807..c96bfaa006 100644 --- a/src/loki/search.cc +++ b/src/loki/search.cc @@ -9,7 +9,6 @@ #include #include #include -#include #include using namespace valhalla::midgard; diff --git a/src/loki/worker.cc b/src/loki/worker.cc index 0a927f21e8..3f96f4f22e 100644 --- a/src/loki/worker.cc +++ b/src/loki/worker.cc @@ -1,10 +1,8 @@ #include #include #include -#include #include #include -#include #include #include "baldr/json.h" diff --git a/src/meili/map_matcher_factory.cc b/src/meili/map_matcher_factory.cc index bf2f6bbd47..f8c3b02ec4 100644 --- a/src/meili/map_matcher_factory.cc +++ b/src/meili/map_matcher_factory.cc @@ -1,5 +1,3 @@ -#include - #include "baldr/graphreader.h" #include "baldr/tilehierarchy.h" #include "sif/autocost.h" diff --git a/src/midgard/aabb2.cc b/src/midgard/aabb2.cc index 2890963ba9..cccf0706a4 100644 --- a/src/midgard/aabb2.cc +++ b/src/midgard/aabb2.cc @@ -5,7 +5,6 @@ #include "midgard/util.h" #include -#include namespace { diff --git a/src/midgard/logging.cc b/src/midgard/logging.cc index d4eaac68c7..caa90292e8 100644 --- a/src/midgard/logging.cc +++ b/src/midgard/logging.cc @@ -8,7 +8,6 @@ #include #include #include -#include #include #ifdef __ANDROID__ diff --git a/src/midgard/point_tile_index.cc b/src/midgard/point_tile_index.cc index 29234c7a99..b76131eb35 100644 --- a/src/midgard/point_tile_index.cc +++ b/src/midgard/point_tile_index.cc @@ -1,5 +1,7 @@ #include "midgard/point_tile_index.h" +#include + namespace valhalla { namespace midgard { diff --git a/src/midgard/util.cc b/src/midgard/util.cc index f1fcdba1f1..182046c3ac 100644 --- a/src/midgard/util.cc +++ b/src/midgard/util.cc @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/mjolnir/bssbuilder.cc b/src/mjolnir/bssbuilder.cc index fe139dfccc..444b7deacf 100644 --- a/src/mjolnir/bssbuilder.cc +++ b/src/mjolnir/bssbuilder.cc @@ -6,7 +6,6 @@ #include #include -#include #include #include #include diff --git a/src/mjolnir/complexrestrictionbuilder.cc b/src/mjolnir/complexrestrictionbuilder.cc index e6f1439547..fe2ce5c403 100644 --- a/src/mjolnir/complexrestrictionbuilder.cc +++ b/src/mjolnir/complexrestrictionbuilder.cc @@ -1,5 +1,4 @@ #include -#include #include #include "baldr/complexrestriction.h" diff --git a/src/mjolnir/convert_transit.cc b/src/mjolnir/convert_transit.cc index c88cfffee2..6cbfb4a6cb 100644 --- a/src/mjolnir/convert_transit.cc +++ b/src/mjolnir/convert_transit.cc @@ -1,19 +1,13 @@ #include #include -#include #include -#include #include -#include -#include -#include #include #include #include #include "baldr/rapidjson_utils.h" #include -#include #include #include diff --git a/src/mjolnir/edgeinfobuilder.cc b/src/mjolnir/edgeinfobuilder.cc index 9e7f5ab249..e37a4af4c1 100644 --- a/src/mjolnir/edgeinfobuilder.cc +++ b/src/mjolnir/edgeinfobuilder.cc @@ -1,5 +1,4 @@ #include -#include #include #include "baldr/edgeinfo.h" diff --git a/src/mjolnir/elevationbuilder.cc b/src/mjolnir/elevationbuilder.cc index cd77909a85..176026d01b 100644 --- a/src/mjolnir/elevationbuilder.cc +++ b/src/mjolnir/elevationbuilder.cc @@ -1,11 +1,10 @@ #include "mjolnir/elevationbuilder.h" #include +#include #include #include -#include - #include "baldr/graphconstants.h" #include "baldr/graphid.h" #include "baldr/graphreader.h" diff --git a/src/mjolnir/graphbuilder.cc b/src/mjolnir/graphbuilder.cc index 47e9fcf0c9..8e13b9ae3c 100644 --- a/src/mjolnir/graphbuilder.cc +++ b/src/mjolnir/graphbuilder.cc @@ -1,6 +1,5 @@ #include #include -#include #include #include diff --git a/src/mjolnir/graphenhancer.cc b/src/mjolnir/graphenhancer.cc index 5b24a9f7f1..95d1d5dd9e 100644 --- a/src/mjolnir/graphenhancer.cc +++ b/src/mjolnir/graphenhancer.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/src/mjolnir/graphvalidator.cc b/src/mjolnir/graphvalidator.cc index 116b3dc509..4a111b2182 100644 --- a/src/mjolnir/graphvalidator.cc +++ b/src/mjolnir/graphvalidator.cc @@ -1,18 +1,14 @@ - #include "mjolnir/graphvalidator.h" #include "mjolnir/graphtilebuilder.h" #include "mjolnir/util.h" #include #include -#include #include #include #include -#include -#include +#include #include -#include #include #include #include diff --git a/src/mjolnir/hierarchybuilder.cc b/src/mjolnir/hierarchybuilder.cc index 533186a306..de0e9f6506 100644 --- a/src/mjolnir/hierarchybuilder.cc +++ b/src/mjolnir/hierarchybuilder.cc @@ -1,13 +1,8 @@ #include "mjolnir/hierarchybuilder.h" #include "mjolnir/graphtilebuilder.h" -#include #include -#include -#include -#include -#include #include #include #include diff --git a/src/mjolnir/idtable.h b/src/mjolnir/idtable.h index 8417a2713d..9ca8eaa3db 100644 --- a/src/mjolnir/idtable.h +++ b/src/mjolnir/idtable.h @@ -5,7 +5,6 @@ #include #include #include -#include #include diff --git a/src/mjolnir/ingest_transit.cc b/src/mjolnir/ingest_transit.cc index c03d0e20e4..f63842b667 100644 --- a/src/mjolnir/ingest_transit.cc +++ b/src/mjolnir/ingest_transit.cc @@ -1,13 +1,10 @@ #include #include #include -#include #include -#include #include #include #include -#include #include #include #include @@ -15,7 +12,6 @@ #include #include -#include #include #include diff --git a/src/mjolnir/linkclassification.cc b/src/mjolnir/linkclassification.cc index 3a7f07a9fb..d7551d0ceb 100644 --- a/src/mjolnir/linkclassification.cc +++ b/src/mjolnir/linkclassification.cc @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/src/mjolnir/osmdata.cc b/src/mjolnir/osmdata.cc index 591e4a57db..3c38a828f0 100644 --- a/src/mjolnir/osmdata.cc +++ b/src/mjolnir/osmdata.cc @@ -1,7 +1,6 @@ #include #include #include -#include #include diff --git a/src/mjolnir/osmpbfparser.cc b/src/mjolnir/osmpbfparser.cc index 0c2af9d7c6..05a105a206 100644 --- a/src/mjolnir/osmpbfparser.cc +++ b/src/mjolnir/osmpbfparser.cc @@ -33,7 +33,6 @@ #else #include #endif -#include #include #include diff --git a/src/mjolnir/osmway.cc b/src/mjolnir/osmway.cc index 4260fe2b28..45db3aade7 100644 --- a/src/mjolnir/osmway.cc +++ b/src/mjolnir/osmway.cc @@ -4,7 +4,6 @@ #include "midgard/logging.h" #include -#include using namespace valhalla::baldr; diff --git a/src/mjolnir/pbfgraphparser.cc b/src/mjolnir/pbfgraphparser.cc index b9106f1e3b..a9587d5df0 100644 --- a/src/mjolnir/pbfgraphparser.cc +++ b/src/mjolnir/pbfgraphparser.cc @@ -1,10 +1,8 @@ -#include #include #include #include #include -#include #include #include "baldr/complexrestriction.h" diff --git a/src/mjolnir/restrictionbuilder.cc b/src/mjolnir/restrictionbuilder.cc index da77858a16..fcc46e70ab 100644 --- a/src/mjolnir/restrictionbuilder.cc +++ b/src/mjolnir/restrictionbuilder.cc @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/src/mjolnir/shortcutbuilder.cc b/src/mjolnir/shortcutbuilder.cc index a9140fb599..9dccb4b9d0 100644 --- a/src/mjolnir/shortcutbuilder.cc +++ b/src/mjolnir/shortcutbuilder.cc @@ -3,11 +3,6 @@ #include #include -#include -#include -#include -#include -#include #include #include #include diff --git a/src/mjolnir/statistics.cc b/src/mjolnir/statistics.cc index debb274c13..faa38c0f72 100644 --- a/src/mjolnir/statistics.cc +++ b/src/mjolnir/statistics.cc @@ -4,13 +4,8 @@ #include "mjolnir/graphvalidator.h" #include "statistics.h" -#include #include #include -#include -#include -#include -#include #include #include #include diff --git a/src/mjolnir/statistics.h b/src/mjolnir/statistics.h index 952ad1837b..fde30e0a50 100644 --- a/src/mjolnir/statistics.h +++ b/src/mjolnir/statistics.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_STATISTICS_H_ #include -#include #include #include #include diff --git a/src/mjolnir/statistics_database.cc b/src/mjolnir/statistics_database.cc index 8f8806f3be..0ba0d00793 100644 --- a/src/mjolnir/statistics_database.cc +++ b/src/mjolnir/statistics_database.cc @@ -1,8 +1,6 @@ #include "statistics.h" #include -#include - #include "filesystem.h" #include "midgard/logging.h" #include "mjolnir/util.h" diff --git a/src/mjolnir/timeparsing.cc b/src/mjolnir/timeparsing.cc index f600c683e6..3b5a852d04 100644 --- a/src/mjolnir/timeparsing.cc +++ b/src/mjolnir/timeparsing.cc @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/src/mjolnir/transitbuilder.cc b/src/mjolnir/transitbuilder.cc index de768dce74..09c73a96f1 100644 --- a/src/mjolnir/transitbuilder.cc +++ b/src/mjolnir/transitbuilder.cc @@ -3,12 +3,10 @@ #include #include -#include #include #include #include #include -#include #include #include diff --git a/src/mjolnir/valhalla_add_predicted_traffic.cc b/src/mjolnir/valhalla_add_predicted_traffic.cc index f415c0aed2..2eb13290e5 100644 --- a/src/mjolnir/valhalla_add_predicted_traffic.cc +++ b/src/mjolnir/valhalla_add_predicted_traffic.cc @@ -1,11 +1,9 @@ #include #include #include -#include #include -#include #include -#include +#include #include #include @@ -18,7 +16,6 @@ #include "baldr/graphreader.h" #include "baldr/predictedspeeds.h" -#include "baldr/rapidjson_utils.h" #include "filesystem.h" #include "midgard/logging.h" #include "midgard/util.h" diff --git a/src/mjolnir/valhalla_assign_speeds.cc b/src/mjolnir/valhalla_assign_speeds.cc index 9dcfc28af1..15971137c4 100644 --- a/src/mjolnir/valhalla_assign_speeds.cc +++ b/src/mjolnir/valhalla_assign_speeds.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/src/mjolnir/valhalla_benchmark_admins.cc b/src/mjolnir/valhalla_benchmark_admins.cc index 7f98e9af85..ea9b13cf87 100644 --- a/src/mjolnir/valhalla_benchmark_admins.cc +++ b/src/mjolnir/valhalla_benchmark_admins.cc @@ -1,14 +1,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include #include -#include #include #include diff --git a/src/mjolnir/valhalla_build_connectivity.cc b/src/mjolnir/valhalla_build_connectivity.cc index 5d9effce0a..7089e42b94 100644 --- a/src/mjolnir/valhalla_build_connectivity.cc +++ b/src/mjolnir/valhalla_build_connectivity.cc @@ -1,6 +1,5 @@ #include #include -#include #include #include #include diff --git a/src/mjolnir/valhalla_build_statistics.cc b/src/mjolnir/valhalla_build_statistics.cc index ec34f9e88f..da32850049 100644 --- a/src/mjolnir/valhalla_build_statistics.cc +++ b/src/mjolnir/valhalla_build_statistics.cc @@ -3,16 +3,13 @@ #include "statistics.h" #include "baldr/rapidjson_utils.h" -#include #include #include #include #include #include #include -#include -#include -#include +#include #include #include #include diff --git a/src/mjolnir/valhalla_query_transit.cc b/src/mjolnir/valhalla_query_transit.cc index 5d35b7f06b..fa3e109f95 100644 --- a/src/mjolnir/valhalla_query_transit.cc +++ b/src/mjolnir/valhalla_query_transit.cc @@ -3,13 +3,10 @@ #include "baldr/rapidjson_utils.h" #include #include -#include #include #include #include #include -#include -#include #include #include diff --git a/src/mjolnir/valhalla_validate_transit.cc b/src/mjolnir/valhalla_validate_transit.cc index 3503ce44b2..2e243be7af 100644 --- a/src/mjolnir/valhalla_validate_transit.cc +++ b/src/mjolnir/valhalla_validate_transit.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/mjolnir/valhalla_ways_to_edges.cc b/src/mjolnir/valhalla_ways_to_edges.cc index 28e5cbd7be..0505707fde 100644 --- a/src/mjolnir/valhalla_ways_to_edges.cc +++ b/src/mjolnir/valhalla_ways_to_edges.cc @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/src/mjolnir/validatetransit.cc b/src/mjolnir/validatetransit.cc index 605f50f90b..2ee45a41fa 100644 --- a/src/mjolnir/validatetransit.cc +++ b/src/mjolnir/validatetransit.cc @@ -5,7 +5,6 @@ #include "mjolnir/servicedays.h" #include -#include #include #include diff --git a/src/odin/directionsbuilder.cc b/src/odin/directionsbuilder.cc index 2f0515f80a..cb6eed7585 100644 --- a/src/odin/directionsbuilder.cc +++ b/src/odin/directionsbuilder.cc @@ -1,8 +1,5 @@ -#include -#include - -#include "midgard/logging.h" #include "odin/directionsbuilder.h" +#include "midgard/logging.h" #include "odin/enhancedtrippath.h" #include "odin/maneuversbuilder.h" #include "odin/markup_formatter.h" diff --git a/src/odin/enhancedtrippath.cc b/src/odin/enhancedtrippath.cc index 595f2feda5..d93017c971 100644 --- a/src/odin/enhancedtrippath.cc +++ b/src/odin/enhancedtrippath.cc @@ -1,6 +1,5 @@ #include #include -#include #include "baldr/turn.h" #include "baldr/turnlanes.h" diff --git a/src/odin/maneuver.cc b/src/odin/maneuver.cc index d2ea4ff5eb..f42c4a5c34 100644 --- a/src/odin/maneuver.cc +++ b/src/odin/maneuver.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/odin/maneuversbuilder.cc b/src/odin/maneuversbuilder.cc index 4723116381..4c306d1eb4 100644 --- a/src/odin/maneuversbuilder.cc +++ b/src/odin/maneuversbuilder.cc @@ -1,11 +1,8 @@ #include #include -#include -#include #include #include #include -#include #include #include diff --git a/src/odin/markup_formatter.cc b/src/odin/markup_formatter.cc index bb6d074cd2..2d33ee8cbc 100644 --- a/src/odin/markup_formatter.cc +++ b/src/odin/markup_formatter.cc @@ -4,7 +4,6 @@ #include #include -#include #include "odin/markup_formatter.h" #include "proto/common.pb.h" diff --git a/src/odin/narrative_dictionary.cc b/src/odin/narrative_dictionary.cc index 7920017f65..b5d168255f 100644 --- a/src/odin/narrative_dictionary.cc +++ b/src/odin/narrative_dictionary.cc @@ -1,5 +1,3 @@ -#include - #include #include "midgard/logging.h" diff --git a/src/odin/narrativebuilder.cc b/src/odin/narrativebuilder.cc index 1d200fd547..db09fca81b 100644 --- a/src/odin/narrativebuilder.cc +++ b/src/odin/narrativebuilder.cc @@ -1,12 +1,10 @@ #include #include -#include #include #include #include #include -#include #include "baldr/verbal_text_formatter.h" #include "midgard/constants.h" diff --git a/src/odin/signs.cc b/src/odin/signs.cc index a9bd13f435..3c10f813ef 100644 --- a/src/odin/signs.cc +++ b/src/odin/signs.cc @@ -1,6 +1,5 @@ #include #include -#include #include "midgard/logging.h" diff --git a/src/odin/worker.cc b/src/odin/worker.cc index 87197e600e..1e69dd76a0 100644 --- a/src/odin/worker.cc +++ b/src/odin/worker.cc @@ -1,10 +1,5 @@ -#include #include -#include -#include #include -#include -#include #include diff --git a/src/sif/dynamiccost.cc b/src/sif/dynamiccost.cc index e26dcc0b09..dfc07f8490 100644 --- a/src/sif/dynamiccost.cc +++ b/src/sif/dynamiccost.cc @@ -1,5 +1,4 @@ #include -#include #include "baldr/graphconstants.h" #include "midgard/util.h" diff --git a/src/sif/nocost.cc b/src/sif/nocost.cc index 083b996a85..dbd2a7a0df 100644 --- a/src/sif/nocost.cc +++ b/src/sif/nocost.cc @@ -10,7 +10,6 @@ #ifdef INLINE_TEST #include "test.h" #include "worker.h" -#include #endif using namespace valhalla::midgard; diff --git a/src/skadi/sample.cc b/src/skadi/sample.cc index 92d8161834..7e1cd5e906 100644 --- a/src/skadi/sample.cc +++ b/src/skadi/sample.cc @@ -2,15 +2,12 @@ #include #include -#include #include -#include #include #include #include -#include -#include #include +#include #include #include diff --git a/src/thor/alternates.cc b/src/thor/alternates.cc index 1a2e946a20..46ee725b1c 100644 --- a/src/thor/alternates.cc +++ b/src/thor/alternates.cc @@ -1,4 +1,3 @@ -#include #include #include "thor/alternates.h" diff --git a/src/thor/astar_bss.cc b/src/thor/astar_bss.cc index 74a44cdce3..e4887b0cfa 100644 --- a/src/thor/astar_bss.cc +++ b/src/thor/astar_bss.cc @@ -2,8 +2,6 @@ #include "baldr/datetime.h" #include "midgard/logging.h" #include -#include // TODO remove if not needed -#include using namespace valhalla::baldr; using namespace valhalla::sif; diff --git a/src/thor/dijkstras.cc b/src/thor/dijkstras.cc index 249e9f7a91..21df148084 100644 --- a/src/thor/dijkstras.cc +++ b/src/thor/dijkstras.cc @@ -3,7 +3,6 @@ #include "midgard/distanceapproximator.h" #include "midgard/logging.h" #include -#include using namespace valhalla::midgard; using namespace valhalla::baldr; diff --git a/src/thor/isochrone.cc b/src/thor/isochrone.cc index 64c3811c49..9865689f72 100644 --- a/src/thor/isochrone.cc +++ b/src/thor/isochrone.cc @@ -3,8 +3,6 @@ #include "midgard/distanceapproximator.h" #include "midgard/logging.h" #include -#include // TODO remove if not needed -#include using namespace valhalla::midgard; using namespace valhalla::baldr; diff --git a/src/thor/map_matcher.cc b/src/thor/map_matcher.cc index cd67c45884..96c4ce3df5 100644 --- a/src/thor/map_matcher.cc +++ b/src/thor/map_matcher.cc @@ -1,6 +1,5 @@ #include "midgard/logging.h" #include -#include #include #include "baldr/datetime.h" diff --git a/src/thor/multimodal.cc b/src/thor/multimodal.cc index 498ad6bd6f..04eff8549d 100644 --- a/src/thor/multimodal.cc +++ b/src/thor/multimodal.cc @@ -3,7 +3,6 @@ #include "midgard/logging.h" #include "worker.h" #include -#include using namespace valhalla::baldr; using namespace valhalla::sif; diff --git a/src/thor/trace_attributes_action.cc b/src/thor/trace_attributes_action.cc index c0140368bd..14a79d859c 100644 --- a/src/thor/trace_attributes_action.cc +++ b/src/thor/trace_attributes_action.cc @@ -1,5 +1,4 @@ #include -#include #include #include #include diff --git a/src/thor/trace_route_action.cc b/src/thor/trace_route_action.cc index a9692536b8..a8ebd7a6f4 100644 --- a/src/thor/trace_route_action.cc +++ b/src/thor/trace_route_action.cc @@ -2,7 +2,6 @@ #include #include -#include #include #include #include diff --git a/src/thor/triplegbuilder.cc b/src/thor/triplegbuilder.cc index 2b1e5e1fff..742504f905 100644 --- a/src/thor/triplegbuilder.cc +++ b/src/thor/triplegbuilder.cc @@ -1,8 +1,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/src/thor/worker.cc b/src/thor/worker.cc index afe1e8373e..0b6ddaf7f2 100644 --- a/src/thor/worker.cc +++ b/src/thor/worker.cc @@ -1,10 +1,7 @@ -#include #include -#include #include #include #include -#include #include "midgard/constants.h" #include "midgard/logging.h" diff --git a/src/tile_server.cc b/src/tile_server.cc index 9d1984a1d9..61b0b9550e 100644 --- a/src/tile_server.cc +++ b/src/tile_server.cc @@ -10,7 +10,6 @@ #include #include -#include #include #include diff --git a/src/tyr/route_serializer.cc b/src/tyr/route_serializer.cc index 370e20be22..90050cf9cd 100644 --- a/src/tyr/route_serializer.cc +++ b/src/tyr/route_serializer.cc @@ -1,5 +1,4 @@ #include -#include #include #include diff --git a/src/tyr/route_serializer_valhalla.cc b/src/tyr/route_serializer_valhalla.cc index 7455cc16c1..39d27d4c08 100644 --- a/src/tyr/route_serializer_valhalla.cc +++ b/src/tyr/route_serializer_valhalla.cc @@ -1,4 +1,3 @@ -#include #include #include "midgard/aabb2.h" diff --git a/src/tyr/serializers.cc b/src/tyr/serializers.cc index f9fa77931d..6cc8404eaa 100644 --- a/src/tyr/serializers.cc +++ b/src/tyr/serializers.cc @@ -1,11 +1,7 @@ #include -#include #include -#include -#include #include #include -#include #include #include "baldr/datetime.h" diff --git a/src/valhalla_export_edges.cc b/src/valhalla_export_edges.cc index 07fda5360a..65867776d5 100644 --- a/src/valhalla_export_edges.cc +++ b/src/valhalla_export_edges.cc @@ -12,7 +12,6 @@ #include #include #include -#include #include "argparse_utils.h" diff --git a/src/valhalla_run_matrix.cc b/src/valhalla_run_matrix.cc index f41f502763..3a1160d049 100644 --- a/src/valhalla_run_matrix.cc +++ b/src/valhalla_run_matrix.cc @@ -7,7 +7,6 @@ #include #include "baldr/rapidjson_utils.h" -#include #include #include "argparse_utils.h" diff --git a/src/valhalla_run_route.cc b/src/valhalla_run_route.cc index e3af0f90dc..64cc726f01 100644 --- a/src/valhalla_run_route.cc +++ b/src/valhalla_run_route.cc @@ -4,9 +4,7 @@ #include #include #include -#include #include -#include #include #include diff --git a/src/valhalla_service.cc b/src/valhalla_service.cc index 95c753259d..54d69913e4 100644 --- a/src/valhalla_service.cc +++ b/src/valhalla_service.cc @@ -1,15 +1,9 @@ #include -#include #include #include -#include -#include -#include -#include #include #include #include -#include #ifdef ENABLE_SERVICES #include diff --git a/src/worker.cc b/src/worker.cc index 04254e8a66..1154e542e1 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/test/access_restriction.cc b/test/access_restriction.cc index 5613753614..46ca8332e7 100644 --- a/test/access_restriction.cc +++ b/test/access_restriction.cc @@ -1,5 +1,3 @@ -#include - #include "baldr/accessrestriction.h" #include "test.h" diff --git a/test/actor.cc b/test/actor.cc index db86ad572f..3b181e3bdc 100644 --- a/test/actor.cc +++ b/test/actor.cc @@ -1,6 +1,4 @@ #include -#include -#include #include #include "tyr/actor.h" diff --git a/test/admin.cc b/test/admin.cc index a9984bf103..f256ccfdd3 100644 --- a/test/admin.cc +++ b/test/admin.cc @@ -1,5 +1,3 @@ -#include - #include "baldr/admin.h" #include "test.h" diff --git a/test/alternates.cc b/test/alternates.cc index 22a2309b25..0230676dd4 100644 --- a/test/alternates.cc +++ b/test/alternates.cc @@ -1,6 +1,5 @@ #include "test.h" -#include #include #include "baldr/rapidjson_utils.h" @@ -10,7 +9,6 @@ #include "odin/worker.h" #include "thor/worker.h" #include "tyr/serializers.h" -#include using namespace valhalla; using namespace valhalla::thor; diff --git a/test/astar.cc b/test/astar.cc index ba1f6a2097..efa1370a9d 100644 --- a/test/astar.cc +++ b/test/astar.cc @@ -1,7 +1,6 @@ #include "midgard/logging.h" #include "test.h" #include -#include #include "baldr/graphid.h" #include "baldr/graphreader.h" @@ -39,7 +38,6 @@ #include "proto/trip.pb.h" #include -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/double_bucket_queue.cc b/test/double_bucket_queue.cc index 5423a5fbb9..a20ed61fd4 100644 --- a/test/double_bucket_queue.cc +++ b/test/double_bucket_queue.cc @@ -5,7 +5,6 @@ #include #include #include -#include #include #include diff --git a/test/edgeinfobuilder.cc b/test/edgeinfobuilder.cc index cbdd7fa120..b2da922b7a 100644 --- a/test/edgeinfobuilder.cc +++ b/test/edgeinfobuilder.cc @@ -2,7 +2,6 @@ #include #include -#include #include #include "baldr/edgeinfo.h" diff --git a/test/graphbuilder.cc b/test/graphbuilder.cc index f7173d91d3..4c8419f097 100644 --- a/test/graphbuilder.cc +++ b/test/graphbuilder.cc @@ -7,7 +7,6 @@ #include "mjolnir/pbfgraphparser.h" #include "mjolnir/util.h" -#include #include #include diff --git a/test/grid_traversal.cc b/test/grid_traversal.cc index 3e32b3852a..f11996235c 100644 --- a/test/grid_traversal.cc +++ b/test/grid_traversal.cc @@ -1,5 +1,3 @@ -#include -#include #include #include "midgard/linesegment2.h" diff --git a/test/gurka/gurka.h b/test/gurka/gurka.h index 5609a33a61..fa163eeb0f 100644 --- a/test/gurka/gurka.h +++ b/test/gurka/gurka.h @@ -25,8 +25,6 @@ #include "tyr/actor.h" #include "tyr/serializers.h" -#include -#include #include #include @@ -36,7 +34,6 @@ #include #include -#include #include #include diff --git a/test/gurka/test_admin.cc b/test/gurka/test_admin.cc index 6300433b1e..0964dfe546 100644 --- a/test/gurka/test_admin.cc +++ b/test/gurka/test_admin.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_bidir_search.cc b/test/gurka/test_bidir_search.cc index 48016dc587..55ca43e2eb 100644 --- a/test/gurka/test_bidir_search.cc +++ b/test/gurka/test_bidir_search.cc @@ -1,6 +1,5 @@ #include "gurka.h" #include "test.h" -#include #include diff --git a/test/gurka/test_costing_with_traffic.cc b/test/gurka/test_costing_with_traffic.cc index 9e82242ae8..de9232e391 100644 --- a/test/gurka/test_costing_with_traffic.cc +++ b/test/gurka/test_costing_with_traffic.cc @@ -1,5 +1,6 @@ #include "gurka.h" #include "test.h" +#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_expansion.cc b/test/gurka/test_expansion.cc index bd285ea807..c504c3e68f 100644 --- a/test/gurka/test_expansion.cc +++ b/test/gurka/test_expansion.cc @@ -1,6 +1,5 @@ #include "gurka.h" #include -#include #include using namespace valhalla; diff --git a/test/gurka/test_fixed_speed.cc b/test/gurka/test_fixed_speed.cc index 0e22c84a18..84528f53df 100644 --- a/test/gurka/test_fixed_speed.cc +++ b/test/gurka/test_fixed_speed.cc @@ -1,7 +1,6 @@ #include "baldr/graphconstants.h" #include "gurka.h" #include "test.h" -#include #include using namespace valhalla; diff --git a/test/gurka/test_hov.cc b/test/gurka/test_hov.cc index b77d9ebdff..d8a313f83f 100644 --- a/test/gurka/test_hov.cc +++ b/test/gurka/test_hov.cc @@ -1,5 +1,6 @@ #include "gurka.h" #include "test.h" +#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_indoor.cc b/test/gurka/test_indoor.cc index 41e05a723d..eaa2a6e60e 100644 --- a/test/gurka/test_indoor.cc +++ b/test/gurka/test_indoor.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_instructions_long_street_names.cc b/test/gurka/test_instructions_long_street_names.cc index 0231315694..d04c0a4c65 100644 --- a/test/gurka/test_instructions_long_street_names.cc +++ b/test/gurka/test_instructions_long_street_names.cc @@ -1,7 +1,6 @@ #include "baldr/rapidjson_utils.h" #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_instructions_roundabout_multicue.cc b/test/gurka/test_instructions_roundabout_multicue.cc index 65db5bf747..564c79b360 100644 --- a/test/gurka/test_instructions_roundabout_multicue.cc +++ b/test/gurka/test_instructions_roundabout_multicue.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_multi_level_loki.cc b/test/gurka/test_multi_level_loki.cc index 14bfcaa888..53f505a984 100644 --- a/test/gurka/test_multi_level_loki.cc +++ b/test/gurka/test_multi_level_loki.cc @@ -1,7 +1,6 @@ #include #include -#include #include #include "baldr/rapidjson_utils.h" diff --git a/test/gurka/test_node_type.cc b/test/gurka/test_node_type.cc index dbc9431288..d016adabe5 100644 --- a/test/gurka/test_node_type.cc +++ b/test/gurka/test_node_type.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_osrm_serializer.cc b/test/gurka/test_osrm_serializer.cc index c1c6822eb4..8313fde980 100644 --- a/test/gurka/test_osrm_serializer.cc +++ b/test/gurka/test_osrm_serializer.cc @@ -1,5 +1,6 @@ #include "baldr/rapidjson_utils.h" #include "gurka.h" +#include #include using namespace valhalla; diff --git a/test/gurka/test_pathlocation_serialization.cc b/test/gurka/test_pathlocation_serialization.cc index 8ee74fc4a8..8c73e46baf 100644 --- a/test/gurka/test_pathlocation_serialization.cc +++ b/test/gurka/test_pathlocation_serialization.cc @@ -5,8 +5,6 @@ // with the same properties. // -#include - #include "baldr/pathlocation.h" #include "gurka.h" diff --git a/test/gurka/test_precision.cc b/test/gurka/test_precision.cc index 33c544ba6d..a7ed8750d7 100644 --- a/test/gurka/test_precision.cc +++ b/test/gurka/test_precision.cc @@ -1,6 +1,5 @@ #include "gurka.h" #include -#include using namespace valhalla; diff --git a/test/gurka/test_ramps_tc.cc b/test/gurka/test_ramps_tc.cc index dbb5e52043..fa0b95bde1 100644 --- a/test/gurka/test_ramps_tc.cc +++ b/test/gurka/test_ramps_tc.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_reach.cc b/test/gurka/test_reach.cc index 55ab4b6e10..76170d1180 100644 --- a/test/gurka/test_reach.cc +++ b/test/gurka/test_reach.cc @@ -4,7 +4,6 @@ #include "sif/dynamiccost.h" #include "test.h" -#include #include #include diff --git a/test/gurka/test_route.cc b/test/gurka/test_route.cc index 2d62d04aca..20b52b02ce 100644 --- a/test/gurka/test_route.cc +++ b/test/gurka/test_route.cc @@ -1,5 +1,6 @@ #include "gurka.h" #include "test.h" +#include using namespace valhalla; @@ -1161,7 +1162,7 @@ TEST(StandAlone, HGVNoAccessPenalty) { A-1--B----C----D----E--2-F----G----H--3-I | | J----K - | | + | | | | L----M )"; diff --git a/test/gurka/test_route_summary.cc b/test/gurka/test_route_summary.cc index 346f7e3a0e..bd292f97f2 100644 --- a/test/gurka/test_route_summary.cc +++ b/test/gurka/test_route_summary.cc @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -10,7 +9,6 @@ #include "mjolnir/adminbuilder.h" #include "test/test.h" -#include #include #include diff --git a/test/gurka/test_route_with_narrative_languages.cc b/test/gurka/test_route_with_narrative_languages.cc index eeef393f9b..956ac4f2ff 100644 --- a/test/gurka/test_route_with_narrative_languages.cc +++ b/test/gurka/test_route_with_narrative_languages.cc @@ -1,6 +1,5 @@ #include "gurka.h" #include -#include #include #include diff --git a/test/gurka/test_shortest.cc b/test/gurka/test_shortest.cc index 994163440f..3c6dfec049 100644 --- a/test/gurka/test_shortest.cc +++ b/test/gurka/test_shortest.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include using namespace valhalla; diff --git a/test/gurka/test_tagged_values.cc b/test/gurka/test_tagged_values.cc index 779683c83a..328aea5040 100644 --- a/test/gurka/test_tagged_values.cc +++ b/test/gurka/test_tagged_values.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_top_speed.cc b/test/gurka/test_top_speed.cc index f9f9945b45..7e4e13173f 100644 --- a/test/gurka/test_top_speed.cc +++ b/test/gurka/test_top_speed.cc @@ -1,7 +1,6 @@ #include "baldr/graphconstants.h" #include "gurka.h" #include "test.h" -#include #include using namespace valhalla; diff --git a/test/gurka/test_traffic.cc b/test/gurka/test_traffic.cc index 411cde3707..42a68235e0 100644 --- a/test/gurka/test_traffic.cc +++ b/test/gurka/test_traffic.cc @@ -4,10 +4,7 @@ #include "baldr/graphreader.h" #include "baldr/traffictile.h" -#include - #include -#include #include #include diff --git a/test/gurka/test_traffic_smoothing.cc b/test/gurka/test_traffic_smoothing.cc index 1f3d76fa40..a4fc2664ee 100644 --- a/test/gurka/test_traffic_smoothing.cc +++ b/test/gurka/test_traffic_smoothing.cc @@ -1,5 +1,6 @@ #include "gurka.h" #include "test.h" +#include using namespace valhalla; diff --git a/test/gurka/test_use.cc b/test/gurka/test_use.cc index 4370d067ac..48a92cc2bc 100644 --- a/test/gurka/test_use.cc +++ b/test/gurka/test_use.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_use_lit.cc b/test/gurka/test_use_lit.cc index c9a35ecd7c..064b310dd3 100644 --- a/test/gurka/test_use_lit.cc +++ b/test/gurka/test_use_lit.cc @@ -1,7 +1,6 @@ #include "baldr/graphconstants.h" #include "gurka.h" #include "test.h" -#include #include using namespace valhalla; diff --git a/test/gurka/test_via_waypoints.cc b/test/gurka/test_via_waypoints.cc index 78f591ae33..9f2f478270 100644 --- a/test/gurka/test_via_waypoints.cc +++ b/test/gurka/test_via_waypoints.cc @@ -1,5 +1,4 @@ #include "gurka.h" -#include #include #if !defined(VALHALLA_SOURCE_DIR) diff --git a/test/gurka/test_warnings.cc b/test/gurka/test_warnings.cc index 4abde53612..f2674b45ed 100644 --- a/test/gurka/test_warnings.cc +++ b/test/gurka/test_warnings.cc @@ -1,6 +1,5 @@ #include "gurka.h" #include -#include using namespace valhalla; diff --git a/test/http_tiles.cc b/test/http_tiles.cc index 3d3952356a..eb924226f1 100644 --- a/test/http_tiles.cc +++ b/test/http_tiles.cc @@ -8,7 +8,6 @@ #include #include -#include #include #include #include diff --git a/test/instructions.cc b/test/instructions.cc index b2a23f7e79..74d386c8b5 100644 --- a/test/instructions.cc +++ b/test/instructions.cc @@ -1,6 +1,4 @@ #include -#include -#include #include #include "baldr/rapidjson_utils.h" diff --git a/test/logging.cc b/test/logging.cc index 6a0abc4ad9..cdf32bd834 100644 --- a/test/logging.cc +++ b/test/logging.cc @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include diff --git a/test/matrix.cc b/test/matrix.cc index 109f9c392f..5428949151 100644 --- a/test/matrix.cc +++ b/test/matrix.cc @@ -1,6 +1,5 @@ #include "test.h" -#include #include #include diff --git a/test/minbb.cc b/test/minbb.cc index 10e9d2a41e..37721246a4 100644 --- a/test/minbb.cc +++ b/test/minbb.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/test/multipoint_routes.cc b/test/multipoint_routes.cc index 3fc92dc24e..26771c8e22 100644 --- a/test/multipoint_routes.cc +++ b/test/multipoint_routes.cc @@ -1,6 +1,5 @@ #include "test.h" -#include #include #include @@ -10,7 +9,6 @@ #include "odin/worker.h" #include "sif/autocost.h" #include "thor/worker.h" -#include using namespace valhalla; using namespace valhalla::thor; diff --git a/test/names.cc b/test/names.cc index 616a153441..078512297c 100644 --- a/test/names.cc +++ b/test/names.cc @@ -2,7 +2,6 @@ #include "midgard/util.h" #include "mjolnir/osmway.h" #include "mjolnir/uniquenames.h" -#include #include "test.h" diff --git a/test/narrativebuilder.cc b/test/narrativebuilder.cc index d398b51a3e..66b2d51fe6 100644 --- a/test/narrativebuilder.cc +++ b/test/narrativebuilder.cc @@ -1,5 +1,4 @@ #include -#include #include "baldr/verbal_text_formatter_factory.h" diff --git a/test/node_search.cc b/test/node_search.cc index f2ba87d603..f10d217ff7 100644 --- a/test/node_search.cc +++ b/test/node_search.cc @@ -5,7 +5,6 @@ #include "baldr/rapidjson_utils.h" #include -#include #include "baldr/graphid.h" #include "baldr/graphreader.h" diff --git a/test/predictive_traffic.cc b/test/predictive_traffic.cc index d3f0fd10fc..e1ff13bae7 100644 --- a/test/predictive_traffic.cc +++ b/test/predictive_traffic.cc @@ -1,6 +1,5 @@ #include "test.h" -#include #include #include diff --git a/test/reach.cc b/test/reach.cc index abaf66b4db..c8b0fded5b 100644 --- a/test/reach.cc +++ b/test/reach.cc @@ -10,7 +10,6 @@ #include "sif/dynamiccost.h" #include -#include using namespace valhalla; using namespace valhalla::midgard; diff --git a/test/recover_shortcut.cc b/test/recover_shortcut.cc index ed40e1f065..726376851a 100644 --- a/test/recover_shortcut.cc +++ b/test/recover_shortcut.cc @@ -1,6 +1,5 @@ #include "test.h" -#include #include #include @@ -10,7 +9,6 @@ #include "midgard/encoded.h" #include "midgard/util.h" #include "src/baldr/shortcut_recovery.h" -#include using namespace valhalla; using namespace valhalla::baldr; diff --git a/test/search.cc b/test/search.cc index 4fdd76c2b5..0053d1e760 100644 --- a/test/search.cc +++ b/test/search.cc @@ -4,7 +4,6 @@ #include #include -#include #include "baldr/graphid.h" #include "baldr/graphreader.h" diff --git a/test/shape_attributes.cc b/test/shape_attributes.cc index dd8f15e6a4..21d47b86da 100644 --- a/test/shape_attributes.cc +++ b/test/shape_attributes.cc @@ -1,8 +1,5 @@ #include "test.h" -#include -#include - #include "midgard/distanceapproximator.h" #include "midgard/encoded.h" #include "midgard/logging.h" diff --git a/test/skadi_service.cc b/test/skadi_service.cc index 94d6c90bde..b6287a023e 100644 --- a/test/skadi_service.cc +++ b/test/skadi_service.cc @@ -2,7 +2,6 @@ #include "pixels.h" #include "test.h" -#include #include #include diff --git a/test/streetname.cc b/test/streetname.cc index 4c215845e5..c302a5d121 100644 --- a/test/streetname.cc +++ b/test/streetname.cc @@ -1,7 +1,5 @@ #include "baldr/streetname.h" -#include - #include "test.h" using namespace std; diff --git a/test/streetname_us.cc b/test/streetname_us.cc index 9171ad30be..2ed76e263c 100644 --- a/test/streetname_us.cc +++ b/test/streetname_us.cc @@ -1,7 +1,5 @@ #include "baldr/streetname_us.h" -#include - #include "test.h" using namespace std; diff --git a/test/streetnames_us.cc b/test/streetnames_us.cc index 255ff84345..893d7d1e20 100644 --- a/test/streetnames_us.cc +++ b/test/streetnames_us.cc @@ -1,7 +1,6 @@ #include "baldr/streetnames_us.h" #include "baldr/streetname_us.h" -#include #include #include "test.h" diff --git a/test/summary.cc b/test/summary.cc index 3954ca1622..e0ea71686b 100644 --- a/test/summary.cc +++ b/test/summary.cc @@ -1,8 +1,6 @@ #include "test.h" -#include #include -#include #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" @@ -10,7 +8,6 @@ #include "odin/worker.h" #include "thor/worker.h" #include "tyr/serializers.h" -#include using namespace valhalla; using namespace valhalla::baldr; diff --git a/test/tar_index.cc b/test/tar_index.cc index 982e8274c1..e44d14c333 100644 --- a/test/tar_index.cc +++ b/test/tar_index.cc @@ -1,7 +1,6 @@ #include "test.h" #include "baldr/graphreader.h" -#include namespace vb = valhalla::baldr; diff --git a/test/test.cc b/test/test.cc index ac7aa7807d..f501d08a8d 100644 --- a/test/test.cc +++ b/test/test.cc @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/test/test.h b/test/test.h index a7f9678e4e..68ec4f1bcc 100644 --- a/test/test.h +++ b/test/test.h @@ -9,10 +9,7 @@ #include "mjolnir/graphtilebuilder.h" #include -#include #include -#include -#include #include #ifndef _MSC_VER #include @@ -23,7 +20,6 @@ #include #include -#include #include namespace test { diff --git a/test/thor_worker.cc b/test/thor_worker.cc index 2287cb9d0a..3c2d7b58ff 100644 --- a/test/thor_worker.cc +++ b/test/thor_worker.cc @@ -6,7 +6,6 @@ #include "thor/worker.h" #include "tyr/actor.h" #include -#include #include using namespace valhalla; diff --git a/test/tilehierarchy.cc b/test/tilehierarchy.cc index e9c93bbfa2..3a52bf2050 100644 --- a/test/tilehierarchy.cc +++ b/test/tilehierarchy.cc @@ -4,8 +4,6 @@ #include "test.h" -#include - using namespace std; using namespace valhalla::baldr; using namespace valhalla::midgard; diff --git a/test/timedep_paths.cc b/test/timedep_paths.cc index e561801c11..4322762508 100644 --- a/test/timedep_paths.cc +++ b/test/timedep_paths.cc @@ -1,8 +1,6 @@ #include "test.h" -#include #include -#include #include "baldr/rapidjson_utils.h" #include "loki/worker.h" diff --git a/test/timeparsing.cc b/test/timeparsing.cc index 04ef77961a..aca50d95f9 100644 --- a/test/timeparsing.cc +++ b/test/timeparsing.cc @@ -1,5 +1,4 @@ #include -#include #include #include diff --git a/test/trivial_paths.cc b/test/trivial_paths.cc index 94b1882b29..b09aa0561f 100644 --- a/test/trivial_paths.cc +++ b/test/trivial_paths.cc @@ -1,8 +1,6 @@ #include "test.h" -#include #include -#include #include "loki/worker.h" #include "midgard/logging.h" diff --git a/test/turn.cc b/test/turn.cc index d962c370f9..49a0cddf8a 100644 --- a/test/turn.cc +++ b/test/turn.cc @@ -1,9 +1,5 @@ -#include - #include "baldr/turn.h" -#include - #include "test.h" using namespace std; diff --git a/test/turnlanes.cc b/test/turnlanes.cc index 46620a23ea..c9ab3594f4 100644 --- a/test/turnlanes.cc +++ b/test/turnlanes.cc @@ -1,8 +1,6 @@ #include #include #include -#include -#include #include #include diff --git a/test/urban.cc b/test/urban.cc index 8049aacacd..f6f6720a3a 100644 --- a/test/urban.cc +++ b/test/urban.cc @@ -1,7 +1,6 @@ #include "test.h" #include -#include #include "baldr/graphreader.h" #include "baldr/rapidjson_utils.h" diff --git a/test/util_mjolnir.cc b/test/util_mjolnir.cc index b4a3e7b04d..7d7581c0b2 100644 --- a/test/util_mjolnir.cc +++ b/test/util_mjolnir.cc @@ -1,7 +1,5 @@ #include -#include #include -#include #include diff --git a/test/utrecht.cc b/test/utrecht.cc index e3b6aea833..6c3ff96ab0 100644 --- a/test/utrecht.cc +++ b/test/utrecht.cc @@ -6,7 +6,6 @@ #include #include -#include #include "baldr/directededge.h" #include "baldr/graphconstants.h" diff --git a/valhalla/baldr/admininfo.h b/valhalla/baldr/admininfo.h index 75ef99b064..0933da7d18 100644 --- a/valhalla/baldr/admininfo.h +++ b/valhalla/baldr/admininfo.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_BALDR_ADMININFO_H_ #define VALHALLA_BALDR_ADMININFO_H_ -#include #include namespace valhalla { diff --git a/valhalla/baldr/complexrestriction.h b/valhalla/baldr/complexrestriction.h index 4c6723473a..399a3280e8 100644 --- a/valhalla/baldr/complexrestriction.h +++ b/valhalla/baldr/complexrestriction.h @@ -2,10 +2,6 @@ #define VALHALLA_BALDR_COMPLEXRESTRICTION_H_ #include -#include -#include -#include -#include #include #include diff --git a/valhalla/baldr/curler.h b/valhalla/baldr/curler.h index 70f2da1cb5..06d5d03fa1 100644 --- a/valhalla/baldr/curler.h +++ b/valhalla/baldr/curler.h @@ -2,7 +2,6 @@ #include -#include #include #include #include diff --git a/valhalla/baldr/datetime.h b/valhalla/baldr/datetime.h index 2ce2ce712f..8080e5a434 100644 --- a/valhalla/baldr/datetime.h +++ b/valhalla/baldr/datetime.h @@ -4,9 +4,7 @@ #include #include #include -#include #include -#include #include #include #include diff --git a/valhalla/baldr/graphid.h b/valhalla/baldr/graphid.h index 7d0ff421e8..d4ba7c9cf8 100644 --- a/valhalla/baldr/graphid.h +++ b/valhalla/baldr/graphid.h @@ -2,8 +2,6 @@ #define VALHALLA_BALDR_GRAPHID_H_ #include -#include -#include #include #include diff --git a/valhalla/baldr/graphreader.h b/valhalla/baldr/graphreader.h index d67e9d7578..47e0cf6ee7 100644 --- a/valhalla/baldr/graphreader.h +++ b/valhalla/baldr/graphreader.h @@ -2,7 +2,6 @@ #include #include -#include #include #include #include diff --git a/valhalla/baldr/json.h b/valhalla/baldr/json.h index 08d241c311..663cbfdda4 100644 --- a/valhalla/baldr/json.h +++ b/valhalla/baldr/json.h @@ -7,10 +7,8 @@ #include #include #include -#include #include #include -#include #include #include #include diff --git a/valhalla/baldr/landmark.h b/valhalla/baldr/landmark.h index 288c9dc8ce..23becf3700 100644 --- a/valhalla/baldr/landmark.h +++ b/valhalla/baldr/landmark.h @@ -3,10 +3,8 @@ #include #include #include -#include #include #include -#include #include #include diff --git a/valhalla/baldr/pathlocation.h b/valhalla/baldr/pathlocation.h index d98afad3b5..909c66c520 100644 --- a/valhalla/baldr/pathlocation.h +++ b/valhalla/baldr/pathlocation.h @@ -1,8 +1,6 @@ #ifndef VALHALLA_BALDR_PATHLOCATION_H_ #define VALHALLA_BALDR_PATHLOCATION_H_ -#include -#include #include #include diff --git a/valhalla/baldr/streetname.h b/valhalla/baldr/streetname.h index 5266117115..9c389b3005 100644 --- a/valhalla/baldr/streetname.h +++ b/valhalla/baldr/streetname.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_BALDR_STREETNAME_H_ #define VALHALLA_BALDR_STREETNAME_H_ -#include #include #include diff --git a/valhalla/baldr/streetname_us.h b/valhalla/baldr/streetname_us.h index 52729402de..a19d513f0d 100644 --- a/valhalla/baldr/streetname_us.h +++ b/valhalla/baldr/streetname_us.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_BALDR_STREETNAME_US_H_ #define VALHALLA_BALDR_STREETNAME_US_H_ -#include #include #include #include diff --git a/valhalla/baldr/streetnames_us.h b/valhalla/baldr/streetnames_us.h index 4b01863f7e..8e73aeb346 100644 --- a/valhalla/baldr/streetnames_us.h +++ b/valhalla/baldr/streetnames_us.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_BALDR_STREETNAMES_US_H_ #define VALHALLA_BALDR_STREETNAMES_US_H_ -#include #include #include diff --git a/valhalla/baldr/tilehierarchy.h b/valhalla/baldr/tilehierarchy.h index c18fcfb445..01a0b0d89a 100644 --- a/valhalla/baldr/tilehierarchy.h +++ b/valhalla/baldr/tilehierarchy.h @@ -2,7 +2,6 @@ #define VALHALLA_MIDGARD_TILEHIERARCHY_H #include -#include #include #include diff --git a/valhalla/baldr/timedomain.h b/valhalla/baldr/timedomain.h index 01f39a8f84..57bf50caec 100644 --- a/valhalla/baldr/timedomain.h +++ b/valhalla/baldr/timedomain.h @@ -2,7 +2,6 @@ #define VALHALLA_BALDR_TIMEDOMAIN_H_ #include -#include namespace valhalla { namespace baldr { diff --git a/valhalla/baldr/traffictile.h b/valhalla/baldr/traffictile.h index c898a7fb7c..cf4678b27d 100644 --- a/valhalla/baldr/traffictile.h +++ b/valhalla/baldr/traffictile.h @@ -16,7 +16,6 @@ #include #include #include -#include #else #include #endif diff --git a/valhalla/baldr/verbal_text_formatter_us_co.h b/valhalla/baldr/verbal_text_formatter_us_co.h index a04f50bb1e..8e96c2954f 100644 --- a/valhalla/baldr/verbal_text_formatter_us_co.h +++ b/valhalla/baldr/verbal_text_formatter_us_co.h @@ -3,9 +3,7 @@ #include -#include #include -#include namespace valhalla { namespace baldr { diff --git a/valhalla/baldr/verbal_text_formatter_us_tx.h b/valhalla/baldr/verbal_text_formatter_us_tx.h index b3f3781d25..c72349b0ef 100644 --- a/valhalla/baldr/verbal_text_formatter_us_tx.h +++ b/valhalla/baldr/verbal_text_formatter_us_tx.h @@ -1,9 +1,7 @@ #ifndef VALHALLA_BALDR_VERBAL_TEXT_FORMATTER_US_TX_H_ #define VALHALLA_BALDR_VERBAL_TEXT_FORMATTER_US_TX_H_ -#include #include -#include #include diff --git a/valhalla/config.h b/valhalla/config.h index 6536a203e5..1d05138239 100644 --- a/valhalla/config.h +++ b/valhalla/config.h @@ -1,6 +1,5 @@ #pragma once #include -#include #include #include diff --git a/valhalla/loki/node_search.h b/valhalla/loki/node_search.h index f3bf5eb53c..316150c31f 100644 --- a/valhalla/loki/node_search.h +++ b/valhalla/loki/node_search.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_LOKI_NODE_SEARCH_H_ #define VALHALLA_LOKI_NODE_SEARCH_H_ -#include #include namespace valhalla { diff --git a/valhalla/loki/search.h b/valhalla/loki/search.h index 9377a4ead0..cb52ceedeb 100644 --- a/valhalla/loki/search.h +++ b/valhalla/loki/search.h @@ -1,15 +1,12 @@ #ifndef VALHALLA_LOKI_SEARCH_H_ #define VALHALLA_LOKI_SEARCH_H_ -#include #include #include #include #include #include -#include - namespace valhalla { namespace loki { diff --git a/valhalla/loki/worker.h b/valhalla/loki/worker.h index 78927cfcf5..aad9d57f6f 100644 --- a/valhalla/loki/worker.h +++ b/valhalla/loki/worker.h @@ -1,7 +1,6 @@ #ifndef __VALHALLA_LOKI_SERVICE_H__ #define __VALHALLA_LOKI_SERVICE_H__ -#include #include #include diff --git a/valhalla/meili/candidate_search.h b/valhalla/meili/candidate_search.h index ea190a9e9b..d3837120a0 100644 --- a/valhalla/meili/candidate_search.h +++ b/valhalla/meili/candidate_search.h @@ -4,9 +4,6 @@ #include #include -#include - -#include #include #include diff --git a/valhalla/meili/map_matcher.h b/valhalla/meili/map_matcher.h index 98793811dd..32159af229 100644 --- a/valhalla/meili/map_matcher.h +++ b/valhalla/meili/map_matcher.h @@ -2,7 +2,6 @@ #ifndef MMP_MAP_MATCHER_H_ #define MMP_MAP_MATCHER_H_ -#include #include #include diff --git a/valhalla/meili/map_matcher_factory.h b/valhalla/meili/map_matcher_factory.h index c5025f638a..be206e3a7a 100644 --- a/valhalla/meili/map_matcher_factory.h +++ b/valhalla/meili/map_matcher_factory.h @@ -1,9 +1,6 @@ // -*- mode: c++ -*- #ifndef MMP_MAP_MATCHER_FACTORY_H_ #define MMP_MAP_MATCHER_FACTORY_H_ -#include - -#include #include diff --git a/valhalla/meili/stateid.h b/valhalla/meili/stateid.h index 4dba146c3b..6938344b33 100644 --- a/valhalla/meili/stateid.h +++ b/valhalla/meili/stateid.h @@ -2,7 +2,6 @@ #define VALHALLA_MEILI_STATE_H_ #include -#include #include namespace valhalla { diff --git a/valhalla/meili/traffic_segment_matcher.h b/valhalla/meili/traffic_segment_matcher.h index 43c88a0a9f..0c4aef2d78 100644 --- a/valhalla/meili/traffic_segment_matcher.h +++ b/valhalla/meili/traffic_segment_matcher.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/valhalla/meili/transition_cost_model.h b/valhalla/meili/transition_cost_model.h index 4d5a6bac73..b7961cf319 100644 --- a/valhalla/meili/transition_cost_model.h +++ b/valhalla/meili/transition_cost_model.h @@ -1,8 +1,6 @@ #ifndef MMP_TRANSITION_COST_MODEL_H_ #define MMP_TRANSITION_COST_MODEL_H_ -#include - #include #include #include diff --git a/valhalla/meili/viterbi_search.h b/valhalla/meili/viterbi_search.h index 240994e9fc..bf81f04b2e 100644 --- a/valhalla/meili/viterbi_search.h +++ b/valhalla/meili/viterbi_search.h @@ -2,7 +2,6 @@ #ifndef MMP_VITERBI_SEARCH_H_ #define MMP_VITERBI_SEARCH_H_ -#include #include #include #include diff --git a/valhalla/midgard/elevation_encoding.h b/valhalla/midgard/elevation_encoding.h index 12eb0a369b..d50e4d952d 100644 --- a/valhalla/midgard/elevation_encoding.h +++ b/valhalla/midgard/elevation_encoding.h @@ -2,7 +2,6 @@ #define VALHALLA_MIDGARD_ELEVATION_ENCODING_H_ #include -#include #include #include diff --git a/valhalla/midgard/point2.h b/valhalla/midgard/point2.h index af4827d1e8..d54094faae 100644 --- a/valhalla/midgard/point2.h +++ b/valhalla/midgard/point2.h @@ -1,10 +1,7 @@ #pragma once #include -#include #include -#include -#include #include #include #include diff --git a/valhalla/midgard/polyline2.h b/valhalla/midgard/polyline2.h index fd126323a9..814c0f2bea 100644 --- a/valhalla/midgard/polyline2.h +++ b/valhalla/midgard/polyline2.h @@ -6,7 +6,6 @@ #include #include -#include #include #include #include diff --git a/valhalla/midgard/sequence.h b/valhalla/midgard/sequence.h index cd619af92a..e5b169db3c 100644 --- a/valhalla/midgard/sequence.h +++ b/valhalla/midgard/sequence.h @@ -8,10 +8,7 @@ #include #include #include -#include #include -#include -#include #include #include #include diff --git a/valhalla/midgard/tiles.h b/valhalla/midgard/tiles.h index afede60786..6e339c8826 100644 --- a/valhalla/midgard/tiles.h +++ b/valhalla/midgard/tiles.h @@ -1,10 +1,8 @@ - #ifndef VALHALLA_MIDGARD_TILES_H_ #define VALHALLA_MIDGARD_TILES_H_ #include #include -#include #include #include diff --git a/valhalla/midgard/util.h b/valhalla/midgard/util.h index 052162be37..6fba10bd87 100644 --- a/valhalla/midgard/util.h +++ b/valhalla/midgard/util.h @@ -2,11 +2,9 @@ #include #include -#include #include -#include +#include #include -#include #include #include #include diff --git a/valhalla/mjolnir/adminbuilder.h b/valhalla/mjolnir/adminbuilder.h index c63c179e5f..553107652d 100644 --- a/valhalla/mjolnir/adminbuilder.h +++ b/valhalla/mjolnir/adminbuilder.h @@ -2,9 +2,6 @@ #include -#include -#include -#include #include #include diff --git a/valhalla/mjolnir/bssbuilder.h b/valhalla/mjolnir/bssbuilder.h index b26eaf6fc0..f34f37ca7b 100644 --- a/valhalla/mjolnir/bssbuilder.h +++ b/valhalla/mjolnir/bssbuilder.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_BSSBUILDER_H #include -#include #include namespace valhalla { diff --git a/valhalla/mjolnir/complexrestrictionbuilder.h b/valhalla/mjolnir/complexrestrictionbuilder.h index af46fca344..04bd8594ea 100644 --- a/valhalla/mjolnir/complexrestrictionbuilder.h +++ b/valhalla/mjolnir/complexrestrictionbuilder.h @@ -2,9 +2,6 @@ #define VALHALLA_MJOLNIR_COMPLEXRESTRICTIONBUILDER_H_ #include -#include -#include -#include #include #include diff --git a/valhalla/mjolnir/edgeinfobuilder.h b/valhalla/mjolnir/edgeinfobuilder.h index d37cfafbab..47d1d76b3c 100644 --- a/valhalla/mjolnir/edgeinfobuilder.h +++ b/valhalla/mjolnir/edgeinfobuilder.h @@ -2,8 +2,6 @@ #define VALHALLA_MJOLNIR_EDGEINFOBUILDER_H_ #include -#include -#include #include #include diff --git a/valhalla/mjolnir/ferry_connections.h b/valhalla/mjolnir/ferry_connections.h index e662826d54..ccaa2515df 100644 --- a/valhalla/mjolnir/ferry_connections.h +++ b/valhalla/mjolnir/ferry_connections.h @@ -2,9 +2,7 @@ #define VALHALLA_MJOLNIR_FERRY_CONNECTIONS_H_ #include -#include #include -#include #include #include diff --git a/valhalla/mjolnir/graphenhancer.h b/valhalla/mjolnir/graphenhancer.h index 327d91d7b1..5ea50457aa 100644 --- a/valhalla/mjolnir/graphenhancer.h +++ b/valhalla/mjolnir/graphenhancer.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_GRAPHENHANCER_H #include -#include #include namespace valhalla { diff --git a/valhalla/mjolnir/graphfilter.h b/valhalla/mjolnir/graphfilter.h index 9c36e06d60..c992a2439b 100644 --- a/valhalla/mjolnir/graphfilter.h +++ b/valhalla/mjolnir/graphfilter.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_GRAPHFILTER_H #include -#include #include diff --git a/valhalla/mjolnir/graphtilebuilder.h b/valhalla/mjolnir/graphtilebuilder.h index 9ae5dedae7..20a330fdea 100644 --- a/valhalla/mjolnir/graphtilebuilder.h +++ b/valhalla/mjolnir/graphtilebuilder.h @@ -4,13 +4,10 @@ #include #include #include -#include -#include #include #include #include #include -#include #include #include diff --git a/valhalla/mjolnir/graphvalidator.h b/valhalla/mjolnir/graphvalidator.h index d2646ad0c8..70d75d6f8d 100644 --- a/valhalla/mjolnir/graphvalidator.h +++ b/valhalla/mjolnir/graphvalidator.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_GRAPHOPTIMIZER_H #include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/hierarchybuilder.h b/valhalla/mjolnir/hierarchybuilder.h index 424fe4d712..25425c8e4e 100644 --- a/valhalla/mjolnir/hierarchybuilder.h +++ b/valhalla/mjolnir/hierarchybuilder.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_HIERARCHYBUILDER_H #include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/ingest_transit.h b/valhalla/mjolnir/ingest_transit.h index 6fa3f984dc..dbb597a3b7 100644 --- a/valhalla/mjolnir/ingest_transit.h +++ b/valhalla/mjolnir/ingest_transit.h @@ -2,11 +2,7 @@ #include #include -#include #include -#include -#include -#include #include #include diff --git a/valhalla/mjolnir/landmarks.h b/valhalla/mjolnir/landmarks.h index 04b24c7aac..02a993dd30 100644 --- a/valhalla/mjolnir/landmarks.h +++ b/valhalla/mjolnir/landmarks.h @@ -5,7 +5,6 @@ #include #include -#include #include diff --git a/valhalla/mjolnir/linkclassification.h b/valhalla/mjolnir/linkclassification.h index e000d54cfe..8e03a153d5 100644 --- a/valhalla/mjolnir/linkclassification.h +++ b/valhalla/mjolnir/linkclassification.h @@ -1,9 +1,7 @@ #ifndef VALHALLA_MJOLNIR_LINK_CLASSIFICATION_H_ #define VALHALLA_MJOLNIR_LINK_CLASSIFICATION_H_ -#include #include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/node_expander.h b/valhalla/mjolnir/node_expander.h index f0d5e1f027..29d08bceef 100644 --- a/valhalla/mjolnir/node_expander.h +++ b/valhalla/mjolnir/node_expander.h @@ -2,8 +2,6 @@ #define VALHALLA_MJOLNIR_NODE_EXPANDER_H_ #include -#include -#include #include #include diff --git a/valhalla/mjolnir/osmaccess.h b/valhalla/mjolnir/osmaccess.h index 2aac686229..1ab85c0622 100644 --- a/valhalla/mjolnir/osmaccess.h +++ b/valhalla/mjolnir/osmaccess.h @@ -2,8 +2,6 @@ #define VALHALLA_MJOLNIR_PBFGRAPHBUILDER_OSMACCESS_H #include -#include -#include #include diff --git a/valhalla/mjolnir/osmadmindata.h b/valhalla/mjolnir/osmadmindata.h index ce687dbc2e..ab0d9e95d2 100644 --- a/valhalla/mjolnir/osmadmindata.h +++ b/valhalla/mjolnir/osmadmindata.h @@ -1,8 +1,6 @@ #pragma once #include -#include -#include #include #include diff --git a/valhalla/mjolnir/osmdata.h b/valhalla/mjolnir/osmdata.h index 1dced82929..a99b47f9f9 100644 --- a/valhalla/mjolnir/osmdata.h +++ b/valhalla/mjolnir/osmdata.h @@ -2,11 +2,8 @@ #define VALHALLA_MJOLNIR_OSMDATA_H #include -#include -#include #include #include -#include #include #include diff --git a/valhalla/mjolnir/osmlinguistic.h b/valhalla/mjolnir/osmlinguistic.h index b49ad30312..c3edd00fdd 100644 --- a/valhalla/mjolnir/osmlinguistic.h +++ b/valhalla/mjolnir/osmlinguistic.h @@ -2,10 +2,7 @@ #define VALHALLA_MJOLNIR_OSMLINGUISTIC_H #include -#include -#include -#include #include namespace valhalla { diff --git a/valhalla/mjolnir/osmnode.h b/valhalla/mjolnir/osmnode.h index 66fa073917..a2e00912f4 100644 --- a/valhalla/mjolnir/osmnode.h +++ b/valhalla/mjolnir/osmnode.h @@ -3,8 +3,6 @@ #include #include -#include -#include #include #include diff --git a/valhalla/mjolnir/pbfadminparser.h b/valhalla/mjolnir/pbfadminparser.h index 292c976433..100a51e7ac 100644 --- a/valhalla/mjolnir/pbfadminparser.h +++ b/valhalla/mjolnir/pbfadminparser.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_PBFADMINPARSER_H #include -#include #include #include diff --git a/valhalla/mjolnir/pbfgraphparser.h b/valhalla/mjolnir/pbfgraphparser.h index a3530b5d11..daa1f9fdeb 100644 --- a/valhalla/mjolnir/pbfgraphparser.h +++ b/valhalla/mjolnir/pbfgraphparser.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_PBFGRAPHPARSER_H #include -#include #include #include diff --git a/valhalla/mjolnir/restrictionbuilder.h b/valhalla/mjolnir/restrictionbuilder.h index eada9ba9df..da6a758a2e 100644 --- a/valhalla/mjolnir/restrictionbuilder.h +++ b/valhalla/mjolnir/restrictionbuilder.h @@ -2,9 +2,6 @@ #define VALHALLA_MJOLNIR_RESTRICTIONBUILDER_H #include -#include -#include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/servicedays.h b/valhalla/mjolnir/servicedays.h index de09ca7ab1..d6f9835ff3 100644 --- a/valhalla/mjolnir/servicedays.h +++ b/valhalla/mjolnir/servicedays.h @@ -4,10 +4,6 @@ #include #include #include -#include -#include -#include -#include #include #include diff --git a/valhalla/mjolnir/shortcutbuilder.h b/valhalla/mjolnir/shortcutbuilder.h index 3fbebe1191..0e8bcdd8d6 100644 --- a/valhalla/mjolnir/shortcutbuilder.h +++ b/valhalla/mjolnir/shortcutbuilder.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_SHORTCUTBUILDER_H #include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/transitbuilder.h b/valhalla/mjolnir/transitbuilder.h index ec15958545..aff172e7a7 100644 --- a/valhalla/mjolnir/transitbuilder.h +++ b/valhalla/mjolnir/transitbuilder.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_TRANSITBUILDER_H #include -#include namespace valhalla { namespace mjolnir { diff --git a/valhalla/mjolnir/validatetransit.h b/valhalla/mjolnir/validatetransit.h index 453df5f3ef..099ba8b2ea 100644 --- a/valhalla/mjolnir/validatetransit.h +++ b/valhalla/mjolnir/validatetransit.h @@ -2,7 +2,6 @@ #define VALHALLA_MJOLNIR_VALIDATETRANSIT_H #include -#include #include #include diff --git a/valhalla/odin/maneuver.h b/valhalla/odin/maneuver.h index 095126a695..22bfd08c69 100644 --- a/valhalla/odin/maneuver.h +++ b/valhalla/odin/maneuver.h @@ -5,7 +5,6 @@ #include #include #include -#include #include #include diff --git a/valhalla/odin/narrative_builder_factory.h b/valhalla/odin/narrative_builder_factory.h index c628bd730a..48433d0eae 100644 --- a/valhalla/odin/narrative_builder_factory.h +++ b/valhalla/odin/narrative_builder_factory.h @@ -2,7 +2,6 @@ #define VALHALLA_ODIN_NARRATIVE_BUILDER_FACTORY_H_ #include -#include #include #include diff --git a/valhalla/odin/util.h b/valhalla/odin/util.h index 0cbe509ce8..d1b848b8c7 100644 --- a/valhalla/odin/util.h +++ b/valhalla/odin/util.h @@ -5,9 +5,6 @@ #include #include #include -#include - -#include #include #include diff --git a/valhalla/sif/autocost.h b/valhalla/sif/autocost.h index 7654520d13..be0095284a 100644 --- a/valhalla/sif/autocost.h +++ b/valhalla/sif/autocost.h @@ -1,9 +1,6 @@ #ifndef VALHALLA_SIF_AUTOCOST_H_ #define VALHALLA_SIF_AUTOCOST_H_ -#include - -#include #include #include #include diff --git a/valhalla/sif/bicyclecost.h b/valhalla/sif/bicyclecost.h index 1448a49c98..f4c4496aa9 100644 --- a/valhalla/sif/bicyclecost.h +++ b/valhalla/sif/bicyclecost.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_SIF_BICYCLECOST_H_ #define VALHALLA_SIF_BICYCLECOST_H_ -#include #include #include #include diff --git a/valhalla/sif/costfactory.h b/valhalla/sif/costfactory.h index a88eaed361..fca04df4a2 100644 --- a/valhalla/sif/costfactory.h +++ b/valhalla/sif/costfactory.h @@ -3,7 +3,6 @@ #include #include -#include #include #include diff --git a/valhalla/sif/edgelabel.h b/valhalla/sif/edgelabel.h index d5a9dc1b23..cf37903dad 100644 --- a/valhalla/sif/edgelabel.h +++ b/valhalla/sif/edgelabel.h @@ -2,7 +2,6 @@ #define VALHALLA_SIF_EDGELABEL_H_ #include -#include #include #include #include diff --git a/valhalla/sif/hierarchylimits.h b/valhalla/sif/hierarchylimits.h index 993096af58..dadbdce640 100644 --- a/valhalla/sif/hierarchylimits.h +++ b/valhalla/sif/hierarchylimits.h @@ -1,7 +1,7 @@ #ifndef VALHALLA_SIF_HIERARCHYLIMITS_H_ #define VALHALLA_SIF_HIERARCHYLIMITS_H_ -#include +#include #include // Default hierarchy transitions. Note that this corresponds to a 3 level diff --git a/valhalla/sif/motorcyclecost.h b/valhalla/sif/motorcyclecost.h index c5024e81ce..b555eec1a9 100644 --- a/valhalla/sif/motorcyclecost.h +++ b/valhalla/sif/motorcyclecost.h @@ -1,9 +1,6 @@ #ifndef VALHALLA_SIF_MOTORCYCLECOST_H_ #define VALHALLA_SIF_MOTORCYCLECOST_H_ -#include - -#include #include #include #include diff --git a/valhalla/sif/motorscootercost.h b/valhalla/sif/motorscootercost.h index 5146690e2f..cb68bb7280 100644 --- a/valhalla/sif/motorscootercost.h +++ b/valhalla/sif/motorscootercost.h @@ -1,9 +1,6 @@ #ifndef VALHALLA_SIF_MOTORSCOOTERCOST_H_ #define VALHALLA_SIF_MOTORSCOOTERCOST_H_ -#include - -#include #include #include #include diff --git a/valhalla/sif/nocost.h b/valhalla/sif/nocost.h index 97b033078b..3bb42909a0 100644 --- a/valhalla/sif/nocost.h +++ b/valhalla/sif/nocost.h @@ -1,9 +1,6 @@ #ifndef VALHALLA_SIF_NOCOST_H_ #define VALHALLA_SIF_NOCOST_H_ -#include - -#include #include #include #include diff --git a/valhalla/sif/pedestriancost.h b/valhalla/sif/pedestriancost.h index d036d6a6fb..1563d260ed 100644 --- a/valhalla/sif/pedestriancost.h +++ b/valhalla/sif/pedestriancost.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_SIF_PEDESTRIANCOST_H_ #define VALHALLA_SIF_PEDESTRIANCOST_H_ -#include #include #include #include diff --git a/valhalla/sif/transitcost.h b/valhalla/sif/transitcost.h index f47f23a2fd..86bb2df6b2 100644 --- a/valhalla/sif/transitcost.h +++ b/valhalla/sif/transitcost.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_SIF_TRANSITCOST_H_ #define VALHALLA_SIF_TRANSITCOST_H_ -#include #include #include #include diff --git a/valhalla/sif/truckcost.h b/valhalla/sif/truckcost.h index e83a0e5984..88be47b2bf 100644 --- a/valhalla/sif/truckcost.h +++ b/valhalla/sif/truckcost.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_SIF_TRUCKCOST_H_ #define VALHALLA_SIF_TRUCKCOST_H_ -#include #include #include #include diff --git a/valhalla/skadi/sample.h b/valhalla/skadi/sample.h index f54449187f..ef88c3e5fa 100644 --- a/valhalla/skadi/sample.h +++ b/valhalla/skadi/sample.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/valhalla/skadi/util.h b/valhalla/skadi/util.h index 026aefb38e..b2d0adc123 100644 --- a/valhalla/skadi/util.h +++ b/valhalla/skadi/util.h @@ -1,7 +1,6 @@ #ifndef __VALHALLA_UTIL_H__ #define __VALHALLA_UTIL_H__ -#include #include #include diff --git a/valhalla/thor/astar_bss.h b/valhalla/thor/astar_bss.h index b4cf6821e5..ffe24dd084 100644 --- a/valhalla/thor/astar_bss.h +++ b/valhalla/thor/astar_bss.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/valhalla/thor/bidirectional_astar.h b/valhalla/thor/bidirectional_astar.h index efbde260a9..b64761c2cf 100644 --- a/valhalla/thor/bidirectional_astar.h +++ b/valhalla/thor/bidirectional_astar.h @@ -2,10 +2,7 @@ #define VALHALLA_THOR_BIDIRECTIONAL_ASTAR_H_ #include -#include #include -#include -#include #include #include diff --git a/valhalla/thor/centroid.h b/valhalla/thor/centroid.h index 1a6e83d5b0..aba4d55cef 100644 --- a/valhalla/thor/centroid.h +++ b/valhalla/thor/centroid.h @@ -1,9 +1,6 @@ #pragma once #include -#include -#include -#include #include #include diff --git a/valhalla/thor/costmatrix.h b/valhalla/thor/costmatrix.h index 157ad98441..e0a2e41544 100644 --- a/valhalla/thor/costmatrix.h +++ b/valhalla/thor/costmatrix.h @@ -2,10 +2,8 @@ #define VALHALLA_THOR_COSTMATRIX_H_ #include -#include #include #include -#include #include #include diff --git a/valhalla/thor/dijkstras.h b/valhalla/thor/dijkstras.h index d8050f388e..91bd822d94 100644 --- a/valhalla/thor/dijkstras.h +++ b/valhalla/thor/dijkstras.h @@ -2,10 +2,8 @@ #define VALHALLA_THOR_Dijkstras_H_ #include -#include #include #include -#include #include #include diff --git a/valhalla/thor/isochrone.h b/valhalla/thor/isochrone.h index 5a62891ff0..79767e60cc 100644 --- a/valhalla/thor/isochrone.h +++ b/valhalla/thor/isochrone.h @@ -2,11 +2,7 @@ #define VALHALLA_THOR_ISOCHRONE_H_ #include -#include #include -#include -#include -#include #include #include diff --git a/valhalla/thor/map_matcher.h b/valhalla/thor/map_matcher.h index 447eec205e..e4a3005b80 100644 --- a/valhalla/thor/map_matcher.h +++ b/valhalla/thor/map_matcher.h @@ -1,11 +1,7 @@ #ifndef VALHALLA_THOR_MAP_MATCHER_H_ #define VALHALLA_THOR_MAP_MATCHER_H_ -#include #include -#include -#include -#include #include #include diff --git a/valhalla/thor/multimodal.h b/valhalla/thor/multimodal.h index 7b622b8663..c0e779a935 100644 --- a/valhalla/thor/multimodal.h +++ b/valhalla/thor/multimodal.h @@ -5,7 +5,6 @@ #include #include #include -#include #include #include diff --git a/valhalla/thor/pathalgorithm.h b/valhalla/thor/pathalgorithm.h index a35da55460..4a75b48793 100644 --- a/valhalla/thor/pathalgorithm.h +++ b/valhalla/thor/pathalgorithm.h @@ -1,10 +1,6 @@ #pragma once #include -#include -#include -#include -#include #include #include diff --git a/valhalla/thor/pathinfo.h b/valhalla/thor/pathinfo.h index ea46cb6e19..1e48fbd7dc 100644 --- a/valhalla/thor/pathinfo.h +++ b/valhalla/thor/pathinfo.h @@ -1,7 +1,6 @@ #ifndef VALHALLA_THOR_PATHINFO_H_ #define VALHALLA_THOR_PATHINFO_H_ -#include #include #include diff --git a/valhalla/thor/route_matcher.h b/valhalla/thor/route_matcher.h index f2e78d519f..b42494cef4 100644 --- a/valhalla/thor/route_matcher.h +++ b/valhalla/thor/route_matcher.h @@ -1,11 +1,6 @@ #ifndef VALHALLA_THOR_ROUTE_MATCHER_H_ #define VALHALLA_THOR_ROUTE_MATCHER_H_ -#include -#include -#include -#include -#include #include #include diff --git a/valhalla/thor/timedistancebssmatrix.h b/valhalla/thor/timedistancebssmatrix.h index 5287d6deb5..eb62014215 100644 --- a/valhalla/thor/timedistancebssmatrix.h +++ b/valhalla/thor/timedistancebssmatrix.h @@ -2,10 +2,8 @@ #define VALHALLA_THOR_TIMEDISTANCEBSSMATRIX_H_ #include -#include #include #include -#include #include #include diff --git a/valhalla/thor/timedistancematrix.h b/valhalla/thor/timedistancematrix.h index 254336c7f5..8d41894b76 100644 --- a/valhalla/thor/timedistancematrix.h +++ b/valhalla/thor/timedistancematrix.h @@ -2,10 +2,8 @@ #define VALHALLA_THOR_TIMEDISTANCEMATRIX_H_ #include -#include #include #include -#include #include #include diff --git a/valhalla/thor/triplegbuilder.h b/valhalla/thor/triplegbuilder.h index 567e787078..d79bc78ddf 100644 --- a/valhalla/thor/triplegbuilder.h +++ b/valhalla/thor/triplegbuilder.h @@ -1,7 +1,5 @@ #pragma once -#include -#include #include #include #include diff --git a/valhalla/thor/unidirectional_astar.h b/valhalla/thor/unidirectional_astar.h index 1f28a68a71..15d2c0072d 100644 --- a/valhalla/thor/unidirectional_astar.h +++ b/valhalla/thor/unidirectional_astar.h @@ -2,7 +2,6 @@ #include #include -#include #include #include diff --git a/valhalla/thor/worker.h b/valhalla/thor/worker.h index 7334cdcac2..fb0ab64033 100644 --- a/valhalla/thor/worker.h +++ b/valhalla/thor/worker.h @@ -1,7 +1,6 @@ #ifndef __VALHALLA_THOR_SERVICE_H__ #define __VALHALLA_THOR_SERVICE_H__ -#include #include #include diff --git a/valhalla/tyr/actor.h b/valhalla/tyr/actor.h index 43ecb75d21..b90ecc5875 100644 --- a/valhalla/tyr/actor.h +++ b/valhalla/tyr/actor.h @@ -3,7 +3,6 @@ #include #include -#include #include #include diff --git a/valhalla/tyr/serializers.h b/valhalla/tyr/serializers.h index f494e802ce..6bb72dfdd8 100644 --- a/valhalla/tyr/serializers.h +++ b/valhalla/tyr/serializers.h @@ -1,8 +1,6 @@ #ifndef __VALHALLA_TYR_SERVICE_H__ #define __VALHALLA_TYR_SERVICE_H__ -#include -#include #include #include #include From e0f33e633896a9bcf5af07de270b7a5f52cd50f0 Mon Sep 17 00:00:00 2001 From: Stepan Kizim <10885920+kinkard@users.noreply.github.com> Date: Mon, 5 Aug 2024 15:34:06 +0200 Subject: [PATCH 109/198] Fix oneway ferry connections classification (#4828) --- CHANGELOG.md | 1 + src/mjolnir/ferry_connections.cc | 39 +++++--- test/gurka/test_ferry_connections.cc | 134 +++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a7a8242da..ef20243b8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ * FIXED: Trivial routes for CostMatrix [#4634](https://github.com/valhalla/valhalla/pull/4634) * FIXED: Reset `not_thru_pruning` in CostMatrix after second pass was used [#4817](https://github.com/valhalla/valhalla/pull/4817) * FIXED: wrong index used in CostMatrix expansion callback inside reverse connection check [#4821](https://github.com/valhalla/valhalla/pull/4821) + * FIXED: oneway ferry connections classification [#4828](https://github.com/valhalla/valhalla/pull/4828) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) diff --git a/src/mjolnir/ferry_connections.cc b/src/mjolnir/ferry_connections.cc index e6506e8485..72bbf8184c 100644 --- a/src/mjolnir/ferry_connections.cc +++ b/src/mjolnir/ferry_connections.cc @@ -380,6 +380,18 @@ void ReclassifyFerryConnections(const std::string& ways_file, if (ret2.second) { outbound_path_found = true; } + + // Reclassify the first/start edge if a connection to higher class roads + // is found. Do this AFTER finding shortest path so we do not immediately + // determine we hit the specified classification + sequence::iterator element = edges[edge.second]; + auto update_edge = *element; + if (ret1.second && ret2.second && update_edge.attributes.importance > kFerryUpClass) { + update_edge.attributes.importance = kFerryUpClass; + update_edge.attributes.reclass_ferry = remove_destonly; + element = update_edge; + total_count++; + } } else { // Check if oneway inbound to the ferry bool inbound = @@ -387,7 +399,19 @@ void ReclassifyFerryConnections(const std::string& ways_file, auto ret = ShortestPath(node_itr.position(), end_node_idx, ways, way_nodes, edges, nodes, inbound, remove_destonly); total_count += ret.first; - if (ret.second > 0) { + if (ret.second) { + // Reclassify the first/start edge if a connection to higher class roads + // is found. Do this AFTER finding shortest path so we do not immediately + // determine we hit the specified classification + sequence::iterator element = edges[edge.second]; + auto update_edge = *element; + if (update_edge.attributes.importance > kFerryUpClass) { + update_edge.attributes.importance = kFerryUpClass; + update_edge.attributes.reclass_ferry = remove_destonly; + element = update_edge; + total_count++; + } + if (inbound) { inbound_path_found = true; } else { @@ -396,19 +420,6 @@ void ReclassifyFerryConnections(const std::string& ways_file, } } ferry_endpoint_count++; - - // Reclassify the first/start edge if a connection to higher class roads - // is found. Do this AFTER finding shortest path so we do not immediately - // determine we hit the specified classification - sequence::iterator element = edges[edge.second]; - auto update_edge = *element; - if (inbound_path_found && outbound_path_found && - update_edge.attributes.importance > kFerryUpClass) { - update_edge.attributes.importance = kFerryUpClass; - update_edge.attributes.reclass_ferry = remove_destonly; - element = update_edge; - total_count++; - } } // Log cases where reclassification fails diff --git a/test/gurka/test_ferry_connections.cc b/test/gurka/test_ferry_connections.cc index a804cd81b7..d0584cfb1c 100644 --- a/test/gurka/test_ferry_connections.cc +++ b/test/gurka/test_ferry_connections.cc @@ -433,6 +433,140 @@ TEST(Standalone, ReclassifyCorrectPath) { valhalla::baldr::RoadClass::kServiceOther); } +TEST(Standalone, ReclassifySeparateInboundAndOutbound) { + // Sometimes ferry path is connected to the separate inbound and outbound pathways. + // Both of them should be reclassified if both of them lead to the high road class. + + const std::string ascii_map = R"( + A + | + | + B + / \ + / \ + C D + / | + E--F---------------G + H-----I------------J + \ / + -- + )"; + + std::map primary = {{"highway", "primary"}, {"oneway", "yes"}}; + std::map service = {{"highway", "service"}, {"oneway", "yes"}}; + + const gurka::ways ways = { + {"AB", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + {"BCF", service}, + {"IDB", service}, + {"EFG", primary}, + {"JIH", primary}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_inbound_outbound"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + // make sure that both service roads get reclassified + auto outbound_service = gurka::findEdge(reader, layout, "BCF", "F"); + EXPECT_EQ(std::get<1>(outbound_service)->classification(), valhalla::baldr::RoadClass::kPrimary); + auto inbound_service = gurka::findEdge(reader, layout, "IDB", "B"); + EXPECT_EQ(std::get<1>(inbound_service)->classification(), valhalla::baldr::RoadClass::kPrimary); +} + +TEST(Standalone, ReclassifyInboundOnly) { + const std::string ascii_map = R"( + A + | + | + B + / + / + C + / + E--F---------------G + H-----I------------J + )"; + + std::map primary = {{"highway", "primary"}, {"oneway", "yes"}}; + std::map service = {{"highway", "service"}, {"oneway", "yes"}}; + + const gurka::ways ways = { + {"AB", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + {"BCF", service}, + {"EFG", primary}, + {"JIH", primary}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_outbound_only"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + auto outbound_service = gurka::findEdge(reader, layout, "BCF", "F"); + EXPECT_EQ(std::get<1>(outbound_service)->classification(), valhalla::baldr::RoadClass::kPrimary); +} + +TEST(Standalone, ReclassifyOutboundOnly) { + const std::string ascii_map = R"( + A + | + | + B + \ + \ + D + | + E--F---------------G + H-----I------------J + \ / + -- + )"; + + std::map primary = {{"highway", "primary"}, {"oneway", "yes"}}; + std::map service = {{"highway", "service"}, {"oneway", "yes"}}; + + const gurka::ways ways = { + {"AB", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + {"IDB", service}, + {"EFG", primary}, + {"JIH", primary}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_reclassify_inbound_only"); + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + auto inbound_service = gurka::findEdge(reader, layout, "IDB", "B"); + EXPECT_EQ(std::get<1>(inbound_service)->classification(), valhalla::baldr::RoadClass::kPrimary); +} + TEST(Standalone, ReclassifyNothingReclassified) { // Test to validate that if no edges are found with the target classification // nothing gets reclassified. From 75cef6a375e5549e7630e9dcd8e96200bd52c20b Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Tue, 6 Aug 2024 03:52:47 +0200 Subject: [PATCH 110/198] Add documentation about alternates in trace_route (#4832) --- docs/docs/api/map-matching/api-reference.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/docs/api/map-matching/api-reference.md b/docs/docs/api/map-matching/api-reference.md index 1385dbd62d..e3dbf34037 100644 --- a/docs/docs/api/map-matching/api-reference.md +++ b/docs/docs/api/map-matching/api-reference.md @@ -14,6 +14,8 @@ The `trace_route` action takes the costing mode and a list of latitude,longitude By default a single trip leg is returned in a trace_route response. You can split the route response into multiple legs by setting `"type":"break"` on any of the input shape objects in the `shape` parameter of your query. The first and last locations should always have type break. Note that setting breaks is not supported for encoded_polyline input, and is only supported for `map_snap` mode of the trace_route endpoint. +If the path contains one or more discontinuities (i.e. no path can be found between two locations), it is split into multiple paths. Any remaining paths from the first discontinuity onwards are stored as route alternates on the response. + ## Trace attributes action The `trace_attributes` action takes the costing mode and a GPS trace or latitude,longitude positions and returns detailed attribution along the portion of the route. This includes details for each section of road along the path, as well as any intersections along the path. Some of the use cases for `trace_attributes` include getting: From 8748f411e466cd183f76b106e8a42082a5dc65cc Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Wed, 7 Aug 2024 04:23:43 -0400 Subject: [PATCH 111/198] fix location search_filter for node snaps (#4835) --- CHANGELOG.md | 1 + src/loki/search.cc | 67 +++++++++++++++++++------------- test/gurka/test_search_filter.cc | 17 ++++++++ test/gurka/test_traffic.cc | 1 - 4 files changed, 59 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef20243b8b..ece0557b6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,7 @@ * FIXED: Reset `not_thru_pruning` in CostMatrix after second pass was used [#4817](https://github.com/valhalla/valhalla/pull/4817) * FIXED: wrong index used in CostMatrix expansion callback inside reverse connection check [#4821](https://github.com/valhalla/valhalla/pull/4821) * FIXED: oneway ferry connections classification [#4828](https://github.com/valhalla/valhalla/pull/4828) + * FIXED: location search_filter ignored in certain cases [#4835](https://github.com/valhalla/valhalla/pull/4835) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) diff --git a/src/loki/search.cc b/src/loki/search.cc index c96bfaa006..cc286a4d24 100644 --- a/src/loki/search.cc +++ b/src/loki/search.cc @@ -22,23 +22,22 @@ template inline T square(T v) { return v * v; } -bool is_search_filter_triggered(const DirectedEdge* edge, - const DynamicCost& costing, - const graph_tile_ptr& tile, - const Location::SearchFilter& search_filter) { +bool search_filter(const DirectedEdge* edge, + const DynamicCost& costing, + const graph_tile_ptr& tile, + const Location::SearchFilter& filter) { // check if this edge matches any of the exclusion filters uint32_t road_class = static_cast(edge->classification()); - uint32_t min_road_class = static_cast(search_filter.min_road_class_); - uint32_t max_road_class = static_cast(search_filter.max_road_class_); + uint32_t min_road_class = static_cast(filter.min_road_class_); + uint32_t max_road_class = static_cast(filter.max_road_class_); // Note that min_ and max_road_class are integers where, by default, max_road_class // is 0 and min_road_class is 7. This filter rejects roads where the functional // road class is outside of the min to max range. return (road_class > min_road_class || road_class < max_road_class) || - (search_filter.exclude_tunnel_ && edge->tunnel()) || - (search_filter.exclude_bridge_ && edge->bridge()) || - (search_filter.exclude_ramp_ && (edge->use() == Use::kRamp)) || - (search_filter.exclude_closures_ && (costing.flow_mask() & kCurrentFlowMask) && + (filter.exclude_tunnel_ && edge->tunnel()) || (filter.exclude_bridge_ && edge->bridge()) || + (filter.exclude_ramp_ && (edge->use() == Use::kRamp)) || + (filter.exclude_closures_ && (costing.flow_mask() & kCurrentFlowMask) && tile->IsClosed(edge)); } @@ -320,8 +319,10 @@ struct bin_handler_t { tangent_angle(index, candidate.point, info.shape(), GetOffsetForHeading(edge->classification(), edge->use()), edge->forward()); auto layer = info.layer(); - // do we want this edge - if (costing->Allowed(edge, tile, kDisallowShortcut)) { + // do we want this edge, note we have to re-evaluate the filter check because we may be + // seeing these edges a second time (filtered out before) + if (costing->Allowed(edge, tile, kDisallowShortcut) && + !search_filter(edge, *costing, tile, location.search_filter_)) { auto reach = get_reach(id, edge); PathLocation::PathEdge path_edge{id, 0, node_ll, distance, PathLocation::NONE, reach.outbound, reach.inbound, @@ -340,7 +341,8 @@ struct bin_handler_t { if (!other_edge) continue; - if (costing->Allowed(other_edge, other_tile, kDisallowShortcut)) { + if (costing->Allowed(other_edge, other_tile, kDisallowShortcut) && + !search_filter(other_edge, *costing, other_tile, location.search_filter_)) { auto opp_angle = std::fmod(angle + 180.f, 360.f); auto reach = get_reach(other_id, other_edge); PathLocation::PathEdge path_edge{other_id, @@ -418,11 +420,13 @@ struct bin_handler_t { PathLocation::PathEdge path_edge{candidate.edge_id, length_ratio, candidate.point, distance, side, reach.outbound, reach.inbound, angle}; - // correlate the edge we found - if (side_filter(path_edge, location, reader) || heading_filter(location, angle) || - layer_filter(location, layer)) { + // correlate the edge we found if its not filtered out + bool hard_filtered = + search_filter(candidate.edge, *costing, candidate.tile, location.search_filter_); + if (!hard_filtered && (side_filter(path_edge, location, reader) || + heading_filter(location, angle) || layer_filter(location, layer))) { filtered.push_back(std::move(path_edge)); - } else if (correlated_edges.insert(candidate.edge_id).second) { + } else if (!hard_filtered && correlated_edges.insert(candidate.edge_id).second) { correlated.edges.push_back(std::move(path_edge)); } // correlate its evil twin @@ -430,7 +434,8 @@ struct bin_handler_t { graph_tile_ptr other_tile; auto opposing_edge_id = reader.GetOpposingEdgeId(candidate.edge_id, other_edge, other_tile); - if (other_edge && costing->Allowed(other_edge, other_tile, kDisallowShortcut)) { + if (other_edge && costing->Allowed(other_edge, other_tile, kDisallowShortcut) && + !search_filter(other_edge, *costing, other_tile, location.search_filter_)) { auto opp_angle = std::fmod(angle + 180.f, 360.f); reach = get_reach(opposing_edge_id, other_edge); PathLocation::PathEdge other_path_edge{opposing_edge_id, 1 - length_ratio, candidate.point, @@ -514,14 +519,22 @@ struct bin_handler_t { continue; } + // lots of places below where we might like to know about the opp edge + const DirectedEdge* opp_edge = nullptr; + graph_tile_ptr opp_tile = tile; + GraphId opp_edgeid; + // if this edge is filtered const auto* edge = tile->directededge(edge_id); if (!costing->Allowed(edge, tile, kDisallowShortcut)) { - // then we try its opposing edge - edge_id = reader.GetOpposingEdgeId(edge_id, edge, tile); // but if we couldnt get it or its filtered too then we move on - if (!edge_id.Is_Valid() || !costing->Allowed(edge, tile, kDisallowShortcut)) + if (!(opp_edgeid = reader.GetOpposingEdgeId(edge_id, opp_edge, opp_tile)) || + !costing->Allowed(opp_edge, opp_tile, kDisallowShortcut)) continue; + // if we will continue with the opposing edge lets swap it in + std::swap(edge, opp_edge); + std::swap(tile, opp_tile); + std::swap(edge_id, opp_edgeid); } // initialize candidates vector: @@ -532,8 +545,12 @@ struct bin_handler_t { bool all_prefiltered = true; for (p_itr = begin; p_itr != end; ++p_itr, ++c_itr) { c_itr->sq_distance = std::numeric_limits::max(); + // for traffic closures we may have only one direction disabled so we must also check opp + // before we can be sure that we can completely filter this edge pair for this location c_itr->prefiltered = - is_search_filter_triggered(edge, *costing, tile, p_itr->location.search_filter_); + search_filter(edge, *costing, tile, p_itr->location.search_filter_) && + (opp_edgeid = reader.GetOpposingEdgeId(edge_id, opp_edge, opp_tile)) && + search_filter(opp_edge, *costing, opp_tile, p_itr->location.search_filter_); // set to false if even one candidate was not filtered all_prefiltered = all_prefiltered && c_itr->prefiltered; } @@ -595,12 +612,10 @@ struct bin_handler_t { // is this edge reachable in the right way bool reachable = reach.outbound >= p_itr->location.min_outbound_reach_ && reach.inbound >= p_itr->location.min_inbound_reach_; - const DirectedEdge* opp_edge = nullptr; - graph_tile_ptr opp_tile = tile; - GraphId opp_edgeid; // it's possible that it isnt reachable but the opposing is, switch to that if so if (!reachable && (opp_edgeid = reader.GetOpposingEdgeId(edge_id, opp_edge, opp_tile)) && - costing->Allowed(opp_edge, opp_tile, kDisallowShortcut)) { + costing->Allowed(opp_edge, opp_tile, kDisallowShortcut) && + !search_filter(opp_edge, *costing, opp_tile, p_itr->location.search_filter_)) { auto opp_reach = check_reachability(begin, end, opp_tile, opp_edge, opp_edgeid); if (opp_reach.outbound >= p_itr->location.min_outbound_reach_ && opp_reach.inbound >= p_itr->location.min_inbound_reach_) { diff --git a/test/gurka/test_search_filter.cc b/test/gurka/test_search_filter.cc index 51f386569f..1bce9f18dd 100644 --- a/test/gurka/test_search_filter.cc +++ b/test/gurka/test_search_filter.cc @@ -61,6 +61,23 @@ TEST_F(SearchFilter, Unfiltered) { gurka::assert::osrm::expect_steps(result, {"AB", "BC"}); gurka::assert::raw::expect_path(result, {"AB", "BC"}); } +TEST_F(SearchFilter, NodeSnapped) { + auto from = "B"; + auto to = "C"; + + const std::string& request = + (boost::format( + R"({"locations":[{"lat":%s,"lon":%s,"search_filter":{"exclude_tunnel":true}},{"lat":%s,"lon":%s}],"costing":"auto"})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + + auto result = gurka::do_action(valhalla::Options::route, map, request); + + // should take the shortest path + gurka::assert::osrm::expect_steps(result, {"AB", "AD", "CD"}); + gurka::assert::raw::expect_path(result, {"AB", "AD", "CD"}); +} TEST_F(SearchFilter, Heading) { auto from = "1"; auto to = "2"; diff --git a/test/gurka/test_traffic.cc b/test/gurka/test_traffic.cc index 42a68235e0..f2618a7b08 100644 --- a/test/gurka/test_traffic.cc +++ b/test/gurka/test_traffic.cc @@ -110,7 +110,6 @@ TEST(Traffic, BasicUpdates) { "it's noticed the changes in the live traffic file" << std::endl; { - auto result = gurka::do_action(valhalla::Options::route, map, {"B", "D"}, "auto", {{"/date_time/type", "0"}}, clean_reader); gurka::assert::osrm::expect_steps(result, {"BC", "CE", "DE"}); From 5faeff858a5f8535d389fe55d9880e1f4c3eb268 Mon Sep 17 00:00:00 2001 From: Stepan Kizim <10885920+kinkard@users.noreply.github.com> Date: Sat, 17 Aug 2024 22:14:01 +0200 Subject: [PATCH 112/198] fix: blocked route reclassification (#4854) --- CHANGELOG.md | 1 + src/mjolnir/ferry_connections.cc | 3 ++ test/gurka/test_ferry_connections.cc | 71 ++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ece0557b6e..df22466588 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ * FIXED: wrong index used in CostMatrix expansion callback inside reverse connection check [#4821](https://github.com/valhalla/valhalla/pull/4821) * FIXED: oneway ferry connections classification [#4828](https://github.com/valhalla/valhalla/pull/4828) * FIXED: location search_filter ignored in certain cases [#4835](https://github.com/valhalla/valhalla/pull/4835) + * FIXED: Ferry reclassification finds shortest path that is blocked by inaccessible node [#4854](https://github.com/valhalla/valhalla/pull/4854) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) diff --git a/src/mjolnir/ferry_connections.cc b/src/mjolnir/ferry_connections.cc index 72bbf8184c..7ab02d749f 100644 --- a/src/mjolnir/ferry_connections.cc +++ b/src/mjolnir/ferry_connections.cc @@ -114,6 +114,9 @@ std::pair ShortestPath(const uint32_t start_node_idx, // Expand all edges from this node auto expand_node_itr = nodes[expand_node_idx]; auto expanded_bundle = collect_node_edges(expand_node_itr, nodes, edges); + if (!(expanded_bundle.node.access() & access_filter)) { + continue; + } // We are finished if node has RC <= rc and beyond first several edges. // Have seen cases where the immediate connections are high class roads diff --git a/test/gurka/test_ferry_connections.cc b/test/gurka/test_ferry_connections.cc index d0584cfb1c..e2b4ae98a1 100644 --- a/test/gurka/test_ferry_connections.cc +++ b/test/gurka/test_ferry_connections.cc @@ -567,6 +567,77 @@ TEST(Standalone, ReclassifyOutboundOnly) { EXPECT_EQ(std::get<1>(inbound_service)->classification(), valhalla::baldr::RoadClass::kPrimary); } +TEST(Standalone, ConsiderBlockedRoads) { + // Nodes that block the path should be considered when the fastest way from ferry to high class road + // is evaluated, as otherwise found shortest path might be not traversable + + const std::string ascii_map = R"( + A-------B M + | | + | | + D---C-1-N + / | + E | + \ | + F-------O + | + P + + )"; + + const gurka::ways ways = { + {"AB", + {{"motor_vehicle", "yes"}, + {"motorcar", "yes"}, + {"bicycle", "yes"}, + {"moped", "yes"}, + {"bus", "yes"}, + {"hov", "yes"}, + {"taxi", "yes"}, + {"motorcycle", "yes"}, + {"route", "ferry"}}}, + + // shorter road that is blocked by block + {"C1N", {{"highway", "service"}}}, + + // longer unblocked road + {"BC", {{"highway", "service"}}}, + {"CD", {{"highway", "service"}}}, + {"DE", {{"highway", "service"}}}, + {"EF", {{"highway", "service"}}}, + {"FO", {{"highway", "service"}}}, + + {"MNOP", {{"highway", "primary"}}}, + }; + + const gurka::nodes nodes = { + {"1", + { + {"barrier", "block"}, + {"motor_vehicle", "no"}, + }}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + auto map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/gurka_reclassify_block"); + + // sanity check that the longer route is taken + auto result = gurka::do_action(valhalla::Options::route, map, {"A", "M"}, "auto"); + gurka::assert::raw::expect_path(result, {"AB", "BC", "CD", "DE", "EF", "FO", "MNOP", "MNOP"}); + + baldr::GraphReader reader(map.config.get_child("mjolnir")); + + // long not blocked road should be reclassified + for (const auto& name : {"BC", "CD", "DE", "EF", "FO"}) { + const auto way = gurka::findEdge(reader, layout, name, name + 1); + EXPECT_EQ(std::get<1>(way)->classification(), valhalla::baldr::RoadClass::kPrimary); + } + + // short blocked route is not reclassified + auto blocked = gurka::findEdge(reader, layout, "C1N", "N"); + EXPECT_EQ(std::get<1>(blocked)->classification(), valhalla::baldr::RoadClass::kServiceOther); +} + TEST(Standalone, ReclassifyNothingReclassified) { // Test to validate that if no edges are found with the target classification // nothing gets reclassified. From b05c9d0894be18617ace532e691a990bfc53e1d3 Mon Sep 17 00:00:00 2001 From: Stepan Kizim <10885920+kinkard@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:55:36 +0200 Subject: [PATCH 113/198] Fix `(Nov - Mar)` condition parsing (#4857) --- CHANGELOG.md | 1 + src/mjolnir/timeparsing.cc | 184 +++++++++++++++++++-------------- test/datetime.cc | 10 ++ test/timeparsing.cc | 201 +++++++++++++++++++++++++------------ 4 files changed, 257 insertions(+), 139 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df22466588..4bf016e236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ * FIXED: oneway ferry connections classification [#4828](https://github.com/valhalla/valhalla/pull/4828) * FIXED: location search_filter ignored in certain cases [#4835](https://github.com/valhalla/valhalla/pull/4835) * FIXED: Ferry reclassification finds shortest path that is blocked by inaccessible node [#4854](https://github.com/valhalla/valhalla/pull/4854) + * FIXED: `(Nov - Mar)` (and similar, months with spaces) condition parsing [#4857](https://github.com/valhalla/valhalla/pull/4857) * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) diff --git a/src/mjolnir/timeparsing.cc b/src/mjolnir/timeparsing.cc index 3b5a852d04..790e84271c 100644 --- a/src/mjolnir/timeparsing.cc +++ b/src/mjolnir/timeparsing.cc @@ -3,7 +3,6 @@ #include #include -#include #include #include "baldr/graphconstants.h" @@ -14,8 +13,78 @@ using namespace valhalla::baldr; using namespace valhalla::mjolnir; -namespace valhalla { -namespace mjolnir { +namespace { +// Dec Su[-1]-Mar 3 => Dec#Su#[5]-Mar#3 +const std::pair kBeginWeekdayOfTheMonth = + {std::regex( + "(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" + "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]\\])-" + "(?:(January|February|March|April|May|June|July|August|September|October|November" + "|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)) (\\d{1,2}))", + std::regex_constants::icase), + "$1#$2#$3-$4#$5"}; + +// Mar 3-Dec Su[-1] => Mar#3-Dec#Su#[5] +const std::pair kEndWeedkayOfTheMonth = + {std::regex( + "(?:(January|February|March|April|May|June|July|August|" + "September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|" + "Nov|Dec)) (\\d{1,2})-(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" + "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]\\])" + ")", + std::regex_constants::icase), + "$1#$2-$3#$4#$5"}; + +// Dec Su[-1] => Dec#Su#[5] +const std::pair kWeekdayOfTheMonth = + {std::regex("(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" + "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]" + "\\]))", + std::regex_constants::icase), + "$1#$2#$3"}; + +// Mon[-1] => Mon#[5], Tue[2] => Tue#[2] +const std::pair kWeekdayOfEveryMonth = + {std::regex("(?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" + "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|" + "Su)(\\[-?[0-9]\\]))", + std::regex_constants::icase), + "$1#$2"}; + +// Feb 16-Oct 15 09:00-18:30 => Feb#16-Oct#15 09:00-18:30 +const std::pair kMonthDay = + {std::regex("(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) (\\d{1,2})", + std::regex_constants::icase), + "$1#$2"}; + +// Feb 2-14 => Feb#2-Feb#14 +const std::pair kRangeWithinMonth = + {std::regex("(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) (\\d{1,2})-(\\d{1,2})", + std::regex_constants::icase), + "$1#$2-$1#$3"}; + +// Nov - Mar => Nov-Mar +const std::pair kMonthRange = + {std::regex("(?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec)) - (?:(January|February|March|April|May|June|July|" + "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" + "Sep|Sept|Oct|Nov|Dec))", + std::regex_constants::icase), + "$1-$2"}; + +// fifth is the equivalent of last week in month (-1) +const std::pair kLastWeekday = {std::regex("\\[-1\\]"), "[5]"}; std::vector GetTokens(const std::string& tag_value, char delim) { std::vector tokens; @@ -31,11 +100,16 @@ bool RegexFound(const std::string& source, const std::regex& regex) { return std::distance(begin, end); } -std::string -FormatCondition(const std::string& source, const std::regex& regex, const std::string& pattern) { - return std::regex_replace(source, regex, pattern); +std::string FormatCondition(const std::string& source, + const std::pair& regex_pattern) { + return std::regex_replace(source, regex_pattern.first, regex_pattern.second); } +} // namespace + +namespace valhalla { +namespace mjolnir { + // get the dow mask from the provided string. try to handle most inputs uint8_t get_dow_mask(const std::string& dow) { @@ -163,84 +237,44 @@ std::vector get_time_range(const std::string& str) { return time_domains; } - // Dec Su[-1]-Mar 3 - std::regex regex = std::regex( - "(?:(January|February|March|April|May|June|July|" - "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" - "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" - "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]\\])-" - "(?:(January|February|March|April|May|June|July|August|September|October|November" - "|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|Nov|Dec)) (\\d{1,2}))", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2#$3-$4#$5"); - // fifth is the equivalent of last week in month (-1) - condition = FormatCondition(condition, std::regex("\\[-1\\]"), "[5]"); + // Dec Su[-1]-Mar 3 => Dec#Su#[5]-Mar#3 + if (RegexFound(condition, kBeginWeekdayOfTheMonth.first)) { + condition = FormatCondition(condition, kBeginWeekdayOfTheMonth); + condition = FormatCondition(condition, kLastWeekday); } else { - // Mar 3-Dec Su[-1] - std::regex regex = std::regex( - "(?:(January|February|March|April|May|June|July|August|" - "September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Sept|Oct|" - "Nov|Dec)) (\\d{1,2})-(?:(January|February|March|April|May|June|July|" - "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" - "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" - "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]\\])" - ")", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2-$3#$4#$5"); - // fifth is the equivalent of last week in month (-1) - condition = FormatCondition(condition, std::regex("\\[-1\\]"), "[5]"); + // Mar 3-Dec Su[-1] => Mar#3-Dec#Su#[5] + if (RegexFound(condition, kEndWeedkayOfTheMonth.first)) { + condition = FormatCondition(condition, kEndWeedkayOfTheMonth); + condition = FormatCondition(condition, kLastWeekday); } else { - // Dec Su[-1] - regex = std::regex( - "(?:(January|February|March|April|May|June|July|" - "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" - "Sep|Sept|Oct|Nov|Dec)) (?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" - "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|Su)(\\[-?[0-9]" - "\\]))", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2#$3"); - // fifth is the equivalent of last week in month (-1) - condition = FormatCondition(condition, std::regex("\\[-1\\]"), "[5]"); + // Dec Su[-1] => Dec#Su#[5] + if (RegexFound(condition, kWeekdayOfTheMonth.first)) { + condition = FormatCondition(condition, kWeekdayOfTheMonth); + condition = FormatCondition(condition, kLastWeekday); } else { - regex = std::regex("(?:(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|" - "Sunday|Mon|Mo|Tues|Tue|Tu|Weds|Wed|We|Thurs|Thur|Th|Fri|Fr|Sat|Sa|Sun|" - "Su)(\\[-?[0-9]\\]))", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2"); - // fifth is the equivalent of last week in month (-1) - condition = FormatCondition(condition, std::regex("\\[-1\\]"), "[5]"); + // Mon[-1] => Mon#[5], Tue[2] => Tue#[2] + if (RegexFound(condition, kWeekdayOfEveryMonth.first)) { + condition = FormatCondition(condition, kWeekdayOfEveryMonth); + condition = FormatCondition(condition, kLastWeekday); } else { - // Feb 16-Oct 15 09:00-18:30 - regex = std:: - regex("(?:(January|February|March|April|May|June|July|" - "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" - "Sep|Sept|Oct|Nov|Dec)) (\\d{1,2})", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2"); + // Feb 16-Oct 15 09:00-18:30 => Feb#16-Oct#15 09:00-18:30 + if (RegexFound(condition, kMonthDay.first)) { + condition = FormatCondition(condition, kMonthDay); } else { - // Feb 2-14 - regex = std:: - regex("(?:(January|February|March|April|May|June|July|" - "August|September|October|November|December|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|" - "Sep|Sept|Oct|Nov|Dec)) (\\d{1,2})-(\\d{1,2})", - std::regex_constants::icase); - - if (RegexFound(condition, regex)) { - condition = FormatCondition(condition, regex, "$1#$2-$1#$3"); + + // Feb 2-14 => Feb#2-Feb#14 + if (RegexFound(condition, kRangeWithinMonth.first)) { + condition = FormatCondition(condition, kRangeWithinMonth); + } else { + + // Nov - Mar => Nov-Mar + if (RegexFound(condition, kMonthRange.first)) { + condition = FormatCondition(condition, kMonthRange); + } } } } diff --git a/test/datetime.cc b/test/datetime.cc index 32dd11d770..07de5345c0 100644 --- a/test/datetime.cc +++ b/test/datetime.cc @@ -550,6 +550,16 @@ TEST(DateTime, TestIsRestricted) { TryIsRestricted(td, "2022-05-10T16:00", false); TryIsRestricted(td, "2021-02-18T16:00", false); TryIsRestricted(td, "2021-06-26T16:00", false); + + td = TimeDomain(35184375234560); // "Jun-Aug" + TryIsRestricted(td, "2024-04-03T08:00", false); + TryIsRestricted(td, "2024-05-31T21:00", false); + TryIsRestricted(td, "2024-06-01T00:01", true); + TryIsRestricted(td, "2024-06-15T16:00", true); + TryIsRestricted(td, "2024-07-10T16:00", true); + TryIsRestricted(td, "2024-08-18T16:00", true); + TryIsRestricted(td, "2024-08-31T23:59", true); + TryIsRestricted(td, "2024-09-01T00:01", false); } TEST(DateTime, TestTimezoneDiff) { diff --git a/test/timeparsing.cc b/test/timeparsing.cc index aca50d95f9..68c8bf7004 100644 --- a/test/timeparsing.cc +++ b/test/timeparsing.cc @@ -48,20 +48,20 @@ void TryConditionalRestrictions(const std::string& condition, } } +struct DateTimePoint { + uint32_t month; + uint32_t day; + uint32_t week; + uint32_t hour; + uint32_t minute; +}; + void TryConditionalRestrictions(const std::string& condition, const uint32_t index, const uint32_t type, const uint32_t dow, - const uint32_t begin_month, - const uint32_t begin_day, - const uint32_t begin_week, - const uint32_t begin_hrs, - const uint32_t begin_mins, - const uint32_t end_month, - const uint32_t end_day, - const uint32_t end_week, - const uint32_t end_hrs, - const uint32_t end_mins) { + const DateTimePoint begin, + const DateTimePoint end) { std::vector results = get_time_range(condition); @@ -69,16 +69,16 @@ void TryConditionalRestrictions(const std::string& condition, EXPECT_EQ(res.type(), type); EXPECT_EQ(res.dow(), dow); - EXPECT_EQ(res.begin_month(), begin_month); - EXPECT_EQ(res.begin_day_dow(), begin_day); - EXPECT_EQ(res.begin_week(), begin_week); - EXPECT_EQ(res.begin_hrs(), begin_hrs); - EXPECT_EQ(res.begin_mins(), begin_mins); - EXPECT_EQ(res.end_month(), end_month); - EXPECT_EQ(res.end_day_dow(), end_day); - EXPECT_EQ(res.end_week(), end_week); - EXPECT_EQ(res.end_hrs(), end_hrs); - EXPECT_EQ(res.end_mins(), end_mins); + EXPECT_EQ(res.begin_month(), begin.month); + EXPECT_EQ(res.begin_day_dow(), begin.day); + EXPECT_EQ(res.begin_week(), begin.week); + EXPECT_EQ(res.begin_hrs(), begin.hour); + EXPECT_EQ(res.begin_mins(), begin.minute); + EXPECT_EQ(res.end_month(), end.month); + EXPECT_EQ(res.end_day_dow(), end.day); + EXPECT_EQ(res.end_week(), end.week); + EXPECT_EQ(res.end_hrs(), end.hour); + EXPECT_EQ(res.end_mins(), end.minute); if (::testing::Test::HasFailure()) { std::cerr << "Time domain: " << condition << std::endl; @@ -102,15 +102,15 @@ TEST(TimeParsing, TestConditionalRestrictions) { TryConditionalRestrictions(conditions.at(x), expected_values); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 62, 0, 0, 0, 6, 0, 0, 0, 0, 11, 0); - TryConditionalRestrictions(conditions.at(x), 1, 0, 62, 0, 0, 0, 17, 0, 0, 0, 0, 19, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 62, {0, 0, 0, 6, 0}, {0, 0, 0, 11, 0}); + TryConditionalRestrictions(conditions.at(x), 1, 0, 62, {0, 0, 0, 17, 0}, {0, 0, 0, 19, 0}); } else if (x == 1) { // Sa 03:30-19:00 std::vector expected_values; expected_values.push_back(40802435968); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 64, 0, 0, 0, 3, 30, 0, 0, 0, 19, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 64, {0, 0, 0, 3, 30}, {0, 0, 0, 19, 0}); } } @@ -123,14 +123,14 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(38654708852); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 58, 0, 0, 0, 12, 0, 0, 0, 0, 18, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 58, {0, 0, 0, 12, 0}, {0, 0, 0, 18, 0}); } else if (x == 1) { // Sa-Su 12:00-17:00 std::vector expected_values; expected_values.push_back(36507225218); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 65, 0, 0, 0, 12, 0, 0, 0, 0, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 65, {0, 0, 0, 12, 0}, {0, 0, 0, 17, 0}); } } @@ -144,14 +144,14 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(1512971146104448); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 64, 7, 23, 0, 14, 0, 8, 21, 0, 20, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 64, {7, 23, 0, 14, 0}, {8, 21, 0, 20, 0}); } else if (x == 1) { // JUL 23-jUl 28 Fr,PH 10:00-20:00 std::vector expected_values; expected_values.push_back(2001154308835904); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 32, 7, 23, 0, 10, 0, 7, 28, 0, 20, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 32, {7, 23, 0, 10, 0}, {7, 28, 0, 20, 0}); } } @@ -171,7 +171,7 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(39610337987200); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 64, 4, 0, 0, 10, 0, 9, 0, 0, 13, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 64, {4, 0, 0, 10, 0}, {9, 0, 0, 13, 0}); } } @@ -191,7 +191,7 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(39610337987200); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 64, 4, 0, 0, 10, 0, 9, 0, 0, 13, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 64, {4, 0, 0, 10, 0}, {9, 0, 0, 13, 0}); } } @@ -205,8 +205,8 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(23622321664); expected_values.push_back(3133178646784); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 11, 0); - TryConditionalRestrictions(conditions.at(x), 1, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 19, 45); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {0, 0, 0, 6, 0}, {0, 0, 0, 11, 0}); + TryConditionalRestrictions(conditions.at(x), 1, 0, 0, {0, 0, 0, 17, 0}, {0, 0, 0, 19, 45}); } } @@ -222,13 +222,13 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(1106007905274112); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 10, 16, 0, 9, 0, 11, 15, 0, 17, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {10, 16, 0, 9, 0}, {11, 15, 0, 17, 30}); } else if (x == 2) { // Nov 16-Feb 15: 09:00-16:30 std::vector expected_values; expected_values.push_back(1066423339714816); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 11, 16, 0, 9, 0, 02, 15, 0, 16, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {11, 16, 0, 9, 0}, {02, 15, 0, 16, 30}); } } @@ -241,27 +241,27 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(2078764173088); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 16, 0, 0, 0, 7, 0, 0, 0, 0, 8, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 16, {0, 0, 0, 7, 0}, {0, 0, 0, 8, 30}); } else if (x == 1) { // th-friday 06:00-09:30 std::vector expected_values; expected_values.push_back(2080911656544); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 48, 0, 0, 0, 6, 0, 0, 0, 0, 9, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 48, {0, 0, 0, 6, 0}, {0, 0, 0, 9, 30}); } else if (x == 2) { // May 15 09:00-11:30 std::vector expected_values; expected_values.push_back(1079606730295552); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 5, 15, 0, 9, 0, 5, 15, 0, 11, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {5, 15, 0, 9, 0}, {5, 15, 0, 11, 30}); } else if (x == 3) { // May 07:00-08:30 std::vector expected_values; expected_values.push_back(24068999350016); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 5, 0, 0, 7, 0, 5, 0, 0, 8, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {5, 0, 0, 7, 0}, {5, 0, 0, 8, 30}); } else if (x == 4) { // May 16-31 11:00-13:30 std::vector expected_values; expected_values.push_back(2205510940494592); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 5, 16, 0, 11, 0, 5, 31, 0, 13, 30); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {5, 16, 0, 11, 0}, {5, 31, 0, 13, 30}); } } @@ -274,16 +274,16 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(29856470044524); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 54, 9, 0, 0, 8, 15, 6, 0, 0, 8, 45); - TryConditionalRestrictions(conditions.at(x), 1, 0, 54, 9, 0, 0, 15, 20, 6, 0, 0, 15, 50); + TryConditionalRestrictions(conditions.at(x), 0, 0, 54, {9, 0, 0, 8, 15}, {6, 0, 0, 8, 45}); + TryConditionalRestrictions(conditions.at(x), 1, 0, 54, {9, 0, 0, 15, 20}, {6, 0, 0, 15, 50}); } else if (x == 1) { // Sep-Jun We 08:15-08:45,11:55-12:35 std::vector expected_values; expected_values.push_back(29497840232464); expected_values.push_back(28819235728144); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 8, 9, 0, 0, 8, 15, 6, 0, 0, 8, 45); - TryConditionalRestrictions(conditions.at(x), 1, 0, 8, 9, 0, 0, 11, 55, 6, 0, 0, 12, 35); + TryConditionalRestrictions(conditions.at(x), 0, 0, 8, {9, 0, 0, 8, 15}, {6, 0, 0, 8, 45}); + TryConditionalRestrictions(conditions.at(x), 1, 0, 8, {9, 0, 0, 11, 55}, {6, 0, 0, 12, 35}); } } @@ -296,7 +296,7 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(9372272830712067); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 1, 10, 1, 5, 9, 0, 3, 5, 4, 16, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 1, {10, 1, 5, 9, 0}, {3, 5, 4, 16, 0}); } else if (x == 1) { // PH 09:00-16:00 Holidays are tossed for now std::vector expected_values; expected_values.push_back(0); @@ -305,7 +305,7 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(11373388284561667); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 1, 3, 1, 5, 9, 0, 10, 1, 5, 18, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 1, {3, 1, 5, 9, 0}, {10, 1, 5, 18, 0}); } else if (x == 3) { // PH 09:00-18:00 Holidays are tossed for now std::vector expected_values; expected_values.push_back(0); @@ -322,14 +322,14 @@ TEST(TimeParsing, TestConditionalRestrictions) { expected_values.push_back(7252416602836867); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 65, 12, 6, 5, 9, 0, 1, 7, 3, 16, 0); - TryConditionalRestrictions(conditions.at(x), 1, 1, 65, 12, 6, 5, 15, 0, 1, 7, 3, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 65, {12, 6, 5, 9, 0}, {1, 7, 3, 16, 0}); + TryConditionalRestrictions(conditions.at(x), 1, 1, 65, {12, 6, 5, 15, 0}, {1, 7, 3, 17, 0}); } else if (x == 1) { // Dec Su[-1] Su-Sa 15:00-17:00 std::vector expected_values; expected_values.push_back(11311813490642943); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 127, 12, 1, 5, 15, 0, 12, 0, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 127, {12, 1, 5, 15, 0}, {12, 0, 5, 17, 0}); } } @@ -343,62 +343,62 @@ TEST(TimeParsing, TestConditionalRestrictions) { std::vector expected_values; expected_values.push_back(34359740674); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 1, 0, 0, 0, 9, 0, 0, 0, 0, 16, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 1, {0, 0, 0, 9, 0}, {0, 0, 0, 16, 0}); } else if (x == 1) { // Su[1] std::vector expected_values; expected_values.push_back(268435459); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 1, {0, 0, 1, 0, 0}, {0, 0, 0, 0, 0}); } else if (x == 2) { // Dec std::vector expected_values; expected_values.push_back(52776564424704); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 0, 0, 12, 0, 0, 0, 0, 12, 0, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 0, {12, 0, 0, 0, 0}, {12, 0, 0, 0, 0}); } else if (x == 3) { // Dec Su[-1] 15:00-17:00 std::vector expected_values; expected_values.push_back(11311813490642943); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 127, 12, 1, 5, 15, 0, 12, 0, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 127, {12, 1, 5, 15, 0}, {12, 0, 5, 17, 0}); } else if (x == 4) { // Dec Su[-1] Th 15:00-17:00 std::vector expected_values; expected_values.push_back(11311813490642721); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 16, 12, 1, 5, 15, 0, 12, 0, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 16, {12, 1, 5, 15, 0}, {12, 0, 5, 17, 0}); } else if (x == 5) { // Dec Su[-1] std::vector expected_values; expected_values.push_back(11311776983417087); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 127, 12, 1, 5, 0, 0, 12, 0, 5, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 127, {12, 1, 5, 0, 0}, {12, 0, 5, 0, 0}); } else if (x == 6) { // Dec Su[-1]-Mar 3 Sat std::vector expected_values; expected_values.push_back(224301728923777); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 12, 1, 5, 0, 0, 3, 3, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {12, 1, 5, 0, 0}, {3, 3, 0, 0, 0}); } else if (x == 7) { // Mar 3-Dec Su[-1] Sat std::vector expected_values; expected_values.push_back(11382144397475969); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 3, 3, 0, 0, 0, 12, 1, 5, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {3, 3, 0, 0, 0}, {12, 1, 5, 0, 0}); } else if (x == 8) { // Dec Su[-1]-Mar 3 Sat 15:00-17:00 std::vector expected_values; expected_values.push_back(224338236149633); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 12, 1, 5, 15, 0, 3, 3, 0, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {12, 1, 5, 15, 0}, {3, 3, 0, 17, 0}); } else if (x == 9) { // Mar 3-Dec Su[-1] Sat 15:00-17:00 std::vector expected_values; expected_values.push_back(11382180904701825); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 3, 3, 0, 15, 0, 12, 1, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {3, 3, 0, 15, 0}, {12, 1, 5, 17, 0}); } else if (x == 10) { // Mar 3-Dec Su[-1] Sat,PH 15:00-17:00 std::vector expected_values; expected_values.push_back(11382180904701825); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 3, 3, 0, 15, 0, 12, 1, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {3, 3, 0, 15, 0}, {12, 1, 5, 17, 0}); } else if (x == 11) { // Mar 3-Dec Su[-1] PH,Sat 15:00-17:00 std::vector expected_values; expected_values.push_back(11382180904701825); TryConditionalRestrictions(conditions.at(x), expected_values); - TryConditionalRestrictions(conditions.at(x), 0, 1, 64, 3, 3, 0, 15, 0, 12, 1, 5, 17, 0); + TryConditionalRestrictions(conditions.at(x), 0, 1, 64, {3, 3, 0, 15, 0}, {12, 1, 5, 17, 0}); } } @@ -407,13 +407,13 @@ TEST(TimeParsing, TestConditionalRestrictions) { for (uint32_t x = 0; x < conditions.size(); x++) { if (x == 0) { TryConditionalRestrictions(conditions.at(x), {4}); - TryConditionalRestrictions(conditions.at(x), 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 2, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}); } else if (x == 1) { TryConditionalRestrictions(conditions.at(x), {16}); - TryConditionalRestrictions(conditions.at(x), 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 8, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}); } else if (x == 2 || x == 3) { TryConditionalRestrictions(conditions.at(x), {64}); - TryConditionalRestrictions(conditions.at(x), 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 32, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}); } } @@ -427,8 +427,8 @@ TEST(TimeParsing, TestConditionalRestrictions) { conditions = GetTagTokens(str, ';'); for (uint32_t x = 0; x < conditions.size(); x++) { TryConditionalRestrictions(conditions.at(x), {2080911656828, 32212258172}); - TryConditionalRestrictions(conditions.at(x), 0, 0, 62, 0, 0, 0, 7, 0, 0, 0, 0, 9, 30); - TryConditionalRestrictions(conditions.at(x), 1, 0, 62, 0, 0, 0, 13, 0, 0, 0, 0, 15, 0); + TryConditionalRestrictions(conditions.at(x), 0, 0, 62, {0, 0, 0, 7, 0}, {0, 0, 0, 9, 30}); + TryConditionalRestrictions(conditions.at(x), 1, 0, 62, {0, 0, 0, 13, 0}, {0, 0, 0, 15, 0}); } // includes end of year @@ -460,6 +460,79 @@ TEST(TimeParsing, TestConditionalRestrictions) { } } +// A test case with exotic conditions extracted from `maxspeed:conditional` OSM field. +TEST(TimeParsing, TestConditionalMaxspeed) { + TryConditionalRestrictions("(19:00-06:00)", 0, 0, 0, {0, 0, 0, 19, 0}, {0, 0, 0, 6, 0}); + TryConditionalRestrictions("(06:00-18:00)", 0, 0, 0, {0, 0, 0, 6, 0}, {0, 0, 0, 18, 0}); + + std::string condition = "(07:00-09:00,13:00-16:00; SH off)"; + ASSERT_EQ(get_time_range(condition).size(), 2); + TryConditionalRestrictions(condition, 0, 0, 0, {0, 0, 0, 7, 0}, {0, 0, 0, 9, 0}); + TryConditionalRestrictions(condition, 1, 0, 0, {0, 0, 0, 13, 0}, {0, 0, 0, 16, 0}); + + TryConditionalRestrictions("Mo-Fr 19:00-07:00,Sa,Su", 0, 0, 62, {0, 0, 0, 19, 0}, {0, 0, 0, 7, 0}); + + condition = "Mo-Fr 06:00-10:00,15:00-19:00 "bij grote verkeersdrukte""; + ASSERT_EQ(get_time_range(condition).size(), 2); + TryConditionalRestrictions(condition, 0, 0, 62, {0, 0, 0, 6, 0}, {0, 0, 0, 10, 0}); + TryConditionalRestrictions(condition, 1, 0, 62, {0, 0, 0, 15, 0}, {0, 0, 0, 19, 0}); + + condition = "(Mo, We, Th, Sa 07:00-15:00)"; + ASSERT_EQ(get_time_range(condition).size(), 1); + TryConditionalRestrictions(condition, 0, 0, 0b01011010, {0, 0, 0, 7, 0}, {0, 0, 0, 15, 0}); + + condition = "(Mo-Sa 07:00-20:00,07:00-20:00; Su 00:00-24:00; PH 00:00-24:00)"; + { + const auto conditions = GetTagTokens(condition, ';'); + ASSERT_EQ(conditions.size(), 3); + + ASSERT_EQ(get_time_range(conditions[0]).size(), 2); + TryConditionalRestrictions(conditions[0], 0, 0, 126, {0, 0, 0, 7, 0}, {0, 0, 0, 20, 0}); + TryConditionalRestrictions(conditions[0], 1, 0, 126, {0, 0, 0, 7, 0}, {0, 0, 0, 20, 0}); + + ASSERT_EQ(get_time_range(conditions[1]).size(), 1); + TryConditionalRestrictions(conditions[1], 0, 0, 1, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}); + + EXPECT_EQ(get_time_range(conditions[2]).size(), 0); + } + + TryConditionalRestrictions("Jun-Aug", 0, 0, 0, {6, 0, 0, 0, 0}, {8, 0, 0, 0, 0}); + TryConditionalRestrictions("(Nov - Mar)", 0, 0, 0, {11, 0, 0, 0, 0}, {3, 0, 0, 0, 0}); + + TryConditionalRestrictions("(Apr 15-Oct 15 00:00-24:00)", 0, 0, 0, {4, 15, 0, 0, 0}, + {10, 15, 0, 0, 0}); + TryConditionalRestrictions("(Aug 01-Jun 30 06:00-18:00)", 0, 0, 0, {8, 1, 0, 6, 0}, + {6, 30, 0, 18, 0}); + TryConditionalRestrictions("(Aug 01-Jun 30 Mo-Fr 07:00-17:00; PH -1 day off; PH off)", 0, 0, 62, + {8, 1, 0, 7, 0}, {6, 30, 0, 17, 0}); + + condition = + "(Jan 01-Jun 15 Mo-Fr 07:00-18:00; PH -1 day off; PH off; Aug 15-Dec 31 Mo-Fr 00:00-24:00; PH -1 day off; PH off)"; + { + const auto conditions = GetTagTokens(condition, ';'); + ASSERT_EQ(conditions.size(), 6); + + ASSERT_EQ(get_time_range(conditions[0]).size(), 1); + TryConditionalRestrictions(conditions[0], 0, 0, 62, {1, 1, 0, 7, 0}, {6, 15, 0, 18, 0}); + + ASSERT_EQ(get_time_range(conditions[1]).size(), 0); + ASSERT_EQ(get_time_range(conditions[2]).size(), 0); + + ASSERT_EQ(get_time_range(conditions[3]).size(), 1); + TryConditionalRestrictions(conditions[3], 0, 0, 62, {8, 15, 0, 0, 0}, {12, 31, 0, 0, 0}); + + ASSERT_EQ(get_time_range(conditions[4]).size(), 0); + ASSERT_EQ(get_time_range(conditions[5]).size(), 0); + } + + TryConditionalRestrictions("(Jun 1-Aug 31 00:00-24:00)", 0, 0, 0, {6, 1, 0, 0, 0}, + {8, 31, 0, 0, 0}); + + // non-standard seasons that are not supported + EXPECT_TRUE(get_time_range("summer").empty()); + EXPECT_TRUE(get_time_range("winter").empty()); +} + int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); From 743d5c9385fe8a48d75a2afcf7b9f7eefb94979b Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Tue, 20 Aug 2024 13:56:13 -0400 Subject: [PATCH 114/198] docker uses newer ubuntu 24.04 (#4805) --- CHANGELOG.md | 5 +++-- Dockerfile | 13 +++++++------ scripts/install-linux-deps.sh | 8 +------- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bf016e236..6def9360d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,7 @@ * FIXED: motorcar:conditional should not apply to motorcycle and moped [#4359](https://github.com/valhalla/valhalla/pull/4359) * FIXED: break shortcuts when there are different restrictions on base edges [#4326](https://github.com/valhalla/valhalla/pull/4326) * FIXED: Incorrect `edge_index` assignment in `thor_worker_t::build_trace` [#4413](https://github.com/valhalla/valhalla/pull/4413) - * FIXED: lots of issues with CostMatrix (primarily deadend logic) with a complete refactor modeling things very close to bidir A*, also to prepare for a unification of the two [#4372](https://github.com/valhalla/valhalla/pull/4372) + * FIXED: lots of issues with CostMatrix (primarily deadend logic) with a complete refactor modeling things very close to bidir A\*, also to prepare for a unification of the two [#4372](https://github.com/valhalla/valhalla/pull/4372) * FIXED: diff_names check was missing for Graphfilter and Shortcutbuilder for AddEdgeInfo call. [#4436](https://github.com/valhalla/valhalla/pull/4436) * FIXED: updated timezone database and added code to keep compatibility with old servers/new data and vice versa [#4446](https://github.com/valhalla/valhalla/pull/4446) * FIXED: retry elevation tile download if the download failed for some reason or the downloaded tile was corrupt [#4461](https://github.com/valhalla/valhalla/pull/4461) @@ -54,6 +54,7 @@ * FIXED: Have the `valhalla_add_predicted_speeds` summary always be created from `mjolnir.tile_dir` [#4722](https://github.com/valhalla/valhalla/pull/4722) * FIXED: Fix inconsistency in graph.lua for motor_vehicle_node [#4723](https://github.com/valhalla/valhalla/issues/4723) * FIXED: Missing algorithm include in `baldr/admin.h` [#4766](https://github.com/valhalla/valhalla/pull/4766) + * FIXED: remove old code that allows bicycle access on hiking trails. [#4781](https://github.com/valhalla/valhalla/pull/4781) * FIXED: Handle list type arguments correctly when overriding config with valhalla_build_config [#4799](https://github.com/valhalla/valhalla/pull/4799) * FIXED: `top_speed` range not fully allowed for trucks [#4793](https://github.com/valhalla/valhalla/pull/4793) * FIXED: Trivial routes for CostMatrix [#4634](https://github.com/valhalla/valhalla/pull/4634) @@ -137,12 +138,12 @@ * ADDED: Added ssmlAnnouncements for voice instructions and removed voice and banner instructions from last step. [#4644](https://github.com/valhalla/valhalla/pull/4644) * ADDED: deadend information in directed edge JSON for `/locate` [#4751](https://github.com/valhalla/valhalla/pull/4751) * ADDED: Dedupe option for expansion, significantly reducing the response size. [#4601](https://github.com/valhalla/valhalla/issues/4601) - * FIXED: remove old code that allows bicycle access on hiking trails. [#4781](https://github.com/valhalla/valhalla/pull/4781) * ADDED: `expansion_type` property to `/expansion` [#4784](https://github.com/valhalla/valhalla/pull/4784) * ADDED: inline config arg for `valhalla_build_elevation` script [#4787](https://github.com/valhalla/valhalla/pull/4787) * ADDED: `use_truck_route` [#4809](https://github.com/valhalla/valhalla/pull/4809) * ADDED: Add option `edge.country_crossing` to trace attributes[4825](https://github.com/valhalla/valhalla/pull/4825) * CHANGED: Unification of turn costs for ramps and roundabouts[4827](https://github.com/valhalla/valhalla/pull/4827) + * CHANGED: updated dockerfile to use ubuntu 24.04 [#4805](https://github.com/valhalla/valhalla/pull/4805) ## Release Date: 2023-05-11 Valhalla 3.4.0 * **Removed** diff --git a/Dockerfile b/Dockerfile index ebcdd52605..eb5fb6c510 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,10 +8,11 @@ # binaries that can target the target architecture. from there bob is your uncle maybe.. #################################################################### -FROM ubuntu:23.04 as builder +FROM ubuntu:24.04 as builder MAINTAINER Kevin Kreiser ARG CONCURRENCY +ARG ADDITIONAL_TARGETS # set paths ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH @@ -34,7 +35,7 @@ RUN rm -rf build && mkdir build WORKDIR /usr/local/src/valhalla/build # switch back to -DCMAKE_BUILD_TYPE=RelWithDebInfo and uncomment the block below if you want debug symbols RUN cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=gcc -DENABLE_SINGLE_FILES_WERROR=Off -RUN make all -j${CONCURRENCY:-$(nproc)} +RUN make all ${ADDITIONAL_TARGETS} -j${CONCURRENCY:-$(nproc)} RUN make install # we wont leave the source around but we'll drop the commit hash we'll also keep the locales @@ -54,7 +55,7 @@ RUN rm -rf valhalla #################################################################### # copy the important stuff from the build stage to the runner image -FROM ubuntu:23.04 as runner +FROM ubuntu:24.04 as runner MAINTAINER Kevin Kreiser # basic paths @@ -67,15 +68,15 @@ LABEL org.opencontainers.image.source = "https://github.com/valhalla/valhalla" # grab the builder stages artifacts COPY --from=builder /usr/local /usr/local -COPY --from=builder /usr/lib/python3/dist-packages/valhalla/* /usr/lib/python3/dist-packages/valhalla/ +COPY --from=builder /usr/local/lib/python3.12/dist-packages/valhalla/* /usr/local/lib/python3.12/dist-packages/valhalla/ # we need to add back some runtime dependencies for binaries and scripts # install all the posix locales that we support RUN export DEBIAN_FRONTEND=noninteractive && apt update && \ apt install -y \ - libcurl4 libczmq4 libluajit-5.1-2 libgdal32 \ + libcurl4 libczmq4 libluajit-5.1-2 libgdal34 \ libprotobuf-lite32 libsqlite3-0 libsqlite3-mod-spatialite libzmq5 zlib1g \ - curl gdb locales parallel python3-minimal python3-distutils python-is-python3 \ + curl gdb locales parallel python3-minimal python-is-python3 python3-shapely python3-requests \ spatialite-bin unzip wget && rm -rf /var/lib/apt/lists/* RUN cat /usr/local/src/valhalla_locales | xargs -d '\n' -n1 locale-gen diff --git a/scripts/install-linux-deps.sh b/scripts/install-linux-deps.sh index eb2b65d16d..de81c7c215 100755 --- a/scripts/install-linux-deps.sh +++ b/scripts/install-linux-deps.sh @@ -43,6 +43,7 @@ env DEBIAN_FRONTEND=noninteractive sudo apt install --yes --quiet \ protobuf-compiler \ python3-all-dev \ python3-shapely \ + python3-requests \ python3-pip \ spatialite-bin \ unzip \ @@ -57,10 +58,3 @@ pushd $primeserver_dir make -j${CONCURRENCY:-$(nproc)} sudo make install popd && rm -rf $primeserver_dir - -# for boost and scripts deps -if [[ $(python3 -c 'import sys; print(int(sys.base_prefix != sys.prefix or hasattr(sys, "real_prefix")))') -eq 1 ]]; then - python3 -m pip install --upgrade requests shapely -else - sudo PIP_BREAK_SYSTEM_PACKAGES=1 python3 -m pip install --upgrade requests shapely -fi From c435717dc01373d9ef4413d52ad0b9b7b72acf96 Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Wed, 21 Aug 2024 13:51:59 -0400 Subject: [PATCH 115/198] release version 3.5.0 (#4860) --- valhalla/valhalla.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/valhalla/valhalla.h b/valhalla/valhalla.h index 421459ff6e..d584964915 100644 --- a/valhalla/valhalla.h +++ b/valhalla/valhalla.h @@ -1,5 +1,5 @@ #pragma once #define VALHALLA_VERSION_MAJOR 3 -#define VALHALLA_VERSION_MINOR 4 +#define VALHALLA_VERSION_MINOR 5 #define VALHALLA_VERSION_PATCH 0 From bb8c8e0f134df675ac4591f3aa18ad0bf23243a7 Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Wed, 21 Aug 2024 13:54:06 -0400 Subject: [PATCH 116/198] Update CHANGELOG.md --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6def9360d4..9448cf9fdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,15 @@ -## Unreleased +## UNRELEASED +* **Removed** +* **Bug Fix** +* **Enhancement** + +## 3.5.0 * **Removed** * REMOVED: needs_ci_run script [#4423](https://github.com/valhalla/valhalla/pull/4423) * REMOVED: unused vehicle types in AutoCost and segway; renamed kTruck to "truck" instead of "tractor_trailer" [#4430](https://github.com/valhalla/valhalla/pull/4430) * REMOVED: ./bench and related files/code [#4560](https://github.com/valhalla/valhalla/pull/4560) * REMOVED: unused headers [#4829](https://github.com/valhalla/valhalla/pull/4829) + * **Bug Fix** * FIXED: gcc13 was missing some std header includes [#4154](https://github.com/valhalla/valhalla/pull/4154) * FIXED: when reclassifying ferry edges, remove destonly from ways only if the connecting way was destonly [#4118](https://github.com/valhalla/valhalla/pull/4118) From 6c79f867fc47cded6d0e80b17f9fb7627b146301 Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Wed, 21 Aug 2024 13:54:38 -0400 Subject: [PATCH 117/198] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9448cf9fdf..a5877d7d80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ * **Bug Fix** * **Enhancement** -## 3.5.0 +## Release Date: 2024-08-21 Valhalla 3.5.0 * **Removed** * REMOVED: needs_ci_run script [#4423](https://github.com/valhalla/valhalla/pull/4423) * REMOVED: unused vehicle types in AutoCost and segway; renamed kTruck to "truck" instead of "tractor_trailer" [#4430](https://github.com/valhalla/valhalla/pull/4430) From 572c334cd21015fe75ac489cd001f79649d41d44 Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Wed, 21 Aug 2024 13:56:08 -0400 Subject: [PATCH 118/198] Update CHANGELOG.md --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5877d7d80..9ebd1fd45a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,6 @@ * REMOVED: unused vehicle types in AutoCost and segway; renamed kTruck to "truck" instead of "tractor_trailer" [#4430](https://github.com/valhalla/valhalla/pull/4430) * REMOVED: ./bench and related files/code [#4560](https://github.com/valhalla/valhalla/pull/4560) * REMOVED: unused headers [#4829](https://github.com/valhalla/valhalla/pull/4829) - * **Bug Fix** * FIXED: gcc13 was missing some std header includes [#4154](https://github.com/valhalla/valhalla/pull/4154) * FIXED: when reclassifying ferry edges, remove destonly from ways only if the connecting way was destonly [#4118](https://github.com/valhalla/valhalla/pull/4118) @@ -70,7 +69,6 @@ * FIXED: location search_filter ignored in certain cases [#4835](https://github.com/valhalla/valhalla/pull/4835) * FIXED: Ferry reclassification finds shortest path that is blocked by inaccessible node [#4854](https://github.com/valhalla/valhalla/pull/4854) * FIXED: `(Nov - Mar)` (and similar, months with spaces) condition parsing [#4857](https://github.com/valhalla/valhalla/pull/4857) - * **Enhancement** * UPDATED: French translations, thanks to @xlqian [#4159](https://github.com/valhalla/valhalla/pull/4159) * CHANGED: -j flag for multithreaded executables to override mjolnir.concurrency [#4168](https://github.com/valhalla/valhalla/pull/4168) From fdcbb2c65359e246d31f8a1a2f6778ce78ee6827 Mon Sep 17 00:00:00 2001 From: Trietes <33934221+Trietes@users.noreply.github.com> Date: Wed, 28 Aug 2024 03:39:53 +0200 Subject: [PATCH 119/198] Improve voice instructions (#4756) --- CHANGELOG.md | 1 + src/tyr/route_serializer_osrm.cc | 190 ++++++++++++++++------------- test/gurka/test_osrm_serializer.cc | 60 ++++----- 3 files changed, 132 insertions(+), 119 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ebd1fd45a..8d5da353f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * **Removed** * **Bug Fix** * **Enhancement** +* * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) ## Release Date: 2024-08-21 Valhalla 3.5.0 * **Removed** diff --git a/src/tyr/route_serializer_osrm.cc b/src/tyr/route_serializer_osrm.cc index 62f4abd7b5..7177fe42a0 100644 --- a/src/tyr/route_serializer_osrm.cc +++ b/src/tyr/route_serializer_osrm.cc @@ -11,6 +11,8 @@ #include "midgard/polyline2.h" #include "midgard/util.h" #include "odin/enhancedtrippath.h" +#include "odin/narrative_builder_factory.h" +#include "odin/narrativebuilder.h" #include "odin/util.h" #include "route_serializer_osrm.h" #include "route_summary_cache.h" @@ -96,9 +98,11 @@ const constexpr PointLL::first_type DOUGLAS_PEUCKER_THRESHOLDS[19] = { 2.6, // z18 }; -const constexpr double SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION = 15.0; -const constexpr double SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION = 5.0; +const constexpr double SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION = 35.0; +const constexpr double SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION = 10.0; +const constexpr double MIN_DISTANCE_VERBAL_PRE_TRANSITION_INSTRUCTION = 40; const constexpr double APPROXIMATE_VERBAL_POSTRANSITION_LENGTH = 110; +const constexpr double APPROXIMATE_VERBAL_PRERANSITION_SECONDS = 3.0; inline double clamp(const double lat) { return std::max(std::min(lat, double(EPSG3857_MAX_LATITUDE)), double(-EPSG3857_MAX_LATITUDE)); @@ -1477,15 +1481,34 @@ float distance_along_geometry(const valhalla::DirectionsLeg::Maneuver* prev_mane } } +void addVoiceInstruction(const std::string& instruction, + double distance_along_geometry, + json::ArrayPtr& voice_instructions) { + json::MapPtr voice_instruction = json::map({}); + voice_instruction->emplace("distanceAlongGeometry", json::fixed_t{distance_along_geometry, 1}); + voice_instruction->emplace("announcement", instruction); + voice_instruction->emplace("ssmlAnnouncement", "" + instruction + ""); + voice_instructions->emplace_back(std::move(voice_instruction)); +} + // Populate the voiceInstructions within a step. json::ArrayPtr voice_instructions(const valhalla::DirectionsLeg::Maneuver* prev_maneuver, const valhalla::DirectionsLeg::Maneuver& maneuver, const double distance, const uint32_t maneuver_index, - valhalla::odin::EnhancedTripLeg* etp) { + valhalla::odin::EnhancedTripLeg* etp, + const valhalla::Options& options) { + // narrative builder for custom pre alert instructions + // TODO: actually we should build the alert instructions with enhanced distance information during + // building the maneuver. The would require enhancing the voice instructions of the maneuver + // providing distance information and therefore a larger refactor + MarkupFormatter nullFormatter; + std::unique_ptr narrative_builder = + NarrativeBuilderFactory::Create(options, etp, nullFormatter); + // voiceInstructions is an array, because there may be similar voice instructions. // When the step is long enough, there may be multiple voice instructions. - json::ArrayPtr voice_instructions_array = json::array({}); + json::ArrayPtr voice_instructions = json::array({}); // distanceAlongGeometry is the distance along the current step from where on this // voice instruction should be played. It is measured from the end of the maneuver. @@ -1494,93 +1517,94 @@ json::ArrayPtr voice_instructions(const valhalla::DirectionsLeg::Maneuver* prev_ // shortly (10 meters at the given speed) after the maneuver has started. // The voice_instruction_beginning starts shortly after the beginning of the step. // The voice_instruction_end starts shortly before the end of the step. - float distance_before_verbal_transition_alert_instruction = -1; - float distance_before_verbal_pre_transition_instruction = -1; - if (prev_maneuver) { - distance_before_verbal_transition_alert_instruction = - distance_along_geometry(prev_maneuver, etp, distance, - SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION); + float distance_before_verbal_transition_alert_instruction = + distance_along_geometry(prev_maneuver, etp, distance, + SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION); + float distance_before_verbal_pre_transition_instruction = + distance_along_geometry(prev_maneuver, etp, distance, + SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION); + + // we want to at least have the pre transition instruction + // MIN_DISTANCE_VERBAL_PRE_TRANSITION_INSTRUCTION meters before the maneuver. So even if we are + // driving really slow we want to have the instruction few meters before the actual maneuver. + if (distance_before_verbal_pre_transition_instruction < + MIN_DISTANCE_VERBAL_PRE_TRANSITION_INSTRUCTION && + distance > MIN_DISTANCE_VERBAL_PRE_TRANSITION_INSTRUCTION) { distance_before_verbal_pre_transition_instruction = - distance_along_geometry(prev_maneuver, etp, distance, - SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION); - if (maneuver_index == 1 && !prev_maneuver->verbal_pre_transition_instruction().empty()) { - // For depart maneuver, we always want to hear the verbal_pre_transition_instruction - // right at the beginning of the navigation. This is something like: - // Drive West on XYZ Street. - // This voice_instruction_start is only created once. It is always played, even when - // the maneuver would otherwise be too short. - json::MapPtr voice_instruction_start = json::map({}); - voice_instruction_start->emplace("distanceAlongGeometry", json::fixed_t{distance, 1}); - voice_instruction_start->emplace("announcement", - prev_maneuver->verbal_pre_transition_instruction()); - voice_instruction_start->emplace("ssmlAnnouncement", - "" + - prev_maneuver->verbal_pre_transition_instruction() + - ""); - voice_instructions_array->emplace_back(std::move(voice_instruction_start)); - } else if (distance > distance_before_verbal_transition_alert_instruction + - APPROXIMATE_VERBAL_POSTRANSITION_LENGTH && - !prev_maneuver->verbal_post_transition_instruction().empty()) { - // In all other cases we want to play the verbal_post_transition_instruction shortly - // after the maneuver has started but only if there is sufficient time to play both - // the upcoming verbal_pre_transition_instruction and the verbal_post_transition_instruction - // itself. The approximation here is that the verbal_post_transition_instruction takes 100 - // meters to play + the 10 meters after the maneuver start which is added so that the - // instruction is not played directly on the intersection where the maneuver starts. - json::MapPtr voice_instruction_beginning = json::map({}); - voice_instruction_beginning->emplace("distanceAlongGeometry", json::fixed_t{distance - 10, 1}); - voice_instruction_beginning->emplace("announcement", - prev_maneuver->verbal_post_transition_instruction()); - voice_instruction_beginning->emplace("ssmlAnnouncement", - "" + - prev_maneuver->verbal_post_transition_instruction() + - ""); - voice_instructions_array->emplace_back(std::move(voice_instruction_beginning)); - } - } - - if (!maneuver.verbal_transition_alert_instruction().empty()) { - json::MapPtr voice_instruction_end = json::map({}); + MIN_DISTANCE_VERBAL_PRE_TRANSITION_INSTRUCTION; + } + // accordingly we will omit the alert instruction if it is closer than + // MIN_DISTANCE_VERBAL_PRE_TRANSITION_INSTRUCTION meters to the maneuver. + if (distance_before_verbal_transition_alert_instruction < + MIN_DISTANCE_VERBAL_PRE_TRANSITION_INSTRUCTION) { + // using -1 here to state that they don't have to be played + distance_before_verbal_transition_alert_instruction = -1; + } + + if (maneuver_index == 1 && !prev_maneuver->verbal_pre_transition_instruction().empty()) { + // For depart maneuver, we always want to hear the verbal_pre_transition_instruction + // right at the beginning of the navigation. This is something like: + // Drive West on XYZ Street. + // This voice_instruction_start is only created once. It is always played, even when + // the maneuver would otherwise be too short. + addVoiceInstruction(prev_maneuver->verbal_pre_transition_instruction(), double(distance), + voice_instructions); + } else if (distance_before_verbal_transition_alert_instruction >= 0.0 && + distance > distance_before_verbal_transition_alert_instruction + + APPROXIMATE_VERBAL_POSTRANSITION_LENGTH && + !prev_maneuver->verbal_post_transition_instruction().empty()) { + // In all other cases we want to play the verbal_post_transition_instruction shortly + // after the maneuver has started but only if there is sufficient time to play. On + // the one hand distance_before_verbal_transition_alert_instruction has to be set, + // so there is enough time to play the transition instruction afterwards. On the other hand + // there has to be enough time to play the upcoming verbal_pre_transition_instruction and + // the verbal_post_transition_instruction itself. + // The approximation here is that the verbal_post_transition_instruction takes 100 + // meters to play + the 10 meters after the maneuver start which is added so that the + // instruction is not played directly on the intersection where the maneuver starts. + addVoiceInstruction(prev_maneuver->verbal_post_transition_instruction(), double(distance - 10), + voice_instructions); + } + + // If there is an alert instruction and we have enough time to play it, we will play it + // TODO: We shouldn't add an alert instruction if there was already a pre/post transition alert + // which is close by + if (!maneuver.verbal_transition_alert_instruction().empty() && + distance_before_verbal_transition_alert_instruction >= 0.0 && + prev_maneuver->time() > SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION + + APPROXIMATE_VERBAL_PRERANSITION_SECONDS) { if (maneuver_index == 1 && distance_before_verbal_transition_alert_instruction == distance) { // For the depart maneuver we want to play both the verbal_post_transition_instruction and // the verbal_transition_alert_instruction even if the maneuver is too short. - voice_instruction_end->emplace("distanceAlongGeometry", json::fixed_t{distance / 2, 1}); - } else { // In all other cases we use distance_before_verbal_transition_alert_instruction value - // as it is capped to the maneuver length - voice_instruction_end - ->emplace("distanceAlongGeometry", - json::fixed_t{distance_before_verbal_transition_alert_instruction, 1}); + // as it is capped to the maneuver length. + distance_before_verbal_transition_alert_instruction = distance / 2; } - voice_instruction_end->emplace("announcement", maneuver.verbal_transition_alert_instruction()); - voice_instruction_end->emplace("ssmlAnnouncement", - "" + maneuver.verbal_transition_alert_instruction() + - ""); - voice_instructions_array->emplace_back(std::move(voice_instruction_end)); - } - + // building voice instructions for the alert. We are enhancing it by the distance information here + // by using the narrative builder + float distance_km = (float)distance_before_verbal_transition_alert_instruction / 1000.0f; + std::string instruction = + narrative_builder + ->FormVerbalAlertApproachInstruction(distance_km, + maneuver.verbal_transition_alert_instruction()); + addVoiceInstruction(instruction, distance_before_verbal_transition_alert_instruction, + voice_instructions); + } + + // add pre transition instruction if available if (!maneuver.verbal_pre_transition_instruction().empty()) { - json::MapPtr voice_instruction_end = json::map({}); if (maneuver_index == 1 && distance_before_verbal_pre_transition_instruction >= distance / 2) { - // For the depart maneuver we want to play the verbal_post_transition_instruction, - // the verbal_transition_alert_instruction and + // For the depart maneuver we want to play the verbal_post_transition_instruction and // the verbal_pre_transition_instruction even if the maneuver is too short. - voice_instruction_end->emplace("distanceAlongGeometry", json::fixed_t{distance / 4, 1}); - } else { - // In all other cases we use distance_before_verbal_pre_transition_instruction value - // as it is capped to the maneuver length - voice_instruction_end->emplace("distanceAlongGeometry", - json::fixed_t{distance_before_verbal_pre_transition_instruction, - 1}); + // In all other cases we use distance_before_verbal_pre_transition_instruction value as is + // because it is capped to the maneuver length. + distance_before_verbal_pre_transition_instruction = distance / 4; } - voice_instruction_end->emplace("announcement", maneuver.verbal_pre_transition_instruction()); - voice_instruction_end->emplace("ssmlAnnouncement", - "" + maneuver.verbal_pre_transition_instruction() + - ""); - voice_instructions_array->emplace_back(std::move(voice_instruction_end)); + addVoiceInstruction(maneuver.verbal_pre_transition_instruction(), + distance_before_verbal_pre_transition_instruction, voice_instructions); } - return voice_instructions_array; + return voice_instructions; } // Get the mode @@ -1846,7 +1870,7 @@ json::ArrayPtr serialize_legs(const google::protobuf::RepeatedPtrFieldemplace("bannerInstructions", banner_instructions(name, dest, ref, prev_maneuver, maneuver, arrive_maneuver, &etp, mnvr_type, modifier, ex, @@ -1860,10 +1884,10 @@ json::ArrayPtr serialize_legs(const google::protobuf::RepeatedPtrFieldemplace("voiceInstructions", voice_instructions(prev_maneuver, maneuver, prev_distance, - maneuver_index, &etp)); + maneuver_index, &etp, options)); } if (arrive_maneuver) { // just add empty array for arrival maneuver @@ -2454,4 +2478,4 @@ int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } -#endif +#endif \ No newline at end of file diff --git a/test/gurka/test_osrm_serializer.cc b/test/gurka/test_osrm_serializer.cc index 8313fde980..d59ea87d55 100644 --- a/test/gurka/test_osrm_serializer.cc +++ b/test/gurka/test_osrm_serializer.cc @@ -606,15 +606,18 @@ TEST_F(VoiceInstructions, VoiceInstructionsPresent) { // // CD: 50m / 30km/h = 50m * 3,600s / 30,000m = 50m * 0.12s/m = 6s // BC: 50m / 50km/h = 50m * 3,600s / 50,000m = 50m * 0.072s/m = 3.6s -// SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION = 15s -// AB: 15s - 6s - 3.6s = 5.4s -// 5.4s * 80 km/h = 5.4s * 80,000m / 3600s = 120m -// => distanceAlongGeometry = 120m + 50m + 50m = 220m +// SECONDS_BEFORE_VERBAL_TRANSITION_ALERT_INSTRUCTION = 35s +// AB: 35s - 6s - 3.6s = 25.4s +// 25.4s * 80 km/h = 25.4s * 80,000m / 3600s ~= 564m +// => distanceAlongGeometry = 564,45m + 50m + 50m = 664m +// => larger then depart_instruction/the maneuver -> won't be played // // verbal_pre_transition_instruction // -// SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION = 5s -// CD: 5s * 30km/h = 5s * 30,000m / 3600s ~= 42m +// SECONDS_BEFORE_VERBAL_PRE_TRANSITION_INSTRUCTION = 10s +// AB: 10s - 6s - 3.6s = 0.4s +// 0.4s * 80 km/h = 0.4s * 80,000m / 3600s ~= 9m +// => distanceAlongGeometry = 9m + 50m + 50m = 109m TEST_F(VoiceInstructions, DistanceAlongGeometryVoiceInstructions) { auto json = json_request("A", "D"); auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); @@ -624,21 +627,17 @@ TEST_F(VoiceInstructions, DistanceAlongGeometryVoiceInstructions) { depart_instruction["announcement"].GetString(), "Drive east on 10th Avenue SE. Then, in 700 meters, You will arrive at your destination."); EXPECT_EQ(depart_instruction["distanceAlongGeometry"].GetFloat(), 650.0); - auto verbal_transition_alert_instruction = steps[0]["voiceInstructions"][1].GetObject(); - EXPECT_STREQ(verbal_transition_alert_instruction["announcement"].GetString(), - "You will arrive at your destination."); - EXPECT_EQ(round(verbal_transition_alert_instruction["distanceAlongGeometry"].GetFloat()), 220); - auto verbal_pre_transition_instruction = steps[0]["voiceInstructions"][2].GetObject(); + auto verbal_pre_transition_instruction = steps[0]["voiceInstructions"][1].GetObject(); EXPECT_STREQ(verbal_pre_transition_instruction["announcement"].GetString(), "You have arrived at your destination."); - EXPECT_EQ(round(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat()), 42); + EXPECT_EQ(round(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat()), 109); } TEST_F(VoiceInstructions, ShortDepartVoiceInstructions) { auto json = json_request("C", "F"); auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); - EXPECT_EQ(steps[0]["voiceInstructions"].Size(), 3); + EXPECT_EQ(steps[0]["voiceInstructions"].Size(), 2); auto depart_instruction = steps[0]["voiceInstructions"][0].GetObject(); EXPECT_STREQ(depart_instruction["announcement"].GetString(), @@ -647,30 +646,19 @@ TEST_F(VoiceInstructions, ShortDepartVoiceInstructions) { auto verbal_transition_alert_instruction = steps[0]["voiceInstructions"][1].GetObject(); EXPECT_STREQ(verbal_transition_alert_instruction["announcement"].GetString(), "Bear right onto Alfred Street."); - EXPECT_EQ(verbal_transition_alert_instruction["distanceAlongGeometry"].GetFloat(), 25.0); - auto verbal_pre_transition_instruction = steps[0]["voiceInstructions"][2].GetObject(); - EXPECT_STREQ(verbal_pre_transition_instruction["announcement"].GetString(), - "Bear right onto Alfred Street."); - EXPECT_EQ(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat(), 12.5); + EXPECT_EQ(verbal_transition_alert_instruction["distanceAlongGeometry"].GetFloat(), 12.5); } TEST_F(VoiceInstructions, ShortIntermediateStepVoiceInstructions) { auto json = json_request("X", "Z"); auto steps = json["routes"][0]["legs"][0]["steps"].GetArray(); - EXPECT_EQ(steps[1]["voiceInstructions"].Size(), 2); // No verbal_post_transition_instruction - - auto verbal_transition_alert_instruction = steps[1]["voiceInstructions"][0].GetObject(); - EXPECT_STREQ(verbal_transition_alert_instruction["announcement"].GetString(), - "Turn right onto Market Street."); - EXPECT_EQ(verbal_transition_alert_instruction["distanceAlongGeometry"].GetFloat(), 50.0); + EXPECT_EQ(steps[1]["voiceInstructions"].Size(), 1); // just pre tansition instruction - auto verbal_pre_transition_instruction = steps[1]["voiceInstructions"][1].GetObject(); + auto verbal_pre_transition_instruction = steps[1]["voiceInstructions"][0].GetObject(); EXPECT_STREQ(verbal_pre_transition_instruction["announcement"].GetString(), "Turn right onto Market Street. Then You will arrive at your destination."); - // ~= 38.2 - EXPECT_GT(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat(), 38); - EXPECT_LT(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat(), 39); + EXPECT_EQ(verbal_pre_transition_instruction["distanceAlongGeometry"].GetFloat(), 50.0); } TEST_F(VoiceInstructions, AllVoiceInstructions) { @@ -684,7 +672,7 @@ TEST_F(VoiceInstructions, AllVoiceInstructions) { auto bear_right_instruction = steps[0]["voiceInstructions"][1].GetObject(); EXPECT_STREQ(bear_right_instruction["announcement"].GetString(), "Bear right onto Alfred Street."); - EXPECT_EQ(round(bear_right_instruction["distanceAlongGeometry"].GetFloat()), 220); + EXPECT_EQ(round(bear_right_instruction["distanceAlongGeometry"].GetFloat()), 109); auto continue_instruction = steps[1]["voiceInstructions"][0].GetObject(); EXPECT_STREQ(continue_instruction["announcement"].GetString(), "Continue for 900 meters."); @@ -692,17 +680,17 @@ TEST_F(VoiceInstructions, AllVoiceInstructions) { auto arrive_instruction = steps[1]["voiceInstructions"][1].GetObject(); EXPECT_STREQ(arrive_instruction["announcement"].GetString(), - "You will arrive at your destination."); - // ~= 125 - EXPECT_GT(arrive_instruction["distanceAlongGeometry"].GetFloat(), 124); - EXPECT_LT(arrive_instruction["distanceAlongGeometry"].GetFloat(), 126); + "In 300 meters, You will arrive at your destination."); + // ~= 291.6 + EXPECT_GT(arrive_instruction["distanceAlongGeometry"].GetFloat(), 291); + EXPECT_LT(arrive_instruction["distanceAlongGeometry"].GetFloat(), 292); auto final_arrive_instruction = steps[1]["voiceInstructions"][2].GetObject(); EXPECT_STREQ(final_arrive_instruction["announcement"].GetString(), "You have arrived at your destination."); - // ~= 42 - EXPECT_GT(final_arrive_instruction["distanceAlongGeometry"].GetFloat(), 41); - EXPECT_LT(final_arrive_instruction["distanceAlongGeometry"].GetFloat(), 43); + // ~= 83.3 + EXPECT_GT(final_arrive_instruction["distanceAlongGeometry"].GetFloat(), 83); + EXPECT_LT(final_arrive_instruction["distanceAlongGeometry"].GetFloat(), 84); auto last_instruction = steps[2]["voiceInstructions"].GetArray(); EXPECT_EQ(last_instruction.Size(), 0); From e39ce9c9abe32cd057c00a348798138ad0215a5f Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Tue, 27 Aug 2024 21:46:17 -0400 Subject: [PATCH 120/198] lint --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d5da353f0..2f7cac4dd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ * **Removed** * **Bug Fix** * **Enhancement** -* * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) + * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) ## Release Date: 2024-08-21 Valhalla 3.5.0 * **Removed** From 5f4f4e965f76254d1c30dc920e94d5a7abb92b2a Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 29 Aug 2024 14:30:04 +0200 Subject: [PATCH 121/198] `clear_reserved_memory_` uninitialized in TD Matrix (#4869) --- valhalla/thor/timedistancematrix.h | 1 - 1 file changed, 1 deletion(-) diff --git a/valhalla/thor/timedistancematrix.h b/valhalla/thor/timedistancematrix.h index 8d41894b76..c93c6213d8 100644 --- a/valhalla/thor/timedistancematrix.h +++ b/valhalla/thor/timedistancematrix.h @@ -94,7 +94,6 @@ class TimeDistanceMatrix : public MatrixAlgorithm { float current_cost_threshold_; uint32_t max_reserved_labels_count_; - bool clear_reserved_memory_; // List of destinations std::vector destinations_; From 53541079d643d28c2f7b2e9b821affc1ff7f132f Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 29 Aug 2024 14:31:20 +0200 Subject: [PATCH 122/198] Add docstring for preferred_layer location parameter (#4867) --- docs/docs/api/turn-by-turn/api-reference.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index c00e646f67..8ea2d1e79a 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -46,7 +46,8 @@ To build a route, you need to specify two `break` locations. In addition, you ca | `street_side_tolerance` | If your input coordinate is less than this tolerance away from the edge centerline then we set your side of street to none otherwise your side of street will be left or right depending on direction of travel. The default is 5 meters. | | `street_side_max_distance` | The max distance in meters that the input coordinates or display ll can be from the edge centerline for them to be used for determining the side of street. Beyond this distance the side of street is set to none. The default is 1000 meters. | | `street_side_cutoff` | Disables the `preferred_side` when set to `same` or `opposite` if the edge has a road class less than that provided by `street_side_cutoff`. The road class must be one of the following strings: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. The default value is `service_other` so that `preferred_side` will not be disabled for any edges. | -| `search_filter` | A set of optional filters to exclude candidate edges based on their attribution. The following exclusion filters are supported:
  • `exclude_tunnel` (boolean, defaults to `false`): whether to exclude roads marked as tunnels
  • `exclude_bridge` (boolean, defaults to `false`): whether to exclude roads marked as bridges
  • `exclude_ramp` (boolean, defaults to `false`): whether to exclude link roads marked as ramps, note that some turn channels are also marked as ramps
  • `exclude_closures` (boolean, defaults to `true`): whether to exclude roads considered closed due to live traffic closure. **Note:** This option cannot be set if `costing_options..ignore_closures` is also specified. An error is returned if both options are specified. **Note 2:** Ignoring closures at destination and source locations does NOT work for date_time type `0/1` & `2` respectively
  • `min_road_class` (string, defaults to `"service_other"`): lowest road class allowed
  • `max_road_class` (string, defaults to `"motorway"`): highest road class allowed
Road classes from highest to lowest are: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. +| `search_filter` | A set of optional filters to exclude candidate edges based on their attribution. The following exclusion filters are supported:
  • `exclude_tunnel` (boolean, defaults to `false`): whether to exclude roads marked as tunnels
  • `exclude_bridge` (boolean, defaults to `false`): whether to exclude roads marked as bridges
  • `exclude_ramp` (boolean, defaults to `false`): whether to exclude link roads marked as ramps, note that some turn channels are also marked as ramps
  • `exclude_closures` (boolean, defaults to `true`): whether to exclude roads considered closed due to live traffic closure. **Note:** This option cannot be set if `costing_options..ignore_closures` is also specified. An error is returned if both options are specified. **Note 2:** Ignoring closures at destination and source locations does NOT work for date_time type `0/1` & `2` respectively
  • `min_road_class` (string, defaults to `"service_other"`): lowest road class allowed
  • `max_road_class` (string, defaults to `"motorway"`): highest road class allowed
Road classes from highest to lowest are: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. | +| `preferred_layer` | The layer on which edges should be considered. If provided, edges whose layer does not match the provided value will be discarded from the candidate search. | Optionally, you can include the following location information without impacting the routing. This information is carried through the request and returned as a convenience. From aba45e14f1d8ee59221bf1f51b2bdb4426e07009 Mon Sep 17 00:00:00 2001 From: Johannes Nonnenmacher <146193501+johannes-no@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:27:58 +0200 Subject: [PATCH 123/198] Enable edge.forward to be traced (#4876) --- CHANGELOG.md | 39 +++++++++++---------- docs/docs/api/map-matching/api-reference.md | 4 ++- proto/trip.proto | 1 + src/baldr/attributes_controller.cc | 1 + src/thor/triplegbuilder.cc | 5 +++ src/tyr/trace_serializer.cc | 3 ++ valhalla/baldr/attributes_controller.h | 1 + 7 files changed, 34 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f7cac4dd5..16e8c269ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * **Bug Fix** * **Enhancement** * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) + * ADDED: Add option `edge.forward` to trace attributes [#4876](https://github.com/valhalla/valhalla/pull/4876) ## Release Date: 2024-08-21 Valhalla 3.5.0 * **Removed** @@ -146,8 +147,8 @@ * ADDED: `expansion_type` property to `/expansion` [#4784](https://github.com/valhalla/valhalla/pull/4784) * ADDED: inline config arg for `valhalla_build_elevation` script [#4787](https://github.com/valhalla/valhalla/pull/4787) * ADDED: `use_truck_route` [#4809](https://github.com/valhalla/valhalla/pull/4809) - * ADDED: Add option `edge.country_crossing` to trace attributes[4825](https://github.com/valhalla/valhalla/pull/4825) - * CHANGED: Unification of turn costs for ramps and roundabouts[4827](https://github.com/valhalla/valhalla/pull/4827) + * ADDED: Add option `edge.country_crossing` to trace attributes [#4825](https://github.com/valhalla/valhalla/pull/4825) + * CHANGED: Unification of turn costs for ramps and roundabouts [#4827](https://github.com/valhalla/valhalla/pull/4827) * CHANGED: updated dockerfile to use ubuntu 24.04 [#4805](https://github.com/valhalla/valhalla/pull/4805) ## Release Date: 2023-05-11 Valhalla 3.4.0 @@ -196,7 +197,7 @@ * ADDED: Support for configuring a universal request timeout [#3966](https://github.com/valhalla/valhalla/pull/3966) * ADDED: optionally include highway=platform edges for pedestrian access [#3971](https://github.com/valhalla/valhalla/pull/3971) * ADDED: `use_lit` costing option for pedestrian costing [#3957](https://github.com/valhalla/valhalla/pull/3957) - * CHANGED: Removed stray NULL values in log output[#3974](https://github.com/valhalla/valhalla/pull/3974) + * CHANGED: Removed stray NULL values in log output [#3974](https://github.com/valhalla/valhalla/pull/3974) * CHANGED: More conservative estimates for cost of walking slopes [#3982](https://github.com/valhalla/valhalla/pull/3982) * ADDED: An option to slim down matrix response [#3987](https://github.com/valhalla/valhalla/pull/3987) * CHANGED: Updated url for just_gtfs library [#3994](https://github.com/valhalla/valhalla/pull/3995) @@ -219,7 +220,7 @@ ## Release Date: 2023-01-03 Valhalla 3.2.1 * **Removed** * **Bug Fix** - * FIXED: valhalla_run_route was missing config logic.[#3824](https://github.com/valhalla/valhalla/pull/3824) + * FIXED: valhalla_run_route was missing config logic. [#3824](https://github.com/valhalla/valhalla/pull/3824) * FIXED: Added missing ferry tag if manoeuver uses a ferry. It's supposed to be there according to the docs. [#3815](https://github.com/valhalla/valhalla/issues/3815) * FIXED: Handle hexlifying strings with unsigned chars [#3842](https://github.com/valhalla/valhalla/pull/3842) * FIXED: Newer clang warns on `sprintf` which becomes a compilation error (due to `Werror`) so we use `snprintf` instead [#3846](https://github.com/valhalla/valhalla/issues/3846) @@ -252,8 +253,8 @@ * **Bug Fix** * FIXED: Fix precision losses while encoding-decoding distance parameter in openlr [#3374](https://github.com/valhalla/valhalla/pull/3374) * FIXED: Fix bearing calculation for openlr records [#3379](https://github.com/valhalla/valhalla/pull/3379) - * FIXED: Some refactoring that was proposed for the PR 3379 [3381](https://github.com/valhalla/valhalla/pull/3381) - * FIXED: Avoid calling out "keep left/right" when passing an exit [3349](https://github.com/valhalla/valhalla/pull/3349) + * FIXED: Some refactoring that was proposed for the PR 3379 [#3381](https://github.com/valhalla/valhalla/pull/3381) + * FIXED: Avoid calling out "keep left/right" when passing an exit [#3349](https://github.com/valhalla/valhalla/pull/3349) * FIXED: Fix iterator decrement beyond begin() in GeoPoint::HeadingAtEndOfPolyline() method [#3393](https://github.com/valhalla/valhalla/pull/3393) * FIXED: Add string for Use:kPedestrianCrossing to fix null output in to_string(Use). [#3416](https://github.com/valhalla/valhalla/pull/3416) * FIXED: Remove simple restrictions check for pedestrian cost calculation. [#3423](https://github.com/valhalla/valhalla/pull/3423) @@ -289,7 +290,7 @@ * FIXED: Removed duplicate degrees/radians constants [#3642](https://github.com/valhalla/valhalla/pull/3642) * FIXED: Forgot to adapt driving side and country access rules in [#3619](https://github.com/valhalla/valhalla/pull/3619) [#3652](https://github.com/valhalla/valhalla/pull/3652) * FIXED: DateTime::is_conditional_active(...) incorrect end week handling [#3655](https://github.com/valhalla/valhalla/pull/3655) - * FIXED: TimeDistanceBSSMatrix: incorrect initialization for destinations[#3659](https://github.com/valhalla/valhalla/pull/3659) + * FIXED: TimeDistanceBSSMatrix: incorrect initialization for destinations [#3659](https://github.com/valhalla/valhalla/pull/3659) * FIXED: Some interpolated points had invalid edge_index in trace_attributes response [#3646](https://github.com/valhalla/valhalla/pull/3670) * FIXED: Use a small node snap distance in map-matching. FIxes issue with incorrect turn followed by Uturn. [#3677](https://github.com/valhalla/valhalla/pull/3677) * FIXED: Conan error when building Docker image. [#3689](https://github.com/valhalla/valhalla/pull/3689) @@ -530,7 +531,7 @@ * FIXED: Store restrictions in the right tile [#2781](https://github.com/valhalla/valhalla/pull/2781) * FIXED: Failing to write tiles because of racing directory creation [#2810](https://github.com/valhalla/valhalla/pull/2810) * FIXED: Regression in stopping expansion on transitions down in time-dependent routes [#2815](https://github.com/valhalla/valhalla/pull/2815) - * FIXED: Fix crash in loki when trace_route is called with 2 locations.[#2817](https://github.com/valhalla/valhalla/pull/2817) + * FIXED: Fix crash in loki when trace_route is called with 2 locations. [#2817](https://github.com/valhalla/valhalla/pull/2817) * FIXED: Mark the restriction start and end as via ways to fix IsBridgingEdge function in Bidirectional Astar [#2796](https://github.com/valhalla/valhalla/pull/2796) * FIXED: Dont add predictive traffic to the tile if it's empty [#2826](https://github.com/valhalla/valhalla/pull/2826) * FIXED: Fix logic bidirectional astar to avoid double u-turns and extra detours [#2802](https://github.com/valhalla/valhalla/pull/2802) @@ -683,12 +684,12 @@ * FIXED: Checks protobuf serialization/parsing success [#2477](https://github.com/valhalla/valhalla/pull/2477) * FIXED: Fix dereferencing of end for std::lower_bound in sequence and possible UB [#2488](https://github.com/valhalla/valhalla/pull/2488) * FIXED: Make tile building reproducible: fix UB-s [#2480](https://github.com/valhalla/valhalla/pull/2480) - * FIXED: Zero initialize EdgeInfoInner.spare0_. Uninitialized spare0_ field produced UB which causes gurka_reproduce_tile_build to fail intermittently. [2499](https://github.com/valhalla/valhalla/pull/2499) + * FIXED: Zero initialize EdgeInfoInner.spare0_. Uninitialized spare0_ field produced UB which causes gurka_reproduce_tile_build to fail intermittently. [#2499](https://github.com/valhalla/valhalla/pull/2499) * FIXED: Drop unused CHANGELOG validation script, straggling NodeJS references [#2506](https://github.com/valhalla/valhalla/pull/2506) * FIXED: Fix missing nullptr checks in graphreader and loki::Reach (causing segfault during routing with not all levels of tiles available) [#2504](https://github.com/valhalla/valhalla/pull/2504) * FIXED: Fix mismatch of triplegedge roadclass and directededge roadclass [#2507](https://github.com/valhalla/valhalla/pull/2507) * FIXED: Improve german destination_verbal_alert phrases [#2509](https://github.com/valhalla/valhalla/pull/2509) - * FIXED: Undefined behavior cases discovered with undefined behavior sanitizer tool. [2498](https://github.com/valhalla/valhalla/pull/2498) + * FIXED: Undefined behavior cases discovered with undefined behavior sanitizer tool. [#2498](https://github.com/valhalla/valhalla/pull/2498) * FIXED: Fixed logic so verbal keep instructions use branch exit sign info for ramps [#2520](https://github.com/valhalla/valhalla/pull/2520) * FIXED: Fix bug in trace_route for uturns causing garbage coordinates [#2517](https://github.com/valhalla/valhalla/pull/2517) * FIXED: Simplify heading calculation for turn type. Remove undefined behavior case. [#2513](https://github.com/valhalla/valhalla/pull/2513) @@ -716,7 +717,7 @@ * FIXED: Improve German post_transition_verbal instruction [#2677](https://github.com/valhalla/valhalla/pull/2677) * FIXED: Lane updates. Add the turn lanes to all edges of the way. Do not "enhance" turn lanes if they are part of a complex restriction. Moved ProcessTurnLanes after UpdateManeuverPlacementForInternalIntersectionTurns. Fix for a missing "uturn" indication for intersections on the previous maneuver, we were serializing an empty list. [#2679](https://github.com/valhalla/valhalla/pull/2679) * FIXED: Fixes OpenLr serialization [#2688](https://github.com/valhalla/valhalla/pull/2688) - * FIXED: Internal edges can't be also a ramp or a turn channel. Also, if an edge is marked as ramp and turn channel mark it as a ramp. [2689](https://github.com/valhalla/valhalla/pull/2689) + * FIXED: Internal edges can't be also a ramp or a turn channel. Also, if an edge is marked as ramp and turn channel mark it as a ramp. [#2689](https://github.com/valhalla/valhalla/pull/2689) * FIXED: Check that speeds are equal for the edges going in the same direction while buildig shortcuts [#2691](https://github.com/valhalla/valhalla/pull/2691) * FIXED: Missing fork or bear instruction [#2683](https://github.com/valhalla/valhalla/pull/2683) * FIXED: Eliminate null pointer dereference in GraphReader::AreEdgesConnected [#2695](https://github.com/valhalla/valhalla/issues/2695) @@ -822,7 +823,7 @@ * ADDED: Cross-compilation ability with MinGW64 [#2619](https://github.com/valhalla/valhalla/pull/2619) * ADDED: Defines the incident tile schema and incident metadata [#2620](https://github.com/valhalla/valhalla/pull/2620) * ADDED: Moves incident serializer logic into a generic serializer [#2621](https://github.com/valhalla/valhalla/pull/2621) - * ADDED: Incident loading singleton for continually refreshing incident tiles[#2573](https://github.com/valhalla/valhalla/pull/2573) + * ADDED: Incident loading singleton for continually refreshing incident tiles [#2573](https://github.com/valhalla/valhalla/pull/2573) * ADDED: One shot mode to valhalla_service so you can run a single request of any type without starting a server [#2624](https://github.com/valhalla/valhalla/pull/2624) * ADDED: Adds text instructions to OSRM output [#2625](https://github.com/valhalla/valhalla/pull/2625) * ADDED: Adds support for alternate routes [#2626](https://github.com/valhalla/valhalla/pull/2626) @@ -836,7 +837,7 @@ * ADDED: Add annotations to osrm response including speed limits, unit and sign conventions [#2668](https://github.com/valhalla/valhalla/pull/2668) * ADDED: Added functions for predicted speeds encoding-decoding [#2674](https://github.com/valhalla/valhalla/pull/2674) * ADDED: Time invariant routing via the bidirectional algorithm. This has the effect that when time dependent routes (arrive_by and depart_at) fall back to bidirectional due to length restrictions they will actually use the correct time of day for one of the search directions [#2660](https://github.com/valhalla/valhalla/pull/2660) - * ADDED: If the length of the edge is greater than kMaxEdgeLength, then consider this a catastrophic error if the should_error bool is true in the set_length function. [2678](https://github.com/valhalla/valhalla/pull/2678) + * ADDED: If the length of the edge is greater than kMaxEdgeLength, then consider this a catastrophic error if the should_error bool is true in the set_length function. [#2678](https://github.com/valhalla/valhalla/pull/2678) * ADDED: Moved lat,lon coordinates structures from single to double precision. Improves geometry accuracy noticibly at zooms above 17 as well as coordinate snapping and any other geometric operations. Adds about a 2% performance penalty for standard routes. Graph nodes now have 7 digits of precision. [#2693](https://github.com/valhalla/valhalla/pull/2693) * ADDED: Added signboards to guidance views. [#2687](https://github.com/valhalla/valhalla/pull/2687) * ADDED: Regular speed on shortcut edges is calculated with turn durations taken into account. Truck, motorcycle and motorscooter profiles use OSRM-like turn duration. [#2662](https://github.com/valhalla/valhalla/pull/2662) @@ -973,7 +974,7 @@ * FIXED: Fix pedestrian routes using walkway_factor [#1780](https://github.com/valhalla/valhalla/pull/1780) * FIXED: Update the begin and end heading of short edges based on use [#1783](https://github.com/valhalla/valhalla/pull/1783) * FIXED: GraphReader::AreEdgesConnected update. If transition count == 0 return false and do not call transition function. [#1786](https://github.com/valhalla/valhalla/pull/1786) - * FIXED: Only edge candidates that were used in the path are send to serializer: [1788](https://github.com/valhalla/valhalla/pull/1788) + * FIXED: Only edge candidates that were used in the path are send to serializer: [#1788](https://github.com/valhalla/valhalla/pull/1788) * FIXED: Added logic to prevent the removal of a destination maneuver when ending on an internal edge [#1792](https://github.com/valhalla/valhalla/pull/1792) * FIXED: Fixed instructions when starting on an internal edge [#1796](https://github.com/valhalla/valhalla/pull/1796) @@ -1050,10 +1051,10 @@ ## Release Date: 2018-09-13 Valhalla 2.7.0 * **Enhancement** - * UPDATED: Refactor to use the pbf options instead of the ptree config [#1428](https://github.com/valhalla/valhalla/pull/1428) This completes [1357](https://github.com/valhalla/valhalla/issues/1357) + * UPDATED: Refactor to use the pbf options instead of the ptree config [#1428](https://github.com/valhalla/valhalla/pull/1428) This completes [#1357](https://github.com/valhalla/valhalla/issues/1357) * UPDATED: Removed the boost/date_time dependency from baldr and odin. We added the Howard Hinnant date and time library as a submodule. [#1494](https://github.com/valhalla/valhalla/pull/1494) - * UPDATED: Fixed 'Drvie' typo [#1505](https://github.com/valhalla/valhalla/pull/1505) This completes [1504](https://github.com/valhalla/valhalla/issues/1504) - * UPDATED: Optimizations of GetSpeed for predicted speeds [1490](https://github.com/valhalla/valhalla/issues/1490) + * UPDATED: Fixed 'Drvie' typo [#1505](https://github.com/valhalla/valhalla/pull/1505) This completes [#1504](https://github.com/valhalla/valhalla/issues/1504) + * UPDATED: Optimizations of GetSpeed for predicted speeds [#1490](https://github.com/valhalla/valhalla/issues/1490) * UPDATED: Isotile optimizations * UPDATED: Added stats to predictive traffic logging * UPDATED: resample_polyline - Breaks the polyline into equal length segments at a sample distance near the resolution. Break out of the loop through polyline points once we reach the specified number of samplesthen append the last @@ -1102,8 +1103,8 @@ polyline point. * FIXED: Fixed trace_route edge_walk server abort [#1365](https://github.com/valhalla/valhalla/pull/1365) * **Enhancement** * ADDED: Added post process for updating free and constrained speeds in the directed edges. - * UPDATED: Parse the json request once and store in a protocol buffer to pass along the pipeline. This completed the first portion of [1357](https://github.com/valhalla/valhalla/issues/1357) - * UPDATED: Changed the shape_match attribute from a string to an enum. Fixes [1376](https://github.com/valhalla/valhalla/issues/1376) + * UPDATED: Parse the json request once and store in a protocol buffer to pass along the pipeline. This completed the first portion of [#1357](https://github.com/valhalla/valhalla/issues/1357) + * UPDATED: Changed the shape_match attribute from a string to an enum. Fixes [#1376](https://github.com/valhalla/valhalla/issues/1376) * ADDED: Node bindings for route [#1341](https://github.com/valhalla/valhalla/pull/1341) * UPDATED: Use a non-linear use_highways factor (to more heavily penalize highways as use_highways approaches 0). diff --git a/docs/docs/api/map-matching/api-reference.md b/docs/docs/api/map-matching/api-reference.md index e3dbf34037..2f24595956 100644 --- a/docs/docs/api/map-matching/api-reference.md +++ b/docs/docs/api/map-matching/api-reference.md @@ -116,6 +116,7 @@ edge.speed_limit edge.truck_speed edge.truck_route edge.country_crossing +edge.forward // Node filter keys node.intersecting_edge.begin_heading @@ -220,7 +221,8 @@ Each `edge` may include: | `truck_route` | True if edge is part of a truck network/route. | | `end_node` | The node at the end of this edge. See the list of [end node items](#end-node-items) for details. | | `landmarks` | List of landmarks along the edge. They are used as direction support in navigation. | | -| `country_crossing` | True if the edge is a country crossing. | +| `country_crossing` | True if the edge is a country crossing. | | +| `forward` | True if the edge is traversed forwards and False if it is traversed backwards. | #### Sign items diff --git a/proto/trip.proto b/proto/trip.proto index 493a5031f3..9bd00882cc 100644 --- a/proto/trip.proto +++ b/proto/trip.proto @@ -181,6 +181,7 @@ message TripLeg { float elevation_sampling_interval = 56; repeated float elevation = 57; bool country_crossing = 58; + bool forward = 59; } message IntersectingEdge { diff --git a/src/baldr/attributes_controller.cc b/src/baldr/attributes_controller.cc index 2f972dd841..1c53378b4a 100644 --- a/src/baldr/attributes_controller.cc +++ b/src/baldr/attributes_controller.cc @@ -82,6 +82,7 @@ const std::unordered_map AttributesController::kDefaultAttrib {kEdgeIndoor, true}, {kEdgeLandmarks, true}, {kEdgeCountryCrossing, true}, + {kEdgeForward, true}, // Node keys {kIncidents, false}, diff --git a/src/thor/triplegbuilder.cc b/src/thor/triplegbuilder.cc index 742504f905..8a68e84a19 100644 --- a/src/thor/triplegbuilder.cc +++ b/src/thor/triplegbuilder.cc @@ -1206,6 +1206,11 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, trip_edge->set_country_crossing(directededge->ctry_crossing()); } + // Set forward if requested + if (controller(kEdgeForward)) { + trip_edge->set_forward(directededge->forward()); + } + uint8_t kAccess = 0; if (mode == sif::TravelMode::kBicycle) { kAccess = kBicycleAccess; diff --git a/src/tyr/trace_serializer.cc b/src/tyr/trace_serializer.cc index a883bc3299..c2bebfb7a6 100644 --- a/src/tyr/trace_serializer.cc +++ b/src/tyr/trace_serializer.cc @@ -192,6 +192,9 @@ void serialize_edges(const AttributesController& controller, if (controller(kEdgeCountryCrossing)) { writer("country_crossing", static_cast(edge.country_crossing())); } + if (controller(kEdgeForward)) { + writer("forward", static_cast(edge.forward())); + } if (controller(kEdgeLength)) { writer.set_precision(3); writer("length", edge.length_km() * scale); diff --git a/valhalla/baldr/attributes_controller.h b/valhalla/baldr/attributes_controller.h index 4b93d5789e..0e213a8396 100644 --- a/valhalla/baldr/attributes_controller.h +++ b/valhalla/baldr/attributes_controller.h @@ -78,6 +78,7 @@ const std::string kEdgeTaggedValues = "edge.tagged_values"; const std::string kEdgeIndoor = "edge.indoor"; const std::string kEdgeLandmarks = "edge.landmarks"; const std::string kEdgeCountryCrossing = "edge.country_crossing"; +const std::string kEdgeForward = "edge.forward"; // Node keys const std::string kNodeIntersectingEdgeBeginHeading = "node.intersecting_edge.begin_heading"; From 35ba52bc921523a49355801f0a1d70dbd0dce185 Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Thu, 5 Sep 2024 08:09:53 -0400 Subject: [PATCH 124/198] Update api-reference.md --- docs/docs/api/map-matching/api-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/api/map-matching/api-reference.md b/docs/docs/api/map-matching/api-reference.md index 2f24595956..c1d7179bed 100644 --- a/docs/docs/api/map-matching/api-reference.md +++ b/docs/docs/api/map-matching/api-reference.md @@ -222,7 +222,7 @@ Each `edge` may include: | `end_node` | The node at the end of this edge. See the list of [end node items](#end-node-items) for details. | | `landmarks` | List of landmarks along the edge. They are used as direction support in navigation. | | | `country_crossing` | True if the edge is a country crossing. | | -| `forward` | True if the edge is traversed forwards and False if it is traversed backwards. | +| `forward` | True if the edge is traversed forwards and False if it is traversed backwards with respect to the reference shape/geometry (ie. the direction in which it was digitized). | #### Sign items From 5a849793e047451febde2994130254deef57072b Mon Sep 17 00:00:00 2001 From: Stepan Kizim <10885920+kinkard@users.noreply.github.com> Date: Wed, 11 Sep 2024 03:26:21 +0200 Subject: [PATCH 125/198] Provide conditional speed limits from "maxspeed:conditional" in `/locate` and proto `/route` responses (#4851) --- CHANGELOG.md | 1 + proto/trip.proto | 27 ++ src/baldr/CMakeLists.txt | 1 + src/baldr/attributes_controller.cc | 1 + src/baldr/edgeinfo.cc | 56 +++- src/baldr/timedomain.cc | 159 +++++++++++ src/mjolnir/graphbuilder.cc | 14 +- src/mjolnir/osmdata.cc | 70 ++++- src/mjolnir/pbfgraphparser.cc | 35 +++ src/thor/triplegbuilder.cc | 33 +++ test/gurka/test_conditional_speedlimit.cc | 314 ++++++++++++++++++++++ valhalla/baldr/attributes_controller.h | 1 + valhalla/baldr/conditional_speed_limit.h | 20 ++ valhalla/baldr/edgeinfo.h | 7 + valhalla/baldr/graphconstants.h | 1 + valhalla/baldr/timedomain.h | 33 ++- valhalla/mjolnir/osmdata.h | 6 + 17 files changed, 747 insertions(+), 32 deletions(-) create mode 100644 src/baldr/timedomain.cc create mode 100644 test/gurka/test_conditional_speedlimit.cc create mode 100644 valhalla/baldr/conditional_speed_limit.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 16e8c269ac..4c1b5c6085 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * **Enhancement** * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) * ADDED: Add option `edge.forward` to trace attributes [#4876](https://github.com/valhalla/valhalla/pull/4876) + * ADDED: Provide conditional speed limits from "maxspeed:conditional" in `/locate` and proto `/route` responses [#4851](https://github.com/valhalla/valhalla/pull/4851) ## Release Date: 2024-08-21 Valhalla 3.5.0 * **Removed** diff --git a/proto/trip.proto b/proto/trip.proto index 9bd00882cc..d83b62a681 100644 --- a/proto/trip.proto +++ b/proto/trip.proto @@ -119,6 +119,32 @@ message TripLeg { uint32 type = 1; } + // represents a single date/time range from https://wiki.openstreetmap.org/wiki/Key:opening_hours + message TimeDomain { + enum DayDowType { + kDayOfMonth = 0; // day_dow is a day of month in range [1,31] + kNthDayOfWeek = 1; // day_dow is a day of week in range [1,7] + } + + DayDowType day_dow_type = 1; // `begin_day_dow` and `end_day_dow` type + uint32 dow_mask = 2; // day of week mask, e.g. 0b0111110 for Mo-Fr as week starts from Su + uint32 begin_hrs = 3; // begin hours, 0 if not set + uint32 begin_mins = 4; // begin minutes, 0 if not set + uint32 begin_month = 5; // begin month, from 1 (January) to 12 (December), 0 if not set + uint32 begin_day_dow = 6; // begin day of month or nth dow, i.e. 1st Sunday + uint32 begin_week = 7; // which week does this start, i.e. 1st week in Oct + uint32 end_hrs = 8; // end hours, 0 if not set + uint32 end_mins = 9; // end minutes, 0 if not set + uint32 end_month = 10; // end month, from 1 (January) to 12 (December), 0 if not set + uint32 end_day_dow = 11; // end day of month or nth dow, i.e. last Sunday + uint32 end_week = 12; // which week does this end, i.e. last week in Oct + } + + message ConditionalSpeedLimit { + uint32 speed_limit = 1; + TimeDomain condition = 2; + } + message Edge { repeated StreetName name = 1; // street names float length_km = 2; // km @@ -182,6 +208,7 @@ message TripLeg { repeated float elevation = 57; bool country_crossing = 58; bool forward = 59; + repeated ConditionalSpeedLimit conditional_speed_limits = 60; } message IntersectingEdge { diff --git a/src/baldr/CMakeLists.txt b/src/baldr/CMakeLists.txt index 893011d014..52ea9937de 100644 --- a/src/baldr/CMakeLists.txt +++ b/src/baldr/CMakeLists.txt @@ -65,6 +65,7 @@ set(sources pathlocation.cc predictedspeeds.cc tilehierarchy.cc + timedomain.cc turn.cc shortcut_recovery.h streetname.cc diff --git a/src/baldr/attributes_controller.cc b/src/baldr/attributes_controller.cc index 1c53378b4a..be75ab8475 100644 --- a/src/baldr/attributes_controller.cc +++ b/src/baldr/attributes_controller.cc @@ -73,6 +73,7 @@ const std::unordered_map AttributesController::kDefaultAttrib {kEdgeSidewalk, true}, {kEdgeDensity, true}, {kEdgeSpeedLimit, true}, + {kEdgeConditionalSpeedLimits, true}, {kEdgeTruckSpeed, true}, {kEdgeTruckRoute, true}, {kEdgeDefaultSpeed, true}, diff --git a/src/baldr/edgeinfo.cc b/src/baldr/edgeinfo.cc index 6c75dd686f..91bcdfe76b 100644 --- a/src/baldr/edgeinfo.cc +++ b/src/baldr/edgeinfo.cc @@ -1,6 +1,5 @@ #include "baldr/edgeinfo.h" #include "baldr/graphconstants.h" - #include "midgard/elevation_encoding.h" using namespace valhalla::baldr; @@ -9,10 +8,7 @@ namespace { // should return true if not equal to TaggedValue::kLinguistic bool IsNonLiguisticTagValue(char ch) { - static const std::unordered_set kNameTags = - {TaggedValue::kBridge, TaggedValue::kTunnel, TaggedValue::kBssInfo, TaggedValue::kLayer, - TaggedValue::kLevel, TaggedValue::kLevelRef, TaggedValue::kLandmark}; - return kNameTags.count(static_cast(static_cast(ch))) > 0; + return static_cast(ch) != TaggedValue::kLinguistic; } json::MapPtr bike_network_json(uint8_t mask) { @@ -47,6 +43,9 @@ std::vector parse_tagged_value(const char* ptr) { size_t landmark_size = landmark_name.size() + 10; return {std::string(ptr, landmark_size)}; } + case TaggedValue::kConditionalSpeedLimits: { + return {std::string(ptr, 1 + sizeof(ConditionalSpeedLimit))}; + } case TaggedValue::kLinguistic: default: return {}; @@ -204,8 +203,9 @@ EdgeInfo::GetNamesAndTypes(bool include_tagged_values) const { if (IsNonLiguisticTagValue(tag)) { name_type_pairs.push_back({std::string(name + 1), false, static_cast(tag)}); } - } else + } else { throw std::runtime_error("GetNamesAndTypes: offset exceeds size of text list"); + } } else if (ni->name_offset_ < names_list_length_) { name_type_pairs.push_back({names_list_ + ni->name_offset_, ni->is_route_num_, 0}); } else { @@ -391,6 +391,17 @@ std::vector EdgeInfo::encoded_elevation(const uint32_t length, double& i return std::vector(encoded_elevation_, encoded_elevation_ + n); } +std::vector EdgeInfo::conditional_speed_limits() const { + std::vector limits; + const auto cond_limits_range = GetTags().equal_range(TaggedValue::kConditionalSpeedLimits); + for (auto it = cond_limits_range.first; it != cond_limits_range.second; ++it) { + const ConditionalSpeedLimit* l = + reinterpret_cast(it->second.data()); + limits.push_back(*l); + } + return limits; +} + int8_t EdgeInfo::layer() const { const auto& tags = GetTags(); auto itr = tags.find(TaggedValue::kLayer); @@ -445,6 +456,39 @@ json::MapPtr EdgeInfo::json() const { edge_info->emplace("speed_limit", static_cast(speed_limit())); } + json::MapPtr conditional_speed_limits; + for (const auto& [tag, value] : GetTags()) { + switch (tag) { + case TaggedValue::kLayer: + break; + case TaggedValue::kLinguistic: + break; + case TaggedValue::kBssInfo: + break; + case TaggedValue::kLevel: + break; + case TaggedValue::kLevelRef: + break; + case TaggedValue::kLandmark: + break; + case TaggedValue::kConditionalSpeedLimits: { + if (!conditional_speed_limits) { + conditional_speed_limits = json::map({}); + } + const ConditionalSpeedLimit* l = reinterpret_cast(value.data()); + conditional_speed_limits->emplace(l->td_.to_string(), static_cast(l->speed_)); + break; + } + case TaggedValue::kTunnel: + break; + case TaggedValue::kBridge: + break; + } + } + if (conditional_speed_limits) { + edge_info->emplace("conditional_speed_limits", std::move(conditional_speed_limits)); + } + return edge_info; } diff --git a/src/baldr/timedomain.cc b/src/baldr/timedomain.cc new file mode 100644 index 0000000000..0087bad132 --- /dev/null +++ b/src/baldr/timedomain.cc @@ -0,0 +1,159 @@ +#include "baldr/timedomain.h" + +#include +#include + +namespace { +std::string month_name(valhalla::baldr::MONTH month) { + using valhalla::baldr::MONTH; + static const std::string months[] = {"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + static_assert(static_cast(MONTH::kDec) == std::size(months) - 1, + "baldr::MONTH enum doesn't match with month names"); + return months[static_cast(month)]; +} + +std::string dow_name(valhalla::baldr::DOW dow) { + static const std::string days[] = {"", "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}; + static_assert(static_cast(valhalla::baldr::DOW::kSaturday) == std::size(days) - 1, + "baldr::DOW enum doesn't match with DoW names"); + return days[static_cast(dow)]; +} + +// Format daterange.dow mask into human-readable format. Week starts from Sunday in the mask +// - 0b1000001 => Su,Sa +// - 0b0111110 => Mo-Fr +// - 0b0101110 => Mo-We,Fr +void format_dow(uint64_t dow_mask, std::ostringstream& ss) { + const std::string days[] = {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}; + + // state machine that helps us to understand the range + bool prev = false; + bool preprev = false; + // flag to track do we need a comma or not + bool empty = true; + + for (int i = 0; i < 7; ++i) { + const bool curr = dow_mask & (1 << i); + + if (curr && !prev) { + // range start + if (!empty) { + ss << ','; + } + ss << dow_name(static_cast(i + 1)); + empty = false; + } + + if (!curr && prev && preprev) { + // we passed the range end - print the previous day + ss << '-' << dow_name(static_cast(i)); + } + + preprev = prev; + prev = curr; + } + + // handle range ending after loop + if (prev && preprev) { + // we passed the range end - print the previous day + ss << '-' << days[6]; + } +} +} // namespace + +namespace valhalla { +namespace baldr { + +std::string TimeDomain::to_string() const { + std::ostringstream ss; + + // Handle daterange.begin_day_dow and daterange.end_day_dow type, day of the + // month or Nth weekday + bool need_space = false; + if (daterange.type == kYMD) { // kYMD - use days of the month + if (daterange.begin_month != 0) { + ss << month_name(static_cast(daterange.begin_month)); + if (daterange.begin_day_dow != 0) { + ss << ' ' << static_cast(daterange.begin_day_dow); + } + need_space = true; + } + if (daterange.end_month != 0 && (daterange.end_month != daterange.begin_month || + daterange.end_day_dow != daterange.begin_day_dow)) { + if (daterange.end_month != daterange.begin_month || daterange.end_day_dow != 0) { + ss << '-'; + } + if (daterange.end_month != 0) { + ss << month_name(static_cast(daterange.end_month)); + if (daterange.end_day_dow != 0) { + ss << ' ' << static_cast(daterange.end_day_dow); + } + need_space = true; + } + } + } else { // daterange.kNthDow - use Nth weekday + if (daterange.begin_month != 0) { + ss << month_name(static_cast(daterange.begin_month)) << ' '; + } + + if (daterange.begin_week) { + // Mo[1] or Su[-1] + // 5 annotates the last week of the month + const int begin_nth_week = + (daterange.begin_week != 5) ? static_cast(daterange.begin_week) : -1; + ss << dow_name(static_cast(daterange.begin_day_dow)) << '[' << begin_nth_week << ']'; + } else { + // Regular date, like March 3-... + ss << static_cast(daterange.begin_day_dow); + } + + if (daterange.end_day_dow != 0 && (daterange.end_day_dow != daterange.begin_day_dow || + daterange.end_week != daterange.begin_week || + daterange.end_month != daterange.begin_month)) { + ss << '-'; + if (daterange.end_month != daterange.begin_month) { + ss << month_name(static_cast(daterange.end_month)) << ' '; + } + + if (daterange.end_week) { + // Mo[1] or Su[-1] + // 5 annotates the last week of the month + const int end_nth_week = + (daterange.end_week != 5) ? static_cast(daterange.end_week) : -1; + ss << dow_name(static_cast(daterange.end_day_dow)) << '[' << end_nth_week << ']'; + } else { + // Regular date, like ...-March 3 + ss << static_cast(daterange.end_day_dow); + } + } + need_space = true; + } + + if (daterange.dow != 0 && daterange.dow != 0b1111111) { + if (need_space) { + ss << ' '; + } + format_dow(daterange.dow, ss); + need_space = true; + } + + // Handle time range + if (daterange.begin_hrs != 0 || daterange.end_hrs != 0 || daterange.begin_mins != 0 || + daterange.end_mins != 0) { + if (need_space) { + ss << ' '; + } + ss << std::setw(2) << std::setfill('0') << static_cast(daterange.begin_hrs) << ':' + << std::setw(2) << std::setfill('0') << static_cast(daterange.begin_mins); + if (daterange.end_hrs != 0 || daterange.end_mins != 0) { + ss << '-' << std::setw(2) << std::setfill('0') << static_cast(daterange.end_hrs) << ':' + << std::setw(2) << std::setfill('0') << static_cast(daterange.end_mins); + } + } + + return ss.str(); +} + +} // namespace baldr +} // namespace valhalla diff --git a/src/mjolnir/graphbuilder.cc b/src/mjolnir/graphbuilder.cc index 8e13b9ae3c..d6bd02d5a5 100644 --- a/src/mjolnir/graphbuilder.cc +++ b/src/mjolnir/graphbuilder.cc @@ -6,6 +6,7 @@ #include #include +#include "baldr/conditional_speed_limit.h" #include "filesystem.h" #include "baldr/datetime.h" @@ -859,8 +860,19 @@ void BuildTileSet(const std::string& ways_file, w.GetTaggedValues(osmdata.name_offset_map, pronunciationMap, langMap, default_languages, tunnel_index, tunnel_lang_index, names.size(), tagged_values, linguistics, type, diff_names); - // Update bike_network type + // Append conditional limits as tagged values + const auto cond_limits_range = osmdata.conditional_speeds.equal_range(w.way_id()); + for (auto it = cond_limits_range.first; it != cond_limits_range.second; ++it) { + std::string value; + value.reserve(1 + sizeof(ConditionalSpeedLimit)); + value += static_cast(TaggedValue::kConditionalSpeedLimits); + value.append(reinterpret_cast(&it->second), + sizeof(ConditionalSpeedLimit)); + tagged_values.push_back(std::move(value)); + } + + // Update bike_network type if (bike_network) { bike_network |= w.bike_network(); } else { diff --git a/src/mjolnir/osmdata.cc b/src/mjolnir/osmdata.cc index 3c38a828f0..ce174476d4 100644 --- a/src/mjolnir/osmdata.cc +++ b/src/mjolnir/osmdata.cc @@ -7,9 +7,9 @@ #include "filesystem.h" #include "midgard/logging.h" #include "mjolnir/osmdata.h" -#include "mjolnir/util.h" using namespace valhalla::mjolnir; +using valhalla::baldr::ConditionalSpeedLimit; namespace { @@ -26,6 +26,7 @@ const std::string unique_names_file = "osmdata_unique_strings.bin"; const std::string lane_connectivity_file = "osmdata_lane_connectivity.bin"; const std::string pronunciation_file = "osmdata_pronunciation_file.bin"; const std::string language_file = "osmdata_language_file.bin"; +const std::string conditional_speed_limit_file = "osmdata_conditional_speed_limit_file.bin"; // Data structures to assist writing and reading data struct TempRestriction { @@ -308,6 +309,24 @@ bool write_linguistic(const std::string& filename, const LinguisticMultiMap& lin return true; } +bool write_conditional_speed_limits(const std::string& filename, + const ConditionalSpeedLimitsMultiMap& speed_map) { + // Open file and truncate + std::ofstream file(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + if (!file.is_open()) { + LOG_ERROR("write_linguistic failed to open output file: " + filename); + return false; + } + + uint32_t sz = speed_map.size(); + file.write(reinterpret_cast(&sz), sizeof(uint32_t)); + for (const std::pair& sp : speed_map) { + file.write(reinterpret_cast(&sp), sizeof(sp)); + } + file.close(); + return true; +} + bool read_restrictions(const std::string& filename, RestrictionsMultiMap& res_map) { // Open file and truncate std::ifstream file(filename, std::ios::in | std::ios::binary); @@ -525,6 +544,28 @@ bool read_linguistic(const std::string& filename, LinguisticMultiMap& ling_map) return true; } +bool read_conditional_speed_limits(const std::string& filename, + ConditionalSpeedLimitsMultiMap& speed_map) { + // Open file and truncate + std::ifstream file(filename, std::ios::in | std::ios::binary); + if (!file.is_open()) { + LOG_ERROR("read_conditional_speed_limits failed to open input file: " + filename); + return false; + } + + // Read the count and following conditional speed limits + uint32_t count = 0; + file.read(reinterpret_cast(&count), sizeof(uint32_t)); + speed_map.reserve(count); + while (count--) { + std::pair sp = {{}, {}}; + file.read(reinterpret_cast(&sp), sizeof(sp)); + speed_map.emplace(std::move(sp)); + } + file.close(); + return true; +} + } // namespace namespace valhalla { @@ -554,17 +595,19 @@ bool OSMData::write_to_temp_files(const std::string& tile_dir) { file.close(); // Write the rest of OSMData - bool status = write_restrictions(tile_dir + restrictions_file, restrictions) && - write_viaset(tile_dir + viaset_file, via_set) && - write_access_restrictions(tile_dir + access_restrictions_file, access_restrictions) && - write_bike_relations(tile_dir + bike_relations_file, bike_relations) && - write_way_refs(tile_dir + way_ref_file, way_ref) && - write_way_refs(tile_dir + way_ref_rev_file, way_ref_rev) && - write_node_names(tile_dir + node_names_file, node_names) && - write_unique_names(tile_dir + unique_names_file, name_offset_map) && - write_lane_connectivity(tile_dir + lane_connectivity_file, lane_connectivity_map) && - write_linguistic(tile_dir + pronunciation_file, pronunciations) && - write_linguistic(tile_dir + language_file, langs); + bool status = + write_restrictions(tile_dir + restrictions_file, restrictions) && + write_viaset(tile_dir + viaset_file, via_set) && + write_access_restrictions(tile_dir + access_restrictions_file, access_restrictions) && + write_bike_relations(tile_dir + bike_relations_file, bike_relations) && + write_way_refs(tile_dir + way_ref_file, way_ref) && + write_way_refs(tile_dir + way_ref_rev_file, way_ref_rev) && + write_node_names(tile_dir + node_names_file, node_names) && + write_unique_names(tile_dir + unique_names_file, name_offset_map) && + write_lane_connectivity(tile_dir + lane_connectivity_file, lane_connectivity_map) && + write_linguistic(tile_dir + pronunciation_file, pronunciations) && + write_linguistic(tile_dir + language_file, langs) && + write_conditional_speed_limits(tile_dir + conditional_speed_limit_file, conditional_speeds); LOG_INFO("Done"); return status; } @@ -609,7 +652,8 @@ bool OSMData::read_from_temp_files(const std::string& tile_dir) { read_unique_names(tile_directory + unique_names_file, name_offset_map) && read_lane_connectivity(tile_directory + lane_connectivity_file, lane_connectivity_map) && read_linguistic(tile_directory + pronunciation_file, pronunciations) && - read_linguistic(tile_directory + language_file, langs); + read_linguistic(tile_directory + language_file, langs) && + read_conditional_speed_limits(tile_directory + lane_connectivity_file, conditional_speeds); LOG_INFO("Done"); initialized = status; return status; diff --git a/src/mjolnir/pbfgraphparser.cc b/src/mjolnir/pbfgraphparser.cc index a9587d5df0..249e60ee82 100644 --- a/src/mjolnir/pbfgraphparser.cc +++ b/src/mjolnir/pbfgraphparser.cc @@ -946,6 +946,41 @@ struct graph_callback : public OSMPBF::Callback { LOG_INFO("out_of_range thrown for way id: " + std::to_string(osmid_)); } }; + tag_handlers_["maxspeed:conditional"] = [this]() { + std::vector tokens = GetTagTokens(tag_.second, '@'); + if (tokens.size() < 2) { + return; // just ignore bad entries + } + + uint8_t speed; + if (tokens.at(0) == "no" || tokens.at(0) == "none") { + // Handle autobahns that have unlimited speed during smaller part of the day + speed = kUnlimitedSpeedLimit; + } else { + try { + const float parsed = std::stof(tokens.at(0)); + if (parsed > kMaxAssumedSpeed) { + // LOG_WARN("Ignoring maxspeed:conditional that exceedes max for way id: " + + // std::to_string(osmid_)); + return; + } + speed = static_cast(parsed + 0.5f); + } catch (const std::invalid_argument&) { + return; // ignore strange things like 'walk @...' + } + } + + std::vector conditions = GetTagTokens(tokens.at(1), ';'); + for (const auto& c : conditions) { + std::vector values = get_time_range(c); + for (const auto& v : values) { + ConditionalSpeedLimit limit = {}; + limit.td_ = TimeDomain(v); + limit.speed_ = speed; + osmdata_.conditional_speeds.emplace(osmid_, limit); + } + } + }; tag_handlers_["truck_route"] = [this]() { way_.set_truck_route(tag_.second == "true" ? true : false); }; diff --git a/src/thor/triplegbuilder.cc b/src/thor/triplegbuilder.cc index 8a68e84a19..a29f241158 100644 --- a/src/thor/triplegbuilder.cc +++ b/src/thor/triplegbuilder.cc @@ -13,6 +13,7 @@ #include "baldr/signinfo.h" #include "baldr/tilehierarchy.h" #include "baldr/time_info.h" +#include "baldr/timedomain.h" #include "meili/match_result.h" #include "midgard/elevation_encoding.h" #include "midgard/encoded.h" @@ -1444,6 +1445,38 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, trip_edge->set_speed_limit(edgeinfo.speed_limit()); } + if (controller(kEdgeConditionalSpeedLimits)) { + auto conditional_limits = edgeinfo.conditional_speed_limits(); + trip_edge->mutable_conditional_speed_limits()->Reserve(conditional_limits.size()); + for (const auto& limit : conditional_limits) { + auto proto = trip_edge->mutable_conditional_speed_limits()->Add(); + proto->set_speed_limit(limit.speed_); + + auto* condition = proto->mutable_condition(); + + switch (limit.td_.type()) { + case kYMD: + condition->set_day_dow_type(TripLeg_TimeDomain_DayDowType_kDayOfMonth); + break; + case kNthDow: + condition->set_day_dow_type(TripLeg_TimeDomain_DayDowType_kNthDayOfWeek); + break; + } + + condition->set_dow_mask(limit.td_.dow()); + condition->set_begin_hrs(limit.td_.begin_hrs()); + condition->set_begin_mins(limit.td_.begin_mins()); + condition->set_begin_month(limit.td_.begin_month()); + condition->set_begin_day_dow(limit.td_.begin_day_dow()); + condition->set_begin_week(limit.td_.begin_week()); + condition->set_end_hrs(limit.td_.end_hrs()); + condition->set_end_mins(limit.td_.end_mins()); + condition->set_end_month(limit.td_.end_month()); + condition->set_end_day_dow(limit.td_.end_day_dow()); + condition->set_end_week(limit.td_.end_week()); + } + } + if (controller(kEdgeDefaultSpeed)) { trip_edge->set_default_speed(directededge->speed()); } diff --git a/test/gurka/test_conditional_speedlimit.cc b/test/gurka/test_conditional_speedlimit.cc new file mode 100644 index 0000000000..2ab028bff5 --- /dev/null +++ b/test/gurka/test_conditional_speedlimit.cc @@ -0,0 +1,314 @@ +#include "baldr/datetime.h" +#include "baldr/timedomain.h" +#include "gurka.h" +#include "test.h" + +#include + +using namespace valhalla; + +TimeDomain from_proto(const TripLeg_TimeDomain& proto) { + TimeDomain td; + td.set_type(proto.day_dow_type()); + td.set_dow(proto.dow_mask()); + td.set_begin_hrs(proto.begin_hrs()); + td.set_begin_mins(proto.begin_mins()); + td.set_begin_month(proto.begin_month()); + td.set_begin_day_dow(proto.begin_day_dow()); + td.set_begin_week(proto.begin_week()); + td.set_end_hrs(proto.end_hrs()); + td.set_end_mins(proto.end_mins()); + td.set_end_month(proto.end_month()); + td.set_end_day_dow(proto.end_day_dow()); + td.set_end_week(proto.end_week()); + return td; +} + +namespace valhalla { +bool operator==(const TripLeg_TimeDomain& proto, const TimeDomain& td) { + return td.td_value() == from_proto(proto).td_value(); +} + +std::ostream& operator<<(std::ostream& os, TripLeg_TimeDomain const& proto) { + return os << from_proto(proto).to_string(); +} + +namespace baldr { +std::ostream& operator<<(std::ostream& os, TimeDomain const& td) { + return os << td.to_string(); +} +} // namespace baldr +} // namespace valhalla + +class ConditionalSpeedlimit : public ::testing::Test { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + constexpr double grid_size_meters = 100; + + const std::string ascii_map = R"( + M + /|\ + / 3 2 + / | \ + A------B-------C---1--D + E------F-------G------H + \ | / + \ | / + \|/ + N + | + 4 + | + O--5--P-6-R + | + 7 + | + Q + )"; + + const gurka::ways ways = { + {"DCBA", + { + {"ref", "A20"}, + {"highway", "motorway"}, + {"oneway", "yes"}, + {"maxspeed", "100"}, + {"maxspeed:conditional", "120 @ (19:00-06:00)"}, + }}, + {"EFGH", + { + {"ref", "A20"}, + {"highway", "motorway"}, + {"oneway", "yes"}, + {"maxspeed", "100"}, + {"maxspeed:conditional", "120 @ (19:00-06:00)"}, + }}, + + {"MB", {{"highway", "motorway_link"}, {"oneway", "yes"}, {"maxspeed", "100"}}}, + {"CM", {{"highway", "motorway_link"}, {"oneway", "yes"}, {"maxspeed", "100"}}}, + {"NG", {{"highway", "motorway_link"}, {"oneway", "yes"}, {"maxspeed", "100"}}}, + {"FN", {{"highway", "motorway_link"}, {"oneway", "yes"}, {"maxspeed", "100"}}}, + + {"MNO", + { + {"highway", "secondary"}, + {"maxspeed", "80"}, + {"maxspeed:conditional", "70 @ (Jun-Aug Sa,Su 08:00-20:00)"}, + }}, + + {"OP", {{"highway", "tertiary"}, {"maxspeed", "50"}}}, + {"PR", + { + {"highway", "tertiary"}, + {"maxspeed", "50"}, + {"maxspeed:conditional", "30 @ (Mo-Fr 07:00-16:00; Sa 09:00-16:00; Su 09:00-16:00)"}, + }}, + {"RQ", + { + {"highway", "tertiary"}, + {"maxspeed", "50"}, + {"maxspeed:conditional", "20 @ (Dec Fr[-1]-Jan Mo[2])"}, + }}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, grid_size_meters); + map = gurka::buildtiles(layout, ways, {}, {}, + VALHALLA_BUILD_DIR "test/data/conditional_speedlimits", + {{"mjolnir.timezone", {VALHALLA_BUILD_DIR "test/data/tz.sqlite"}}}); + } +}; + +gurka::map ConditionalSpeedlimit::map = {}; + +/*************************************************************/ + +TEST_F(ConditionalSpeedlimit, RouteApiProto) { + const auto result = gurka::do_action(valhalla::Options::route, map, {"D", "Q"}, "auto", + {{"/date_time/value", "2024-08-15T21:00"}}); + ASSERT_EQ(result.trip().routes_size(), 1); + ASSERT_EQ(result.trip().routes(0).legs_size(), 1); + const auto leg = result.trip().routes(0).legs(0); + ASSERT_EQ(leg.node_size(), 8); + + gurka::assert::raw::expect_path(result, {"A20/DCBA", "CM", "MNO", "MNO", "OP", "PR", "RQ"}); + // Regular speed limit are not affected, even if date was passed + EXPECT_EQ(leg.node(0).edge().speed_limit(), 100); // DC + EXPECT_EQ(leg.node(1).edge().speed_limit(), 100); // CM + EXPECT_EQ(leg.node(2).edge().speed_limit(), 80); // MN + EXPECT_EQ(leg.node(3).edge().speed_limit(), 80); // NO + EXPECT_EQ(leg.node(4).edge().speed_limit(), 50); // OP + EXPECT_EQ(leg.node(5).edge().speed_limit(), 50); // PR + EXPECT_EQ(leg.node(6).edge().speed_limit(), 50); // RQ + EXPECT_EQ(leg.node(7).edge().speed_limit(), 0); // the end + + // Now check conditional speed limits + // 120 @ (19:00-06:00) + ASSERT_EQ(leg.node(0).edge().conditional_speed_limits_size(), 1); // one condition on DC + EXPECT_EQ(leg.node(0).edge().conditional_speed_limits(0).speed_limit(), 120); + { + baldr::TimeDomain condition; + condition.set_begin_hrs(19); + condition.set_end_hrs(6); + EXPECT_EQ(leg.node(0).edge().conditional_speed_limits(0).condition(), condition); + } + + EXPECT_EQ(leg.node(1).edge().conditional_speed_limits_size(), 0); + + // 90 @ (Jun-Aug Sa,Su 08:00-20:00) + { + baldr::TimeDomain condition; + condition.set_begin_month(6); + condition.set_end_month(8); + condition.set_dow(0b1000001); // Week starts from kSunday + condition.set_begin_hrs(8); + condition.set_end_hrs(20); + + ASSERT_EQ(leg.node(2).edge().conditional_speed_limits_size(), 1); + EXPECT_EQ(leg.node(2).edge().conditional_speed_limits(0).speed_limit(), 70); + EXPECT_EQ(leg.node(2).edge().conditional_speed_limits(0).condition(), condition); + + ASSERT_EQ(leg.node(3).edge().conditional_speed_limits_size(), 1); + EXPECT_EQ(leg.node(3).edge().conditional_speed_limits(0).speed_limit(), 70); + EXPECT_EQ(leg.node(3).edge().conditional_speed_limits(0).condition(), condition); + } + + EXPECT_EQ(leg.node(4).edge().conditional_speed_limits_size(), 0); + + // "30 @ (Mo-Fr 07:00-16:00; Sa 09:00-16:00; Su 09:00-16:00)"} + EXPECT_EQ(leg.node(5).edge().conditional_speed_limits_size(), 3); // three time ranges on PR + std::vector condtitions; + for (const auto& conditional_speed_limit : leg.node(5).edge().conditional_speed_limits()) { + EXPECT_EQ(conditional_speed_limit.speed_limit(), 30); + condtitions.emplace_back(conditional_speed_limit.condition()); + } + // Conditions are stored unsorted, so let's sort them for comparison + std::sort(condtitions.begin(), condtitions.end(), [](const auto& lha, const auto& rha) { + if (lha.dow_mask() < rha.dow_mask()) { + return true; + } else if (lha.dow_mask() > rha.dow_mask()) { + return false; + } else if (lha.begin_hrs() < rha.begin_hrs()) { + return true; + } else if (lha.begin_hrs() > rha.begin_hrs()) { + return false; + } else if (lha.end_hrs() < rha.end_hrs()) { + return true; + } else { + return false; + } + }); + + { + // Su 09:00-16:00 + baldr::TimeDomain condition; + condition.set_dow(0b0000001); // Week starts from kSunday + condition.set_begin_hrs(9); + condition.set_end_hrs(16); + EXPECT_EQ(condtitions[0], condition); + } + { + // Mo-Fr 07:00-16:00 + baldr::TimeDomain condition; + condition.set_dow(0b0111110); // Week starts from kSunday + condition.set_begin_hrs(7); + condition.set_end_hrs(16); + EXPECT_EQ(condtitions[1], condition); + } + { + // Sa 09:00-16:00 + baldr::TimeDomain condition; + condition.set_dow(0b1000000); // Week starts from kSunday + condition.set_begin_hrs(9); + condition.set_end_hrs(16); + EXPECT_EQ(condtitions[2], condition); + } + + ASSERT_EQ(leg.node(6).edge().conditional_speed_limits_size(), 1); + EXPECT_EQ(leg.node(6).edge().conditional_speed_limits(0).speed_limit(), 20); + { + // Dec Fr[-1]-Jan Mo[2] + baldr::TimeDomain condition; + condition.set_type(kNthDow); + condition.set_dow(0b1111111); // Magic mask that means every day, don't ask why it is not 0 + condition.set_begin_month(12); // Dec + condition.set_begin_day_dow(6); // Fr + condition.set_begin_week(5); // [-1] + condition.set_end_month(1); // Jan + condition.set_end_day_dow(2); // Mo + condition.set_end_week(2); // [2] + EXPECT_EQ(leg.node(6).edge().conditional_speed_limits(0).condition(), condition); + } + + EXPECT_EQ(leg.node(7).edge().conditional_speed_limits_size(), 0); +} + +TEST_F(ConditionalSpeedlimit, LocateApiJson) { + auto reader = test::make_clean_graphreader(map.config.get_child("mjolnir")); + std::string json; + const auto result = + gurka::do_action(valhalla::Options::locate, map, {"1", "2", "3", "4", "5", "6", "7"}, "auto", + {}, reader, &json); + + rapidjson::Document root; + root.Parse(json); + ASSERT_FALSE(root.HasParseError()) << json; + ASSERT_EQ(root.GetArray().Size(), 7); + + const auto& edge_info_0 = *rapidjson::Pointer("/0/edges/0/edge_info").Get(root); + EXPECT_EQ(edge_info_0["speed_limit"], 100); + ASSERT_TRUE(edge_info_0.HasMember("conditional_speed_limits")); + for (const auto& [k, v] : edge_info_0["conditional_speed_limits"].GetObject()) { + EXPECT_STREQ(k.GetString(), "19:00-06:00"); + EXPECT_EQ(v, 120); + } + + const auto& edge_info_1 = *rapidjson::Pointer("/1/edges/0/edge_info").Get(root); + EXPECT_EQ(edge_info_1["speed_limit"], 100); + EXPECT_FALSE(edge_info_1.HasMember("conditional_speed_limits")); + + const auto& edge_info_2 = *rapidjson::Pointer("/2/edges/0/edge_info").Get(root); + EXPECT_EQ(edge_info_2["speed_limit"], 80); + ASSERT_TRUE(edge_info_2.HasMember("conditional_speed_limits")); + for (const auto& [k, v] : edge_info_2["conditional_speed_limits"].GetObject()) { + EXPECT_STREQ(k.GetString(), "Jun-Aug Su,Sa 08:00-20:00"); + EXPECT_EQ(v, 70); + } + + const auto& edge_info_3 = *rapidjson::Pointer("/3/edges/0/edge_info").Get(root); + EXPECT_EQ(edge_info_3["speed_limit"], 80); + ASSERT_TRUE(edge_info_3.HasMember("conditional_speed_limits")); + for (const auto& [k, v] : edge_info_3["conditional_speed_limits"].GetObject()) { + EXPECT_STREQ(k.GetString(), "Jun-Aug Su,Sa 08:00-20:00"); + EXPECT_EQ(v, 70); + } + + const auto& edge_info_4 = *rapidjson::Pointer("/4/edges/0/edge_info").Get(root); + EXPECT_EQ(edge_info_4["speed_limit"], 50); + EXPECT_FALSE(edge_info_4.HasMember("conditional_speed_limits")); + + const auto& edge_info_5 = *rapidjson::Pointer("/5/edges/0/edge_info").Get(root); + EXPECT_EQ(edge_info_5["speed_limit"], 50); + ASSERT_TRUE(edge_info_5.HasMember("conditional_speed_limits")); + + // Conditions might be in different order, let's sort them and then check + std::vector conditions_str; + for (const auto& [name, value] : edge_info_5["conditional_speed_limits"].GetObject()) { + conditions_str.push_back(name.GetString()); + EXPECT_EQ(value, 30); + } + std::sort(conditions_str.begin(), conditions_str.end()); + ASSERT_EQ(conditions_str.size(), 3); + EXPECT_EQ(conditions_str[0], "Mo-Fr 07:00-16:00"); + EXPECT_EQ(conditions_str[1], "Sa 09:00-16:00"); + EXPECT_EQ(conditions_str[2], "Su 09:00-16:00"); + + const auto& edge_info_6 = *rapidjson::Pointer("/6/edges/0/edge_info").Get(root); + EXPECT_EQ(edge_info_6["speed_limit"], 50); + ASSERT_TRUE(edge_info_6.HasMember("conditional_speed_limits")); + for (const auto& [k, v] : edge_info_6["conditional_speed_limits"].GetObject()) { + EXPECT_STREQ(k.GetString(), "Dec Fr[-1]-Jan Mo[2]"); + EXPECT_EQ(v, 20); + } +} diff --git a/valhalla/baldr/attributes_controller.h b/valhalla/baldr/attributes_controller.h index 0e213a8396..8972bbfe54 100644 --- a/valhalla/baldr/attributes_controller.h +++ b/valhalla/baldr/attributes_controller.h @@ -69,6 +69,7 @@ const std::string kEdgeShoulder = "edge.shoulder"; const std::string kEdgeSidewalk = "edge.sidewalk"; const std::string kEdgeDensity = "edge.density"; const std::string kEdgeSpeedLimit = "edge.speed_limit"; +const std::string kEdgeConditionalSpeedLimits = "edge.conditional_speed_limits"; const std::string kEdgeTruckSpeed = "edge.truck_speed"; const std::string kEdgeTruckRoute = "edge.truck_route"; const std::string kEdgeDefaultSpeed = "edge.default_speed"; diff --git a/valhalla/baldr/conditional_speed_limit.h b/valhalla/baldr/conditional_speed_limit.h new file mode 100644 index 0000000000..ca3fdc260f --- /dev/null +++ b/valhalla/baldr/conditional_speed_limit.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace valhalla { +namespace baldr { + +/** A combination of `TimeDomain` condition and a speed limit in a single 8b word */ +union ConditionalSpeedLimit { + TimeDomain td_; + struct { + uint64_t padding_ : 54; // padding over the TimeDomain meaningful bits + uint64_t speed_ : 8; // speed limit in KPH + uint64_t spare_ : 2; + }; +}; +static_assert(sizeof(ConditionalSpeedLimit) == 8, "invalid ConditionalSpeedLimit struct size"); + +} // namespace baldr +} // namespace valhalla diff --git a/valhalla/baldr/edgeinfo.h b/valhalla/baldr/edgeinfo.h index b34732c205..2870d45612 100644 --- a/valhalla/baldr/edgeinfo.h +++ b/valhalla/baldr/edgeinfo.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -251,6 +252,12 @@ class EdgeInfo { */ std::vector encoded_elevation(const uint32_t length, double& interval) const; + /** + * Returns the list of conditional speed limits for the edge. + * @return Conditional speed limits for the edge. + */ + std::vector conditional_speed_limits() const; + /** * Get layer index of the edge relatively to other edges(Z-level). Can be negative. * @see https://wiki.openstreetmap.org/wiki/Key:layer diff --git a/valhalla/baldr/graphconstants.h b/valhalla/baldr/graphconstants.h index f5fb6bf499..076fb97730 100644 --- a/valhalla/baldr/graphconstants.h +++ b/valhalla/baldr/graphconstants.h @@ -385,6 +385,7 @@ enum class TaggedValue : uint8_t { // must start at 1 due to nulls kLevel = 4, kLevelRef = 5, kLandmark = 6, + kConditionalSpeedLimits = 7, // we used to have bug when we encoded 1 and 2 as their ASCII codes, but not actual 1 and 2 values // see https://github.com/valhalla/valhalla/issues/3262 kTunnel = static_cast('1'), diff --git a/valhalla/baldr/timedomain.h b/valhalla/baldr/timedomain.h index 57bf50caec..ade16dd47b 100644 --- a/valhalla/baldr/timedomain.h +++ b/valhalla/baldr/timedomain.h @@ -3,6 +3,8 @@ #include +#include + namespace valhalla { namespace baldr { @@ -309,20 +311,27 @@ union TimeDomain { return value; } + /** + * Provides a string representation of a condition in format close to the + * [opening hours](https://wiki.openstreetmap.org/wiki/Key:opening_hours/specification) + */ + std::string to_string() const; + protected: + // represents a single date/time range from https://wiki.openstreetmap.org/wiki/Key:opening_hours struct DateRange { - uint64_t type : 1; - uint64_t dow : 7; // day of week for this restriction - uint64_t begin_hrs : 5; // begin hours - uint64_t begin_mins : 6; // begin minutes - uint64_t begin_month : 4; // begin month - uint64_t begin_day_dow : 5; // begin day or dow enum i.e. 1st Sunday - uint64_t begin_week : 3; // which week does this start. i.e. 1st week in Oct - uint64_t end_hrs : 5; // end hours - uint64_t end_mins : 6; // end minutes - uint64_t end_month : 4; // end month - uint64_t end_day_dow : 5; // end day or dow enum i.e. last Sunday - uint64_t end_week : 3; // which week does this end. i.e. last week in Oct + uint64_t type : 1; // type of day_dow, 0 - day of month [1,31], 1 - nth day of week [1,7] + uint64_t dow : 7; // day of week mask, e.g. 0b0111110 for Mo-Fr as week starts from Su + uint64_t begin_hrs : 5; // begin hours, 0 if not set + uint64_t begin_mins : 6; // begin minutes, 0 if not set + uint64_t begin_month : 4; // begin month, from 1 (January) to 12 (December), 0 if not set + uint64_t begin_day_dow : 5; // begin day of month or nth dow, i.e. 1st Sunday + uint64_t begin_week : 3; // which week does this start, i.e. 1st week in Oct + uint64_t end_hrs : 5; // end hours, 0 if not set + uint64_t end_mins : 6; // end minutes, 0 if not set + uint64_t end_month : 4; // end month, from 1 (January) to 12 (December), 0 if not set + uint64_t end_day_dow : 5; // end day of month or nth dow, i.e. last Sunday + uint64_t end_week : 3; // which week does this end, i.e. last week in Oct uint64_t spare : 10; } daterange; diff --git a/valhalla/mjolnir/osmdata.h b/valhalla/mjolnir/osmdata.h index a99b47f9f9..0f6187abd0 100644 --- a/valhalla/mjolnir/osmdata.h +++ b/valhalla/mjolnir/osmdata.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,8 @@ using AccessRestrictionsMultiMap = std::unordered_multimap; using OSMLaneConnectivityMultiMap = std::unordered_multimap; using LinguisticMultiMap = std::unordered_multimap; +using ConditionalSpeedLimitsMultiMap = + std::unordered_multimap; // OSMString map uses the way Id as the key and the name index into UniqueNames as the value using OSMStringMap = std::unordered_map; @@ -135,6 +138,9 @@ struct OSMData { // Stores the pronunciation languages. Indexed by the way Id. LinguisticMultiMap langs; + // Stores the conditional speed limits ("maxspeed:conditional" osm key). Indexed by the way Id. + ConditionalSpeedLimitsMultiMap conditional_speeds; + bool initialized = false; }; From 3a385045919967a14d5c9cc57610b2111936ac64 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Sun, 15 Sep 2024 22:18:10 +0900 Subject: [PATCH 126/198] Fix logging properly in valhalla_export_edges (#4892) --- CHANGELOG.md | 1 + src/valhalla_export_edges.cc | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c1b5c6085..ff7cd424ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## UNRELEASED * **Removed** * **Bug Fix** + * FIXED: All logging in `valhalla_export_edges` now goes to stderr [#4892](https://github.com/valhalla/valhalla/pull/4892) * **Enhancement** * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) * ADDED: Add option `edge.forward` to trace attributes [#4876](https://github.com/valhalla/valhalla/pull/4876) diff --git a/src/valhalla_export_edges.cc b/src/valhalla_export_edges.cc index 65867776d5..09e8963268 100644 --- a/src/valhalla_export_edges.cc +++ b/src/valhalla_export_edges.cc @@ -177,12 +177,12 @@ int main(int argc, char* argv[]) { return EXIT_FAILURE; } - // get something we can use to fetch tiles - valhalla::baldr::GraphReader reader(config.get_child("mjolnir")); - // configure logging here, we want it to go to stderr valhalla::midgard::logging::Configure({{"type", "std_err"}, {"color", "true"}}); + // get something we can use to fetch tiles + valhalla::baldr::GraphReader reader(config.get_child("mjolnir")); + // keep the global number of edges encountered at the point we encounter each tile // this allows an edge to have a sequential global id and makes storing it very small LOG_INFO("Enumerating edges..."); From 523847c290cd8641d2f27018b4e9a9a2d93ba164 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 19 Sep 2024 12:23:16 +0200 Subject: [PATCH 127/198] Update update_public_servers.yml --- .github/workflows/update_public_servers.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/update_public_servers.yml b/.github/workflows/update_public_servers.yml index c17eb27c61..5519c60f8a 100644 --- a/.github/workflows/update_public_servers.yml +++ b/.github/workflows/update_public_servers.yml @@ -6,11 +6,11 @@ on: - master paths: - .github/workflows - - 'src' - - 'valhalla' - - 'proto' - - 'scripts' - - 'lua' + - src + - valhalla + - proto + - scripts + - lua workflow_dispatch: jobs: From 3bdfc5e55e7e17e47ea29b4e2c12f4ac4666db31 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 19 Sep 2024 12:30:06 +0200 Subject: [PATCH 128/198] Update update_public_servers.yml --- .github/workflows/update_public_servers.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/update_public_servers.yml b/.github/workflows/update_public_servers.yml index 5519c60f8a..0d242b7ba2 100644 --- a/.github/workflows/update_public_servers.yml +++ b/.github/workflows/update_public_servers.yml @@ -4,13 +4,6 @@ on: push: branches: - master - paths: - - .github/workflows - - src - - valhalla - - proto - - scripts - - lua workflow_dispatch: jobs: From 9a1f2b395d54ab9563344476697b855ca7ec4308 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 19 Sep 2024 12:36:12 +0200 Subject: [PATCH 129/198] Update update_public_servers.yml --- .github/workflows/update_public_servers.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/update_public_servers.yml b/.github/workflows/update_public_servers.yml index 0d242b7ba2..c236bacb2b 100644 --- a/.github/workflows/update_public_servers.yml +++ b/.github/workflows/update_public_servers.yml @@ -4,6 +4,8 @@ on: push: branches: - master + paths: + - .github/workflows/ workflow_dispatch: jobs: From d6de753e20eff7f4226b0e41e2e66700e60ec796 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 19 Sep 2024 12:37:06 +0200 Subject: [PATCH 130/198] Update update_public_servers.yml --- .github/workflows/update_public_servers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update_public_servers.yml b/.github/workflows/update_public_servers.yml index c236bacb2b..7c66db053f 100644 --- a/.github/workflows/update_public_servers.yml +++ b/.github/workflows/update_public_servers.yml @@ -5,7 +5,7 @@ on: branches: - master paths: - - .github/workflows/ + - '.github/workflows/**' workflow_dispatch: jobs: From ca3c2f076e7cdba6d09744304121a30d87fbf94f Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 19 Sep 2024 12:42:37 +0200 Subject: [PATCH 131/198] Update update_public_servers.yml --- .github/workflows/update_public_servers.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/update_public_servers.yml b/.github/workflows/update_public_servers.yml index 7c66db053f..4af133fb32 100644 --- a/.github/workflows/update_public_servers.yml +++ b/.github/workflows/update_public_servers.yml @@ -6,6 +6,15 @@ on: - master paths: - '.github/workflows/**' + - 'src/**' + - 'valhalla/**' + - 'proto/**' + - 'scripts/**' + - 'lua/**' + - 'cmake/**' + - 'third_party/**' + - 'locales/**' + - 'CMakeLists.txt' workflow_dispatch: jobs: From 4d311dc93749b0c4f585767e567ab1fd7583eeb1 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 19 Sep 2024 12:47:51 +0200 Subject: [PATCH 132/198] Update update_public_server.sh --- scripts/update_public_server.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update_public_server.sh b/scripts/update_public_server.sh index 9777434f46..7740104fc4 100644 --- a/scripts/update_public_server.sh +++ b/scripts/update_public_server.sh @@ -10,7 +10,7 @@ git config --global --add safe.directory /src/valhalla git -C "${src_dir}" checkout master git -C "${src_dir}" pull -git submodule update --init --recursive +git -C "${src_dir}" submodule update --init --recursive # remove the build folder first rm -r "${src_dir}"/build From c411feb309618c956b5fa7170d3845fb9fa75a06 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 19 Sep 2024 12:54:22 +0200 Subject: [PATCH 133/198] Update update_public_server.sh --- scripts/update_public_server.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/update_public_server.sh b/scripts/update_public_server.sh index 7740104fc4..7d92b3186a 100644 --- a/scripts/update_public_server.sh +++ b/scripts/update_public_server.sh @@ -8,6 +8,10 @@ src_dir="/src/valhalla" git config --global --add safe.directory /src/valhalla +for s in $(ls /src/valhalla/third_party); do + git config --global --add safe.directory /src/valhalla/third_party/$s +done + git -C "${src_dir}" checkout master git -C "${src_dir}" pull git -C "${src_dir}" submodule update --init --recursive From ac0cc3d9eaaed575b15e621b2c0790af7c603f90 Mon Sep 17 00:00:00 2001 From: Girish Yadav <7981232+girishyadav20@users.noreply.github.com> Date: Thu, 19 Sep 2024 11:27:21 -0400 Subject: [PATCH 134/198] Fix for bug #4880 (#4881) --- src/bindings/python/actor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bindings/python/actor.py b/src/bindings/python/actor.py index 44aced95e0..483df74642 100644 --- a/src/bindings/python/actor.py +++ b/src/bindings/python/actor.py @@ -43,11 +43,11 @@ def matrix(self, req: Union[str, dict]): @dict_or_str def trace_route(self, req: Union[str, dict]): - return super().traceRoute(req) + return super().trace_route(req) @dict_or_str def trace_attributes(self, req: Union[str, dict]): - return super().traceAttributes(req) + return super().trace_attributes(req) @dict_or_str def height(self, req: Union[str, dict]): From 5f48bfa4664dd73f0901e1ce22cfdaede6debe70 Mon Sep 17 00:00:00 2001 From: Stepan Kizim <10885920+kinkard@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:32:50 +0200 Subject: [PATCH 135/198] perf: Iterate over only `kLandmark` tagged values in `AddLandmarks` (#4873) --- CHANGELOG.md | 1 + src/thor/triplegbuilder.cc | 70 ++++++++++++++++++-------------------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff7cd424ae..6f02cadfc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * **Removed** * **Bug Fix** * FIXED: All logging in `valhalla_export_edges` now goes to stderr [#4892](https://github.com/valhalla/valhalla/pull/4892) + * FIXED: Iterate over only `kLandmark` tagged values in `AddLandmarks()` [#4873](https://github.com/valhalla/valhalla/pull/4873) * **Enhancement** * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) * ADDED: Add option `edge.forward` to trace attributes [#4876](https://github.com/valhalla/valhalla/pull/4876) diff --git a/src/thor/triplegbuilder.cc b/src/thor/triplegbuilder.cc index a29f241158..8a796252e3 100644 --- a/src/thor/triplegbuilder.cc +++ b/src/thor/triplegbuilder.cc @@ -507,42 +507,40 @@ void AddLandmarks(const EdgeInfo& edgeinfo, return; } - for (const auto& tag : edgeinfo.GetTags()) { - // get landmarks from tagged values in the edge info - if (tag.first == baldr::TaggedValue::kLandmark) { - Landmark lan(tag.second); - PointLL landmark_point = {lan.lng, lan.lat}; - - // find the closed point on edge to the landmark - auto closest = landmark_point.ClosestPoint(shape, begin_index); - // TODO: in the future maybe we could allow a request option to have a tighter threshold on - // how far landmarks should be away from an edge - - // add the landmark to trip leg - auto* landmark = trip_edge->mutable_landmarks()->Add(); - landmark->set_name(lan.name); - landmark->set_type(static_cast(lan.type)); - landmark->mutable_lat_lng()->set_lng(lan.lng); - landmark->mutable_lat_lng()->set_lat(lan.lat); - - // calculate the landmark's distance along the edge - // that is to accumulate distance from the begin point to the closest point to it on the edge - int closest_idx = std::get<2>(closest); - double distance_along_edge = 0; - for (int idx = begin_index + 1; idx <= closest_idx; ++idx) { - distance_along_edge += shape[idx].Distance(shape[idx - 1]); - } - distance_along_edge += shape[closest_idx].Distance(std::get<0>(closest)); - // the overall distance shouldn't be larger than edge length - distance_along_edge = std::min(distance_along_edge, static_cast(edge->length())); - landmark->set_distance(distance_along_edge); - // check which side of the edge the landmark is on - // quirks of the ClosestPoint function - bool is_right = closest_idx == (int)shape.size() - 1 - ? landmark_point.IsLeft(shape[closest_idx - 1], shape[closest_idx]) < 0 - : landmark_point.IsLeft(shape[closest_idx], shape[closest_idx + 1]) < 0; - landmark->set_right(is_right); - } + const auto landmark_range = edgeinfo.GetTags().equal_range(baldr::TaggedValue::kLandmark); + for (auto it = landmark_range.first; it != landmark_range.second; ++it) { + Landmark lan(it->second); + PointLL landmark_point = {lan.lng, lan.lat}; + + // find the closed point on edge to the landmark + auto closest = landmark_point.ClosestPoint(shape, begin_index); + // TODO: in the future maybe we could allow a request option to have a tighter threshold on + // how far landmarks should be away from an edge + + // add the landmark to trip leg + auto* landmark = trip_edge->mutable_landmarks()->Add(); + landmark->set_name(lan.name); + landmark->set_type(static_cast(lan.type)); + landmark->mutable_lat_lng()->set_lng(lan.lng); + landmark->mutable_lat_lng()->set_lat(lan.lat); + + // calculate the landmark's distance along the edge + // that is to accumulate distance from the begin point to the closest point to it on the edge + int closest_idx = std::get<2>(closest); + double distance_along_edge = 0; + for (int idx = begin_index + 1; idx <= closest_idx; ++idx) { + distance_along_edge += shape[idx].Distance(shape[idx - 1]); + } + distance_along_edge += shape[closest_idx].Distance(std::get<0>(closest)); + // the overall distance shouldn't be larger than edge length + distance_along_edge = std::min(distance_along_edge, static_cast(edge->length())); + landmark->set_distance(distance_along_edge); + // check which side of the edge the landmark is on + // quirks of the ClosestPoint function + bool is_right = closest_idx == (int)shape.size() - 1 + ? landmark_point.IsLeft(shape[closest_idx - 1], shape[closest_idx]) < 0 + : landmark_point.IsLeft(shape[closest_idx], shape[closest_idx + 1]) < 0; + landmark->set_right(is_right); } } From 0c848128c7fe213f3af96e48501e61e39a451923 Mon Sep 17 00:00:00 2001 From: Ian Wagner Date: Tue, 24 Sep 2024 22:27:15 +0900 Subject: [PATCH 136/198] Loop match walk or snap fix (#4895) --- CHANGELOG.md | 1 + src/thor/route_matcher.cc | 29 ++++++++++++++++------------- test/mapmatch.cc | 27 +++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f02cadfc9..ac2d138673 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * **Bug Fix** * FIXED: All logging in `valhalla_export_edges` now goes to stderr [#4892](https://github.com/valhalla/valhalla/pull/4892) * FIXED: Iterate over only `kLandmark` tagged values in `AddLandmarks()` [#4873](https://github.com/valhalla/valhalla/pull/4873) + * FIXED: `walk_or_snap` mode edge case with loop routes [#4895](https://github.com/valhalla/valhalla/pull/4895) * **Enhancement** * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) * ADDED: Add option `edge.forward` to trace attributes [#4876](https://github.com/valhalla/valhalla/pull/4876) diff --git a/src/thor/route_matcher.cc b/src/thor/route_matcher.cc index 23fb1c5c23..7294fa6778 100644 --- a/src/thor/route_matcher.cc +++ b/src/thor/route_matcher.cc @@ -492,20 +492,23 @@ bool RouteMatcher::FormPath(const sif::mode_costing_t& mode_costing, index++; } - // Did not find the end of the origin edge. Check for trivial route on a single edge - for (const auto& end : end_nodes) { - if (end.second.first.graph_id() == edge.graph_id()) { - // Update the elapsed time based on edge cost - uint8_t flow_sources; - elapsed += mode_costing[static_cast(mode)]->EdgeCost(de, end_node_tile, time_info, - flow_sources) * - (end.second.first.percent_along() - edge.percent_along()); - if (options.use_timestamps()) - elapsed.secs = options.shape().rbegin()->time() - options.shape(0).time(); + // Look for trivial cases if we didn't bail based on checking more than the edge length + if (length <= de_length) { + // Did not find the end of the origin edge. Check for trivial route on a single edge + for (const auto& end : end_nodes) { + if (end.second.first.graph_id() == edge.graph_id()) { + // Update the elapsed time based on edge cost + uint8_t flow_sources; + elapsed += mode_costing[static_cast(mode)]->EdgeCost(de, end_node_tile, time_info, + flow_sources) * + (end.second.first.percent_along() - edge.percent_along()); + if (options.use_timestamps()) + elapsed.secs = options.shape().rbegin()->time() - options.shape(0).time(); - // Add end edge - path_infos.emplace_back(mode, elapsed, GraphId(edge.graph_id()), 0, 0.f, -1); - return true; + // Add end edge + path_infos.emplace_back(mode, elapsed, GraphId(edge.graph_id()), 0, 0.f, -1); + return true; + } } } } diff --git a/test/mapmatch.cc b/test/mapmatch.cc index ea61dfae03..d0c5b4e4e4 100644 --- a/test/mapmatch.cc +++ b/test/mapmatch.cc @@ -1232,6 +1232,33 @@ TEST(Mapmatch, test_loop_matching) { } } +TEST(Mapmatch, test_loop_matching_walk_or_snap) { + // The walk_or_snap case sometimes incorrectly identifies trivial cases where it can optimize away. + // This tests loops which are not trivial. + std::vector test_cases = { + // Simple loop around a block; incorrectly identified as trivial in such a way that + // it caused an exception in TripLegBuilder (no maneuvers) + R"({"shape_match":"walk_or_snap", "shape":[ + {"lat": 52.11870746741026, "lon": 5.117783546447755, "type": "break", "node_snap_tolerance": 0}, + {"lat": 52.11905661947785, "lon": 5.118234157562257, "type": "via", "node_snap_tolerance": 0}, + {"lat": 52.11934977334695, "lon": 5.117751359939576, "type": "via", "node_snap_tolerance": 0}, + {"lat": 52.118852398789194, "lon": 5.116935968399049, "type": "via", "node_snap_tolerance": 0}, + {"lat": 52.11852959643748, "lon": 5.117445588111878, "type": "via", "node_snap_tolerance": 0}, + {"lat": 52.11870746741026, "lon": 5.117783546447755, "type": "break", "node_snap_tolerance": 0}], + "costing":"auto"})", + }; + + api_tester tester; + for (size_t i = 0; i < test_cases.size(); ++i) { + // Verify that the map_snap output matches the walk_or_snap output. + // This approach is inspired by the above. + auto map_snap_case = R"({"shape_match":"map_snap)" + test_cases[i].substr(28); + auto map_snapped = tester.match(map_snap_case); + auto matched = tester.match(test_cases[i]); + compare_results(map_snapped, matched); + } +} + TEST(Mapmatch, test_intersection_matching) { std::vector test_cases = { R"({"shape":[ From be5a4d5cafbef42a1ef8f87f7561eae214b155a7 Mon Sep 17 00:00:00 2001 From: Stepan Kizim <10885920+kinkard@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:45:22 +0200 Subject: [PATCH 137/198] fix: `-Wdefaulted-function-deleted` warning/error in `NarrativeBuilder` (#4877) --- CHANGELOG.md | 1 + valhalla/odin/narrativebuilder.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac2d138673..60b1fcaa46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * FIXED: All logging in `valhalla_export_edges` now goes to stderr [#4892](https://github.com/valhalla/valhalla/pull/4892) * FIXED: Iterate over only `kLandmark` tagged values in `AddLandmarks()` [#4873](https://github.com/valhalla/valhalla/pull/4873) * FIXED: `walk_or_snap` mode edge case with loop routes [#4895](https://github.com/valhalla/valhalla/pull/4895) + * FIXED: `-Wdefaulted-function-deleted` compilation warning/error in `NarrativeBuilder` [#4877](https://github.com/valhalla/valhalla/pull/4877) * **Enhancement** * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) * ADDED: Add option `edge.forward` to trace attributes [#4876](https://github.com/valhalla/valhalla/pull/4876) diff --git a/valhalla/odin/narrativebuilder.h b/valhalla/odin/narrativebuilder.h index 9ed444e3dd..42c4bb3f5d 100644 --- a/valhalla/odin/narrativebuilder.h +++ b/valhalla/odin/narrativebuilder.h @@ -27,10 +27,10 @@ class NarrativeBuilder { virtual ~NarrativeBuilder() = default; NarrativeBuilder(NarrativeBuilder&&) = default; - NarrativeBuilder& operator=(NarrativeBuilder&&) = default; + NarrativeBuilder& operator=(NarrativeBuilder&&) = delete; NarrativeBuilder(const NarrativeBuilder&) = default; - NarrativeBuilder& operator=(const NarrativeBuilder&) = default; + NarrativeBuilder& operator=(const NarrativeBuilder&) = delete; void Build(std::list& maneuvers); From a01d80ce737083a733ab203c44fbadb3642fe9b7 Mon Sep 17 00:00:00 2001 From: Chris Fischer Date: Mon, 30 Sep 2024 15:41:22 -0400 Subject: [PATCH 138/198] Fix bullet point in date_time request option reference (#4914) --- docs/docs/api/turn-by-turn/api-reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 8ea2d1e79a..42f84d81b4 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -347,7 +347,7 @@ For example a bus request with the result in Spanish using the OSRM (Open Source | :------------------ | :----------- | | `exclude_locations` | A set of locations to exclude or avoid within a route can be specified using a JSON array of avoid_locations. The avoid_locations have the same format as the locations list. At a minimum each avoid location must include latitude and longitude. The avoid_locations are mapped to the closest road or roads and these roads are excluded from the route path computation.| | `exclude_polygons` | One or multiple exterior rings of polygons in the form of nested JSON arrays, e.g. `[[[lon1, lat1], [lon2,lat2]],[[lon1,lat1],[lon2,lat2]]]`. Roads intersecting these rings will be avoided during path finding. If you only need to avoid a few specific roads, it's **much** more efficient to use `exclude_locations`. Valhalla will close open rings (i.e. copy the first coordinate to the last position).| -| `date_time` | This is the local date and time at the location.
  • `type`
    • 0 - Current departure time.
    • 1 - Specified departure time
    • 2 - Specified arrival time. Not yet implemented for multimodal costing method.
    • 3 - Invariant specified time. Time does not vary over the course of the path. Not implemented for multimodal or bike share routing
  • `value` - the date and time is specified in ISO 8601 format (YYYY-MM-DDThh:mm) in the local time zone of departure or arrival. For example "2016-07-03T08:06"

| +| `date_time` | This is the local date and time at the location.
  • `type`
    • 0 - Current departure time.
    • 1 - Specified departure time
    • 2 - Specified arrival time. Not yet implemented for multimodal costing method.
    • 3 - Invariant specified time. Time does not vary over the course of the path. Not implemented for multimodal or bike share routing
  • `value` - the date and time is specified in ISO 8601 format (YYYY-MM-DDThh:mm) in the local time zone of departure or arrival. For example "2016-07-03T08:06"

| | `elevation_interval` | Elevation interval (meters) for requesting elevation along the route. Valhalla data must have been generated with elevation data. If no `elevation_interval` is specified, no elevation will be returned for the route. An elevation interval of 30 meters is recommended when elevation along the route is desired, matching the default data source's resolution. | | `id` | Name your route request. If `id` is specified, the naming will be sent thru to the response. | | `linear_references` | When present and `true`, the successful `route` response will include a key `linear_references`. Its value is an array of base64-encoded [OpenLR location references][openlr], one for each graph edge of the road network matched by the input trace. | From e3118f0d972ff646b67784783659e3186609d35b Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Tue, 8 Oct 2024 13:28:03 +0200 Subject: [PATCH 139/198] Support multiple levels and level ranges (#4879) --- CHANGELOG.md | 1 + docs/docs/api/locate/api-reference.md | 10 +- lua/graph.lua | 5 - proto/common.proto | 2 + src/baldr/edgeinfo.cc | 120 ++++++++++++-- src/mjolnir/osmway.cc | 114 ++++++++++++- src/odin/enhancedtrippath.cc | 27 +++- src/odin/maneuversbuilder.cc | 18 ++- test/gurka/test_indoor.cc | 170 +++++++++++++++++--- test/scripts/test_valhalla_build_extract.py | 4 +- valhalla/baldr/edgeinfo.h | 34 +++- valhalla/baldr/graphconstants.h | 9 +- valhalla/odin/enhancedtrippath.h | 2 +- 13 files changed, 458 insertions(+), 58 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60b1fcaa46..9c77ea6397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) * ADDED: Add option `edge.forward` to trace attributes [#4876](https://github.com/valhalla/valhalla/pull/4876) * ADDED: Provide conditional speed limits from "maxspeed:conditional" in `/locate` and proto `/route` responses [#4851](https://github.com/valhalla/valhalla/pull/4851) + * ADDED: Support multiple levels and level ranges [#4879](https://github.com/valhalla/valhalla/pull/4879) ## Release Date: 2024-08-21 Valhalla 3.5.0 * **Removed** diff --git a/docs/docs/api/locate/api-reference.md b/docs/docs/api/locate/api-reference.md index 17bfe07717..a97c27da24 100644 --- a/docs/docs/api/locate/api-reference.md +++ b/docs/docs/api/locate/api-reference.md @@ -334,8 +334,16 @@ Here are some sample results with `verbose` set to `true`: ``` ### Attribute Descriptions for Responses +#### Edge +##### Verbose -TODO: +| Key | Description | +| :------------------ | :----------- | +| `access_restrictions` | A list of access restrictions that apply to the edge. Each entry contains information about its access mode mask, value, and more | +| `edge_info` | Contains information stored in the edge's `EdgeInfo` struct | +| `edge_info.conditional_speed_limits` | The key describes the time window during which the speed limit applies, the value is the limit | +| `edge_info.speed_limit` | The edge's speed limit | +| `edge_info.levels` | An array containing the edge's levels as derived from the `level=*` tag. Values are either numeric, or another array containing two elements, which denote the start and end of a range (inclusive) | ### HTTP status codes and error messages diff --git a/lua/graph.lua b/lua/graph.lua index 1587725c5b..37dc30805f 100644 --- a/lua/graph.lua +++ b/lua/graph.lua @@ -1870,11 +1870,6 @@ function filter_tags_generic(kv) kv["bike_local_ref"] = lref kv["bike_network_mask"] = bike_mask - -- turn semicolon into colon due to challenges to store ";" in string - if kv["level"] ~= nil then - kv["level"] = kv["level"]:gsub(";", ":") - end - -- Explicitly turn off access for construction type. It's done for backward compatibility -- of valhalla tiles and valhalla routing. In case we allow non-zero access then older -- versions of router will work with new tiles incorrectly. They would start to route diff --git a/proto/common.proto b/proto/common.proto index 649d8a8c74..f6efd5bdb5 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -344,6 +344,8 @@ message TaggedValue { kLevel = 4; kLevelRef = 5; kLandmark = 6; + kConditionalSpeedLimits = 7; + kLevels = 8; kTunnel = 49; kBridge = 50; } diff --git a/src/baldr/edgeinfo.cc b/src/baldr/edgeinfo.cc index 91bcdfe76b..4c69d00f1b 100644 --- a/src/baldr/edgeinfo.cc +++ b/src/baldr/edgeinfo.cc @@ -28,6 +28,26 @@ json::ArrayPtr names_json(const std::vector& names) { return a; } +/** + * Parse a 7-bit encoded varint. + * + * @param encoded the encoded c string + * @param i value to store the varint size in + * + */ +int32_t parse_varint(const char*& encoded) { + int32_t byte = 0, shift = 0, result = 0; + + while (byte & 0x80 || shift == 0) { + byte = int32_t(*encoded); + result |= (byte & 0x7f) << shift; + shift += 7; + ++encoded; + } + + return (result & 1 ? ~result : result) >> 1; +} + // per tag parser. each returned string includes the leading TaggedValue. std::vector parse_tagged_value(const char* ptr) { switch (static_cast(ptr[0])) { @@ -43,6 +63,11 @@ std::vector parse_tagged_value(const char* ptr) { size_t landmark_size = landmark_name.size() + 10; return {std::string(ptr, landmark_size)}; } + case TaggedValue::kLevels: { + auto start = ptr + 1; + int size = static_cast(parse_varint(start)); + return {std::string(ptr, (start + size) - ptr)}; + } case TaggedValue::kConditionalSpeedLimits: { return {std::string(ptr, 1 + sizeof(ConditionalSpeedLimit))}; } @@ -57,6 +82,47 @@ std::vector parse_tagged_value(const char* ptr) { namespace valhalla { namespace baldr { +std::pair>, uint32_t> decode_levels(const std::string& encoded) { + uint32_t precision = 0; + std::vector> decoded; + decoded.reserve(4); + + auto ptr = encoded.data(); + + // first varint is the size + auto size = parse_varint(ptr); + // we keep track of where the string ends + auto end = ptr + size; + // second varint is the precision + if (int32_t prec_power = parse_varint(ptr) > 0) + precision = pow(10, prec_power); + + // keep track of whether we're looking at the first or second value + // of a contiguous block + bool prev = false; + + while (ptr != end) { + int32_t val = parse_varint(ptr); + if (val == kLevelRangeSeparator) { + prev = false; + continue; + } + float f = precision == 0 ? static_cast(val) + : static_cast(val) / static_cast(precision); + if (!prev) { + // first value + // (temporarily) set to the same value to indicate a single number + decoded.emplace_back(std::pair{f, f}); + prev = true; + } else { + // we found a second value + decoded.back().second = f; + } + } + + return {decoded, static_cast(precision)}; +} + EdgeInfo::EdgeInfo(char* ptr, const char* names_list, const size_t names_list_length) : names_list_(names_list), names_list_length_(names_list_length) { @@ -415,24 +481,38 @@ int8_t EdgeInfo::layer() const { return static_cast(value.front()); } -std::string EdgeInfo::level() const { +std::pair>, uint32_t> EdgeInfo::levels() const { const auto& tags = GetTags(); - auto itr = tags.find(TaggedValue::kLevel); + auto itr = tags.find(TaggedValue::kLevels); if (itr == tags.end()) { - return ""; + return {}; } - const std::string& value = itr->second; - return value; + try { + return decode_levels(itr->second); + } catch (...) { throw std::runtime_error("failed to decode levels"); }; } -std::string EdgeInfo::level_ref() const { +bool EdgeInfo::includes_level(float lvl) const { const auto& tags = GetTags(); - auto itr = tags.find(TaggedValue::kLevelRef); + auto itr = tags.find(TaggedValue::kLevels); if (itr == tags.end()) { - return ""; + return false; + } + auto decoded = std::get<0>(decode_levels(itr->second)); + auto lower = std::lower_bound(decoded.cbegin(), decoded.cend(), lvl, + [&](const decltype(decoded)::value_type& val, float lvl) { + return val.second < lvl; + }); + return lower != decoded.end() && lower->first <= lvl && lvl <= lower->second; +} + +std::vector EdgeInfo::level_ref() const { + const auto& tags = GetTags(); + std::vector values; + for (auto [itr, range_end] = tags.equal_range(TaggedValue::kLevelRef); itr != range_end; ++itr) { + values.emplace_back(itr->second); } - const std::string& value = itr->second; - return value; + return values; } json::MapPtr EdgeInfo::json() const { @@ -471,6 +551,26 @@ json::MapPtr EdgeInfo::json() const { break; case TaggedValue::kLandmark: break; + case TaggedValue::kLevels: { + json::ArrayPtr levels = json::array({}); + std::vector> decoded; + uint32_t precision; + std::tie(decoded, precision) = decode_levels(value); + for (auto& range : decoded) { + if (range.first == range.second) { + // single number + levels->emplace_back(json::fixed_t{range.first, precision}); + } else { + // range + json::ArrayPtr level = json::array({}); + level->emplace_back(json::fixed_t{range.first, precision}); + level->emplace_back(json::fixed_t{range.second, precision}); + levels->emplace_back(level); + } + } + edge_info->emplace("levels", levels); + break; + } case TaggedValue::kConditionalSpeedLimits: { if (!conditional_speed_limits) { conditional_speed_limits = json::map({}); diff --git a/src/mjolnir/osmway.cc b/src/mjolnir/osmway.cc index 45db3aade7..7f1c10eff4 100644 --- a/src/mjolnir/osmway.cc +++ b/src/mjolnir/osmway.cc @@ -1,6 +1,7 @@ #include "mjolnir/osmway.h" #include "baldr/edgeinfo.h" #include "mjolnir/util.h" +#include "regex" #include "midgard/logging.h" #include @@ -13,6 +14,37 @@ constexpr uint32_t kMaxNodesPerWay = 65535; constexpr uint8_t kUnlimitedOSMSpeed = std::numeric_limits::max(); constexpr float kMaxOSMSpeed = 140.0f; +const std::regex kFloatRegex("\\d+\\.(\\d+)"); + +/** + * For levels we use 7-bit varint encoding, with arbitrary precision stored separately. + * + * @param lvl the level to encode + * @param precision the precision with which to encode the level value + * + * @return a string containing the encoded value + */ +std::string encode_level(const float lvl, const int precision = 0) { + std::string encoded; + // most of the time precision will be zero and level values will be lower + // than 4191 (even in the case of higher precision) + encoded.reserve(2); + + int val = static_cast(lvl * pow(10, precision)); + + val = val < 0 ? ~(*reinterpret_cast(&val) << 1) : val << 1; + // we take 7 bits of this at a time + while (val > 0x7f) { + // marking the most significant bit means there are more pieces to come + int next_value = (0x80 | (val & 0x7f)); + encoded.push_back(static_cast(next_value)); + val >>= 7; + } + // write the last chunk + encoded.push_back(static_cast(val & 0x7f)); + return encoded; +} + } // namespace namespace valhalla { @@ -1067,10 +1099,88 @@ void OSMWay::GetTaggedValues(const UniqueNames& name_offset_map, if (level_index_ != 0) { // level + + std::vector> values; + const char dash = '-'; auto tokens = GetTagTokens(name_offset_map.name(level_index_)); - for (const auto& t : tokens) { - names.emplace_back(encode_tag(TaggedValue::kLevel) + t); + + // we store the precision once for all values + // so we keep track of the max + int precision = 0; + for (size_t i = 0; i < tokens.size(); ++i) { + const auto token = tokens[i]; + auto dash_pos = token.find(dash); + std::pair range; + if (dash_pos != std::string::npos && dash_pos != 0) { + // we're dealing with a range + std::vector nums; + boost::algorithm::split( + nums, token, [&](const char c) { return c == dash; }, + boost::algorithm::token_compress_on); + + try { + std::smatch match; + for (const auto& num : nums) { + if (std::regex_search(num, match, kFloatRegex)) { + precision = std::max(precision, static_cast(match[1].str().size())); + } + } + range.first = std::stof(nums[0]); + range.second = std::stof(nums[1]); + } catch (...) { + LOG_ERROR("Invalid level: " + token + "; way_id " + std::to_string(osmwayid_)); + continue; + } + + if (range.first > range.second) { + LOG_ERROR("Invalid level range, " + std::to_string(range.first) + " - " + + std::to_string(range.second) + "; way_id " + std::to_string(osmwayid_)); + continue; + } + + } else { // we have a number + try { + std::smatch match; + if (std::regex_search(token, match, kFloatRegex)) { + precision = std::max(precision, static_cast(match[1].str().size())); + } + range.first = std::stof(token); + range.second = range.first; + } catch (...) { + LOG_ERROR("Invalid level: " + token + "; way_id " + std::to_string(osmwayid_)); + continue; + } + } + values.emplace_back(range); } + std::sort(values.begin(), values.end(), + [](auto& left, auto& right) { return left.first < right.first; }); + + std::string levels_encoded; + // 2 bytes per value and 2 for each sentinel with some headroom should be good + levels_encoded.reserve(values.size() * 4); + + // sentinel value that marks discontinuity + std::string sep = encode_level(kLevelRangeSeparator); + + auto last_it = --values.end(); + auto prec_power = pow(10, precision); + for (auto it = values.begin(); it != values.end(); ++it) { + auto& range = *it; + levels_encoded.append(encode_level(range.first * prec_power)); + if (range.first != range.second) + levels_encoded.append(encode_level(range.second * prec_power)); + + if (it != last_it) + levels_encoded.append(sep); + } + + std::string precision_enc = encode_level(static_cast(precision)); + std::string encoded = + encode_tag(TaggedValue::kLevels) + + encode_level(static_cast(levels_encoded.size() + precision_enc.size())) + + precision_enc + levels_encoded; + names.emplace_back(encoded); } if (level_ref_index_ != 0) { // level:ref diff --git a/src/odin/enhancedtrippath.cc b/src/odin/enhancedtrippath.cc index d93017c971..0249905ccc 100644 --- a/src/odin/enhancedtrippath.cc +++ b/src/odin/enhancedtrippath.cc @@ -1,6 +1,7 @@ #include #include +#include "baldr/edgeinfo.h" #include "baldr/turn.h" #include "baldr/turnlanes.h" #include "midgard/constants.h" @@ -523,19 +524,31 @@ std::vector> EnhancedTripLeg_Edge::GetNameList() co return name_list; } -std::string EnhancedTripLeg_Edge::GetLevelRef() const { - std::string level_ref; +std::vector EnhancedTripLeg_Edge::GetLevelRef() const { + std::vector level_refs; + std::vector levels; + + // try to get level_refs, else create some from levels as fallback if (!tagged_value().empty()) { for (int t = 0; t < tagged_value().size(); ++t) { if (tagged_value().Get(t).type() == TaggedValue_Type_kLevelRef) { - level_ref = tagged_value().Get(t).value(); - break; - } else if (tagged_value().Get(t).type() == TaggedValue_Type_kLevel) { - level_ref = "Level " + tagged_value().Get(t).value(); + level_refs.emplace_back(tagged_value().Get(t).value()); + } else if (tagged_value().Get(t).type() == TaggedValue_Type_kLevels) { + // parse varint encoded levels, we're only interested in single + // level values though + const auto& encoded = tagged_value().Get(t).value(); + std::vector> decoded; + uint32_t precision; + std::tie(decoded, precision) = baldr::decode_levels(encoded); + if (decoded.size() == 1 && decoded[0].first == decoded[0].second) { + std::stringstream ss; + ss << std::fixed << std::setprecision(precision) << decoded[0].first; + levels.emplace_back("Level " + ss.str()); + } } } } - return level_ref; + return level_refs.empty() ? levels : level_refs; } float EnhancedTripLeg_Edge::GetLength(const Options::Units& units) { diff --git a/src/odin/maneuversbuilder.cc b/src/odin/maneuversbuilder.cc index 4c306d1eb4..f5648dd9cd 100644 --- a/src/odin/maneuversbuilder.cc +++ b/src/odin/maneuversbuilder.cc @@ -1157,7 +1157,11 @@ void ManeuversBuilder::InitializeManeuver(Maneuver& maneuver, int node_index) { // Set the end level ref if (curr_edge && !curr_edge->GetLevelRef().empty()) { - maneuver.set_end_level_ref(curr_edge->GetLevelRef()); + if (curr_edge->GetLevelRef().size() > 1) { + maneuver.set_end_level_ref(""); + } else { + maneuver.set_end_level_ref(curr_edge->GetLevelRef()[0]); + } } // Elevator @@ -1452,7 +1456,11 @@ void ManeuversBuilder::FinalizeManeuver(Maneuver& maneuver, int node_index) { maneuver.set_elevator(true); // Set the end level ref if (curr_edge && !curr_edge->GetLevelRef().empty()) { - maneuver.set_end_level_ref(curr_edge->GetLevelRef()); + if (curr_edge->GetLevelRef().size() > 1) { + maneuver.set_end_level_ref(""); + } else { + maneuver.set_end_level_ref(curr_edge->GetLevelRef()[0]); + } } } @@ -4006,10 +4014,12 @@ void ManeuversBuilder::AddLandmarksFromTripLegToManeuvers(std::list& m for (auto node = man->begin_node_index(); node < man->end_node_index(); ++node) { auto curr_edge = trip_path_->GetCurrEdge(node); - // multipoint routes with `through` or `via` types can have consecutive copies of the same edge + // multipoint routes with `through` or `via` types can have consecutive copies of the same + // edge if (curr_edge != trip_path_->GetCurrEdge(node + 1)) { // every time we are about to leave an edge, collect all landmarks in it - // and reset distance of each landmark to the distance from the landmark to the maneuver point + // and reset distance of each landmark to the distance from the landmark to the maneuver + // point auto curr_landmarks = curr_edge->landmarks(); for (auto& l : curr_landmarks) { double new_distance = diff --git a/test/gurka/test_indoor.cc b/test/gurka/test_indoor.cc index eaa2a6e60e..24350937ac 100644 --- a/test/gurka/test_indoor.cc +++ b/test/gurka/test_indoor.cc @@ -1,4 +1,7 @@ #include "gurka.h" +#include "mjolnir/osmway.h" +#include "test.h" +#include #include #if !defined(VALHALLA_SOURCE_DIR) @@ -6,8 +9,17 @@ #endif using namespace valhalla; +using namespace mjolnir; const std::unordered_map build_config{{}}; +struct range_t { + range_t(float s, float e) : start(s), end(e){}; + range_t(float s) : start(s), end(s){}; + + float start; + float end; +}; + class Indoor : public ::testing::Test { protected: static gurka::map map; @@ -54,7 +66,7 @@ class Indoor : public ::testing::Test { {"DN", {{"highway", "steps"}}}, {"NO", {{"highway", "footway"}}}, - {"Cx", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "1;2"}}}, + {"Cx", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "-1;0-2"}}}, {"xy", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "2;3"}}}, {"yJ", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "3"}}}, }; @@ -117,26 +129,6 @@ TEST_F(Indoor, DirectedEdge) { EXPECT_TRUE(directededge->reverseaccess() & baldr::kPedestrianAccess); } -TEST_F(Indoor, EdgeInfo) { - baldr::GraphReader graphreader(map.config.get_child("mjolnir")); - - auto get_level = [&](auto from, auto to) { - auto edgeId = std::get<0>(gurka::findEdgeByNodes(graphreader, layout, from, to)); - return graphreader.edgeinfo(edgeId).level(); - }; - EXPECT_EQ(get_level("A", "B"), "0"); - EXPECT_EQ(get_level("B", "C"), "0:1"); - EXPECT_EQ(get_level("C", "F"), "1"); - - auto get_level_ref = [&](auto from, auto to) { - auto edge_id = std::get<0>(gurka::findEdgeByNodes(graphreader, layout, from, to)); - return graphreader.edgeinfo(edge_id).level_ref(); - }; - EXPECT_EQ(get_level_ref("A", "B"), "Parking"); - EXPECT_EQ(get_level_ref("B", "C"), ""); - EXPECT_EQ(get_level_ref("C", "F"), "Lobby"); -} - TEST_F(Indoor, ElevatorPenalty) { // first route should take the elevator node auto result = gurka::do_action(valhalla::Options::route, map, {"E", "J"}, "pedestrian"); @@ -280,4 +272,140 @@ TEST_F(Indoor, CombineStepsManeuvers) { gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, "Take the stairs to Level 3.", "", "", "", ""); +} + +/****************************************************************************************/ + +class Levels : public ::testing::Test { +protected: + static gurka::map map; + static std::string ascii_map; + static gurka::nodelayout layout; + + static void SetUpTestSuite() { + constexpr double gridsize_metres = 100; + ascii_map = R"( + A-z-B-y-C-x-D-w-E-v-F-u-G-t-H-s-I-r-J + )"; + + const gurka::ways ways = { + {"AB", + {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "1"}, {"level:ref", "Parking"}}}, + {"BC", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "0"}, {"level:ref", "Lobby"}}}, + {"CD", + {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "2"}, {"level:ref", "2nd Floor"}}}, + {"DE", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "100"}}}, + {"EF", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "12;14"}}}, + {"FG", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "-1;0"}}}, + {"GH", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "85.5;12"}}}, + {"HI", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "-2;-1;0-3"}}}, + {"IJ", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "0-2.5"}}}, + }; + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres); + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_levels"); + } +}; + +gurka::map Levels::map = {}; +std::string Levels::ascii_map = {}; +gurka::nodelayout Levels::layout = {}; + +TEST_F(Levels, EdgeInfoIncludes) { + + baldr::GraphReader graphreader(map.config.get_child("mjolnir")); + + std::vector, std::vector>> values = { + {{"A", "B"}, {1}}, + {{"B", "C"}, {0}}, + {{"C", "D"}, {2}}, + {{"D", "E"}, {100}}, + {{"E", "F"}, {12, 14}}, + {{"F", "G"}, {-1, 0}}, + {{"G", "H"}, {85.5, 12}}, + {{"H", "I"}, {-2, -1, 0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5, 2.75, 3}}, + {{"I", "J"}, {0, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.25, 2.5}}, + }; + + for (const auto& [way, levels] : values) { + + auto edgeId = std::get<0>(gurka::findEdgeByNodes(graphreader, layout, way[0], way[1])); + for (float i = -2.0f; i <= 100.0f; i = i + 0.25f) { + bool expected_includes_level = std::find(levels.begin(), levels.end(), i) != levels.end(); + bool includes = graphreader.edgeinfo(edgeId).includes_level(i); + EXPECT_EQ(includes, expected_includes_level) << "Failing level " << i << "\n"; + } + } +} + +TEST_F(Levels, EdgeInfoJson) { + + auto graphreader = test::make_clean_graphreader(map.config.get_child("mjolnir")); + std::string json; + std::vector locs = { + "z", // AB + "y", // BC + "x", // CD + "w", // DE + "v", // EF + "u", // FG + "t", // GH + "s", // HI + "r", // IJ + }; + auto result = + gurka::do_action(valhalla::Options::locate, map, locs, "pedestrian", {}, graphreader, &json); + + std::unordered_map> expected_levels_map = { + {"z", {{1.f}}}, + {"y", {{0.f}}}, + {"x", {{2.f}}}, + {"w", {{100.f}}}, + {"v", {{12.f}, {14.f}}}, + {"u", {{-1.f}, {0.f}}}, + {"t", {{12.0f}, {85.5f}}}, + {"s", {{-2.f}, {-1.f}, {0.f, 3.f}}}, + {"r", {{0.f, 2.5f}}}, + }; + rapidjson::Document response; + response.Parse(json); + + if (response.HasParseError()) + throw std::runtime_error("bad json response"); + + ASSERT_EQ(response.GetArray().Size(), 9); + + for (size_t i = 0; i < locs.size(); ++i) { + auto name = locs[i]; + auto edges = rapidjson::Pointer("/" + std::to_string(i) + "/edges").Get(response)->GetArray(); + ASSERT_EQ(edges.Size(), 2); + auto expected_levels = expected_levels_map[name]; + for (size_t edge_ix = 0; edge_ix < 2; ++edge_ix) { + auto actual_levels = rapidjson::Pointer("/" + std::to_string(i) + "/edges/" + + std::to_string(edge_ix) + "/edge_info/levels") + .Get(response) + ->GetArray(); + ASSERT_EQ(actual_levels.Size(), expected_levels.size()); + for (size_t j = 0; j < expected_levels.size(); ++j) { + auto expected_level = expected_levels[j]; + auto ptr = rapidjson::Pointer("/" + std::to_string(i) + "/edges/" + std::to_string(edge_ix) + + "/edge_info/levels/" + std::to_string(j)) + .Get(response); + if (expected_level.start == expected_level.end) { + EXPECT_EQ(expected_level.start, ptr->GetFloat()); + continue; + } + auto actual_level = ptr->GetArray(); + ASSERT_EQ(actual_level.Size(), 2); + for (size_t k = 0; k < 2; ++k) { + auto expected_level_value = k == 0 ? expected_level.start : expected_level.end; + auto actual_level_value = + rapidjson::Pointer("/" + std::to_string(i) + "/edges/" + std::to_string(edge_ix) + + "/edge_info/levels/" + std::to_string(j) + "/" + std::to_string(k)) + .Get(response) + ->GetFloat(); + EXPECT_EQ(actual_level_value, expected_level_value); + } + } + } + } } \ No newline at end of file diff --git a/test/scripts/test_valhalla_build_extract.py b/test/scripts/test_valhalla_build_extract.py index ae62f3d903..f1fc30d87f 100644 --- a/test/scripts/test_valhalla_build_extract.py +++ b/test/scripts/test_valhalla_build_extract.py @@ -151,7 +151,7 @@ def test_create_extracts(self): tile_count = len(tile_resolver.matched_paths) # test that the index has the right offsets/sizes - exp_tuples = ((2560, 25568, 296768), (301056, 410441, 665624), (968704, 6549282, 6137080)) + exp_tuples = ((2560, 25568, 296768), (301056, 410441, 665624), (968704, 6549282, 6137088)) self.check_tar(EXTRACT_PATH, exp_tuples, tile_count * INDEX_BIN_SIZE) # same for traffic.tar exp_tuples = ((1536, 25568, 25856), (28160, 410441, 64400), (93184, 6549282, 604608)) @@ -159,7 +159,7 @@ def test_create_extracts(self): # tests the implementation using the tile_dir new_tile_extract = TILE_PATH.joinpath("tiles2.tar") - exp_tuples = ((2560, 25568, 296768), (301056, 410441, 665624), (968704, 6549282, 6137080)) + exp_tuples = ((2560, 25568, 296768), (301056, 410441, 665624), (968704, 6549282, 6137088)) tile_resolver = TileResolver(EXTRACT_PATH) tile_resolver.matched_paths = tile_resolver.normalized_tile_paths valhalla_build_extract.create_extracts(config, True, tile_resolver, new_tile_extract) diff --git a/valhalla/baldr/edgeinfo.h b/valhalla/baldr/edgeinfo.h index 2870d45612..6b91d32f28 100644 --- a/valhalla/baldr/edgeinfo.h +++ b/valhalla/baldr/edgeinfo.h @@ -73,6 +73,26 @@ struct linguistic_text_header_t { uint32_t DO_NOT_USE_ : 8; // DONT EVER USE THIS WE DON'T ACTUALLY STORE IT IN THE TEXT LIST }; +/** + * Decode the level information encoded as variable length, variable precision numbers. + * + * The first varint denotes the string size, to avoid the value 0 from being interpreted + * as a null character. The second varint denotes the precision to apply to all values + * except for the sentinel value used as a separator of continuous ranges. + * + * + * The returned array includes level values and sentinel values. Ex.: + * "12;14" translates to {12, 1048575, 14} + * "0-12;14" translates to {0, 12, 1048575, 14} + * + * @param encoded the encoded varint array + * + * @return a tuple that contains + * a) the decoded levels array and + * b) the precision used + */ +std::pair>, uint32_t> decode_levels(const std::string& encoded); + /** * Edge information not required in shortest path algorithm and is * common among the 2 directions. @@ -266,18 +286,24 @@ class EdgeInfo { int8_t layer() const; /** - * Get level of the edge. + * Get levels of the edge. * @see https://wiki.openstreetmap.org/wiki/Key:level - * @return layer index of the edge + * @return a pair where the first member is a vector of contiguous level ranges (inclusive) and + * the the second member is the max precision found on any of the level tokens. + */ + std::pair>, uint32_t> levels() const; + + /** + * Convenience method that checks whether the edge connects the passed level. */ + bool includes_level(float lvl) const; - std::string level() const; /** * Get layer:ref of the edge. * @see https://wiki.openstreetmap.org/wiki/Key:level:ref * @return layer index of the edge */ - std::string level_ref() const; + std::vector level_ref() const; /** * Returns json representing this object diff --git a/valhalla/baldr/graphconstants.h b/valhalla/baldr/graphconstants.h index 076fb97730..4a4f78eda1 100644 --- a/valhalla/baldr/graphconstants.h +++ b/valhalla/baldr/graphconstants.h @@ -209,6 +209,12 @@ constexpr uint32_t kMaxAddedTime = 255; // this is the minimum we support, i.e. -500 m would result in "no elevation" constexpr float kNoElevationData = -500.0f; +// (building) level constants +// highest 3-byte value +constexpr float kLevelRangeSeparator = 1048575.0f; +constexpr float kMinLevel = std::numeric_limits::min(); +constexpr float kMaxLevel = std::numeric_limits::max(); + // Node types. enum class NodeType : uint8_t { kStreetIntersection = 0, // Regular intersection of 2 roads @@ -382,10 +388,11 @@ enum class TaggedValue : uint8_t { // must start at 1 due to nulls kLayer = 1, kLinguistic = 2, kBssInfo = 3, - kLevel = 4, + kLevel = 4, // deprecated in favor of kLevels kLevelRef = 5, kLandmark = 6, kConditionalSpeedLimits = 7, + kLevels = 8, // we used to have bug when we encoded 1 and 2 as their ASCII codes, but not actual 1 and 2 values // see https://github.com/valhalla/valhalla/issues/3262 kTunnel = static_cast('1'), diff --git a/valhalla/odin/enhancedtrippath.h b/valhalla/odin/enhancedtrippath.h index 343c015a1f..888753abd6 100644 --- a/valhalla/odin/enhancedtrippath.h +++ b/valhalla/odin/enhancedtrippath.h @@ -422,7 +422,7 @@ class EnhancedTripLeg_Edge { std::vector> GetNameList() const; - std::string GetLevelRef() const; + std::vector GetLevelRef() const; float GetLength(const Options::Units& units); From da252176c6f8a198e5b0c4b97bb6f99d2cabf26e Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Tue, 8 Oct 2024 10:17:16 -0400 Subject: [PATCH 140/198] dont use static_cast for negative varint encoding (#4925) --- CHANGELOG.md | 1 + valhalla/midgard/encoded.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c77ea6397..059492fa0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * FIXED: Iterate over only `kLandmark` tagged values in `AddLandmarks()` [#4873](https://github.com/valhalla/valhalla/pull/4873) * FIXED: `walk_or_snap` mode edge case with loop routes [#4895](https://github.com/valhalla/valhalla/pull/4895) * FIXED: `-Wdefaulted-function-deleted` compilation warning/error in `NarrativeBuilder` [#4877](https://github.com/valhalla/valhalla/pull/4877) + * FIXED: For a long time we were potentially wrongly encoding varints by using `static_cast` vs `reinterpret_cast` [#4877]https://github.com/valhalla/valhalla/pull/4925 * **Enhancement** * CHANGED: voice instructions for OSRM serializer to work better in real-world environment [#4756](https://github.com/valhalla/valhalla/pull/4756) * ADDED: Add option `edge.forward` to trace attributes [#4876](https://github.com/valhalla/valhalla/pull/4876) diff --git a/valhalla/midgard/encoded.h b/valhalla/midgard/encoded.h index cdc955ba38..9df14bc7da 100644 --- a/valhalla/midgard/encoded.h +++ b/valhalla/midgard/encoded.h @@ -177,7 +177,7 @@ std::string encode(const container_t& points, const int precision = ENCODE_PRECI // handy lambda to turn an integer into an encoded string auto serialize = [&output](int number) { // move the bits left 1 position and flip all the bits if it was a negative number - number = number < 0 ? ~(static_cast(number) << 1) : (number << 1); + number = number < 0 ? ~(*reinterpret_cast(&number) << 1) : (number << 1); // write 5 bit chunks of the number while (number >= 0x20) { int nextValue = (0x20 | (number & 0x1f)) + 63; @@ -224,7 +224,7 @@ std::string encode7(const container_t& points, const int precision = ENCODE_PREC auto serialize = [&output](int number) { // get the sign bit down on the least significant end to // make the most significant bits mostly zeros - number = number < 0 ? ~(static_cast(number) << 1) : number << 1; + number = number < 0 ? ~(*reinterpret_cast(&number) << 1) : number << 1; // we take 7 bits of this at a time while (number > 0x7f) { // marking the most significant bit means there are more pieces to come From 7516a103509e99e4aeaf7ba16fa3431d8253436f Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 10 Oct 2024 04:10:18 +0200 Subject: [PATCH 141/198] remove conditional speeds temp file (#4928) --- src/mjolnir/osmdata.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mjolnir/osmdata.cc b/src/mjolnir/osmdata.cc index ce174476d4..59677983d0 100644 --- a/src/mjolnir/osmdata.cc +++ b/src/mjolnir/osmdata.cc @@ -723,6 +723,7 @@ void OSMData::cleanup_temp_files(const std::string& tile_dir) { remove_temp_file(tile_dir + lane_connectivity_file); remove_temp_file(tile_dir + pronunciation_file); remove_temp_file(tile_dir + language_file); + remove_temp_file(tile_dir + conditional_speed_limit_file); } } // namespace mjolnir From d377c8ace9ea88dfa989466258bf738b1080f22a Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Thu, 10 Oct 2024 07:07:06 -0400 Subject: [PATCH 142/198] prep for release (#4931) --- CHANGELOG.md | 5 +++++ valhalla/valhalla.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 059492fa0d..2a102bdeef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## UNRELEASED * **Removed** +* **Bug Fix** +* **Enhancement** + +## Release Date: 2024-10-10 Valhalla 3.5.1 +* **Removed** * **Bug Fix** * FIXED: All logging in `valhalla_export_edges` now goes to stderr [#4892](https://github.com/valhalla/valhalla/pull/4892) * FIXED: Iterate over only `kLandmark` tagged values in `AddLandmarks()` [#4873](https://github.com/valhalla/valhalla/pull/4873) diff --git a/valhalla/valhalla.h b/valhalla/valhalla.h index d584964915..c1cf3776f1 100644 --- a/valhalla/valhalla.h +++ b/valhalla/valhalla.h @@ -2,4 +2,4 @@ #define VALHALLA_VERSION_MAJOR 3 #define VALHALLA_VERSION_MINOR 5 -#define VALHALLA_VERSION_PATCH 0 +#define VALHALLA_VERSION_PATCH 1 From 1967e337a44decdb95a4f82a40a762c80d2aaaf8 Mon Sep 17 00:00:00 2001 From: Eike Send Date: Mon, 14 Oct 2024 09:13:43 +0200 Subject: [PATCH 143/198] Fix incremental_build_tiles script (#4909) --- CHANGELOG.md | 1 + scripts/incremental_build_tiles | 37 ++++----------------------------- 2 files changed, 5 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a102bdeef..5f8d81b685 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## UNRELEASED * **Removed** * **Bug Fix** + * FIXED: `incremental_build_tiles` script works again [#4909](https://github.com/valhalla/valhalla/pull/4909) * **Enhancement** ## Release Date: 2024-10-10 Valhalla 3.5.1 diff --git a/scripts/incremental_build_tiles b/scripts/incremental_build_tiles index f158213eed..fa37a11f2f 100755 --- a/scripts/incremental_build_tiles +++ b/scripts/incremental_build_tiles @@ -11,7 +11,7 @@ function usage() { } if [ -z "$2" ]; then - usage + usage fi config=$1 @@ -22,35 +22,6 @@ if [ -z "$build_tiles" ]; then build_tiles="../build/valhalla_build_tiles" fi -# Initialize (e.g. create the directory or remove files if it already exists) -$build_tiles --config $1 --start initialize --end initialize || error_exit "[Error] tile initialization failed!" - -# Parse OSM PBF -$build_tiles --config $1 --start parse --end parse $datafiles || error_exit "[Error] OSM PBF parsing failed!" - -# Build tiles -$build_tiles --config $1 --start build --end build || error_exit "[Error] build tiles failed!" - -# Enhance tiles -$build_tiles --config $1 --start enhance --end enhance || error_exit "[Error] Enhance tiles failed!" - -# Filter tiles (optional - based on config) -$build_tiles --config $1 --start filter --end filter || error_exit "[Error] Filter tiles failed!" - -# Add transit tiles (optional - based on config) -$build_tiles --config $1 --start transit --end transit || error_exit "[Error] Add transit tiles failed!" - -# Create hierarchy (optional - based on config) -$build_tiles --config $1 --start hierarchy --end hierarchy || error_exit "[Error] Hierarchy building failed!" - -# Create shortcuts (optional - based on config) -$build_tiles --config $1 --start shortcuts --end shortcuts || error_exit "[Error] Shortcut building failed!" - -# Add restrictions -$build_tiles --config $1 --start restrictions --end restrictions || error_exit "[Error] Adding complex restrictions failed!" - -# Validate data -$build_tiles --config $1 --start validate --end validate || error_exit "[Error] Validate tiles failed!" - -# Cleanup temporary files -$build_tiles --config $1 --start cleanup --end cleanup || error_exit "[Error] Cleanup temporary data failed!" +valhalla_build_tiles --help | awk '/^ initialize/,/^ cleanup/' | while read stage; do + $build_tiles --config $config --start $stage --end $stage $datafiles || error_exit "[Error] Stage $stage failed!" +done From b71dc969ceb8e67ee2cdd3113a3514e154ba816f Mon Sep 17 00:00:00 2001 From: Stepan Kizim <10885920+kinkard@users.noreply.github.com> Date: Thu, 17 Oct 2024 14:48:03 +0200 Subject: [PATCH 144/198] fix: Fix ability to use Valhalla via cmake `add_subdirectory` (#4930) --- CHANGELOG.md | 1 + CMakeLists.txt | 6 +++--- cmake/ValhallaPkgConfig.cmake | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f8d81b685..8041277594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * **Removed** * **Bug Fix** * FIXED: `incremental_build_tiles` script works again [#4909](https://github.com/valhalla/valhalla/pull/4909) + * FIXED: Fix ability to use Valhalla via cmake `add_subdirectory` [#4930](https://github.com/valhalla/valhalla/pull/4930) * **Enhancement** ## Release Date: 2024-10-10 Valhalla 3.5.1 diff --git a/CMakeLists.txt b/CMakeLists.txt index 3af4452a73..46064579f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,10 +127,10 @@ find_package(Threads REQUIRED) # resolve vendored libraries set(date_include_dir ${VALHALLA_SOURCE_DIR}/third_party/date/include) -set(rapidjson_include_dir ${CMAKE_SOURCE_DIR}/third_party/rapidjson/include) -set(robinhoodhashing_include_dir ${CMAKE_SOURCE_DIR}/third_party/robin-hood-hashing/src/include) +set(rapidjson_include_dir ${VALHALLA_SOURCE_DIR}/third_party/rapidjson/include) +set(robinhoodhashing_include_dir ${VALHALLA_SOURCE_DIR}/third_party/robin-hood-hashing/src/include) set(cxxopts_include_dir ${VALHALLA_SOURCE_DIR}/third_party/cxxopts/include) -set(dirent_include_dir ${CMAKE_SOURCE_DIR}/third_party/dirent/include) +set(dirent_include_dir ${VALHALLA_SOURCE_DIR}/third_party/dirent/include) if (PREFER_EXTERNAL_DEPS) # date find_package(date QUIET) diff --git a/cmake/ValhallaPkgConfig.cmake b/cmake/ValhallaPkgConfig.cmake index a0483c801a..6bd38190f3 100644 --- a/cmake/ValhallaPkgConfig.cmake +++ b/cmake/ValhallaPkgConfig.cmake @@ -48,7 +48,7 @@ function(configure_valhalla_pc) list(JOIN LIBS_PRIVATE " " LIBS_PRIVATE) configure_file( - ${CMAKE_SOURCE_DIR}/libvalhalla.pc.in - ${CMAKE_BINARY_DIR}/libvalhalla.pc + ${VALHALLA_SOURCE_DIR}/libvalhalla.pc.in + ${VALHALLA_BUILD_DIR}/libvalhalla.pc @ONLY) endfunction() From 40e898e5025a5be479fc622b10f730673e6ddcc4 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 17 Oct 2024 15:30:08 +0200 Subject: [PATCH 145/198] Level location filtering (#4926) --- CHANGELOG.md | 1 + docs/docs/api/turn-by-turn/api-reference.md | 2 +- proto/common.proto | 4 + src/baldr/location.cc | 5 +- src/loki/isochrone_action.cc | 2 +- src/loki/locate_action.cc | 2 +- src/loki/matrix_action.cc | 6 +- src/loki/route_action.cc | 2 +- src/loki/search.cc | 3 +- src/loki/worker.cc | 27 +++- src/worker.cc | 8 +- ...level_loki.cc => test_multi_layer_loki.cc} | 26 ++-- test/gurka/test_search_filter.cc | 128 ++++++++++++++++++ valhalla/baldr/graphconstants.h | 3 + valhalla/baldr/location.h | 5 +- valhalla/baldr/pathlocation.h | 2 + valhalla/loki/worker.h | 1 + 17 files changed, 200 insertions(+), 27 deletions(-) rename test/gurka/{test_multi_level_loki.cc => test_multi_layer_loki.cc} (82%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8041277594..9c8e1c6d2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * ADDED: Add option `edge.forward` to trace attributes [#4876](https://github.com/valhalla/valhalla/pull/4876) * ADDED: Provide conditional speed limits from "maxspeed:conditional" in `/locate` and proto `/route` responses [#4851](https://github.com/valhalla/valhalla/pull/4851) * ADDED: Support multiple levels and level ranges [#4879](https://github.com/valhalla/valhalla/pull/4879) + * ADDED: Level location search filter [#4926](https://github.com/valhalla/valhalla/pull/4926) ## Release Date: 2024-08-21 Valhalla 3.5.0 * **Removed** diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 42f84d81b4..8eb1c72245 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -46,7 +46,7 @@ To build a route, you need to specify two `break` locations. In addition, you ca | `street_side_tolerance` | If your input coordinate is less than this tolerance away from the edge centerline then we set your side of street to none otherwise your side of street will be left or right depending on direction of travel. The default is 5 meters. | | `street_side_max_distance` | The max distance in meters that the input coordinates or display ll can be from the edge centerline for them to be used for determining the side of street. Beyond this distance the side of street is set to none. The default is 1000 meters. | | `street_side_cutoff` | Disables the `preferred_side` when set to `same` or `opposite` if the edge has a road class less than that provided by `street_side_cutoff`. The road class must be one of the following strings: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. The default value is `service_other` so that `preferred_side` will not be disabled for any edges. | -| `search_filter` | A set of optional filters to exclude candidate edges based on their attribution. The following exclusion filters are supported:
  • `exclude_tunnel` (boolean, defaults to `false`): whether to exclude roads marked as tunnels
  • `exclude_bridge` (boolean, defaults to `false`): whether to exclude roads marked as bridges
  • `exclude_ramp` (boolean, defaults to `false`): whether to exclude link roads marked as ramps, note that some turn channels are also marked as ramps
  • `exclude_closures` (boolean, defaults to `true`): whether to exclude roads considered closed due to live traffic closure. **Note:** This option cannot be set if `costing_options..ignore_closures` is also specified. An error is returned if both options are specified. **Note 2:** Ignoring closures at destination and source locations does NOT work for date_time type `0/1` & `2` respectively
  • `min_road_class` (string, defaults to `"service_other"`): lowest road class allowed
  • `max_road_class` (string, defaults to `"motorway"`): highest road class allowed
Road classes from highest to lowest are: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. | +| `search_filter` | A set of optional filters to exclude candidate edges based on their attribution. The following exclusion filters are supported:
  • `exclude_tunnel` (boolean, defaults to `false`): whether to exclude roads marked as tunnels
  • `exclude_bridge` (boolean, defaults to `false`): whether to exclude roads marked as bridges
  • `exclude_ramp` (boolean, defaults to `false`): whether to exclude link roads marked as ramps, note that some turn channels are also marked as ramps
  • `exclude_closures` (boolean, defaults to `true`): whether to exclude roads considered closed due to live traffic closure. **Note:** This option cannot be set if `costing_options..ignore_closures` is also specified. An error is returned if both options are specified. **Note 2:** Ignoring closures at destination and source locations does NOT work for date_time type `0/1` & `2` respectively
  • `min_road_class` (string, defaults to `"service_other"`): lowest road class allowed
  • `max_road_class` (string, defaults to `"motorway"`): highest road class allowed
  • **BETA** `level` (float): if specified, will only consider edges that are on or traverse the passed floor level. It will set `search_cutoff` to a default value of 300 meters if no cutoff value is passed. Additionally, if a `search_cutoff` is passed, it will be clamped to 1000 meters.
Road classes from highest to lowest are: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. | | `preferred_layer` | The layer on which edges should be considered. If provided, edges whose layer does not match the provided value will be discarded from the candidate search. | Optionally, you can include the following location information without impacting the routing. This information is carried through the request and returned as a convenience. diff --git a/proto/common.proto b/proto/common.proto index f6efd5bdb5..d2e8bb4230 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -61,6 +61,10 @@ message SearchFilter { oneof has_exclude_closures { bool exclude_closures = 6;// whether to exclude roads marked as closed due to traffic [default = true] } + + oneof has_level { + float level = 7; // level to filter edges by + } } message PathEdge { diff --git a/src/baldr/location.cc b/src/baldr/location.cc index bd9b9e4066..2fc0669f9b 100644 --- a/src/baldr/location.cc +++ b/src/baldr/location.cc @@ -12,10 +12,11 @@ Location::SearchFilter::SearchFilter(valhalla::RoadClass min_road_class, bool exclude_tunnel, bool exclude_bridge, bool exclude_ramp, - bool exclude_closures) + bool exclude_closures, + float level) : min_road_class_(min_road_class), max_road_class_(max_road_class), exclude_tunnel_(exclude_tunnel), exclude_bridge_(exclude_bridge), exclude_ramp_(exclude_ramp), - exclude_closures_(exclude_closures) { + exclude_closures_(exclude_closures), level_(level) { } // TODO: get defaults from config singleton diff --git a/src/loki/isochrone_action.cc b/src/loki/isochrone_action.cc index c1960e6bd1..1641488570 100644 --- a/src/loki/isochrone_action.cc +++ b/src/loki/isochrone_action.cc @@ -33,7 +33,7 @@ void loki_worker_t::init_isochrones(Api& request) { auto& options = *request.mutable_options(); // strip off unused information - parse_locations(options.mutable_locations()); + parse_locations(options.mutable_locations(), request); if (options.locations_size() < 1) { throw valhalla_exception_t{120}; }; diff --git a/src/loki/locate_action.cc b/src/loki/locate_action.cc index 86cdfb28a3..45a8397439 100644 --- a/src/loki/locate_action.cc +++ b/src/loki/locate_action.cc @@ -9,7 +9,7 @@ namespace valhalla { namespace loki { void loki_worker_t::init_locate(Api& request) { - parse_locations(request.mutable_options()->mutable_locations()); + parse_locations(request.mutable_options()->mutable_locations(), request); if (request.options().locations_size() < 1) throw valhalla_exception_t{120}; diff --git a/src/loki/matrix_action.cc b/src/loki/matrix_action.cc index 19f869c041..206eca6732 100644 --- a/src/loki/matrix_action.cc +++ b/src/loki/matrix_action.cc @@ -60,11 +60,11 @@ void loki_worker_t::init_matrix(Api& request) { // we require sources and targets auto& options = *request.mutable_options(); if (options.action() == Options::sources_to_targets || options.action() == Options::expansion) { - parse_locations(options.mutable_sources(), valhalla_exception_t{112}); - parse_locations(options.mutable_targets(), valhalla_exception_t{112}); + parse_locations(options.mutable_sources(), request, valhalla_exception_t{112}); + parse_locations(options.mutable_targets(), request, valhalla_exception_t{112}); } // optimized route uses locations but needs to do a matrix else { - parse_locations(options.mutable_locations(), valhalla_exception_t{112}); + parse_locations(options.mutable_locations(), request, valhalla_exception_t{112}); if (options.locations_size() < 2) { throw valhalla_exception_t{120}; }; diff --git a/src/loki/route_action.cc b/src/loki/route_action.cc index 02bb2ba3ab..3ff19dc45a 100644 --- a/src/loki/route_action.cc +++ b/src/loki/route_action.cc @@ -44,7 +44,7 @@ namespace valhalla { namespace loki { void loki_worker_t::init_route(Api& request) { - parse_locations(request.mutable_options()->mutable_locations()); + parse_locations(request.mutable_options()->mutable_locations(), request); // need to check location size here instead of in parse_locations because of locate action needing // a different size if (request.options().locations_size() < 2) { diff --git a/src/loki/search.cc b/src/loki/search.cc index cc286a4d24..9d35d1c68e 100644 --- a/src/loki/search.cc +++ b/src/loki/search.cc @@ -38,7 +38,8 @@ bool search_filter(const DirectedEdge* edge, (filter.exclude_tunnel_ && edge->tunnel()) || (filter.exclude_bridge_ && edge->bridge()) || (filter.exclude_ramp_ && (edge->use() == Use::kRamp)) || (filter.exclude_closures_ && (costing.flow_mask() & kCurrentFlowMask) && - tile->IsClosed(edge)); + tile->IsClosed(edge)) || + (filter.level_ != kMaxLevel && !tile->edgeinfo(edge).includes_level(filter.level_)); } bool side_filter(const PathLocation::PathEdge& edge, const Location& location, GraphReader& reader) { diff --git a/src/loki/worker.cc b/src/loki/worker.cc index 3f96f4f22e..c7bad30732 100644 --- a/src/loki/worker.cc +++ b/src/loki/worker.cc @@ -29,7 +29,10 @@ using namespace valhalla::loki; namespace valhalla { namespace loki { void loki_worker_t::parse_locations(google::protobuf::RepeatedPtrField* locations, + Api& request, std::optional required_exception) { + bool has_302 = false, has_303 = false; + if (locations->size()) { for (auto& location : *locations) { if (!location.has_minimum_reachability_case()) @@ -48,15 +51,35 @@ void loki_worker_t::parse_locations(google::protobuf::RepeatedPtrField kMaxIndoorSearchCutoff) { + has_303 = true; + location.set_search_cutoff(kMaxIndoorSearchCutoff); + } + } else if (!location.has_search_cutoff_case() && has_level) { + // level and no cutoff, set special default + location.set_search_cutoff(kDefaultIndoorSearchCutoff); + has_302 = true; + } + // if there is a level search filter and if (!location.has_street_side_tolerance_case()) location.set_street_side_tolerance(default_street_side_tolerance); if (!location.has_street_side_max_distance_case()) location.set_street_side_max_distance(default_street_side_max_distance); } + if (has_302) + add_warning(request, 302, std::to_string(kDefaultIndoorSearchCutoff)); + if (has_303) + add_warning(request, 303, std::to_string(kMaxIndoorSearchCutoff)); + } else if (required_exception) { throw *required_exception; } diff --git a/src/worker.cc b/src/worker.cc index 1154e542e1..90f334fe19 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -158,9 +158,11 @@ const std::unordered_map warning_codes = { {205, R"("disable_hierarchy_pruning" exceeded the max distance, ignoring disable_hierarchy_pruning)"}, {206, R"(CostMatrix does not consider "targets" with "date_time" set, ignoring date_time)"}, {207, R"(TimeDistanceMatrix does not consider "shape_format", ignoring shape_format)"}, - // 3xx is used when costing options were specified but we had to change them internally for some reason + // 3xx is used when costing or location options were specified but we had to change them internally for some reason {300, R"(Many:Many CostMatrix was requested, but server only allows 1:Many TimeDistanceMatrix)"}, {301, R"(1:Many TimeDistanceMatrix was requested, but server only allows Many:Many CostMatrix)"}, + {302, R"("search_filter.level" was specified without a custom "search_cutoff", setting default default cutoff to )"}, + {303, R"("search_cutoff" exceeds maximum allowed value due to "search_filter.level" being specified, clamping cutoff to )"}, // 4xx is used when we do sneaky important things the user should be aware of {400, R"(CostMatrix turned off destination-only on a second pass for connections: )"} }; @@ -408,6 +410,8 @@ void parse_location(valhalla::Location* location, // search_filter.exclude_ramp location->mutable_search_filter()->set_exclude_ramp( rapidjson::get(*search_filter, "/exclude_ramp", false)); + location->mutable_search_filter()->set_level( + rapidjson::get(*search_filter, "/level", baldr::kMaxLevel)); // search_filter.exclude_closures exclude_closures = rapidjson::get_optional(*search_filter, "/exclude_closures"); } // or is it pbf @@ -436,6 +440,8 @@ void parse_location(valhalla::Location* location, if (!location->search_filter().has_max_road_class_case()) { location->mutable_search_filter()->set_max_road_class(valhalla::kMotorway); } + if (!location->search_filter().has_level_case()) + location->mutable_search_filter()->set_level(baldr::kMaxLevel); float waiting_secs = rapidjson::get(r_loc, "/waiting", 0.f); switch (location->type()) { diff --git a/test/gurka/test_multi_level_loki.cc b/test/gurka/test_multi_layer_loki.cc similarity index 82% rename from test/gurka/test_multi_level_loki.cc rename to test/gurka/test_multi_layer_loki.cc index 53f505a984..a6251760e8 100644 --- a/test/gurka/test_multi_level_loki.cc +++ b/test/gurka/test_multi_layer_loki.cc @@ -21,7 +21,7 @@ struct Waypoint { } // namespace -class MultiLevelLoki : public ::testing::Test { +class MultiLayerLoki : public ::testing::Test { protected: static gurka::map map; static std::string ascii_map; @@ -34,7 +34,7 @@ class MultiLevelLoki : public ::testing::Test { H | | - B-------E + B-------E | | | | I | @@ -58,7 +58,7 @@ class MultiLevelLoki : public ::testing::Test { }; layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres); - map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_multi_level_loki", build_config); + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_multi_layer_loki", build_config); } valhalla::Api Route(const std::vector& waypoints) { @@ -76,21 +76,21 @@ class MultiLevelLoki : public ::testing::Test { return gurka::do_action(valhalla::Options::route, map, nodes, "auto", options); } }; -gurka::map MultiLevelLoki::map = {}; -std::string MultiLevelLoki::ascii_map = {}; -gurka::nodelayout MultiLevelLoki::layout = {}; +gurka::map MultiLayerLoki::map = {}; +std::string MultiLayerLoki::ascii_map = {}; +gurka::nodelayout MultiLayerLoki::layout = {}; -TEST_F(MultiLevelLoki, test_multilevel_loki) { - auto result = Route({{"B", -1}, {"G"}}); +TEST_F(MultiLayerLoki, test_multilevel_loki) { + auto result = Route({{"B", -1}, {"G", std::nullopt}}); gurka::assert::osrm::expect_steps(result, std::vector({"HI"})); - result = Route({{"B", 0}, {"G"}}); + result = Route({{"B", 0}, {"G", std::nullopt}}); gurka::assert::osrm::expect_steps(result, std::vector({"BE", "EF", "FD", "DG"})); - result = Route({{"B"}, {"G"}}); + result = Route({{"B", std::nullopt}, {"G", std::nullopt}}); gurka::assert::osrm::expect_steps(result, std::vector({"HI"})); } -TEST_F(MultiLevelLoki, test_no_matching_layer) { - auto result = Route({{"E", -1}, {"G"}}); +TEST_F(MultiLayerLoki, test_no_matching_layer) { + auto result = Route({{"E", -1}, {"G", std::nullopt}}); gurka::assert::osrm::expect_steps(result, std::vector({"EF", "FD", "DG"})); -} +} \ No newline at end of file diff --git a/test/gurka/test_search_filter.cc b/test/gurka/test_search_filter.cc index 1bce9f18dd..02a0f6af92 100644 --- a/test/gurka/test_search_filter.cc +++ b/test/gurka/test_search_filter.cc @@ -1141,3 +1141,131 @@ TEST_P(ClosuresWithTimedepRoutes, IgnoreClosureWithTimedepReverse) { } INSTANTIATE_TEST_SUITE_P(SearchFilter, ClosuresWithTimedepRoutes, ::testing::ValuesIn(buildParams())); + +/*********************************************************************/ + +struct Waypoint { + std::string node; + int16_t preferred_level; +}; +class LevelSearchFilter : public ::testing::Test { +protected: + static gurka::map map; + static std::string ascii_map; + static gurka::nodelayout layout; + static void SetUpTestSuite() { + constexpr double gridsize_metres = 50; + + /** + * Difficult to represent visually, so here is a stacked view: + * + * ground level: + * C-----------------D + * | | + * | | + * | | + * (A)---------------(B) + * + * first floor: + * G---------------H + * | | + * | | + * | | + * (E)-------------(F) + * + * () = connected via stairs + */ + ascii_map = R"( + C-G-----------H-D + | | x y | | + | | | | + | | | | z + | | | | w + | | | | + A~E-----------F~B + )"; + + const gurka::ways ways = { + // ground floor + {"AB", {{"highway", "corridor"}, {"level", "0"}}}, + {"AC", {{"highway", "corridor"}, {"level", "0"}}}, + {"CD", {{"highway", "corridor"}, {"level", "0"}}}, + {"DB", {{"highway", "corridor"}, {"level", "0"}}}, + // level 1 + {"EG", {{"highway", "corridor"}, {"level", "1"}}}, + {"EF", {{"highway", "corridor"}, {"level", "1"}}}, + {"GH", {{"highway", "corridor"}, {"level", "1"}}}, + {"HF", {{"highway", "corridor"}, {"level", "1"}}}, + // stairs + {"AE", {{"highway", "steps"}, {"level", "0;1"}}}, + {"FB", {{"highway", "steps"}, {"level", "0;1"}}}, + + }; + + layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres); + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_multi_level_loki", {}); + } + + valhalla::Api Route(const std::vector& waypoints, unsigned int cutoff = 0) { + std::vector nodes; + std::unordered_map options; + for (size_t index = 0; index < waypoints.size(); ++index) { + const auto& wp = waypoints[index]; + nodes.emplace_back(wp.node); + options["/locations/" + std::to_string(index) + "/search_filter/level"] = + std::to_string(wp.preferred_level); + if (cutoff > 0) { + options["/locations/" + std::to_string(index) + "/search_cutoff"] = std::to_string(cutoff); + } + } + return gurka::do_action(valhalla::Options::route, map, nodes, "pedestrian", options); + } +}; +gurka::map LevelSearchFilter::map = {}; +std::string LevelSearchFilter::ascii_map = {}; +gurka::nodelayout LevelSearchFilter::layout = {}; + +TEST_F(LevelSearchFilter, TraverseLevels) { + auto result = Route({{"x", 0}, {"y", 1}}); + ASSERT_EQ(result.info().warnings().size(), 1); + EXPECT_EQ(result.info().warnings().Get(0).code(), 302); + gurka::assert::raw::expect_path(result, {"CD", "AC", "AE", "EG", "GH"}); +} + +TEST_F(LevelSearchFilter, NonExistentLevel) { + try { + auto result = Route({{"x", 0}, {"y", 6}}); + FAIL() << "We should not get to here"; + } catch (const valhalla_exception_t& e) { + EXPECT_EQ(e.code, 171); + EXPECT_STREQ(e.what(), "No suitable edges near location"); + } catch (...) { FAIL() << "Failed with unexpected exception type"; } +} + +TEST_F(LevelSearchFilter, Cutoff) { + try { + auto result = Route({{"x", 0}, {"z", 1}}); + FAIL() << "We should not get to here"; + } catch (const valhalla_exception_t& e) { + EXPECT_EQ(e.code, 171); + EXPECT_STREQ(e.what(), "No suitable edges near location"); + } catch (...) { FAIL() << "Failed with unexpected exception type"; } +} + +TEST_F(LevelSearchFilter, CutoffOverride) { + try { + auto result = Route({{"x", 0}, {"z", 1}}, 9000); + EXPECT_EQ(result.info().warnings().size(), 1); + } catch (...) { FAIL() << "Shoud succeed"; } +} + +TEST_F(LevelSearchFilter, CutoffClamped) { + try { + // w is about 1300m away, so the search_cutoff being clamped to 1000 should result + // in an exception + auto result = Route({{"x", 0}, {"w", 1}}, 2000); + FAIL() << "Should fail"; + } catch (const valhalla_exception_t& e) { EXPECT_EQ(e.code, 171); } catch (...) { + FAIL() << "Failed with unexpected exception type"; + }; +} \ No newline at end of file diff --git a/valhalla/baldr/graphconstants.h b/valhalla/baldr/graphconstants.h index 4a4f78eda1..2f17fa2862 100644 --- a/valhalla/baldr/graphconstants.h +++ b/valhalla/baldr/graphconstants.h @@ -209,6 +209,9 @@ constexpr uint32_t kMaxAddedTime = 255; // this is the minimum we support, i.e. -500 m would result in "no elevation" constexpr float kNoElevationData = -500.0f; +constexpr uint32_t kDefaultIndoorSearchCutoff = 300; +constexpr uint32_t kMaxIndoorSearchCutoff = 1000; + // (building) level constants // highest 3-byte value constexpr float kLevelRangeSeparator = 1048575.0f; diff --git a/valhalla/baldr/location.h b/valhalla/baldr/location.h index 5cba77dd76..147dfad505 100644 --- a/valhalla/baldr/location.h +++ b/valhalla/baldr/location.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -44,7 +45,8 @@ struct Location { bool exclude_tunnel = false, bool exclude_bridge = false, bool exclude_ramp = false, - bool exclude_closures = true); + bool exclude_closures = true, + float level = kMaxLevel); valhalla::RoadClass min_road_class_; valhalla::RoadClass max_road_class_; @@ -52,6 +54,7 @@ struct Location { bool exclude_bridge_; bool exclude_ramp_; bool exclude_closures_; + float level_; protected: }; diff --git a/valhalla/baldr/pathlocation.h b/valhalla/baldr/pathlocation.h index 909c66c520..853cb80071 100644 --- a/valhalla/baldr/pathlocation.h +++ b/valhalla/baldr/pathlocation.h @@ -129,6 +129,7 @@ struct PathLocation : public Location { l->mutable_search_filter()->set_exclude_bridge(pl.search_filter_.exclude_bridge_); l->mutable_search_filter()->set_exclude_ramp(pl.search_filter_.exclude_ramp_); l->mutable_search_filter()->set_exclude_closures(pl.search_filter_.exclude_closures_); + l->mutable_search_filter()->set_level(pl.search_filter_.level_); auto* path_edges = l->mutable_correlation()->mutable_edges(); for (const auto& e : pl.edges) { @@ -228,6 +229,7 @@ struct PathLocation : public Location { l.search_filter_.exclude_bridge_ = loc.search_filter().exclude_bridge(); l.search_filter_.exclude_ramp_ = loc.search_filter().exclude_ramp(); l.search_filter_.exclude_closures_ = loc.search_filter().exclude_closures(); + l.search_filter_.level_ = loc.search_filter().level(); } if (loc.has_display_ll()) { l.display_latlng_ = midgard::PointLL{loc.display_ll().lng(), loc.display_ll().lat()}; diff --git a/valhalla/loki/worker.h b/valhalla/loki/worker.h index aad9d57f6f..84a418f7f1 100644 --- a/valhalla/loki/worker.h +++ b/valhalla/loki/worker.h @@ -48,6 +48,7 @@ class loki_worker_t : public service_worker_t { protected: void parse_locations(google::protobuf::RepeatedPtrField* locations, + Api& request, std::optional required_exception = valhalla_exception_t{ 110}); void parse_trace(Api& request); From 21dd8ebdeb089aac84f9e117c20d4ceaea83fd48 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 24 Oct 2024 12:25:05 +0200 Subject: [PATCH 146/198] Fix macOS CI (#4945) --- .github/workflows/osx.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 100c22dd4d..4c09df6b22 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -2,7 +2,7 @@ name: OSX CI on: push: paths-ignore: - - '*.md' + - "*.md" - .circleci/ - docs/ - run_route_scripts/ @@ -11,7 +11,7 @@ on: - master pull_request: paths-ignore: - - '*.md' + - "*.md" - .circleci/ - docs/ - run_route_scripts/ @@ -22,7 +22,7 @@ on: inputs: debug_enabled: type: boolean - description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + description: "Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)" required: false default: false @@ -33,14 +33,15 @@ jobs: - uses: actions/checkout@v4 with: submodules: recursive - + - uses: actions/setup-python@v5 with: - python-version: '3.12' - + python-version: "3.12" + - name: Install dependencies run: | - HOMEBREW_NO_AUTO_UPDATE=1 brew install autoconf automake protobuf cmake ccache libtool sqlite3 libspatialite luajit curl wget czmq lz4 spatialite-tools unzip boost gdal + HOMEBREW_NO_AUTO_UPDATE=1 brew install python autoconf automake protobuf cmake ccache libtool sqlite3 libspatialite luajit curl wget czmq lz4 spatialite-tools unzip boost gdal + export PATH="$(brew --prefix python)/libexec/bin:$PATH" sudo python -m pip install --break-system-packages requests shapely git clone https://github.com/kevinkreiser/prime_server --recurse-submodules && cd prime_server && ./autogen.sh && ./configure && make -j$(sysctl -n hw.logicalcpu) && sudo make install @@ -71,10 +72,10 @@ jobs: with: path: ~/Library/Caches/ccache key: ${{ steps.cache-ccache-restore.outputs.cache-primary-key }} - + - name: Run Tests run: make -C build -j$(sysctl -n hw.logicalcpu) check - + - name: Setup tmate session uses: mxschmitt/action-tmate@v3 # only run this if manually invoked or a previous job failed @@ -82,4 +83,3 @@ jobs: with: detached: true timeout-minutes: 15 - From 531206c5a6d818edd1d3cedbc6595c675b4875ce Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 24 Oct 2024 12:27:19 +0200 Subject: [PATCH 147/198] log invalid level as warning (#4946) --- src/mjolnir/osmway.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mjolnir/osmway.cc b/src/mjolnir/osmway.cc index 7f1c10eff4..bea4e0bbab 100644 --- a/src/mjolnir/osmway.cc +++ b/src/mjolnir/osmway.cc @@ -1128,13 +1128,13 @@ void OSMWay::GetTaggedValues(const UniqueNames& name_offset_map, range.first = std::stof(nums[0]); range.second = std::stof(nums[1]); } catch (...) { - LOG_ERROR("Invalid level: " + token + "; way_id " + std::to_string(osmwayid_)); + LOG_WARN("Invalid level: " + token + "; way_id " + std::to_string(osmwayid_)); continue; } if (range.first > range.second) { - LOG_ERROR("Invalid level range, " + std::to_string(range.first) + " - " + - std::to_string(range.second) + "; way_id " + std::to_string(osmwayid_)); + LOG_WARN("Invalid level range, " + std::to_string(range.first) + " - " + + std::to_string(range.second) + "; way_id " + std::to_string(osmwayid_)); continue; } @@ -1147,7 +1147,7 @@ void OSMWay::GetTaggedValues(const UniqueNames& name_offset_map, range.first = std::stof(token); range.second = range.first; } catch (...) { - LOG_ERROR("Invalid level: " + token + "; way_id " + std::to_string(osmwayid_)); + LOG_WARN("Invalid level: " + token + "; way_id " + std::to_string(osmwayid_)); continue; } } From 8423bc0c59eaf44a8a96095436bf0fed435d282f Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 24 Oct 2024 18:17:33 +0200 Subject: [PATCH 148/198] Fix transit build (#4943) --- src/mjolnir/transitbuilder.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mjolnir/transitbuilder.cc b/src/mjolnir/transitbuilder.cc index 09c73a96f1..6f594143d8 100644 --- a/src/mjolnir/transitbuilder.cc +++ b/src/mjolnir/transitbuilder.cc @@ -400,6 +400,7 @@ std::vector MakeConnections(const graph_tile_ptr& local_tile, if (!closest_edge) { LOG_WARN("Could not find connection point for in/egress near: " + std::to_string(egress_ll.second) + "," + std::to_string(egress_ll.first)); + continue; } // TODO: if the point we found is further away than the tile edge then there could be a better From 329c4e62428a249c9b59a674baf1241e6bb8ef8d Mon Sep 17 00:00:00 2001 From: Greg Knisely Date: Tue, 29 Oct 2024 15:56:57 -0400 Subject: [PATCH 149/198] Consider smoothness in all profiles that use surface (#4949) --- CHANGELOG.md | 1 + lua/graph.lua | 6 +- src/mjolnir/pbfgraphparser.cc | 27 +- taginfo.json | 7 + test/gurka/test_exclude_unpaved.cc | 138 ------ test/gurka/test_exclude_unpaved_smoothness.cc | 395 ++++++++++++++++++ 6 files changed, 432 insertions(+), 142 deletions(-) delete mode 100644 test/gurka/test_exclude_unpaved.cc create mode 100644 test/gurka/test_exclude_unpaved_smoothness.cc diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c8e1c6d2d..ada76adc9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * FIXED: `incremental_build_tiles` script works again [#4909](https://github.com/valhalla/valhalla/pull/4909) * FIXED: Fix ability to use Valhalla via cmake `add_subdirectory` [#4930](https://github.com/valhalla/valhalla/pull/4930) * **Enhancement** + * ADDED: Consider smoothness in all profiles that use surface [#4949](https://github.com/valhalla/valhalla/pull/4949) ## Release Date: 2024-10-10 Valhalla 3.5.1 * **Removed** diff --git a/lua/graph.lua b/lua/graph.lua index 37dc30805f..808e28cc2c 100644 --- a/lua/graph.lua +++ b/lua/graph.lua @@ -907,7 +907,7 @@ function filter_tags_generic(kv) kv[k] = v end - if kv["impassable"] == "yes" or access == "false" or (kv["access"] == "private" and (kv["emergency"] == "yes" or kv["service"] == "emergency_access")) then + if kv["impassable"] == "yes" or kv["smoothness"] == "impassable" or access == "false" or (kv["access"] == "private" and (kv["emergency"] == "yes" or kv["service"] == "emergency_access")) then kv["auto_forward"] = "false" kv["truck_forward"] = "false" @@ -996,7 +996,7 @@ function filter_tags_generic(kv) default_val = tostring(rail) end - if ((ferry == false and rail == false) or kv["impassable"] == "yes" or access == "false" or (kv["access"] == "private" and (kv["emergency"] == "yes" or kv["service"] == "emergency_access"))) then + if ((ferry == false and rail == false) or kv["impassable"] == "yes" or kv["smoothness"] == "impassable" or access == "false" or (kv["access"] == "private" and (kv["emergency"] == "yes" or kv["service"] == "emergency_access"))) then kv["auto_forward"] = "false" kv["truck_forward"] = "false" @@ -1931,7 +1931,7 @@ function nodes_proc (kv, nokeys) local initial_access = access[kv["access"]] local access = initial_access or "true" - if (kv["impassable"] == "yes" or (kv["access"] == "private" and (kv["emergency"] == "yes" or kv["service"] == "emergency_access"))) then + if (kv["impassable"] == "yes" or kv["smoothness"] == "impassable" or (kv["access"] == "private" and (kv["emergency"] == "yes" or kv["service"] == "emergency_access"))) then access = "false" end diff --git a/src/mjolnir/pbfgraphparser.cc b/src/mjolnir/pbfgraphparser.cc index 249e60ee82..c21bf2843b 100644 --- a/src/mjolnir/pbfgraphparser.cc +++ b/src/mjolnir/pbfgraphparser.cc @@ -1481,6 +1481,28 @@ struct graph_callback : public OSMPBF::Callback { has_surface_ = false; } }; + + // surface and tracktype tag should win over smoothness. + tag_handlers_["smoothness"] = [this]() { + if (!has_surface_tag_ && !has_tracktype_tag_) { + has_surface_ = true; + if (tag_.second == "excellent" || tag_.second == "good") { + way_.set_surface(Surface::kPavedSmooth); + } else if (tag_.second == "intermediate") { + way_.set_surface(Surface::kPavedRough); + } else if (tag_.second == "bad") { + way_.set_surface(Surface::kCompacted); + } else if (tag_.second == "very_bad") { + way_.set_surface(Surface::kDirt); + } else if (tag_.second == "horrible") { + way_.set_surface(Surface::kGravel); + } else if (tag_.second == "very_horrible") { + way_.set_surface(Surface::kPath); + } else { + has_surface_ = false; + } + } + }; // surface tag should win over tracktype. tag_handlers_["tracktype"] = [this]() { if (!has_surface_tag_) { @@ -2481,6 +2503,9 @@ struct graph_callback : public OSMPBF::Callback { has_surface_ = false; } + const auto& tracktype_exists = results.find("tracktype"); + has_tracktype_tag_ = (tracktype_exists != results.end()); + way_.set_drive_on_right(true); // default for (const auto& kv : results) { @@ -4822,7 +4847,7 @@ struct graph_callback : public OSMPBF::Callback { bool has_default_speed_ = false, has_max_speed_ = false; bool has_average_speed_ = false, has_advisory_speed_ = false; bool has_surface_ = true; - bool has_surface_tag_ = true; + bool has_surface_tag_ = true, has_tracktype_tag_ = true; OSMAccess osm_access_; std::map, uint32_t> pronunciationMap; std::map, uint32_t> langMap; diff --git a/taginfo.json b/taginfo.json index 8d91ce36bc..7ed7d9129a 100644 --- a/taginfo.json +++ b/taginfo.json @@ -7661,6 +7661,13 @@ "way" ] }, + { + "key": "smoothness", + "description": "Road smoothness. Note that surface and tracktype keys have a higher priority than smoothness.", + "object_types": [ + "way" + ] + }, { "key": "taxi", "value": "no", diff --git a/test/gurka/test_exclude_unpaved.cc b/test/gurka/test_exclude_unpaved.cc deleted file mode 100644 index 21565141af..0000000000 --- a/test/gurka/test_exclude_unpaved.cc +++ /dev/null @@ -1,138 +0,0 @@ -#include "gurka.h" - -#include - -using namespace valhalla; - -namespace { -const std::vector kSupportedCostingModels = { - "auto", - "taxi", - "bus", - "truck", -}; -} // namespace - -class ExcludeUnpavedTest : public ::testing::Test { -protected: - static gurka::map map; - - static void SetUpTestSuite() { - constexpr double grid_size_meters = 100.; - - const std::string ascii_map = R"( - E----F----G----H----I----A----J----K----L - | | - | | - | | - | | - M----N - )"; - - const gurka::ways ways = { - {"EF", {{"highway", "residential"}, {"surface", "compacted"}}}, - {"FG", {{"highway", "residential"}, {"surface", "compacted"}}}, - {"GH", {{"highway", "residential"}}}, - {"HI", {{"highway", "residential"}, {"surface", "unpaved"}}}, - {"IA", {{"highway", "residential"}, {"surface", "unpaved"}}}, - {"IJ", {{"highway", "residential"}}}, - {"JK", {{"highway", "residential"}, {"surface", "gravel"}}}, - {"KL", {{"highway", "residential"}}}, - {"JM", {{"highway", "residential"}}}, - {"MN", {{"highway", "residential"}}}, - {"NK", {{"highway", "residential"}}}, - }; - - const auto layout = gurka::detail::map_to_coordinates(ascii_map, grid_size_meters); - map = gurka::buildtiles(layout, ways, {}, {}, "test/data/exclude_unpaved"); - } -}; - -gurka::map ExcludeUnpavedTest::map = {}; - -TEST_F(ExcludeUnpavedTest, UnpavedRoadsInTheMiddle) { - // Without options - for (const auto& costing : kSupportedCostingModels) { - const auto result = gurka::do_action(valhalla::Options::route, map, {"I", "L"}, costing); - gurka::assert::raw::expect_path(result, {"IJ", "JK", "KL"}); - } - - // Use unpaved roads - for (const auto& costing : kSupportedCostingModels) { - const auto result = gurka::do_action(valhalla::Options::route, map, {"I", "L"}, costing, - {{"/costing_options/" + costing + "/exclude_unpaved", "0"}}); - gurka::assert::raw::expect_path(result, {"IJ", "JK", "KL"}); - } - - // Do not use unpaved roads - for (const auto& costing : kSupportedCostingModels) { - const auto result = gurka::do_action(valhalla::Options::route, map, {"I", "L"}, costing, - {{"/costing_options/" + costing + "/exclude_unpaved", "1"}}); - gurka::assert::raw::expect_path(result, {"IJ", "JM", "MN", "NK", "KL"}); - } -} - -TEST_F(ExcludeUnpavedTest, UnpavedRoadsUnsupported) { - const std::string start = "E"; - const std::string end = "L"; - for (const auto& costing : std::vector{"bicycle", "pedestrian"}) { - const auto result_0 = - gurka::do_action(valhalla::Options::route, map, {start, end}, costing, - {{"/costing_options/" + costing + "/exclude_unpaved", "1"}}); - EXPECT_EQ(result_0.trip().routes_size(), 1); - const auto result_1 = - gurka::do_action(valhalla::Options::route, map, {start, end}, costing, - {{"/costing_options/" + costing + "/exclude_unpaved", "0"}}); - EXPECT_EQ(result_1.trip().routes_size(), 1); - EXPECT_EQ(gurka::detail::get_paths(result_0), gurka::detail::get_paths(result_1)); - } - - // motor_scooter, motorcycle are unsupported costing models too, but the engine does not get routes - // through unpaved roads. These edges are not allowed in the Allowed and ReverseAllowed methods. It - // includes only roads that have surface greater than Surface::kDirt(See kMinimumMotorcycleSurface, - // kMinimumScooterSurface constants). -} - -TEST_F(ExcludeUnpavedTest, UnpavedRoadsInTheBeginning) { - // Without options - for (const auto& costing : kSupportedCostingModels) { - const auto result = gurka::do_action(valhalla::Options::route, map, {"E", "H"}, costing); - gurka::assert::raw::expect_path(result, {"EF", "FG", "GH"}); - } - - // Use unpaved roads - for (const auto& costing : kSupportedCostingModels) { - const auto result = gurka::do_action(valhalla::Options::route, map, {"E", "H"}, costing, - {{"/costing_options/" + costing + "/exclude_unpaved", "0"}}); - gurka::assert::raw::expect_path(result, {"EF", "FG", "GH"}); - } - - // Do not use unpaved roads - for (const auto& costing : kSupportedCostingModels) { - const auto result = gurka::do_action(valhalla::Options::route, map, {"E", "H"}, costing, - {{"/costing_options/" + costing + "/exclude_unpaved", "1"}}); - gurka::assert::raw::expect_path(result, {"EF", "FG", "GH"}); - } -} - -TEST_F(ExcludeUnpavedTest, UnpavedRoadsInTheEnd) { - // Without options - for (const auto& costing : kSupportedCostingModels) { - const auto result = gurka::do_action(valhalla::Options::route, map, {"G", "A"}, costing); - gurka::assert::raw::expect_path(result, {"GH", "HI", "IA"}); - } - - // Use unpaved roads - for (const auto& costing : kSupportedCostingModels) { - const auto result = gurka::do_action(valhalla::Options::route, map, {"G", "A"}, costing, - {{"/costing_options/" + costing + "/exclude_unpaved", "0"}}); - gurka::assert::raw::expect_path(result, {"GH", "HI", "IA"}); - } - - // Do not use unpaved roads - for (const auto& costing : kSupportedCostingModels) { - const auto result = gurka::do_action(valhalla::Options::route, map, {"G", "A"}, costing, - {{"/costing_options/" + costing + "/exclude_unpaved", "1"}}); - gurka::assert::raw::expect_path(result, {"GH", "HI", "IA"}); - } -} diff --git a/test/gurka/test_exclude_unpaved_smoothness.cc b/test/gurka/test_exclude_unpaved_smoothness.cc new file mode 100644 index 0000000000..cca8b773b2 --- /dev/null +++ b/test/gurka/test_exclude_unpaved_smoothness.cc @@ -0,0 +1,395 @@ +#include + +#include "gurka.h" +#include "test/test.h" +#include + +#if !defined(VALHALLA_SOURCE_DIR) +#define VALHALLA_SOURCE_DIR +#endif + +using namespace valhalla; +using namespace valhalla::baldr; +using namespace valhalla::gurka; +using namespace valhalla::mjolnir; + +namespace { +const std::vector kSupportedCostingModels = { + "auto", + "taxi", + "bus", + "truck", +}; +} // namespace + +class ExcludeUnpavedTest : public ::testing::Test { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + constexpr double grid_size_meters = 100.; + + const std::string ascii_map = R"( + E----F----G----H----I----A----J----K----L + | | + | | + | | + | | + M----N + )"; + + const gurka::ways ways = { + {"EF", {{"highway", "residential"}, {"surface", "compacted"}}}, + {"FG", {{"highway", "residential"}, {"surface", "compacted"}}}, + {"GH", {{"highway", "residential"}}}, + {"HI", {{"highway", "residential"}, {"surface", "unpaved"}}}, + {"IA", {{"highway", "residential"}, {"surface", "unpaved"}}}, + {"IJ", {{"highway", "residential"}}}, + {"JK", {{"highway", "residential"}, {"surface", "gravel"}}}, + {"KL", {{"highway", "residential"}}}, + {"JM", {{"highway", "residential"}}}, + {"MN", {{"highway", "residential"}}}, + {"NK", {{"highway", "residential"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, grid_size_meters); + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/exclude_unpaved"); + } +}; + +gurka::map ExcludeUnpavedTest::map = {}; + +TEST_F(ExcludeUnpavedTest, UnpavedRoadsInTheMiddle) { + // Without options + for (const auto& costing : kSupportedCostingModels) { + const auto result = gurka::do_action(valhalla::Options::route, map, {"I", "L"}, costing); + gurka::assert::raw::expect_path(result, {"IJ", "JK", "KL"}); + } + + // Use unpaved roads + for (const auto& costing : kSupportedCostingModels) { + const auto result = gurka::do_action(valhalla::Options::route, map, {"I", "L"}, costing, + {{"/costing_options/" + costing + "/exclude_unpaved", "0"}}); + gurka::assert::raw::expect_path(result, {"IJ", "JK", "KL"}); + } + + // Do not use unpaved roads + for (const auto& costing : kSupportedCostingModels) { + const auto result = gurka::do_action(valhalla::Options::route, map, {"I", "L"}, costing, + {{"/costing_options/" + costing + "/exclude_unpaved", "1"}}); + gurka::assert::raw::expect_path(result, {"IJ", "JM", "MN", "NK", "KL"}); + } +} + +TEST_F(ExcludeUnpavedTest, UnpavedRoadsUnsupported) { + const std::string start = "E"; + const std::string end = "L"; + for (const auto& costing : std::vector{"bicycle", "pedestrian"}) { + const auto result_0 = + gurka::do_action(valhalla::Options::route, map, {start, end}, costing, + {{"/costing_options/" + costing + "/exclude_unpaved", "1"}}); + EXPECT_EQ(result_0.trip().routes_size(), 1); + const auto result_1 = + gurka::do_action(valhalla::Options::route, map, {start, end}, costing, + {{"/costing_options/" + costing + "/exclude_unpaved", "0"}}); + EXPECT_EQ(result_1.trip().routes_size(), 1); + EXPECT_EQ(gurka::detail::get_paths(result_0), gurka::detail::get_paths(result_1)); + } + + // motor_scooter, motorcycle are unsupported costing models too, but the engine does not get routes + // through unpaved roads. These edges are not allowed in the Allowed and ReverseAllowed methods. It + // includes only roads that have surface greater than Surface::kDirt(See kMinimumMotorcycleSurface, + // kMinimumScooterSurface constants). +} + +TEST_F(ExcludeUnpavedTest, UnpavedRoadsInTheBeginning) { + // Without options + for (const auto& costing : kSupportedCostingModels) { + const auto result = gurka::do_action(valhalla::Options::route, map, {"E", "H"}, costing); + gurka::assert::raw::expect_path(result, {"EF", "FG", "GH"}); + } + + // Use unpaved roads + for (const auto& costing : kSupportedCostingModels) { + const auto result = gurka::do_action(valhalla::Options::route, map, {"E", "H"}, costing, + {{"/costing_options/" + costing + "/exclude_unpaved", "0"}}); + gurka::assert::raw::expect_path(result, {"EF", "FG", "GH"}); + } + + // Do not use unpaved roads + for (const auto& costing : kSupportedCostingModels) { + const auto result = gurka::do_action(valhalla::Options::route, map, {"E", "H"}, costing, + {{"/costing_options/" + costing + "/exclude_unpaved", "1"}}); + gurka::assert::raw::expect_path(result, {"EF", "FG", "GH"}); + } +} + +TEST_F(ExcludeUnpavedTest, UnpavedRoadsInTheEnd) { + // Without options + for (const auto& costing : kSupportedCostingModels) { + const auto result = gurka::do_action(valhalla::Options::route, map, {"G", "A"}, costing); + gurka::assert::raw::expect_path(result, {"GH", "HI", "IA"}); + } + + // Use unpaved roads + for (const auto& costing : kSupportedCostingModels) { + const auto result = gurka::do_action(valhalla::Options::route, map, {"G", "A"}, costing, + {{"/costing_options/" + costing + "/exclude_unpaved", "0"}}); + gurka::assert::raw::expect_path(result, {"GH", "HI", "IA"}); + } + + // Do not use unpaved roads + for (const auto& costing : kSupportedCostingModels) { + const auto result = gurka::do_action(valhalla::Options::route, map, {"G", "A"}, costing, + {{"/costing_options/" + costing + "/exclude_unpaved", "1"}}); + gurka::assert::raw::expect_path(result, {"GH", "HI", "IA"}); + } +} + +valhalla::gurka::map BuildPBF(const std::string& workdir) { + const std::string ascii_map = R"( + + A--------B-----1--C-------D + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "primary"}, {"smoothness", "impassable"}}}, + {"B1C", {{"highway", "primary"}, {"smoothness", "great"}}}, + }; + + const gurka::nodes nodes = {{"1", {{"barrier", "gate"}, {"smoothness", "impassable"}}}}; + + constexpr double gridsize = 100; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {5.1079374, 52.0887174}); + + auto pbf_filename = workdir + "/map.pbf"; + detail::build_pbf(layout, ways, nodes, {}, pbf_filename); + + valhalla::gurka::map result; + result.nodes = layout; + + return result; +} + +TEST(Standalone, SmoothnessAccess) { + + const std::string workdir = "test/data/gurka_smoothness_access"; + + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + + valhalla::gurka::map map = BuildPBF(workdir); + + const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/netherlands_admin.sqlite"}; + boost::property_tree::ptree& pt = map.config; + pt.put("mjolnir.tile_dir", workdir + "/tiles"); + pt.put("mjolnir.admin", sqlite); + + std::vector input_files = {workdir + "/map.pbf"}; + + build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, + false); + + GraphReader graph_reader(pt.get_child("mjolnir")); + + { + GraphId edge_id_1; + const DirectedEdge* edge_1 = nullptr; + GraphId edge_id_2; + const DirectedEdge* edge_2 = nullptr; + + std::tie(edge_id_1, edge_1, edge_id_2, edge_2) = findEdge(graph_reader, map.nodes, "AB", "B"); + // no access due to smoothness = impassable and are therefore tossed. + // edge_1 = AB + // edge_2 = BA + EXPECT_EQ(edge_1, nullptr); + EXPECT_EQ(edge_2, nullptr); + + std::tie(edge_id_1, edge_1, edge_id_2, edge_2) = findEdge(graph_reader, map.nodes, "B1C", "C"); + // edge_1 = B1C + // edge_2 = C1B + // edge is not tossed + EXPECT_NE(edge_1->forwardaccess(), 0); + EXPECT_NE(edge_1->reverseaccess(), 0); + EXPECT_NE(edge_2->forwardaccess(), 0); + EXPECT_NE(edge_2->reverseaccess(), 0); + + auto node_id = gurka::findNode(graph_reader, map.nodes, "1"); + const auto* node = graph_reader.nodeinfo(node_id); + // no access due to smoothness = impassable + EXPECT_EQ(node->access(), 0); + } +} + +TEST(Standalone, SmoothnessWithSurface) { + + const std::string ascii_map = R"( + A----B + )"; + + for (const auto& smoothness_tag : + std::map{{"smoothness", "excellent"}, + {"smoothness", "good"}, + {"smoothness", "intermediate"}, + {"smoothness", "bad"}, + {"smoothness", "very_bad"}, + {"smoothness", "horrible"}, + {"smoothness", "very_horrible"}, + {"smoothness", "blah"}}) { + const gurka::ways ways = { + {"AB", + {{"highway", "path"}, + {"foot", "yes"}, + {"bicycle", "yes"}, + {"horse", "yes"}, + {"surface", "ground"}, + {"trail_visibility", "good"}, + {"width", "0.5"}, + smoothness_tag}}, + }; + + constexpr double gridsize = 100; + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize, {5.1079374, 52.0887174}); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_smoothness_with_surface"); + + GraphReader graph_reader(map.config.get_child("mjolnir")); + + { + GraphId AB_edge_id; + const DirectedEdge* AB_edge = nullptr; + GraphId BA_edge_id; + const DirectedEdge* BA_edge = nullptr; + std::tie(AB_edge_id, AB_edge, BA_edge_id, BA_edge) = + findEdge(graph_reader, map.nodes, "AB", "B"); + EXPECT_NE(AB_edge, nullptr); + EXPECT_NE(BA_edge, nullptr); + + // surface always wins + EXPECT_EQ(AB_edge->surface(), Surface::kDirt); + EXPECT_EQ(BA_edge->surface(), Surface::kDirt); + } + } +} + +TEST(Standalone, SmoothnessNoSurface) { + + const std::string ascii_map = R"( + A----B + )"; + + for (const auto& smoothness_tag : + std::map{{"smoothness", "excellent"}, + {"smoothness", "good"}, + {"smoothness", "intermediate"}, + {"smoothness", "bad"}, + {"smoothness", "very_bad"}, + {"smoothness", "horrible"}, + {"smoothness", "very_horrible"}, + {"smoothness", "blah"}}) { + const gurka::ways ways = { + {"AB", + {{"highway", "path"}, + {"foot", "yes"}, + {"bicycle", "yes"}, + {"horse", "yes"}, + {"trail_visibility", "good"}, + {"width", "0.5"}, + smoothness_tag}}, + }; + + constexpr double gridsize = 100; + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize, {5.1079374, 52.0887174}); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_smoothness_no_surface"); + + GraphReader graph_reader(map.config.get_child("mjolnir")); + + { + GraphId AB_edge_id; + const DirectedEdge* AB_edge = nullptr; + GraphId BA_edge_id; + const DirectedEdge* BA_edge = nullptr; + std::tie(AB_edge_id, AB_edge, BA_edge_id, BA_edge) = + findEdge(graph_reader, map.nodes, "AB", "B"); + EXPECT_NE(AB_edge, nullptr); + EXPECT_NE(BA_edge, nullptr); + + if (smoothness_tag.second == "excellent" || smoothness_tag.second == "good") { + EXPECT_EQ(AB_edge->surface(), Surface::kPavedSmooth); + EXPECT_EQ(BA_edge->surface(), Surface::kPavedSmooth); + } else if (smoothness_tag.second == "intermediate") { + EXPECT_EQ(AB_edge->surface(), Surface::kPavedRough); + EXPECT_EQ(BA_edge->surface(), Surface::kPavedRough); + } else if (smoothness_tag.second == "bad") { + EXPECT_EQ(AB_edge->surface(), Surface::kCompacted); + EXPECT_EQ(BA_edge->surface(), Surface::kCompacted); + } else if (smoothness_tag.second == "very_bad") { + EXPECT_EQ(AB_edge->surface(), Surface::kDirt); + EXPECT_EQ(BA_edge->surface(), Surface::kDirt); + } else if (smoothness_tag.second == "horrible") { + EXPECT_EQ(AB_edge->surface(), Surface::kGravel); + EXPECT_EQ(BA_edge->surface(), Surface::kGravel); + } else if (smoothness_tag.second == "very_horrible") { + EXPECT_EQ(AB_edge->surface(), Surface::kPath); + EXPECT_EQ(BA_edge->surface(), Surface::kPath); + } else { // fallback to defaults + EXPECT_EQ(AB_edge->surface(), Surface::kCompacted); + EXPECT_EQ(BA_edge->surface(), Surface::kCompacted); + } + } + } +} +TEST(Standalone, SmoothnessWithTrackType) { + + const std::string ascii_map = R"( + A----B + )"; + + for (const auto& smoothness_tag : + std::map{{"smoothness", "excellent"}, + {"smoothness", "good"}, + {"smoothness", "intermediate"}, + {"smoothness", "bad"}, + {"smoothness", "very_bad"}, + {"smoothness", "horrible"}, + {"smoothness", "very_horrible"}, + {"smoothness", "blah"}}) { + const gurka::ways ways = { + {"AB", + {{"highway", "track"}, + {"tracktype", "grade1"}, + {"foot", "yes"}, + {"bicycle", "yes"}, + {"horse", "yes"}, + {"trail_visibility", "good"}, + {"width", "0.5"}, + smoothness_tag}}, + }; + + constexpr double gridsize = 100; + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize, {5.1079374, 52.0887174}); + auto map = gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_smoothness_no_surface"); + + GraphReader graph_reader(map.config.get_child("mjolnir")); + + { + GraphId AB_edge_id; + const DirectedEdge* AB_edge = nullptr; + GraphId BA_edge_id; + const DirectedEdge* BA_edge = nullptr; + std::tie(AB_edge_id, AB_edge, BA_edge_id, BA_edge) = + findEdge(graph_reader, map.nodes, "AB", "B"); + EXPECT_NE(AB_edge, nullptr); + EXPECT_NE(BA_edge, nullptr); + + // tracktype always wins + EXPECT_EQ(AB_edge->surface(), Surface::kPavedRough); + EXPECT_EQ(BA_edge->surface(), Surface::kPavedRough); + } + } +} From a9071e36c9cd081ebadd5fd30d8c67b1b29abe36 Mon Sep 17 00:00:00 2001 From: Adrika Mukherjee <132841831+ADMUKHE@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:19:29 +0100 Subject: [PATCH 150/198] Hard avoids highway ferry (#4524) Co-authored-by: amaury.zarzelli Co-authored-by: adrika.mukherjee Co-authored-by: Johannes Nonnenmacher Co-authored-by: Janusz --- CHANGELOG.md | 1 + docs/docs/api/turn-by-turn/api-reference.md | 19 +- proto/common.proto | 4 +- proto/options.proto | 5 + scripts/valhalla_build_config | 2 + src/baldr/location.cc | 7 +- src/loki/search.cc | 2 + src/loki/worker.cc | 24 ++- src/sif/autocost.cc | 18 +- src/sif/bicyclecost.cc | 4 +- src/sif/dynamiccost.cc | 10 +- src/sif/motorcyclecost.cc | 4 +- src/sif/motorscootercost.cc | 4 +- src/sif/pedestriancost.cc | 5 +- src/sif/truckcost.cc | 5 +- src/thor/worker.cc | 2 +- src/valhalla_run_matrix.cc | 2 +- src/worker.cc | 7 + test/bindings/python/valhalla.json | 5 +- test/gurka/test_ferry_connections.cc | 51 +++++- ...est_hard_exclusions_bridge_tunnel_tolls.cc | 125 +++++++++++++ test/gurka/test_hard_exclusions_highways.cc | 167 ++++++++++++++++++ test/gurka/test_search_filter.cc | 57 +++++- ..._option.txt => exclude_unpaved_option.txt} | 0 valhalla/baldr/location.h | 4 + valhalla/baldr/pathlocation.h | 4 + valhalla/loki/worker.h | 1 + valhalla/sif/dynamiccost.h | 41 ++++- valhalla/sif/edgelabel.h | 29 ++- 29 files changed, 571 insertions(+), 38 deletions(-) create mode 100644 test/gurka/test_hard_exclusions_bridge_tunnel_tolls.cc create mode 100644 test/gurka/test_hard_exclusions_highways.cc rename test_requests/{use_unpaved_roads_option.txt => exclude_unpaved_option.txt} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ada76adc9a..ee01b4cd15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -226,6 +226,7 @@ * ADDED: "has_transit_tiles" & "osm_changeset" to verbose status response [#4062](https://github.com/valhalla/valhalla/pull/4062) * ADDED: time awareness to CostMatrix for e.g. traffic support [#4071](https://github.com/valhalla/valhalla/pull/4071) * UPDATED: transifex translations [#4102](https://github.com/valhalla/valhalla/pull/4102) + * ADDED: costing parameters to exclude certain edges `exclude_tolls`, `exclude_bridges`, `exclude_tunnels`, `exclude_highways`, `exclude_ferries`. They need to be enabled in the config with `service_limits.allow_hard_exclusions`. Also added location search filters `exclude_ferry` and `exclude_toll` to complement these changes. [#4524](https://github.com/valhalla/valhalla/pull/4524) ## Release Date: 2023-01-03 Valhalla 3.3.0 * **Removed** diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 8eb1c72245..23ad25ff32 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -46,7 +46,7 @@ To build a route, you need to specify two `break` locations. In addition, you ca | `street_side_tolerance` | If your input coordinate is less than this tolerance away from the edge centerline then we set your side of street to none otherwise your side of street will be left or right depending on direction of travel. The default is 5 meters. | | `street_side_max_distance` | The max distance in meters that the input coordinates or display ll can be from the edge centerline for them to be used for determining the side of street. Beyond this distance the side of street is set to none. The default is 1000 meters. | | `street_side_cutoff` | Disables the `preferred_side` when set to `same` or `opposite` if the edge has a road class less than that provided by `street_side_cutoff`. The road class must be one of the following strings: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. The default value is `service_other` so that `preferred_side` will not be disabled for any edges. | -| `search_filter` | A set of optional filters to exclude candidate edges based on their attribution. The following exclusion filters are supported:
  • `exclude_tunnel` (boolean, defaults to `false`): whether to exclude roads marked as tunnels
  • `exclude_bridge` (boolean, defaults to `false`): whether to exclude roads marked as bridges
  • `exclude_ramp` (boolean, defaults to `false`): whether to exclude link roads marked as ramps, note that some turn channels are also marked as ramps
  • `exclude_closures` (boolean, defaults to `true`): whether to exclude roads considered closed due to live traffic closure. **Note:** This option cannot be set if `costing_options..ignore_closures` is also specified. An error is returned if both options are specified. **Note 2:** Ignoring closures at destination and source locations does NOT work for date_time type `0/1` & `2` respectively
  • `min_road_class` (string, defaults to `"service_other"`): lowest road class allowed
  • `max_road_class` (string, defaults to `"motorway"`): highest road class allowed
  • **BETA** `level` (float): if specified, will only consider edges that are on or traverse the passed floor level. It will set `search_cutoff` to a default value of 300 meters if no cutoff value is passed. Additionally, if a `search_cutoff` is passed, it will be clamped to 1000 meters.
Road classes from highest to lowest are: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. | +| `search_filter` | A set of optional filters to exclude candidate edges based on their attribution. The following exclusion filters are supported:
  • `exclude_tunnel` (boolean, defaults to `false`): whether to exclude roads marked as tunnels
  • `exclude_bridge` (boolean, defaults to `false`): whether to exclude roads marked as bridges
  • `exclude_toll` (boolean, defaults to `false`): whether to exclude toll
  • `exclude_ferry` (boolean, defaults to `false`): whether to exclude ferry
  • `exclude_ramp` (boolean, defaults to `false`): whether to exclude link roads marked as ramps, note that some turn channels are also marked as ramps
  • `exclude_closures` (boolean, defaults to `true`): whether to exclude roads considered closed due to live traffic closure.
    **Note:** This option cannot be set if `costing_options..ignore_closures` is also specified. An error is returned if both options are specified.
    **Note 2:** Ignoring closures at destination and source locations does NOT work for date_time type `0/1` & `2` respectively
  • `min_road_class` (string, defaults to `"service_other"`): lowest road class allowed
  • `max_road_class` (string, defaults to `"motorway"`): highest road class allowed
  • **BETA** `level` (float): if specified, will only consider edges that are on or traverse the passed floor level. It will set `search_cutoff` to a default value of 300 meters if no cutoff value is passed. Additionally, if a `search_cutoff` is passed, it will be clamped to 1000 meters.
Road classes from highest to lowest are: motorway, trunk, primary, secondary, tertiary, unclassified, residential, service_other. | | `preferred_layer` | The layer on which edges should be considered. If provided, edges whose layer does not match the provided value will be discarded from the candidate search. | Optionally, you can include the following location information without impacting the routing. This information is carried through the request and returned as a convenience. @@ -90,7 +90,7 @@ Costing methods can have several options that can be adjusted to develop the rou * Penalty options are fixed costs in seconds that are only added to the path cost. Penalties can influence the route path determination but do not add to the estimated time along the path. For example, add a `toll_booth_penalty` to create route paths that tend to avoid toll booths. Penalties must be in the range of 0.0 seconds to 43200.0 seconds (12 hours), otherwise a default value will be assigned. * Factor options are used to multiply the cost along an edge or road section in a way that influences the path to favor or avoid a particular attribute. Factor options do not impact estimated time along the path, though. Factors must be in the range 0.1 to 100000.0, where factors of 1.0 have no influence on cost. Anything outside of this range will be assigned a default value. Use a factor less than 1.0 to attempt to favor paths containing preferred attributes, and a value greater than 1.0 to avoid paths with undesirable attributes. Avoidance factors are more effective than favor factors at influencing a path. A factor's impact also depends on the length of road containing the specified attribute, as longer roads have more impact on the costing than very short roads. For this reason, penalty options tend to be better at influencing paths. -A special costing option is `shortest`, which, when `true`, will solely use distance as cost and disregard all other costs, penalties and factors. It's available for all costing models except `multimodal` & `bikeshare`. +A special costing option is `shortest`, which, when `true`, will solely use distance as cost and disregard all other costs, penalties and factors. It's available for all costing models except `multimodal` & `bikeshare`. Another special case is `disable_hierarchy_pruning` costing option. As the name indicates, `disable_hierarchy_pruning = true` will disable hierarchies in routing algorithms, which allows us to find the actual optimal route even in edge cases. For example, together with `shortest = true` they can find the actual shortest route. When `disable_hierarchy_pruning` is `true` and arc distances between source and target are not above the max limit, the actual optimal route will be calculated at the expense of performance. Note that if arc distances between locations exceed the max limit, `disable_hierarchy_pruning` is `true` will not be applied. This costing option is available for all motorized costing models, i.e `auto`, `motorcycle`, `motor_scooter`, `bus`, `truck` & `taxi`. For `bicycle` and `pedestrian` hierarchies are always disabled by default. @@ -234,7 +234,7 @@ These options are available for pedestrian costing methods. | `use_living_streets` | This value indicates the willingness to take living streets. This is a range of values between 0 and 1. Values near 0 attempt to avoid living streets and values near 1 will favor living streets. The default value is 0.6. Note that sometimes living streets are required to complete a route so values of 0 are not guaranteed to avoid living streets entirely. | | `use_tracks` | This value indicates the willingness to take track roads. This is a range of values between 0 and 1. Values near 0 attempt to avoid tracks and values near 1 will favor tracks a little bit. The default value is 0.5. Note that sometimes tracks are required to complete a route so values of 0 are not guaranteed to avoid tracks entirely. | | `use_hills` | This is a range of values from 0 to 1, where 0 attempts to avoid hills and steep grades even if it means a longer (time and distance) path, while 1 indicates the pedestrian does not fear hills and steeper grades. Based on the `use_hills` factor, penalties are applied to roads based on elevation change and grade. These penalties help the path avoid hilly roads in favor of flatter roads or less steep grades where available. Note that it is not always possible to find alternate paths to avoid hills (for example when route locations are in mountainous areas). The default value is 0.5. | -| `use_lit` | This value is a range of values from 0 to 1, where 0 indicates indifference towards lit streets, and 1 indicates that unlit streets should be avoided. Note that even with values near 1, there is no guarantee the returned route will include lit segments. The default value is 0. | +| `use_lit` | This value is a range of values from 0 to 1, where 0 indicates indifference towards lit streets, and 1 indicates that unlit streets should be avoided. Note that even with values near 1, there is no guarantee the returned route will include lit segments. The default value is 0. | | `service_penalty` | A penalty applied for transition to generic service road. The default penalty is 0. | | `service_factor` | A factor that modifies (multiplies) the cost when generic service roads are encountered. The default `service_factor` is 1. | | `destination_only_penalty` | A penalty applied when entering an road which is only allowed to enter if necessary to reach the [destination](https://wiki.openstreetmap.org/wiki/Tag:vehicle%3Ddestination) | @@ -259,6 +259,17 @@ These options are available for transit costing when the multimodal costing mode | `use_transfers` |User's desire to favor transfers. Range of values from 0 (try to avoid transfers) to 1 (totally comfortable with transfers).| | `filters` | A way to filter for one or more ~~`stops`~~ (TODO: need to re-enable), `routes`, or `operators`. Filters must contain a list of so-called Onestop IDs, which is (supposed to be) a unique identifier for GTFS data, and an `action`. The OneStop ID is simply the feeds's directory name and the object's GTFS ID separated by an underscore, i.e. a route with `route_id: AUR` in `routes.txt` from the feed `NYC` would have the OneStop ID `NYC_AUR`, similar with operators/agencies.
  • `ids`: any number of Onestop IDs
  • `action`: either `exclude` to exclude all of the `ids` listed in the filter or `include` to include only the `ids` listed in the filter
+##### Hard exclusions -> **EXPERIMENTAL** + +The following options are available for all costing methods. Those options are not available by default, the server config must have `service_limits.allow_hard_exclusions` set to true in order to allow them. If not allowed and any of the hard excludes is set to true, the server will return a warning and ignore the hard excludes. + +| Vehicle Options | Description | +| :-------------------------- | :----------- | +| `exclude_bridges` | This value indicates whether or not the path may include bridges. If `exclude_bridges` is set to true it is allowed to start and end with bridges, but is not allowed to have them in the middle of the route path, otherwise they are allowed. If set to true, it is highly plausible that no path will be found. Default false. | +| `exclude_tunnels` | This value indicates whether or not the path may include tunnels. If `exclude_tunnels` is set to true it is allowed to start and end with tunnels, but is not allowed to have them in the middle of the route path, otherwise they are allowed. If set to true, it is highly plausible that no path will be found. Default false. | +| `exclude_tolls` | This value indicates whether or not the path may include tolls. If `exclude_tolls` is set to true it is allowed to start and end with tolls, but is not allowed to have them in the middle of the route path, otherwise they are allowed. If set to true, it is highly plausible that no path will be found. Default false. | +| `exclude_highways` | This value indicates whether or not the path may include highways. If `exclude_highways` is set to true it is allowed to start and end with highways, but is not allowed to have them in the middle of the route path, otherwise they are allowed. If set to true, it is highly plausible that no path will be found. Default false. | +| `exclude_ferries` | This value indicates whether or not the path may include ferries. If `exclude_ferries` is set to true it is allowed to start and end with ferries, but is not allowed to have them in the middle of the route path, otherwise they are allowed. If set to true, it is highly plausible that no path will be found. Default false. | ##### Sample JSON payloads for multimodal requests with transit @@ -371,7 +382,7 @@ Basic trip information includes: | `units` | The specified units of length are returned, either kilometers or miles. | | `language` | The language of the narration instructions. If the user specified a language in the directions options and the specified language was supported - this returned value will be equal to the specified value. Otherwise, this value will be the default (en-US) language. | | `locations` | Location information is returned in the same form as it is entered with additional fields to indicate the side of the street. | -| `warnings` (optional) | This array may contain warning objects informing about deprecated request parameters, clamped values etc. | +| `warnings` (optional) | This array may contain warning objects informing about deprecated request parameters, clamped values etc. | The summary JSON object includes: diff --git a/proto/common.proto b/proto/common.proto index d2e8bb4230..1044eaf230 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -61,9 +61,11 @@ message SearchFilter { oneof has_exclude_closures { bool exclude_closures = 6;// whether to exclude roads marked as closed due to traffic [default = true] } + bool exclude_toll = 7; // whether to exclude toll routes from loki results [default = false] + bool exclude_ferry = 8; // whether to exclude ferry routes from loki results [default = false] oneof has_level { - float level = 7; // level to filter edges by + float level = 9; // level to filter edges by } } diff --git a/proto/options.proto b/proto/options.proto index 3f3795e8fd..233161c705 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -317,6 +317,11 @@ message Costing { float hgv_no_access_penalty = 85; } float use_truck_route = 86; + bool exclude_bridges = 87; + bool exclude_tunnels = 88; + bool exclude_tolls = 89; + bool exclude_highways = 90; + bool exclude_ferries = 91; } oneof has_options { diff --git a/scripts/valhalla_build_config b/scripts/valhalla_build_config index caea2e863e..efe0041078 100755 --- a/scripts/valhalla_build_config +++ b/scripts/valhalla_build_config @@ -273,6 +273,7 @@ config = { 'max_alternates': 2, 'max_exclude_polygons_length': 10000, 'max_distance_disable_hierarchy_culling': 0, + 'allow_hard_exclusions': False, }, 'statsd': { 'host': Optional(str), @@ -549,6 +550,7 @@ help_text = { 'max_alternates': 'Maximum number of alternate routes to allow in a request', 'max_exclude_polygons_length': 'Maximum total perimeter of all exclude_polygons in meters', 'max_distance_disable_hierarchy_culling': 'Maximum search distance allowed with hierarchy culling disabled', + 'allow_hard_exclusions': 'Whether hard exclusions, such as exclude_bridges, exclude_tunnels, exclude_tolls, exclude_highways or exclude_ferries are allowed on this server', }, 'statsd': { 'host': 'The statsd host address', diff --git a/src/baldr/location.cc b/src/baldr/location.cc index 2fc0669f9b..301bd436ab 100644 --- a/src/baldr/location.cc +++ b/src/baldr/location.cc @@ -11,12 +11,15 @@ Location::SearchFilter::SearchFilter(valhalla::RoadClass min_road_class, valhalla::RoadClass max_road_class, bool exclude_tunnel, bool exclude_bridge, + bool exclude_toll, bool exclude_ramp, + bool exclude_ferry, bool exclude_closures, float level) : min_road_class_(min_road_class), max_road_class_(max_road_class), - exclude_tunnel_(exclude_tunnel), exclude_bridge_(exclude_bridge), exclude_ramp_(exclude_ramp), - exclude_closures_(exclude_closures), level_(level) { + exclude_tunnel_(exclude_tunnel), exclude_bridge_(exclude_bridge), exclude_toll_(exclude_toll), + exclude_ramp_(exclude_ramp), exclude_ferry_(exclude_ferry), exclude_closures_(exclude_closures), + level_(level) { } // TODO: get defaults from config singleton diff --git a/src/loki/search.cc b/src/loki/search.cc index 9d35d1c68e..de1af85ddc 100644 --- a/src/loki/search.cc +++ b/src/loki/search.cc @@ -36,7 +36,9 @@ bool search_filter(const DirectedEdge* edge, // road class is outside of the min to max range. return (road_class > min_road_class || road_class < max_road_class) || (filter.exclude_tunnel_ && edge->tunnel()) || (filter.exclude_bridge_ && edge->bridge()) || + (filter.exclude_toll_ && edge->toll()) || (filter.exclude_ramp_ && (edge->use() == Use::kRamp)) || + (filter.exclude_ferry_ && (edge->use() == Use::kFerry || edge->use() == Use::kRailFerry)) || (filter.exclude_closures_ && (costing.flow_mask() & kCurrentFlowMask) && tile->IsClosed(edge)) || (filter.level_ != kMaxLevel && !tile->edgeinfo(edge).includes_level(filter.level_)); diff --git a/src/loki/worker.cc b/src/loki/worker.cc index c7bad30732..92632bb2db 100644 --- a/src/loki/worker.cc +++ b/src/loki/worker.cc @@ -92,6 +92,24 @@ void loki_worker_t::parse_costing(Api& api, bool allow_none) { throw valhalla_exception_t{124}; } + if (!allow_hard_exclusions) { + bool exclusion_detected = false; + for (auto& pair : options.costings()) { + auto opts = pair.second.options(); + exclusion_detected = exclusion_detected || opts.exclude_bridges() || opts.exclude_tolls() || + opts.exclude_tunnels() || opts.exclude_highways() || + opts.exclude_ferries(); + opts.set_exclude_bridges(false); + opts.set_exclude_tolls(false); + opts.set_exclude_tunnels(false); + opts.set_exclude_highways(false); + opts.set_exclude_ferries(false); + } + if (exclusion_detected) { + add_warning(api, 208); + } + } + const auto& costing_str = Costing_Enum_Name(options.costing_type()); try { // For the begin and end of multimodal we expect you to be walking @@ -184,7 +202,8 @@ loki_worker_t::loki_worker_t(const boost::property_tree::ptree& config, max_trace_shape(config.get("service_limits.trace.max_shape")), sample(config.get("additional_data.elevation", "")), max_elevation_shape(config.get("service_limits.skadi.max_shape")), - min_resample(config.get("service_limits.skadi.min_resample")) { + min_resample(config.get("service_limits.skadi.min_resample")), + allow_hard_exclusions(config.get("service_limits.allow_hard_exclusions", false)) { // Keep a string noting which actions we support, throw if one isnt supported Options::Action action; @@ -208,7 +227,7 @@ loki_worker_t::loki_worker_t(const boost::property_tree::ptree& config, kv.first == "max_timedep_distance_matrix" || kv.first == "max_alternates" || kv.first == "max_exclude_polygons_length" || kv.first == "max_distance_disable_hierarchy_culling" || kv.first == "skadi" || - kv.first == "status") { + kv.first == "status" || kv.first == "allow_hard_exclusions") { continue; } if (kv.first != "trace") { @@ -271,6 +290,7 @@ loki_worker_t::loki_worker_t(const boost::property_tree::ptree& config, // assign max_distance_disable_hierarchy_culling max_distance_disable_hierarchy_culling = config.get("service_limits.max_distance_disable_hierarchy_culling", 0.f); + allow_hard_exclusions = config.get("service_limits.allow_hard_exclusions", false); // signal that the worker started successfully started(); diff --git a/src/sif/autocost.cc b/src/sif/autocost.cc index e280e52dee..b5c3b02b93 100644 --- a/src/sif/autocost.cc +++ b/src/sif/autocost.cc @@ -429,7 +429,8 @@ bool AutoCost::Allowed(const baldr::DirectedEdge* edge, edge->surface() == Surface::kImpassable || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || (pred.closure_pruning() && IsClosed(edge, tile)) || - (exclude_unpaved_ && !pred.unpaved() && edge->unpaved()) || !IsHOVAllowed(edge)) { + (exclude_unpaved_ && !pred.unpaved() && edge->unpaved()) || !IsHOVAllowed(edge) || + CheckExclusions(edge, pred)) { return false; } @@ -454,7 +455,8 @@ bool AutoCost::AllowedReverse(const baldr::DirectedEdge* edge, opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || - (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved()) || !IsHOVAllowed(opp_edge)) { + (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved()) || !IsHOVAllowed(opp_edge) || + CheckExclusions(opp_edge, pred)) { return false; } @@ -792,7 +794,8 @@ bool BusCost::Allowed(const baldr::DirectedEdge* edge, edge->surface() == Surface::kImpassable || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || (pred.closure_pruning() && IsClosed(edge, tile)) || - (exclude_unpaved_ && !pred.unpaved() && edge->unpaved())) { + (exclude_unpaved_ && !pred.unpaved() && edge->unpaved()) || !IsHOVAllowed(edge) || + CheckExclusions(edge, pred)) { return false; } @@ -817,7 +820,8 @@ bool BusCost::AllowedReverse(const baldr::DirectedEdge* edge, opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || - (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved())) { + (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved()) || !IsHOVAllowed(opp_edge) || + CheckExclusions(opp_edge, pred)) { return false; } @@ -971,7 +975,8 @@ bool TaxiCost::Allowed(const baldr::DirectedEdge* edge, edge->surface() == Surface::kImpassable || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || (pred.closure_pruning() && IsClosed(edge, tile)) || - (exclude_unpaved_ && !pred.unpaved() && edge->unpaved())) { + (exclude_unpaved_ && !pred.unpaved() && edge->unpaved()) || !IsHOVAllowed(edge) || + CheckExclusions(edge, pred)) { return false; } @@ -996,7 +1001,8 @@ bool TaxiCost::AllowedReverse(const baldr::DirectedEdge* edge, opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || - (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved())) { + (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved()) || !IsHOVAllowed(opp_edge) || + CheckExclusions(opp_edge, pred)) { return false; } return DynamicCost::EvaluateRestrictions(access_mask_, opp_edge, false, tile, opp_edgeid, diff --git a/src/sif/bicyclecost.cc b/src/sif/bicyclecost.cc index 720c159547..cfa00e0985 100644 --- a/src/sif/bicyclecost.cc +++ b/src/sif/bicyclecost.cc @@ -546,7 +546,7 @@ bool BicycleCost::Allowed(const baldr::DirectedEdge* edge, (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx() && pred.mode() == TravelMode::kBicycle) || (!ignore_turn_restrictions_ && (pred.restrictions() & (1 << edge->localedgeidx()))) || - IsUserAvoidEdge(edgeid)) { + IsUserAvoidEdge(edgeid) || CheckExclusions(edge, pred)) { return false; } @@ -583,7 +583,7 @@ bool BicycleCost::AllowedReverse(const baldr::DirectedEdge* edge, (!pred.deadend() && pred.opp_local_idx() == edge->localedgeidx() && pred.mode() == TravelMode::kBicycle) || (!ignore_turn_restrictions_ && (opp_edge->restrictions() & (1 << pred.opp_local_idx()))) || - IsUserAvoidEdge(opp_edgeid)) { + IsUserAvoidEdge(opp_edgeid) || CheckExclusions(opp_edge, pred)) { return false; } diff --git a/src/sif/dynamiccost.cc b/src/sif/dynamiccost.cc index dfc07f8490..22c0b3d3ee 100644 --- a/src/sif/dynamiccost.cc +++ b/src/sif/dynamiccost.cc @@ -134,7 +134,9 @@ BaseCostingOptionsConfig::BaseCostingOptionsConfig() kDefaultUseTracks, 1.f}, use_living_streets_{0.f, kDefaultUseLivingStreets, 1.f}, use_lit_{0.f, kDefaultUseLit, 1.f}, - closure_factor_{kClosureFactorRange}, exclude_unpaved_(false), + closure_factor_{kClosureFactorRange}, exclude_unpaved_(false), exclude_bridges_(false), + exclude_tunnels_(false), exclude_tolls_(false), exclude_highways_(false), + exclude_ferries_(false), has_excludes_(false), exclude_cash_only_tolls_(false), include_hot_{false}, include_hov2_{false}, include_hov3_{ false} { } @@ -454,6 +456,12 @@ void ParseBaseCostOptions(const rapidjson::Value& json, JSON_PBF_DEFAULT(co, cfg.exclude_unpaved_, json, "/exclude_unpaved", exclude_unpaved); + JSON_PBF_DEFAULT_V2(co, cfg.exclude_bridges_, json, "/exclude_bridges", exclude_bridges); + JSON_PBF_DEFAULT_V2(co, cfg.exclude_tunnels_, json, "/exclude_tunnels", exclude_tunnels); + JSON_PBF_DEFAULT_V2(co, cfg.exclude_tolls_, json, "/exclude_tolls", exclude_tolls); + JSON_PBF_DEFAULT_V2(co, cfg.exclude_highways_, json, "/exclude_highways", exclude_highways); + JSON_PBF_DEFAULT_V2(co, cfg.exclude_ferries_, json, "/exclude_ferries", exclude_ferries); + JSON_PBF_DEFAULT(co, cfg.exclude_cash_only_tolls_, json, "/exclude_cash_only_tolls", exclude_cash_only_tolls); diff --git a/src/sif/motorcyclecost.cc b/src/sif/motorcyclecost.cc index 040fede3ee..55d4b4bfb9 100644 --- a/src/sif/motorcyclecost.cc +++ b/src/sif/motorcyclecost.cc @@ -362,7 +362,7 @@ bool MotorcycleCost::Allowed(const baldr::DirectedEdge* edge, ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_turn_restrictions_) || (edge->surface() > kMinimumMotorcycleSurface) || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || - (pred.closure_pruning() && IsClosed(edge, tile))) { + (pred.closure_pruning() && IsClosed(edge, tile)) || CheckExclusions(edge, pred)) { return false; } @@ -386,7 +386,7 @@ bool MotorcycleCost::AllowedReverse(const baldr::DirectedEdge* edge, ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || (opp_edge->surface() > kMinimumMotorcycleSurface) || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || - (pred.closure_pruning() && IsClosed(opp_edge, tile))) { + (pred.closure_pruning() && IsClosed(opp_edge, tile)) || CheckExclusions(opp_edge, pred)) { return false; } diff --git a/src/sif/motorscootercost.cc b/src/sif/motorscootercost.cc index 9923acb54a..526a8a920f 100644 --- a/src/sif/motorscootercost.cc +++ b/src/sif/motorscootercost.cc @@ -381,7 +381,7 @@ bool MotorScooterCost::Allowed(const baldr::DirectedEdge* edge, ((pred.restrictions() & (1 << edge->localedgeidx())) && !ignore_turn_restrictions_) || (edge->surface() > kMinimumScooterSurface) || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly()) || - (pred.closure_pruning() && IsClosed(edge, tile))) { + (pred.closure_pruning() && IsClosed(edge, tile)) || CheckExclusions(edge, pred)) { return false; } @@ -405,7 +405,7 @@ bool MotorScooterCost::AllowedReverse(const baldr::DirectedEdge* edge, ((opp_edge->restrictions() & (1 << pred.opp_local_idx())) && !ignore_turn_restrictions_) || (opp_edge->surface() > kMinimumScooterSurface) || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly()) || - (pred.closure_pruning() && IsClosed(opp_edge, tile))) { + (pred.closure_pruning() && IsClosed(opp_edge, tile)) || CheckExclusions(opp_edge, pred)) { return false; } diff --git a/src/sif/pedestriancost.cc b/src/sif/pedestriancost.cc index 1833b68851..abaaeb579b 100644 --- a/src/sif/pedestriancost.cc +++ b/src/sif/pedestriancost.cc @@ -648,7 +648,8 @@ bool PedestrianCost::Allowed(const baldr::DirectedEdge* edge, pred.mode() == TravelMode::kPedestrian) || // (edge->max_up_slope() > max_grade_ || edge->max_down_slope() > max_grade_) || // path_distance for multimodal is currently checked inside the algorithm - (!allow_transit_connections_ && pred.path_distance() + edge->length()) > max_distance_) { + ((!allow_transit_connections_ && pred.path_distance() + edge->length()) > max_distance_) || + CheckExclusions(edge, pred)) { return false; } @@ -683,7 +684,7 @@ bool PedestrianCost::AllowedReverse(const baldr::DirectedEdge* edge, pred.mode() == TravelMode::kPedestrian) || // (opp_edge->max_up_slope() > max_grade_ || opp_edge->max_down_slope() > max_grade_) || opp_edge->use() == Use::kTransitConnection || opp_edge->use() == Use::kEgressConnection || - opp_edge->use() == Use::kPlatformConnection) { + opp_edge->use() == Use::kPlatformConnection || CheckExclusions(opp_edge, pred)) { return false; } diff --git a/src/sif/truckcost.cc b/src/sif/truckcost.cc index de4a8152c7..5054dac7aa 100644 --- a/src/sif/truckcost.cc +++ b/src/sif/truckcost.cc @@ -463,7 +463,7 @@ inline bool TruckCost::Allowed(const baldr::DirectedEdge* edge, edge->surface() == Surface::kImpassable || IsUserAvoidEdge(edgeid) || (!allow_destination_only_ && !pred.destonly() && edge->destonly_hgv()) || (pred.closure_pruning() && IsClosed(edge, tile)) || - (exclude_unpaved_ && !pred.unpaved() && edge->unpaved())) { + (exclude_unpaved_ && !pred.unpaved() && edge->unpaved()) || CheckExclusions(edge, pred)) { return false; } @@ -487,7 +487,8 @@ bool TruckCost::AllowedReverse(const baldr::DirectedEdge* edge, opp_edge->surface() == Surface::kImpassable || IsUserAvoidEdge(opp_edgeid) || (!allow_destination_only_ && !pred.destonly() && opp_edge->destonly_hgv()) || (pred.closure_pruning() && IsClosed(opp_edge, tile)) || - (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved())) { + (exclude_unpaved_ && !pred.unpaved() && opp_edge->unpaved()) || + CheckExclusions(opp_edge, pred)) { return false; } diff --git a/src/thor/worker.cc b/src/thor/worker.cc index 0b6ddaf7f2..82cbb3a3e5 100644 --- a/src/thor/worker.cc +++ b/src/thor/worker.cc @@ -82,7 +82,7 @@ thor_worker_t::thor_worker_t(const boost::property_tree::ptree& config, kv.first == "max_timedep_distance_matrix" || kv.first == "max_alternates" || kv.first == "max_exclude_polygons_length" || kv.first == "skadi" || kv.first == "trace" || kv.first == "isochrone" || kv.first == "centroid" || kv.first == "status" || - kv.first == "max_distance_disable_hierarchy_culling") { + kv.first == "max_distance_disable_hierarchy_culling" || kv.first == "allow_hard_exclusions") { continue; } diff --git a/src/valhalla_run_matrix.cc b/src/valhalla_run_matrix.cc index 3a1160d049..cb65cce435 100644 --- a/src/valhalla_run_matrix.cc +++ b/src/valhalla_run_matrix.cc @@ -176,7 +176,7 @@ int main(int argc, char* argv[]) { kv.first == "trace" || kv.first == "isochrone" || kv.first == "centroid" || kv.first == "max_alternates" || kv.first == "max_exclude_polygons_length" || kv.first == "status" || kv.first == "max_timedep_distance_matrix" || - kv.first == "max_distance_disable_hierarchy_culling") { + kv.first == "max_distance_disable_hierarchy_culling" || kv.first == "allow_hard_exclusions") { continue; } max_matrix_distance.emplace(kv.first, config.get("service_limits." + kv.first + diff --git a/src/worker.cc b/src/worker.cc index 90f334fe19..6594db6dc7 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -158,6 +158,7 @@ const std::unordered_map warning_codes = { {205, R"("disable_hierarchy_pruning" exceeded the max distance, ignoring disable_hierarchy_pruning)"}, {206, R"(CostMatrix does not consider "targets" with "date_time" set, ignoring date_time)"}, {207, R"(TimeDistanceMatrix does not consider "shape_format", ignoring shape_format)"}, + {208, R"(Hard exclusions are not allowed on this server, ignoring hard excludes)"}, // 3xx is used when costing or location options were specified but we had to change them internally for some reason {300, R"(Many:Many CostMatrix was requested, but server only allows 1:Many TimeDistanceMatrix)"}, {301, R"(1:Many TimeDistanceMatrix was requested, but server only allows Many:Many CostMatrix)"}, @@ -407,9 +408,15 @@ void parse_location(valhalla::Location* location, // search_filter.exclude_bridge location->mutable_search_filter()->set_exclude_bridge( rapidjson::get(*search_filter, "/exclude_bridge", false)); + // search_filter.exclude_toll + location->mutable_search_filter()->set_exclude_toll( + rapidjson::get(*search_filter, "/exclude_toll", false)); // search_filter.exclude_ramp location->mutable_search_filter()->set_exclude_ramp( rapidjson::get(*search_filter, "/exclude_ramp", false)); + // search_filter.exclude_ferry + location->mutable_search_filter()->set_exclude_ferry( + rapidjson::get(*search_filter, "/exclude_ferry", false)); location->mutable_search_filter()->set_level( rapidjson::get(*search_filter, "/level", baldr::kMaxLevel)); // search_filter.exclude_closures diff --git a/test/bindings/python/valhalla.json b/test/bindings/python/valhalla.json index e9fd61ff71..941d7d1273 100644 --- a/test/bindings/python/valhalla.json +++ b/test/bindings/python/valhalla.json @@ -274,10 +274,11 @@ "max_timedep_distance": 500000, "max_alternates": 2, "max_exclude_polygons_length": 10000, - "max_distance_disable_hierarchy_culling": 0 + "max_distance_disable_hierarchy_culling": 0, + "allow_hard_exclusions": false }, "statsd": { "port": 8125, "prefix": "valhalla" } -} \ No newline at end of file +} diff --git a/test/gurka/test_ferry_connections.cc b/test/gurka/test_ferry_connections.cc index e2b4ae98a1..d5494c960f 100644 --- a/test/gurka/test_ferry_connections.cc +++ b/test/gurka/test_ferry_connections.cc @@ -680,6 +680,55 @@ TEST(Standalone, ReclassifyNothingReclassified) { EXPECT_TRUE(std::get<1>(not_upclassed3)->classification() == valhalla::baldr::RoadClass::kTertiary); } +class ExcludeFerryTest : public ::testing::TestWithParam { +protected: + static gurka::map map; + static void SetUpTestSuite() { + + const std::string ascii_map = R"( + A----1---B----C--D------E + )"; + + const gurka::ways ways = { + {"AB", {{"highway", "secondary"}}}, + {"BC", {{"route", "ferry"}}}, + {"CD", {{"highway", "secondary"}}}, + {"DE", {{"highway", "secondary"}}}, + }; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, 500); + + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/exclude_ferry"); + baldr::GraphReader graph_reader(map.config.get_child("mjolnir")); + } +}; + +gurka::map ExcludeFerryTest::map = {}; + +TEST_P(ExcludeFerryTest, ExcludeFerry) { + const auto default_result = gurka::do_action(valhalla::Options::route, map, {"1", "D"}, GetParam()); + gurka::assert::raw::expect_path(default_result, {"AB", "BC", "CD"}); + + try { + const auto result = + gurka::do_action(valhalla::Options::route, map, {"1", "D"}, GetParam(), + {{"/costing_options/" + GetParam() + "/exclude_ferries", "1"}}); + FAIL() << "Expected no path to be found"; + } catch (valhalla_exception_t& e) { EXPECT_EQ(e.code, 442); } catch (...) { + FAIL() << "Failed with unexpected error code"; + } +} +INSTANTIATE_TEST_SUITE_P(ExcludeFerry, + ExcludeFerryTest, + ::testing::Values("auto", + "truck", + "motor_scooter", + "pedestrian", + "bicycle", + "motorcycle", + "taxi", + "bus")); + INSTANTIATE_TEST_SUITE_P(FerryConnectionTest, FerryTest, - ::testing::Values("motorcar", "hgv", "moped", "motorcycle", "taxi", "bus")); + ::testing::Values("motorcar", "hgv", "moped", "motorcycle", "taxi", "bus")); \ No newline at end of file diff --git a/test/gurka/test_hard_exclusions_bridge_tunnel_tolls.cc b/test/gurka/test_hard_exclusions_bridge_tunnel_tolls.cc new file mode 100644 index 0000000000..0a9a5ca94c --- /dev/null +++ b/test/gurka/test_hard_exclusions_bridge_tunnel_tolls.cc @@ -0,0 +1,125 @@ +#include "gurka.h" + +#include + +using namespace valhalla; + +namespace { +const std::vector kSupportedCostingModels = {"auto", "taxi", "bus", + "truck", "pedestrian", "bicycle", + "motor_scooter", "motorcycle"}; + +const std::vector kExclusionParameters = {"exclude_bridges", "exclude_tunnels", + "exclude_tolls"}; + +constexpr double grid_size_meters = 100.; + +const std::string ascii_map = R"( + E----F----G----H----I----A----J----K----L + | | + | | + | | + | | + M----N + )"; + +const gurka::ways ways = { + {"EF", {{"highway", "residential"}, {"bridge", "yes"}, {"tunnel", "yes"}, {"toll", "yes"}}}, + {"FG", {{"highway", "residential"}, {"bridge", "yes"}, {"tunnel", "yes"}, {"toll", "yes"}}}, + {"GH", {{"highway", "residential"}}}, + {"HI", {{"highway", "residential"}, {"bridge", "yes"}, {"tunnel", "yes"}, {"toll", "yes"}}}, + {"IA", {{"highway", "residential"}}}, + {"IJ", {{"highway", "residential"}}}, + {"JK", {{"highway", "residential"}, {"bridge", "yes"}, {"tunnel", "yes"}, {"toll", "yes"}}}, + {"KL", {{"highway", "residential"}}}, + {"JM", {{"highway", "residential"}}}, + {"MN", {{"highway", "residential"}}}, + {"NK", {{"highway", "residential"}}}, +}; + +const auto layout = gurka::detail::map_to_coordinates(ascii_map, grid_size_meters); + +void check_result(const std::string& exclude_parameter_value, + const std::vector& waypoints, + const std::vector& expected_names, + const gurka::map& map, + const std::vector& props = {}) { + + const std::string& costing = props[0]; + const std::string& exclude_parameter = props[1]; + const auto result = gurka::do_action(valhalla::Options::route, map, waypoints, costing, + {{"/costing_options/" + costing + "/" + exclude_parameter, + exclude_parameter_value}}); + gurka::assert::raw::expect_path(result, expected_names); +} + +} // namespace + +class ExclusionTest : public ::testing::TestWithParam> { +protected: + static gurka::map map; + static gurka::map mapNotAllowed; + + static void SetUpTestSuite() { + + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/hard_exclude_bridge_tunnel_tolls", + {{"service_limits.allow_hard_exclusions", "true"}}); + mapNotAllowed = + gurka::buildtiles(layout, ways, {}, {}, "test/data/hard_exclude_bridge_tunnel_tolls"); + } +}; + +gurka::map ExclusionTest::map = {}; +gurka::map ExclusionTest::mapNotAllowed = {}; + +TEST_P(ExclusionTest, InTheMiddle) { + check_result("0", {"I", "L"}, {"IJ", "JK", "KL"}, map, GetParam()); + check_result("1", {"I", "L"}, {"IJ", "JM", "MN", "NK", "KL"}, map, GetParam()); +} + +TEST_P(ExclusionTest, InTheBeginning) { + check_result("0", {"E", "H"}, {"EF", "FG", "GH"}, map, GetParam()); + check_result("1", {"E", "H"}, {"EF", "FG", "GH"}, map, GetParam()); +} + +TEST_P(ExclusionTest, InTheEnd) { + check_result("0", {"H", "E"}, {"GH", "FG", "EF"}, map, GetParam()); + check_result("1", {"H", "E"}, {"GH", "FG", "EF"}, map, GetParam()); +} + +TEST_P(ExclusionTest, InTheMiddleNotAllowed) { + check_result("0", {"I", "L"}, {"IJ", "JK", "KL"}, mapNotAllowed, GetParam()); + try { + check_result("1", {"I", "L"}, {"IJ", "JM", "MN", "NK", "KL"}, mapNotAllowed, GetParam()); + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 145); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; +} + +TEST_P(ExclusionTest, InTheBeginningNotAllowed) { + check_result("0", {"E", "H"}, {"EF", "FG", "GH"}, mapNotAllowed, GetParam()); + try { + check_result("1", {"E", "H"}, {"EF", "FG", "GH"}, mapNotAllowed, GetParam()); + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 145); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; +} + +TEST_P(ExclusionTest, InTheEndNotAllowed) { + check_result("0", {"H", "E"}, {"GH", "FG", "EF"}, mapNotAllowed, GetParam()); + try { + check_result("1", {"H", "E"}, {"GH", "FG", "EF"}, mapNotAllowed, GetParam()); + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 145); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; +} + +INSTANTIATE_TEST_SUITE_P(ExcludePropsTest, ExclusionTest, ::testing::ValuesIn([]() { + std::vector> values; + for (const auto& costing : kSupportedCostingModels) { + for (const auto& param : kExclusionParameters) { + values.push_back({costing, param}); + } + } + return values; + }())); \ No newline at end of file diff --git a/test/gurka/test_hard_exclusions_highways.cc b/test/gurka/test_hard_exclusions_highways.cc new file mode 100644 index 0000000000..f4540f10be --- /dev/null +++ b/test/gurka/test_hard_exclusions_highways.cc @@ -0,0 +1,167 @@ +#include "gurka.h" + +#include + +using namespace valhalla; + +namespace { +const std::vector kCostingModelsExcludeHighways = {"auto", "taxi", "bus", "truck", + "motorcycle"}; + +const std::vector kCostingModelsNoHardExcludeSetA = {"auto", "taxi", "bus", "truck", + "motorcycle"}; + +const std::vector kCostingModelsNoHardExcludeSetB = {"pedestrian", "bicycle", + "motor_scooter"}; + +const std::vector kExclusionParameters = {"exclude_highways"}; + +constexpr double grid_size_meters = 100.; + +const std::string ascii_map = R"( + E----F----G----H----I----A----J----K----L----O----P----Q----R + | | | | + | | | | + | | | | + | | | | + M----N T----U + )"; + +const gurka::ways ways = { + {"EF", {{"highway", "motorway"}}}, {"FG", {{"highway", "residential"}}}, + {"GH", {{"highway", "residential"}}}, {"HI", {{"highway", "residential"}}}, + {"IA", {{"highway", "residential"}}}, {"IJ", {{"highway", "residential"}}}, + {"JK", {{"highway", "motorway"}}}, {"KL", {{"highway", "residential"}}}, + {"JM", {{"highway", "residential"}}}, {"MN", {{"highway", "residential"}}}, + {"NK", {{"highway", "residential"}}}, {"LO", {{"highway", "motorway"}}}, + {"OP", {{"highway", "motorway"}}}, {"PQ", {{"highway", "residential"}}}, + {"QR", {{"highway", "residential"}}}, {"OT", {{"highway", "residential"}}}, + {"TU", {{"highway", "residential"}}}, {"UP", {{"highway", "residential"}}}, +}; + +const auto layout = gurka::detail::map_to_coordinates(ascii_map, grid_size_meters); + +void check_result(const std::string& exclude_parameter_value, + const std::vector& waypoints, + const std::vector& expected_names, + const gurka::map& map, + const std::vector& props = {}) { + + const std::string& costing = props[0]; + const std::string& exclude_parameter = props[1]; + const auto result = gurka::do_action(valhalla::Options::route, map, waypoints, costing, + {{"/costing_options/" + costing + "/" + exclude_parameter, + exclude_parameter_value}}); + gurka::assert::raw::expect_path(result, expected_names); +} + +} // namespace + +class ExclusionTestExcludeHighways : public ::testing::TestWithParam> { +protected: + static gurka::map map; + static gurka::map mapNotAllowed; + + static void SetUpTestSuite() { + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/hard_exclude_highways", + {{"service_limits.allow_hard_exclusions", "true"}}); + + mapNotAllowed = gurka::buildtiles(layout, ways, {}, {}, "test/data/hard_exclude_highways"); + } +}; + +gurka::map ExclusionTestExcludeHighways::map = {}; +gurka::map ExclusionTestExcludeHighways::mapNotAllowed = {}; + +TEST_P(ExclusionTestExcludeHighways, ExcludeHighways) { + check_result("1", {"I", "L"}, {"IJ", "JM", "MN", "NK", "KL"}, map, GetParam()); +} + +TEST_P(ExclusionTestExcludeHighways, InTheBeginningWithNoExit) { + check_result("0", {"E", "H"}, {"EF", "FG", "GH"}, map, GetParam()); + check_result("1", {"E", "H"}, {"EF", "FG", "GH"}, map, GetParam()); +} + +// It does not exit to the residential OT but keeps driving on the motorway OP +TEST_P(ExclusionTestExcludeHighways, InTheBeginningWithPossibleExit) { + check_result("0", {"L", "U"}, {"LO", "OP", "UP"}, map, GetParam()); + check_result("1", {"L", "U"}, {"LO", "OP", "UP"}, map, GetParam()); +} + +TEST_P(ExclusionTestExcludeHighways, InTheBeginningNotAllowed) { + check_result("0", {"E", "H"}, {"EF", "FG", "GH"}, mapNotAllowed, GetParam()); + try { + check_result("1", {"E", "H"}, {"EF", "FG", "GH"}, mapNotAllowed, GetParam()); + } catch (const valhalla_exception_t& err) { EXPECT_EQ(err.code, 145); } catch (...) { + FAIL() << "Expected valhalla_exception_t."; + }; +} + +INSTANTIATE_TEST_SUITE_P(ExcludeHighwaysTests, + ExclusionTestExcludeHighways, + ::testing::ValuesIn([]() { + std::vector> values; + for (const auto& costing : kCostingModelsExcludeHighways) { + for (const auto& param : kExclusionParameters) { + values.push_back({costing, param}); + } + } + return values; + }())); + +class ExclusionTestNoHardExcludeHighways : public ::testing::TestWithParam> { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/hard_exclude_highways", + {{"service_limits.allow_hard_exclusions", "true"}}); + } +}; + +gurka::map ExclusionTestNoHardExcludeHighways::map = {}; + +TEST_P(ExclusionTestNoHardExcludeHighways, NoHardExcludeHighways) { + check_result("0", {"I", "L"}, {"IJ", "JK", "KL"}, map, GetParam()); +} + +INSTANTIATE_TEST_SUITE_P(NoHardExcludeHighwaysTests, + ExclusionTestNoHardExcludeHighways, + ::testing::ValuesIn([]() { + std::vector> values; + for (const auto& costing : kCostingModelsNoHardExcludeSetA) { + for (const auto& param : kExclusionParameters) { + values.push_back({costing, param}); + } + } + return values; + }())); + +class ExclusionTestNoHardExcludeHighwaysForOtherModes + : public ::testing::TestWithParam> { +protected: + static gurka::map map; + + static void SetUpTestSuite() { + map = gurka::buildtiles(layout, ways, {}, {}, "test/data/hard_exclude_highways", + {{"service_limits.allow_hard_exclusions", "true"}}); + } +}; + +gurka::map ExclusionTestNoHardExcludeHighwaysForOtherModes::map = {}; + +TEST_P(ExclusionTestNoHardExcludeHighwaysForOtherModes, NoHardExcludeHighwaysForOtherModes) { + check_result("0", {"I", "L"}, {"IJ", "JM", "MN", "NK", "KL"}, map, GetParam()); +} + +INSTANTIATE_TEST_SUITE_P(NoHardExcludeHighwaysForOtherModesTests, + ExclusionTestNoHardExcludeHighwaysForOtherModes, + ::testing::ValuesIn([]() { + std::vector> values; + for (const auto& costing : kCostingModelsNoHardExcludeSetB) { + for (const auto& param : kExclusionParameters) { + values.push_back({costing, param}); + } + } + return values; + }())); diff --git a/test/gurka/test_search_filter.cc b/test/gurka/test_search_filter.cc index 02a0f6af92..f3189c8fb1 100644 --- a/test/gurka/test_search_filter.cc +++ b/test/gurka/test_search_filter.cc @@ -1,3 +1,4 @@ + #include "gurka.h" #include "test.h" @@ -30,6 +31,10 @@ class SearchFilter : public ::testing::Test { \ 5 | \ 6 | F-----E + | | + | y + | | + G-x---H )"; const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize); const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}, @@ -37,8 +42,11 @@ class SearchFilter : public ::testing::Test { {"CD", {{"highway", "primary"}, {"oneway", "-1"}}}, {"AD", {{"highway", "primary"}}}, {"DE", {{"highway", "primary"}}}, - {"EF", {{"highway", "primary"}, {"bridge", "yes"}}}, - {"AF", {{"highway", "motorway_link"}}}}; + {"EF", {{"highway", "primary"}, {"bridge", "yes"}, {"toll", "yes"}}}, + {"AF", {{"highway", "motorway_link"}}}, + {"FG", {{"highway", "secondary"}}}, + {"GH", {{"route", "ferry"}}}, + {"HE", {{"highway", "secondary"}}}}; map = gurka::buildtiles(layout, ways, {}, {}, "test/data/search_filter"); } }; @@ -222,6 +230,51 @@ TEST_F(SearchFilter, ExcludeRamp) { gurka::assert::osrm::expect_steps(result_filtered, {"AD", "AB", "BC"}); gurka::assert::raw::expect_path(result_filtered, {"AD", "AB", "BC"}); } +TEST_F(SearchFilter, ExcludeFerry) { + auto from = "x"; + auto to = "y"; + const std::string& request_unfiltered = + (boost::format(R"({"locations":[{"lat":%s,"lon":%s},{"lat":%s,"lon":%s}],"costing":"auto"})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + auto result_unfiltered = gurka::do_action(valhalla::Options::route, map, request_unfiltered); + gurka::assert::osrm::expect_steps(result_unfiltered, {"GH", "HE"}); + gurka::assert::raw::expect_path(result_unfiltered, {"GH", "HE"}); + + const std::string& request_filtered = + (boost::format( + R"({"locations":[{"lat":%s,"lon":%s,"search_filter":{"exclude_ferry":true}},{"lat":%s,"lon":%s}],"costing":"auto"})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + auto result_filtered = gurka::do_action(valhalla::Options::route, map, request_filtered); + + gurka::assert::osrm::expect_steps(result_filtered, {"FG", "EF", "HE"}); + gurka::assert::raw::expect_path(result_filtered, {"FG", "EF", "HE"}); +} +TEST_F(SearchFilter, ExcludeToll) { + auto from = "6"; + auto to = "3"; + const std::string& request_unfiltered = + (boost::format(R"({"locations":[{"lat":%s,"lon":%s},{"lat":%s,"lon":%s}],"costing":"auto"})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + auto result_unfiltered = gurka::do_action(valhalla::Options::route, map, request_unfiltered); + gurka::assert::osrm::expect_steps(result_unfiltered, {"EF", "DE"}); + gurka::assert::raw::expect_path(result_unfiltered, {"EF", "DE", "CD"}); + + const std::string& request_filtered = + (boost::format( + R"({"locations":[{"lat":%s,"lon":%s,"search_filter":{"exclude_toll":true}},{"lat":%s,"lon":%s}],"costing":"auto"})") % + std::to_string(map.nodes.at(from).lat()) % std::to_string(map.nodes.at(from).lng()) % + std::to_string(map.nodes.at(to).lat()) % std::to_string(map.nodes.at(to).lng())) + .str(); + auto result_filtered = gurka::do_action(valhalla::Options::route, map, request_filtered); + gurka::assert::osrm::expect_steps(result_filtered, {"AD", "CD"}); + gurka::assert::raw::expect_path(result_filtered, {"AD", "CD"}); +} /*************************************************************/ namespace { diff --git a/test_requests/use_unpaved_roads_option.txt b/test_requests/exclude_unpaved_option.txt similarity index 100% rename from test_requests/use_unpaved_roads_option.txt rename to test_requests/exclude_unpaved_option.txt diff --git a/valhalla/baldr/location.h b/valhalla/baldr/location.h index 147dfad505..0fc215cece 100644 --- a/valhalla/baldr/location.h +++ b/valhalla/baldr/location.h @@ -44,7 +44,9 @@ struct Location { valhalla::RoadClass max_road_class = valhalla::RoadClass::kMotorway, bool exclude_tunnel = false, bool exclude_bridge = false, + bool exclude_toll_ = false, bool exclude_ramp = false, + bool exclude_ferry_ = false, bool exclude_closures = true, float level = kMaxLevel); @@ -52,7 +54,9 @@ struct Location { valhalla::RoadClass max_road_class_; bool exclude_tunnel_; bool exclude_bridge_; + bool exclude_toll_; bool exclude_ramp_; + bool exclude_ferry_; bool exclude_closures_; float level_; diff --git a/valhalla/baldr/pathlocation.h b/valhalla/baldr/pathlocation.h index 853cb80071..a895c14e97 100644 --- a/valhalla/baldr/pathlocation.h +++ b/valhalla/baldr/pathlocation.h @@ -127,7 +127,9 @@ struct PathLocation : public Location { l->mutable_search_filter()->set_max_road_class(pl.search_filter_.max_road_class_); l->mutable_search_filter()->set_exclude_tunnel(pl.search_filter_.exclude_tunnel_); l->mutable_search_filter()->set_exclude_bridge(pl.search_filter_.exclude_bridge_); + l->mutable_search_filter()->set_exclude_toll(pl.search_filter_.exclude_toll_); l->mutable_search_filter()->set_exclude_ramp(pl.search_filter_.exclude_ramp_); + l->mutable_search_filter()->set_exclude_ferry(pl.search_filter_.exclude_ferry_); l->mutable_search_filter()->set_exclude_closures(pl.search_filter_.exclude_closures_); l->mutable_search_filter()->set_level(pl.search_filter_.level_); @@ -227,7 +229,9 @@ struct PathLocation : public Location { l.search_filter_.max_road_class_ = loc.search_filter().max_road_class(); l.search_filter_.exclude_tunnel_ = loc.search_filter().exclude_tunnel(); l.search_filter_.exclude_bridge_ = loc.search_filter().exclude_bridge(); + l.search_filter_.exclude_toll_ = loc.search_filter().exclude_toll(); l.search_filter_.exclude_ramp_ = loc.search_filter().exclude_ramp(); + l.search_filter_.exclude_ferry_ = loc.search_filter().exclude_ferry(); l.search_filter_.exclude_closures_ = loc.search_filter().exclude_closures(); l.search_filter_.level_ = loc.search_filter().level(); } diff --git a/valhalla/loki/worker.h b/valhalla/loki/worker.h index 84a418f7f1..85ec862dcb 100644 --- a/valhalla/loki/worker.h +++ b/valhalla/loki/worker.h @@ -104,6 +104,7 @@ class loki_worker_t : public service_worker_t { float min_resample; unsigned int max_alternates; bool allow_verbose; + bool allow_hard_exclusions; // add max_distance_disable_hierarchy_culling float max_distance_disable_hierarchy_culling; diff --git a/valhalla/sif/dynamiccost.h b/valhalla/sif/dynamiccost.h index bf89d9a3de..d9f42a29c1 100644 --- a/valhalla/sif/dynamiccost.h +++ b/valhalla/sif/dynamiccost.h @@ -289,6 +289,27 @@ class DynamicCost { const uint32_t tz_index, uint8_t& restriction_idx) const = 0; + /** + * Checks if any edge exclusion is present. + * This checks if bridges, tolls, tunnels, ferries + * or highways are excluded in the request. + * @param edge Pointer to a directed edge. + * @param pred Predecessor edge information. + * @return Returns true if edge should be excluded. + */ + inline bool CheckExclusions(const baldr::DirectedEdge* edge, const EdgeLabel& pred) const { + return has_excludes_ && + ((exclude_bridges_ && !pred.bridge() && edge->bridge()) || + (exclude_tunnels_ && !pred.tunnel() && edge->tunnel()) || + (exclude_tolls_ && !pred.toll() && edge->toll()) || + (exclude_highways_ && pred.classification() != baldr::RoadClass::kMotorway && + edge->classification() == baldr::RoadClass::kMotorway) || + (exclude_ferries_ && + !(pred.use() == baldr::Use::kFerry || pred.use() == baldr::Use::kRailFerry) && + (edge->use() == baldr::Use::kFerry || edge->use() == baldr::Use::kRailFerry)) || + (edge->is_shortcut() && (exclude_bridges_ || exclude_tunnels_))); + } + /** * Checks if access is allowed for the provided node. Node access can * be restricted if bollards are present. @@ -1050,6 +1071,12 @@ class DynamicCost { bool penalize_uturns_; bool exclude_unpaved_{false}; + bool exclude_bridges_{false}; + bool exclude_tunnels_{false}; + bool exclude_tolls_{false}; + bool exclude_highways_{false}; + bool exclude_ferries_{false}; + bool has_excludes_{false}; bool exclude_cash_only_tolls_{false}; @@ -1150,7 +1177,13 @@ class DynamicCost { fixed_speed_ == baldr::kDisableFixedSpeed ? costing_options.top_speed() : fixed_speed_; exclude_unpaved_ = costing_options.exclude_unpaved(); - + exclude_bridges_ = costing_options.exclude_bridges(); + exclude_tunnels_ = costing_options.exclude_tunnels(); + exclude_tolls_ = costing_options.exclude_tolls(); + exclude_highways_ = costing_options.exclude_highways(); + exclude_ferries_ = costing_options.exclude_ferries(); + has_excludes_ = exclude_bridges_ || exclude_tunnels_ || exclude_tolls_ || exclude_highways_ || + exclude_ferries_; exclude_cash_only_tolls_ = costing_options.exclude_cash_only_tolls(); } @@ -1259,6 +1292,12 @@ struct BaseCostingOptionsConfig { ranged_default_t closure_factor_; bool exclude_unpaved_; + bool exclude_bridges_; + bool exclude_tunnels_; + bool exclude_tolls_; + bool exclude_highways_; + bool exclude_ferries_; + bool has_excludes_; bool exclude_cash_only_tolls_ = false; diff --git a/valhalla/sif/edgelabel.h b/valhalla/sif/edgelabel.h index cf37903dad..771d6cb83b 100644 --- a/valhalla/sif/edgelabel.h +++ b/valhalla/sif/edgelabel.h @@ -38,7 +38,7 @@ class EdgeLabel { endnode_(baldr::kInvalidGraphId), use_(0), classification_(0), shortcut_(0), dest_only_(0), origin_(0), destination_(0), toll_(0), not_thru_(0), deadend_(0), on_complex_rest_(0), closure_pruning_(0), path_id_(0), restriction_idx_(0), internal_turn_(0), unpaved_(0), - has_measured_speed_(0), hgv_access_(0), cost_(0, 0), sortcost_(0) { + has_measured_speed_(0), hgv_access_(0), bridge_(0), tunnel_(0), cost_(0, 0), sortcost_(0) { assert(path_id_ <= baldr::kMaxMultiPathId); } @@ -87,8 +87,8 @@ class EdgeLabel { edge->end_restriction()), closure_pruning_(closure_pruning), path_id_(path_id), restriction_idx_(restriction_idx), internal_turn_(static_cast(internal_turn)), unpaved_(edge->unpaved()), - has_measured_speed_(has_measured_speed), hgv_access_(hgv_access), cost_(cost), - sortcost_(sortcost) { + has_measured_speed_(has_measured_speed), hgv_access_(hgv_access), bridge_(edge->bridge()), + tunnel_(edge->tunnel()), cost_(cost), sortcost_(sortcost) { dest_only_ = destonly ? destonly : edge->destonly(); assert(path_id_ <= baldr::kMaxMultiPathId); } @@ -373,6 +373,21 @@ class EdgeLabel { return unpaved_; } + /** + * Get the bridge flag. + * @return Returns true if the edge is a bridge, otherwise false. + */ + bool bridge() const { + return bridge_; + } + + /** + * Get the tunnel flag. + * @return Returns true if the edge is a tunnel, otherwise false. + */ + bool tunnel() const { + return tunnel_; + } /** * Does it have HGV access? * @return Returns true if the (opposing) edge had HGV access @@ -443,9 +458,15 @@ class EdgeLabel { // Flag indicating edge is an unpaved road. uint32_t unpaved_ : 1; uint32_t has_measured_speed_ : 1; + // Flag if this edge had HGV access uint32_t hgv_access_ : 1; - uint32_t spare : 12; + // Flag indicating edge is a bridge. + uint32_t bridge_ : 1; + // Flag indicating edge is a tunnel. + uint32_t tunnel_ : 1; + + uint32_t spare : 10; Cost cost_; // Cost and elapsed time along the path. float sortcost_; // Sort cost - includes A* heuristic. From 18984b444962709d9d3ab02f95fa9c6dc84542d7 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Thu, 31 Oct 2024 16:41:54 +0100 Subject: [PATCH 151/198] Optionally add admin crossings to route response (#4941) Co-authored-by: Kevin Kreiser --- CHANGELOG.md | 1 + docs/docs/api/turn-by-turn/api-reference.md | 1 + proto/options.proto | 1 + src/tyr/route_serializer_valhalla.cc | 38 ++ src/worker.cc | 5 + test/gurka/test_admin.cc | 398 +++++++++++++++++++- 6 files changed, 440 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee01b4cd15..3c9fcca195 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * FIXED: Fix ability to use Valhalla via cmake `add_subdirectory` [#4930](https://github.com/valhalla/valhalla/pull/4930) * **Enhancement** * ADDED: Consider smoothness in all profiles that use surface [#4949](https://github.com/valhalla/valhalla/pull/4949) + * ADDED: `admin_crossings` request parameter for `/route` [#4941](https://github.com/valhalla/valhalla/pull/4941) ## Release Date: 2024-10-10 Valhalla 3.5.1 * **Removed** diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 23ad25ff32..5f7c3ccf64 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -364,6 +364,7 @@ For example a bus request with the result in Spanish using the OSRM (Open Source | `linear_references` | When present and `true`, the successful `route` response will include a key `linear_references`. Its value is an array of base64-encoded [OpenLR location references][openlr], one for each graph edge of the road network matched by the input trace. | | `prioritize_bidirectional` | Prioritize `bidirectional a*` when `date_time.type = depart_at/current`. By default `time_dependent_forward a*` is used in these cases, but `bidirectional a*` is much faster. Currently it does not update the time (and speeds) when searching for the route path, but the ETA on that route is recalculated based on the time-dependent speeds | | `roundabout_exits` | A boolean indicating whether exit instructions at roundabouts should be added to the output or not. Default is true. | +| `admin_crossings` | When present and `true`, the successful route summary will include the two keys `admins` and `admin_crossings`. `admins` is an array of administrative regions the route lies within. `admin_crossings` is an array of objects that contain `from_admin_index` and `to_admin_index`, which are indices into the `admins` array. They also contain `from_shape_index` and `to_shape_index`, which are start and end indices of the edge along which an administrative boundary is crossed. | [openlr]: https://www.openlr-association.com/fileadmin/user_upload/openlr-whitepaper_v1.5.pdf diff --git a/proto/options.proto b/proto/options.proto index 233161c705..af2837ca73 100644 --- a/proto/options.proto +++ b/proto/options.proto @@ -503,4 +503,5 @@ message Options { bool voice_instructions = 57; // Whether to return voiceInstructions in the OSRM serializer response bool dedupe = 58; // Keep track of edges and override their properties during expansion, // ensuring that each edge appears in the output only once. [default = false] + bool admin_crossings = 59; // Include administrative boundary crossings } diff --git a/src/tyr/route_serializer_valhalla.cc b/src/tyr/route_serializer_valhalla.cc index 39d27d4c08..bb92ca3311 100644 --- a/src/tyr/route_serializer_valhalla.cc +++ b/src/tyr/route_serializer_valhalla.cc @@ -531,6 +531,44 @@ void legs(const valhalla::Api& api, int route_index, rapidjson::writer_wrapper_t } writer.start_object("summary"); + + // Does the user want admin info? + if (api.options().admin_crossings()) { + // write the admin array + writer.start_array("admins"); + for (const auto& admin : trip_leg_itr->admin()) { + writer.start_object(); + writer("country_code", admin.country_code()); + writer("country_text", admin.country_text()); + writer("state_code", admin.state_code()); + writer("state_text", admin.state_text()); + writer.end_object(); + } + writer.end_array(); + + if (trip_leg_itr->admin_size() > 1) { + // write the admin crossings + auto node_itr = trip_leg_itr->node().begin(); + auto next_node_itr = trip_leg_itr->node().begin(); + next_node_itr++; + writer.start_array("admin_crossings"); + + while (next_node_itr != trip_leg_itr->node().end()) { + if (next_node_itr->admin_index() != node_itr->admin_index()) { + writer.start_object(); + writer("from_admin_index", static_cast(node_itr->admin_index())); + writer("to_admin_index", static_cast(next_node_itr->admin_index())); + writer("begin_shape_index", static_cast(node_itr->edge().begin_shape_index())); + writer("end_shape_index", static_cast(node_itr->edge().end_shape_index())); + writer.end_object(); + } + ++node_itr; + ++next_node_itr; + } + writer.end_array(); + } + } + writer("has_time_restrictions", has_time_restrictions); writer("has_toll", has_toll); writer("has_highway", has_highway); diff --git a/src/worker.cc b/src/worker.cc index 6594db6dc7..f0d9506284 100644 --- a/src/worker.cc +++ b/src/worker.cc @@ -881,6 +881,11 @@ void from_json(rapidjson::Document& doc, Options::Action action, Api& api) { options.set_linear_references(*linear_references); } + auto admin_crossings = rapidjson::get_optional(doc, "/admin_crossings"); + if (admin_crossings) { + options.set_admin_crossings(*admin_crossings); + } + // whatever our costing is, check to see if we are going to ignore_closures std::stringstream ss; ss << "/costing_options/" << costing_str << "/ignore_closures"; diff --git a/test/gurka/test_admin.cc b/test/gurka/test_admin.cc index 0964dfe546..94d622755a 100644 --- a/test/gurka/test_admin.cc +++ b/test/gurka/test_admin.cc @@ -1,4 +1,6 @@ #include "gurka.h" +#include "mjolnir/adminbuilder.h" +#include "test.h" #include #if !defined(VALHALLA_SOURCE_DIR) @@ -9,7 +11,7 @@ using namespace valhalla; const std::unordered_map build_config{ {"mjolnir.data_processing.use_admin_db", "false"}}; -class Admin : public ::testing::Test { +class AdminTest : public ::testing::Test { protected: static gurka::map map; @@ -38,13 +40,13 @@ class Admin : public ::testing::Test { map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/gurka_admin", build_config); } }; -gurka::map Admin::map = {}; +gurka::map AdminTest::map = {}; Api api; rapidjson::Document d; /*************************************************************/ -TEST_F(Admin, Iso) { +TEST_F(AdminTest, Iso) { auto result = gurka::do_action(valhalla::Options::route, map, {"A", "C"}, "auto"); // rest_area @@ -58,7 +60,7 @@ TEST_F(Admin, Iso) { EXPECT_TRUE(leg.node(1).edge().drive_on_left()); } -TEST_F(Admin, test_admin_response) { +TEST_F(AdminTest, test_osrm_response) { auto result = gurka::do_action(valhalla::Options::route, map, {"A", "C"}, "auto"); auto d = gurka::convert_to_json(result, valhalla::Options_Format_osrm); @@ -93,3 +95,391 @@ TEST_F(Admin, test_admin_response) { EXPECT_TRUE(steps[step_index]["intersections"][0].HasMember("admin_index")); EXPECT_EQ(steps[step_index]["intersections"][0]["admin_index"].GetInt(), 1); } + +/**************************************************************************** */ + +TEST(Standalone, AdminCrossingsCountry) { + constexpr double gridsize_metres = 100; + + // Border between C and D + const std::string ascii_map = R"( + A + | + B + | + C + --\-- + D + \ + E + | + F + | + G + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}, {"BC", {{"highway", "motorway"}}}, + {"CD", {{"highway", "motorway"}}}, {"DE", {{"highway", "motorway"}}}, + {"EF", {{"highway", "motorway"}}}, {"FG", {{"highway", "motorway"}}}}; + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-111.962238354, 49.003392362}); + const auto map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_admin_country", + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}}); + std::string result_json; + rapidjson::Document result; + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto", + {{"/admin_crossings", "1"}}, {}, &result_json); + result.Parse(result_json.c_str()); + + ASSERT_EQ(result["trip"]["legs"].Size(), 1); + + // Expect admin list at leg level + auto summary = result["trip"]["legs"][0]["summary"].GetObject(); + ASSERT_EQ(summary["admins"].Size(), 2); + EXPECT_TRUE(summary.HasMember("admins")); + EXPECT_STREQ(summary["admins"][0]["country_code"].GetString(), "CA"); + EXPECT_STREQ(summary["admins"][0]["state_code"].GetString(), ""); + EXPECT_STREQ(summary["admins"][0]["country_text"].GetString(), "Canada"); + EXPECT_STREQ(summary["admins"][0]["state_text"].GetString(), ""); + EXPECT_STREQ(summary["admins"][1]["country_code"].GetString(), "US"); + EXPECT_STREQ(summary["admins"][1]["state_code"].GetString(), ""); + EXPECT_STREQ(summary["admins"][1]["country_text"].GetString(), "United States"); + EXPECT_STREQ(summary["admins"][1]["state_text"].GetString(), ""); + auto crossings = summary["admin_crossings"].GetArray(); + + // Total of 1 crossing + EXPECT_EQ(crossings.Size(), 1); + EXPECT_EQ(crossings[0]["from_admin_index"], 0); + EXPECT_EQ(crossings[0]["to_admin_index"], 1); + EXPECT_EQ(crossings[0]["begin_shape_index"].GetUint64(), 2); + EXPECT_EQ(crossings[0]["end_shape_index"].GetUint64(), 3); +} + +TEST(Standalone, AdminCrossingsState) { + constexpr double gridsize_metres = 100; + + // border between E and F + const std::string ascii_map = R"( + | + A-B-C-D-E+F-G + | + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}, {"BC", {{"highway", "motorway"}}}, + {"CD", {{"highway", "motorway"}}}, {"DE", {{"highway", "motorway"}}}, + {"EF", {{"highway", "motorway"}}}, {"FG", {{"highway", "motorway"}}}}; + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-80.527768076, 41.934220471}); + const auto map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_admin_state", + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}}); + std::string result_json; + rapidjson::Document result; + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto", + {{"/admin_crossings", "1"}}, {}, &result_json); + result.Parse(result_json.c_str()); + + ASSERT_EQ(result["trip"]["legs"].Size(), 1); + + // Expect admin list at leg level + auto summary = result["trip"]["legs"][0]["summary"].GetObject(); + ASSERT_EQ(summary["admins"].Size(), 2); + EXPECT_TRUE(summary.HasMember("admins")); + EXPECT_STREQ(summary["admins"][0]["country_code"].GetString(), "US"); + EXPECT_STREQ(summary["admins"][0]["state_code"].GetString(), "OH"); + EXPECT_STREQ(summary["admins"][0]["country_text"].GetString(), "United States"); + EXPECT_STREQ(summary["admins"][0]["state_text"].GetString(), "Ohio"); + EXPECT_STREQ(summary["admins"][1]["country_code"].GetString(), "US"); + EXPECT_STREQ(summary["admins"][1]["state_code"].GetString(), "PA"); + EXPECT_STREQ(summary["admins"][1]["country_text"].GetString(), "United States"); + EXPECT_STREQ(summary["admins"][1]["state_text"].GetString(), "Pennsylvania"); + auto crossings = summary["admin_crossings"].GetArray(); + + // Total of 1 crossing + EXPECT_EQ(crossings.Size(), 1); + EXPECT_EQ(crossings[0]["from_admin_index"], 0); + EXPECT_EQ(crossings[0]["to_admin_index"], 1); + EXPECT_EQ(crossings[0]["begin_shape_index"].GetUint64(), 4); + EXPECT_EQ(crossings[0]["end_shape_index"].GetUint64(), 5); +} + +TEST(Standalone, AdminCrossingsMultiple) { + constexpr double gridsize_metres = 100; + + // Borders between AB, DE and FG + const std::string ascii_map = R"( + A G + | | + | ---+-+ + | / | F + -+- | | + B | | + C---D-+-E + | + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}, {"BC", {{"highway", "motorway"}}}, + {"CD", {{"highway", "motorway"}}}, {"DE", {{"highway", "motorway"}}}, + {"EF", {{"highway", "motorway"}}}, {"FG", {{"highway", "motorway"}}}}; + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-80.528267838, 42.3255}); + const auto map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_admin_state_country", + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}}); + std::string result_json; + rapidjson::Document result; + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto", + {{"/admin_crossings", "1"}}, {}, &result_json); + result.Parse(result_json.c_str()); + + ASSERT_EQ(result["trip"]["legs"].Size(), 1); + + // Expect admin list at leg level + auto summary = result["trip"]["legs"][0]["summary"].GetObject(); + ASSERT_EQ(summary["admins"].Size(), 3); + EXPECT_TRUE(summary.HasMember("admins")); + EXPECT_STREQ(summary["admins"][0]["country_code"].GetString(), "CA"); + EXPECT_STREQ(summary["admins"][0]["state_code"].GetString(), "ON"); + EXPECT_STREQ(summary["admins"][0]["country_text"].GetString(), "Canada"); + EXPECT_STREQ(summary["admins"][0]["state_text"].GetString(), "Ontario"); + EXPECT_STREQ(summary["admins"][1]["country_code"].GetString(), "US"); + EXPECT_STREQ(summary["admins"][1]["state_code"].GetString(), "OH"); + EXPECT_STREQ(summary["admins"][1]["country_text"].GetString(), "United States"); + EXPECT_STREQ(summary["admins"][1]["state_text"].GetString(), "Ohio"); + EXPECT_STREQ(summary["admins"][2]["country_code"].GetString(), "US"); + EXPECT_STREQ(summary["admins"][2]["state_code"].GetString(), "PA"); + EXPECT_STREQ(summary["admins"][2]["country_text"].GetString(), "United States"); + EXPECT_STREQ(summary["admins"][2]["state_text"].GetString(), "Pennsylvania"); + auto crossings = summary["admin_crossings"].GetArray(); + + // Total of 3 crossings + EXPECT_EQ(crossings.Size(), 3); + EXPECT_EQ(crossings[0]["from_admin_index"], 0); + EXPECT_EQ(crossings[0]["to_admin_index"], 1); + EXPECT_EQ(crossings[0]["begin_shape_index"].GetUint64(), 0); + EXPECT_EQ(crossings[0]["end_shape_index"].GetUint64(), 1); + EXPECT_EQ(crossings[1]["from_admin_index"], 1); + EXPECT_EQ(crossings[1]["to_admin_index"], 2); + EXPECT_EQ(crossings[1]["begin_shape_index"].GetUint64(), 3); + EXPECT_EQ(crossings[1]["end_shape_index"].GetUint64(), 4); + EXPECT_EQ(crossings[2]["from_admin_index"], 2); + EXPECT_EQ(crossings[2]["to_admin_index"], 0); + EXPECT_EQ(crossings[2]["begin_shape_index"].GetUint64(), 5); + EXPECT_EQ(crossings[2]["end_shape_index"].GetUint64(), 6); +} + +TEST(Standalone, AdminCrossingsNone) { + constexpr double gridsize_metres = 100; + + // No borders, just one admin + const std::string ascii_map = R"( + A + | + B + | + C + \ + D + \ + E + | + F + | + G + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}, {"BC", {{"highway", "motorway"}}}, + {"CD", {{"highway", "motorway"}}}, {"DE", {{"highway", "motorway"}}}, + {"EF", {{"highway", "motorway"}}}, {"FG", {{"highway", "motorway"}}}}; + + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-111.96, 50.0}); + const auto map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_admin_country", + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}}); + std::string result_json; + rapidjson::Document result; + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto", + {{"/admin_crossings", "1"}}, {}, &result_json); + result.Parse(result_json.c_str()); + + ASSERT_EQ(result["trip"]["legs"].Size(), 1); + + // Expect admin list at leg level + auto summary = result["trip"]["legs"][0]["summary"].GetObject(); + EXPECT_TRUE(summary.HasMember("admins")); + EXPECT_EQ(summary["admins"].GetArray().Size(), 1); + EXPECT_STREQ(summary["admins"][0]["country_code"].GetString(), "CA"); + EXPECT_STREQ(summary["admins"][0]["state_code"].GetString(), ""); + EXPECT_STREQ(summary["admins"][0]["country_text"].GetString(), "Canada"); + EXPECT_STREQ(summary["admins"][0]["state_text"].GetString(), ""); + + // But no admin_crossings + EXPECT_FALSE(summary.HasMember("admin_crossings")); +} + +// Crossing from no admin into an admin +TEST(Standalone, AdminCrossingsEnter) { + constexpr double gridsize_metres = 100; + + // From no admin to GB at DE + const std::string ascii_map = R"( + A-B-C-D+E-F-G + )"; + + const gurka::ways ways = {{"AB", {{"highway", "motorway"}}}, {"BC", {{"highway", "motorway"}}}, + {"CD", {{"highway", "motorway"}}}, {"DE", {{"highway", "motorway"}}}, + {"EF", {{"highway", "motorway"}}}, {"FG", {{"highway", "motorway"}}}}; + + const auto layout = + gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {-4.96381, 53.437069}); + const auto map = + gurka::buildtiles(layout, ways, {}, {}, "test/data/gurka_admin_enter", + {{"mjolnir.admin", {VALHALLA_SOURCE_DIR "test/data/language_admin.sqlite"}}}); + std::string result_json; + rapidjson::Document result; + auto api = gurka::do_action(valhalla::Options::route, map, {"A", "G"}, "auto", + {{"/admin_crossings", "1"}}, {}, &result_json); + result.Parse(result_json.c_str()); + + ASSERT_EQ(result["trip"]["legs"].Size(), 1); + + // Expect admin list at leg level + auto summary = result["trip"]["legs"][0]["summary"].GetObject(); + EXPECT_TRUE(summary.HasMember("admins")); + EXPECT_EQ(summary["admins"].GetArray().Size(), 2); + EXPECT_STREQ(summary["admins"][0]["country_code"].GetString(), ""); + EXPECT_STREQ(summary["admins"][0]["state_code"].GetString(), ""); + EXPECT_STREQ(summary["admins"][0]["country_text"].GetString(), "None"); + EXPECT_STREQ(summary["admins"][0]["state_text"].GetString(), "None"); + EXPECT_STREQ(summary["admins"][1]["country_code"].GetString(), "GB"); + EXPECT_STREQ(summary["admins"][1]["state_code"].GetString(), "WLS"); + EXPECT_STREQ(summary["admins"][1]["country_text"].GetString(), "United Kingdom"); + EXPECT_STREQ(summary["admins"][1]["state_text"].GetString(), "Cymru / Wales"); + + auto crossings = summary["admin_crossings"].GetArray(); + EXPECT_EQ(crossings.Size(), 1); + EXPECT_EQ(crossings[0]["from_admin_index"], 0); + EXPECT_EQ(crossings[0]["to_admin_index"], 1); + EXPECT_EQ(crossings[0]["begin_shape_index"].GetUint64(), 3); + EXPECT_EQ(crossings[0]["end_shape_index"].GetUint64(), 4); +} + +// A border runs colinearly with a way +TEST(Standalone, AdminAlongEdge) { + constexpr double gridsize_metres = 100; + + const std::string ascii_map = R"( + A-------B-------C + | | | + | | | + | G---X | + | | | + | Y---H | + | | | + F-------E-------D + )"; + + const gurka::ways ways = { + {"AB", {}}, + {"AF", {}}, + {"EF", {}}, + {"XB", {}}, + {"EY", {}}, + {"YX", {}}, + {"BCDE", {}}, + {"GX", {{"highway", "primary"}}}, + {"XY", {{"highway", "primary"}}}, + {"YH", {{"highway", "primary"}}}, + }; + + const gurka::relations relations = { + {{{ + {gurka::way_member, "AB", "outer"}, + {gurka::way_member, "EY", "outer"}, + {gurka::way_member, "YX", "outer"}, + {gurka::way_member, "XB", "outer"}, + {gurka::way_member, "EF", "outer"}, + {gurka::way_member, "AF", "outer"}, + }}, + {{"type", "boundary"}, + {"boundary", "administrative"}, + {"admin_level", "4"}, + {"name", "Colorado"}}}, + {{{ + {gurka::way_member, "BCDE", "outer"}, + {gurka::way_member, "EY", "outer"}, + {gurka::way_member, "YX", "outer"}, + {gurka::way_member, "XB", "outer"}, + }}, + {{"type", "boundary"}, + {"boundary", "administrative"}, + {"admin_level", "4"}, + {"name", "Utah"}}}, + {{{ + {gurka::way_member, "AB", "outer"}, + {gurka::way_member, "BCDE", "outer"}, + {gurka::way_member, "EF", "outer"}, + {gurka::way_member, "AF", "outer"}, + }}, + {{"type", "boundary"}, {"boundary", "administrative"}, {"admin_level", "2"}, {"name", "USA"}}}, + }; + + // build the PBF + const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize_metres, {0, 0}); + std::string workdir = "test/data/gurka_admin_along"; + if (!std::filesystem::exists(workdir)) { + bool created = std::filesystem::create_directories(workdir); + EXPECT_TRUE(created); + } + std::string pbf_fname = workdir + "/map.pbf"; + std::vector input_files = {pbf_fname}; + gurka::detail::build_pbf(layout, ways, {}, relations, pbf_fname, 0, false); + + // build the admin + std::unordered_map options = { + {"mjolnir.concurrency", "1"}, + {"mjolnir.tile_dir", workdir + "/tiles"}, + {"mjolnir.id_table_size", "1000"}, + {"mjolnir.admin", workdir + "/admin.sqlite"}, + {"mjolnir.timezone", workdir + "/not_needed.sqlite"}, + }; + + auto config = test::make_config(workdir, options); + valhalla::gurka::map map{config, layout}; + bool ret = valhalla::mjolnir::BuildAdminFromPBF(map.config.get_child("mjolnir"), input_files); + EXPECT_TRUE(ret); + + // and finally the graph + build_tile_set(map.config, input_files, mjolnir::BuildStage::kInitialize, + mjolnir::BuildStage::kValidate, false); + + // get a test route + std::string result_json; + rapidjson::Document api_result; + + auto api = gurka::do_action(valhalla::Options::route, map, {"G", "H"}, "auto", + {{"/admin_crossings", "1"}}, {}, &result_json); + api_result.Parse(result_json.c_str()); + + ASSERT_EQ(api_result["trip"]["legs"].Size(), 1); + + // Expect admin list at leg level + auto summary = api_result["trip"]["legs"][0]["summary"].GetObject(); + EXPECT_TRUE(summary.HasMember("admins")); + EXPECT_EQ(summary["admins"].GetArray().Size(), 2); + EXPECT_STREQ(summary["admins"][0]["country_text"].GetString(), "USA"); + EXPECT_STREQ(summary["admins"][0]["state_text"].GetString(), "Colorado"); + EXPECT_STREQ(summary["admins"][1]["country_text"].GetString(), "USA"); + EXPECT_STREQ(summary["admins"][1]["state_text"].GetString(), "Utah"); + + auto crossings = summary["admin_crossings"].GetArray(); + EXPECT_EQ(crossings.Size(), 1); + EXPECT_EQ(crossings[0]["from_admin_index"], 0); + EXPECT_EQ(crossings[0]["to_admin_index"], 1); + EXPECT_EQ(crossings[0]["begin_shape_index"].GetUint64(), 2); + EXPECT_EQ(crossings[0]["end_shape_index"].GetUint64(), 3); +} \ No newline at end of file From 23ca3428096e3c63b70b3eb2330e512905fc999f Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Tue, 5 Nov 2024 11:30:31 +0000 Subject: [PATCH 152/198] Generate level change info for `/route` response (#4942) --- CHANGELOG.md | 1 + docs/docs/api/turn-by-turn/api-reference.md | 2 +- proto/common.proto | 7 ++ proto/directions.proto | 1 + proto/trip.proto | 7 ++ src/baldr/attributes_controller.cc | 1 + src/odin/directionsbuilder.cc | 1 + src/thor/triplegbuilder.cc | 71 ++++++++++++++------- src/tyr/route_serializer_valhalla.cc | 14 ++++ src/tyr/trace_serializer.cc | 14 ++++ test/gurka/test_indoor.cc | 67 ++++++++++++++++++- valhalla/baldr/attributes_controller.h | 1 + valhalla/odin/enhancedtrippath.h | 8 +++ 13 files changed, 169 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c9fcca195..496c44fcc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * **Enhancement** * ADDED: Consider smoothness in all profiles that use surface [#4949](https://github.com/valhalla/valhalla/pull/4949) * ADDED: `admin_crossings` request parameter for `/route` [#4941](https://github.com/valhalla/valhalla/pull/4941) + * ADDED: include level change info in `/route` response [#4942](https://github.com/valhalla/valhalla/pull/4942) ## Release Date: 2024-10-10 Valhalla 3.5.1 * **Removed** diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 5f7c3ccf64..7c0acac98f 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -398,7 +398,7 @@ The summary JSON object includes: | `min_lon` | Minimum longitude of a bounding box containing the route. | | `max_lat` | Maximum latitude of a bounding box containing the route. | | `max_lon` | Maximum longitude of a bounding box containing the route. | - +| `level_changes` | If a trip leg includes level changes (i.e. when navigating inside a building), the summary will include an array in the form of `[[shape_index, level], ...]` that can be used to split up the geometry along the level changes. | ### Trip legs and maneuvers diff --git a/proto/common.proto b/proto/common.proto index 1044eaf230..86c07d1912 100644 --- a/proto/common.proto +++ b/proto/common.proto @@ -409,3 +409,10 @@ message Summary { bool has_ferry = 6; bool has_highway = 7; } + +message LevelChange { + uint32 shape_index = 1; + float level = 2; + uint32 precision = 3; +} + diff --git a/proto/directions.proto b/proto/directions.proto index ada56686d7..caafff7e77 100644 --- a/proto/directions.proto +++ b/proto/directions.proto @@ -141,6 +141,7 @@ message DirectionsLeg { Summary summary = 5; repeated Maneuver maneuver = 6; string shape = 7; + repeated LevelChange level_changes = 8; } message DirectionsRoute { diff --git a/proto/trip.proto b/proto/trip.proto index d83b62a681..12dcae9b87 100644 --- a/proto/trip.proto +++ b/proto/trip.proto @@ -209,6 +209,12 @@ message TripLeg { bool country_crossing = 58; bool forward = 59; repeated ConditionalSpeedLimit conditional_speed_limits = 60; + message Level { + float start = 1; + float end = 2; + } + repeated Level levels = 61; + uint32 level_precision = 62; } message IntersectingEdge { @@ -316,6 +322,7 @@ message TripLeg { repeated string algorithms = 12; repeated Closure closures = 13; Summary summary = 14; + repeated LevelChange level_changes = 15; } message TripRoute { diff --git a/src/baldr/attributes_controller.cc b/src/baldr/attributes_controller.cc index be75ab8475..523e24832e 100644 --- a/src/baldr/attributes_controller.cc +++ b/src/baldr/attributes_controller.cc @@ -84,6 +84,7 @@ const std::unordered_map AttributesController::kDefaultAttrib {kEdgeLandmarks, true}, {kEdgeCountryCrossing, true}, {kEdgeForward, true}, + {kEdgeLevels, true}, // Node keys {kIncidents, false}, diff --git a/src/odin/directionsbuilder.cc b/src/odin/directionsbuilder.cc index cb6eed7585..0b57d212e0 100644 --- a/src/odin/directionsbuilder.cc +++ b/src/odin/directionsbuilder.cc @@ -96,6 +96,7 @@ void DirectionsBuilder::PopulateDirectionsLeg(const Options& options, trip_directions.set_trip_id(etp->trip_id()); trip_directions.set_leg_id(etp->leg_id()); trip_directions.set_leg_count(etp->leg_count()); + trip_directions.mutable_level_changes()->CopyFrom(etp->level_changes()); // Populate locations trip_directions.mutable_location()->CopyFrom(etp->location()); diff --git a/src/thor/triplegbuilder.cc b/src/thor/triplegbuilder.cc index 8a796252e3..9a7288a898 100644 --- a/src/thor/triplegbuilder.cc +++ b/src/thor/triplegbuilder.cc @@ -1039,24 +1039,25 @@ void ProcessTunnelBridgeTaggedValue(valhalla::StreetName* trip_edge_name, * Add trip edge. (TODO more comments) * @param controller Controller to determine which attributes to set. * @param edge Identifier of an edge within the tiled, hierarchical graph. - * @param trip_id Trip Id (0 if not a transit edge). + * @param edge_itr PathInfo iterator * @param block_id Transit block Id (0 if not a transit edge) * @param mode Travel mode for the edge: Biking, walking, etc. * @param directededge Directed edge information. * @param drive_right Right side driving for this edge. * @param trip_node Trip node to add the edge information to. * @param graphtile Graph tile for accessing data. - * @param second_of_week The time, from the beginning of the week in seconds at which + * @param time_info The time, from the beginning of the week in seconds at which * the path entered this edge (always monday at noon on timeless route) * @param start_node_idx The start node index * @param has_junction_name True if named junction exists, false otherwise * @param start_tile The start tile of the start node * @param blind_instructions Whether instructions should be generated for blind users - * + * @param edgeinfo EdgeInfo of the directed edge + * @param levels level information of the edge */ TripLeg_Edge* AddTripEdge(const AttributesController& controller, const GraphId& edge, - const uint32_t trip_id, + const std::vector::const_iterator& edge_itr, const uint32_t block_id, const sif::TravelMode mode, const uint8_t travel_type, @@ -1069,16 +1070,15 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, const uint32_t start_node_idx, const bool has_junction_name, const graph_tile_ptr& start_tile, - const uint8_t restrictions_idx, - float elapsed_secs, - bool blind_instructions) { + bool blind_instructions, + EdgeInfo& edgeinfo, + const std::pair>, uint32_t>& levels) { // Index of the directed edge within the tile uint32_t idx = edge.id(); TripLeg_Edge* trip_edge = trip_node->mutable_edge(); // Get the edgeinfo - auto edgeinfo = graphtile->edgeinfo(directededge); // Add names to edge if requested if (controller(kEdgeNames)) { @@ -1190,7 +1190,7 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, if (mode == sif::TravelMode::kPublicTransit) { // TODO(nils): get the actual speed here by passing in the elapsed seconds (or the whole // pathinfo) - speed = directededge->length() / elapsed_secs * kMetersPerSectoKPH; + speed = directededge->length() / edge_itr->elapsed_cost.secs * kMetersPerSectoKPH; } else { uint8_t flow_sources; speed = directededge->length() / @@ -1210,6 +1210,15 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, trip_edge->set_forward(directededge->forward()); } + if (controller(kEdgeLevels)) { + trip_edge->set_level_precision(std::max(static_cast(1), levels.second)); + for (const auto& level : levels.first) { + auto proto_level = trip_edge->mutable_levels()->Add(); + proto_level->set_start(level.first); + proto_level->set_end(level.second); + } + } + uint8_t kAccess = 0; if (mode == sif::TravelMode::kBicycle) { kAccess = kBicycleAccess; @@ -1252,14 +1261,14 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, } } - if (directededge->access_restriction() && restrictions_idx != kInvalidRestriction) { + if (directededge->access_restriction() && edge_itr->restriction_index != kInvalidRestriction) { const std::vector& restrictions = graphtile->GetAccessRestrictions(edge.id(), costing->access_mode()); trip_edge->mutable_restriction()->set_type( - static_cast(restrictions[restrictions_idx].type())); + static_cast(restrictions[edge_itr->restriction_index].type())); } - trip_edge->set_has_time_restrictions(restrictions_idx != kInvalidRestriction); + trip_edge->set_has_time_restrictions(edge_itr->restriction_index != kInvalidRestriction); // Set the trip path use based on directed edge use if requested if (controller(kEdgeUse)) { @@ -1489,7 +1498,7 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, ///////////////////////////////////////////////////////////////////////////// // Process transit information - if (trip_id && (directededge->use() == Use::kRail || directededge->use() == Use::kBus)) { + if (edge_itr->trip_id && (directededge->use() == Use::kRail || directededge->use() == Use::kBus)) { TransitRouteInfo* transit_route_info = trip_edge->mutable_transit_route_info(); @@ -1500,11 +1509,11 @@ TripLeg_Edge* AddTripEdge(const AttributesController& controller, // Set trip_id if requested if (controller(kEdgeTransitRouteInfoTripId)) { - transit_route_info->set_trip_id(trip_id); + transit_route_info->set_trip_id(edge_itr->trip_id); } const TransitDeparture* transit_departure = - graphtile->GetTransitDeparture(directededge->lineid(), trip_id, + graphtile->GetTransitDeparture(directededge->lineid(), edge_itr->trip_id, time_info.second_of_week % kSecondsPerDay); if (transit_departure) { @@ -1789,6 +1798,9 @@ void TripLegBuilder::Build( // prepare to make some edges! trip_path.mutable_node()->Reserve((path_end - path_begin) + 1); + // collect the level changes + float prev_level = kMaxLevel; + // we track the intermediate locations while we iterate so we can update their shape index // from the edge index that we assigned to them earlier in route_action auto intermediate_itr = trip_path.mutable_location()->begin() + 1; @@ -1891,21 +1903,36 @@ void TripLegBuilder::Build( multimodal_builder.Build(trip_node, edge_itr->trip_id, node, startnode, directededge, edge, start_tile, graphtile, mode_costing, controller, graphreader); + uint32_t begin_index = is_first_edge ? 0 : trip_shape.size() - 1; + auto edgeinfo = graphtile->edgeinfo(directededge); + std::pair>, uint32_t> levels = edgeinfo.levels(); // Add edge to the trip node and set its attributes TripLeg_Edge* trip_edge = - AddTripEdge(controller, edge, edge_itr->trip_id, multimodal_builder.block_id, mode, - travel_type, costing, directededge, node->drive_on_right(), trip_node, graphtile, - time_info, startnode.id(), node->named_intersection(), start_tile, - edge_itr->restriction_index, edge_itr->elapsed_cost.secs, - travel_type == PedestrianType::kBlind && mode == sif::TravelMode::kPedestrian); + AddTripEdge(controller, edge, edge_itr, multimodal_builder.block_id, mode, travel_type, + costing, directededge, node->drive_on_right(), trip_node, graphtile, time_info, + startnode.id(), node->named_intersection(), start_tile, + travel_type == PedestrianType::kBlind && mode == sif::TravelMode::kPedestrian, + edgeinfo, levels); + + // for the level changes, only consider edges on a single level + if (levels.first.size() == 1 && levels.first[0].first == levels.first[0].second) { + float lvl = levels.first[0].first; + // if this edge is on a different level than the previous one, + // add a level change + if (std::fabs(lvl - prev_level) >= std::numeric_limits::epsilon()) { + auto* change = trip_path.add_level_changes(); + change->set_level(lvl); + change->set_shape_index(begin_index); + change->set_precision(std::max(static_cast(1), levels.second)); + prev_level = lvl; + } + } // some information regarding shape/length trimming float trim_start_pct = is_first_edge ? start_pct : 0; float trim_end_pct = is_last_edge ? end_pct : 1; // Some edges at the beginning and end of the path and at intermediate locations will need trimmed - uint32_t begin_index = is_first_edge ? 0 : trip_shape.size() - 1; - auto edgeinfo = graphtile->edgeinfo(directededge); auto trimming = edge_trimming.end(); if (!edge_trimming.empty() && (trimming = edge_trimming.find(edge_index)) != edge_trimming.end()) { diff --git a/src/tyr/route_serializer_valhalla.cc b/src/tyr/route_serializer_valhalla.cc index bb92ca3311..b23c37bb38 100644 --- a/src/tyr/route_serializer_valhalla.cc +++ b/src/tyr/route_serializer_valhalla.cc @@ -569,6 +569,20 @@ void legs(const valhalla::Api& api, int route_index, rapidjson::writer_wrapper_t } } + // are there any level changes along the leg + if (directions_leg.level_changes().size() > 0) { + writer.start_array("level_changes"); + for (auto& level_change : directions_leg.level_changes()) { + writer.start_array(); + writer(static_cast(level_change.shape_index())); + writer.set_precision(std::max(level_change.precision(), static_cast(1))); + writer(level_change.level()); + writer.set_precision(3); + writer.end_array(); + } + writer.end_array(); + } + writer("has_time_restrictions", has_time_restrictions); writer("has_toll", has_toll); writer("has_highway", has_highway); diff --git a/src/tyr/trace_serializer.cc b/src/tyr/trace_serializer.cc index c2bebfb7a6..a34e35abd6 100644 --- a/src/tyr/trace_serializer.cc +++ b/src/tyr/trace_serializer.cc @@ -195,6 +195,20 @@ void serialize_edges(const AttributesController& controller, if (controller(kEdgeForward)) { writer("forward", static_cast(edge.forward())); } + if (controller(kEdgeLevels)) { + if (edge.levels_size()) { + writer.start_array("levels"); + writer.set_precision(edge.level_precision()); + for (const auto& level : edge.levels()) { + writer.start_array(); + writer(static_cast(level.start())); + writer(static_cast(level.end())); + writer.end_array(); + } + writer.end_array(); + writer.set_precision(3); + } + } if (controller(kEdgeLength)) { writer.set_precision(3); writer("length", edge.length_km() * scale); diff --git a/test/gurka/test_indoor.cc b/test/gurka/test_indoor.cc index 24350937ac..1f3ba507d7 100644 --- a/test/gurka/test_indoor.cc +++ b/test/gurka/test_indoor.cc @@ -20,6 +20,11 @@ struct range_t { float end; }; +struct level_change_t { + uint32_t index; + float level; +}; + class Indoor : public ::testing::Test { protected: static gurka::map map; @@ -30,7 +35,7 @@ class Indoor : public ::testing::Test { constexpr double gridsize_metres = 100; ascii_map = R"( - A + AzZ-Y-X-W | B | @@ -69,6 +74,10 @@ class Indoor : public ::testing::Test { {"Cx", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "-1;0-2"}}}, {"xy", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "2;3"}}}, {"yJ", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "3"}}}, + {"AZ", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "0-4"}}}, + {"ZY", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "4"}}}, + {"YX", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "4;5"}}}, + {"XW", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "5"}}}, }; const gurka::nodes nodes = { @@ -84,6 +93,24 @@ gurka::map Indoor::map = {}; std::string Indoor::ascii_map = {}; gurka::nodelayout Indoor::layout = {}; +/** + * Convenience function to make sure that + * a) the JSON response has a "level_changes" member and + * b) that it indicates level changes as expected + */ +void check_level_changes(rapidjson::Document& doc, const std::vector& expected) { + EXPECT_TRUE(doc["trip"]["legs"][0]["summary"].HasMember("level_changes")); + auto level_changes = doc["trip"]["legs"][0]["summary"]["level_changes"].GetArray(); + EXPECT_EQ(level_changes.Size(), expected.size()); + for (size_t i = 0; i < expected.size(); ++i) { + auto expected_entry = expected[i]; + auto change_entry = level_changes[i].GetArray(); + EXPECT_EQ(change_entry.Size(), 2); + EXPECT_EQ(change_entry[0].GetUint64(), expected_entry.index); + EXPECT_EQ(change_entry[1].GetFloat(), expected_entry.level); + } +} + /*************************************************************/ TEST_F(Indoor, NodeInfo) { @@ -274,6 +301,42 @@ TEST_F(Indoor, CombineStepsManeuvers) { ""); } +TEST_F(Indoor, StepsLevelChanges) { + // get a route via steps and check the level changelog + std::string route_json; + auto result = + gurka::do_action(valhalla::Options::route, map, {"A", "J"}, "pedestrian", + {{"/costing_options/pedestrian/elevator_penalty", "3600"}}, {}, &route_json); + gurka::assert::raw::expect_path(result, {"AB", "BC", "Cx", "xy", "yJ"}); + rapidjson::Document doc; + doc.Parse(route_json.c_str()); + + check_level_changes(doc, {{0, 0.f}, {4, 3.f}}); +} + +TEST_F(Indoor, EdgeElevatorLevelChanges) { + // get a route via an edge-modeled elevator and check the level changelog + std::string route_json; + auto result = + gurka::do_action(valhalla::Options::route, map, {"F", "I"}, "pedestrian", {}, {}, &route_json); + gurka::assert::raw::expect_path(result, {"FG", "GH", "HI"}); + rapidjson::Document doc; + doc.Parse(route_json.c_str()); + + check_level_changes(doc, {{0, 1.f}, {2, 2.f}}); +} + +TEST_F(Indoor, NodeElevatorLevelChanges) { + // get a route via a node-modeled elevator and check the level changelog + std::string route_json; + auto result = + gurka::do_action(valhalla::Options::route, map, {"H", "J"}, "pedestrian", {}, {}, &route_json); + gurka::assert::raw::expect_path(result, {"HI", "IJ"}); + rapidjson::Document doc; + doc.Parse(route_json.c_str()); + + check_level_changes(doc, {{0, 2.f}, {1, 3.f}}); +} /****************************************************************************************/ class Levels : public ::testing::Test { @@ -311,7 +374,6 @@ std::string Levels::ascii_map = {}; gurka::nodelayout Levels::layout = {}; TEST_F(Levels, EdgeInfoIncludes) { - baldr::GraphReader graphreader(map.config.get_child("mjolnir")); std::vector, std::vector>> values = { @@ -338,7 +400,6 @@ TEST_F(Levels, EdgeInfoIncludes) { } TEST_F(Levels, EdgeInfoJson) { - auto graphreader = test::make_clean_graphreader(map.config.get_child("mjolnir")); std::string json; std::vector locs = { diff --git a/valhalla/baldr/attributes_controller.h b/valhalla/baldr/attributes_controller.h index 8972bbfe54..564dedf5d0 100644 --- a/valhalla/baldr/attributes_controller.h +++ b/valhalla/baldr/attributes_controller.h @@ -80,6 +80,7 @@ const std::string kEdgeIndoor = "edge.indoor"; const std::string kEdgeLandmarks = "edge.landmarks"; const std::string kEdgeCountryCrossing = "edge.country_crossing"; const std::string kEdgeForward = "edge.forward"; +const std::string kEdgeLevels = "edge.levels"; // Node keys const std::string kNodeIntersectingEdgeBeginHeading = "node.intersecting_edge.begin_heading"; diff --git a/valhalla/odin/enhancedtrippath.h b/valhalla/odin/enhancedtrippath.h index 888753abd6..9cdf588297 100644 --- a/valhalla/odin/enhancedtrippath.h +++ b/valhalla/odin/enhancedtrippath.h @@ -80,6 +80,10 @@ class EnhancedTripLeg { return trip_path_.leg_count(); } + const ::google::protobuf::RepeatedPtrField<::valhalla::LevelChange>& level_changes() const { + return trip_path_.level_changes(); + } + const ::google::protobuf::RepeatedPtrField<::valhalla::Location>& location() const { return trip_path_.location(); } @@ -364,6 +368,10 @@ class EnhancedTripLeg_Edge { return mutable_edge_->indoor(); } + const google::protobuf::RepeatedPtrField& levels() const { + return mutable_edge_->levels(); + } + bool IsUnnamed() const; // Use From 0ce48a01bba23644b1b7678c125cb4596c47606b Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Fri, 8 Nov 2024 17:10:22 +0100 Subject: [PATCH 153/198] docs: remove second `ignore_closures` (#4957) --- docs/docs/api/turn-by-turn/api-reference.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/docs/api/turn-by-turn/api-reference.md b/docs/docs/api/turn-by-turn/api-reference.md index 7c0acac98f..c674c0a2a7 100644 --- a/docs/docs/api/turn-by-turn/api-reference.md +++ b/docs/docs/api/turn-by-turn/api-reference.md @@ -131,13 +131,12 @@ These options are available for `auto`, `bus`, and `truck` costing methods. | `disable_hierarchy_pruning` | Disable hierarchies to calculate the actual optimal route. The default is `false`. **Note:** This could be quite a performance drainer so there is a upper limit of distance. If the upper limit is exceeded, this option will always be `false`. | | `top_speed` | Top speed the vehicle can go. Also used to avoid roads with higher speeds than this value. `top_speed` must be between 10 and 252 KPH. The default value is 120 KPH for `truck` and 140 KPH for `auto` and `bus`. | | `fixed_speed` | Fixed speed the vehicle can go. Used to override the calculated speed. Can be useful if speed of vehicle is known. `fixed_speed` must be between 1 and 252 KPH. The default value is 0 KPH which disables fixed speed and falls back to the standard calculated speed based on the road attribution. | -| `ignore_closures` | If set to `true`, ignores all closures, marked due to live traffic closures, during routing. **Note:** This option cannot be set if `location.search_filter.exclude_closures` is also specified in the request and will return an error if it is | +| `ignore_closures` | If set to `true`, ignores all closures, marked due to live traffic closures, during routing. **Note:** This option cannot be set if `location.search_filter.exclude_closures` is also specified in the request and will return an error if it is. Default is `false` | | `closure_factor` | A factor that penalizes the cost when traversing a closed edge (eg: if `search_filter.exclude_closures` is `false` for origin and/or destination location and the route starts/ends on closed edges). Its value can range from `1.0` - don't penalize closed edges, to `10.0` - apply high cost penalty to closed edges. Default value is `9.0`. **Note:** This factor is applicable only for motorized modes of transport, i.e `auto`, `motorcycle`, `motor_scooter`, `bus`, `truck` & `taxi`. | | `ignore_restrictions` | If set to `true`, ignores any restrictions (e.g. turn/dimensional/conditional restrictions). Especially useful for matching GPS traces to the road network regardless of restrictions. Default is `false`. | | `ignore_oneways` | If set to `true`, ignores one-way restrictions. Especially useful for matching GPS traces to the road network ignoring uni-directional traffic rules. Not included in `ignore_restrictions` option. Default is `false`. | | `ignore_non_vehicular_restrictions` | Similar to `ignore_restrictions`, but will respect restrictions that impact vehicle safety, such as weight and size restrictions. | | `ignore_access` | Will ignore mode-specific access tags. Especially useful for matching GPS traces to the road network regardless of restrictions. Default is `false`. | -| `ignore_closures` | Will ignore traffic closures. Default is `false`. | | `speed_types` | Will determine which speed sources are used, if available. A list of strings with the following possible values:
  • freeflow
  • constrained
  • predicted
  • current
Default is all sources (again, only if available). | ###### Other costing options From 61949e92c72037ac2cfd59f458cbd6b199ed8ff0 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Mon, 11 Nov 2024 13:43:35 +0100 Subject: [PATCH 154/198] Improve steps maneuvers (#4960) --- CHANGELOG.md | 1 + src/odin/maneuversbuilder.cc | 7 ++++++- test/gurka/test_indoor.cc | 29 +++++++++++++++++++++++------ valhalla/odin/enhancedtrippath.h | 6 ++++++ 4 files changed, 36 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 496c44fcc7..33e6e72bf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * ADDED: Consider smoothness in all profiles that use surface [#4949](https://github.com/valhalla/valhalla/pull/4949) * ADDED: `admin_crossings` request parameter for `/route` [#4941](https://github.com/valhalla/valhalla/pull/4941) * ADDED: include level change info in `/route` response [#4942](https://github.com/valhalla/valhalla/pull/4942) + * ADDED: steps and elevator maneuver improvements [#4960](https://github.com/valhalla/valhalla/pull/4960) ## Release Date: 2024-10-10 Valhalla 3.5.1 * **Removed** diff --git a/src/odin/maneuversbuilder.cc b/src/odin/maneuversbuilder.cc index f5648dd9cd..a71af0adfd 100644 --- a/src/odin/maneuversbuilder.cc +++ b/src/odin/maneuversbuilder.cc @@ -1292,6 +1292,11 @@ void ManeuversBuilder::InitializeManeuver(Maneuver& maneuver, int node_index) { } } + // only set steps to true if it involves a level change or the steps are long enough to + // not be considered trivial + maneuver.set_steps(prev_edge->use() == TripLeg_Use_kStepsUse && + (prev_edge->traverses_levels() || prev_edge->length_km() >= 0.003)); + if (maneuver.pedestrian_type() == PedestrianType::kBlind) { if (prev_edge->use() == TripLeg_Use_kStepsUse) maneuver.set_steps(true); @@ -1808,7 +1813,7 @@ void ManeuversBuilder::SetManeuverType(Maneuver& maneuver, bool none_type_allowe LOG_TRACE("ManeuverType=ELEVATOR"); } // Process steps - else if (maneuver.indoor_steps()) { + else if (maneuver.indoor_steps() || maneuver.is_steps()) { maneuver.set_type(DirectionsLeg_Maneuver_Type_kStepsEnter); LOG_TRACE("ManeuverType=STEPS"); } diff --git a/test/gurka/test_indoor.cc b/test/gurka/test_indoor.cc index 1f3ba507d7..85e616ca66 100644 --- a/test/gurka/test_indoor.cc +++ b/test/gurka/test_indoor.cc @@ -41,7 +41,7 @@ class Indoor : public ::testing::Test { | C---------x--------y | | - D----E----F----G----H----I---J + D----E----F----G----H----I---J--S--T--U | | N K | | @@ -74,6 +74,9 @@ class Indoor : public ::testing::Test { {"Cx", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "-1;0-2"}}}, {"xy", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "2;3"}}}, {"yJ", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "3"}}}, + {"JS", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "3"}}}, + {"ST", {{"highway", "steps"}, {"level", "3;4"}}}, + {"TU", {{"highway", "footway"}, {"level", "4"}}}, {"AZ", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "0-4"}}}, {"ZY", {{"highway", "corridor"}, {"indoor", "yes"}, {"level", "4"}}}, {"YX", {{"highway", "steps"}, {"indoor", "yes"}, {"level", "4;5"}}}, @@ -221,17 +224,14 @@ TEST_F(Indoor, OutdoorStepsManeuver) { // Verify maneuver types gurka::assert::raw::expect_maneuvers(result, {DirectionsLeg_Maneuver_Type_kStart, - DirectionsLeg_Maneuver_Type_kLeft, + DirectionsLeg_Maneuver_Type_kStepsEnter, DirectionsLeg_Maneuver_Type_kContinue, DirectionsLeg_Maneuver_Type_kDestination}); // Verify steps instructions int maneuver_index = 1; gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, - "Turn left onto DN.", "Turn left.", - "Turn left onto DN.", - "Turn left onto DN.", - "Continue for 200 meters."); + "Take the stairs.", "", "", "", ""); } TEST_F(Indoor, EscalatorManeuver) { @@ -301,6 +301,23 @@ TEST_F(Indoor, CombineStepsManeuvers) { ""); } +// Dont combine maneuvers if there is a level change +TEST_F(Indoor, OutdoorStepsLevelChange) { + auto result = gurka::do_action(valhalla::Options::route, map, {"J", "U"}, "pedestrian", {}); + gurka::assert::raw::expect_path(result, {"JS", "ST", "TU"}); + + // Verify maneuver types + gurka::assert::raw::expect_maneuvers(result, {DirectionsLeg_Maneuver_Type_kStart, + DirectionsLeg_Maneuver_Type_kStepsEnter, + DirectionsLeg_Maneuver_Type_kContinue, + DirectionsLeg_Maneuver_Type_kDestination}); + + // Verify steps instructions + int maneuver_index = 1; + gurka::assert::raw::expect_instructions_at_maneuver_index(result, maneuver_index, + "Take the stairs to Level 4.", "", "", "", + ""); +} TEST_F(Indoor, StepsLevelChanges) { // get a route via steps and check the level changelog std::string route_json; diff --git a/valhalla/odin/enhancedtrippath.h b/valhalla/odin/enhancedtrippath.h index 9cdf588297..0ad389aae3 100644 --- a/valhalla/odin/enhancedtrippath.h +++ b/valhalla/odin/enhancedtrippath.h @@ -372,6 +372,12 @@ class EnhancedTripLeg_Edge { return mutable_edge_->levels(); } + bool traverses_levels() const { + return !mutable_edge_->levels().empty() && + (mutable_edge_->levels().size() > 1 || + mutable_edge_->levels()[0].start() != mutable_edge_->levels()[0].end()); + } + bool IsUnnamed() const; // Use From c4681ef12d2ce62f63a07dfd230fd6adad3ff38c Mon Sep 17 00:00:00 2001 From: David Nesbitt Date: Thu, 21 Nov 2024 09:12:59 -0500 Subject: [PATCH 155/198] Fix valhalla_benchmark-loki (#4981) --- CHANGELOG.md | 1 + src/valhalla_benchmark_loki.cc | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33e6e72bf0..bcb389eaff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * **Bug Fix** * FIXED: `incremental_build_tiles` script works again [#4909](https://github.com/valhalla/valhalla/pull/4909) * FIXED: Fix ability to use Valhalla via cmake `add_subdirectory` [#4930](https://github.com/valhalla/valhalla/pull/4930) + * FIXED: Fix valhalla_benchmark_loki benchmark application. [#4981](https://github.com/valhalla/valhalla/pull/4981) * **Enhancement** * ADDED: Consider smoothness in all profiles that use surface [#4949](https://github.com/valhalla/valhalla/pull/4949) * ADDED: `admin_crossings` request parameter for `/route` [#4941](https://github.com/valhalla/valhalla/pull/4941) diff --git a/src/valhalla_benchmark_loki.cc b/src/valhalla_benchmark_loki.cc index 8f3b988707..4beb5ad92d 100644 --- a/src/valhalla_benchmark_loki.cc +++ b/src/valhalla_benchmark_loki.cc @@ -146,7 +146,7 @@ int main(int argc, char** argv) { ("j,concurrency", "Number of threads to use. Defaults to all threads.", cxxopts::value()) ("b,batch", "Number of locations to group together per search", cxxopts::value(batch)->default_value("1")) ("e,extrema", "Show the input locations of the extrema for a given statistic", cxxopts::value(extrema)->default_value("false")) - ("i,reach", "How many edges need to be reachable before considering it as connected to the larger network", cxxopts::value(isolated)) + ("i,reach", "How many edges need to be reachable before considering it as connected to the larger network", cxxopts::value(isolated)->default_value("50")) ("r,radius", "How many meters to search away from the input location", cxxopts::value(radius)->default_value("0")) ("costing", "Which costing model to use.", cxxopts::value(costing_str)->default_value("auto")) ("input_files", "positional arguments", cxxopts::value>(input_files)); @@ -188,7 +188,7 @@ int main(int argc, char** argv) { throw std::runtime_error("Latitude must be in the range [-90, 90] degrees"); } float lon = valhalla::midgard::circular_range_clamp(std::stof(parts[1]), -180, 180); - valhalla::midgard::PointLL ll(lat, lon); + valhalla::midgard::PointLL ll(lon, lat); valhalla::baldr::Location loc(ll); loc.min_inbound_reach_ = loc.min_outbound_reach_ = isolated; loc.radius_ = radius; From a2577908dec09f92d0a653c925f5d81614460043 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Mon, 25 Nov 2024 17:55:40 +0100 Subject: [PATCH 156/198] =?UTF-8?q?=20Fix=20macOS=20CI=20=E2=80=93=20homeb?= =?UTF-8?q?rew=20links=20two=20versions=20of=20pkg-config=20(#4995)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/osx.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 4c09df6b22..1224d7584f 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -40,6 +40,7 @@ jobs: - name: Install dependencies run: | + brew unlink pkg-config@0.29.2 # todo(chris): remove once GH deploys https://github.com/actions/runner-images/pull/11015 HOMEBREW_NO_AUTO_UPDATE=1 brew install python autoconf automake protobuf cmake ccache libtool sqlite3 libspatialite luajit curl wget czmq lz4 spatialite-tools unzip boost gdal export PATH="$(brew --prefix python)/libexec/bin:$PATH" sudo python -m pip install --break-system-packages requests shapely From c5e942572e899d7d4b1d4873c319aadad634b488 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Mon, 25 Nov 2024 19:19:35 +0100 Subject: [PATCH 157/198] Fix win build workflow (#4993) --- .github/workflows/win.yml | 15 +++++++-------- docs/docs/building.md | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index 8de02d329c..2105b72dd5 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -2,7 +2,7 @@ name: Windows CI on: push: paths-ignore: - - '*.md' + - "*.md" - .circleci/ - docs/ - run_route_scripts/ @@ -12,7 +12,7 @@ on: - master pull_request: paths-ignore: - - '*.md' + - "*.md" - .circleci/ - docs/ - run_route_scripts/ @@ -24,14 +24,14 @@ on: inputs: debug_enabled: type: boolean - description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + description: "Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)" required: false default: false env: BUILD_TYPE: Release - MSVC_VERSION: '2022' - VCPKG_VERSION: '8040303' + MSVC_VERSION: "2022" + VCPKG_VERSION: "5e5d0e1" VCPKG_INSTALL_OPTIONS: --x-abi-tools-use-exact-versions VCPKG_DISABLE_COMPILER_TRACKING: ON @@ -123,18 +123,17 @@ jobs: - name: Build Valhalla run: | cmake --build build --config Release -- /clp:ErrorsOnly /p:BuildInParallel=true /m:4 - + - name: Test Executable shell: bash run: | set PATH=$PATH:${{ github.workspace }}/build/vcpkg_installed/$BUILD_TYPE/bin ./build/$BUILD_TYPE/valhalla_build_tiles.exe -c ./test/win/valhalla.json ./test/data/utrecht_netherlands.osm.pbf ./build/$BUILD_TYPE/valhalla_run_isochrone.exe --config ./test/win/valhalla.json -j "{\"locations\": [{\"lat\": 52.10205, \"lon\": 5.114651}], \"costing\": \"auto\", \"contours\":[{\"time\":15,\"color\":\"ff0000\"}]}" - + - name: Setup tmate session uses: mxschmitt/action-tmate@v3 # only run this if manually invoked or a previous job failed if: ${{ (github.event_name == 'workflow_dispatch' && inputs.debug_enabled) || failure() }} with: detached: true - diff --git a/docs/docs/building.md b/docs/docs/building.md index 50cb7dac78..63c494b54b 100644 --- a/docs/docs/building.md +++ b/docs/docs/building.md @@ -42,7 +42,7 @@ git clone https://github.com/microsoft/vcpkg && git -C vcpkg checkout ./vcpkg/bootstrap-vcpkg.sh # windows: cmd.exe /c bootstrap-vcpkg.bat # only build Release versions of dependencies, not Debug -echo "set(VCPKG_BUILD_TYPE release)" >> vcpkg/triplets/x64-linux.cmake +echo "set(VCPKG_BUILD_TYPE Release)" >> vcpkg/triplets/x64-linux.cmake # windows: echo.set(VCPKG_BUILD_TYPE release)>> .\vcpkg\triplets\x64-windows.cmake # osx: echo "set(VCPKG_BUILD_TYPE release)" >> vcpkg/triplets/arm64-osx.cmake @@ -114,7 +114,7 @@ It's recommended to work with the following toolset: ``` git -C C:\path\to\vcpkg checkout f330a32 # only build release versions for vcpkg packages -echo.set(VCPKG_BUILD_TYPE release)>> path\to\vcpkg\triplets\x64-windows.cmake +echo.set(VCPKG_BUILD_TYPE Release)>> path\to\vcpkg\triplets\x64-windows.cmake cd C:\path\to\valhalla C:\path\to\vcpkg.exe install --triplet x64-windows ``` From a674f9f24080f5540d1afc7ba70fbe6cdbad2a6f Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Tue, 26 Nov 2024 08:34:11 +0100 Subject: [PATCH 158/198] CostMatrix: check for empty edgelabels when building date time info (#4972) --- src/thor/costmatrix.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/thor/costmatrix.cc b/src/thor/costmatrix.cc index 4c1cf16a5c..2f87309603 100644 --- a/src/thor/costmatrix.cc +++ b/src/thor/costmatrix.cc @@ -270,10 +270,13 @@ bool CostMatrix::SourceToTarget(Api& request, auto dt_info = DateTime::offset_date(source_location_list[source_idx].date_time(), time_infos[source_idx].timezone_index, - graphreader.GetTimezoneFromEdge(edgelabel_[MATRIX_REV][target_idx] - .front() - .edgeid(), - tile), + edgelabel_[MATRIX_REV][target_idx].empty() + ? 0 + : graphreader + .GetTimezoneFromEdge(edgelabel_[MATRIX_REV][target_idx] + .front() + .edgeid(), + tile), time); *matrix.mutable_date_times(connection_idx) = dt_info.date_time; *matrix.mutable_time_zone_offsets(connection_idx) = dt_info.time_zone_offset; From 91a3855b07650483bc54b152267b97b87697e250 Mon Sep 17 00:00:00 2001 From: Kevin Kreiser Date: Wed, 27 Nov 2024 10:36:33 -0500 Subject: [PATCH 159/198] strip the right python bindings location in docker build --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index eb5fb6c510..fe89374db5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -51,7 +51,7 @@ RUN rm -rf valhalla #RUN rm -f valhalla_*.debug #RUN strip --strip-debug --strip-unneeded valhalla_* || true #RUN strip /usr/local/lib/libvalhalla.a -#RUN strip /usr/lib/python3/dist-packages/valhalla/python_valhalla*.so +#RUN strip /usr/local/lib/python3.12/dist-packages/valhalla/python_valhalla*.so #################################################################### # copy the important stuff from the build stage to the runner image From 4e51f0cead193747ff4e7d9715787a6e47a3af58 Mon Sep 17 00:00:00 2001 From: Stepan Kizim <10885920+kinkard@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:02:50 +0100 Subject: [PATCH 160/198] fix: Double free crash during tiles build inside libxml2 on concurrent `spatialite_cleanup_ex()` calls (#5005) --- CHANGELOG.md | 1 + src/mjolnir/util.cc | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcb389eaff..e9bd5d8f1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * FIXED: `incremental_build_tiles` script works again [#4909](https://github.com/valhalla/valhalla/pull/4909) * FIXED: Fix ability to use Valhalla via cmake `add_subdirectory` [#4930](https://github.com/valhalla/valhalla/pull/4930) * FIXED: Fix valhalla_benchmark_loki benchmark application. [#4981](https://github.com/valhalla/valhalla/pull/4981) + * FIXED: Double free crash during tiles build inside libxml2 on concurrent `spatialite_cleanup_ex()` calls [#5005](https://github.com/valhalla/valhalla/pull/5005) * **Enhancement** * ADDED: Consider smoothness in all profiles that use surface [#4949](https://github.com/valhalla/valhalla/pull/4949) * ADDED: `admin_crossings` request parameter for `/route` [#4941](https://github.com/valhalla/valhalla/pull/4941) diff --git a/src/mjolnir/util.cc b/src/mjolnir/util.cc index 2960778d47..39a863cfb9 100644 --- a/src/mjolnir/util.cc +++ b/src/mjolnir/util.cc @@ -216,7 +216,17 @@ std::shared_ptr make_spatialite_cache(sqlite3* handle) { spatialite_singleton_t::get_instance(); void* conn = spatialite_alloc_connection(); spatialite_init_ex(handle, conn, 0); - return {conn, [](void* c) { spatialite_cleanup_ex(c); }}; + + // Sadly, `spatialite_cleanup_ex` calls `xmlCleanupParser()` (via `free_internal_cache()`) which is + // not thread-safe and may cause a crash on double-free if called from multiple threads. + // This static mutex works around the issue until the spatialite library is fixed: + // - https://www.gaia-gis.it/fossil/libspatialite/tktview/855ef62a68b9ac6e500b54883707b2876c390c01 + // For full "double free" issue details follow https://github.com/valhalla/valhalla/issues/4904 + static std::mutex spatialite_mutex; + return std::shared_ptr(conn, [](void* c) { + std::lock_guard lock(spatialite_mutex); + spatialite_cleanup_ex(c); + }); } bool build_tile_set(const boost::property_tree::ptree& original_config, From a1f182497a0e0a3cfff72a3cade31b0b62bc6b97 Mon Sep 17 00:00:00 2001 From: Christian Beiwinkel Date: Tue, 3 Dec 2024 15:05:12 +0100 Subject: [PATCH 161/198] More CI fixes (#5002) --- .circleci/config.yml | 6 +- .github/workflows/osx.yml | 1 - CHANGELOG.md | 1 + CMakeLists.txt | 2 +- scripts/format.sh | 4 +- test/gurka/test_exclude_unpaved_smoothness.cc | 39 +- test/narrative_dictionary.cc | 489 +++++++++--------- 7 files changed, 260 insertions(+), 282 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 68f9e8b288..8e71f115aa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,7 +16,7 @@ commands: jobs: lint-build-debug: docker: - - image: ubuntu:23.04 + - image: ubuntu:24.04 resource_class: xlarge steps: - checkout @@ -56,7 +56,7 @@ jobs: build-release: docker: - - image: ubuntu:23.04 + - image: ubuntu:24.04 resource_class: xlarge steps: - checkout @@ -89,7 +89,7 @@ jobs: build-arm-release: docker: - - image: arm64v8/ubuntu:23.04 + - image: arm64v8/ubuntu:24.04 resource_class: arm.xlarge steps: - checkout diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index 1224d7584f..4c09df6b22 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -40,7 +40,6 @@ jobs: - name: Install dependencies run: | - brew unlink pkg-config@0.29.2 # todo(chris): remove once GH deploys https://github.com/actions/runner-images/pull/11015 HOMEBREW_NO_AUTO_UPDATE=1 brew install python autoconf automake protobuf cmake ccache libtool sqlite3 libspatialite luajit curl wget czmq lz4 spatialite-tools unzip boost gdal export PATH="$(brew --prefix python)/libexec/bin:$PATH" sudo python -m pip install --break-system-packages requests shapely diff --git a/CHANGELOG.md b/CHANGELOG.md index e9bd5d8f1d..a1138dccb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * FIXED: Fix ability to use Valhalla via cmake `add_subdirectory` [#4930](https://github.com/valhalla/valhalla/pull/4930) * FIXED: Fix valhalla_benchmark_loki benchmark application. [#4981](https://github.com/valhalla/valhalla/pull/4981) * FIXED: Double free crash during tiles build inside libxml2 on concurrent `spatialite_cleanup_ex()` calls [#5005](https://github.com/valhalla/valhalla/pull/5005) + * FIXED: update CircleCI runners to Ubuntu 24.04 [#5002](https://github.com/valhalla/valhalla/pull/5002) * **Enhancement** * ADDED: Consider smoothness in all profiles that use surface [#4949](https://github.com/valhalla/valhalla/pull/4949) * ADDED: `admin_crossings` request parameter for `/route` [#4941](https://github.com/valhalla/valhalla/pull/4941) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46064579f6..686c5c8475 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -365,7 +365,7 @@ if(ENABLE_COVERAGE) DEPENDS check) add_custom_target(coverage - COMMAND ${GENHTML_PATH} --prefix ${CMAKE_CURRENT_BINARY_DIR} --output-directory coverage --title "Test Coverage" --legend --show-details coverage.info + COMMAND ${GENHTML_PATH} --prefix ${CMAKE_CURRENT_BINARY_DIR} --output-directory coverage --title "Test Coverage" --legend --show-details coverage.info --ignore-errors=negative WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/coverage.info) set_target_properties(coverage PROPERTIES FOLDER "Tests") diff --git a/scripts/format.sh b/scripts/format.sh index 5d9b68248c..3070693775 100755 --- a/scripts/format.sh +++ b/scripts/format.sh @@ -30,9 +30,9 @@ find src valhalla test -type f -name '*.h' -o -name '*.cc' \ py=$(setup_python) if [[ $(python3 -m pip list | grep -c "black\|flake8") -ne 2 ]]; then if [[ $(python3 -c 'import sys; print(int(sys.base_prefix != sys.prefix or hasattr(sys, "real_prefix")))') -eq 1 ]]; then - ${py} -m pip install black==22.10.0 flake8==5.0.4 + ${py} -m pip install black==24.10.0 flake8==7.1.1 else - sudo PIP_BREAK_SYSTEM_PACKAGES=1 ${py} -m pip install black==22.10.0 flake8==5.0.4 + sudo PIP_BREAK_SYSTEM_PACKAGES=1 ${py} -m pip install black==24.10.0 flake8==7.1.1 fi fi python_sources=$(LANG=C find scripts src/bindings/python -type f -exec file {} \; | grep -F "Python script" | sed 's/:.*//') diff --git a/test/gurka/test_exclude_unpaved_smoothness.cc b/test/gurka/test_exclude_unpaved_smoothness.cc index cca8b773b2..fd7af1455a 100644 --- a/test/gurka/test_exclude_unpaved_smoothness.cc +++ b/test/gurka/test_exclude_unpaved_smoothness.cc @@ -146,7 +146,8 @@ TEST_F(ExcludeUnpavedTest, UnpavedRoadsInTheEnd) { } } -valhalla::gurka::map BuildPBF(const std::string& workdir) { +TEST(Standalone, SmoothnessAccess) { + const std::string ascii_map = R"( A--------B-----1--C-------D @@ -163,37 +164,13 @@ valhalla::gurka::map BuildPBF(const std::string& workdir) { const auto layout = gurka::detail::map_to_coordinates(ascii_map, gridsize, {5.1079374, 52.0887174}); - auto pbf_filename = workdir + "/map.pbf"; - detail::build_pbf(layout, ways, nodes, {}, pbf_filename); - - valhalla::gurka::map result; - result.nodes = layout; - - return result; -} - -TEST(Standalone, SmoothnessAccess) { - - const std::string workdir = "test/data/gurka_smoothness_access"; - - if (!std::filesystem::exists(workdir)) { - bool created = std::filesystem::create_directories(workdir); - EXPECT_TRUE(created); - } - - valhalla::gurka::map map = BuildPBF(workdir); + auto map = gurka::buildtiles(layout, ways, nodes, {}, "test/data/smoothness_access"); const std::string sqlite = {VALHALLA_SOURCE_DIR "test/data/netherlands_admin.sqlite"}; boost::property_tree::ptree& pt = map.config; - pt.put("mjolnir.tile_dir", workdir + "/tiles"); pt.put("mjolnir.admin", sqlite); - std::vector input_files = {workdir + "/map.pbf"}; - - build_tile_set(pt, input_files, mjolnir::BuildStage::kInitialize, mjolnir::BuildStage::kValidate, - false); - - GraphReader graph_reader(pt.get_child("mjolnir")); + auto graph_reader = test::make_clean_graphreader(pt.get_child("mjolnir")); { GraphId edge_id_1; @@ -201,14 +178,14 @@ TEST(Standalone, SmoothnessAccess) { GraphId edge_id_2; const DirectedEdge* edge_2 = nullptr; - std::tie(edge_id_1, edge_1, edge_id_2, edge_2) = findEdge(graph_reader, map.nodes, "AB", "B"); + std::tie(edge_id_1, edge_1, edge_id_2, edge_2) = findEdge(*graph_reader, map.nodes, "AB", "B"); // no access due to smoothness = impassable and are therefore tossed. // edge_1 = AB // edge_2 = BA EXPECT_EQ(edge_1, nullptr); EXPECT_EQ(edge_2, nullptr); - std::tie(edge_id_1, edge_1, edge_id_2, edge_2) = findEdge(graph_reader, map.nodes, "B1C", "C"); + std::tie(edge_id_1, edge_1, edge_id_2, edge_2) = findEdge(*graph_reader, map.nodes, "B1C", "C"); // edge_1 = B1C // edge_2 = C1B // edge is not tossed @@ -217,8 +194,8 @@ TEST(Standalone, SmoothnessAccess) { EXPECT_NE(edge_2->forwardaccess(), 0); EXPECT_NE(edge_2->reverseaccess(), 0); - auto node_id = gurka::findNode(graph_reader, map.nodes, "1"); - const auto* node = graph_reader.nodeinfo(node_id); + auto node_id = gurka::findNode(*graph_reader, map.nodes, "1"); + const auto* node = graph_reader->nodeinfo(node_id); // no access due to smoothness = impassable EXPECT_EQ(node->access(), 0); } diff --git a/test/narrative_dictionary.cc b/test/narrative_dictionary.cc index 1f6c716f26..b71a19ea63 100644 --- a/test/narrative_dictionary.cc +++ b/test/narrative_dictionary.cc @@ -374,14 +374,14 @@ const std::map kExpectedArrivePhrases = const std::map kExpectedArriveVerbalPhrases = {{"0", "Arrive at