Metro transit detection

Where Is Tereza? auto-classifies every GPS point into a transport mode — walking, running, cycling, driving. Metro transit detection is the special-case layer that recognises when you've boarded an underground train and renders the segment as a dashed public-transport line instead of a straight gold line across the city.

Why it matters

Underground sections are GPS dead zones. Without detection, a metro ride from one station to another would either:

  • Leave a long gap in the route (no points between stations), or
  • Snap to whatever surface road happens to lie above the tunnel — which is rarely the actual line.

The detector recognises the pattern (last GPS point at a station entrance, gap of 1–25 minutes, next point at a station entrance 0.5–15 km away) and labels the connecting segment as public_transport, drawing it as a dashed line.

How it works

  1. Station database. A built-in list of station coordinates per city (see Coverage below). Each entry is a (latitude, longitude, name, line) tuple.
  2. Bounding-box pre-check. For each candidate GPS pair we first check which city's footprint they could fall in. Stations in other cities are skipped — adding more cities scales linearly only inside the city the GPS point is actually in.
  3. Detection criteria. A segment qualifies as metro travel when:
    • both endpoints are within 350 m of different stations;
    • the time gap is 60 s – 1500 s (1–25 minutes);
    • the straight-line distance is 0.5 km – 15 km;
    • both stations belong to the same city (no inter-city teleport).
  4. Classification. Matching segments get mode: public_transport and render as a dashed line on the public tracker map, distinct from the walking / cycling / driving polylines.

Coverage

Live as of build 0.2.1042. Numbers reflect the curated station list shipped in backend/services/metro.py. The detector covers the busiest central + interchange stations; less-used outer terminals may be missing for now.

City Country System Stations Lines Bounding box (lat / lon)
Prague CZ Metro 61 A, B, C 49.95–50.17 / 14.24–14.60
London UK Underground 67 Bakerloo, Central, Circle, District, Jubilee, Northern, Piccadilly, Victoria 51.41–51.58 / −0.28–0.03
Paris FR Métro 81 L1, L2, L4, L6, L7, L9, L10, L11, L12, L13, L14 48.77–48.93 / 2.23–2.44
Berlin DE U-Bahn 66 U1, U2, U3, U4, U6, U7, U8, U9 52.43–52.62 / 13.22–13.50
Barcelona ES Metro 48 L1, L2, L3, L4, L5 41.32–41.47 / 2.09–2.26
Madrid ES Metro 67 L1, L2, L3, L4, L5, L6, L8, L9, L10 40.34–40.54 / −3.78–−3.52
Vienna AT U-Bahn 77 U1, U2, U3, U4, U6 48.06–48.38 / 16.26–16.53
Total 467

The bounding boxes are auto-derived from the stations themselves plus a 0.05° (~5 km) margin. If you walk just outside a city's bbox (e.g. the airport), the detector simply skips that city — no false positives.

Adding a city

Internally, extending the detector is a one-file change in backend/services/metro.py:

  1. Add a (lat, lon, name, line) list per city — one tuple per station entrance.
  2. Append a CITIES entry: {"name", "country", "system", "stations": YOUR_LIST}.
  3. The bbox is auto-computed; no further config needed.

For end-users: spotted a city we don't cover? File a bug report from the docs footer with the name + a sample trip URL where the metro segments are missing — we add cities in priority order based on usage data.

What it gets right

  • Single-line rides between adjacent stations.
  • Multi-line rides with one or two transfers (each transfer surfaces briefly in the station mezzanine, generates new GPS in passing and re-anchors the detection).
  • Trips where the user pulled their phone out at the platform and posted a photo — the photo location lands at the platform, the algorithm snaps the segment to that station.

What it gets wrong

  • Brand-new stations whose coordinates aren't in our database yet — the segment falls back to a straight gold line.
  • Surface-running suburban rail. S-Bahn, RER, London Overground etc. are surface trains; GPS works fine on those. We deliberately exclude them from the metro lists so they don't get mis-detected as underground when GPS dropped for an unrelated reason.
  • Bus tunnels (Boston, San Francisco, Bratislava have a few) surface to street and are classified as driving / public_transport, not metro.

In your stats

Metro segments contribute to:

  • Total distance as the straight-line distance between stations, not the actual underground tunnel length (we don't have tunnel geometry data — typical error 5–15 %).
  • Active time is excluded from stop / rest detection. Metro rides DON'T count as a stop even though the user is sitting still.
  • Naismith equivalent (terrain-adjusted distance) treats metro rides as flat ground, regardless of the actual underground gradient.

Plan availability

The detector itself runs on every plan today. Per-plan gating is on the roadmap — see the platform admin "Metro transit detection" feature flag.


Need help? Contact support · Where Is Tereza?