Screenshot of the Pickups and Deliveries Tutorial App
Pickups and Deliveries In this tutorial you will learn how our advanced Route optimization app is implemented. The app optimizes specified transports (pickups and deliveries) and displays the optimal routes for each vehicle. To focus solely on the core principles, we have implemented a basic error handling only. The objective of the code examples is to illustrate the use of the PTV Developer Route Optimization API.

Prerequisites

  • JavaScript knowledge, including up-to-date knowledge of ES6 features.
  • Basic knowledge of JavaScript asynchronous programming using Promises.
  • HTML page for displaying PTVs Raster Map with Leaflet. For help, please visit this tutorial on Raster Maps.

How to use this tutorial

This tutorial is intended to introduce how to use the PTV Developer Route Optimization API with JavaScript.

Getting started

  • Request an API key:
    • Register and login at myptv.com
    • Activate PTV Developer
    • Create your API key
  • Download an editor such as Visual Studio Code or Atom which work with HTML and JavaScript.
  • You can find more details of requests and responses in the reference manual.
  • Detailed information about the PTV Developer Route Optimization API is also available in the Quick Start.
  • For more information, please visit the Leaflet reference documentation.

Create the JavaScript code

Create a new JavaScript file and reference it in the HTML page for display in the map.

Create an optimization plan

The first step is to add the code below and replace the string "YOUR_API_KEY" with your PTV Developer API key if this has not already been done.

The plan to be optimized consists of locations, transports and used vehicles.
Part of the result is the generated unique planId, which is used for all following API requests.

                
                    const api_key = "YOUR_API_KEY";

                    // Always add the API key to request headers
                    const applyAPIKey = (configuration) => ({
                        ...configuration,
                        headers: {
                           "apiKey": api_key,
                           ...configuration ? { ...configuration.headers } : {}
                       }
                    });

                    const createOptimizationPlan = () => {
                        const vehicles = [];
                        const numberOfVehicles = 2; // Set number of vehicles to use
                        const vehicleProfile = "EUR_CAR"; // Set profile for the vehicles

                        for (let i = 1; i <= numberOfVehicles; i++) {
                            vehicles.push({
                                id: "Vehicle " + i,
                                profile: vehicleProfile
                            });
                        }

                        const planToBeOptimized = {
                            locations: [...], // Specified locations 
                            transports: [...], // Specified transports
                            vehicles
                        };

                        return fetch(
                                    "https://api.myptv.com/routeoptimization/v1/plans",
                                    applyAPIKey({
                                        method: "POST",
                                        headers: {
                                            "Content-Type": "application/json"
                                        },
                                        body: JSON.stringify(planToBeOptimized)
                                    })
                                )
                                .then(response => response.ok ? response.json() : console.error(response))
                    };
                
            

You can use the PTV Developer Geocoding API to find locations by a given search text. For more information to this API, please visit the reference documentation.

                
                    const searchText = "Berlin";
                    
                    fetch(
                        `https://api.myptv.com/geocoding/v1/locations/by-text?searchText=${searchText}`,
                        applyAPIKey()
                    )
                    .then(response => response.json())
                    .then({ locations } => console.log(locations));
                
            

Start the optimization process for that plan

After creating a plan, start the optimization based on the planId.

                
                    const startOptimization = (planId) =>
                        fetch(
                            `https://api.myptv.com/routeoptimization/v1/plans/${planId}/operation/optimization`,
                            applyAPIKey({
                                method: "POST",
                                headers: {
                                    "Content-Type": "application/json"
                                }
                            })
                        ).then(response => response.ok ? true : console.error(response));
                
            

Check the progress of the optimization process

Retrieve the optimization process status to check if the optimized plan is finished.

                
                    const getOptimizationProgress = (planId) =>
                        fetch(
                            `https://api.myptv.com/routeoptimization/v1/plans/${planId}/operation`,
                            applyAPIKey()
                        ).then(response => response.ok ? response.json() : console.error(response));
                
            

Get the optimized plan

After the optimization process has been successfully completed (status = SUCCEEDED) the plan is updated internally and you can request the optimized plan.

                
                    const getOptimizedPlan = (planId) =>
                        fetch(
                            `https://api.myptv.com/routeoptimization/v1/plans/${planId}`,
                            applyAPIKey()
                        ).then(response => response.ok ? response.json() : console.error(response));
                
            

Put the optimization process together

Now you can put the separate steps of the optimization process together to optimize specified transports.

                
                    const optimizeTransports = async () => {
                        const plan = await createOptimizationPlan(); // Create an optimization plan
                        if (!plan) return;
                    
                        const startSuccessful = await startOptimization(plan.id); // Start the optimization
                        if (!startSuccessful) return;
                    
                        const interval = setInterval( async () => {
                            const progress = await getOptimizationProgress(plan.id); // Get optimization progress
                            if (!progress) return clearInterval(interval);
                    
                            // Only if the optimization has been finished and successful
                            if (progress.status === "SUCCEEDED") {
                                clearInterval(interval);
                                const optimizedPlan = await getOptimizedPlan(plan.id); // Get optimized plan
                                if (!optimizedPlan) return;

                                console.log(optimizedPlan); 
                            }
                        }, 500);
                    };
                
            

Display optimized routes on the map

To display the routes given by the optimized plan as a Leaflet Polyline on the map, retrieve the optimized plan and add two more functions.

                
                    const map = new L.Map(...); // Your Leaflet map instance

                    const addPolylineToMap = (coordinates) => {
                        const randomColor = "#" + (Math.random() * 0xFFFFFF << 0).toString(16).padStart(6, "0"); // Choose a random color
                        const polyline = L.polyline(coordinates, {color: randomColor});

                        polyline.addTo(map);
                        
                        map.fitBounds(polyline.getBounds()); // Fit map to added polyline
                    }

                    const drawRoutes = (optimizedPlan) => {
                        const { locations, routes } = optimizedPlan;
                        routes.forEach(route => {
                            const locationIds = route.stops.map(stop => stop.locationId);
                            // Extract the coordinates
                            const coordinates = locationIds.map(locationId => {
                                const location = locations.find(location => location.id === locationId)
                                return [location.latitude, location.longitude];
                            });
                            addPolylineToMap(coordinates);
                        });
                    };

                    // Adapt this existing function
                    const optimizeTransports = async () => {

                        ...

                        const interval = setInterval( async () => {

                            ...

                            if (progress.status === "SUCCEEDED") {
                                
                                ...

                                console.log(optimizedPlan); 
                                drawRoutes(optimizedPlan); // Call the function to draw the routes
                            }
                        }, 500);
                    };