SIG-Bus — QGIS Plugin for Public Transport Analysis¶
Português | English
A QGIS plugin that integrates GTFS (General Transit Feed Specification) data with passenger boarding demand data per bus stop, enabling visualisation and allocation of passenger loads along route alignments.
Developed as part of the undergraduate research project PIBIC DPPG 113/2021.
Features¶
The GTFS reader is built-in (gtfs_reader.py), adapted from the GTFS Loader
plugin by CTU GeoForAll Lab (GPL v2+). No external plugin is required.
Demand analysis¶
- Check GTFS: validates the
.zipfeed and synthesisescalendar.txtfromcalendar_dates.txtwhen the feed only provides the latter. - Load GTFS: imports the feed into a GeoPackage via GDAL (streaming — memory-
efficient for large feeds). Builds the route alignments layer (
shapes) and creates join indexes. - Insert demand: imports a boarding-by-stop/hour CSV into a GeoPackage
(
sigt.gpkg). - Filter data: given a selected route (
route_short_name), highlights the alignment inshapes, filtersdados_demanda, and loads stop-level timetables (horarios_paradas) in the background. - Allocate Demand: distributes boardings from the CSV across the segments
(links) of the route, producing the
tramos_demandalayer with: embarques— boardings allocated to the upstream stop of the linkpassageiros_acum— cumulative passenger load on the bus at that linkn_viagens— GTFS trips that departed within the selected hour- Hour selector: filters the allocation by time slot (0 h–23 h) or the full daily total. When an hour is selected, the dominant shape among trips that departed in that hour is used.
- Reconnect GeoPackage: restores GTFS layers to the project without reprocessing the feed (useful after closing and reopening QGIS).
Build GTFS¶
- Build GTFS: creates a GTFS feed from scratch via an interactive assistant (see sig_bus/GUIA_CONSTRUIR_GTFS.md):
- Agency and Routes: define the transit operator and route details.
- Geocoded Stops: search stop addresses through a geocoding cascade — Google (optional, requires an API key) → Nominatim → Photon → an Overpass-based street-name corrector — showing a status label with the source (
✓ localizado (Nominatim),✓ localizado (via: <real name> — OSM)); addresses follow a suggested pattern (Street, Number - Neighborhood). An address that isn't found never blocks the flow. - Batch Import: load stops via CSV (see sig_bus/MODELO_PARADAS_CSV.md).
- Mark on the Map: click directly on the canvas to place a stop, for rural points without a geocodable address.
- Sequence: arrange the stops in the correct visiting order.
- Timetables: generate trips from up to 3 time bands (e.g. morning peak, off-peak, afternoon peak), each with its own start time, end time, headway and trip duration, then fine-tune them trip by trip on the diagram (see Block Diagram below) before anything is written; Restore regular frequency discards the manual tuning and regenerates the grid from the bands.
- Review and save: the route is written to an isolated
feed_edit.gpkg, then validated and exported by the same engine as Edit GTFS. - OSM Routing: route alignments (
shapes) follow the real OpenStreetMap street network between consecutive stops; a straight line is used as a fallback only on the segments the fetched network does not cover or connect. - Dual progress bar: shows how far the feed is from a minimum GTFS (required files and fields) and, beyond that, from a complete one (optional fields,
shapes, second direction), naming what is still missing at each step.
Edit and export GTFS¶
- Edit GTFS: allows editing GTFS fields and geometry in an isolated working copy (
feed_edit.gpkg), with built-in validation and normalized export (see sig_bus/GUIA_EDICAO_GTFS.md). - Adjust schedules: with a route selected, opens a schedule matrix for that route — one tab per direction, stops as rows and trips as columns — where the times are typed directly into the cells and Apply to feed writes them back into
feed_edit.gpkg, leaving the shapes, thetrip_ids and the number of trips untouched. The window opens already sized to fit the screen it's opened on and can be maximized; it reopens at the size and position it was last left in (kept in the QGIS profile, never in the project file nor in the feed) as long as that still fits the current screen, and the matrix sits above the block diagram on a splitter that can be dragged up or down to give the table or the diagram more room without either panel disappearing.
Block Diagram¶
- Block Diagram: a time × distance chart of the operation, in two modes — Trips mode (one bar per GTFS trip) and Blocks mode, in which the vehicle blocks are inferred by chaining trips, because the BHTrans feed carries no
trips.block_id. See sig_bus/DIAGRAMA_BLOCOS.md. - Headway dimension line: selecting a trip draws a technical-drawing dimension line between two consecutive departures of the same route and direction, labelled with the measure alone (e.g.
12 min). - Departure ruler: one short tick per departure along the foot of the time axis — outbound on the upper band, inbound on the lower one — so peak and off-peak read straight from the density of ticks.
- Fine schedule tuning: with a trip selected,
>and<shift only the departure or only the arrival, while+and-shift the whole trip preserving its duration; the framing is kept between redraws, and Fit all re-frames the whole grid on demand. In the Build GTFS wizard the adjustment happens on the diagram, in memory, before the route is written, and applies to every day covered by thecalendarof thatservice_id; in the Edit GTFS tab the same times are adjusted in a matrix (one tab per direction), over a feed already saved in the working copy. - PDF Report: generates an A4 landscape print layout with the map of the filtered route, legend, header, and two bar charts (outbound and inbound) of boardings grouped by K-means cluster.
Documentation¶
The same documentation is browsable at https://sigbus.dcamargo.com.br. The files in the table below are the offline copy that ships with the plugin.
| Document | What it answers |
|---|---|
sig_bus/DOCUMENTACAO.md |
What each button does, output layer fields, and known limitations (EN + PT-BR) |
sig_bus/METHODS.md |
Theoretical foundation of the demand allocation method (EN) |
sig_bus/DIAGRAMA_BLOCOS.md |
How to read the Block Diagram, its two modes, and the block inference (PT-BR) |
sig_bus/GUIA_CONSTRUIR_GTFS.md |
Step-by-step of the "Build GTFS" wizard (PT-BR) |
sig_bus/GUIA_EDICAO_GTFS.md |
Step-by-step of the "Edit GTFS" tab and its common errors (PT-BR) |
sig_bus/MODELO_PARADAS_CSV.md |
Column layout of the CSV for batch stop import (PT-BR) |
sig_bus/ARQUITETURA_CONSTRUIR_GTFS.md |
Internal design of GTFS creation (PT-BR) |
sig_bus/ARQUITETURA_EDICAO_GTFS.md |
Internal design of GTFS editing (PT-BR) |
Version-by-version history is in CHANGELOG.md; the current
version is the one declared in sig_bus/metadata.txt.
Repository Structure¶
.
├── CHANGELOG.md # version-by-version history
├── docs/
│ ├── gtfsfiles.zip # sample GTFS feed for testing
│ └── PyQGIS_PIBIC.pdf # original research documentation
└── sig_bus/ # plugin code (install into QGIS)
├── __init__.py
├── SigBus.py # plugin main class
├── SigBus_dialog.py # dialog logic + background tasks
├── SigBus_dialog_base.ui
├── gtfs_reader.py # built-in GTFS reader
├── gtfs_schema.py # single source of truth for the GTFS spec
├── gtfs_builder_core.py # builds a feed from scratch (progress, expansion)
├── gtfs_edit_core.py # isolated working copy (feed_edit.gpkg)
├── gtfs_validator.py # referential and format integrity checks
├── gtfs_export.py # normalized export to .zip
├── geocoding.py # geocoding cascade (Google/Nominatim/Photon)
├── geocoding_config.py # provider mode and API key in QSettings
├── street_index.py # Overpass-based street-name corrector
├── address_format.py # suggested address pattern
├── osm_routing.py # shapes over the real OSM street network
├── map_tools.py # place a stop by clicking the canvas
├── stops_csv.py # batch stop import from CSV
├── schedule_edit_core.py # in-memory schedule fine tuning
├── schedule_table_core.py # schedule matrix (stops × trips), no Qt
├── schedule_grid_widget.py # the schedule table itself (stops × trips grid)
├── block_core.py # Block Diagram model and block inference
├── block_scene.py # Block Diagram drawing (bars, headway, ruler)
├── block_view.py # Block Diagram view: zoom, pan, shortcuts
├── block_diagram_dialog.py # Block Diagram window
├── test_*.py # test suite, runs outside QGIS
├── conftest.py # qgis module stubs used by the suite
├── scripts/check_qgis_compat.py # manual probe against the installed QGIS
├── ARQUITETURA_CONSTRUIR_GTFS.md # technical architecture for GTFS creation (PT-BR)
├── ARQUITETURA_EDICAO_GTFS.md # technical architecture for GTFS editing (PT-BR)
├── DIAGRAMA_BLOCOS.md # Block Diagram documentation (PT-BR)
├── DOCUMENTACAO.md # detailed feature documentation (EN + PT-BR)
├── GUIA_CONSTRUIR_GTFS.md # user guide for GTFS creation (PT-BR)
├── GUIA_EDICAO_GTFS.md # user guide for GTFS editing (PT-BR)
├── METHODS.md # theoretical foundation of the allocation method
├── MODELO_PARADAS_CSV.md # documentation for CSV stops batch import (PT-BR)
├── modelo_paradas.csv # template file for batch stops import
├── metadata.txt
├── icon.png
└── resources.py / resources.qrc
Requirements¶
- QGIS 3.34 LTR through 4.x — runs on both Qt 5 (QGIS 3.x) and Qt 6 (QGIS 4.x)
(declared range
3.34–4.99; probed on 3.34.4, tested on 3.44 and 4.2) - QGIS built-in Python (no external dependencies beyond QGIS itself)
Installation¶
- Copy the
sig_bus/folder to the QGIS plugins directory: - Linux:
~/.local/share/QGIS/QGIS3/profiles/default/python/plugins/ - Windows:
%APPDATA%\QGIS\QGIS3\profiles\default\python\plugins\ - Enable the SIG-Bus plugin under Plugins → Manage and Install Plugins → Installed.
- Access it via Plugins → SIG-Bus.
Geocoding configuration¶
Configuring a Google Maps API key is optional. Without any key the OSM cascade (Nominatim → Photon → Overpass street-name corrector) keeps working exactly as before — nothing to set up.
- The key and the provider mode are stored in QGIS's
QSettings(SIG-Bus/geocoding/google_api_keyandSIG-Bus/geocoding/provider, handled bygeocoding_config.py) — never in the project and never in the feed. - Provider mode:
autotries Google first when a key is configured and falls back to the OSM cascade;osmignores any key and uses the OSM cascade only. - The key is never written to
feed_edit.gpkg(which is shared) and never reaches the QGIS log — every logged URL is redacted tokey=***.
Workflow¶
The plugin has three main entry paths. Paths (b) and (c) share the same validator and export engine.
a) Analyze an existing feed¶
Check GTFS → Load GTFS → Insert demand
→ Select route → Filter data
→ Choose hour → Allocate Demand
See sig_bus/DOCUMENTACAO.md for a detailed description of each step, output
layer fields, and known limitations. For the theoretical background of the
demand allocation method, see sig_bus/METHODS.md.
b) Build a GTFS from scratch¶
Build GTFS → Route wizard → Export .zip
See sig_bus/GUIA_CONSTRUIR_GTFS.md for the user guide.
c) Edit a loaded feed¶
Edit GTFS → Edit data → Validate → Export .zip
See sig_bus/GUIA_EDICAO_GTFS.md for the user guide.
Sample Data¶
docs/gtfsfiles.zip contains a GTFS feed for testing.
Expected demand data follows the SIU-BHTrans format
(;-delimited CSV, columns 0–23 with hourly boardings).
Tests¶
The test suite runs entirely outside QGIS, on stubs of the qgis modules
(sig_bus/conftest.py) — no QGIS installation is required. From the repository
root:
python3 -m pytest sig_bus -q
Besides the unit tests, the suite carries regression guards:
test_qt6_compat.py (unqualified enums, which break QGIS 4),
test_metadata.py (declared QGIS version range and a closed CHANGELOG matching
metadata.txt) and test_readme.py (relative links in this file, and the two
halves staying in step). sig_bus/scripts/check_qgis_compat.py is a manual
probe against an installed QGIS and does not run under pytest.
Schedule editing is covered by test_schedule_table_core.py (schedule matrix),
test_gtfs_edit_stop_times.py (reading and writing stop_times filtered by
route, plus the out-of-order check in the validator) and
test_block_view_zoom.py (zoom, and framing kept between redraws).
Release Process¶
To package and release a new version of the SIG-Bus plugin:
- Update version: Edit
sig_bus/metadata.txtto incrementversion=X.Y.Z(keepingqgisMinimumVersion=3.34,qgisMaximumVersion=4.99andsupportsQt6=True— without the maximum, QGIS assumes3.99and rejects every QGIS 4.x). The third digit is mandatory: three-digit versions are the standard across the other projects, andtest_metadata.pyrejects any other format. - Update Changelog: Add release notes under a new version heading in
CHANGELOG.md. - Run Test Suite: Execute
pytestto verify all tests and guards pass. - Probe the installed QGIS: Run
python3 sig_bus/scripts/check_qgis_compat.pyagainst the QGIS you are targeting — it imports every module, builds aQgsField, checks the qualified-enum inventory and loads the.ui, printingOK/FAILper item. - Package Plugin: Create the distribution
.ziparchive usingqgis-plugin-ciormake package: - Via
qgis-plugin-ci:qgis-plugin-ci package <version> - Via
make:cd sig_bus && make package VERSION=v<version> - Tag and Publish: Create and push a Git tag (e.g.
git tag -a v0.5 -m "Release 0.5" && git push origin v0.5) and upload the generated.zipto QGIS Plugin Repository or GitHub Releases.
Author¶
Diego Camargo — diegocamargo.bft@gmail.com
Repository: https://github.com/d-camargo/sig-bus