|     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 |