1
+ import csv
2
+ import sys
3
+ from argparse import ArgumentParser
4
+ from pathlib import Path
5
+
6
+ emissions_per_route_type = {
7
+ 3 : {"avg_co2_per_vehicle_per_km" : 85 , "avg_passenger_count" : 1 }, # Bus
8
+ 0 : {"avg_co2_per_vehicle_per_km" : 37.7 , "avg_passenger_count" : 1 }, # Interpretiere Tram, Streetcar, Light rail wie S-Bahn
9
+ 109 : {"avg_co2_per_vehicle_per_km" : 37.7 , "avg_passenger_count" : 1 }, # S-Bahn
10
+ 2 : {"avg_co2_per_vehicle_per_km" : 54 , "avg_passenger_count" : 1 }, # Intepretiere Rails (Long Distance) wie Regionalbahn
11
+ 106 : {"avg_co2_per_vehicle_per_km" : 54 , "avg_passenger_count" : 1 }, # Regionalbahn
12
+ 1 : {"avg_co2_per_vehicle_per_km" : 2.6 , "avg_passenger_count" : 1 }, # interpretiere Subway, Metro wie Stadtbahn
13
+ 403 : {"avg_co2_per_vehicle_per_km" : 2.6 , "avg_passenger_count" : 1 }, # Stadtbahn
14
+ 4 : {"avg_co2_per_vehicle_per_km" : 2600 , "avg_passenger_count" : 1.4 }, # Ferries
15
+ 7 : {"avg_co2_per_vehicle_per_km" : 2.6 , "avg_passenger_count" : 1 }, # Funicular, Übernahme Wert Stadtbahn
16
+ }
17
+
18
+ # Estimating CO2 emissions for funiculars:
19
+ # VVS does not provide numbers neither for Seilbahn ("Außerhalb Tarifgebiet")
20
+ # nor Zahnradbahn ("1.2km Fußweg (while it's 2.4km [1] or 1.6 fly distance)")
21
+ # We assume it is approximately the Stadtbahn.
22
+ # [1] https://herrenberg.stadtnavi.de/reiseplan/Marienplatz%2C%20Stuttgart%3A%3A48.7642519%2C9.1681266/Degerloch%2C%20Stuttgart%3A%3A48.7485636%2C9.1682656/walk?time=1731217647
23
+
24
+ # Estimating CO2 emissions for ferries:
25
+ # CO2 emissions for ferries are barely available. We deduced them from a single datapoint
26
+ # we found: 2.8l per car for the Horgener Zürichsee-Ferry [1]. As the distance between Horgen
27
+ # and Meilen is, according to OSM exactly 2.8km, we assume it's 1l/km. As 1l Diesel
28
+ # corresponds to 2.6kg CO2 [3], that corresponds to 1l/PKWkm*2600g => 2600g/km.
29
+ # Per car we assume a medium occupancy of 1.4
30
+ # [1] https://www.tagesanzeiger.ch/die-faehre-verbraucht-einen-liter-fuer-2-8-kilometer-pro-auto-737265054748
31
+ # [2] https://www.openstreetmap.org/directions?engine=graphhopper_foot&route=47.25919%2C8.60453%3B47.26770%2C8.63839
32
+ # [3] https://www.econologie.de/Emissions-co2-Liter-Kraftstoff-Benzin-oder-Diesel-gpl/
33
+
34
+
35
+
36
+ def eprint (* args , ** kwargs ):
37
+ print (* args , file = sys .stderr , ** kwargs )
38
+
39
+ def map_to_standard_route_type (extended_route_type : int ) -> int :
40
+ if extended_route_type < 15 :
41
+ return extended_route_type
42
+ if extended_route_type >= 400 and extended_route_type < 500 :
43
+ return 1
44
+ if extended_route_type >= 100 and extended_route_type < 200 :
45
+ return 2
46
+ if extended_route_type >= 700 and extended_route_type < 800 :
47
+ return 3
48
+ if extended_route_type >= 1400 and extended_route_type < 1500 :
49
+ return 7
50
+ eprint (f"WARN: no mapping to standard route_type for { extended_route_type } " )
51
+ return extended_route_type
52
+
53
+ def print_mappings (used_route_types : set [int ]):
54
+ eprint ("The following co2 emissions per route type where used:" )
55
+ eprint (f'route_type|avg_co2_per_vehicle_per_km|avg_passenger_count' )
56
+ eprint (f'----------|--------------------------|-------------------' )
57
+
58
+ for route_type in used_route_types :
59
+ mapped_route_type = map_to_standard_route_type (route_type )
60
+ emissions = emissions_per_route_type [mapped_route_type ]
61
+ avg_co2_per_vehicle_per_km = emissions ['avg_co2_per_vehicle_per_km' ]
62
+ avg_passenger_count = emissions ['avg_passenger_count' ]
63
+ eprint (f'{ route_type : 10} |{ avg_co2_per_vehicle_per_km : 26} |{ avg_passenger_count : 19} ' )
64
+
65
+ def generate_emissions_file (routes_file_path : str , emissions_file_path : str ) -> None :
66
+ used_route_types = set ()
67
+ with open (routes_file_path ) as f :
68
+ reader = csv .DictReader (f )
69
+
70
+ emissions = []
71
+ for route in reader :
72
+ route_type = int (route ["route_type" ])
73
+ used_route_types .add (route_type )
74
+ standard_route_type = map_to_standard_route_type (route_type )
75
+ route_id = route ["route_id" ]
76
+ route_type_emissions = emissions_per_route_type .get (route_type , emissions_per_route_type .get (standard_route_type ))
77
+ if not route_type_emissions :
78
+ eprint (f"Error: no emissions defined for route_type { route_type } or route { route_id } " )
79
+ else :
80
+ route_emission = {}
81
+ route_emission ["route_id" ] = route_id
82
+ route_emission ["avg_co2_per_vehicle_per_km" ] = route_type_emissions ['avg_co2_per_vehicle_per_km' ]
83
+ route_emission ["avg_passenger_count" ] = route_type_emissions ['avg_passenger_count' ]
84
+ emissions .append (route_emission )
85
+
86
+ with open (emissions_file_path , 'w' ) as f :
87
+ fieldnames = ['route_id' , 'avg_co2_per_vehicle_per_km' , 'avg_passenger_count' ]
88
+ writer = csv .DictWriter (f , fieldnames = fieldnames )
89
+
90
+ writer .writeheader ()
91
+ for route_emisions in emissions :
92
+ writer .writerow (route_emisions )
93
+
94
+ print_mappings (used_route_types )
95
+
96
+ if __name__ == "__main__" :
97
+ parser = ArgumentParser (prog = 'generate_emissions_file' )
98
+ parser .add_argument ('gtfs_dir' , help = 'Path to GTFS directory' )
99
+ args = parser .parse_args ()
100
+ generate_emissions_file (Path (args .gtfs_dir , 'routes.txt' ), Path (args .gtfs_dir , 'emissions.txt' ))
101
+
102
+
0 commit comments