Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/nigiri/loader/gtfs/files.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ constexpr auto const kRoutesFile = std::string_view{"routes.txt"};
constexpr auto const kTripsFile = std::string_view{"trips.txt"};
constexpr auto const kShapesFile = std::string_view{"shapes.txt"};
constexpr auto const kStopTimesFile = std::string_view{"stop_times.txt"};
constexpr auto const kStopGroupElementsFile =
std::string_view{"stop_group_elements.txt"};
constexpr auto const kCalenderFile = std::string_view{"calendar.txt"};
constexpr auto const kCalendarDatesFile =
std::string_view{"calendar_dates.txt"};
Expand Down
14 changes: 14 additions & 0 deletions include/nigiri/loader/gtfs/stop_group.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <string_view>

#include "nigiri/loader/gtfs/stop.h"
#include "nigiri/timetable.h"

namespace nigiri::loader::gtfs {

void add_stop_groups(timetable&,
std::string_view stop_group_elements_file_content,
stops_map_t const&);

} // namespace nigiri::loader::gtfs
2 changes: 2 additions & 0 deletions src/loader/gtfs/load_timetable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "nigiri/loader/gtfs/shape.h"
#include "nigiri/loader/gtfs/shape_prepare.h"
#include "nigiri/loader/gtfs/stop.h"
#include "nigiri/loader/gtfs/stop_group.h"
#include "nigiri/loader/gtfs/stop_seq_number_encoding.h"
#include "nigiri/loader/gtfs/stop_time.h"
#include "nigiri/loader/gtfs/translations.h"
Expand Down Expand Up @@ -97,6 +98,7 @@ void load_timetable(loader_config const& config,
auto const [stops, seated_transfers] = read_stops(
src, tt, i18n, timezones, load(kStopFile).data(),
load(kTransfersFile).data(), config.link_stop_distance_, user_script);
add_stop_groups(tt, load(kStopGroupElementsFile).data(), stops);
auto const routes =
read_routes(src, tt, i18n, timezones, agencies, load(kRoutesFile).data(),
config.default_tz_, user_script);
Expand Down
65 changes: 65 additions & 0 deletions src/loader/gtfs/stop_group.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "nigiri/loader/gtfs/stop_group.h"

#include <cmath>
#include <algorithm>

#include "utl/parser/buf_reader.h"
#include "utl/parser/csv_range.h"
#include "utl/parser/line_range.h"
#include "utl/pipes/for_each.h"

#include "nigiri/logging.h"

namespace nigiri::loader::gtfs {

void add_stop_groups(timetable& tt,
std::string_view stop_group_elements_file_content,
stops_map_t const& stops) {
struct stop_group_element_record {
utl::csv_col<utl::cstr, UTL_NAME("stop_group_id")> stop_group_id_;
utl::csv_col<utl::cstr, UTL_NAME("stop_id")> stop_id_;
};

if (stop_group_elements_file_content.empty()) {
return;
}

utl::line_range{utl::make_buf_reader(stop_group_elements_file_content)} //
| utl::csv<stop_group_element_record>() //
| utl::for_each([&](stop_group_element_record const& r) {
auto const stop_group_id = r.stop_group_id_->trim().view();
auto const stop_id = r.stop_id_->trim().view();

auto const meta_it = stops.find(stop_group_id);
if (meta_it == end(stops)) {
log(log_lvl::error, "loader.gtfs.stop_groups",
"stop_group_id={:?} not found", stop_group_id);
return;
}

auto const stop_it = stops.find(stop_id);
if (stop_it == end(stops)) {
log(log_lvl::error, "loader.gtfs.stop_groups",
"stop_id={:?} not found", stop_id);
return;
}

auto const meta_l = meta_it->second;
if (tt.locations_.coordinates_[meta_l] != geo::latlng{0, 0}) {
log(log_lvl::error, "loader.gtfs.stop_groups",
"stop_group_id=\"{}\" is not at (0,0)", stop_group_id);
return;
}

auto const stop_l = stop_it->second;
tt.locations_.equivalences_[meta_l].emplace_back(stop_l);
for (auto const child1 : tt.locations_.children_[stop_l]) {
tt.locations_.equivalences_[meta_l].emplace_back(child1);
for (auto const child2 : tt.locations_.children_[child1]) {
tt.locations_.equivalences_[meta_l].emplace_back(child2);
}
}
});
}

} // namespace nigiri::loader::gtfs
3 changes: 1 addition & 2 deletions src/rt/gtfsrt_update.cc
Original file line number Diff line number Diff line change
Expand Up @@ -819,8 +819,7 @@ statistics gtfsrt_update_buf(timetable const& tt,
msg.Clear();

auto const success =
msg.ParseFromArray(reinterpret_cast<void const*>(protobuf.data()),
static_cast<int>(protobuf.size()));
msg.ParseFromArray(protobuf.data(), static_cast<int>(protobuf.size()));
if (!success) {
log(log_lvl::debug, "rt.gtfs",
"GTFS-RT error (tag={}): unable to parse protobuf message: {}", tag,
Expand Down
86 changes: 86 additions & 0 deletions test/loader/gtfs/stop_group_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include "gtest/gtest.h"

#include "nigiri/loader/gtfs/load_timetable.h"
#include "nigiri/loader/init_finish.h"
#include "nigiri/routing/raptor/pong.h"
#include "nigiri/routing/raptor/raptor_state.h"
#include "nigiri/routing/search.h"
#include "nigiri/timetable.h"

using namespace date;
using namespace nigiri;
using namespace nigiri::loader;
using namespace nigiri::loader::gtfs;
using namespace std::chrono_literals;

namespace {

mem_dir stop_group_files() {
return mem_dir::read(R"(
# agency.txt
agency_id,agency_name,agency_url,agency_timezone
DB,Deutsche Bahn,https://deutschebahn.com,Europe/Berlin

# stops.txt
stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station
G1,Group 1,,0.0,0.0,,0,
G2,Group 2,,0.0,0.0,,0,
A,Stop A,,48.1,11.5,,0,
B,Stop B,,48.2,11.6,,0,

# stop_group_elements.txt
stop_group_id,stop_id
G1,A
G2,B

# calendar_dates.txt
service_id,date,exception_type
S1,20200101,1

# routes.txt
route_id,agency_id,route_short_name,route_long_name,route_desc,route_type
R1,DB,R1,,,3

# trips.txt
route_id,service_id,trip_id,trip_headsign,block_id
R1,S1,T1,R1,

# stop_times.txt
trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type
T1,10:00:00,10:00:00,A,1,0,0
T1,10:30:00,10:30:00,B,2,0,0
)");
}

} // namespace

TEST(gtfs, stop_groups_equivalent_routing) {
timetable tt;
register_special_stations(tt);
tt.date_range_ = {sys_days{2020_y / January / 1},
sys_days{2020_y / January / 2}};

load_timetable({}, source_idx_t{0}, stop_group_files(), tt);
finalize(tt);

auto search_state = routing::search_state{};
auto raptor_state = routing::raptor_state{};

auto const src = source_idx_t{0};
auto q = routing::query{
.start_time_ = interval<unixtime_t>{sys_days{2020_y / January / 1} + 0h,
sys_days{2020_y / January / 1} + 24h},
.start_match_mode_ = routing::location_match_mode::kEquivalent,
.dest_match_mode_ = routing::location_match_mode::kEquivalent,
.use_start_footpaths_ = true,
.start_ = {{tt.locations_.location_id_to_idx_.at({"G1", src}), 0_minutes,
0U}},
.destination_ = {
{tt.locations_.location_id_to_idx_.at({"G2", src}), 0_minutes, 0U}}};

auto const result =
routing::pong_search(tt, nullptr, search_state, raptor_state,
std::move(q), direction::kForward);

ASSERT_FALSE(result.journeys_->empty());
}
Loading