Wed, 29 Jul 2020 23:45:53 +0300
begin work on bus compiler
#!/usr/bin/env python3 def via_factor(region_name, *, regions): # how important is it that if region_name in regions: return float(regions[region_name]['via_factor']) else: return 0 def simplify_name(region_name, *, regions, replace = False): # take short_name to account region = regions.get(region_name) if region: return region.get('short_name', region_name) else: return region_name def destinations_list( itinerary, *, trip_length, regions, whole = False, format = 'medium', ): ''' Produces a sign of destinations for the given itinerary. `itinerary`: list of region names passed through `trip_length`: length of the itinerary in meters. `regions`: the regions table, used to decide what is important to show. `whole`: whether or not the starting place is also included. `format` controls what kind of sign to produce: - 'short': at most 2 destinations, with reducing - 'medium': at most 3 destinations, with reducing - 'long': at most 4 destinations, no reducing. Returns a list of region names. e.g. ['Turun keskusta', 'Ihala', 'Kauppakeskus Mylly'] for Föli bus route 220 at the student village. ''' # prefer longer destination signs on longer routes length = ((trip_length / 600) * 3 + len(itinerary) * 2) / 5 # collect regions along the itinerary have_already = set() i = 0 if not itinerary: # not going anywhere? return '' while i < len(itinerary): region = regions.get(itinerary[i]) if not itinerary[i] or itinerary[i] in have_already: del itinerary[i] else: have_already.add(itinerary[i]) i += 1 from_place = itinerary[0] destination = itinerary[-1] route_weights = {} # create weights for all places along the way. Transforming by x^-0.3 # lessens weights for places further out in the itinerary. f = lambda i: i**-0.3 # this factor scales the weights so that they become comparable against # constant values factor = 1 / max(f(i + 1) for i in range(len(itinerary))) while via_factor(itinerary[-1], regions = regions) < 0: del itinerary[-1] if not itinerary: return '' destination = itinerary[-1] for i, stop in enumerate(itinerary): # transform index by: # - our gradually decreasing x^-0.3 curve, # - our normalising factor, # - and the via_factor of the stop route_weights[stop] = f(i + 1) * factor * via_factor(stop, regions = regions) # ensure that the starting region does not make it into the destinations # sign by setting its weight to 0 if from_place in route_weights: route_weights[from_place] = 0 # ensure that the destination does make it to the signpost route_weights[destination] = 1.0e+10 # sort destinations by importance weights = sorted( [ (stop, route_weights[stop], i) for i, stop in enumerate(itinerary) if route_weights[stop] >= 1 ], key = lambda stop: -stop[1]) # now consider what do we want to display: if format == 'long': # long format, just take at most four destinations weights = weights[:4] elif format == 'short': # short format, take at most two destinations weights = weights[:2] # possibly drop the via-region as well try: if weights[1][0] != destination and weights[1][1] < (500 / length ** 1.15): del weights[1] except IndexError: pass elif format == 'medium': # regular format, at most three destinations weights = weights[:3] # if the third sign value is not significant enough, drop it try: if weights[2][0] != destination and weights[2][1] < (725 / length ** 0.8): del weights[2] except IndexError: pass # and repeat for the second sign value try: if weights[1][0] != destination and weights[1][1] < (500 / length ** 1.15): del weights[1] except IndexError: pass else: raise ValueError(str.format('Unknown format {format}', format = repr(format))) # restore the signpost back to itinerary order weights.sort(key = lambda weight_data: weight_data[2]) # form the sign sign = [paino[0] for paino in weights] old_sign = sign.copy() sign = [] for place in old_sign: if place not in sign: sign.append(place) if whole: # whole format, also include the starting point sign = [from_place] + sign if not sign: sign = [destination] return sign