Skip to content

Commit

Permalink
Add configurable maximum travel time (#155)
Browse files Browse the repository at this point in the history
* Add option to limit maximum travel time

* Revert changes to fastest_direct

* Simplify runtime check for maximum travel time

* Add test with invalid duration

* Add tests for maximum transfers

* Remove optional

* Rename variable

* Rename test file

* Set upper limit for calculations

* Simplify search interface for tests

* Remove no longer used parameter

* Remove unused include

* Add test for edge case

* Fix formatting

* Improve input validation

* Revert 35f64c3

* Sanitize query passed to raptor_search()

* Remove hardly applicable assertion

* Merge sanitizer functions into single function

* Fix code style
  • Loading branch information
MichaelKutzner authored Dec 9, 2024
1 parent 0568714 commit 2cf648a
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 90 deletions.
7 changes: 7 additions & 0 deletions include/nigiri/routing/query.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <cinttypes>
#include <limits>
#include <optional>
#include <variant>
#include <vector>

Expand All @@ -14,6 +15,10 @@
#include "nigiri/td_footpath.h"
#include "nigiri/types.h"

namespace nigiri {
struct timetable;
}

namespace nigiri::routing {

// Integer value that enables the caller to know
Expand Down Expand Up @@ -62,6 +67,7 @@ using start_time_t = std::variant<unixtime_t, interval<unixtime_t>>;

struct query {
friend bool operator==(query const&, query const&) = default;
void sanitize(timetable const&);

start_time_t start_time_;
location_match_mode start_match_mode_{
Expand All @@ -73,6 +79,7 @@ struct query {
hash_map<location_idx_t, std::vector<td_offset>> td_start_{}, td_dest_{};
duration_t max_start_offset_{kMaxTravelTime};
std::uint8_t max_transfers_{kMaxTransfers};
duration_t max_travel_time_{kMaxTravelTime};
unsigned min_connection_count_{0U};
bool extend_interval_earlier_{false};
bool extend_interval_later_{false};
Expand Down
15 changes: 10 additions & 5 deletions include/nigiri/routing/search.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
#include "nigiri/routing/get_fastest_direct.h"
#include "nigiri/routing/interval_estimate.h"
#include "nigiri/routing/journey.h"
#include "nigiri/routing/limits.h"
#include "nigiri/routing/pareto_set.h"
#include "nigiri/routing/query.h"
#include "nigiri/routing/sanitize_via_stops.h"
#include "nigiri/routing/start_times.h"
#include "nigiri/timetable.h"
#include "nigiri/types.h"
Expand Down Expand Up @@ -175,7 +175,7 @@ struct search {
timeout_(timeout) {
utl::sort(q_.start_);
utl::sort(q_.destination_);
sanitize_via_stops(tt_, q_);
q.sanitize(tt);
}

routing_result<algo_stats_t> execute() {
Expand Down Expand Up @@ -295,7 +295,7 @@ struct search {
utl::erase_if(state_.results_, [&](journey const& j) {
return !search_interval_.contains(j.start_time_) ||
j.travel_time() >= fastest_direct_ ||
j.travel_time() > kMaxTravelTime;
j.travel_time() > q_.max_travel_time_;
});
utl::sort(state_.results_, [](journey const& a, journey const& b) {
return a.start_time_ < b.start_time_;
Expand Down Expand Up @@ -401,9 +401,14 @@ struct search {
algo_.add_start(s.stop_, s.time_at_stop_);
}

/*
* Upper bound: Search journeys faster than 'worst_time_at_dest'
* It will not find journeys with the same duration
*/
auto const worst_time_at_dest =
start_time +
(kFwd ? 1 : -1) * std::min(fastest_direct_, kMaxTravelTime);
start_time + (kFwd ? 1 : -1) *
(std::min(fastest_direct_, q_.max_travel_time_) +
duration_t{1});
algo_.execute(start_time, q_.max_transfers_, worst_time_at_dest,
q_.prf_idx_, state_.results_);

Expand Down
2 changes: 1 addition & 1 deletion src/routing/dijkstra.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ void dijkstra(timetable const& tt,
for (auto const& [from, td] : q.td_dest_) {
for (auto const& fp : td) {
if (fp.duration_ != footpath::kMaxDuration &&
fp.duration_ < kMaxTravelTime) {
fp.duration_ < q.max_travel_time_) {
update_min(from, fp.duration_);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#pragma once
#include "nigiri/routing/query.h"

#include "nigiri/for_each_meta.h"
#include "nigiri/routing/query.h"
#include "nigiri/timetable.h"

namespace nigiri::routing {

inline void sanitize_query(query& q) {
if (q.max_travel_time_.count() < 0 || q.max_travel_time_ > kMaxTravelTime) {
q.max_travel_time_ = kMaxTravelTime;
}
}

inline void sanitize_via_stops(timetable const& tt, query& q) {
while (q.via_stops_.size() >= 2) {
auto updated = false;
Expand All @@ -26,4 +30,9 @@ inline void sanitize_via_stops(timetable const& tt, query& q) {
}
}

} // namespace nigiri::routing
void query::sanitize(timetable const& tt) {
sanitize_query(*this);
sanitize_via_stops(tt, *this);
}

} // namespace nigiri::routing
4 changes: 2 additions & 2 deletions src/routing/raptor_search.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include "utl/verify.h"

#include "nigiri/get_otel_tracer.h"
#include "nigiri/routing/sanitize_via_stops.h"
#include "nigiri/routing/query.h"

namespace nigiri::routing {

Expand Down Expand Up @@ -50,7 +50,7 @@ routing_result<raptor_stats> raptor_search_with_dir(
raptor_state& r_state,
query q,
std::optional<std::chrono::seconds> const timeout) {
sanitize_via_stops(tt, q);
q.sanitize(tt);
utl::verify(q.via_stops_.size() <= kMaxVias,
"too many via stops: {}, limit: {}", q.via_stops_.size(),
kMaxVias);
Expand Down
64 changes: 41 additions & 23 deletions test/raptor_search.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "./raptor_search.h"

#include "nigiri/routing/limits.h"
#include "nigiri/routing/raptor/raptor.h"
#include "nigiri/routing/raptor_search.h"
#include "nigiri/routing/search.h"
Expand Down Expand Up @@ -32,17 +33,15 @@ pareto_set<routing::journey> raptor_search(timetable const& tt,
.journeys_);
}

pareto_set<routing::journey> raptor_search(
timetable const& tt,
rt_timetable const* rtt,
std::string_view from,
std::string_view to,
routing::start_time_t time,
direction const search_dir,
routing::clasz_mask_t const mask,
bool const require_bikes_allowed,
profile_idx_t const profile,
routing::transfer_time_settings const tts) {
pareto_set<routing::journey> raptor_search(timetable const& tt,
rt_timetable const* rtt,
std::string_view from,
std::string_view to,
routing::start_time_t time,
direction const search_dir,
routing::clasz_mask_t const mask,
bool const require_bikes_allowed,
profile_idx_t const profile) {
auto const src = source_idx_t{0};
auto q = routing::query{
.start_time_ = time,
Expand All @@ -53,23 +52,42 @@ pareto_set<routing::journey> raptor_search(
.prf_idx_ = profile,
.allowed_claszes_ = mask,
.require_bike_transport_ = require_bikes_allowed,
.transfer_time_settings_ = tts,
.via_stops_ = {}};
return raptor_search(tt, rtt, std::move(q), search_dir);
}

pareto_set<routing::journey> raptor_search(
timetable const& tt,
rt_timetable const* rtt,
std::string_view from,
std::string_view to,
std::string_view time,
direction const search_dir,
routing::clasz_mask_t mask,
bool const require_bikes_allowed,
routing::transfer_time_settings const tts) {
pareto_set<routing::journey> raptor_search(timetable const& tt,
rt_timetable const* rtt,
std::string_view from,
std::string_view to,
std::string_view time,
direction const search_dir,
routing::clasz_mask_t mask,
bool const require_bikes_allowed) {
return raptor_search(tt, rtt, from, to, parse_time(time, "%Y-%m-%d %H:%M %Z"),
search_dir, mask, require_bikes_allowed, 0U, tts);
search_dir, mask, require_bikes_allowed, 0U);
}

pareto_set<routing::journey> raptor_search(timetable const& tt,
rt_timetable const* rtt,
routing::query&& q,
std::string_view from,
std::string_view to,
std::string_view time,
direction const search_dir) {
auto const src = source_idx_t{0};
if (!from.empty()) {
q.start_ = {
{tt.locations_.location_id_to_idx_.at({from, src}), 0_minutes, 0U}};
}
if (!to.empty()) {
q.destination_ = {
{tt.locations_.location_id_to_idx_.at({to, src}), 0_minutes, 0U}};
}
if (!time.empty()) {
q.start_time_ = parse_time(time, "%Y-%m-%d %H:%M %Z");
}
return raptor_search(tt, rtt, std::move(q), search_dir);
}

pareto_set<routing::journey> raptor_intermodal_search(
Expand Down
14 changes: 10 additions & 4 deletions test/raptor_search.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ pareto_set<routing::journey> raptor_search(
std::string_view time,
direction = direction::kForward,
routing::clasz_mask_t mask = routing::all_clasz_allowed(),
bool require_bikes_allowed = false,
routing::transfer_time_settings tts = {});
bool require_bikes_allowed = false);

pareto_set<routing::journey> raptor_search(
timetable const&,
Expand All @@ -31,14 +30,21 @@ pareto_set<routing::journey> raptor_search(
direction = direction::kForward,
routing::clasz_mask_t mask = routing::all_clasz_allowed(),
bool require_bikes_allowed = false,
profile_idx_t const profile = 0U,
routing::transfer_time_settings tts = {});
profile_idx_t const profile = 0U);

pareto_set<routing::journey> raptor_search(timetable const&,
rt_timetable const*,
routing::query,
direction = direction::kForward);

pareto_set<routing::journey> raptor_search(timetable const& tt,
rt_timetable const* rtt,
routing::query&& q,
std::string_view from,
std::string_view to,
std::string_view time,
direction const search_dir);

pareto_set<routing::journey> raptor_intermodal_search(
timetable const&,
rt_timetable const*,
Expand Down
Loading

0 comments on commit 2cf648a

Please sign in to comment.