busroute.py

changeset 1
f9788970fa46
equal deleted inserted replaced
0:659ab465152e 1:f9788970fa46
1 #!/usr/bin/env python3
2 def via_factor(region_name, *, regions):
3 # how important is it that
4 if region_name in regions:
5 return float(regions[region_name]['via_factor'])
6 else:
7 return 0
8
9 def simplify_name(region_name, *, regions, replace = False):
10 # take short_name to account
11 region = regions.get(region_name)
12 if region:
13 return region.get('short_name', region_name)
14 else:
15 return region_name
16
17 def destinations_list(
18 itinerary,
19 *, trip_length,
20 regions,
21 whole = False,
22 format = 'medium',
23 ):
24 '''
25 Produces a sign of destinations for the given itinerary.
26 `itinerary`: list of region names passed through
27 `trip_length`: length of the itinerary in meters.
28 `regions`: the regions table, used to decide what is important to show.
29 `whole`: whether or not the starting place is also included.
30 `format` controls what kind of sign to produce:
31 - 'short': at most 2 destinations, with reducing
32 - 'medium': at most 3 destinations, with reducing
33 - 'long': at most 4 destinations, no reducing.
34 Returns a list of region names.
35 e.g. ['Turun keskusta', 'Ihala', 'Kauppakeskus Mylly']
36 for Föli bus route 220 at the student village.
37 '''
38 # prefer longer destination signs on longer routes
39 length = ((trip_length / 600) * 3 + len(itinerary) * 2) / 5
40 # collect regions along the itinerary
41 have_already = set()
42 i = 0
43 if not itinerary:
44 # not going anywhere?
45 return ''
46 while i < len(itinerary):
47 region = regions.get(itinerary[i])
48 if not itinerary[i] or itinerary[i] in have_already:
49 del itinerary[i]
50 else:
51 have_already.add(itinerary[i])
52 i += 1
53 from_place = itinerary[0]
54 destination = itinerary[-1]
55 route_weights = {}
56 # create weights for all places along the way. Transforming by x^-0.3
57 # lessens weights for places further out in the itinerary.
58 f = lambda i: i**-0.3
59 # this factor scales the weights so that they become comparable against
60 # constant values
61 factor = 1 / max(f(i + 1) for i in range(len(itinerary)))
62 while via_factor(itinerary[-1], regions = regions) < 0:
63 del itinerary[-1]
64 if not itinerary:
65 return ''
66 destination = itinerary[-1]
67 for i, stop in enumerate(itinerary):
68 # transform index by:
69 # - our gradually decreasing x^-0.3 curve,
70 # - our normalising factor,
71 # - and the via_factor of the stop
72 route_weights[stop] = f(i + 1) * factor * via_factor(stop, regions = regions)
73 # ensure that the starting region does not make it into the destinations
74 # sign by setting its weight to 0
75 if from_place in route_weights:
76 route_weights[from_place] = 0
77 # ensure that the destination does make it to the signpost
78 route_weights[destination] = 1.0e+10
79 # sort destinations by importance
80 weights = sorted(
81 [
82 (stop, route_weights[stop], i)
83 for i, stop in enumerate(itinerary)
84 if route_weights[stop] >= 1
85 ], key = lambda stop: -stop[1])
86 # now consider what do we want to display:
87 if format == 'long':
88 # long format, just take at most four destinations
89 weights = weights[:4]
90 elif format == 'short':
91 # short format, take at most two destinations
92 weights = weights[:2]
93 # possibly drop the via-region as well
94 try:
95 if weights[1][0] != destination and weights[1][1] < (500 / length ** 1.15):
96 del weights[1]
97 except IndexError:
98 pass
99 elif format == 'medium':
100 # regular format, at most three destinations
101 weights = weights[:3]
102 # if the third sign value is not significant enough, drop it
103 try:
104 if weights[2][0] != destination and weights[2][1] < (725 / length ** 0.8):
105 del weights[2]
106 except IndexError:
107 pass
108 # and repeat for the second sign value
109 try:
110 if weights[1][0] != destination and weights[1][1] < (500 / length ** 1.15):
111 del weights[1]
112 except IndexError:
113 pass
114 else:
115 raise ValueError(str.format('Unknown format {format}', format = repr(format)))
116 # restore the signpost back to itinerary order
117 weights.sort(key = lambda weight_data: weight_data[2])
118 # form the sign
119 sign = [paino[0] for paino in weights]
120 old_sign = sign.copy()
121 sign = []
122 for place in old_sign:
123 if place not in sign:
124 sign.append(place)
125 if whole:
126 # whole format, also include the starting point
127 sign = [from_place] + sign
128 if not sign:
129 sign = [destination]
130 return sign

mercurial