Skip to main content

Convert GTFS feeds to GeoJSON with optional Folium preview maps.

Project description

gtfs2geojson

Convert any GTFS feed (zip file or directory) into a clean, styled GeoJSON FeatureCollection, with an optional interactive Folium preview map.

Built for transit data folks who want a quick, dependency-light way to go from gtfs.zip → something they can drop into kepler.gl, QGIS, deck.gl, Mapbox, or a Leaflet/Folium map — without spinning up Postgres/PostGIS.

Features

  • Single command, no database: gtfs2geojson feed.zip -o feed.geojson
  • Reads zip or directory GTFS sources, handles UTF-8 BOM
  • Routes as MultiLineString features, one per route_id (all shape variants merged)
  • Stops as Point features
  • Mode-aware styling — readable labels (Bus / Metro / Tram / Ferry / Trolleybus / …) with sensible default colours, falling back from route_color when missing
  • Reconstructs missing geometry from stop_times when a route has no entry in shapes.txt (common for metros/trams in older feeds)
  • Filters: by mode (--mode Bus), by agency (--agency OASA), by bounding box (--bbox w,s,e,n), by service day (--date 20260615)
  • Line simplification with Ramer-Douglas-Peucker (--simplify 0.0001) for smaller, faster-rendering output
  • Output formats: standard GeoJSON or GeoJSON-seq (--format geojsonseq) for tippecanoe / ogr2ogr pipelines
  • Introspection: --list-modes and --list-agencies (with --json) to peek at a feed without converting
  • Optional Folium preview with per-mode toggleable layers and clustered stops
  • Extended GTFS route types supported (HVT codes 100–1700)

Install

pip install gtfs2geojson           # core
pip install "gtfs2geojson[preview]" # also installs folium for --preview

Or from source:

git clone https://github.com/acesonic7/gtfs2geojson
cd gtfs2geojson
pip install -e ".[preview]"

CLI

# basic
gtfs2geojson feed.zip -o feed.geojson

# with interactive map
gtfs2geojson feed.zip -o feed.geojson --preview map.html

# filter to buses + metro inside a bbox, skip stops
gtfs2geojson feed.zip \
  --mode Bus --mode Metro \
  --bbox 23.6,37.9,23.8,38.05 \
  --no-stops \
  -o athens_core.geojson

# only trips active on a particular calendar date (combines calendar.txt
# weekday rules with calendar_dates.txt exceptions)
gtfs2geojson feed.zip --date 20260615 -o that_monday.geojson

# simplify polylines for web/mobile rendering (~10 m at most latitudes)
gtfs2geojson feed.zip --simplify 0.0001 -o feed.simplified.geojson

# pipe straight into tippecanoe (GeoJSON-seq, one Feature per record)
gtfs2geojson feed.zip --format geojsonseq | tippecanoe -o feed.mbtiles

# peek at what a feed contains, without converting
gtfs2geojson feed.zip --list-modes
gtfs2geojson feed.zip --list-agencies --json | jq '.[] | select(.n_routes > 5)'

# feed already extracted to a directory
gtfs2geojson ./gtfs_extracted/ -o feed.geojson

Stream to stdout (default) for piping into other tools:

gtfs2geojson feed.zip | jq '.features | length'

Python API

from gtfs2geojson import convert, write, list_modes, list_agencies
from gtfs2geojson.preview import render

geo = convert(
    "feed.zip",
    modes=["Bus", "Trolleybus"],
    bbox=(23.6, 37.9, 23.8, 38.05),
    service_date="2026-06-15",       # also accepts "20260615" or datetime.date
    simplify_tolerance=0.0001,       # ~10 m perpendicular tolerance
    include_stops=True,
)
write(geo, "feed.geojson")                    # default
write(geo, "feed.geojsonseq", format="geojsonseq")  # one Feature per RFC-8142 record
render(geo, "preview.html", title="Athens core network")

# Peek at a feed without converting
list_modes("feed.zip")        # -> {"Bus": 42, "Metro": 3, ...}
list_agencies("feed.zip")     # -> [{"agency_id": "OASA", "agency_name": "OASA", "n_routes": 45}, ...]

Output schema

Each route feature carries:

Property Description
feature_type always "route"
route_id, route_short_name, route_long_name from routes.txt
route_type original GTFS integer
mode human label (Bus, Metro, Tram, Ferry, …)
route_color, route_text_color hex strings, with mode-based defaults
agency_id, agency_name joined from agency.txt
headsigns up to 6 distinct headsigns concatenated with |
shape_source "shapes.txt" or "stop_sequence" (reconstructed)
n_trips number of trips serving this route
n_stops number of distinct stops touched by any trip on this route
length_km total network length, sum of all shape variants (haversine, km)

Stop features carry:

Property Description
feature_type always "stop"
stop_id, stop_name, stop_code from stops.txt
route_ids sorted list of routes serving this stop
modes sorted list of distinct modes among those routes

By default, stops not served by any kept trip are dropped — pass --keep-orphan-stops (CLI) or keep_orphan_stops=True (Python) to retain them. If stop_times.txt is absent, all stops are kept regardless.

Why?

Converting GTFS to GeoJSON should be a one-liner. Most existing options either:

  • require Postgres/PostGIS (too heavy for a quick visualisation),
  • assume shapes.txt is always present (it isn't — many metros, trams, and older feeds skip it),
  • or output undecorated geometries with no styling hooks.

gtfs2geojson aims to be the boring, dependable, do-one-thing tool you can plug into a Makefile or notebook.

Limitations / non-goals

  • No frequency or schedule data on the output.
  • No realtime (GTFS-RT) support — schedule feeds only.

If you need any of the above, gtfs_kit and partridge are excellent.

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gtfs2geojson-0.4.0.tar.gz (24.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gtfs2geojson-0.4.0-py3-none-any.whl (17.0 kB view details)

Uploaded Python 3

File details

Details for the file gtfs2geojson-0.4.0.tar.gz.

File metadata

  • Download URL: gtfs2geojson-0.4.0.tar.gz
  • Upload date:
  • Size: 24.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gtfs2geojson-0.4.0.tar.gz
Algorithm Hash digest
SHA256 e3731499b5b1c592dd9120e6b30c8c3e1c6799d23feab24fe3a9c59c6c86eccc
MD5 514f33add166c508817be15c073db32e
BLAKE2b-256 8a91078fb4aa61cc094e5cd3cd255cb60da90ff8f849ee18b548a1900cfa5b96

See more details on using hashes here.

Provenance

The following attestation bundles were made for gtfs2geojson-0.4.0.tar.gz:

Publisher: publish.yml on acesonic7/gtfs2geojson

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gtfs2geojson-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: gtfs2geojson-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 17.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gtfs2geojson-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1bcd6712d0bd6d344bdcbe80d30eb8ed4943d5616df6fd5c161129b6ad1fcedc
MD5 42d12e813b7db0e495dd445d0a7e19ef
BLAKE2b-256 451f46d2bb691847d47abdfd2ccf0cb2a04158061a2d3f567e518791b754d6e3

See more details on using hashes here.

Provenance

The following attestation bundles were made for gtfs2geojson-0.4.0-py3-none-any.whl:

Publisher: publish.yml on acesonic7/gtfs2geojson

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page