This is the documentation of version 1.0.0-rc.4. Go to the latest version, 1.0.0-rc.5.

Route sections

The Routing module's showRoutes() draws an overlay for each stretch of road the response describes, such as urban stretches, motorways, low-emission zones, tunnels, ferries, traffic jams and toll roads. You configure each under sections, keyed by section type, with one set of knobs, each type taking the ones it can honour. The response carries only the types asked for in sectionTypes.

const routingModule = await RoutingModule.create(map, {
    sections: {
        urban: { visible: true },                                            // draw a type nothing draws by default
        lowEmissionZone: { color: '#1B7A43', opacity: 0.8 },                 // deepen the green of one that is
        tunnel: { color: '#402060', style: 'halo' },                         // restyle one that is drawn already
        tollRoad: { icon: { image: 'poi-toll_plaza', placement: 'along' } },  // put an icon on another
        traffic: { visible: false }                                          // and drop one
    }
});

Which sections draw by default

A type draws by default when its stretches are sparse along a route and consequential for the driver, such as money to pay, a permit to hold, a road the vehicle may not use, or the vehicle going onto a train.

Section knobs

Knob What it does
visible Whether the section is drawn at all.
color Line colour. On a type drawn with more than one line, it sets the principal line, and the module derives the rest from it.
sign The road sign a section posts its own value on. Only speedLimit is drawn this way.
kerb The stripes over a tight curve, as { visible, color }. Only curve takes it, in place of pattern.
opacity Line opacity, 0 to 1.
widthPreset Line width preset, s, m or l. Defaults to the route's own widthPreset.
widthFactor Multiplier over widthPreset, 0.5 to 2. Defaults to the route's own widthFactor. A halo section never draws narrower than 1.26 times the route's outline width, whatever its widthPreset and widthFactor.
style halo or inline, described below.
pattern solid, dashed or dotted.
icon An icon on the section, as { image, placement, sizeFactor }, where image is a sprite id, placement is center or along, and sizeFactor multiplies the type's icon size, 0.25 to 2. On ferry, tollRoad, tunnel and vehicleRestricted it replaces the icon drawn where each section starts (tunnel and closed-road signs).

style sets both the layer order and the width, because neither reads as intended without the other:

The knobs a type takes are part of its TypeScript type:

if (sectionSupportsKnob(type, 'sign')) {
    const { default: minZoom } = knobEntryOf(routingKnobCatalogue, `sections.${type}.sign.minZoom`);
}

The types are carpool, carTrain, curve, curvyStretch, ferry, importantRoadStretch, lowEmissionZone, motorway, pedestrian, speedLimit, tollRoad, tollVignette, traffic, tunnel, unpaved, urban and vehicleRestricted. Each one is live on the route sections playground.

Layer overrides

The full layer spec is under layers.sections, keyed by the same types. A generated type's layer id comes from its name, so routeSectionMotorwayLine draws motorway and routeSectionSpeedLimitSign draws speedLimit:

const routingModule = await RoutingModule.create(map, {
    layers: {
        sections: {
            motorway: {
                routeSectionMotorwayLine: {
                    paint: { 'line-color': '#1F6FB2', 'line-dasharray': [2, 1] }
                }
            },
            tunnel: {
                routeTunnelLine: { paint: { 'line-blur': 2 } }
            }
        }
    }
});

A traffic section takes the colour of its delay (minor, moderate or major). Those colours follow the map's incident colour knobs, as Borrowed looks describes.

Tight curves carry a kerb

A curve draws in rose, and a curvy stretch in a fainter rose beneath its curves, so from afar the stretch joins them into one winding road. Neither takes a colour of a traffic delay. The harder a curve is to drive, by its severity, the deeper its rose, so a gentle bend fades toward the map and a hairpin draws in full colour.

From zoom 12, a curve with a radius of 60 m or less, the kind a driver slows right down for, also carries a kerb: short stripes a little wider than the curve, in a faint shade of its colour, faded toward white on a light map and toward near-black on a dark one.

const routingModule = await RoutingModule.create(map, {
    sections: { curve: { visible: true }, curvyStretch: { visible: true } }
});

Tunnels and closed roads post a sign where they start

tunnel and vehicleRestricted post a road sign where each of their sections starts, in the direction of travel. A vehicleRestricted stretch is one the requested vehicle is not permitted to use but the route still takes. To get the signs, ask for the sections:

const routes = await calculateRoute({ locations, sectionTypes: ['tunnel', 'vehicleRestricted'] });
await routingModule.showRoutes(routes);

The sign layers are routeTunnelSymbol and routeVehicleRestrictedSymbol, under layers.sections. They take any MapLibre layer property except visibility, which the module sets each time it shows a route:

const routingModule = await RoutingModule.create(map, {
    layers: {
        sections: {
            tunnel: { routeTunnelSymbol: { minzoom: 13 } },                    // post tunnel signs only close in
            vehicleRestricted: { routeVehicleRestrictedSymbol: { minzoom: 8 } } // post the closed sign earlier
        }
    }
});

A click on a sign reaches the same scope as a click on its stretch, and hands over the section:

routingModule.events.vehicleRestricted.on('click', (section) => {
    console.log('Closed to vehicles from route point', section.properties.startPointIndex);
});

On the route sections playground, clicking the tunnel or vehicleRestricted count flies to the next sign.

Speed limits are posted, not banded

speedLimit carries a number, not a stretch, so the module posts a road sign per section and draws nothing else:

// The signs need no configuration — asking for the sections is enough.
const routes = await calculateRoute({ locations, sectionTypes: ['speedLimit', 'country'] });
await routingModule.showRoutes(routes);

With country sections as well, each sign follows the convention of the country its stretch runs through:

Country Face Unit Numerals
most of Europe white disc km/h near-black
Sweden, Finland, Iceland yellow disc km/h near-black
United Kingdom, Jersey, Guernsey, Isle of Man, Anguilla, British Virgin Islands, Cayman Islands, Falkland Islands, Montserrat, Saint Helena, Turks and Caicos Islands, Liberia, Myanmar white disc mph near-black
Antigua and Barbuda, Bahamas, Belize, Dominica, Grenada, Saint Kitts and Nevis, Saint Lucia, Saint Vincent and the Grenadines, Samoa white disc mph near-black
United States and its territories SPEED LIMIT plaque mph near-black
Japan white disc km/h blue

The sign knob covers the rest:

Knob What it does
sign.placement atChange (the default) posts one sign per limit change. along repeats it down the stretch at a fixed screen spacing, so a reader zoomed into the middle of a 50 km stretch still sees the limit in force.
sign.minZoom The lowest zoom the signs draw at. Defaults to 12.5, two and a half zooms past the tunnel and closed-road signs. A route posts many more speed limits, and further out they crowd the overview of the route. Lower it for a map about the limits themselves.
sign.priority What the signs give way to, as belowMapLabels, belowRouteIcons or aboveRouteIcons. The default depends on the placement.
sign.unit 'km/h' or 'mph' for every sign, whatever the road posts.
const routingModule = await RoutingModule.create(map, {
    sections: {
        speedLimit: {
            sign: { minZoom: 8, priority: 'aboveRouteIcons' }
        }
    }
});

priority decides which symbol survives when two want the same spot. MapLibre resolves collisions from the topmost layer down, so the layer underneath gives way:

The default follows placement, because the cost of a dropped sign does. It is belowRouteIcons under atChange, where that sign is the only one posting its change, and belowMapLabels under along, where the next repeat posts the same limit again.

Both knobs are live on the route sections playground. Speed limit signs by country posts one limit four ways.

Border crossings

country sections partition a route end to end, so every seam between two of them is a border. A crossing is that seam, a point and not a stretch, so it has its own knobs under countryCrossings instead of a place among the section knobs. Each is a bubble above the route naming both countries in the direction of travel by their ISO 3166-1 alpha-2 codes, such as ES → FR. Ask for country sections and the crossings draw by default:

const routes = await calculateRoute({ locations, sectionTypes: ['country'] });
await routingModule.showRoutes(routes);
Knob What it does
visible Whether the crossings draw. Defaults to true.
minZoom The lowest zoom a crossing draws at. Defaults to 4, low enough for a continental route to show its borders while framed whole.
color The bubble colour. Defaults to the selected summary bubble's default, not to a summaryBubbles.color you set: the map's labelHalo lightened when a theme sets it, else white on a light style and dark grey on a dark one.
label.color The label colour. Defaults to the map's label colour when a theme sets it and color is left out, else black or white, whichever reads on the bubble.
const routingModule = await RoutingModule.create(map, {
    countryCrossings: { minZoom: 6, color: '#0B5FA5', label: { color: '#FFFFFF' } }
});

Left at their defaults, the crossings follow a style switch and setMapColors() as the summary bubbles do, so a themed map needs no crossing config.

A clicked crossing hands over the two country sections it joins, so you reach the stretch on either side, not only the codes in the bubble:

routingModule.events.countryCrossings.on('click', (crossing) => {
    const { label, fromSection, toSection } = crossing.properties;
    console.log(label, fromSection.endPointIndex, toSection.startPointIndex); // 'ES → FR' 1941 1941
});

Focusing sections

To point at sections of the shown routes, such as the leg or the traffic delay a row of your UI describes, call setSectionFocus with their ids. Any section of a shown route can be focused, whether or not it is drawn. To clear the focus, pass null. Showing new routes clears it too.

const [firstLeg] = route.properties.sections.leg;
await routingModule.setSectionFocus([firstLeg.id]);

await routingModule.setSectionFocus(null);

A focused section is ringed by an outline around the route, clear of the widest line the route draws, so the route and every section drawn on it stay in sight. A section of an alternative route is ringed under the selected route. By default, the outline is black on a light map style and white on a dark one, like the traffic incident highlight. highlight.section.outline.color sets a colour of your own, and highlight.section.outline.widthFactor multiplies the outline's width, clamped to 0.5 to 2.

routingModule.applyConfig({ highlight: { section: { outline: { color: '#E63312', widthFactor: 1.5 } } } });

getShown().sectionFocus holds the focused sections. To restyle the outline beyond the knobs, use a layers.sectionFocus.routeSectionFocusOutline override, or routeDeselectedSectionFocusOutline for the alternative routes.

Section events and shown data

Each section type gets its own event scope and getShown() entry. The generated types are keyed <type>Sections. The other five keep the names of their sources: ferries, tollRoads, incidents, tunnels and vehicleRestricted.

routingModule.events.lowEmissionZoneSections.on('click', (feature, lngLat) => {
    console.log('Low-emission zone section');
});

const urbanSections = routingModule.getShown().urbanSections;

Related guides and examples

Related guides

Related examples