Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
306 changes: 305 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,305 @@
# RailNetworkGraph
# RailNetworkGraph

**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](LICENSE) for details)*

---

## ⚙️ Technologies

### Runtime
- **Python** 3.12–3.14
- **CustomTkinter** – GUI
- **Pandas** – GTFS data processing
- **Matplotlib / GeoPandas** – Interactive map plotting
- **NetworkX** – Railway graph analysis (in `distance_counter` module)

### Production / Development
- **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** – `.exe` build (`RailNetworkGraph.spec`)
- **mypy** – Static type checking
- **Black** – Code formatting
- **Ruff** – Linting and style enforcement

---

## 🧠 How It Works

### 1. Data Loading
- 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).

### 2. User Input
- The user provides **origin station**, **destination station**, and **start time**.
- Optionally selects a **discount**.

### 3. Route Finding ("Search" Button)
- `SubmitHandler` launches `PathFinderDijkstra`.
- The algorithm finds the **fastest path** on the time-expanded graph, minimizing travel + transfer times.

### 4. Analysis (Distance & Price)
- 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`).

### 5. Presentation
- `TicketPopup` displays route, duration, distance, and price.
- The route is highlighted in red on the map.
- Clicking any station opens `StationInfoPopup` with nearest departures.

---

## 🗂️ Project Structure

```
## 🗂️ 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


```
## 🔧 Installation

### Option A — pip

**Users (runtime only):**
```bash
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):**
```bash
pip install -r requirements.txt -r requirements-dev.txt
pip install -e .
```

---

### Option B — Poetry

**Users (without dev):**
```bash
poetry install --without dev
poetry run rail_network_graph
```

**Developers (with dev):**
```bash
poetry install
poetry run rail_network_graph
```

---

## 📚 Documentation

Built with **mkdocs**.

```bash
mkdocs serve # local preview (http://127.0.0.1:8000)
mkdocs build # build into site/
```

---

## 🏗️ Build Executable

To build a Windows `.exe` with **PyInstaller**:

```bash
pyinstaller RailNetworkGraph.spec
```

The resulting binary will be in `dist/`.

---

## 🛠️ Development Tools

This project uses several tools to keep the codebase clean and consistent:

### Type checking
```bash
mypy src/
```

### Linting
```bash
ruff check src/
```

### Auto-formatting
```bash
black src/
```

### Run all pre-commit hooks locally
```bash
pre-commit run --all-files
```

---

## ▶️ Running the App

### From source
```bash
python -m rail_network_graph
```

### With Poetry
```bash
poetry run rail_network_graph
```

### After installation
```bash
rail_network_graph
```

---

## 💻 Usage

1. Launch the app (`rail_network_graph`).
2. Enter **Station 1** (origin), **Station 2** (destination), and **Start time** (`HH:MM:SS`).
3. Optionally, select a **discount**.
4. Click **Search**.
5. View route details (time, price, distance) in the popup window.
6. See the route highlighted on the map.
7. (Optional) Click any station on the map to view its nearest departures.

---

## 🖼️ Screenshots

**GIF:**
![Main GIF](screenshots/railnetworkgraph.gif)

**Main application screen:**
![Main application screen:](screenshots/main_application_screen.png)

**Search result (ticket popup):**
![Search result (ticket popup):](screenshots/search_result.png)

**Station info (on-click):**
![Station info (on-click):](screenshots/station_info.png)

---

## 📜 License

Released under **CC0-1.0 (public domain)**.
You may copy, modify, distribute, and use it commercially without asking for permission.
Binary file added screenshots/main_application_screen.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/railnetworkgraph.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/search_result.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/station_info.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.