diff --git a/include/nigiri/abi.h b/include/nigiri/abi.h index 39a422c5..1f14dbb4 100644 --- a/include/nigiri/abi.h +++ b/include/nigiri/abi.h @@ -17,6 +17,14 @@ struct nigiri_transport { }; typedef struct nigiri_transport nigiri_transport_t; +static const uint32_t kTargetBits = 22U; +static const uint32_t kDurationBits = 8 * sizeof(uint32_t) - kTargetBits; +struct nigiri_footpath { + unsigned int target_location_idx : kTargetBits; + unsigned int duration : kDurationBits; +}; +typedef struct nigiri_footpath nigiri_footpath_t; + struct nigiri_location { const char* id; uint32_t id_len; @@ -25,6 +33,8 @@ struct nigiri_location { double lon; double lat; uint16_t transfer_time; + nigiri_footpath_t* footpaths; + uint32_t n_footpaths; uint32_t parent; }; typedef struct nigiri_location nigiri_location_t; @@ -48,16 +58,49 @@ typedef struct nigiri_route nigiri_route_t; struct nigiri_event_change { uint32_t transport_idx; uint16_t day_idx; - uint32_t stop_idx; + uint16_t stop_idx; bool is_departure; - int16_t delay; - bool cancelled; + bool stop_change; + uint32_t stop_location_idx; // ignore if UINT_MAX or stop_change == false + bool stop_in_out_allowed; // ignore if stop_change == false + int16_t delay; // ignore if stop_change == true }; typedef struct nigiri_event_change nigiri_event_change_t; +struct nigiri_leg { + bool is_footpath; + uint32_t transport_idx; + uint16_t day_idx; + uint16_t from_stop_idx; + uint32_t from_location_idx; + uint16_t to_stop_idx; + uint32_t to_location_idx; + uint32_t duration; +}; +typedef struct nigiri_leg nigiri_leg_t; + +struct nigiri_journey { + uint16_t n_legs; + nigiri_leg_t* legs; + int64_t start_time; + int64_t dest_time; +}; +typedef struct nigiri_journey nigiri_journey_t; + +struct nigiri_pareto_set { + uint16_t n_journeys; + nigiri_journey_t* journeys; +}; +typedef struct nigiri_pareto_set nigiri_pareto_set_t; + nigiri_timetable_t* nigiri_load(const char* path, int64_t from_ts, int64_t to_ts); +nigiri_timetable_t* nigiri_load_linking_stops(const char* path, + int64_t from_ts, + int64_t to_ts, + unsigned link_stop_distance); + void nigiri_destroy(const nigiri_timetable_t* t); int64_t nigiri_get_start_day_ts(const nigiri_timetable_t* t); uint16_t nigiri_get_day_count(const nigiri_timetable_t* t); @@ -68,11 +111,14 @@ void nigiri_destroy_transport(const nigiri_transport_t* transport); bool nigiri_is_transport_active(const nigiri_timetable_t* t, const uint32_t transport_idx, uint16_t day_idx); +uint32_t nigiri_get_route_count(const nigiri_timetable_t* t); nigiri_route_t* nigiri_get_route(const nigiri_timetable_t* t, uint32_t idx); void nigiri_destroy_route(const nigiri_route_t* route); uint32_t nigiri_get_location_count(const nigiri_timetable_t* t); nigiri_location_t* nigiri_get_location(const nigiri_timetable_t* t, uint32_t idx); +nigiri_location_t* nigiri_get_location_with_footpaths( + const nigiri_timetable_t* t, uint32_t idx, bool incoming_footpaths); void nigiri_destroy_location(const nigiri_location_t* location); void nigiri_update_with_rt(const nigiri_timetable_t* t, @@ -81,6 +127,14 @@ void nigiri_update_with_rt(const nigiri_timetable_t* t, void* context), void* context); +nigiri_pareto_set_t* nigiri_get_journeys(const nigiri_timetable_t* t, + uint32_t start_location_idx, + uint32_t destination_location_idx, + int64_t time, + bool backward_search); + +void nigiri_destroy_journeys(const nigiri_pareto_set_t* journeys); + #ifdef __cplusplus } #endif diff --git a/include/nigiri/rt/rt_timetable.h b/include/nigiri/rt/rt_timetable.h index bfbc2a0a..a606ce77 100644 --- a/include/nigiri/rt/rt_timetable.h +++ b/include/nigiri/rt/rt_timetable.h @@ -4,17 +4,20 @@ #include "nigiri/common/delta_t.h" #include "nigiri/common/interval.h" +#include "nigiri/rt/run.h" #include "nigiri/stop.h" #include "nigiri/timetable.h" #include "nigiri/types.h" namespace nigiri { -using change_callback_t = std::function; +using change_callback_t = + std::function const location_idx, + std::optional const in_out_allowed, + std::optional const delay)>; // General note: // - The real-time timetable does not use bitfields. It requires an initial copy @@ -63,16 +66,36 @@ struct rt_timetable { void reset_change_callback() { change_callback_ = nullptr; } - void dispatch_event_change(transport const t, - stop_idx_t const stop_idx, - event_type const ev_type, - duration_t const delay, - bool const cancelled) { - if (change_callback_) { - change_callback_(t, stop_idx, ev_type, delay, cancelled); + void dispatch_event(rt::run const& r, + stop_idx_t const stop_idx, + event_type const ev_type, + std::optional const location_idx, + std::optional const in_out_allowed, + std::optional const delay) { + if (change_callback_ && + ((ev_type == event_type::kArr && stop_idx != r.stop_range_.from_) || + (ev_type == event_type::kDep && stop_idx != r.stop_range_.to_ - 1))) { + change_callback_(r.t_, stop_idx, ev_type, location_idx, in_out_allowed, + delay); } } + void dispatch_delay(rt::run const& r, + stop_idx_t const stop_idx, + event_type const ev_type, + duration_t const delay) { + dispatch_event(r, stop_idx, ev_type, std::nullopt, std::nullopt, delay); + } + + void dispatch_stop_change(rt::run const& r, + stop_idx_t const stop_idx, + event_type const ev_type, + std::optional const location_idx, + std::optional const in_out_allowed) { + dispatch_event(r, stop_idx, ev_type, location_idx, in_out_allowed, + std::nullopt); + } + unixtime_t unix_event_time(rt_transport_idx_t const rt_t, stop_idx_t const stop_idx, event_type const ev_type) const { diff --git a/src/abi.cc b/src/abi.cc index 4ca0a658..ccdcdd34 100644 --- a/src/abi.cc +++ b/src/abi.cc @@ -7,6 +7,7 @@ #include "date/date.h" #include "utl/helpers/algorithm.h" +#include "utl/overloaded.h" #include "utl/progress_tracker.h" #include "utl/verify.h" @@ -24,6 +25,11 @@ #include "nigiri/timetable.h" #include "nigiri/types.h" +#include "nigiri/routing/journey.h" +#include "nigiri/routing/raptor/raptor.h" +#include "nigiri/routing/search.h" +#include "nigiri/rt/frun.h" + #include "nigiri/common/interval.h" #include "cista/memory_holder.h" @@ -36,7 +42,8 @@ struct nigiri_timetable { nigiri_timetable_t* nigiri_load_from_dir(nigiri::loader::dir const& d, int64_t from_ts, - int64_t to_ts) { + int64_t to_ts, + unsigned link_stop_distance) { auto loaders = std::vector>{}; loaders.emplace_back(std::make_unique()); @@ -66,9 +73,13 @@ nigiri_timetable_t* nigiri_load_from_dir(nigiri::loader::dir const& d, static_cast(to_ts)))}; nigiri::loader::register_special_stations(*t->tt); + auto local_bitfield_indices = nigiri::hash_map{}; - (*c)->load({}, src, d, *t->tt, local_bitfield_indices, nullptr, nullptr); + + (*c)->load({.link_stop_distance_ = link_stop_distance, + .default_tz_ = "Europe/Berlin"}, + src, d, *t->tt, local_bitfield_indices, nullptr, nullptr); nigiri::loader::finalize(*t->tt); t->rtt = std::make_shared( @@ -79,12 +90,19 @@ nigiri_timetable_t* nigiri_load_from_dir(nigiri::loader::dir const& d, nigiri_timetable_t* nigiri_load(const char* path, int64_t from_ts, int64_t to_ts) { + return nigiri_load_linking_stops(path, from_ts, to_ts, 0); +} + +nigiri_timetable_t* nigiri_load_linking_stops(const char* path, + int64_t from_ts, + int64_t to_ts, + unsigned link_stop_distance) { auto const progress_tracker = utl::activate_progress_tracker("libnigiri"); auto const silencer = utl::global_progress_bars{true}; auto const tt_path = std::filesystem::path{path}; auto const d = nigiri::loader::make_dir(tt_path); - return nigiri_load_from_dir(*d, from_ts, to_ts); + return nigiri_load_from_dir(*d, from_ts, to_ts, link_stop_distance); } void nigiri_destroy(const nigiri_timetable_t* t) { delete t; } @@ -149,17 +167,20 @@ bool nigiri_is_transport_active(const nigiri_timetable_t* t, return t->tt->bitfields_[t->tt->transport_traffic_days_[tidx]].test(day_idx); } +uint32_t nigiri_get_route_count(const nigiri_timetable_t* t) { + return t->tt->n_routes(); +} + nigiri_route_t* nigiri_get_route(const nigiri_timetable_t* t, uint32_t idx) { auto const ridx = nigiri::route_idx_t{idx}; auto stops = t->tt->route_location_seq_[ridx]; - auto const n_stops = stops.size(); - auto route_stops = new nigiri_route_stop_t[n_stops]; - std::memcpy(route_stops, &stops.front(), - sizeof(nigiri_route_stop_t) * n_stops); - auto route = new nigiri_route_t; - - route->stops = route_stops; + auto const n_stops = stops.size(); + route->stops = new nigiri_route_stop_t[n_stops]; + if (n_stops > 0) { + std::memcpy(route->stops, &stops.front(), + sizeof(nigiri_route_stop_t) * n_stops); + } route->n_stops = static_cast(n_stops); route->clasz = static_cast(t->tt->route_section_clasz_[ridx].front()); @@ -175,8 +196,8 @@ uint32_t nigiri_get_location_count(const nigiri_timetable_t* t) { return t->tt->n_locations(); } -nigiri_location_t* nigiri_get_location(const nigiri_timetable_t* t, - uint32_t idx) { +nigiri_location_t* nigiri_get_location_with_footpaths( + const nigiri_timetable_t* t, uint32_t idx, bool incoming_footpaths) { auto const lidx = nigiri::location_idx_t{idx}; auto location = new nigiri_location_t; auto l = t->tt->locations_.get(lidx); @@ -187,6 +208,16 @@ nigiri_location_t* nigiri_get_location(const nigiri_timetable_t* t, location->lat = l.pos_.lat_; location->lon = l.pos_.lng_; location->transfer_time = static_cast(l.transfer_time_.count()); + auto footpaths = incoming_footpaths + ? t->tt->locations_.footpaths_in_[0][lidx] + : t->tt->locations_.footpaths_out_[0][lidx]; + auto const n_footpaths = footpaths.size(); + location->footpaths = new nigiri_footpath_t[n_footpaths]; + if (n_footpaths > 0) { + std::memcpy(location->footpaths, &footpaths.front(), + sizeof(nigiri_footpath_t) * n_footpaths); + } + location->n_footpaths = static_cast(n_footpaths); location->parent = l.parent_ == nigiri::location_idx_t::invalid() ? 0 @@ -194,7 +225,13 @@ nigiri_location_t* nigiri_get_location(const nigiri_timetable_t* t, return location; } +nigiri_location_t* nigiri_get_location(const nigiri_timetable_t* t, + uint32_t idx) { + return nigiri_get_location_with_footpaths(t, idx, false); +} + void nigiri_destroy_location(const nigiri_location_t* location) { + delete[] location->footpaths; delete location; } @@ -208,16 +245,21 @@ void nigiri_update_with_rt_from_buf(const nigiri_timetable_t* t, auto const rtt_callback = [&](nigiri::transport const transport, nigiri::stop_idx_t const stop_idx, - nigiri::event_type const ev_type, nigiri::duration_t const delay, - bool const cancelled) { + nigiri::event_type const ev_type, + std::optional const location_idx, + std::optional in_out_allowed, + std::optional const delay) { nigiri_event_change_t const c = { .transport_idx = static_cast(transport.t_idx_), .day_idx = static_cast(transport.day_), .stop_idx = stop_idx, .is_departure = ev_type != nigiri::event_type::kArr, - .delay = delay.count(), - .cancelled = cancelled}; + .stop_change = !delay.has_value(), + .stop_location_idx = static_cast( + location_idx.value_or(nigiri::location_idx_t::invalid())), + .stop_in_out_allowed = in_out_allowed.value_or(true), + .delay = delay.value_or(nigiri::duration_t{0}).count()}; callback(c, context); }; @@ -242,3 +284,125 @@ void nigiri_update_with_rt(const nigiri_timetable_t* t, auto const file = cista::mmap{gtfsrt_pb_path, cista::mmap::protection::READ}; return nigiri_update_with_rt_from_buf(t, file.view(), callback, context); } + +nigiri::pareto_set raptor_search( + nigiri::timetable const& tt, + nigiri::rt_timetable const* rtt, + nigiri::routing::query q, + bool backward_search) { + static auto search_state = nigiri::routing::search_state{}; + static auto algo_state = nigiri::routing::raptor_state{}; + if (backward_search) { + using algo_t = + nigiri::routing::raptor; + return *(nigiri::routing::search{ + tt, rtt, search_state, algo_state, std::move(q)} + .execute() + .journeys_); + } else { + using algo_t = + nigiri::routing::raptor; + return *(nigiri::routing::search{ + tt, rtt, search_state, algo_state, std::move(q)} + .execute() + .journeys_); + } +} + +nigiri_pareto_set_t* nigiri_get_journeys(const nigiri_timetable_t* t, + uint32_t start_location_idx, + uint32_t destination_location_idx, + int64_t time, + bool backward_search) { + using namespace date; + using namespace nigiri; + auto q = nigiri::routing::query{ + .start_time_ = floor( + std::chrono::system_clock::from_time_t(static_cast(time))), + .start_ = {{nigiri::location_idx_t{start_location_idx}, 0_minutes, 0U}}, + .destination_ = {{nigiri::location_idx_t{destination_location_idx}, + 0_minutes, 0U}}, + .prf_idx_ = 0}; + + auto journeys = raptor_search(*t->tt, t->rtt.get(), q, backward_search); + auto const n_journeys = + static_cast(std::distance(journeys.begin(), journeys.end())); + auto js = new nigiri_journey_t[n_journeys]; + + auto const pareto_set = new nigiri_pareto_set_t; + pareto_set->n_journeys = static_cast(n_journeys); + pareto_set->journeys = js; + + auto i = 0; + for (auto it = journeys.begin(); it != journeys.end(); it++, i++) { + js[i].n_legs = static_cast(it->legs_.size()); + js[i].legs = new nigiri_leg_t[it->legs_.size()]; + js[i].start_time = std::chrono::system_clock::to_time_t(it->start_time_); + js[i].dest_time = std::chrono::system_clock::to_time_t(it->dest_time_); + + for (auto const [j, leg] : utl::enumerate(it->legs_)) { + auto const l = &js[i].legs[j]; + + auto const set_run = + [&](nigiri::routing::journey::run_enter_exit const& run) { + auto const frun = nigiri::rt::frun{*t->tt, t->rtt.get(), run.r_}; + auto const from = frun[run.stop_range_.from_]; + auto const to = frun[run.stop_range_.to_ - 1U]; + l->is_footpath = false; + l->transport_idx = + run.r_.is_scheduled() + ? static_cast( + run.r_.t_.t_idx_) + : 0; + l->day_idx = + run.r_.is_scheduled() + ? static_cast(run.r_.t_.day_) + : 0; + l->from_stop_idx = run.stop_range_.from_; + l->from_location_idx = static_cast( + from.get_location_idx()); + l->to_stop_idx = run.stop_range_.to_ - 1U; + l->to_location_idx = static_cast( + to.get_location_idx()); + l->duration = + static_cast((to.time(nigiri::event_type::kArr) - + from.time(nigiri::event_type::kDep)) + .count()); + }; + auto const set_footpath = [&, leg](nigiri::footpath const fp) { + l->is_footpath = true; + l->transport_idx = 0; + l->day_idx = 0; + l->from_stop_idx = 0; + l->from_location_idx = + static_cast(leg.from_); + l->to_stop_idx = 0; + l->to_location_idx = + static_cast(leg.to_); + l->duration = static_cast(fp.duration().count()); + }; + auto const set_offset = [&, leg](nigiri::routing::offset const x) { + l->is_footpath = true; + l->transport_idx = 0; + l->day_idx = 0; + l->from_stop_idx = 0; + l->from_location_idx = + static_cast(leg.from_); + l->to_stop_idx = 0; + l->to_location_idx = + static_cast(leg.to_); + l->duration = static_cast(x.duration().count()); + }; + std::visit(utl::overloaded{set_run, set_footpath, set_offset}, leg.uses_); + } + } + return pareto_set; +} + +void nigiri_destroy_journeys(const nigiri_pareto_set_t* journeys) { + for (int i = 0; i < journeys->n_journeys; i++) { + delete[] journeys->journeys[i].legs; + } + delete[] journeys->journeys; + delete journeys; +} diff --git a/src/rt/gtfsrt_update.cc b/src/rt/gtfsrt_update.cc index e5b83402..86a4f899 100644 --- a/src/rt/gtfsrt_update.cc +++ b/src/rt/gtfsrt_update.cc @@ -71,10 +71,12 @@ delay_propagation update_delay(timetable const& tt, duration_t const delay, std::optional const min) { auto const static_time = tt.event_time(r.t_, stop_idx, ev_type); - rtt.update_time(r.rt_, stop_idx, ev_type, - min.has_value() ? std::max(*min, static_time + delay) - : static_time + delay); - rtt.dispatch_event_change(r.t_, stop_idx, ev_type, delay, false); + auto const lower_bounded_new_time = min.has_value() + ? std::max(*min, static_time + delay) + : static_time + delay; + rtt.update_time(r.rt_, stop_idx, ev_type, lower_bounded_new_time); + rtt.dispatch_delay(r, stop_idx, ev_type, + lower_bounded_new_time - static_time); return {rtt.unix_event_time(r.rt_, stop_idx, ev_type), delay}; } @@ -95,12 +97,12 @@ delay_propagation update_event(timetable const& tt, auto const new_time = unixtime_t{std::chrono::duration_cast( std::chrono::seconds{ev.time()})}; - rtt.update_time( - r.rt_, stop_idx, ev_type, - pred_time.has_value() ? std::max(*pred_time, new_time) : new_time); - rtt.dispatch_event_change(r.t_, stop_idx, ev_type, new_time - static_time, - false); - return {new_time, new_time - static_time}; + auto const lower_bounded_new_time = + pred_time.has_value() ? std::max(*pred_time, new_time) : new_time; + rtt.update_time(r.rt_, stop_idx, ev_type, lower_bounded_new_time); + rtt.dispatch_delay(r, stop_idx, ev_type, + lower_bounded_new_time - static_time); + return {lower_bounded_new_time, new_time - static_time}; } } @@ -113,6 +115,11 @@ void cancel_run(timetable const&, rt_timetable& rtt, run& r) { rtt.bitfields_.emplace_back(bf).set(to_idx(r.t_.day_), false); rtt.transport_traffic_days_[r.t_.t_idx_] = bitfield_idx_t{rtt.bitfields_.size() - 1U}; + + for (auto i = r.stop_range_.from_; i != r.stop_range_.to_; ++i) { + rtt.dispatch_stop_change(r, i, event_type::kArr, std::nullopt, false); + rtt.dispatch_stop_change(r, i, event_type::kDep, std::nullopt, false); + } } } @@ -160,9 +167,11 @@ void update_run( auto& stp = rtt.rt_transport_location_seq_[r.rt_][stop_idx]; if (upd_it->schedule_relationship() == gtfsrt::TripUpdate_StopTimeUpdate_ScheduleRelationship_SKIPPED) { + auto l_idx = stop{stp}.location_idx(); // Cancel skipped stops (in_allowed = out_allowed = false). - stp = - stop{stop{stp}.location_idx(), false, false, false, false}.value(); + stp = stop{l_idx, false, false, false, false}.value(); + rtt.dispatch_stop_change(r, stop_idx, event_type::kArr, l_idx, false); + rtt.dispatch_stop_change(r, stop_idx, event_type::kDep, l_idx, false); } else if (upd_it->stop_time_properties().has_assigned_stop_id() || (upd_it->has_stop_id() && upd_it->stop_id() != @@ -185,13 +194,26 @@ void update_run( if (utl::find(transports, r.rt_) == end(transports)) { transports.push_back(r.rt_); } + rtt.dispatch_stop_change(r, stop_idx, event_type::kArr, l_it->second, + s.out_allowed()); + rtt.dispatch_stop_change(r, stop_idx, event_type::kDep, l_it->second, + s.in_allowed()); } else { log(log_lvl::error, "gtfsrt.stop_assignment", "stop assignment: src={}, stop_id=\"{}\" not found", src, new_id); } } else { // Just reset in case a track change / skipped stop got reversed. - stp = location_seq[stop_idx]; + if (location_seq[stop_idx] != stp) { + stp = location_seq[stop_idx]; + auto reset_stop = stop{stp}; + rtt.dispatch_stop_change(r, stop_idx, event_type::kArr, + reset_stop.location_idx(), + reset_stop.out_allowed()); + rtt.dispatch_stop_change(r, stop_idx, event_type::kDep, + reset_stop.location_idx(), + reset_stop.in_allowed()); + } } } diff --git a/src/rt/vdv/vdv_update.cc b/src/rt/vdv/vdv_update.cc index ea883f6a..a04f8ca9 100644 --- a/src/rt/vdv/vdv_update.cc +++ b/src/rt/vdv/vdv_update.cc @@ -332,7 +332,7 @@ void update_event(rt_timetable& rtt, et == event_type::kArr ? "ARR" : "DEP", rs.scheduled_time(et), delay.count() >= 0 ? "+" : "", delay.count()); rtt.update_time(rs.fr_->rt_, rs.stop_idx_, et, new_time); - rtt.dispatch_event_change(rs.fr_->t_, rs.stop_idx_, et, delay, false); + rtt.dispatch_delay(*rs.fr_, rs.stop_idx_, et, delay); if (delay_propagation != nullptr) { *delay_propagation = delay; } @@ -372,7 +372,7 @@ void updater::update_run(rt_timetable& rtt, et == event_type::kArr ? "ARR" : "DEP", rs.scheduled_time(et), delay->count() >= 0 ? "+" : "", delay->count()); rtt.update_time(fr.rt_, rs.stop_idx_, et, rs.scheduled_time(et) + *delay); - rtt.dispatch_event_change(fr.t_, rs.stop_idx_, et, *delay, false); + rtt.dispatch_delay(fr, rs.stop_idx_, et, *delay); ++stats_.propagated_delays_; }; diff --git a/test/abi_test.cc b/test/abi_test.cc index aef93d38..ffd88d82 100644 --- a/test/abi_test.cc +++ b/test/abi_test.cc @@ -2,13 +2,15 @@ #include "date/date.h" +#include #include "nigiri/loader/dir.h" #include "nigiri/abi.h" #include "nigiri/rt/util.h" nigiri_timetable_t* nigiri_load_from_dir(nigiri::loader::dir const& d, int64_t from_ts, - int64_t to_ts); + int64_t to_ts, + unsigned link_stop_distance); void nigiri_update_with_rt_from_buf(const nigiri_timetable_t* t, std::string_view protobuf, void (*callback)(nigiri_event_change_t, @@ -299,7 +301,7 @@ auto const kTripUpdate = "departure": { "time": "1691661276" }, - "stopId": "1916", + "stopId": "1918", "scheduleRelationship": "SCHEDULED" }, { @@ -311,7 +313,7 @@ auto const kTripUpdate = "time": "1691661366" }, "stopId": "1918", - "scheduleRelationship": "SCHEDULED" + "scheduleRelationship": "SKIPPED" }, { "stopSequence": 28, @@ -332,13 +334,17 @@ auto const kTripUpdate = } // namespace -TEST(rt, abi_1) { +TEST(rt, abi_timetable) { auto const t = nigiri_load_from_dir( test_files(), std::chrono::system_clock::to_time_t(date::sys_days{2023_y / August / 9}), std::chrono::system_clock::to_time_t( - date::sys_days{2023_y / August / 12})); + date::sys_days{2023_y / August / 12}), + 0); + + auto const route_count = nigiri_get_route_count(t); + EXPECT_EQ(1, route_count); auto const transport_count = nigiri_get_transport_count(t); EXPECT_EQ(1, transport_count); @@ -349,6 +355,7 @@ TEST(rt, abi_1) { auto const l0 = nigiri_get_location(t, 0); auto const l0_name = std::string{l0->name, l0->name_len}; EXPECT_EQ("START", l0_name); + EXPECT_EQ(0, l0->n_footpaths); nigiri_destroy_location(l0); auto const l9 = nigiri_get_location(t, 9); @@ -356,28 +363,34 @@ TEST(rt, abi_1) { EXPECT_EQ("Block Line Station", l9_name); auto const l9_id = std::string{l9->id, l9->id_len}; EXPECT_EQ("2351", l9_id); + EXPECT_EQ(0, l9->n_footpaths); EXPECT_FLOAT_EQ(43.422095, l9->lat); EXPECT_FLOAT_EQ(-80.462740, l9->lon); EXPECT_EQ(2, l9->transfer_time); EXPECT_EQ(0, l9->parent); nigiri_destroy_location(l9); - auto const l35 = nigiri_get_location(t, 35); + auto const l35 = nigiri_get_location_with_footpaths(t, 35, false); auto const l35_name = std::string{l35->name, l35->name_len}; EXPECT_EQ("King / Manulife", l35_name); auto const l35_id = std::string{l35->id, l35->id_len}; EXPECT_EQ("1918", l35_id); + EXPECT_EQ(1, l35->n_footpaths); + EXPECT_EQ(33, l35->footpaths[0].target_location_idx); + EXPECT_EQ(2, l35->footpaths[0].duration); EXPECT_FLOAT_EQ(43.491207, l35->lat); EXPECT_FLOAT_EQ(-80.528026, l35->lon); EXPECT_EQ(2, l35->transfer_time); EXPECT_EQ(33, l35->parent); nigiri_destroy_location(l35); - auto const l36 = nigiri_get_location(t, location_count - 1); + auto const l36 = + nigiri_get_location_with_footpaths(t, location_count - 1, true); auto const l36_name = std::string{l36->name, l36->name_len}; EXPECT_EQ("Conestoga Station", l36_name); auto const l36_id = std::string{l36->id, l36->id_len}; EXPECT_EQ("1127", l36_id); + EXPECT_EQ(0, l36->n_footpaths); EXPECT_FLOAT_EQ(43.498036, l36->lat); EXPECT_FLOAT_EQ(-80.528999, l36->lon); EXPECT_EQ(2, l36->transfer_time); @@ -432,39 +445,91 @@ TEST(rt, abi_1) { EXPECT_EQ(0, evt.transport_idx); EXPECT_EQ(6, evt.day_idx); - EXPECT_EQ(false, evt.cancelled); if (*test_event_change_counter_ptr == 0) { + EXPECT_EQ(false, evt.stop_change); + EXPECT_EQ(true, evt.stop_in_out_allowed); + EXPECT_EQ(UINT32_MAX, evt.stop_location_idx); EXPECT_EQ(14, evt.stop_idx); EXPECT_EQ(false, evt.is_departure); EXPECT_EQ(1, evt.delay); } if (*test_event_change_counter_ptr == 1) { + EXPECT_EQ(false, evt.stop_change); + EXPECT_EQ(true, evt.stop_in_out_allowed); + EXPECT_EQ(UINT32_MAX, evt.stop_location_idx); EXPECT_EQ(14, evt.stop_idx); EXPECT_EQ(true, evt.is_departure); EXPECT_EQ(1, evt.delay); } if (*test_event_change_counter_ptr == 8) { + EXPECT_EQ(false, evt.stop_change); + EXPECT_EQ(true, evt.stop_in_out_allowed); + EXPECT_EQ(UINT32_MAX, evt.stop_location_idx); EXPECT_EQ(18, evt.stop_idx); EXPECT_EQ(false, evt.is_departure); EXPECT_EQ(2, evt.delay); } if (*test_event_change_counter_ptr == 9) { + EXPECT_EQ(false, evt.stop_change); + EXPECT_EQ(true, evt.stop_in_out_allowed); + EXPECT_EQ(UINT32_MAX, evt.stop_location_idx); EXPECT_EQ(18, evt.stop_idx); EXPECT_EQ(true, evt.is_departure); EXPECT_EQ(2, evt.delay); } if (*test_event_change_counter_ptr == 22) { + EXPECT_EQ(true, evt.stop_change); + EXPECT_EQ(true, evt.stop_in_out_allowed); + EXPECT_EQ(35, evt.stop_location_idx); EXPECT_EQ(25, evt.stop_idx); EXPECT_EQ(false, evt.is_departure); - EXPECT_EQ(-1, evt.delay); + EXPECT_EQ(0, evt.delay); } if (*test_event_change_counter_ptr == 23) { + EXPECT_EQ(true, evt.stop_change); + EXPECT_EQ(true, evt.stop_in_out_allowed); + EXPECT_EQ(35, evt.stop_location_idx); + EXPECT_EQ(25, evt.stop_idx); + EXPECT_EQ(true, evt.is_departure); + EXPECT_EQ(0, evt.delay); + } + if (*test_event_change_counter_ptr == 24) { + EXPECT_EQ(false, evt.stop_change); + EXPECT_EQ(true, evt.stop_in_out_allowed); + EXPECT_EQ(UINT32_MAX, evt.stop_location_idx); + EXPECT_EQ(25, evt.stop_idx); + EXPECT_EQ(false, evt.is_departure); + EXPECT_EQ(-1, evt.delay); + } + if (*test_event_change_counter_ptr == 25) { + EXPECT_EQ(false, evt.stop_change); + EXPECT_EQ(true, evt.stop_in_out_allowed); + EXPECT_EQ(UINT32_MAX, evt.stop_location_idx); EXPECT_EQ(25, evt.stop_idx); EXPECT_EQ(true, evt.is_departure); EXPECT_EQ(-1, evt.delay); } if (*test_event_change_counter_ptr == 26) { + EXPECT_EQ(true, evt.stop_change); + EXPECT_EQ(false, evt.stop_in_out_allowed); + EXPECT_EQ(35, evt.stop_location_idx); + EXPECT_EQ(26, evt.stop_idx); + EXPECT_EQ(false, evt.is_departure); + EXPECT_EQ(0, evt.delay); + } + if (*test_event_change_counter_ptr == 27) { + EXPECT_EQ(true, evt.stop_change); + EXPECT_EQ(false, evt.stop_in_out_allowed); + EXPECT_EQ(35, evt.stop_location_idx); + EXPECT_EQ(26, evt.stop_idx); + EXPECT_EQ(true, evt.is_departure); + EXPECT_EQ(0, evt.delay); + } + if (*test_event_change_counter_ptr == 30) { + EXPECT_EQ(false, evt.stop_change); + EXPECT_EQ(true, evt.stop_in_out_allowed); + EXPECT_EQ(UINT32_MAX, evt.stop_location_idx); EXPECT_EQ(27, evt.stop_idx); EXPECT_EQ(false, evt.is_departure); EXPECT_EQ(0, evt.delay); @@ -474,7 +539,35 @@ TEST(rt, abi_1) { nigiri_update_with_rt_from_buf(t, msg, my_test_callback, &test_event_change_counter); - EXPECT_EQ(27, test_event_change_counter); + EXPECT_EQ(31, test_event_change_counter); nigiri_destroy(t); } + +TEST(rt, abi_journeys) { + + auto const t = nigiri_load_from_dir( + test_files(), + std::chrono::system_clock::to_time_t(date::sys_days{2023_y / August / 9}), + std::chrono::system_clock::to_time_t( + date::sys_days{2023_y / August / 12}), + 0); + + auto const journeys = nigiri_get_journeys(t, 10, 15, 1691660000, false); + EXPECT_EQ(1, journeys->n_journeys); + EXPECT_EQ(1, journeys->journeys[0].n_legs); + EXPECT_EQ(1691659980, journeys->journeys[0].start_time); + EXPECT_EQ(1691745840, journeys->journeys[0].dest_time); + auto const l0 = journeys->journeys[0].legs[0]; + EXPECT_EQ(0, l0.is_footpath); + EXPECT_EQ(0, l0.transport_idx); + EXPECT_EQ(7, l0.day_idx); + EXPECT_EQ(1, l0.from_stop_idx); + EXPECT_EQ(10, l0.from_location_idx); + EXPECT_EQ(6, l0.to_stop_idx); + EXPECT_EQ(15, l0.to_location_idx); + EXPECT_EQ(8, l0.duration); + + nigiri_destroy_journeys(journeys); + nigiri_destroy(t); +}