Traffic Volume

Service version: 1
Last edit: 2026.03.23

Important Note
Explore ready-to-use traffic reports and data visualizations immediately by signing up for a 30-day free trial on the MOVE Portal. Once registered, you'll receive an API key to start using the Traffic Analytics APIs right away. Alternatively, you may contact our Sales team for a tailored solution.

Purpose

The Traffic Stats API Traffic Volume service provides estimated historical vehicle counts for road segments within an area. This service is asynchronous.

Unlike Traffic Density, which reports the number of observed probe samples, Traffic Volume uses a statistical model to estimate the total number of vehicle passages — including vehicles not captured by probe data. The result represents a model-based estimate of all historical traffic, not just the sampled portion.

There are two endpoints consuming two types of requests. The first one is only for ordering jobs with POST requests. In its response the user receives a jobId, which should be used in the second endpoint with a GET request to the check job's status. When the job is successfully finished, the user will receive links in the response to download the results.

Using this service

There are some important notes about using this API service:

  • The latitude and longitude coordinates are in EPSG4326 / WGS84 format for both input and output. Other coordinate systems are not supported.
  • The current API version is 1.
  • The maximum length of date range is 366 days.
  • The maximum number of time sets is 24.
  • Network size, calculated as the size of the convex hull of a defined geometry, cannot be greater than 20000 km².
  • As the service is asynchronous it can take a while before results are available, depending on how busy the service is. Do not repost your request multiple times if you do not get feedback within minutes, we will ensure that the results are delivered.
  • At the same time you can have only 5 jobs in CALCULATIONS and SCHEDULED statuses per each developer key. When you reach this limit you can still create new jobs as they will be queued until at least one of currently running jobs is processed.
  • The map version is always selected automatically by the system based on the requested date range.
  • Traffic Volume data always covers all vehicle types — both passenger and fleet vehicles combined. The data composition cannot be changed.
  • Segment results in the GeoJSON output may include optional third-party ID mappings (openLr, gersIdMapping, osmIdMapping). These fields are only present when enabled at the contract level and are not available in SHAPE format.

Traffic Volume Coverage

Traffic Volume data is available on the Orbis map only. Coverage is limited to the countries listed below.

Americas

Market

Country code

Available from

United StatesUS2023

Asia Pacific

Market

Country code

Available from

AustraliaAU2024

Europe

Market

Country code

Available from

BelgiumBE2024
NetherlandsNL2024
NorwayNO2024
SwedenSE2024
United KingdomGB2024

Request data

HTTPS method: POST

Constants and parameters enclosed in curly brackets { } must be replaced with their values.

URL format

post
URL request format
https://api.tomtom.com/traffic/trafficstats/trafficvolume/1?key={Your_API_Key}

curl format

post
curl request format
curl -X POST -H"Content-type: application/json" -d'{request body here}' 'https://api.tomtom.com/traffic/trafficstats/trafficvolume/1?key={Your_API_Key}'

Required headers

HeaderValue
Content-Type

application/json

Request parameters

The following table describes all of the parameters that can be used in a request. Required parameters must be used or the call will fail. Optional parameters may be used.

Required parametersDescription

key
string

Authorization key for access to the API.
Value: Your valid API Key.

Request POST body example - JSON

post
Request POST body - JSON
1{
2 "jobName": "Test job",
3 "distanceUnit": "KILOMETERS",
4 "acceptMode": "MANUAL",
5 "network": {
6 "name": "City of London",
7 "geometry": {
8 "type": "Polygon",
9 "coordinates": [
10 [
11 [-0.1004, 51.5123],
12 [-0.0932, 51.5123],
13 [-0.0932, 51.5178],
14 [-0.1004, 51.5178],
15 [-0.1004, 51.5123]
16 ]
17 ]
18 },
19 "timeZoneId": "Europe/London",
20 "frcs": [0, 1, 2, 3, 4]
21 },
22 "dateRange": {
23 "name": "Wednesday 15 January 2025",
24 "from": "2025-01-15",
25 "to": "2025-01-15"
26 },
27 "timeSets": [
28 {
29 "name": "Wednesday morning peak",
30 "timeGroups": [
31 {
32 "days": ["WED"],
33 "times": ["7:00-9:00"]
34 }
35 ]
36 }
37 ]
38}

Request POST body parameters - JSON

The following JSON parameters refer to the POST request body.

Required parametersDescription

jobName
string

Job name. Given for the user's convenience.
Value: The job name.

distanceUnit
string

Base unit used for distance and speed values.
Values:

  • KILOMETERS
  • MILES

network
object

Network for calculations. See the Structure of network section.
Value: An object defining network.

dateRange
object

Range of dates for calculations. See the Structure of dateRange section.
Value: An object containing date range.

timeSets array

Time sets for calculations. See the Structure of timeSets section.
Value: An array containing time sets.

Optional parametersDescription

acceptMode string

Defines whether the job should be accepted manually or automatically. Default value: AUTO
Other value: MANUAL

Structure of network

Required parametersDescription

name
string

Name of the network.
Value: A network name.

timeZoneId string

In which time zone all times are given. Value: A time zone like: Europe/Amsterdam or UTC. See more.

frcs
array (of integers)

Functional road classes.
Value: A list of integers from range 0-8

geometry
object

Geometry of the network in GeoJSON format. Supported geometry types are Polygon and MultiPolygon. See RFC 7946. Value: An object defining geometry.

Structure of dateRange

Required parametersDescription

name
string

Date range name. Given for the user's convenience.
Value: A date range name. For example: "Last working week of January".

from
date

The date from (inclusive).
Value: Date in the format: YYYY-MM-DD

to
date

The date to (inclusive).
Value: Date in the format: YYYY-MM-DD The to date must be no later than 3 days before today.

Optional parametersDescription

exclusions
array

List of days excluded from the date range.
Value: List of dates in the format: YYYY-MM-DD.

excludedDaysOfWeek
array

List of days of the week to be excluded.
Values:

  • MON

  • TUE

  • WED

  • THU

  • FRI

  • SAT

  • SUN

Structure of timeSets

Required parametersDescription

name
string

Time set name.
Value: Name given for the user's convenience.

timeGroups
array

List of time groups. See the Structure of timeGroups section.

Structure of timeGroups

Required parametersDescription

days array

Days of week for the time group. List of values selected from:

  • MON

  • TUE

  • WED

  • THU

  • FRI

  • SAT

  • SUN

times array

Time ranges for the time group. A list of values in the format: HH:mm-HH:mm. Use 00:00-24:00 for whole day. Start and end times must be on the hour (e.g. 01:00-03:00 is valid; 00:30-01:30 will be rejected).

Response data

In the response to the request a jobId is provided, which is required for further communication about the query. A JSON response content example:

Response body - JSON
1{
2 "jobId": "677",
3 "responseStatus": "OK",
4 "messages": ["Job created. Processing started."]
5}

Check job status

When a job has been initiated via the API request, it is possible to check the status.

Job status request

get
Job status URL request format
https://api.tomtom.com/traffic/trafficstats/status/1/{job_id}?key={Your_API_Key}

Job status request parameters

The following table describes all of the parameters that can be used in a request. Required parameters must be used or the call will fail. Optional parameters may be used.

Required parametersDescription

jobId
integer

The job ID.
Value: Example: 123

key
string

Authorization key for access to the API.
Value: Your valid API Key.

JSON response

Response body - JSON
1{
2 "jobId": "{job_id}",
3 "jobState": "{job_status}",
4 "responseStatus": "OK"
5}

Job status flow

During the process there are different stages applicable. Via the get state request you can see what the state of your job is. The following table shows the different stages of the process.

StatusDescription
NEWThe job is waiting for scheduling.
SCHEDULEDJob is scheduled for processing.
MAPMATCHINGMapmatching is in progress.
MAPMATCHEDMapmatching is done and the job is waiting for Geobase reading.
READING_GEOBASEGeobase reading is in progress.
CALCULATIONSCalculations are in progress.
NEED_CONFIRMATION

Occurs only if manual acceptance mode was used. The results are waiting to be accepted or rejected. In this state network coverage summaries for the job are provided when checking the job status.

DONEThe results are waiting to be downloaded.
ERROR

Processing failed. This can occur at any place in the flow.

REJECTED

The job is rejected. During processing it turned out that the job exceeds set limits, or geometry cannot be processed (it is effectively empty because there are no roads with selected frcs or it is defined on an area which is not supported). This can occur at any place in the flow.

CANCELLED

The job is cancelled. The owner of the job stopped processing it. It can be done at any time before the job is processed.

EXPIREDThe job is older than 2 years and all data has been removed.

Confirming job results

When the job's state is NEED_CONFIRMATION, then a summary URL will be available in the response.

Response body - JSON
1{
2 "jobId": "{job_id}",
3 "jobState": "NEED_CONFIRMATION",
4 "sampleDetailsUrl": "{url_for_network_coverage_summary_file}",
5 "responseStatus": "OK",
6 "messages": ["For more details see: {url_for_network_coverage_summary_file}"]
7}

A network coverage summary file looks like this:

Network coverage summary file - JSON
1{
2 "summaries": [
3 {
4 "locationName": "City of London",
5 "dateRangeName": "Wednesday 15 January 2025",
6 "timeSetName": "Wednesday morning peak",
7 "networkLength": 12.53,
8 "coveredNetworkLength": 11.11,
9 "distanceUnit": "KILOMETERS",
10 "frcDetails": [
11 {
12 "frc": 0,
13 "networkLength": 0.0,
14 "coveredNetworkLength": 0.0,
15 "distanceUnit": "KILOMETERS"
16 },
17 {
18 "frc": 1,
19 "networkLength": 3.25,
20 "coveredNetworkLength": 3.25,
21 "distanceUnit": "KILOMETERS"
22 },
23 {
24 "frc": 2,
25 "networkLength": 1.92,
26 "coveredNetworkLength": 1.92,
27 "distanceUnit": "KILOMETERS"
28 },
29 {
30 "frc": 3,
31 "networkLength": 0.31,
32 "coveredNetworkLength": 0.31,
33 "distanceUnit": "KILOMETERS"
34 },
35 {
36 "frc": 4,
37 "networkLength": 0.51,
38 "coveredNetworkLength": 0.51,
39 "distanceUnit": "KILOMETERS"
40 },
41 {
42 "frc": 5,
43 "networkLength": 0.0,
44 "coveredNetworkLength": 0.0,
45 "distanceUnit": "KILOMETERS"
46 },
47 {
48 "frc": 6,
49 "networkLength": 0.97,
50 "coveredNetworkLength": 0.87,
51 "distanceUnit": "KILOMETERS"
52 },
53 {
54 "frc": 7,
55 "networkLength": 5.56,
56 "coveredNetworkLength": 4.24,
57 "distanceUnit": "KILOMETERS"
58 }
59 ]
60 }
61 ]
62}

If you wish to accept the job, make the following request:

post
URL request example - accept the job
https://api.tomtom.com/traffic/trafficstats/status/1/{job_id}/accept?key={Your_API_Key}

Otherwise reject the job with the following request:

post
URL request example - reject the job
https://api.tomtom.com/traffic/trafficstats/status/1/{job_id}/reject?key={Your_API_Key}

Response data

Final DONE response

Response body - JSON
1{
2 "jobId": "{job_id}",
3 "jobState": "DONE",
4 "responseStatus": "OK",
5 "urls": [
6 "{url_for_geojson_result_file}",
7 "{url_for_shape_result_file}"
8 ]
9}

Results

Output of Traffic Volume is available in the following formats:

  • GeoJSON
  • SHAPE

GeoJSON results are provided in a gzip compressed format. SHAPE files are packed into zip archives.

GeoJSON result description

The GeoJSON result is a FeatureCollection. The first feature contains job-level metadata in its properties, with geometry set to null. Each subsequent feature represents a single road segment.

GeoJSON result example
1{
2 "type": "FeatureCollection",
3 "features": [
4 {
5 "type": "Feature",
6 "geometry": null,
7 "properties": {
8 "jobName": "Copy of BARCELONA",
9 "creationTime": "2026-04-01T07:57:57.108025573Z",
10 "userPreference": {
11 "distanceUnit": "KILOMETERS"
12 },
13 "dateRanges": [
14 {
15 "@id": 1,
16 "name": "FWB",
17 "from": "2025-10-01",
18 "to": "2025-10-31",
19 "exclusions": [],
20 "excludedDaysOfWeek": []
21 }
22 ],
23 "timeSets": [
24 {
25 "@id": 2,
26 "name": "0:00-24:00",
27 "dayToTimeRanges": [
28 { "dayOfWeek": "MONDAY", "timeRanges": ["00:00-24:00"] },
29 { "dayOfWeek": "TUESDAY", "timeRanges": ["00:00-24:00"] },
30 { "dayOfWeek": "WEDNESDAY", "timeRanges": ["00:00-24:00"] },
31 { "dayOfWeek": "THURSDAY", "timeRanges": ["00:00-24:00"] },
32 { "dayOfWeek": "FRIDAY", "timeRanges": ["00:00-24:00"] },
33 { "dayOfWeek": "SATURDAY", "timeRanges": ["00:00-24:00"] },
34 { "dayOfWeek": "SUNDAY", "timeRanges": ["00:00-24:00"] }
35 ]
36 }
37 ],
38 "networkName": "BARCELONA",
39 "zoneId": "Europe/Madrid",
40 "probeSource": "ALL",
41 "mapsVersions": ["SouthWestEurope_eur2025.12.1800-23.125-0 OPEN_DSEG"]
42 }
43 },
44 {
45 "type": "Feature",
46 "geometry": {
47 "type": "LineString",
48 "coordinates": [
49 [2.12857, 41.37935],
50 [2.12853, 41.37937],
51 [2.12847, 41.37941],
52 [2.12840, 41.37946],
53 [2.12834, 41.37952],
54 [2.12829, 41.37958],
55 [2.12828, 41.37964],
56 [2.12828, 41.37970],
57 [2.12831, 41.37976]
58 ]
59 },
60 "properties": {
61 "segmentId": 1156333907664011264,
62 "newSegmentId": "1156333907664011264",
63 "speedLimit": 50,
64 "frc": 2,
65 "streetName": "Gran Via de Carles III",
66 "distance": 54.99,
67 "openLr": "CwGDfx1s4hZbAP/nACkWEA==",
68 "gersIdMapping": {
69 "id": "ca10e7c8-3502-4f11-80a7-bbc39004ff84",
70 "startOffset": 0.0,
71 "endOffset": 0.0
72 },
73 "osmIdMapping": [
74 { "osmId": "86163010", "length": 54.99, "offset": 0.0 }
75 ],
76 "segmentVolumes": [
77 { "timeSet": 2, "dateRange": 1, "volume": 160129 }
78 ]
79 }
80 }
81 ]
82}

Job metadata feature

FieldDescription

jobName
string

From the request.
Value: As in the request.

creationTime
datetime

The job creation time.
Value: Timestamp in the format: yyyy-MM-ddTHH:mm:ss.SSSZ

userPreference
object

Requested preference.
Value: distanceUnit as in the request.

dateRanges
array

From the request. Value: See the Structure of dateRanges section.

timeSets
array

From the request.
Value: See the Structure of timeSets section.

networkName
string

From and as in the request.

zoneId
string

From and as in the request.

probeSource
string

Always ALL. Traffic Volume always uses all vehicle types (passenger
and fleet combined).

mapsVersions
array (of strings)

Version(s) of maps used for map matching. Selected automatically by the
system.

Structure of dateRanges

FieldDescription

@id
integer

Helper reference for further use (in
features.properties.segmentVolumes).

name string

From and as in the request.

from, to date

From and as in the request.

exclusions array

From and as in the request.

excludedDaysOfWeek array

Based on request values from: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY.

Structure of timeSets

FieldDescription

@id
integer

Helper reference for further use (in features.properties.segmentVolumes).

name
string

From and as in the request.

dayToTimeRanges
array

Time ranges from the request, grouped per day of the week:

  • dayOfWeek - Day of the week for a time group, values from: MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY.

  • timeRanges - Time ranges for a time group, a list of values in the format of: HH:mm-HH:mm.

Segment features

Each segment feature has a LineString geometry and the following properties:

FieldDescription

segmentId long

A unique numeric identifier for the road segment.

newSegmentId
string

A string representation of the unique segment identifier.

speedLimit
integer

Speed limit (in kmph or mph according to userPreference).

frc
integer (0-8)

Functional Road Class.

streetName
string

The street name.

distance
float

The length of a segment (in meters or feet according to
userPreference).

segmentVolumes
array

See the Structure of segmentVolumes
section.

openLr
string
(optional)

The geometry of the segment encoded in OpenLR
format, represented as a Base64 string. Not all segments have this
value. Encoding and decoding may not always succeed in both directions.
Available only in GeoJSON output, when enabled at the contract level.

gersIdMapping object
(optional)

Mapping from this segment to a
GERS (Global Entity Reference
System) feature. Many-to-one: multiple segments may map to the same GERS
feature. See the Structure of
gersIdMapping
section. Available only in
GeoJSON output, when enabled at the contract level.

osmIdMapping
array
(optional)

Mapping from this segment to one or more OpenStreetMap ways.
Many-to-many: a single segment may map to multiple OSM ways, and the
same OSM way may appear more than once with different offsets. See the
Structure of osmIdMapping section.
Available only in GeoJSON output, when enabled at the contract level.

Structure of segmentVolumes

Field

Description

timeSet integerReference to TimeSet in which calculations were made.
dateRange integerReference to DateRange in which calculations were made.
volume integer

The estimated number of vehicle passages on the segment for the given
time set and date range. This is a model-based estimate of total
traffic, not a count of observed probe samples.

Structure of gersIdMapping

Field

Description

id string

The GERS ID of the Overture Maps feature this segment maps to. Formatted
as a UUID-like string.

startOffset float

Distance in meters or feet (according to userPreference) from the
start of the Overture Maps feature to the start of this segment.

endOffset float

Distance in meters or feet (according to userPreference) from the end
of this segment to the end of the Overture Maps feature.

Example: understanding startOffset and endOffset

startOffset is the gap between the GERS feature start and this segment's start. endOffset is the gap between this segment's end and the GERS feature end. The invariant is: startOffset + segmentLength + endOffset ≈ total GERS feature length.

Three segments tile GERS feature 3ccf363f-6b9b-4a01-b3ac-4dd110c7b9b4 end-to-end:

1GERS feature 3ccf363f-6b9b-4a01-b3ac-4dd110c7b9b4 (total ~105.168 m):
2|←————————————————————————————————————————————————→|
3
4Segment A — covers [050 m] (segmentLength = 50.0 m):
5|←——————————————————————→ |
6 startOffset = 0.0 endOffset = 55.168
7
8Segment B — covers [5077.58 m] (segmentLength = 27.58 m):
9| ←———————————→ |
10 startOffset = 50.0 endOffset = 27.584
11
12Segment C — covers [77.584105.168 m] (segmentLength = 27.58 m):
13| ←———————————→|
14 startOffset = 77.584 endOffset = 0.0

JSON example (Segment A):

1{
2 "segmentId": 1161128122481737730,
3 "gersIdMapping": {
4 "id": "3ccf363f-6b9b-4a01-b3ac-4dd110c7b9b4",
5 "startOffset": 0.0,
6 "endOffset": 55.168
7 }
8}

JSON example (Segment B):

1{
2 "segmentId": 1161128122481737732,
3 "gersIdMapping": {
4 "id": "3ccf363f-6b9b-4a01-b3ac-4dd110c7b9b4",
5 "startOffset": 50.0,
6 "endOffset": 27.584
7 }
8}

JSON example (Segment C):

1{
2 "segmentId": 1161128122481737734,
3 "gersIdMapping": {
4 "id": "3ccf363f-6b9b-4a01-b3ac-4dd110c7b9b4",
5 "startOffset": 77.584,
6 "endOffset": 0.0
7 }
8}
Example: reversed segments and GERS geometry direction

A GERS feature has a fixed geometry direction (from its defined start to its defined end). TomTom road segments mapped to the same GERS feature can run in either direction — with or against the GERS feature's direction. This is common for bidirectional roads, where both travel directions share the same GERS feature.

For a segment running in the same direction as the GERS feature ("forward"):

  • startOffset = distance from GERS start to the segment's start
  • endOffset = distance from the segment's end to GERS end

For a segment running opposite to the GERS feature ("reversed"):

  • startOffset = distance from GERS end to the segment's start
  • endOffset = distance from GERS start to the segment's end

The invariant startOffset + segmentLength + endOffset ≈ total GERS length holds in both cases.

1GERS feature (total ~1225.191 m, direction →):
2A (0 m) B (500 m) E (1225.191 m)
3|←————————————————————————→|←————————————————————————————→|
4
5Forward segment AB (runs with GERS direction):
6 startOffset = 0.0 endOffset = 725.191
7 Sub-geometry: GERS[AB] = GERS[0500 m] ← already in correct direction
8
9Reversed segment BA (runs against GERS direction):
10 startOffset = 725.191 endOffset = 0.0
11 Sub-geometry: GERS[endOffset → total−startOffset] = GERS[0500 m], then reverse

To detect whether a segment is reversed: compare the segment's first coordinate with the GERS feature geometry (fetched from Overture Maps by id). If it is near the GERS end rather than the GERS start, the segment is reversed.

JSON example — forward segment (A→B):

1{
2 "segmentId": 1160846647740727298,
3 "gersIdMapping": {
4 "id": "3495e20b-7ad6-4348-b2b5-66dd5d9690f2",
5 "startOffset": 0.0,
6 "endOffset": 725.191
7 }
8}

JSON example — reversed segment (B→A), same physical road section:

1{
2 "segmentId": 1160846647740727299,
3 "gersIdMapping": {
4 "id": "3495e20b-7ad6-4348-b2b5-66dd5d9690f2",
5 "startOffset": 725.191,
6 "endOffset": 0.0
7 }
8}

Both segments cover the same 500 m stretch of road. The reversed segment's offsets are "mirrored" relative to the GERS feature ends. To apply the GERS geometry for the reversed segment, extract the sub-geometry from endOffset (0 m) to total − startOffset (500 m), then reverse it to match the segment's travel direction.

Structure of osmIdMapping

Each entry in the osmIdMapping array describes the relationship between this segment and one OpenStreetMap way.

Field

Description

osmId stringThe OpenStreetMap way ID.
length float

The total length of the OSM way, in meters or feet (according to
userPreference).

offset float

Position of the OSM way relative to the start of this segment, in meters
or feet (according to userPreference). A negative value means the OSM
way starts that many meters or feet before the segment start and extends into
the segment. A positive value means the segment starts that many meters or feet
before the start of the OSM way.

Example: understanding the offset field

The offset field answers one question: where does the OSM way start, relative to this segment's start?

  • Positive offset — the OSM way starts ahead of the segment start. The segment begins before the OSM way and enters it after offset meters or feet.
  • Negative offset — the OSM way starts behind the segment start. The segment starts already inside the OSM way, at position −offset from the OSM way start.

The formula is: position_of_segment_start_along_OSM_way = −offset

The following three segments all run along the same OSM way (131023305, 388.698 m). Their offsets chain together — each segment starts exactly where the previous one ends on the OSM way.

1OSM way 131023305 (388.698 m):
2 |←———————————————————————————————————————————————————————————————————————————→|
3
4Seg 204 (200.0 m, offset = +122.137):
5|←—————————————————————————————————————→|
6|←————— 122.137 m —————→|
7
8Seg 206 (155.4 m, offset =77.863):
9 |←————————————————————————————→|
10 |←— 77.863 m ——→|
11
12Seg 208 (155.43 m, offset =233.267):
13 |←————————————————————————————→|
14 |←———————————————— 233.267 m —————————————————→|

Reading the diagram:

  • Seg 204 starts 122.137 m before the OSM way. It enters the OSM way 122.137 m into its own journey and covers the first 77.863 m of the OSM way (200.0 − 122.137 = 77.863 m).
  • Seg 206 starts right where Seg 204 ends on the OSM way (77.863 m from the OSM start) and covers the next 155.4 m.
  • Seg 208 starts at 233.267 m into the OSM way and runs to its end (233.267 + 155.43 ≈ 388.7 m).

JSON examples:

{ "osmId": "131023305", "length": 388.698, "offset": 122.137 }

Seg 204 starts 122.137 m before the OSM way start.

{ "osmId": "131023305", "length": 388.698, "offset": -77.863 }

Seg 206 starts 77.863 m into the OSM way.

{ "osmId": "131023305", "length": 388.698, "offset": -233.267 }

Seg 208 starts 233.267 m into the OSM way.

Example: reversed segments and OSM way direction

An OSM way has a fixed geometry direction (from its defined start to its defined end). TomTom road segments mapped to the same OSM way can run in either direction — with or against the OSM way's direction. This is common for bidirectional roads, where both travel directions share the same OSM way.

For a segment running in the same direction as the OSM way ("forward"):

segment_start_position_in_OSM_way = −offset

For a segment running opposite to the OSM way ("reversed"):

segment_start_position_in_OSM_way = length + offset

The sub-geometry to extract differs accordingly:

  • Forward: extract OSM geometry from (−offset) to (−offset + segmentLength)
  • Reversed: extract OSM geometry from (length + offset − segmentLength) to (length + offset), then reverse it
1OSM way 7390012 (total ~1206.45 m, direction →):
2A (0 m) B (500 m) E (1206.45 m)
3|←————————————————————————→|←————————————————————————————→|
4
5Forward segment AB (runs with OSM direction):
6 offset = 0.0 → start = −offset = 0 m from OSM start
7 Extract OSM[AB] = OSM[0500 m] ← already in correct direction
8
9Reversed segment BA (runs against OSM direction):
10 offset =706.45
11 end = length + offset = 1206.45 + (706.45) = 500 m
12 start = length + offset − segLength = 1206.45 + (706.45)500 = 0 m
13 Extract OSM[AB] = OSM[0500 m], then reverse

To detect whether a segment is reversed: compare the segment's first coordinate with the OSM way geometry (fetched from OpenStreetMap by osmId). If it is near the OSM way's end rather than its start, the segment is reversed.

JSON example — forward segment (A→B):

{ "osmId": "7390012", "length": 1206.45, "offset": 0.0 }

JSON example — reversed segment (B→A), same physical road section:

{ "osmId": "7390012", "length": 1206.45, "offset": -706.45 }

Both segments cover the same 500 m stretch of road. For the reversed segment, length + offset gives the sub-geometry end (500 m from OSM start), and length + offset − segmentLength gives its start (0 m). Extract OSM[0 → 500 m], then reverse it to match the segment's travel direction.