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, mode legend, summary sidebar, stop search, and configurable tiles (--tiles dark_matter)
  • 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 (CARTO Positron tiles by default)
gtfs2geojson feed.zip -o feed.geojson --preview map.html

# dark basemap for nighttime / dataviz
gtfs2geojson feed.zip --preview map.html --tiles dark_matter

# 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", tiles="dark_matter")

# 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.5.0.tar.gz (27.0 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.5.0-py3-none-any.whl (18.6 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for gtfs2geojson-0.5.0.tar.gz
Algorithm Hash digest
SHA256 b993fc5b9325e53c5f5e8100de360d41b4f6f05825724628519c328593b6153b
MD5 8752cbe15bff1ff7524d10511789992f
BLAKE2b-256 f214c3df9bb28f3c73ba8aba9dc4267939cc032e83f8feb4bb180e26b549f8ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for gtfs2geojson-0.5.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.5.0-py3-none-any.whl.

File metadata

  • Download URL: gtfs2geojson-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 18.6 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.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e0f12b420508de31ba53d55f91defb298bcbd7131b99da90d28087bb0422517f
MD5 879225df1ecf1a0cee5c1810e1571bc3
BLAKE2b-256 30f17c4eb150eb7668eac9f639d48caffa0efcf90d221b1555b3b2a9093b1497

See more details on using hashes here.

Provenance

The following attestation bundles were made for gtfs2geojson-0.5.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