diff -r 88cfb916c852 -r f3791dccfd03 bussit.py --- a/bussit.py Sat Jun 10 16:36:36 2017 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,211 +0,0 @@ -#!/usr/bin/env python3 -import enum, json -from sys import stderr -from datetime import date, time, datetime, timedelta -from copy import copy -from misc import * -from geometria import * -Suunta = enum.Enum('Suunta', [('Taaksepäin', 0), ('Eteenpäin', 1)]) - -def muunna_ajovuoro_tunniste(tunniste): - return tunniste - -class Ajovuoro: - def __init__(self, tunniste, linja, palvelu, kyltti, suunta): - self.tunniste, self.linja, self.palvelu, self.kyltti, self.suunta = tunniste, linja, \ - palvelu, kyltti, suunta - self.reitti = [] - self.nimi = muunna_ajovuoro_tunniste(tunniste) - def __repr__(self): - return 'ajot[%r]' % self.nimi - def pysäkkiReitillä(self, pysäkki): - for pysähdys in self.reitti: - if pysähdys.pysäkki is pysäkki: - return pysähdys - else: - return None - def ajetaan_päivänä(self, päivä): - try: - return self.palvelu in palvelut_per_päivä[päivä] - except KeyError: - return False - def suppea_reitti(self, pysäkistä = None): - if pysäkistä and pysäkistä in self.reitti: - reitti = copy(self.reitti) - reitti = reitti[reitti.index(pysäkistä):] - else: - reitti = self.reitti - käytetyt_alueet = set() - tulos = [] - for pysähdys in reitti: - pysäkki = pysähdys.pysäkki - if pysäkki.alue and pysäkki.alue not in käytetyt_alueet: - käytetyt_alueet.add(pysäkki.alue) - tulos.append(pysäkki.alue) - return tulos - -class Linja: - def __init__(self, tietue): - self.tunniste = tietue['route_id'] - self.viite = tietue['route_short_name'] - self.selite = tietue['route_long_name'] - def __repr__(self): - return 'linjat[%r]' % self.viite - -class Palvelu: - def __init__(self, tunniste): - self.tunniste = tunniste - self.päivät = set() - def __repr__(self): - return 'palvelut[%r]' % self.tunniste - -class Pysäkki: - def __init__(self, tunniste, nimi, sijainti): - self.tunniste, self.nimi, self.sijainti = tunniste, nimi, sijainti - def __repr__(self): - return 'pysäkit[%r]' % self.tunniste - def aikataulu(self, määrä = 50): - ''' - Hakee tämän pysäkin seuraavat `määrä` lähtöä. Päätepysäkille saapuvia busseja ei - lasketa. Palauttaa pysähdykset listana jossa alkiot ovat muotoa (aika, pysähdys), - jossa: - - `aika` on saapumishetki muotoa datetime ja - - `pysähdys` on vastaava Pysähdys olio. - - Mikäli pysäkille ei ole määrätty riittävästi pysähdyksiä kalenterissa, tuloslista - jää alimittaiseksi, mahdollisesti jopa tyhjäksi. - ''' - class PäivätLoppuError(Exception): - pass - # Hakee pysäkin aikataulut tiettynä päivänä. - def aikataulu_päivänä(päivä): - # Jos päädyttiin aikataulukalenterin ulkopuolelle, niin tuotetaan virhe. Jos vain - # palautettaisiin tyhjä tulos, niin algoritmi jatkaisi etsintää loputtomiin. - if päivä > viimeinen_käyttöpäivä: - raise PäivätLoppuError() - taulu = [] - # Jokaiselle ajovuorolle, - for ajo in ajot.values(): - # jos tämä ajovuoro ajetaan tänä päivänä - if ajo.ajetaan_päivänä(päivä): - # ja jos tämä ajo pysähtyy tällä pysäkillä, ei kuitenkaan saapuen - # päätepysäkille, - pysähdys = ajo.pysäkkiReitillä(self) - if pysähdys and pysähdys is not ajo.reitti[-1]: - # ja jos tämä pysähdys on tulevaisuudessa, - aika = datetime.combine(päivä, time()) + pysähdys.saapumisaika - if aika >= nyt(): - # lisää pysähdys listaan. - taulu.append((aika, pysähdys)) - # Lajittele lopputulos saapumisajan mukaan. - taulu.sort(key = lambda tietue: tietue[0]) - return taulu - taulu = [] - päivä = tänään() - # Niin kauan kuin aikatauluja ei ole vielä tarpeeksi, - while len(taulu) < määrä: - try: - # hae nykyisen päivän aikataulut ja lisää ne, - taulu += aikataulu_päivänä(päivä) - except PäivätLoppuError: - # paitsi jos mentiin kalenterin ulkopuolelle, jolloin lopetetaan, - break - # ja siirry seuraavaan päivään. - päivä += timedelta(1) - # Typistä lopputulos haluttuun tulosmäärään. - return taulu[:määrä] - @property - def linkki_karttaan(self): - return 'http://www.openstreetmap.org/#map=19/%f/%f' % (self.sijainti.leveys, self.sijainti.pituus) - -class Pysähdys: - def __init__(self, saapumisaika, lähtöaika, pysäkki, ajo): - self.saapumisaika, self.lähtöaika, self.pysäkki, self.ajo = saapumisaika, lähtöaika, \ - pysäkki, ajo - def __repr__(self): - return 'Pysähdys(%r, %r, %r, %r)' % (self.saapumisaika, self.lähtöaika, self.pysäkki, self.ajo) - -linjat = {} -linjat_per_tunniste = {} -ajot = {} -ajot_per_numero = {} -palvelut = {} -pysäkit = {} - -print('Ladataan linjat... ', file = stderr, end = '', flush = True) -with open('gtfs/routes.txt') as tiedosto: - for rivi in lue_csv(tiedosto): - linja = Linja(rivi) - linja.tunniste = linja.tunniste - linjat[linja.viite] = linja - linjat_per_tunniste[linja.tunniste] = linja -print('%d linjaa' % len(linjat), file = stderr) - -print('Ladataan ajot... ', file = stderr, end = '', flush = True) -with open('gtfs/trips.txt') as tiedosto: - for rivi in lue_csv(tiedosto, muunnokset = {'direction_id': lambda k: Suunta(int(k))}): - if rivi['service_id'] not in palvelut: - palvelut[rivi['service_id']] = Palvelu(rivi['service_id']) - linja = linjat_per_tunniste[rivi['route_id']] - ajo = Ajovuoro(tunniste = rivi['trip_id'], - linja = linja, - palvelu = palvelut[rivi['service_id']], - kyltti = rivi['trip_headsign'], - suunta = rivi['direction_id']) - assert ajo.nimi not in ajot - ajot[ajo.nimi] = ajo -print('%d ajoa' % len(ajot), file = stderr) - -def lue_päiväys(teksti): - return date(int(teksti[:4]), int(teksti[4:6]), int(teksti[6:])) - -def lue_aika(teksti): - tunti, minuutti, sekunti = map(int, teksti.split(':')) - return timedelta(hours = tunti, minutes = minuutti, seconds = sekunti) - -print('Ladataan päiväykset... ', file = stderr, flush = True) - -viimeinen_käyttöpäivä = date.today() -palvelut_per_päivä = {} - -with open('gtfs/calendar_dates.txt') as tiedosto: - for rivi in lue_csv(tiedosto): - palvelu = palvelut[rivi['service_id']] - päivä = lue_päiväys(rivi['date']) - palvelu.päivät.add(päivä) - if päivä not in palvelut_per_päivä: - palvelut_per_päivä[päivä] = set() - palvelut_per_päivä[päivä].add(palvelu) - viimeinen_käyttöpäivä = max(päivä, viimeinen_käyttöpäivä) - -def palvelut_käytössä(päivä): - for palvelu in palvelut.values(): - if päivä in palvelu.päivät: - yield palvelu - -print('Ladataan pysäkit... ', file = stderr, end = '', flush = True) -with open('gtfs/stops.txt') as file: - for rivi in lue_csv(file): - sijainti = Sijainti(float(rivi['stop_lat']), float(rivi['stop_lon'])) - pysäkki = Pysäkki(rivi['stop_id'], rivi['stop_name'], sijainti) - pysäkit[pysäkki.tunniste] = pysäkki -with open('pysäkkialueet.json') as file: - for pysäkkitunniste, alue in json.load(file).items(): - pysäkit[pysäkkitunniste].alue = alue -print('%d pysäkkiä' % len(pysäkit), file = stderr) - -print('Ladataan aikataulut... ', end = '', flush = True, file = stderr) -with open('gtfs/stop_times.txt') as file: - rivimäärä = sum(line.count('\n') for line in file) - laskettu = 0 - file.seek(0) - for rivi in lue_csv(file): - ajo = ajot[muunna_ajovuoro_tunniste(rivi['trip_id'])] - saapumisaika = lue_aika(rivi['arrival_time']) - lähtöaika = lue_aika(rivi['departure_time']) - pysäkki = pysäkit[rivi['stop_id']] - ajo.reitti.append(Pysähdys(saapumisaika, lähtöaika, pysäkki, ajo)) - laskettu += 1 - if laskettu % 1000 == 0: - print('\rLadataan aikataulut... %.1f%%' % (laskettu * 100 / rivimäärä), end = ' ', file = stderr) -print('\rLadataan aikataulut... ladattu', file = stderr)