RailNetworkGraph is a desktop application (built with CustomTkinter) that finds the fastest railway connections based on GTFS data.
It calculates route distance, ticket price, and visualizes the path on an interactive map.
The application supports:
- Finding the fastest route (including transfers) using Dijkstra’s algorithm on a time-expanded graph.
- Calculating ticket prices based on the physical railway track distance, not as-the-crow-flies.
- Displaying an interactive map (Matplotlib) with stations, connections, and region borders.
- Showing a timetable (nearest departures) when a station is clicked.
License:
CC0-1.0(see LICENSE for details)
- Python 3.12–3.14
- CustomTkinter – GUI
- Pandas – GTFS data processing
- Matplotlib / GeoPandas – Interactive map plotting
- NetworkX – Railway graph analysis (in
distance_countermodule)
- Poetry – Dependency and package management
- requirements.txt / requirements-dev.txt – pip installation
- mkdocs – Documentation (
docs/,mkdocs.yml) - pre-commit – Auto-formatting and linting
- CI/CD – GitHub workflows (
.github/) - logging_config – Colored logs with environment detection
- PyInstaller –
.exebuild (RailNetworkGraph.spec) - mypy – Static type checking
- Black – Code formatting
- Ruff – Linting and style enforcement
- On startup, the app loads GTFS timetable data.
- It builds two graphs:
- Time-Expanded Graph: Nodes represent departure events (e.g.
(Station A, Train 123, 08:15)), used by Dijkstra to find the fastest route. - Station Graph: Nodes represent stations, used for map visualization and simple pathfinding (BFS).
- Time-Expanded Graph: Nodes represent departure events (e.g.
- The user provides origin station, destination station, and start time.
- Optionally selects a discount.
SubmitHandlerlaunchesPathFinderDijkstra.- The algorithm finds the fastest path on the time-expanded graph, minimizing travel + transfer times.
- Retrieves physical distance by summing pre-computed segment lengths (
railway_segment_lengths.txt). - Calculates ticket price using distance (
base_price_table.txt) and applies discount (discounts_percentages.txt).
TicketPopupdisplays route, duration, distance, and price.- The route is highlighted in red on the map.
- Clicking any station opens
StationInfoPopupwith nearest departures.
## 🗂️ Project Structure
RailNetworkGraph/
├─ .gitignore # Git ignore rules
├─ .pre-commit-config.yaml # pre-commit hooks (Black, Ruff, mypy, etc.)
├─ LICENSE # License (CC0-1.0, see exceptions inside)
├─ mkdocs.yml # MkDocs site configuration
├─ poetry.lock # Poetry lockfile
├─ pyproject.toml # Poetry project config (deps, tools)
├─ README.md # Project readme
├─ requirements.txt # runtime dependencies
├─ RailNetworkGraph.spec # PyInstaller build specification
│
├─ .github/
│ └─ workflows/
│ ├─ build.yml # Build workflow (package/test build)
│ ├─ ci.yml # CI workflow (lint, tests)
│ └─ release.yml # Release workflow (PyInstaller, publish artifacts)
│
├─ docs/ # Documentation (MkDocs site content)
│ ├─ index.md # Project introduction (homepage)
│ ├─ css/
│ │ ├─ mkdocstrings.css # Styling for mkdocstrings plugin
│ │ └─ theme-variants.css # Additional theme variants
│ └─ gen_ref_pages/ # Scripts for generating API reference pages
│ ├─ config.py
│ ├─ context.py
│ ├─ generate.py
│ ├─ gen_ref_pages.py
│ ├─ helpers.py
│ └─ traverse.py
│
└─ src/
└─ rail_network_graph/
├─ app.py # Main application (data loading, graph init, GUI setup)
├─ config.py # Project configuration (paths, constants, settings)
├─ logging_config.py # logging config (colors, ANSI detection)
├─ __main__.py # Entry point (python -m rail_network_graph)
│
├─ assets/
│ ├─ data/
│ │ ├─ gtfs_data/
│ │ │ ├─ routes.txt # GTFS routes file (names)
│ │ │ ├─ stops.txt # GTFS stops file (locations, names, parent stations)
│ │ │ ├─ stop_times.txt # GTFS stop times file (schedule)
│ │ │ └─ trips.txt # GTFS trips file (links stop_times to routes)
│ │ ├─ railway_distances/
│ │ │ └─ railway_segment_lengths.txt # Output file: station-pair distances (km)
│ │ ├─ tickets_price/
│ │ │ ├─ base_price_table.txt # Distance-to-price lookup table
│ │ │ └─ discounts_percentages.txt # Discount types and percentages
│ │ └─ voivodeship_border/
│ │ └─ dolnoslaskie.geojson # GeoJSON file for the region map boundary
│ └─ img/
│ └─ icon.ico # Windows icon
│
├─ data_processing/
│ ├─ data_loader.py # Loads raw GTFS .txt files into pandas DataFrames
│ └─ data_processor.py # Processes raw GTFS data (merging, mapping stops to stations)
│
├─ distance_counter/
│ ├─ distance_counter.py # Main script to run the distance calculation pipeline
│ ├─ graph_snapper.py # Snaps station coordinates (lat/lon) to the nearest railway graph node
│ ├─ railway_graph_builder.py # Builds NetworkX graph from railway GeoJSON line data
│ ├─ railway_route_calculator.py # Calculates shortest path distances (km) on the railway graph
│ └─ results_exporter.py # Exports calculated distances to a .txt file
│
├─ graphs/
│ ├─ base_graph.py # Base class for a simple directed graph
│ └─ stations_graph.py # Graph of station-to-station connections based on GTFS trip sequences
│
├─ GUI/
│ ├─ gui_creator.py # Main GUI builder, initializes and connects all UI components
│ ├─ counter_panel/
│ │ ├─ input_panel.py # UI for station/time inputs and discount selection
│ │ ├─ submit_handler.py # Logic for the 'Search' button (runs pathfinder, gets price, updates UI)
│ │ └─ ticket_popup.py # Popup window to display the final route, time, and price
│ ├─ map/
│ │ ├─ map_canvas.py # The interactive Matplotlib canvas (handles zoom, pan, click)
│ │ └─ map_plotter.py # Logic for drawing stations, connections, and highlights on the map
│ └─ station_info/
│ ├─ station_info_logic.py # Fetches connection data/timetable for a clicked station
│ └─ station_info_popup.py # Popup window to display timetable/info for a single station
│
├─ path_finder/
│ ├─ cleaner.py # Cleans the raw path result (removes cycles, collapses duplicates)
│ ├─ graph.py # Builds the time-expanded graph for Dijkstra (nodes = departure events)
│ ├─ models.py # Data models (e.g., StopRow) for pathfinding
│ ├─ path_finder.py # Main Dijkstra algorithm implementation on the time-expanded graph
│ └─ time_utils.py # Utility functions for handling time (HH:MM:SS <-> minutes)
│
└─ utils/
├─ distance_calculator.py # Calculates total route distance using pre-computed segment lengths
└─ price_calculator.py # Calculates ticket price based on distance and pricing table
Users (runtime only):
python -m venv .venv
source .venv/bin/activate # Linux/macOS
.venv\Scripts\activate # Windows
pip install --upgrade pip
pip install -r requirements.txt
pip install -e .Developers (runtime + dev):
pip install -r requirements.txt -r requirements-dev.txt
pip install -e .Users (without dev):
poetry install --without dev
poetry run rail_network_graphDevelopers (with dev):
poetry install
poetry run rail_network_graphBuilt with mkdocs.
mkdocs serve # local preview (http://127.0.0.1:8000)
mkdocs build # build into site/To build a Windows .exe with PyInstaller:
pyinstaller RailNetworkGraph.specThe resulting binary will be in dist/.
This project uses several tools to keep the codebase clean and consistent:
mypy src/ruff check src/black src/pre-commit run --all-filespython -m rail_network_graphpoetry run rail_network_graphrail_network_graph- Launch the app (
rail_network_graph). - Enter Station 1 (origin), Station 2 (destination), and Start time (
HH:MM:SS). - Optionally, select a discount.
- Click Search.
- View route details (time, price, distance) in the popup window.
- See the route highlighted on the map.
- (Optional) Click any station on the map to view its nearest departures.
Released under CC0-1.0 (public domain).
You may copy, modify, distribute, and use it commercially without asking for permission.



