57 self.dates = set() |
57 self.dates = set() |
58 def __repr__(self): |
58 def __repr__(self): |
59 return 'services[%r]' % self.reference |
59 return 'services[%r]' % self.reference |
60 |
60 |
61 class BusStop: |
61 class BusStop: |
62 def __init__(self, reference, name, location): |
62 def __init__(self, reference, name, location, code = None): |
63 self.reference, self.name, self.location = reference, name, location |
63 self.reference, self.name, self.location = reference, name, location |
|
64 self.code = code or reference |
64 self.cluster = None |
65 self.cluster = None |
65 self.pairs = set() # samannimiset lähellä olevat pysäkit |
66 self.pairs = set() # samannimiset lähellä olevat pysäkit |
66 self.involved_trips = set() |
67 self.involved_trips = set() |
67 def __repr__(self): |
68 def __repr__(self): |
68 return 'bus_stops[%r]' % self.reference |
69 return 'bus_stops[%r]' % self.reference |
106 # jos tämä ajovuoro ajetaan tänä päivänä |
107 # jos tämä ajovuoro ajetaan tänä päivänä |
107 if trip.is_served_at(date): |
108 if trip.is_served_at(date): |
108 # ja jos tämä trip pysähtyy tällä pysäkillä, ei kuitenkaan saapuen |
109 # ja jos tämä trip pysähtyy tällä pysäkillä, ei kuitenkaan saapuen |
109 # päätepysäkille, |
110 # päätepysäkille, |
110 stop = trip.contains_stop(self) |
111 stop = trip.contains_stop(self) |
111 if stop and not stop.isArrival: # stop is not trip.schedule[-1]: |
112 if stop and not stop.is_arrival: # stop is not trip.schedule[-1]: |
112 # ja jos tämä halt on tulevaisuudessa, |
113 # ja jos tämä halt on tulevaisuudessa, |
113 stop_time = datetime.combine(date, time()) + stop.arrival_time |
114 stop_time = datetime.combine(date, time()) + stop.arrival_time |
114 if stop_time >= now(): |
115 if stop_time >= now(): |
115 # lisää halt listaan. |
116 # lisää halt listaan. |
116 result.append({ |
117 result.append({ |
126 def __init__(self, arrival_time, departure_time, stop, trip, traveled_distance): |
127 def __init__(self, arrival_time, departure_time, stop, trip, traveled_distance): |
127 self.arrival_time, self.departure_time, self.stop, self.trip = arrival_time, departure_time, \ |
128 self.arrival_time, self.departure_time, self.stop, self.trip = arrival_time, departure_time, \ |
128 stop, trip |
129 stop, trip |
129 self.traveled_distance = traveled_distance |
130 self.traveled_distance = traveled_distance |
130 @property |
131 @property |
131 def isArrival(self): |
132 def is_arrival(self): |
132 if not hasattr(self, 'cachedIsArrival'): |
133 if not hasattr(self, 'cachedIsArrival'): |
133 iterator = iter(self.trip.schedule) |
134 if self.stop.region: |
134 stop = next(iterator) |
135 iterator = iter(self.trip.schedule) |
135 while stop is not self: |
|
136 stop = next(iterator) |
136 stop = next(iterator) |
137 for stop in iterator: |
137 while stop is not self: |
138 if stop.stop.region != self.stop.region: |
138 stop = next(iterator) |
139 self.cachedIsArrival = False |
139 for stop in iterator: |
140 break |
140 if stop.stop.region != self.stop.region: |
|
141 self.cachedIsArrival = False |
|
142 break |
|
143 else: |
|
144 self.cachedIsArrival = True |
141 else: |
145 else: |
142 self.cachedIsArrival = True |
146 self.cachedIsArrival = False |
143 return self.cachedIsArrival |
147 return self.cachedIsArrival |
144 def __repr__(self): |
148 def __repr__(self): |
145 return 'BusHalt(%r, %r, %r, %r)' % (self.arrival_time, self.departure_time, self.stop, self.trip) |
149 return 'BusHalt(%r, %r, %r, %r)' % (self.arrival_time, self.departure_time, self.stop, self.trip) |
146 |
150 |
147 routes = {} |
151 routes = {} |
180 route.trips.add(trip) |
184 route.trips.add(trip) |
181 assert trip.name not in all_trips |
185 assert trip.name not in all_trips |
182 all_trips[trip.name] = trip |
186 all_trips[trip.name] = trip |
183 print('%d ajoa' % len(all_trips), file = stderr) |
187 print('%d ajoa' % len(all_trips), file = stderr) |
184 |
188 |
185 def lue_päiväys(teksti): |
189 def read_date(teksti): |
186 return date(int(teksti[:4]), int(teksti[4:6]), int(teksti[6:])) |
190 return date(int(teksti[:4]), int(teksti[4:6]), int(teksti[6:])) |
187 |
191 |
188 def read_time(teksti): |
192 def read_time(teksti): |
189 tunti, minuutti, sekunti = map(int, teksti.split(':')) |
193 tunti, minuutti, sekunti = map(int, teksti.split(':')) |
190 return timedelta(hours = tunti, minutes = minuutti, seconds = sekunti) |
194 return timedelta(hours = tunti, minutes = minuutti, seconds = sekunti) |
192 print('Ladataan päiväykset... ', file = stderr, flush = True) |
196 print('Ladataan päiväykset... ', file = stderr, flush = True) |
193 |
197 |
194 viimeinen_käyttöpäivä = date.today() |
198 viimeinen_käyttöpäivä = date.today() |
195 services_for_day = {} |
199 services_for_day = {} |
196 |
200 |
197 with open('gtfs/calendar_dates.txt') as file: |
201 def date_range(start_date, end_date, *, include_end = False): |
198 for row in read_csv(file): |
202 ''' Generates date from start_date to end_date. If include_end is True, then end_date will be yielded. ''' |
199 service = services[row['service_id']] |
203 current_date = start_date |
200 day = lue_päiväys(row['date']) |
204 while current_date < end_date: |
|
205 yield current_date |
|
206 current_date += timedelta(1) |
|
207 if include_end: |
|
208 yield end_date |
|
209 |
|
210 def add_day_to_service(service_name, day): |
|
211 try: |
|
212 service = services[service_name] |
|
213 except KeyError: |
|
214 return |
|
215 else: |
201 service.dates.add(day) |
216 service.dates.add(day) |
202 if day not in services_for_day: |
217 if day not in services_for_day: |
203 services_for_day[day] = set() |
218 services_for_day[day] = set() |
204 services_for_day[day].add(service) |
219 services_for_day[day].add(service) |
|
220 global viimeinen_käyttöpäivä |
205 viimeinen_käyttöpäivä = max(day, viimeinen_käyttöpäivä) |
221 viimeinen_käyttöpäivä = max(day, viimeinen_käyttöpäivä) |
|
222 |
|
223 def filter_day(row, day): |
|
224 day_names = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'] |
|
225 return int(row[day_names[day.isoweekday() - 1]]) |
|
226 |
|
227 with open('gtfs/calendar.txt') as file: |
|
228 for row in read_csv(file): |
|
229 for day in date_range(read_date(row['start_date']), read_date(row['end_date']), include_end = True): |
|
230 if filter_day(row, day): |
|
231 add_day_to_service(service_name = row['service_id'], day = day) |
|
232 |
|
233 with open('gtfs/calendar_dates.txt') as file: |
|
234 for row in read_csv(file): |
|
235 add_day_to_service(service_name = row['service_id'], day = read_date(row['date'])) |
206 |
236 |
207 def services_available_at(day): |
237 def services_available_at(day): |
208 for service in services.values(): |
238 for service in services.values(): |
209 if day in service.dates: |
239 if day in service.dates: |
210 yield service |
240 yield service |
211 |
241 |
212 print('Ladataan pysäkit... ', file = stderr, end = '', flush = True) |
242 print('Ladataan pysäkit... ', file = stderr, end = '', flush = True) |
213 with open('gtfs/stops.txt') as file: |
243 with open('gtfs/stops.txt') as file: |
214 for row in read_csv(file): |
244 for row in read_csv(file): |
215 location = Sijainti(float(row['stop_lat']), float(row['stop_lon'])) |
245 location = Sijainti(float(row['stop_lat']), float(row['stop_lon'])) |
216 stop = BusStop(row['stop_id'], row['stop_name'], location) |
246 stop = BusStop( |
|
247 reference = row['stop_id'], |
|
248 name = row['stop_name'], |
|
249 location = location, |
|
250 code = row['stop_code'], |
|
251 ) |
217 bus_stops[stop.reference] = stop |
252 bus_stops[stop.reference] = stop |
218 with open('regions-per-stop.json') as file: |
253 with open('regions-per-stop.json') as file: |
219 for stop_reference, region in json.load(file).items(): |
254 for stop_reference, region in json.load(file).items(): |
220 bus_stops[stop_reference].region = region |
255 bus_stops[stop_reference].region = region |
221 print('%d pysäkkiä' % len(bus_stops), file = stderr) |
256 print('%d pysäkkiä' % len(bus_stops), file = stderr) |
274 if not bus_stop.cluster: |
309 if not bus_stop.cluster: |
275 stops_to_cluster = {bus_stop} |
310 stops_to_cluster = {bus_stop} |
276 # etsi pysäkin samannimiset vastaparit |
311 # etsi pysäkin samannimiset vastaparit |
277 for pair_candidate in bus_stops_by_name[bus_stop.name]: |
312 for pair_candidate in bus_stops_by_name[bus_stop.name]: |
278 distance = pair_candidate.location.etäisyys(bus_stop.location) |
313 distance = pair_candidate.location.etäisyys(bus_stop.location) |
279 if pair_candidate is not bus_stop and distance <= 0.3: |
314 if pair_candidate is not bus_stop and distance <= 0.4: |
280 stops_to_cluster.add(pair_candidate) |
315 stops_to_cluster.add(pair_candidate) |
281 for stop_to_cluster in stops_to_cluster: |
316 for stop_to_cluster in stops_to_cluster: |
282 if stop_to_cluster.cluster: |
317 if stop_to_cluster.cluster: |
283 cluster = stop_to_cluster.cluster |
318 cluster = stop_to_cluster.cluster |
284 break |
319 break |
297 if len(bus_stop.cluster.stops) == 1: |
332 if len(bus_stop.cluster.stops) == 1: |
298 possibilities = set() |
333 possibilities = set() |
299 for cluster in all_clusters: |
334 for cluster in all_clusters: |
300 if cluster is not bus_stop.cluster: |
335 if cluster is not bus_stop.cluster: |
301 distance = cluster.center.etäisyys(bus_stop.location) |
336 distance = cluster.center.etäisyys(bus_stop.location) |
302 if distance <= 0.3: |
337 if distance <= 0.4: |
303 possibilities.add((distance, cluster)) |
338 possibilities.add((distance, cluster)) |
304 if possibilities: |
339 if possibilities: |
305 best = min(possibilities)[1] |
340 best = min(possibilities)[1] |
306 all_clusters.remove(bus_stop.cluster) |
341 all_clusters.remove(bus_stop.cluster) |
307 best.merge(bus_stop.cluster) |
342 best.merge(bus_stop.cluster) |
354 cluster_bus_stops() |
389 cluster_bus_stops() |
355 name_clusters() |
390 name_clusters() |
356 |
391 |
357 clusters_by_name = {} |
392 clusters_by_name = {} |
358 for cluster in all_clusters: |
393 for cluster in all_clusters: |
359 assert cluster.url_name not in clusters_by_name |
394 if cluster.url_name in clusters_by_name: |
360 clusters_by_name[cluster.url_name] = cluster |
395 print('Warning: Clusters %r and %r share the same URL name: %r' % (cluster.name, clusters_by_name[cluster.url_name].name, cluster.url_name)) |
|
396 else: |
|
397 clusters_by_name[cluster.url_name] = cluster |
361 |
398 |
362 print('Ladataan aikataulut... ', end = '', flush = True, file = stderr) |
399 print('Ladataan aikataulut... ', end = '', flush = True, file = stderr) |
363 with open('gtfs/stop_times.txt') as file: |
400 with open('gtfs/stop_times.txt') as file: |
364 row_count = sum(line.count('\n') for line in file) |
401 row_count = sum(line.count('\n') for line in file) |
365 progress = 0 |
402 progress = 0 |