buses.py

changeset 24
e6bdb9c54096
parent 23
3a495bc4b7b5
child 26
69cc75506d4f
equal deleted inserted replaced
23:3a495bc4b7b5 24:e6bdb9c54096
3 from sys import stderr 3 from sys import stderr
4 from datetime import date, time, datetime, timedelta 4 from datetime import date, time, datetime, timedelta
5 from copy import copy 5 from copy import copy
6 from misc import * 6 from misc import *
7 from geometry import * 7 from geometry import *
8 Suunta = enum.Enum('Suunta', [('Taaksepäin', 0), ('Eteenpäin', 1)]) 8
9 9 def transform_trip_reference(reference):
10 def muunna_ajovuoro_tunniste(tunniste): 10 return reference
11 return tunniste 11
12 12 class BusTrip:
13 class Ajovuoro: 13 def __init__(self, reference, route, service, length):
14 def __init__(self, tunniste, linja, palvelu, kyltti, suunta, length): 14 self.reference, self.route, self.service = reference, route, service
15 self.tunniste, self.linja, self.palvelu, self.kyltti, self.suunta = tunniste, linja, \
16 palvelu, kyltti, suunta
17 self.length = length 15 self.length = length
18 self.reitti = [] 16 self.schedule = []
19 self.nimi = muunna_ajovuoro_tunniste(tunniste) 17 self.name = transform_trip_reference(reference)
20 def __repr__(self): 18 def __repr__(self):
21 return 'ajot[%r]' % self.nimi 19 return 'all_trips[%r]' % self.name
22 def pysäkkiReitillä(self, pysäkki): 20 def contains_stop(self, stop):
23 for pysähdys in self.reitti: 21 for halt in self.schedule:
24 if pysähdys.pysäkki is pysäkki: 22 if halt.stop is stop:
25 return pysähdys 23 return halt
26 else: 24 else:
27 return None 25 return None
28 def ajetaan_päivänä(self, päivä): 26 def is_served_at(self, day):
29 try: 27 try:
30 return self.palvelu in palvelut_per_päivä[päivä] 28 return self.service in services_for_day[day]
31 except KeyError: 29 except KeyError:
32 return False 30 return False
33 def suppea_reitti(self, pysäkistä = None): 31 def concise_schedule(self, starting_stop = None):
34 if pysäkistä and pysäkistä in self.reitti: 32 if starting_stop and starting_stop in self.schedule:
35 reitti = copy(self.reitti) 33 schedule = copy(self.schedule)
36 reitti = reitti[reitti.index(pysäkistä):] 34 schedule = schedule[schedule.index(starting_stop):]
37 else: 35 else:
38 reitti = self.reitti 36 schedule = self.schedule
39 käytetyt_alueet = set() 37 used_areas = set()
40 tulos = [] 38 result = []
41 for pysähdys in reitti: 39 for halt in schedule:
42 pysäkki = pysähdys.pysäkki 40 stop = halt.stop
43 if pysäkki.alue and pysäkki.alue not in käytetyt_alueet: 41 if stop.region and stop.region not in used_areas:
44 käytetyt_alueet.add(pysäkki.alue) 42 used_areas.add(stop.region)
45 tulos.append(pysäkki.alue) 43 result.append(stop.region)
46 return tulos 44 return result
47 45
48 class Linja: 46 class BusRoute:
49 def __init__(self, tietue): 47 def __init__(self, entry):
50 self.tunniste = tietue['route_id'] 48 self.id = entry['route_id']
51 self.viite = tietue['route_short_name'] 49 self.reference = entry['route_short_name']
52 self.selite = tietue['route_long_name'] 50 def __repr__(self):
53 def __repr__(self): 51 return 'routes[%r]' % self.reference
54 return 'linjat[%r]' % self.viite 52
55 53 class BusService:
56 class Palvelu: 54 def __init__(self, reference):
57 def __init__(self, tunniste): 55 self.reference = reference
58 self.tunniste = tunniste 56 self.dates = set()
59 self.päivät = set() 57 def __repr__(self):
60 def __repr__(self): 58 return 'services[%r]' % self.reference
61 return 'palvelut[%r]' % self.tunniste 59
62 60 class BusStop:
63 class Pysäkki: 61 def __init__(self, reference, name, location):
64 def __init__(self, tunniste, nimi, sijainti): 62 self.reference, self.name, self.location = reference, name, location
65 self.tunniste, self.nimi, self.sijainti = tunniste, nimi, sijainti
66 self.cluster = None 63 self.cluster = None
67 self.pairs = set() # samannimiset lähellä olevat pysäkit 64 self.pairs = set() # samannimiset lähellä olevat pysäkit
68 def __repr__(self): 65 def __repr__(self):
69 return 'pysäkit[%r]' % self.tunniste 66 return 'bus_stops[%r]' % self.reference
70 def schedule(self, max_amount = 50): 67 def schedule(self, max_amount = 50):
71 ''' 68 '''
72 Hakee tämän pysäkin seuraavat `määrä` lähtöä. Päätepysäkille saapuvia busseja ei 69 Hakee tämän pysäkin seuraavat `määrä` lähtöä. Päätepysäkille saapuvia busseja ei
73 lasketa. Palauttaa pysähdykset listana jossa alkiot ovat muotoa (aika, pysähdys), 70 lasketa. Palauttaa pysähdykset listana jossa alkiot ovat muotoa (aika, halt),
74 jossa: 71 jossa:
75 - `aika` on saapumishetki muotoa datetime ja 72 - `aika` on saapumishetki muotoa datetime ja
76 - `pysähdys` on vastaava Pysähdys olio. 73 - `halt` on vastaava BusHalt olio.
77 74
78 Mikäli pysäkille ei ole määrätty riittävästi pysähdyksiä kalenterissa, tuloslista 75 Mikäli pysäkille ei ole määrätty riittävästi pysähdyksiä kalenterissa, tuloslista
79 jää alimittaiseksi, mahdollisesti jopa tyhjäksi. 76 jää alimittaiseksi, mahdollisesti jopa tyhjäksi.
80 ''' 77 '''
81 result = [] 78 result = []
82 # -1 päivää yövuoroja varten 79 # -1 päivää yövuoroja varten
83 date = tänään() - timedelta(days = 1) 80 date = today() - timedelta(days = 1)
84 # Niin kauan kuin aikatauluja ei ole vielä tarpeeksi, 81 # Niin kauan kuin aikatauluja ei ole vielä tarpeeksi,
85 while len(result) < max_amount: 82 while len(result) < max_amount:
86 try: 83 try:
87 # hae nykyisen päivän aikataulut ja lisää ne, 84 # hae nykyisen päivän aikataulut ja lisää ne,
88 result += self.schedule_for_day(date) 85 result += self.schedule_for_day(date)
96 def schedule_for_day(self, date): 93 def schedule_for_day(self, date):
97 ''' 94 '''
98 Hakee pysäkin aikataulut tiettynä päivänä. 95 Hakee pysäkin aikataulut tiettynä päivänä.
99 ''' 96 '''
100 # Jos päädyttiin aikataulukalenterin ulkopuolelle, niin tuotetaan virhe. Jos vain 97 # Jos päädyttiin aikataulukalenterin ulkopuolelle, niin tuotetaan virhe. Jos vain
101 # palautettaisiin tyhjä tulos, niin algoritmi jatkaisi etsintää loputtomiin. 98 # palautettaisiin tyhjä result, niin algoritmi jatkaisi etsintää loputtomiin.
102 if date > viimeinen_käyttöpäivä: 99 if date > viimeinen_käyttöpäivä:
103 raise ValueError('tried to retrieve schedule for date %s which is outside schedule data' % date) 100 raise ValueError('tried to retrieve schedule for date %s which is outside schedule data' % date)
104 result = [] 101 result = []
105 # Jokaiselle ajovuorolle, 102 # Jokaiselle ajovuorolle,
106 for trip in ajot.values(): 103 for trip in all_trips.values():
107 # jos tämä ajovuoro ajetaan tänä päivänä 104 # jos tämä ajovuoro ajetaan tänä päivänä
108 if trip.ajetaan_päivänä(date): 105 if trip.is_served_at(date):
109 # ja jos tämä ajo pysähtyy tällä pysäkillä, ei kuitenkaan saapuen 106 # ja jos tämä trip pysähtyy tällä pysäkillä, ei kuitenkaan saapuen
110 # päätepysäkille, 107 # päätepysäkille,
111 stop = trip.pysäkkiReitillä(self) 108 stop = trip.contains_stop(self)
112 if stop and not stop.isArrival: # stop is not trip.reitti[-1]: 109 if stop and not stop.isArrival: # stop is not trip.schedule[-1]:
113 # ja jos tämä pysähdys on tulevaisuudessa, 110 # ja jos tämä halt on tulevaisuudessa,
114 stop_time = datetime.combine(date, time()) + stop.saapumisaika 111 stop_time = datetime.combine(date, time()) + stop.arrival_time
115 if stop_time >= nyt(): 112 if stop_time >= now():
116 # lisää pysähdys listaan. 113 # lisää halt listaan.
117 result.append({ 114 result.append({
118 'time': stop_time, 115 'time': stop_time,
119 'trip': trip, 116 'trip': trip,
120 'stop': stop, 117 'stop': stop,
121 }) 118 })
122 # Lajittele lopputulos saapumisajan mukaan. 119 # Lajittele lopputulos saapumisajan mukaan.
123 result.sort(key = lambda schedule_entry: schedule_entry['time']) 120 result.sort(key = lambda schedule_entry: schedule_entry['time'])
124 return result 121 return result
125 122
126 class Pysähdys: 123 class BusHalt:
127 def __init__(self, saapumisaika, lähtöaika, pysäkki, ajo, ajettu_matka): 124 def __init__(self, arrival_time, departure_time, stop, trip, traveled_distance):
128 self.saapumisaika, self.lähtöaika, self.pysäkki, self.ajo = saapumisaika, lähtöaika, \ 125 self.arrival_time, self.departure_time, self.stop, self.trip = arrival_time, departure_time, \
129 pysäkki, ajo 126 stop, trip
130 self.ajettu_matka = ajettu_matka 127 self.traveled_distance = traveled_distance
131 self._isArrival = None
132 @property 128 @property
133 def isArrival(self): 129 def isArrival(self):
134 if self._isArrival is None: 130 if not hasattr(self, 'cachedIsArrival'):
135 iterator = iter(self.ajo.reitti) 131 iterator = iter(self.trip.schedule)
136 stop = next(iterator) 132 stop = next(iterator)
137 while stop is not self: 133 while stop is not self:
138 stop = next(iterator) 134 stop = next(iterator)
139 for stop in iterator: 135 for stop in iterator:
140 if stop.pysäkki.alue != self.pysäkki.alue: 136 if stop.stop.region != self.stop.region:
141 self._isArrival = False 137 self.cachedIsArrival = False
142 break 138 break
143 else: 139 else:
144 self._isArrival = True 140 self.cachedIsArrival = True
145 return self._isArrival 141 return self.cachedIsArrival
146 def __repr__(self): 142 def __repr__(self):
147 return 'Pysähdys(%r, %r, %r, %r)' % (self.saapumisaika, self.lähtöaika, self.pysäkki, self.ajo) 143 return 'BusHalt(%r, %r, %r, %r)' % (self.arrival_time, self.departure_time, self.stop, self.trip)
148 144
149 linjat = {} 145 routes = {}
150 linjat_per_tunniste = {} 146 routes_per_id = {}
151 ajot = {} 147 all_trips = {}
152 ajot_per_numero = {} 148 services = {}
153 palvelut = {} 149 bus_stops = {}
154 pysäkit = {}
155 all_clusters = set() 150 all_clusters = set()
156 151
157 print('Ladataan linjat... ', file = stderr, end = '', flush = True) 152 print('Ladataan routes... ', file = stderr, end = '', flush = True)
158 with open('gtfs/routes.txt') as tiedosto: 153 with open('gtfs/routes.txt') as file:
159 for rivi in lue_csv(tiedosto): 154 for row in read_csv(file):
160 linja = Linja(rivi) 155 route = BusRoute(row)
161 linja.tunniste = linja.tunniste 156 routes[route.reference] = route
162 linjat[linja.viite] = linja 157 routes_per_id[route.id] = route
163 linjat_per_tunniste[linja.tunniste] = linja 158 print('%d linjaa' % len(routes), file = stderr)
164 print('%d linjaa' % len(linjat), file = stderr) 159
165 160 print('Ladataan ajovuorot... ', file = stderr, end = '', flush = True)
166 print('Ladataan ajot... ', file = stderr, end = '', flush = True)
167 161
168 shape_distances = {} 162 shape_distances = {}
169 with open('gtfs/shapes.txt') as file: 163 with open('gtfs/shapes.txt') as file:
170 for row in lue_csv(file): 164 for row in read_csv(file):
171 shape_distances[row['shape_id']] = max(shape_distances.get(row['shape_id'], 0), float(row['shape_dist_traveled'])) 165 shape_distances[row['shape_id']] = max(shape_distances.get(row['shape_id'], 0), float(row['shape_dist_traveled']))
172 166
173 with open('gtfs/trips.txt') as tiedosto: 167 with open('gtfs/trips.txt') as file:
174 for rivi in lue_csv(tiedosto, muunnokset = {'direction_id': lambda k: Suunta(int(k))}): 168 for row in read_csv(file):
175 if rivi['service_id'] not in palvelut: 169 if row['service_id'] not in services:
176 palvelut[rivi['service_id']] = Palvelu(rivi['service_id']) 170 services[row['service_id']] = BusService(row['service_id'])
177 linja = linjat_per_tunniste[rivi['route_id']] 171 route = routes_per_id[row['route_id']]
178 ajo = Ajovuoro( 172 trip = BusTrip(
179 tunniste = rivi['trip_id'], 173 reference = row['trip_id'],
180 linja = linja, 174 route = route,
181 palvelu = palvelut[rivi['service_id']], 175 service = services[row['service_id']],
182 kyltti = rivi['trip_headsign'], 176 length = shape_distances[row['shape_id']]
183 suunta = rivi['direction_id'],
184 length = shape_distances[rivi['shape_id']]
185 ) 177 )
186 assert ajo.nimi not in ajot 178 assert trip.name not in all_trips
187 ajot[ajo.nimi] = ajo 179 all_trips[trip.name] = trip
188 print('%d ajoa' % len(ajot), file = stderr) 180 print('%d ajoa' % len(all_trips), file = stderr)
189 181
190 def lue_päiväys(teksti): 182 def lue_päiväys(teksti):
191 return date(int(teksti[:4]), int(teksti[4:6]), int(teksti[6:])) 183 return date(int(teksti[:4]), int(teksti[4:6]), int(teksti[6:]))
192 184
193 def lue_aika(teksti): 185 def read_time(teksti):
194 tunti, minuutti, sekunti = map(int, teksti.split(':')) 186 tunti, minuutti, sekunti = map(int, teksti.split(':'))
195 return timedelta(hours = tunti, minutes = minuutti, seconds = sekunti) 187 return timedelta(hours = tunti, minutes = minuutti, seconds = sekunti)
196 188
197 print('Ladataan päiväykset... ', file = stderr, flush = True) 189 print('Ladataan päiväykset... ', file = stderr, flush = True)
198 190
199 viimeinen_käyttöpäivä = date.today() 191 viimeinen_käyttöpäivä = date.today()
200 palvelut_per_päivä = {} 192 services_for_day = {}
201 193
202 with open('gtfs/calendar_dates.txt') as tiedosto: 194 with open('gtfs/calendar_dates.txt') as file:
203 for rivi in lue_csv(tiedosto): 195 for row in read_csv(file):
204 palvelu = palvelut[rivi['service_id']] 196 service = services[row['service_id']]
205 päivä = lue_päiväys(rivi['date']) 197 day = lue_päiväys(row['date'])
206 palvelu.päivät.add(päivä) 198 service.dates.add(day)
207 if päivä not in palvelut_per_päivä: 199 if day not in services_for_day:
208 palvelut_per_päivä[päivä] = set() 200 services_for_day[day] = set()
209 palvelut_per_päivä[päivä].add(palvelu) 201 services_for_day[day].add(service)
210 viimeinen_käyttöpäivä = max(päivä, viimeinen_käyttöpäivä) 202 viimeinen_käyttöpäivä = max(day, viimeinen_käyttöpäivä)
211 203
212 def palvelut_käytössä(päivä): 204 def services_available_at(day):
213 for palvelu in palvelut.values(): 205 for service in services.values():
214 if päivä in palvelu.päivät: 206 if day in service.dates:
215 yield palvelu 207 yield service
216 208
217 print('Ladataan pysäkit... ', file = stderr, end = '', flush = True) 209 print('Ladataan pysäkit... ', file = stderr, end = '', flush = True)
218 with open('gtfs/stops.txt') as file: 210 with open('gtfs/stops.txt') as file:
219 for rivi in lue_csv(file): 211 for row in read_csv(file):
220 sijainti = Sijainti(float(rivi['stop_lat']), float(rivi['stop_lon'])) 212 location = Sijainti(float(row['stop_lat']), float(row['stop_lon']))
221 pysäkki = Pysäkki(rivi['stop_id'], rivi['stop_name'], sijainti) 213 stop = BusStop(row['stop_id'], row['stop_name'], location)
222 pysäkit[pysäkki.tunniste] = pysäkki 214 bus_stops[stop.reference] = stop
223 with open('regions-per-stop.json') as file: 215 with open('regions-per-stop.json') as file:
224 for pysäkkitunniste, alue in json.load(file).items(): 216 for stop_reference, region in json.load(file).items():
225 pysäkit[pysäkkitunniste].alue = alue 217 bus_stops[stop_reference].region = region
226 print('%d pysäkkiä' % len(pysäkit), file = stderr) 218 print('%d pysäkkiä' % len(bus_stops), file = stderr)
227 219
228 220
229 class BusStopCluster: 221 class BusStopCluster:
230 def __init__(self): 222 def __init__(self):
231 self.stops = set() 223 self.stops = set()
242 @property 234 @property
243 def center(self): 235 def center(self):
244 if not self._center: 236 if not self._center:
245 if self.stops: 237 if self.stops:
246 from statistics import median 238 from statistics import median
247 pointtype = type(next(iter(self.stops)).sijainti) 239 pointtype = type(next(iter(self.stops)).location)
248 self._center = pointtype( 240 self._center = pointtype(
249 median(stop.sijainti.x for stop in self.stops), 241 median(stop.location.x for stop in self.stops),
250 median(stop.sijainti.y for stop in self.stops), 242 median(stop.location.y for stop in self.stops),
251 ) 243 )
252 else: 244 else:
253 raise ValueError('an empty cluster has no center point') 245 raise ValueError('an empty cluster has no center point')
254 return self._center 246 return self._center
255 def merge(self, other): 247 def merge(self, other):
265 result.sort(key = lambda schedule_entry: schedule_entry['time']) 257 result.sort(key = lambda schedule_entry: schedule_entry['time'])
266 return result[:max_amount] 258 return result[:max_amount]
267 259
268 from collections import defaultdict 260 from collections import defaultdict
269 bus_stops_by_name = defaultdict(set) 261 bus_stops_by_name = defaultdict(set)
270 for bus_stop in pysäkit.values(): 262 for bus_stop in bus_stops.values():
271 bus_stops_by_name[bus_stop.nimi].add(bus_stop) 263 bus_stops_by_name[bus_stop.name].add(bus_stop)
272 bus_stops_by_name = dict(bus_stops_by_name) 264 bus_stops_by_name = dict(bus_stops_by_name)
273 265
274 # ryhmittele pysäkit nimen mukaan 266 # ryhmittele bus_stops nimen mukaan
275 all_clusters = [] 267 all_clusters = []
276 def cluster_bus_stops(): 268 def cluster_bus_stops():
277 sorted_bus_stops = sorted(pysäkit.values(), key = lambda bus_stop: bus_stop.nimi) 269 sorted_bus_stops = sorted(bus_stops.values(), key = lambda bus_stop: bus_stop.name)
278 for bus_stop in sorted_bus_stops: 270 for bus_stop in sorted_bus_stops:
279 if not bus_stop.cluster: 271 if not bus_stop.cluster:
280 stops_to_cluster = {bus_stop} 272 stops_to_cluster = {bus_stop}
281 # etsi pysäkin samannimiset vastaparit 273 # etsi pysäkin samannimiset vastaparit
282 for pair_candidate in bus_stops_by_name[bus_stop.nimi]: 274 for pair_candidate in bus_stops_by_name[bus_stop.name]:
283 distance = pair_candidate.sijainti.etäisyys(bus_stop.sijainti) 275 distance = pair_candidate.location.etäisyys(bus_stop.location)
284 if pair_candidate is not bus_stop and distance <= 0.3: 276 if pair_candidate is not bus_stop and distance <= 0.3:
285 stops_to_cluster.add(pair_candidate) 277 stops_to_cluster.add(pair_candidate)
286 for stop_to_cluster in stops_to_cluster: 278 for stop_to_cluster in stops_to_cluster:
287 if stop_to_cluster.cluster: 279 if stop_to_cluster.cluster:
288 cluster = stop_to_cluster.cluster 280 cluster = stop_to_cluster.cluster
292 all_clusters.append(cluster) 284 all_clusters.append(cluster)
293 for stop_to_cluster in stops_to_cluster: 285 for stop_to_cluster in stops_to_cluster:
294 if not stop_to_cluster.cluster: 286 if not stop_to_cluster.cluster:
295 cluster.add_stop(stop_to_cluster) 287 cluster.add_stop(stop_to_cluster)
296 # Merkitse muistiin pysäkkien vastaparit käyttäen hyväksi tämänhetkistä ryhmittelytietoa 288 # Merkitse muistiin pysäkkien vastaparit käyttäen hyväksi tämänhetkistä ryhmittelytietoa
297 for bus_stop in pysäkit.values(): 289 for bus_stop in bus_stops.values():
298 if bus_stop.cluster: 290 if bus_stop.cluster:
299 bus_stop.pairs = bus_stop.cluster.stops - {bus_stop} 291 bus_stop.pairs = bus_stop.cluster.stops - {bus_stop}
300 # Ryhmitä ne pysäkit, joilla ei ollut omaa vastaparia, muiden pysäkkien kanssa 292 # Ryhmitä ne bus_stops, joilla ei ollut omaa vastaparia, muiden pysäkkien kanssa
301 for bus_stop in sorted_bus_stops: 293 for bus_stop in sorted_bus_stops:
302 if len(bus_stop.cluster.stops) == 1: 294 if len(bus_stop.cluster.stops) == 1:
303 possibilities = set() 295 possibilities = set()
304 for cluster in all_clusters: 296 for cluster in all_clusters:
305 if cluster is not bus_stop.cluster: 297 if cluster is not bus_stop.cluster:
306 distance = cluster.center.etäisyys(bus_stop.sijainti) 298 distance = cluster.center.etäisyys(bus_stop.location)
307 if distance <= 0.3: 299 if distance <= 0.3:
308 possibilities.add((distance, cluster)) 300 possibilities.add((distance, cluster))
309 if possibilities: 301 if possibilities:
310 best = min(possibilities)[1] 302 best = min(possibilities)[1]
311 all_clusters.remove(bus_stop.cluster) 303 all_clusters.remove(bus_stop.cluster)
321 def name_clusters(): 313 def name_clusters():
322 from collections import defaultdict 314 from collections import defaultdict
323 from pprint import pprint 315 from pprint import pprint
324 clusters_per_name = defaultdict(set) 316 clusters_per_name = defaultdict(set)
325 for cluster in all_clusters: 317 for cluster in all_clusters:
326 name_representing_stop = min((len(pysäkki.tunniste), pysäkki.tunniste, pysäkki) for pysäkki in cluster.stops)[2] 318 name_representing_stop = min((len(stop.reference), stop.reference, stop) for stop in cluster.stops)[2]
327 clusters_per_name[name_representing_stop.nimi].add(cluster) 319 clusters_per_name[name_representing_stop.name].add(cluster)
328 for name, clusters in clusters_per_name.items(): 320 for name, clusters in clusters_per_name.items():
329 if len(clusters) == 1: 321 if len(clusters) == 1:
330 # Ryhmä on ainoa jolla on varaus tälle nimelle. Sen kuin vaan. 322 # Ryhmä on ainoa jolla on varaus tälle nimelle. Sen kuin vaan.
331 next(iter(clusters)).name = name 323 next(iter(clusters)).name = name
332 else: 324 else:
333 # Olisiko kaikki klusterit eri alueilla? 325 # Olisiko kaikki klusterit eri alueilla?
334 common_regions = shared_elements_in_n_sets({stop.alue for stop in cluster.stops} for cluster in clusters) 326 common_regions = shared_elements_in_n_sets({stop.region for stop in cluster.stops} for cluster in clusters)
335 # Esitys: ryhmä -> ne alueet jotka ovat tälle ryhmälle ainutlaatuisia 327 # Esitys: ryhmä -> ne alueet jotka ovat tälle ryhmälle ainutlaatuisia
336 proposal = { 328 proposal = {
337 cluster: {stop.alue for stop in cluster.stops} - common_regions - {None} 329 cluster: {stop.region for stop in cluster.stops} - common_regions - {None}
338 for cluster in clusters 330 for cluster in clusters
339 } 331 }
340 # Jos enintään yksi klusteri tässä esityksessä on kokonaan ilman omaa aluetta, jolla se voisi eritellä, 332 # Jos enintään yksi klusteri tässä esityksessä on kokonaan ilman omaa aluetta, jolla se voisi eritellä,
341 # niin nimetään klusterit näiden alueiden mukaan. 333 # niin nimetään klusterit näiden alueiden mukaan.
342 # Se klusteri jolla ei ole omaa aluetta (jos on) jätetään ilman aluepäätettä. 334 # Se klusteri jolla ei ole omaa aluetta (jos on) jätetään ilman aluepäätettä.
347 individual_cluster_name += ' (' + min(unique_areas) + ')' 339 individual_cluster_name += ' (' + min(unique_areas) + ')'
348 cluster.name = individual_cluster_name 340 cluster.name = individual_cluster_name
349 else: 341 else:
350 # Typerä reunatapaus. Indeksoidaan numeroin... 342 # Typerä reunatapaus. Indeksoidaan numeroin...
351 for n, (_, cluster) in enumerate(sorted( 343 for n, (_, cluster) in enumerate(sorted(
352 min((stop.tunniste.lower(), cluster) for stop in cluster.stops) 344 min((stop.reference.lower(), cluster) for stop in cluster.stops)
353 for cluster in clusters 345 for cluster in clusters
354 ), 1): 346 ), 1):
355 individual_cluster_name = name + '-' + str(n) 347 individual_cluster_name = name + '-' + str(n)
356 cluster.name = individual_cluster_name 348 cluster.name = individual_cluster_name
357 349
365 clusters_by_name[cluster.url_name] = cluster 357 clusters_by_name[cluster.url_name] = cluster
366 358
367 print('Ladataan aikataulut... ', end = '', flush = True, file = stderr) 359 print('Ladataan aikataulut... ', end = '', flush = True, file = stderr)
368 with open('gtfs/stop_times.txt') as file: 360 with open('gtfs/stop_times.txt') as file:
369 rivimäärä = sum(line.count('\n') for line in file) 361 rivimäärä = sum(line.count('\n') for line in file)
370 laskettu = 0 362 progress = 0
371 file.seek(0) 363 file.seek(0)
372 for rivi in lue_csv(file): 364 for row in read_csv(file):
373 ajo = ajot[muunna_ajovuoro_tunniste(rivi['trip_id'])] 365 trip = all_trips[transform_trip_reference(row['trip_id'])]
374 saapumisaika = lue_aika(rivi['arrival_time']) 366 arrival_time = read_time(row['arrival_time'])
375 lähtöaika = lue_aika(rivi['departure_time']) 367 departure_time = read_time(row['departure_time'])
376 pysäkki = pysäkit[rivi['stop_id']] 368 stop = bus_stops[row['stop_id']]
377 ajettu_matka = float(rivi['shape_dist_traveled']) 369 traveled_distance = float(row['shape_dist_traveled'])
378 ajo.reitti.append(Pysähdys(saapumisaika, lähtöaika, pysäkki, ajo, ajettu_matka)) 370 trip.schedule.append(BusHalt(arrival_time, departure_time, stop, trip, traveled_distance))
379 laskettu += 1 371 progress += 1
380 if laskettu % 1000 == 0: 372 if progress % 1000 == 0:
381 print('\rLadataan aikataulut... %.1f%%' % (laskettu * 100 / rivimäärä), end = ' ', file = stderr) 373 print('\rLadataan aikataulut... %.1f%%' % (progress * 100 / rivimäärä), end = ' ', file = stderr)
382 print('\rLadataan aikataulut... ladattu', file = stderr) 374 print('\rLadataan aikataulut... ladattu', file = stderr)

mercurial