# 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](https://docs.astral.sh/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 ```python 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: ```python 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): 1. **Filtruj przystanki** - wybierz te osiągalne w danym czasie 2. **Zbierz punkty** - współrzędne geograficzne przystanków 3. **Generuj wielokąt**: - Jeśli < 3 punkty: bufor wokół punktów - Jeśli ≥ 3 punkty: convex hull z buforem ```python 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:** ```json { "origin_stop_id": "5100069", "time_intervals": [30, 60, 90, 120] } ``` **Response:** ```json { "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: 1. **GTFS Realtime** - aktualne opóźnienia pociągów 2. **Różne godziny wyjazdu** - zmienny graf w ciągu dnia 3. **Dni tygodnia** - uwzględnienie calendar.txt 4. **Przesiadki** - dodanie czasu na przesiadkę (np. 5 min) 5. **Concave hull** - bardziej precyzyjne wielokąty (alpha shapes) 6. **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: ```python # 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): ```dockerfile FROM python:3.9 WORKDIR /app COPY backend/ . RUN pip install -r requirements.txt CMD ["gunicorn", "app:app"] ```