2020#include " pugixml.hpp"
2121
2222#include " nigiri/loader/gtfs/route_key.h"
23+ #include " nigiri/loader/gtfs/shape.h"
24+ #include " nigiri/loader/gtfs/shape_prepare.h"
2325#include " nigiri/loader/gtfs/trip.h"
2426#include " nigiri/loader/loader_interface.h"
2527#include " nigiri/logging.h"
28+ #include " nigiri/shapes_storage.h"
2629#include " nigiri/timetable.h"
2730
2831#include " utl/progress_tracker.h"
@@ -85,6 +88,12 @@ std::string_view id(pugi::xml_node n) {
8588 return str == nullptr ? std::string_view{} : std::string_view{str};
8689}
8790
91+ geo::latlng get_pos (pugi::xml_node const x) {
92+ auto const loc = x.child (" Location" );
93+ return geo::latlng{utl::parse<double >(val (loc, " Latitude" )),
94+ utl::parse<double >(val (loc, " Longitude" ))};
95+ }
96+
8897std::uint16_t get_route_type (std::string_view transport_mode,
8998 std::string_view rail_sub_mode) {
9099 switch (cista::hash (transport_mode)) {
@@ -236,6 +245,8 @@ using destination_display_map_t =
236245destination_display_map_t get_destination_displays (
237246 pugi::xml_document const & doc) {
238247 auto destination_displays = destination_display_map_t {};
248+ destination_displays.emplace (std::string_view{},
249+ uniq (destination_display{.direction_ = " " }));
239250 for (auto const display : doc.select_nodes (
240251 " //ServiceFrame/destinationDisplays/DestinationDisplay" )) {
241252 auto const n = display.node ();
@@ -270,15 +281,6 @@ stop_map_t get_stops(pugi::xml_document const& doc) {
270281 " Value" );
271282 };
272283
273- auto const get_pos = [](pugi::xml_node const x) {
274- return geo::latlng{
275- utl::parse<double >(
276- x.select_node (" Centroid/Location/Latitude" ).node ().child_value ()),
277- utl::parse<double >(x.select_node (" Centroid/Location/Longitude" )
278- .node ()
279- .child_value ())};
280- };
281-
282284 auto const stop_id = id (n);
283285 auto const global_stop_id = get_global_id (n);
284286 auto const parent =
@@ -287,7 +289,7 @@ stop_map_t get_stops(pugi::xml_document const& doc) {
287289 uniq (stop{.id_ = !global_stop_id.empty () ? global_stop_id
288290 : stop_id,
289291 .name_ = val (n, " Name" ),
290- .pos_ = get_pos (n)}))
292+ .pos_ = get_pos (n. child ( " Centroid " ) )}))
291293 .first ->second .get ();
292294
293295 for (auto const q : n.select_nodes (" quays/Quay" )) {
@@ -299,7 +301,7 @@ stop_map_t get_stops(pugi::xml_document const& doc) {
299301 uniq (stop{.parent_ = parent,
300302 .id_ = !global_quay_id.empty () ? global_quay_id : quay_id,
301303 .name_ = val (qn, " Name" ),
302- .pos_ = get_pos (qn)}));
304+ .pos_ = get_pos (qn. child ( " Centroid " ) )}));
303305 }
304306 }
305307 return stops;
@@ -384,17 +386,8 @@ day_type_assignment_map_t get_day_type_assignments(
384386 " //ServiceCalendarFrame/ServiceCalendar/dayTypeAssignments/"
385387 " DayTypeAssignment" )) {
386388 auto const n = x.node ();
387- auto const op_period = ref (n, " OperatingPeriodRef" );
388- auto const day_type = ref (n, " DayTypeRef" );
389-
390- auto const it = operating_periods.find (op_period);
391- if (it == end (operating_periods)) {
392- log (log_lvl::error, " netex.DayTypeAssignment" ,
393- " OperatingPeriodRef=\" {}\" not found" , op_period);
394- continue ;
395- }
396-
397- days.emplace (day_type, it->second .get ());
389+ days.emplace (ref (n, " DayTypeRef" ),
390+ operating_periods.at (ref (n, " OperatingPeriodRef" )).get ());
398391 }
399392 return days;
400393}
@@ -422,9 +415,33 @@ journey_pattern_map_t get_journey_patterns(
422415 pugi::xml_document const & doc,
423416 stop_assignment_map_t const & stop_assignments,
424417 destination_display_map_t const & destination_displays,
425- line_map_t const & lines) {
426-
418+ line_map_t const & lines,
419+ stop_map_t & stops) {
427420 auto journey_patterns = journey_pattern_map_t {};
421+
422+ auto const get_stop =
423+ [&](std::string_view stop_point_ref) -> netex::stop const * {
424+ auto const it = stop_assignments.find (stop_point_ref);
425+ if (it != end (stop_assignments)) {
426+ return it->second ;
427+ }
428+
429+ // Invalid - fall back to information from ScheduledStopPoint
430+ return utl::get_or_create (
431+ stops, stop_point_ref,
432+ [&]() {
433+ auto const stop_point = doc.select_node (
434+ fmt::format (" //ServiceFrame/scheduledStopPoints/"
435+ " ScheduledStopPoint[id='{}']" ,
436+ stop_point_ref)
437+ .c_str ());
438+ return uniq (stop{.id_ = stop_point_ref,
439+ .name_ = val (stop_point.node (), " Name" ),
440+ .pos_ = get_pos (stop_point.node ())});
441+ })
442+ .get ();
443+ };
444+
428445 for (auto const j : doc.select_nodes (
429446 " //ServiceFrame/journeyPatterns/ServiceJourneyPattern" )) {
430447 auto const n = j.node ();
@@ -435,7 +452,7 @@ journey_pattern_map_t get_journey_patterns(
435452 auto const out = val (sp, " ForAlighting" );
436453 stop_points.push_back (journey_pattern::stop_point{
437454 .id_ = id (sp),
438- .stop_ = stop_assignments. at (ref (sp, " ScheduledStopPointRef" )),
455+ .stop_ = get_stop (ref (sp, " ScheduledStopPointRef" )),
439456 .destination_display_ =
440457 destination_displays.at (ref (sp, " DestinationDisplayRef" )).get (),
441458 .in_allowed_ = in.empty () || in == " true" sv,
@@ -594,27 +611,27 @@ cista::hash_t hash(dir const& d) {
594611 }
595612
596613 auto h = std::uint64_t {0U };
597- auto const hash_file = [&](fs::path const & p) {
598- if (!d.exists (p)) {
599- h = wyhash64 (h, _wyp[0 ]);
600- } else {
601- auto const f = d.get_file (p);
602- auto const data = f.data ();
603- h = wyhash (data.data (), data.size (), h, _wyp);
604- }
605- };
606-
607- for (auto const & f : d.list_files (" /" )) {
608- if (is_xml_file (f)) {
609- hash_file (f);
610- }
611- }
614+ // auto const hash_file = [&](fs::path const& p) {
615+ // if (!d.exists(p)) {
616+ // h = wyhash64(h, _wyp[0]);
617+ // } else {
618+ // auto const f = d.get_file(p);
619+ // auto const data = f.data();
620+ // h = wyhash(data.data(), data.size(), h, _wyp);
621+ // }
622+ // };
623+ //
624+ // for (auto const& f : d.list_files("/")) {
625+ // if (is_xml_file(f)) {
626+ // hash_file(f);
627+ // }
628+ // }
612629
613630 return h;
614631}
615632
616633bool applicable (dir const & d) {
617- return utl::any_of (d.list_files (" / " ), is_xml_file);
634+ return utl::any_of (d.list_files (" . " ), is_xml_file);
618635}
619636
620637void load_timetable (loader_config const & config,
@@ -623,16 +640,18 @@ void load_timetable(loader_config const& config,
623640 timetable& tt,
624641 hash_map<bitfield, bitfield_idx_t >& bitfield_indices,
625642 assistance_times* /* assistance*/ ,
626- shapes_storage* /* shapes_data*/ ) {
643+ shapes_storage* shapes_data) {
627644 auto const global_timer = nigiri::scoped_timer{" netex parser" };
628645
646+ auto const locations_start = location_idx_t {tt.n_locations ()};
647+
629648 tt.n_sources_ = std::max (tt.n_sources_ , to_idx (src + 1U ));
630649 tt.fares_ .emplace_back ();
631650 tt.src_end_date_ .push_back (date::sys_days::max ());
632651 tt.route_ids_ .emplace_back ();
633652
634653 auto const xml_files =
635- utl::all (d.list_files (" " )) //
654+ utl::all (d.list_files (" . " )) //
636655 | utl::remove_if ([&](fs::path const & f) { return !is_xml_file (f); }) //
637656 | utl::vec ();
638657
@@ -693,7 +712,7 @@ void load_timetable(loader_config const& config,
693712 [&](fs::path const & path) {
694713 auto im = intermediate{};
695714
696- {
715+ try {
697716 auto f = d.get_file (path);
698717 auto doc = pugi::xml_document{};
699718 auto const result =
@@ -715,16 +734,19 @@ void load_timetable(loader_config const& config,
715734 im.stops_ = get_stops (doc);
716735 im.lines_ = get_lines (doc, im.authorities_ );
717736 im.destination_displays_ = get_destination_displays (doc);
718- im.journey_patterns_ =
719- get_journey_patterns ( doc, get_stop_assignments (doc, im.stops_ ),
720- im.destination_displays_ , im.lines_ );
737+ im.journey_patterns_ = get_journey_patterns (
738+ doc, get_stop_assignments (doc, im.stops_ ),
739+ im.destination_displays_ , im.lines_ , im. stops_ );
721740 im.operating_periods_ =
722741 get_operating_periods (doc, tt.internal_interval_days ());
723742 im.service_journeys_ = get_service_journeys (
724743 doc, get_day_type_assignments (doc, im.operating_periods_ ),
725744 im.journey_patterns_ );
726745 im.f_ = std::move (f);
727746 im.doc_ = std::move (doc);
747+ } catch (std::exception const & e) {
748+ std::cout << " ERROR: " << e.what () << " IN " << path << " \n " ;
749+ return ;
728750 }
729751
730752 {
@@ -742,6 +764,13 @@ void load_timetable(loader_config const& config,
742764 }
743765
744766 auto const add_stop = [&](stop* stop) {
767+ auto const existing =
768+ tt.locations_ .find (location_id{stop->id_ , src});
769+ if (existing.has_value ()) {
770+ stop->location_ = existing->l_ ;
771+ return ;
772+ }
773+
745774 auto s = loader::location{stop->id_ ,
746775 stop->name_ ,
747776 " " ,
@@ -835,6 +864,11 @@ void load_timetable(loader_config const& config,
835864 source_file_idx, static_cast <unsigned >(sj.dbg_offset_ ),
836865 static_cast <unsigned >(sj.dbg_offset_ )});
837866 tt.trip_stop_seq_numbers_ .add_back_sized (0U );
867+ if (shapes_data != nullptr ) {
868+ shapes_data->add_trip_shape_offsets (
869+ trip_idx, cista::pair{shape_idx_t::invalid (),
870+ shape_offset_idx_t::invalid ()});
871+ }
838872
839873 auto const c = gtfs::to_clasz (sj.route_type_ );
840874 auto const stops = stop_seq (sj);
@@ -956,6 +990,64 @@ void load_timetable(loader_config const& config,
956990 assert (tt.location_routes_ .size () == l + 1U );
957991 }
958992 }
993+
994+ // Generate default footpaths.
995+ auto const locations_end = location_idx_t {tt.n_locations ()};
996+ auto const new_locations = interval{locations_start, locations_end};
997+ auto const get_location = [&](std::size_t const i) {
998+ return new_locations.from_ + static_cast <unsigned >(i);
999+ };
1000+ auto const get_new_location = [&](location_idx_t const l) {
1001+ return to_idx (l - new_locations.from_ );
1002+ };
1003+
1004+ auto const stop_rtree = geo::make_point_rtree (
1005+ new_locations,
1006+ [&](location_idx_t const l) { return tt.locations_ .coordinates_ [l]; });
1007+
1008+ auto metas = std::vector<std::vector<footpath>>{};
1009+ metas.resize (new_locations.size ());
1010+
1011+ utl::parallel_for_run (
1012+ new_locations.size (),
1013+ [&](std::size_t const new_l) {
1014+ auto const l = get_location (new_l);
1015+ auto const pos = tt.locations_ .coordinates_ [l];
1016+ if (std::abs (pos.lat_ ) < 2.0 && std::abs (pos.lng_ ) < 2.0 ) {
1017+ return ;
1018+ }
1019+ auto const dist_lng_degrees = geo::approx_distance_lng_degrees (pos);
1020+ for (auto const & eq :
1021+ stop_rtree.in_radius (pos, config.link_stop_distance_ )) {
1022+ auto const neighbor = get_location (eq);
1023+ auto const neighbor_pos = tt.locations_ .coordinates_ [neighbor];
1024+ auto const dist = std::sqrt (geo::approx_squared_distance (
1025+ pos, neighbor_pos, dist_lng_degrees));
1026+ auto const duration = duration_t {std::max (
1027+ 2 , static_cast <int >(std::ceil ((dist / kWalkSpeed ) / 60.0 )))};
1028+ metas[get_new_location (l)].emplace_back (neighbor, duration);
1029+ }
1030+ },
1031+ pt->update_fn ());
1032+
1033+ for (auto const [i, fps] : utl::enumerate (metas)) {
1034+ auto const from = get_location (i);
1035+ for (auto const fp : fps) {
1036+ tt.locations_ .equivalences_ [from].emplace_back (fp.target ());
1037+ tt.locations_ .preprocessing_footpaths_out_ [from].emplace_back (fp);
1038+ tt.locations_ .preprocessing_footpaths_in_ [fp.target ()].emplace_back (
1039+ footpath{from, fp.duration ()});
1040+ }
1041+ }
1042+
1043+ if (shapes_data != nullptr ) {
1044+ auto const trips = vector_map<gtfs::gtfs_trip_idx_t , gtfs::trip>{};
1045+ auto const shape_states = gtfs::shape_loader_state{};
1046+ gtfs::calculate_shape_offsets_and_bboxes (tt, *shapes_data, shape_states,
1047+ trips);
1048+ }
1049+
1050+ tt.location_areas_ .resize (tt.n_locations ());
9591051}
9601052
9611053} // namespace nigiri::loader::netex
0 commit comments