Back to all examples

Route Avoid Options Playground

This example on GitHub

Experiment with avoid road types and avoid areas on a route between Paris and Amsterdam

Route Avoid Options Playground
import { BBox, bboxFromGeoJSON, getSectionBBox, Route, SectionProps, TomTomConfig } from '@tomtom-org/maps-sdk/core';
import { RoutingModule, TomTomMap } from '@tomtom-org/maps-sdk/map';
import { calculateRoute, geocodeOne } from '@tomtom-org/maps-sdk/services';
import type { LngLat } from 'maplibre-gl';
import { setupAvoidedAreas } from './avoidedAreas';
import { setupAvoidOptions } from './avoidOptions';
import { API_KEY } from './config';
import { buildAvoidHTML, createSectionPopup } from './popup';
import './style.css';
import { initTogglePanel } from './togglePanel';

TomTomConfig.instance.put({ apiKey: API_KEY });

(async () => {
    const locations = await Promise.all(['London', 'Brussels'].map(geocodeOne));

    const map = new TomTomMap({
        mapLibre: {
            container: 'sdk-map',
            bounds: bboxFromGeoJSON(locations),
            fitBoundsOptions: { padding: 100 },
        },
    });

    const routingModule = await RoutingModule.get(map);

    let currentRoute: Route;
    let activePopup: ReturnType<typeof createSectionPopup> | null = null;
    let suppressNextMapClick = false;
    let avoidedAreasManager: ReturnType<typeof setupAvoidedAreas>;
    let avoidOptions: ReturnType<typeof setupAvoidOptions>;

    const recalculate = async () => {
        const areas = avoidedAreasManager.areas;
        const avoidAreas = areas.length > 0 ? areas.map((a) => a.bbox) : undefined;
        const routeResult = await calculateRoute({
            locations,
            costModel: { avoid: avoidOptions.activeAvoidTypes, avoidAreas },
        });
        currentRoute = routeResult.features[0];
        await routingModule.showWaypoints(locations);
        await routingModule.showRoutes(routeResult);
    };

    avoidedAreasManager = setupAvoidedAreas(
        map.mapLibreMap,
        async (index) => {
            avoidedAreasManager.remove(index);
            await recalculate();
        },
        () => {
            suppressNextMapClick = true;
        },
    );
    avoidOptions = setupAvoidOptions(recalculate, async () => {
        avoidedAreasManager.reset();
        await recalculate();
    });

    const showAvoidPopup = (lngLat: LngLat, bbox: BBox, label: string) => {
        suppressNextMapClick = true;
        activePopup?.remove();
        const popup = createSectionPopup().setLngLat(lngLat).setHTML(buildAvoidHTML()).addTo(map.mapLibreMap);
        activePopup = popup;
        popup
            .getElement()
            .querySelector('.avoid-popup-btn')
            ?.addEventListener('click', async () => {
                popup.remove();
                activePopup = null;
                avoidedAreasManager.add({ bbox, label });
                await recalculate();
            });
    };

    const makeClickHandler = (typeLabel: string) => (section: { properties: SectionProps }, lngLat: LngLat) => {
        const bbox = getSectionBBox(currentRoute, section.properties);
        if (bbox) showAvoidPopup(lngLat, bbox, typeLabel);
    };

    routingModule.events.user.ferries.on('click', makeClickHandler('Ferry'));
    routingModule.events.user.tollRoads.on('click', makeClickHandler('Toll Road'));
    routingModule.events.user.tunnels.on('click', makeClickHandler('Tunnel'));
    routingModule.events.user.vehicleRestricted.on('click', makeClickHandler('Restricted'));
    routingModule.events.user.incidents.on('click', (section, lngLat) => {
        const bbox = getSectionBBox(currentRoute, section.properties);
        if (bbox) showAvoidPopup(lngLat, bbox, section.properties.categories.join(', '));
    });

    // Close all popups on empty map click (skip when a layer click just opened one).
    map.mapLibreMap.on('click', () => {
        if (suppressNextMapClick) {
            suppressNextMapClick = false;
            return;
        }
        activePopup?.remove();
        activePopup = null;
        avoidedAreasManager.closeClearPopup();
    });

    recalculate();
    initTogglePanel();
})();

Related examples