def test_create_beeline_footpaths(): wattwil = Stop("Wattwil", "", "", 9.0864591001338, 47.2994765484827) wattwil_bahnhof = Stop("Wattwil, Bahnhof", "", "", 9.08679147678897, 47.2994582722627) pontresina = Stop("Pontresina", "", "", 9.89607473321206, 46.4906506582138) pontresina_bahnhof = Stop("Pontresina, Bahnhof", "", "", 9.89608371636491, 46.4910341056312) pontresina_post = Stop("Pontresina, Post", "", "", 9.90654010627351, 46.4880901497136) bern = Stop("Bern", "", "", 7.43911954873327, 46.9488249647708) bern_bahnhof = Stop("Bern, Bahnhof", "", "", 7.44020651022721, 46.948107473715) stops = [ wattwil, wattwil_bahnhof, pontresina, pontresina_bahnhof, pontresina_post, bern, bern_bahnhof ] stops_per_id = {s.id: s for s in stops} footpaths = { Footpath(wattwil.id, wattwil_bahnhof.id, 120), Footpath(wattwil_bahnhof.id, wattwil.id, 120), } footpaths_per_from_to_stop_id = {(f.from_stop_id, f.to_stop_id): f for f in footpaths} create_beeline_footpaths(stops_per_id, footpaths_per_from_to_stop_id, beeline_distance=100, walking_speed=2 / 3.6) assert 120 == footpaths_per_from_to_stop_id[wattwil.id, wattwil_bahnhof.id].walking_time assert 120 == footpaths_per_from_to_stop_id[wattwil_bahnhof.id, wattwil.id].walking_time assert (pontresina.id, pontresina_bahnhof.id) in footpaths_per_from_to_stop_id assert (pontresina_bahnhof.id, pontresina.id) in footpaths_per_from_to_stop_id assert (pontresina.id, pontresina_post.id) not in footpaths_per_from_to_stop_id assert (pontresina_post.id, pontresina.id) not in footpaths_per_from_to_stop_id assert (bern.id, bern_bahnhof.id) not in footpaths_per_from_to_stop_id assert (bern_bahnhof.id, bern.id) not in footpaths_per_from_to_stop_id
def test_make_transitive_nothing_to_do(): footpaths = [ Footpath("s1", "s2", 60), Footpath("s2", "s3", 70), Footpath("s1", "s3", 90), ] footpaths_per_from_to_stop_id = {(f.from_stop_id, f.to_stop_id): f for f in footpaths} make_transitive(footpaths_per_from_to_stop_id) assert 3 == len(footpaths_per_from_to_stop_id)
def test_make_tranksitive_change_time(): footpaths = [ Footpath("s1", "s2", 60), Footpath("s2", "s3", 70), Footpath("s1", "s3", 140), ] footpaths_per_from_to_stop_id = {(f.from_stop_id, f.to_stop_id): f for f in footpaths} make_transitive(footpaths_per_from_to_stop_id) assert 3 == len(footpaths_per_from_to_stop_id) assert 130 == footpaths_per_from_to_stop_id[("s1", "s3")].walking_time
def check_for_transitivity(footpaths_per_from_to_stop_id): """Checks the footpaths for transitivity and returns missing footpaths and modified footpaths violating the triangle inequality. More precisely: - if there are three stops s_1, s_2 and s_3 with a footpath from s_1 to s_2 and a footpath from s_2 to s_3, but no footpath from s_1 to s_3, a new footpath from s_1 to s_3 is created (with walking time equal to the sum of the walking time of the two existing footpaths. This new footpath is added to the list returned in the first entry of the returned tuple. - if there are three stops s_1, s_2 and s_3 with a footpath from s_1 to s_2, s_2 to s_3 and s_1 to s_3, but the sum of the walking times of s_1 to s_2 and s_2 to s_3 is smaller than the walking time from s_1 to s_3, a new footpath from s_1 to s_3 is created with walking time equal to the sum of the two other walking times (only if this sum is positive). This new footpath is added to the list returned in the second entry of the returned tuple. Args: footpaths_per_from_to_stop_id (dict): footpath per (from_stop_id, to_stop_id)-tuple. Returns: tuple: new and modified footpaths (see above for the details). """ log_start("checking footpaths for transitivity", log) footpaths_per_from_stop_id = {} for (from_stop_id, _), footpath in footpaths_per_from_to_stop_id.items(): footpaths_per_from_stop_id[ from_stop_id] = footpaths_per_from_stop_id.get(from_stop_id, []) + [footpath] new_footpaths = [] footpaths_with_time_change = [] footpaths_per_from_stop_id = dict(footpaths_per_from_stop_id) for from_stop_id, outgoing_footpaths in footpaths_per_from_stop_id.items(): for first_footpath in outgoing_footpaths: for second_footpath in footpaths_per_from_stop_id.get( first_footpath.to_stop_id, []): second_to_stop_id = second_footpath.to_stop_id new_wt = first_footpath.walking_time + second_footpath.walking_time if (from_stop_id, second_to_stop_id ) not in footpaths_per_from_to_stop_id: new_footpaths += [ Footpath(from_stop_id, second_to_stop_id, new_wt) ] elif 0 < new_wt < footpaths_per_from_to_stop_id[( from_stop_id, second_to_stop_id)].walking_time: footpaths_with_time_change += [ Footpath(from_stop_id, second_to_stop_id, new_wt) ] log_end() return new_footpaths, footpaths_with_time_change
def test_make_transitive_simple(): footpaths = [ Footpath("s1", "s2", 60), Footpath("s2", "s3", 70), ] footpaths_per_from_to_stop_id = {(f.from_stop_id, f.to_stop_id): f for f in footpaths} make_transitive(footpaths_per_from_to_stop_id) assert 3 == len(footpaths_per_from_to_stop_id) footpath_s1_s3 = footpaths_per_from_to_stop_id[("s1", "s3")] assert "s1" == footpath_s1_s3.from_stop_id assert "s3" == footpath_s1_s3.to_stop_id assert 130 == footpath_s1_s3.walking_time
def test_make_transitive_two_iterations(): footpaths = [ Footpath("s1", "s2", 60), Footpath("s2", "s3", 70), Footpath("s3", "s4", 70), ] footpaths_per_from_to_stop_id = {(f.from_stop_id, f.to_stop_id): f for f in footpaths} make_transitive(footpaths_per_from_to_stop_id) assert 6 == len(footpaths_per_from_to_stop_id) assert 130 == footpaths_per_from_to_stop_id[("s1", "s3")].walking_time assert 200 == footpaths_per_from_to_stop_id[("s1", "s4")].walking_time assert 140 == footpaths_per_from_to_stop_id[("s2", "s4")].walking_time
def create_beeline_footpaths(stops_per_id, footpaths_per_from_to_stop_id, beeline_distance, walking_speed): """Creates for every stop new footpaths to the other stops within the beeline distance if they are not already defined. The new footpaths are added to footpaths_per_from_to_stop_id. The walking time of the new footpath is calculated from the beeline distance and the specific walking speed. Args: stops_per_id (dict): stops per stop id. footpaths_per_from_to_stop_id (): footpaths per (from_stop_id, to_stop_id) tuple. beeline_distance (float): the beeline distance in meters. walking_speed (float): walking speed in meters per second. """ nb_footpaths_perimeter = 0 log_start("adding footpaths in beeline perimeter with radius {}m".format(beeline_distance), log) log_start("transforming coordinates", log) stop_list = list(stops_per_id.values()) easting_northing_list = [(s.easting, s.northing) for s in stop_list] x_y_coordinates = [wgs84_to_spherical_mercator(p[0], p[1]) for p in easting_northing_list] log_end() log_start("creating quadtree for fast perimeter search", log) tree = spatial.KDTree(x_y_coordinates) log_end() log_start("perimeter search around every stop", log) for ind, a_stop in enumerate(stop_list): x_y_a_stop = x_y_coordinates[ind] for another_ind in tree.query_ball_point(x_y_a_stop, beeline_distance): x_y_another_stop = x_y_coordinates[another_ind] d = distance(x_y_a_stop, x_y_another_stop) # distance in meters. # note that distances can be very inaccurate (up to factor 2 on a lat of 60°), but this should be ok here. walking_time = d / walking_speed another_stop = stop_list[another_ind] key = (a_stop.id, another_stop.id) if key not in footpaths_per_from_to_stop_id: footpaths_per_from_to_stop_id[key] = Footpath(key[0], key[1], walking_time) nb_footpaths_perimeter += 1 if (key[1], key[0]) not in footpaths_per_from_to_stop_id: footpaths_per_from_to_stop_id[(key[1], key[0])] = Footpath(key[1], key[0], walking_time) nb_footpaths_perimeter += 1 log_end() log_end(additional_message="# footpath within perimeter added: {}. # footpaths total: {}".format( nb_footpaths_perimeter, len(footpaths_per_from_to_stop_id) ))
def test_journey_leg_1(): journey = Journey() journey.prepend_journey_leg( JourneyLeg(Connection("t2", "s7", "s8", 50, 60), Connection("t2", "s12", "s13", 80, 90), None)) journey.prepend_journey_leg( JourneyLeg(Connection("t1", "s1", "s2", 10, 20), Connection("t1", "s5", "s6", 30, 40), Footpath("s6", "s7", 1))) journey.prepend_journey_leg(JourneyLeg(None, None, Footpath("s0", "s1", 2))) assert journey.has_legs() assert journey.is_first_leg_footpath() assert not journey.is_last_leg_footpath() assert "s0" == journey.get_first_stop_id() assert "s13" == journey.get_last_stop_id() assert 3 == journey.get_nb_journey_legs() assert 2 == journey.get_nb_pt_journey_legs()
def test_journey_leg_without_in_out_connection(): footpath = Footpath("s6", "s7", 1) journey_leg = JourneyLeg(None, None, footpath) assert journey_leg.in_connection is None assert journey_leg.out_connection is None assert footpath == journey_leg.footpath assert journey_leg.get_in_stop_id() is None assert journey_leg.get_out_stop_id() is None assert journey_leg.get_dep_time_in_stop_id() is None assert journey_leg.get_arr_time_out_stop_id() is None
def test_journey_prepend_journey_leg_not_stop_consistent_1(): journey = Journey() journey.prepend_journey_leg( JourneyLeg(Connection("t2", "s6", "s8", 50, 60), Connection("t2", "s12", "s13", 80, 90), None)) with pytest.raises(ValueError): journey.prepend_journey_leg( JourneyLeg(Connection("t1", "s1", "s2", 10, 20), Connection("t1", "s5", "s6", 30, 40), Footpath("s6", "s7", 1)))
def test_connectionscan_data_constructor_basic(): stops_per_id = { "1": Stop("1", "c1", "n1", 0.0, 0.0), "2": Stop("2", "c2", "n2", 1.0, 1.0), "2a": Stop("2a", "c2a", "n2a", 1.1, 1.1), "3": Stop("3", "c3", "n3", 3.0, 3.0), } footpaths_per_from_to_stop_id = { ("1", "1"): Footpath("1", "1", 60), ("2", "2"): Footpath("2", "2", 70), ("2a", "2a"): Footpath("2a", "2a", 71), ("3", "3"): Footpath("3", "3", 80), ("2", "2a"): Footpath("2", "2a", 75), ("2a", "2"): Footpath("2a", "2", 75), } con_1_1 = Connection("t1", "1", "2", 60, 70) con_1_2 = Connection("t1", "2", "3", 72, 80) con_2_1 = Connection("t2", "2", "3", 50, 59) con_2_2 = Connection("t2", "3", "1", 60, 72) trips_per_id = { "t1": Trip("t1", [con_1_1, con_1_2]), "t2": Trip("t2", [con_2_1, con_2_2]) } cs_data = ConnectionScanData(stops_per_id, footpaths_per_from_to_stop_id, trips_per_id) assert 4 == len(cs_data.stops_per_id) assert 4 == len(cs_data.stops_per_id) assert 2 == len(cs_data.trips_per_id) assert [con_2_1, con_1_1, con_2_2, con_1_2] == cs_data.sorted_connections
def test_journey_leg(): in_connection = Connection("t1", "s1", "s2", 10, 20) out_connection = Connection("t1", "s5", "s6", 30, 40) footpath = Footpath("s6", "s7", 1) journey_leg = JourneyLeg(in_connection, out_connection, footpath) assert in_connection == journey_leg.in_connection assert out_connection == journey_leg.out_connection assert footpath == journey_leg.footpath assert "t1" == journey_leg.get_trip_id() assert "s1" == journey_leg.get_in_stop_id() assert "s6" == journey_leg.get_out_stop_id() assert "s1" == journey_leg.get_first_stop_id() assert "s7" == journey_leg.get_last_stop_id() assert 10 == journey_leg.get_dep_time_in_stop_id() assert 40 == journey_leg.get_arr_time_out_stop_id()
def create_test_connectionscan_data(): stops_per_id = { s.id: s for s in [ fribourg, bern, zuerich_hb, winterthur, st_gallen, interlaken_ost, basel_sbb, chur, thusis, samedan, st_moritz, bern_duebystrasse, koeniz_zentrum, bern_bahnhof, ostermundigen_bahnhof, samedan_bahnhof, samedan_spital, ] } footpaths_per_from_stop_to_stop_id = {(s.id, s.id): Footpath(s.id, s.id, 2 * 60) for s in stops_per_id.values()} footpaths_per_from_stop_to_stop_id[(zuerich_hb.id, zuerich_hb.id)] = Footpath( zuerich_hb.id, zuerich_hb.id, 7 * 60) footpaths_per_from_stop_to_stop_id[(bern.id, bern.id)] = Footpath( bern.id, bern.id, 5 * 60) footpaths_per_from_stop_to_stop_id[(bern_bahnhof.id, bern.id)] = Footpath( bern_bahnhof.id, bern.id, 5 * 60) footpaths_per_from_stop_to_stop_id[(bern.id, bern_bahnhof.id)] = Footpath( bern.id, bern_bahnhof.id, 5 * 60) footpaths_per_from_stop_to_stop_id[(chur.id, chur.id)] = Footpath( chur.id, chur.id, 4 * 60) footpaths_per_from_stop_to_stop_id[(samedan.id, samedan_bahnhof.id)] = Footpath( samedan.id, samedan_bahnhof.id, 3 * 60) footpaths_per_from_stop_to_stop_id[(samedan_bahnhof.id, samedan.id)] = Footpath( samedan_bahnhof.id, samedan.id, 3 * 60) trips = [] trips += get_forth_and_back_trips( [fribourg, bern, zuerich_hb, winterthur, st_gallen], [22 * 60, 56 * 60, 26 * 60, 35 * 60], [6 * 60, 9 * 60, 3 * 60], hhmmss_to_sec("05:34:00"), 32, 30 * 60) trips += get_forth_and_back_trips([interlaken_ost, bern, basel_sbb], [52 * 60, 55 * 60], [12 * 60], hhmmss_to_sec("05:00:00"), 16, 60 * 60) trips += get_forth_and_back_trips([basel_sbb, zuerich_hb, chur], [53 * 60, 75 * 60], [11 * 60], hhmmss_to_sec("05:33:00"), 16, 60 * 60) trips += get_forth_and_back_trips([chur, thusis, samedan, st_moritz], [30 * 60, 75 * 60, 12 * 60], [2 * 60, 6 * 60], hhmmss_to_sec("05:58:00"), 16, 60 * 60) trips += get_forth_and_back_trips([ koeniz_zentrum, bern_duebystrasse, bern_bahnhof, ostermundigen_bahnhof ], [6 * 60, 7 * 60, 15 * 60], [0, 0], hhmmss_to_sec("05:00:00"), 10 * 16, 6 * 60) trips += get_forth_and_back_trips([samedan_bahnhof, samedan_spital], [7 * 60], [], hhmmss_to_sec("15:00:00"), 1, 24 * 60 * 60) return ConnectionScanData(stops_per_id, footpaths_per_from_stop_to_stop_id, {t.id: t for t in trips})
def test_journey_leg_constructor_not_time_consistent(): in_connection = Connection("t1", "s1", "s2", 10, 20) out_connection = Connection("t1", "s5", "s6", 5, 9) footpath = Footpath("s6", "s7", 1) with pytest.raises(ValueError): JourneyLeg(in_connection, out_connection, footpath)
def test_footpath_constructor(): a_footpath = Footpath("1", "2", 60) assert "1" == a_footpath.from_stop_id assert "2" == a_footpath.to_stop_id assert 60 == a_footpath.walking_time
def test_journey_leg_constructor_not_in_out_connection_consistent_2(): in_connection = None out_connection = Connection("t1", "s5", "s6", 30, 40) footpath = Footpath("s10", "s7", 1) with pytest.raises(ValueError): JourneyLeg(in_connection, out_connection, footpath)
def parse_gtfs( path_to_gtfs_zip, desired_date, add_beeline_footpaths=True, beeline_distance=100.0, walking_speed=2.0 / 3.6, make_footpaths_transitive=False ): """Parses a gtfs-file and returns the corresponding timetable data of a specific date. In many GTFS files the information about the footpaths/transfers is not complete. In these cases it is recommended to define appropriate footpaths within a beeline distance. Args: path_to_gtfs_zip (str): path to the gtfs-file (weblink or path to a zip-file). desired_date (date): date on which the timetable data is read. add_beeline_footpaths (obj:`bool`, optional): specifies whether footpaths should be created depending on the beeline (air distance) and independent of the transfers.txt gtfs-file or not. beeline_distance (obj:`float`, optional): radius in meter of the perimeter (circle) to create the beeline footpaths (only relevant if add_beeline_footpaths is True). walking_speed (obj:`float`, optional): walking speed in meters per second for calculating the walking time of the created beeline footpaths (only relevant if add_beeline_footpaths is True). make_footpaths_transitive (obj:`bool`, optional): True if the footpaths are to be made transitive, else False. Making footpaths transitive can lead to long running times and implausible results. Returns: ConnectionScanData: timetable data of the specific date. """ log_start("parsing gtfs-file for desired date {} ({})".format(desired_date, path_to_gtfs_zip), log) stops_per_id = {} footpaths_per_from_to_stop_id = {} trips_per_id = {} with ZipFile(path_to_gtfs_zip, "r") as zip_file: log_start("parsing stops.txt", log) with zip_file.open("stops.txt", "r") as gtfs_file: # required reader = csv.reader(TextIOWrapper(gtfs_file, ENCODING)) header = next(reader) id_index = header.index("stop_id") # required code_index = get_index_with_default(header, "stop_code") # optional name_index = get_index_with_default(header, "stop_name") # conditionally required lat_index = get_index_with_default(header, "stop_lat") # conditionally required lon_index = get_index_with_default(header, "stop_lon") # conditionally required location_type_index = get_index_with_default(header, "location_type") parent_station_index = get_index_with_default(header, "parent_station") for row in reader: stop_id = row[id_index] is_station = row[location_type_index] == "1" if location_type_index else False parent_station_id = ((row[parent_station_index] if row[parent_station_index] != "" else None) if parent_station_index else None) stops_per_id[stop_id] = Stop( stop_id, row[code_index] if code_index else "", row[name_index] if name_index else "", float(row[lon_index]) if lon_index else 0.0, float(row[lat_index]) if lat_index else 0.0, is_station=is_station, parent_station_id=parent_station_id ) log_end(additional_message="# stops: {}".format(len(stops_per_id))) log_start("parsing transfers.txt", log) if "transfers.txt" in zip_file.namelist(): with zip_file.open("transfers.txt", "r") as gtfs_file: # optional reader = csv.reader(TextIOWrapper(gtfs_file, ENCODING)) header = next(reader) from_stop_id_index = header.index("from_stop_id") # required to_stop_id_index = header.index("to_stop_id") # required transfer_type_index = header.index("transfer_type") # required min_transfer_time_index = get_index_with_default(header, "min_transfer_time") # optional if min_transfer_time_index: nb_footpaths_not_added = 0 for row in reader: if row[transfer_type_index] == "2": from_stop_id = row[from_stop_id_index] to_stop_id = row[to_stop_id_index] if from_stop_id in stops_per_id and to_stop_id in stops_per_id: footpaths_per_from_to_stop_id[(from_stop_id, to_stop_id)] = Footpath( from_stop_id, to_stop_id, int(row[min_transfer_time_index]) ) else: nb_footpaths_not_added += 1 log.debug(("footpath from {} to {} cannot be defined since not both stops are defined " "in stops.txt").format(from_stop_id, to_stop_id)) if nb_footpaths_not_added > 0: log.info(("{} rows from transfers.txt were not added to footpaths since either the " "from_stop_id or to_stop_id is not defined in stops.txt.").format( nb_footpaths_not_added)) else: raise ValueError(("min_transfer_time column in gtfs transfers.txt file is not defined, " "cannot calculate footpaths.")) log_end(additional_message="# footpaths from transfers.txt: {}".format(len(footpaths_per_from_to_stop_id))) log_start("adding footpaths to parent station", log) nb_parent_footpaths = 0 for a_stop in stops_per_id.values(): if a_stop.parent_station_id is not None: key = (a_stop.id, a_stop.parent_station_id) if key not in footpaths_per_from_to_stop_id: footpaths_per_from_to_stop_id[key] = Footpath(key[0], key[1], 0) nb_parent_footpaths += 1 if (key[1], key[0]) not in footpaths_per_from_to_stop_id: footpaths_per_from_to_stop_id[(key[1], key[0])] = Footpath(key[1], key[0], 0) nb_parent_footpaths += 1 log_end(additional_message="# footpath from/to parent_station added: {}. # footpaths total: {}".format( nb_parent_footpaths, len(footpaths_per_from_to_stop_id))) log_start("adding footpaths within stops (if not defined)", log) nb_loops = 0 for stop_id in stops_per_id.keys(): from_to_stop_id = (stop_id, stop_id) if from_to_stop_id not in footpaths_per_from_to_stop_id: footpaths_per_from_to_stop_id[from_to_stop_id] = Footpath(stop_id, stop_id, 0) # best guess!! nb_loops += 1 log_end(additional_message="# footpath loops added: {}, # footpaths total: {}".format(nb_loops, len( footpaths_per_from_to_stop_id))) if add_beeline_footpaths: create_beeline_footpaths(stops_per_id, footpaths_per_from_to_stop_id, beeline_distance, walking_speed) else: log.info("adding beeline footpaths is deactivated") if make_footpaths_transitive: make_transitive(footpaths_per_from_to_stop_id) else: log.info("making footpaths transitive is deactivated") log_start("parsing calendar.txt and calendar_dates.txt", log) service_available_at_date_per_service_id = get_service_available_at_date_per_service_id(zip_file, desired_date) log_end() log_start("parsing trips.txt", log) trip_available_at_date_per_trip_id, route_id_per_trip_id = \ get_trip_available_at_date_per_trip_id(zip_file, service_available_at_date_per_service_id) if len(trip_available_at_date_per_trip_id): msg = "# trips available at {}: {}".format(desired_date, len(trip_available_at_date_per_trip_id)) else: msg = "no trips available at {}. assure that the date is within the timetable period.".format(desired_date) log_end(additional_message=msg) log_start("parsing routes.txt and assigning route_type to trip_id", log) with zip_file.open("routes.txt", "r") as gtfs_file: # required reader = csv.reader(TextIOWrapper(gtfs_file, ENCODING)) header = next(reader) route_id_index = header.index("route_id") # required route_type_index = header.index("route_type") # required route_type_per_route_id = {} for row in reader: route_type_per_route_id[row[route_id_index]] = int(row[route_type_index]) route_type_per_trip_id = {trip_id: route_type_per_route_id[route_id_per_trip_id[trip_id]] for trip_id in route_id_per_trip_id} log_end() log_start("parsing stop_times.txt", log) with zip_file.open("stop_times.txt", "r") as gtfs_file: # required reader = csv.reader(TextIOWrapper(gtfs_file, ENCODING)) header = next(reader) trip_id_index = header.index("trip_id") # required stop_id_index = header.index("stop_id") # required arrival_time_index = get_index_with_default(header, "arrival_time") # conditionally required departure_time_index = get_index_with_default(header, "departure_time") # conditionally required def process_rows_of_trip(rows): if rows: trip_id = rows[0][trip_id_index] if trip_available_at_date_per_trip_id[trip_id]: connections = [] for i in range(len(rows) - 1): from_row = rows[i] to_row = rows[i + 1] con_dep = from_row[departure_time_index] if departure_time_index else None con_arr = to_row[arrival_time_index] if arrival_time_index else None if con_dep and con_arr: connections += [Connection( trip_id, from_row[stop_id_index], to_row[stop_id_index], hhmmss_to_sec(con_dep), hhmmss_to_sec(con_arr))] else: return # we do not want trips with missing times try: trip_type = TripType(route_type_per_trip_id[trip_id]) except ValueError: trip_type = TripType.UNKNOWN trips_per_id[trip_id] = Trip(trip_id, connections, trip_type) last_trip_id = None row_list = [] for row in reader: act_trip_id = row[trip_id_index] if last_trip_id == act_trip_id: row_list += [row] else: process_rows_of_trip(row_list) last_trip_id = act_trip_id row_list = [row] process_rows_of_trip(row_list) log_end(additional_message="# trips: {}".format(len(trips_per_id))) cs_data = ConnectionScanData(stops_per_id, footpaths_per_from_to_stop_id, trips_per_id) log_end(additional_message="{}".format(cs_data)) return cs_data
def test_connectionscan_data_constructor_stops_in_footpath_and_stops_not_consistent(): with pytest.raises(ValueError): ConnectionScanData({"s1": Stop("s1", "", "", 0.0, 0.0)}, {("s1", "s2"): Footpath("s1", "s2", 60)}, {}) log_end(additional_message="test failed successful")