How PortoMove collects, processes, and presents transit performance data for Porto's STCP network.
All transit data originates from STCP (Sociedade de Transportes Colectivos do Porto), officially published on the Porto Open Data portal.
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 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.
Individual bus trips are reconstructed from the GPS breadcrumb trail. A new trip is detected when:
Trips with fewer than 3 position samples are discarded as noise.
For each route and direction, we compute headways (time between consecutive trips passing a reference point). From these headways we derive:
When scheduled headway is unknown, the median observed headway is used as the reference.
Each route receives a letter grade based on EWT and headway adherence:
| Grade | EWT | Adherence |
|---|---|---|
| A | < 60 seconds | > 90% |
| B | < 120 seconds | > 80% |
| C | < 180 seconds | > 70% |
| D | < 300 seconds | > 50% |
| F | ≥ 300 seconds | ≤ 50% |
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.
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.
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.