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