Live Train Tracker Map: What the Data Can and Cannot Show
Contents
No single feed entity carries everything a live train tracker map would need, and the specification says so outright. One published specification defines a common format for live transit data: GTFS Realtime, whose .proto file, copyright 2015 The GTFS Specifications Authors, states the purpose in one sentence: “GTFS Realtime lets transit agencies provide consumers with realtime information about disruptions to their service (stations closed, lines not operating, important delays etc), location of their vehicles and expected arrival times.”
The specification defines three core feed entity types, and a single feed entity carries one and only one of them. Nothing obliges an operator to publish all three. That answers the real question behind the hunt: why one screen shows a train crawling across a map while another shows the same train only as minutes late. Those are separate publications, and no app can render what was not published.
Key takeaways
- GTFS Realtime is a public specification, so what such a feed can carry is documented.
- A feed entity carries exactly one thing — the proto reads “Exactly one of the following fields must be present (unless the entity is being deleted)”.
- A moving dot needs a VehiclePosition; a “minutes late” figure needs a TripUpdate. Publishing one does not imply the other.
- Silence is not reassurance — the trip updates documentation states “The data consumer should not assume that the trip is running on time.”
- Best practice asks for a refresh at least once every 30 seconds, with data no older than 90 seconds for trip updates and vehicle positions.
A live train tracker map is a rendering, not a source
The map is the last step, not the first. GTFS is two specifications, and the documentation overview maintained by MobilityData separates them: GTFS Schedule defines a format for “static public transportation information”, while GTFS Realtime provides “current arrival and departure times, service alerts, and vehicle position.” The live half cannot stand alone: of a stop identifier, the specification says “The value must be the same as in stops.txt in the corresponding GTFS feed.”
Delivery makes the same point: best practice guidance on gtfs.org, maintained by MobilityData, says feeds should provide “protocol buffer-encoded feed content” over HTTP. A feed is binary, not a web page: something must decode it, match it to the timetable and draw it.
One entity, one job — and never more than one
This is the fact the subject turns on. The proto defines the three core types in one line each. TripUpdate is a “Realtime update of the progress of a vehicle along a trip.” VehiclePosition is “Realtime positioning information for a given vehicle.” Alert is “An alert, indicating some sort of incident in the public transit network.” It then constrains how they combine: “Exactly one of the following fields must be present (unless the entity is being deleted).”
The GTFS Realtime Reference on gtfs.org states the rule as a floor — “At least one of the fields trip_update, vehicle, alert, or shape must be provided” — and names “stop” and “trip_modification” among the options too. Either way, position, prediction and disruption text are independent publications. An operator can publish delays with no vehicles, or vehicles with no delays, and stay compliant.
| Feed entity | Specification wording | What a map can draw from it |
|---|---|---|
| VehiclePosition | “Realtime positioning information for a given vehicle.” (.proto) | A dot; heading or speed only where those optional fields exist |
| TripUpdate | “Realtime update on the progress of a vehicle along a trip.” (Reference) | An arrival time or delay at listed stops — no location |
| Alert | “An alert, indicating some sort of incident in the public transit network.” (.proto) | Disruption text with a cause and effect from a fixed list |
What a vehicle position can and cannot tell you
A dot has a source and an age. The vehicle positions documentation describes the entity as “automatically generated information on the location of a vehicle, such as from a GPS device on board.” Coordinates are given in “Degrees North, in the WGS-84 coordinate system”, and the specification timestamps the moment “at which the vehicle’s position was measured” separately from the feed itself.
The second structural surprise: publishing a VehiclePosition does not guarantee coordinates. The proto marks the vehicle’s position — “Current position of this vehicle.” — as optional, so a vehicle entity can exist carrying no location at all. Where a position is present, the specification states that “Latitude and longitude are required, the other fields are optional” — bearing, odometer and speed among them. That is why one tracker draws an arrow with a speed readout and another a plain circle.
The proto supplies the words beside a moving train: “INCOMING_AT: The vehicle is just about to arrive at the stop (on a stop display, the vehicle symbol typically flashes).”, “STOPPED_AT: The vehicle is standing at the stop.” and “IN_TRANSIT_TO: The vehicle has departed and is in transit to the next stop.” Absent that field there is a documented default: “If current_status is missing IN_TRANSIT_TO is assumed.”
What a delay number is actually made of
A prediction is a small structure, not one number. Trip updates “represent fluctuations in the timetable”, and each “should contain either an absolute time or a delay (i.e. an offset from the scheduled time in seconds).” That is the difference between a screen showing a clock time and one showing a plus-or-minus figure. Delay is signed: the specification says it “can be positive (meaning that the vehicle is late) or negative (meaning that the vehicle is ahead of schedule)”, so a train shown early is a reported value.
Feeds need not speak about every stop. A value will “apply for all the following stops of the trip up to the next specified one”, so one figure propagates forward until something replaces it. Propagation depends on easily confused flags: “updates with a schedule relationship of SKIPPED will not stop delay propagation, but updates with schedule relationships of SCHEDULED (also the default value if schedule relationship is not provided) or NO_DATA will.” SKIPPED itself means “the vehicle will not stop at this stop.”
Two limits close the picture. Delay “can only be used in case the trip update refers to a scheduled GTFS trip, as opposed to a frequency-based trip”. And uncertainty — “the expected error in true delay as an integer in seconds” — is no promise when absent: “If uncertainty is omitted, it is interpreted as unknown.”
Pro tip
When two screens disagree, ask which entity each is reading. A view built on TripUpdate can be exact about a delay while knowing nothing about where the train is; one built on VehiclePosition can show movement but no prediction for your stop. The Reference encourages a per-update timestamp “in order to evaluate the freshness of the data” — so a screen showing the age of its own data tells you more than a confident number does.
Why “no update” never means “on time”
A gap in the data is easy to misread. The trip updates documentation is direct: “In case there is no trip update for a scheduled trip, it will be concluded that no realtime data is available for the trip.” It then closes the optimistic reading: “The data consumer should not assume that the trip is running on time.” A blank space on a live train tracker map is a statement about the feed, not the train, and whatever an interface layers over that silence is an editorial choice.
The third entity: words rather than dots
Alerts are the channel for disruption. The service alerts documentation describes them as a way to “provide updates whenever there is disruption on the network”. They carry a controlled vocabulary. The effect values are: No service, Reduced service, Significant delays, Detour, Additional service, Modified service, Stop moved, Other effect, Unknown effect, No effect, Accessibility issue. The causes are: Unknown cause, Other cause, Technical problem, Strike, Demonstration, Accident, Holiday, Weather, Maintenance, Construction, Police activity, Medical emergency, Special Event.
Alerts are targeted, too, through an informed entity selector: “When multiple fields are included in one informed_entity, they should be interpreted as being joined by the AND logical operator.” Adding detail narrows an alert rather than widening it.
Freshness is the other half of accuracy
Live data is a snapshot with an expiry. Best practice guidance recommends feeds “should be refreshed at least once every 30 seconds, or whenever the information represented within the feed (position of a vehicle) changes, whichever is more frequent”, and that data “should not be older than 90 seconds for Trip Updates and Vehicle Positions and not older than 10 minutes for Service Alerts.” Positions change fastest: they “tend to change more frequently than other feed entities and should be updated as frequently as possible.”
Each fetch is complete in itself. Feeds “are considered to be stateless, meaning that each feed reflects the entire real-time state of the transit system”, so a tracker that loses signal can be correct again on the next request. Clocks matter as much as intervals: the Reference advises deriving the timestamp “from a time server” to avoid time skew. A minute of clock error looks exactly like a minute of delay.
Common mistake
Treating a stationary dot as a stopped train. A dot is a measurement with its own timestamp, and the spec makes optional several things readers assume are guaranteed: the position on a vehicle entity, the bearing and speed inside a position, the uncertainty on a prediction. A dot that has not moved may be a train at a platform — or the last measurement published.
Frequently asked questions
Why does one tracker show a moving train while another shows only a delay?
Because those are different feed entities. A moving dot comes from a VehiclePosition, defined in the .proto file as “Realtime positioning information for a given vehicle.”; a delay comes from a TripUpdate. A feed entity carries exactly one, and only one entity field has to be provided.
Does a missing entry mean the train is running to time?
No. Where there is no trip update for a scheduled trip, the documentation says “it will be concluded that no realtime data is available for the trip”, and adds that “The data consumer should not assume that the trip is running on time.”
Why do some trackers show a heading arrow and a speed while others show only a dot?
Because those fields are optional. The specification states that “Latitude and longitude are required, the other fields are optional”, so bearing and speed may be absent. Nothing can display a heading nobody published.
How fresh should the data behind a live tracker be?
Best practice asks for a refresh at least once every 30 seconds, or whenever a vehicle position changes, whichever is more frequent, and says data should be no older than 90 seconds for trip updates and vehicle positions, or 10 minutes for alerts. That guidance can be revised, so check current documentation.
Why can two views of the same train disagree about later stops?
Because of how values carry forward. A value applies “for all the following stops of the trip up to the next specified one”, and propagation differs by flag: updates marked SKIPPED “will not stop delay propagation”, while SCHEDULED — the default — or NO_DATA will.
What is the difference between GTFS Schedule and GTFS Realtime?
The documentation overview covers GTFS Schedule as “static public transportation information” and GTFS Realtime as “current arrival and departure times, service alerts, and vehicle position.” Realtime messages reference the static files directly, so a tracker needs both.