Skip to content

Repository files navigation

Lynx

Lynx is a real-time public transport map for Lviv, Ukraine. It combines GTFS static data with GTFS-Realtime vehicle positions to show vehicles, stops, arrival estimates, route progress, and delay information.

Features

  • Live vehicle updates over WebSocket
  • Bus, tram, and trolleybus markers on an interactive map
  • Stop search and viewport-based stop loading
  • Live ETA with schedule fallback
  • Vehicle trip progress and delay estimates
  • PostgreSQL, PostGIS, TimescaleDB, and Redis storage
  • Mock vehicle mode when no GTFS-Realtime feed is configured
  • Docker Compose development environment

Screenshots

Lynx mobile trip progress

The mobile view shows live vehicles on the map together with route progress, upcoming stops, arrival times, and delay estimates.

Tech stack

Area Technology
Frontend React, Vite, Leaflet, React Leaflet
API Node.js, Fastify
Live updates Redis Pub/Sub, WebSocket
Database PostgreSQL, PostGIS, TimescaleDB
Data GTFS Static, GTFS-Realtime

Quick start with Docker

Requirements: Docker Desktop and Docker Compose.

cp .env.example .env
docker compose up --build

Open:

The included gtfs/ directory is imported automatically when the database starts for the first time. If GTFS_RT_URL is empty, the API uses mock vehicle positions.

To reset the database and import GTFS data again:

docker compose down -v
docker compose up --build

Local development

Requirements: Node.js 22+, PostgreSQL with PostGIS and TimescaleDB, and Redis.

cp .env.example .env
npm install
npm --prefix frontend install
npm run gtfs:import -- ./gtfs

Run the backend and frontend in separate terminals:

npm run dev
npm run frontend:dev

Environment variables

Variable Purpose
DATABASE_URL PostgreSQL connection string
REDIS_URL Redis connection string
GTFS_RT_URL GTFS-Realtime vehicle positions URL; empty enables mock mode
GTFS_RT_API_KEY Optional bearer token for the realtime feed
POLL_INTERVAL_MS Realtime polling interval
AGENCY_TIMEZONE GTFS agency timezone, defaults to Europe/Kyiv
NODE_ENV development or production
JWT_SECRET Reserved for authentication; replace before production use

Never commit .env. Only .env.example belongs in the repository.

Useful commands

npm start                  # start the API
npm run dev                # start the API in watch mode
npm run frontend:dev       # start the Vite frontend
npm run frontend:build     # build the frontend
npm run gtfs:import -- ./gtfs
npm run gtfs:download
npm test                   # syntax checks and frontend production build

Main API routes

GET /health
GET /vehicles
GET /vehicles/:vehicleId/track
GET /vehicles/:vehicleId/trip-progress
GET /routes
GET /routes/:routeId/stops
GET /routes/:routeId/vehicles
GET /stops
GET /stops/search?q=...
GET /stops/nearby?lat=...&lon=...
GET /stops/:stopId/eta
GET /stops/:stopId/schedule
GET /analytics/delays
GET /analytics/heatmap
WS  /ws

Project structure

frontend/             React and Leaflet client
src/
  db/                 PostgreSQL and Redis clients
  routes/             REST and WebSocket routes
  services/           ETA, GTFS import, and realtime polling
  workers/            GTFS downloader
scripts/init.sql      Database schema
gtfs/                 Static GTFS files
docs/screenshots/     README images

Before production

  • Replace development database credentials and JWT_SECRET.
  • Set a trusted CORS origin instead of allowing every origin.
  • Put the API and frontend behind HTTPS and a reverse proxy.
  • Confirm that your GTFS data provider permits redistribution.
  • Add API and integration tests.
  • Add monitoring, backups, and rate limits appropriate for your traffic.

License

MIT

About

Real-time public transport map for Lviv using GTFS and GTFS-Realtime

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages