Search for content

Track Matching with Toll Calculation

Track Matching Tutorial

In this tutorial you will learn how a basic Track Matching workflow works and how to calculate toll costs for a matched track. First, you use Track Matching with the calculationMode parameter as a POST request and pass the positions in JSON format. If the request succeeds, the response contains an ID. Using this ID you can request the results of the match track calculation, including route IDs.

With a GET request to the calculateRoute endpoint of the Routing API you can then calculate the toll costs for the matched track.

Try it! Download from GitHub

Prerequisites

  • Basic JavaScript knowledge.
  • Basic knowledge of JavaScript asynchronous programming using Promises.
  • Helpful: Basic knowledge of jQuery. A page like the W3 Schools jQuery tutorial is sufficient.

How to use this tutorial

The tutorial will guide you like a cooking recipe. This tutorial will focus on how to call the Track Matching service, what you can do with the results, and how to calculate toll costs for the matched track.

Getting started

Get the track ID by requesting calculationMode and pass the positions. 

function buildRequestBody() {
    return {
        positions: inputPositions.map(pos => ({
            latitude: pos.lat,
            longitude: pos.lng
        }))
    };
}

function createMatchedTrack() {
    const url = "https://api.myptv.com/mapmatch/v1/tracks?calculationMode=QUALITY";

    fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'apiKey': api_key
        },
        body: JSON.stringify(buildRequestBody())
    })
    .then(response => response.json())
    .then(result => {
        if (result.id) {
            pollForResult(result.id);
        }
    });
}

Explanation of the code

The code defines the method createMatchedTrack. The calculationMode parameter can be assigned different values that affect the quality and performance of the matching. The positions are passed in JSON format. The coordinates are given in WGS84 format (latitude, longitude). The positions must contain at least latitude and longitude. All other parameters are optional. If the request is successful you get the track ID.

The URL consists of:

  • the protocol "https",
  • the host name "api.myptv.com",
  • the path to the endpoint "mapmatch/v1/tracks"
  •  calculationMode with following possible values "STANDARD", "QUALITY" and "PERFORMANCE",
  • positions in JSON format,
  • the ApiKey, also as a request parameter.

Pass the track id and get the track results. 

If the request with calculationMode succeeds, the response contains an ID. 

{
   "id": "1e05ad60-35c1-48f9-be4d-1ead39b3d0c6"
}

Using this ID you can request the results of the match track calculation. Note that `ROUTE_ID` is included in the results parameter — this is required for the toll calculation in the next step:

function pollForResult(trackId, attempt) {
    attempt = attempt || 1;
    const maxAttempts = 20;
    const url = `https://api.myptv.com/mapmatch/v1/tracks/${trackId}?results=GEOMETRY,PATHS,TRACK_POSITIONS,ROUTE_ID`;

    fetch(url, {
        method: 'GET',
        headers: {
            'Content-Type': 'application/json',
            'apiKey': api_key
        }
    })
    .then(response => response.json())
    .then(result => {
        if (result.status === 'RUNNING') {
            if (attempt >= maxAttempts) return;
            setTimeout(() => pollForResult(trackId, attempt + 1), 1000);
        } else if (result.status === 'SUCCEEDED') {
            displayMatchedTrack(result.matchedTrack);
        }
    });
}

Explanation of the code

The method pollForResult defines the request to fetch the results of the calculation. It polls the API until the status changes from "RUNNING" to "SUCCEEDED" or the maximum number of attempts is reached. You have to provide the track ID of the createMatchedTrack response as path parameter. By using the `results` query parameter you specify which kind of result you want to get. The `ROUTE_ID` result is required in order to be able to calculate the toll afterwards.

The URL consists of:

  • the protocol "https",
  • the host name "api.myptv.com",
  • the path to the endpoint "mapmatch/v1/tracks",
  • track ID of the createMatchedTrack response as path parameter,
  • `results` with following possible values "GEOMETRY", "TRACK_POSITIONS", "PATHS", "SEGMENT_ATTRIBUTES", "ROUTE_ID",
  • the ApiKey, also as a request parameter.

If the request was successful the response looks like this:

{
    "status": "SUCCEEDED",
    "matchedTrack": {
        "id": "b5770be0-bc5f-43ca-8db6-b2b44f2b71dd",
        "distance": 1592,
        "paths": [
            {
                "distance": 695,
                "startTime": "2024-10-24T08:00:00.000+02:00",
                "startTrackPositionIndex": 0,
                "endTime": "2024-10-24T08:10:00.000+02:00",
                "endTrackPositionIndex": 1,
                "routeId": "d08aac06-a50a-4676-b637-2b68fffe3503"
            },
            {
                "distance": 897,
                "startTime": "2024-10-24T08:15:00.000+02:00",
                "startTrackPositionIndex": 2,
                "endTime": "2024-10-24T08:20:00.000+02:00",
                "endTrackPositionIndex": 4,
                "routeId": "3bc38d66-c597-4d19-b925-0378e5b61958"
            }
        ]
    }
}

Calculate Toll

You can calculate toll for a driven route by combining the PTV Developer Map Matching API and PTV Developer Routing API. You can use this for compliance measurements like a toll cost validation. For each path in the matched track response you use the respective routeId and (for time-dependent tolls) the respective startTime. The toll for the entire track is the sum of the toll for the individual paths.

function calculateToll() {
    const paths = lastMatchedTrack.paths.filter(p => p.routeId);

    const tollPromises = paths.map(path => {
        let url = `https://api.myptv.com/routing/v1/routes?routeId=${path.routeId}&results=TOLL_COSTS`;
        if (path.startTime) {
            url += `&options[startTime]=${path.startTime}`;
        }
        return fetch(url, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json',
                'apiKey': api_key
            }
        }).then(response => response.json());
    });

    Promise.all(tollPromises)
        .then(results => {
            let totalTollByCurrency = {};

            results.forEach(result => {
                if (result.toll && result.toll.costs && result.toll.costs.prices) {
                    result.toll.costs.prices.forEach(p => {
                        totalTollByCurrency[p.currency] =
                            (totalTollByCurrency[p.currency] || 0) + p.price;
                    });
                }
            });

            console.log('Total toll:', totalTollByCurrency);
        });
}

Explanation of the code

The code defines the function calculateToll(), which iterates over all paths from the matched track response and sends a GET request to the Routing API for each path. The function collects toll costs from all paths and sums them up by currency.

The URL consists of:

  • the protocol "https",
  • the host name "api.myptv.com",
  • the path to the endpoint "routing/v1/routes",
  • routeId — the route ID from the matched track path,
  • the result parameter with the value "TOLL_COSTS",
  • options[startTime] — the start time of the path (for time-dependent tolls, optional),
  • the ApiKey, also as a request parameter.
If the request was successful the response containing the toll costs looks like this:
{
    "distance": 4701,
    "travelTime": 321,
    "trafficDelay": 0,
    "violated": false,
    "toll": {
        "costs": {
            "prices": [
                {
                    "price": 2.25,
                    "currency": "EUR"
                }
            ],
            "countries": [
                {
                    "countryCode": "FR",
                    "price": {
                        "price": 2.25,
                        "currency": "EUR"
                    }
                }
            ]
        }
    }
}

Next steps to try

As a next step you might want to display more properties of the result (like e.g. the country), or extend the request parameter results to request additional result fields, like segment attributes or geometry. In order to learn more about that, please refer to the PTV Developer Map Matching API documentation and the  PTV Developer Track Matching concept.