How PortoMove collects, processes, and presents transit performance data for Porto's STCP network.

Data Sources

All transit data originates from STCP (Sociedade de Transportes Colectivos do Porto), officially published on the Porto Open Data portal.

  • Bus positions — FIWARE Urban Platform (NGSI v2). Real-time GPS snapshots of ~400–600 STCP buses, collected every 30 seconds by our worker process. Each record includes: vehicle ID, lat/lon, speed, heading, route, trip ID, direction.
  • Route patterns & schedules — Porto OpenTripPlanner GraphQL API. Provides route geometries (polylines), stop locations, and scheduled departure times.
  • Bike infrastructure — Explore Porto API. Bike parks and bike lanes (ciclovias).

Data Collection

A dedicated worker process running on Fly.io queries the FIWARE API every 30 seconds and writes each bus position to a PostgreSQL database (Neon). Raw position data is retained for 24 hours; after daily aggregation, it is cleaned up to manage storage costs.

On a typical day, the collector records ~100,000–150,000 position samples across ~120 active vehicles and ~49 routes.

Route Segments

Route polylines from OTP are split into ~200-meter segments. Each GPS position is snapped to the nearest segment (within 150m) using the Haversine formula. This allows us to compute speed statistics at a granular geographic level.

Trip Reconstruction

Individual bus trips are reconstructed from the GPS breadcrumb trail. A new trip is detected when:

  • The trip ID changes between consecutive positions
  • There is a gap of more than 10 minutes between consecutive positions

Trips with fewer than 3 position samples are discarded as noise.

Headway Metrics

For each route and direction, we compute headways (time between consecutive trips passing a reference point). From these headways we derive:

  • Average Wait Time (AWT) = Σ(H²) / (2 · ΣH), where H is the observed headway. This accounts for the fact that passengers are more likely to arrive during long gaps.
  • Scheduled Wait Time (SWT) = scheduled headway / 2 (for perfectly regular service).
  • Excess Wait Time (EWT) = AWT − SWT. The additional time passengers wait beyond what the schedule promises.
  • Headway Adherence — percentage of headways within the scheduled headway + 3 minutes.
  • Bunching — percentage of headways below 50% of scheduled (buses arriving too close together).
  • Gapping — percentage of headways above 150% of scheduled (long gaps in service).

When scheduled headway is unknown, the median observed headway is used as the reference.

Grading Scale

Each route receives a letter grade based on EWT and headway adherence:

GradeEWTAdherence
A< 60 seconds> 90%
B< 120 seconds> 80%
C< 180 seconds> 70%
D< 300 seconds> 50%
F≥ 300 seconds≤ 50%

Velocity Heatmap

The heatmap colors each ~200m route segment by average commercial speed. Speed is computed from the GPS-reported speed field in the FIWARE data, aggregated hourly per segment. Percentiles (p10, median, p90) are also computed to show speed variability.

Color scale: red (≤5 km/h) through orange, yellow, lime, to green (≥25 km/h). Gray segments have no data for the selected period.

Data Retention & Freshness

Raw GPS positions are retained for 24 hours in the database. Each night at 03:00 UTC, the aggregation pipeline runs to compute daily route performance metrics and hourly segment speeds. At 04:00 UTC, positions older than 24 hours are cleaned up.

Route segment definitions are refreshed weekly from OTP to capture any route changes.

Limitations

  • GPS accuracy varies; positions may be off by 10–30 meters, affecting segment snapping.
  • The FIWARE API occasionally has gaps or delays, which may cause some trips to be split or missed entirely.
  • Scheduled headways are not always available from OTP, so we fall back to median observed headway as the reference.
  • Speed data reflects commercial speed (including dwell time at stops), not free-flow traffic speed.
  • Only STCP buses are tracked. Metro, Fertagus, and other operators are not included in the position data.

Open Data

All aggregated data is available for download on the data page. Formats include JSON, CSV, and GeoJSON. We encourage researchers, journalists, and civic hackers to use this data and help improve public transit in Porto.

If you find errors or have suggestions, please open an issue on our GitHub repository.