4.7 KiB
4.7 KiB
Dokumentacja techniczna
Stack technologiczny
- Backend: Python 3.9+, Flask, gtfs-kit, NetworkX, GeoPandas
- Frontend: Leaflet, OpenStreetMap, vanilla JavaScript
- Dane: GTFS (General Transit Feed Specification)
- Zarządzanie zależnościami: uv - szybki menedżer pakietów
Architektura
Backend (Python)
backend/
├── app.py # Flask API server
├── gtfs_loader.py # Ładowanie i parsowanie GTFS
├── isochrone_calculator.py # Algorytm obliczania izochron
└── download_gtfs.py # Pobieranie danych GTFS
Frontend (JavaScript)
frontend/
├── index.html # Struktura strony
├── map.js # Logika mapy Leaflet i API
└── style.css # Stylowanie
Algorytm obliczania izochron
1. Budowanie grafu połączeń
- Węzły: przystanki kolejowe (stop_id)
- Krawędzie: bezpośrednie połączenia między przystankami
- Wagi: czas podróży w minutach
G = nx.DiGraph()
G.add_node(stop_id, name=..., lat=..., lon=...)
G.add_edge(from_stop, to_stop, weight=travel_time_minutes)
2. Algorytm Dijkstry
Obliczamy najkrótsze ścieżki z punktu startowego do wszystkich osiągalnych przystanków:
lengths = nx.single_source_dijkstra_path_length(
graph, origin_stop_id, cutoff=max_time, weight='weight'
)
3. Generowanie wielokątów izochron
Dla każdego przedziału czasowego (30, 60, 90, 120 min):
- Filtruj przystanki - wybierz te osiągalne w danym czasie
- Zbierz punkty - współrzędne geograficzne przystanków
- Generuj wielokąt:
- Jeśli < 3 punkty: bufor wokół punktów
- Jeśli ≥ 3 punkty: convex hull z buforem
points = [(lon, lat) for each reachable stop]
polygon = MultiPoint(points).convex_hull.buffer(0.05)
Format danych GTFS
Używane pliki:
- stops.txt - przystanki (stop_id, stop_name, stop_lat, stop_lon)
- routes.txt - linie (route_id, route_short_name, route_type)
- trips.txt - kursy (trip_id, route_id, service_id)
- stop_times.txt - rozkład jazdy (trip_id, stop_id, arrival_time, departure_time)
Przykład struktury:
stops.txt:
stop_id,stop_name,stop_lat,stop_lon
5100069,Wrocław Główny,51.0989,17.0368
stop_times.txt:
trip_id,arrival_time,departure_time,stop_id,stop_sequence
trip_1,08:00:00,08:00:00,5100069,1
trip_1,08:45:00,08:46:00,5100011,2
API Reference
POST /api/isochrones
Oblicza izochrony dla wybranej stacji.
Request:
{
"origin_stop_id": "5100069",
"time_intervals": [30, 60, 90, 120]
}
Response:
{
"origin_stop_id": "5100069",
"isochrones": [
{
"time": 30,
"geometry": { "type": "Polygon", "coordinates": [...] },
"stops": ["id1", "id2", ...],
"stop_count": 15
}
],
"reachable_stops": [
{
"stop_id": "...",
"name": "...",
"lat": 51.0,
"lon": 17.0,
"time": 25.5
}
]
}
Optymalizacja
Wydajność:
- Cache grafu - graf budowany raz przy starcie
- Cutoff w Dijkstra - tylko obliczamy ścieżki do max_time
- Buforowanie wyników - możliwość dodania Redis dla cache'owania
Możliwe ulepszenia:
- GTFS Realtime - aktualne opóźnienia pociągów
- Różne godziny wyjazdu - zmienny graf w ciągu dnia
- Dni tygodnia - uwzględnienie calendar.txt
- Przesiadki - dodanie czasu na przesiadkę (np. 5 min)
- Concave hull - bardziej precyzyjne wielokąty (alpha shapes)
- Filtrowanie przewoźników - tylko PKP IC lub tylko KD
Znane limity
- Uproszczony model: jeden "reprezentatywny" dzień, bez uwzględnienia różnic w rozkładzie
- Convex hull: wielokąty mogą obejmować obszary bez realnych połączeń
- Brak czasu przesiadki: zakłada natychmiastową przesiadkę
- Brak uwzględnienia opóźnień: tylko statyczne rozkłady
Testy
Przykładowe testy dla backendu:
# Testowanie ładowania GTFS
loader = GTFSLoader('data/polish_trains.zip')
assert len(loader.get_stops()) > 0
# Testowanie budowania grafu
graph = loader.build_route_graph()
assert graph.number_of_nodes() > 0
# Testowanie obliczeń izochron
calc = IsochroneCalculator(graph)
isochrones = calc.create_isochrones('5100069', [30, 60])
assert len(isochrones) == 2
Deployment
Lokalne:
- Backend: Flask development server
- Frontend: Otwarcie pliku HTML lub
python -m http.server
Produkcja:
- Backend: Gunicorn + Nginx
- Frontend: Nginx / GitHub Pages / Netlify
- Dane: CDN dla GTFS lub okresowe aktualizacje
Docker (przyszłe):
FROM python:3.9
WORKDIR /app
COPY backend/ .
RUN pip install -r requirements.txt
CMD ["gunicorn", "app:app"]