Pular para conteúdo

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 .zip feed and synthesises calendar.txt from calendar_dates.txt when 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 in shapes, filters dados_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_demanda layer with:
  • embarques — boardings allocated to the upstream stop of the link
  • passageiros_acum — cumulative passenger load on the bus at that link
  • n_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, the trip_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 the calendar of that service_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.344.99; probed on 3.34.4, tested on 3.44 and 4.2)
  • QGIS built-in Python (no external dependencies beyond QGIS itself)

Installation

  1. Copy the sig_bus/ folder to the QGIS plugins directory:
  2. Linux: ~/.local/share/QGIS/QGIS3/profiles/default/python/plugins/
  3. Windows: %APPDATA%\QGIS\QGIS3\profiles\default\python\plugins\
  4. Enable the SIG-Bus plugin under Plugins → Manage and Install Plugins → Installed.
  5. 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_key and SIG-Bus/geocoding/provider, handled by geocoding_config.py) — never in the project and never in the feed.
  • Provider mode: auto tries Google first when a key is configured and falls back to the OSM cascade; osm ignores 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 to key=***.

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 023 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:

  1. Update version: Edit sig_bus/metadata.txt to increment version=X.Y.Z (keeping qgisMinimumVersion=3.34, qgisMaximumVersion=4.99 and supportsQt6=True — without the maximum, QGIS assumes 3.99 and rejects every QGIS 4.x). The third digit is mandatory: three-digit versions are the standard across the other projects, and test_metadata.py rejects any other format.
  2. Update Changelog: Add release notes under a new version heading in CHANGELOG.md.
  3. Run Test Suite: Execute pytest to verify all tests and guards pass.
  4. Probe the installed QGIS: Run python3 sig_bus/scripts/check_qgis_compat.py against the QGIS you are targeting — it imports every module, builds a QgsField, checks the qualified-enum inventory and loads the .ui, printing OK/FAIL per item.
  5. Package Plugin: Create the distribution .zip archive using qgis-plugin-ci or make package:
  6. Via qgis-plugin-ci: qgis-plugin-ci package <version>
  7. Via make: cd sig_bus && make package VERSION=v<version>
  8. 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 .zip to QGIS Plugin Repository or GitHub Releases.

Author

Diego Camargo — diegocamargo.bft@gmail.com
Repository: https://github.com/d-camargo/sig-bus