Digitransit APIs require registration. More information
Topics:

High-frequency positioning

The open HFP API can be used to subscribe to vehicle movements in soft real time.

Service Architecture

Most of the vehicles in the HSL area should publish their status, including their position, once per second. The devices of the end users, e.g. smartphones, may subscribe to receive the relevant messages based on their context, e.g. filtered on the mode of transport, the route ID, the geographical region etc. The subscription scope is specified by the MQTT topic structure of the API.

Vehicle positions are also available in GTFS-RT format, see HSL GTFS-RT API documentation

Quickstart

Try this example to get an quick idea of what kind of data is available from the API:

  1. Install MQTT.js command line tools:
npm install -g mqtt
  1. Use MQTT.js to subscribe to HFP messages:
mqtt subscribe -h mqtt.hsl.fi -p 8883 -l mqtts -v -t "/hfp/v2/journey/#"

API endpoints

URL Description
mqtts://mqtt.hsl.fi:8883/ The bare MQTT protocol with TLS, lightweight.
wss://mqtt.hsl.fi:443/ MQTT over WebSockets with TLS, for browsers.
mqtt://mqtt.hsl.fi:1883/ The bare MQTT protocol without TLS, lightweight. Prefer the port 8883 to respect the locational privacy of your users.
ws://mqtt.hsl.fi:1883/ MQTT over WebSockets without TLS. Prefer the port 443 to respect the locational privacy of your users.

Message format

From a client's point of view, every MQTT message has two parts, the topic and the binary payload.

The topic

The topic looks similar to a file path with levels separated by /. When subscribing, all values for a level or several levels may be specified with a wildcard. + refers to all values for one level, # refers to all values of the parent level and its child levels. See the advanced examples further below on how to tailor the subscription to your needs.

Here is an example of an HFP message topic:

/hfp/v2/journey/ongoing/vp/bus/0055/01216/1069/1/Malmi/07:20/1130106/2/60;24/19/73/44

It can be split into these parts:

/<prefix>/<version>/<journey_type>/<temporal_type>/<event_type>/<transport_mode>/<operator_id>/<vehicle_number>/<route_id>/<direction_id>/<headsign>/<start_time>/<next_stop>/<geohash_level>/<geohash>/<sid>/#

Topic levels up to vehicle_number are present in all types of topics and levels after vehicle_number are only present with vehicle journey position messages (i.e. when journey_type is journey).

Note: For future proofing your subscription, add wildcard # to the end of the topic. We may change the HFP topic structure by adding new filters to the end without updating the version number.

Attribute Description
prefix /hfp/ is the root of the topic tree.
version v2 is the current version of the HFP topic and the payload format.
journey_type The type of the journey. Either journey, deadrun or signoff.
journey refers to a vehicle that is running on a specific public transport journey.
deadrun refers to a vehicle that is not on any specific route, but instead coming from a depot, for example.
signoff is used when the vehicle PC is shut down.

Note: deadrun and signoff messages are only available for authorized users.
temporal_type The status of the journey, ongoing or upcoming.
ongoing describes a journey that is currently in operation.
upcoming refers to the next expected journey of the same vehicle. upcoming messages are broadcasted shortly before the start of the next journey. One use of upcoming is to show the relevant vehicle to your users even before the driver has signed on to the journey that your users are interested in.
event_type One of vp, due, arr, dep, ars, pde, pas, wait, doo, doc, tlr, tla, da, dout, ba, bout, vja, vjout.
See descriptions for these values below.
transport_mode The type of the vehicle. One of bus, tram, train, ferry, metro, ubus (used by U-line buses and other vehicles with limited realtime information) or robot (used by robot buses).
operator_id The unique ID of the operator that owns the vehicle. See the list of operators below.
Note: Operator ids must be exactly 4 digits long in the topic filter, so prefix them with zeroes if needed (e.g. 800080)
vehicle_number The vehicle number that can be seen painted on the side of the vehicle, often next to the front door. Different operators may use overlapping vehicle numbers. operator_id/vehicle_number uniquely identifies the vehicle.
Note: Vehicle numbers must be exactly 5 digits long in the topic filter, so prefix them with zeroes if needed.
route_id The ID of the route the vehicle is running on. This matches route_id in GTFS (field gtfsId of Route in the routing API).
direction_id The line direction of the trip, either 1 or 2.
Note: This does not exactly match direction_id in GTFS or the routing API.
Value 1 here is same as 0 in GTFS and the Routing API.
Value 2 here is same as 1 in GTFS and the Routing API.
headsign The destination name, e.g. Aviapolis. Note: This does NOT match trip_headsign in GTFS exactly.
start_time The scheduled start time of the trip, i.e. the scheduled departure time from the first stop of the trip. The format follows HH:mm in 24-hour local time, not the 30-hour overlapping operating days present in GTFS.
next_stop The ID of next stop or station. Updated on each departure from or passing of a stop. EOL (end of line) after final stop and empty if the vehicle is leaving HSL area. Matches stop_id in GTFS (value of gtfsId field, without HSL: prefix, in Stop type in the routing API).
geohash_level The geohash level represents the magnitude of change in the GPS coordinates since the previous message from the same vehicle. More exactly, geohash_level is equal to the minimum of the digit positions of the most significant changed digit in the latitude and the longitude since the previous message. For example, if the previous message has value (60.12345, 25.12345) for (lat, long) and the current message has value (60.12499, 25.12388), then the third digit of the fractional part is the most significant changed digit and geohash_level has value 3.
However, geohash_level value 0 is overloaded. geohash_level is 0 if:
  • the integer part of the latitude or the longitude has changed,
  • the previous or the current message has null for coordinates or
  • the non-location parts of the topic have changed, e.g. when a bus departs from a stop.
By subscribing to specific geohash levels, you can reduce the amount of traffic into the client. By only subscribing to level 0 the client gets the most important status changes. The rough percentages of messages with a specific geohash_level value out of all ongoing messages are:
  • 0: 3 %
  • 1: 0.09 %
  • 2: 0.9 %
  • 3: 8 %
  • 4: 43 %
  • 5: 44 %
geohash The latitude and the longitude of the vehicle. The digits of the integer parts are separated into their own level in the format <lat>;<long>, e.g. 60;24. The digits of the fractional parts are split and interleaved into a custom format so that e.g. (60.123, 24.789) becomes 60;24/17/28/39. This format enables subscribing to specific geographic boundaries easily.
If the coordinates are missing, geohash_level and geohash have the concatenated value 0////.
This geohash scheme is greatly simplified from the original geohash scheme
sid Junction ID, corresponds to sid in the payload. Only available for tlr and tla event types, empty for other event types.

Event types

The most notable change in HFP 2.0 is introduction of different types of messages. Whereas HFP 1.0 had only vehicle position messages, in HFP 2.0 vehicles also send messages from different types of event happening during the journey (for example, arriving to a stop etc.).

Note: events are not available for metros (metro), U-line buses (ubus), robot buses (robot) and ferries (ferry).

List of possible events:

Event type Description
vp Vehicle position
due Vehicle will soon arrive to a stop
arr Vehicle arrives inside of a stop radius
dep Vehicle departs from a stop and leaves the stop radius
ars Vehicle has arrived to a stop
pde Vehicle is ready to depart from a stop
pas Vehicle passes through a stop without stopping
wait Vehicle is waiting at a stop
doo Doors of the vehicle are opened
doc Doors of the vehicle are closed
tlr Vehicle is requesting traffic light priority
tla Vehicle receives a response to traffic light priority request
da Driver signs in to the vehicle
dout Driver signs out of the vehicle
ba Driver selects the block that the vehicle will run
bout Driver signs out from the selected block (usually from a depot)
vja   Vehicle signs in to a service journey (i.e. a single public transport journey from location A to location B, also known as trip)
vjout Vehicle signs off from a service journey, after reaching the final stop

The payload

The payload is an UTF-8 encoded, compact JSON string. The JSON contains a single key-value pair where the key is equivalent to the event type of the message and the value is an object describing the event. Fields of the event object depend on event type.

Here is an example of a vehicle position message (with VP key):

{
  "VP": {
    "desi": "551",
    "dir": "1",
    "oper": 12,
    "veh": 10,
    "tst": "2019-05-23T14:15:16.000Z",
    "tsi": 1416308975,
    "spd": 12.5,
    "hdg": 354,
    "lat": 24.9435,
    "long": 60.1967,
    "acc": -12.34,
    "dl": 600,
    "odo": 45.12,
    "drst": 0,
    "oday": "2019-05-23",
    "jrn": 1,
    "line": 264,
    "start": "13:40",
    "loc": "GPS",
    "stop": null,
    "route": "2551",
    "occu": 0
  }
}

The fields are described below:

Field Type Not available on events Description
desi String da, dout, ba, bout  Route number visible to passengers.
dir String da, dout, ba, bout  Route direction of the trip. After type conversion matches direction_id in GTFS and the topic. Either "1" or "2".
oper Integer  Unique ID of the operator running the trip (i.e. this value can be different than the operator ID in the topic, for example if the service has been subcontracted to another operator).
The unique ID does not have prefix zeroes here.
veh Integer  Vehicle number that can be seen painted on the side of the vehicle, often next to the front door. Different operators may use overlapping vehicle numbers. Matches vehicle_number in the topic except without the prefix zeroes.
tst String UTC timestamp with millisecond precision from the vehicle in ISO 8601 format (yyyy-MM-dd'T'HH:mm:ss.SSSZ).
tsi Integer Unix time in seconds from the vehicle.
spd Floating-point number  Speed of the vehicle, in meters per second (m/s).
hdg Integer  Heading of the vehicle, in degrees (⁰) starting clockwise from geographic north. Valid values are on the closed interval [0, 360].
lat Floating-point number WGS 84 latitude in degrees.
null if location is unavailable.
long Floating-point number WGS 84 longitude in degrees.
null if location is unavailable.
acc Floating-point number  Acceleration (m/s^2), calculated from the speed on this and the previous message. Negative values indicate that the speed of the vehicle is decreasing.
dl Integer da, dout, ba, bout Offset from the scheduled timetable in seconds (s). Negative values indicate lagging behind the schedule, positive values running ahead of schedule.
odo Integer The odometer reading in meters (m) since the start of the trip. Currently the values not very reliable.
drst Integer Door status.
0 if all the doors are closed.
1 if any of the doors are open.
oday String da, dout Operating day of the trip. The exact time when an operating day ends depends on the route. For most routes, the operating day ends at 4:30 AM on the next day. In that case, for example, the final moment of the operating day "2018-04-05" would be at 2018-04-06T04:30 local time.
jrn Integer da, dout, ba, bout Internal journey descriptor, not meant to be useful for external use.
line Integer da, dout, ba, bout  Internal line descriptor, not meant to be useful for external use.
start String da, dout, ba, bout Scheduled start time of the trip, i.e. the scheduled departure time from the first stop of the trip. The format follows HH:mm in 24-hour local time, not the 30-hour overlapping operating days present in GTFS. Matches start_time in the topic.
loc String Location source, either GPS, ODO, MAN, DR or N/A.
  • GPS - location is received from GPS
  • ODO - location is calculated based on odometer value
  • MAN - location is specified manually
  • DR - location is calculated using dead reckoning (used in tunnels and other locations without GPS signal)
  • N/A - location is unavailable
stop  String da, dout, ba, bout ID of the stop related to the event (e.g. ID of the stop where the vehicle departed from in case of dep event or the stop where the vehicle currently is in case of vp event).
null if the event is not related to any stop.
route  String da, dout, ba, bout ID of the route the vehicle is currently running on. Matches route_id in the topic.
occu Integer da, dout, ba, bout Integer describing passenger occupancy level of the vehicle. Valid values are on interval [0, 100].

Currently passenger occupancy level is only available for Suomenlinna ferries as a proof-of-concept. The value will be available shortly after departure when the ferry operator has registered passenger count for the journey.

For other vehicles, currently only values used are 0 (= vehicle has space and is accepting passengers) and 100 (= vehicle is full and might not accept passengers)
 seq Integer Sequence number of the unit when the journey is operated with a vehicle that consists of multiple units (e.g. metros, trains). Sequence number starts from 1.
Note: seq is currently only available for metros.
label  String User visible label that helps to identify the vehicle. Currently available only for Suomenlinna ferries with values being vessel names.
ttarr  String vp, da, dout, ba, bout, vja, vjout UTC timestamp of scheduled arrival time to the stop
ttdep  String vp, da, dout, ba, bout, vja, vjout UTC timestamp of scheduled departure time from the stop
 dr-type Integer Other than da, dout, ba, bout, vja, vjout  Type of the driver, either 0 or 1.
  • 0 = service technician
  • 1 = normal driver
tlp-requestid  Integer Other than tlr, tla Traffic light priority request ID. Valid values are on interval [0, 255].
tlp-requesttype String Other than tlr Traffic light priority request type, either NORMAL, DOOR_CLOSE, DOOR_OPEN or ADVANCE.
tlp-prioritylevel String Other than tlr Priority level of a traffic light priority request. Either normal, high or norequest.
tlp-reason  String  Other than tlr Reason for not sending a traffic light priority request. Either GLOBAL, AHEAD, LINE or PRIOEXEP.
 tlp-att-seq  Integer Other than tlr Traffic light priority request attempt sequence number
tlp-decision String Other than tla Response for traffic light priority request. Either ACK or NAK.

Also the following fields are available on traffic light priority request events (event type tlr), but they probably have no use for 3rd party users:

Field Type Description
sid Integer Junction ID
signal-groupid Integer Signal group (a group of traffic lights at a junction) ID
tlp-signalgroupnbr Integer ID of the specific traffic light in a signal group. Possibly negative
tlp-line-configid Integer ID of the line configuration in DOI
tlp-point-configid  Integer Point configuration ID
tlp-frequency Integer Frequency used for traffic light prority request
tlp-protocol String Protocol used for traffic light priority request. Either MQTT or KAR-MQTT

Operators

The numerical values for the different transit operators are listed below:

oper Operator name
6 Oy Pohjolan Liikenne Ab2
12 Helsingin Bussiliikenne Oy
17 Tammelundin Liikenne Oy
18 Oy Pohjolan Liikenne Ab
20 Bus Travel Åbergin Linja Oy
21 Bus Travel Oy Reissu Ruoti
22 Nobina Finland Oy
30 Savonlinja Oy
36 Nurmijärven Linja Oy
40 HKL-Raitioliikenne
47 Taksikuljetus Oy
50 HKL-Metroliikenne
51 Korsisaari Oy
54 V-S Bussipalvelut Oy
58 Koillisen Liikennepalvelut Oy
60 Suomenlinnan Liikenne Oy
59 Tilausliikenne Nikkanen Oy
89 Metropolia
90 VR Oy
130 Matkahuolto1
195 Siuntio1

1Multiple smaller operators operate under this operator ID
2This value is present only in the oper field of the payload. Operator 6 is the same as 18.

Examples

Topics

The HFP topic format forms a tree. By combining wildcards with several topic filters in one MQTT SUBSCRIBE packet you can carve quite interesting subsets of the tree to serve different use cases. As the MQTT broker handles the resolving of the topic filters, handling messages for complicated subscriptions does not have inherent overhead in the client compared to simple subscriptions. Go hog wild.

Command line

Below are sample subscriptions utilizing MQTT.js command line tools.
MQTT.js command line tools can be installed with:

npm install -g mqtt

If you insist on using mosquitto, try this for TLS access:

mosquitto_sub --capath "/etc/ssl/certs/" -h mqtt.hsl.fi -p 8883 -v -t "/hfp/v2/journey/#"

A situational overview

To get just the most significant vehicle position updates, use:

mqtt subscribe -h mqtt.hsl.fi -l mqtts -p 8883 -v \
  -t "/hfp/v2/journey/ongoing/vp/+/+/+/+/+/+/+/+/0/#"

A route in one direction

To subscribe to all vehicle position messages from vehicles currently on the route 551 (route_short_name in GTFS) going in direction 1, subscribe to the corresponding route_id 2551:

mqtt subscribe -h mqtt.hsl.fi -l mqtts -p 8883 -v \
  -t "/hfp/v2/journey/ongoing/vp/+/+/+/2551/1/#"

All trams

Subscribe to all vehicle position messages from trams with:

mqtt subscribe -h mqtt.hsl.fi -l mqtts -p 8883 -v \
  -t "/hfp/v2/journey/ongoing/vp/tram/#"

A certain trip

Subscribe to vehicle position messages of a certain trip, even slightly before the driver has signed onto the trip:

mqtt subscribe -h mqtt.hsl.fi -l mqtts -p 8883 -v \
  -t "/hfp/v2/journey/+/vp/+/+/+/1069/1/+/07:20/#"

Or if your users would find it confusing to see a vehicle going in the wrong direction, subscribe to the ongoing messages only:

mqtt subscribe -h mqtt.hsl.fi -l mqtts -p 8883 -v \
  -t "/hfp/v2/journey/ongoing/vp/+/+/+/1069/1/+/07:20/#"

All vehicles arriving to a certain stop

To subscribe to all vehicles arriving to a certain stop, use:

mqtt subscribe -h mqtt.hsl.fi -l mqtts -p 8883 -v \
  -t "/hfp/v2/journey/ongoing/arr/+/+/+/+/+/+/+/1293140/#"

Note: depending on the current traffic situation, you might have to wait a while to receive data

Door status of a certain vehicle

To subscribe to door status of a certain vehicle, use:

mqtt subscribe -h mqtt.hsl.fi -l mqtts -p 8883 -v \
  -t "/hfp/v2/journey/ongoing/doo/+/0012/01312/#" \
  -t "/hfp/v2/journey/ongoing/doc/+/0012/01312/#"

Note: you will have to change operator ID and vehicle number in the topic if vehicle 0012/01312 is not running currently.

A bounding box

  • See this example on how to generate topic filters for a bounding box

Let's assume that you wish to subscribe to all action inside the following GeoJSON Polygon:

{
  "type": "Feature",
  "geometry": {
    "type": "Polygon",
    "coordinates": [
      [
        [24.9578905105, 60.1836538254],
        [24.9646711349, 60.1836538254],
        [24.9646711349, 60.1894146967],
        [24.9578905105, 60.1894146967],
        [24.9578905105, 60.1836538254]
      ]
    ]
  }
}

The box bounded by the latitude interval [60.18, 60.19[ and the longitude interval [24.95, 24.97[ corresponds with the following HFP subscription:

mqtt subscribe -h mqtt.hsl.fi -l mqtts -p 8883 -v \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/#"

For the precision of one more digit of latitude and longitude, one would need 56 topic filters for the bounding box:

mqtt subscribe -h mqtt.hsl.fi -l mqtts -p 8883 -v \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/37/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/38/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/39/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/30/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/31/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/32/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/33/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/34/#" \
  \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/47/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/48/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/49/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/40/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/41/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/42/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/43/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/44/#" \
  \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/57/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/58/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/59/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/50/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/51/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/52/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/53/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/54/#" \
  \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/67/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/68/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/69/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/60/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/61/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/62/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/63/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/64/#" \
  \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/77/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/78/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/79/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/70/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/71/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/72/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/73/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/74/#" \
  \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/87/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/88/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/89/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/80/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/81/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/82/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/83/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/84/#" \
  \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/97/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/98/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/85/99/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/90/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/91/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/92/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/93/#" \
  -t "/hfp/v2/journey/ongoing/+/+/+/+/+/+/+/+/+/+/60;24/19/86/94/#"

There is no need to restrict yourself to just one rectangle like above, though.

For example, you could try to generate an HFP subscription for all ongoing vehicles in the minimal geographic area encompassing the Kontula borough with the precision of two digits in the fractional part.

Querying a trip corresponding to a vehicle position

The Routing API can be used to query a trip correspoding to a vehicle position message.

  • Query type fuzzyTrip can be used to query a trip without its id, if other details uniquely identifying the trip are available

For example, from the following vehicle position message

{
  "VP": {
    "desi":"550",
    "dir":"1",
    "oper":12,
    "veh":1306,
    "tst":"2019-06-28T09:49:01.457Z",
    "tsi":1561715341,
    "spd":12.29,
    "hdg":47,
    "lat":60.182376,
    "long":24.825781,
    "acc":0.44,
    "dl":-2,
    "odo":24627,
    "drst":0,
    "oday":"2019-06-28",
    "jrn":99,
    "line":261,
    "start":"11:57",
    "loc":"GPS",
    "stop":null,
    "route":"2550",
    "occu":0
  }
}

it is possible to parse:

  • Route id from the message: 2550
  • Direction id from the topic: 1
  • Departure time from the message: 11:57
  • Departure date from the message: 2019-06-28

Note:

  1. Vehicle position messages use different direction id than the Routing API

    • Direction id 1 in a vehicle position is same as direction id 0 in the Routing API
    • Direction id 2 in a vehicle position is same as direction id 1 in the Routing API
  2. Departure time must be in seconds

    • e.g. 11:57 = 11 * 60 * 60 + 57 * 60 = 43020
    • If the date in fields oday and tst is not the same and the departure time (start) is earlier than the time in tst, add 86400 seconds to departure time

      • This is due to differences in time formats, when vehicles which have departed after midnight have the previous date as operating day
      • e.g.

        • tst = 2018-08-16T00:15:00.836Z (note that this is in UTC time)
        • oday = 2018-08-15
        • start = 03:10
        • 03:10 = 3 * 60 * 60 + 10 * 60 + 86400 = 97800
  3. Due to a bug in the vehicle position API, some route ids don't match the route id in the routing API

    • In this case, fuzzyTrip query returns null

For example, the following query checks if the vehicle, which sent the vehicle position message above, is wheelchair accessible:

{
  fuzzyTrip(route: "HSL:2550", direction: 0, date: "2019-06-28", time: 43020) {
    route {
      shortName
    }
    wheelchairAccessible
  }
}

Further reading

  • The MQTT Essentials series introduces the MQTT protocol in more detail.
  • LightMQTT is an MQTT client library written in Swift by one of the developers in the HSL developer community.
© Digitransit 2024