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

Traffic area analytics module

TrafficAreaAnalyticsModule draws a region's historical traffic from a trafficAreaAnalytics response as hexagon or square grids, flat or extruded, or as a density heatmap, with the region's boundary.

The module owns the grid and heatmap layers it renders, and their look, set by the colour, height and layer-position configuration below. It never restyles the base map underneath, which belongs to the Styling Foundations module, and styling knobs never touch these layers. See Who owns what.

Initialization

import { TomTomMap, TrafficAreaAnalyticsModule } from '@tomtom-org/maps-sdk/map';

const map = new TomTomMap({ mapLibre: { container: 'map' } });
const analyticsModule = await TrafficAreaAnalyticsModule.create(map, {
    displayMode: 'hexgrid-3d',
    activeMetric: 'congestionLevel',
    metrics: { congestionLevel: { palette: 'trafficLight' } },
});

Fetching and displaying data

Pass a trafficAreaAnalytics response to show() as it arrives. To switch metrics later without fetching again, ask for metrics: 'all':

import { geocodeOne, geometryData, trafficAreaAnalytics } from '@tomtom-org/maps-sdk/services';

const place = await geocodeOne('Amsterdam, Netherlands');
const boundary = await geometryData({ geometries: [place] });

const analytics = await trafficAreaAnalytics({
    apiKey: 'YOUR_MOVE_PORTAL_KEY',
    startDate: '2024-08-06',
    endDate: '2024-08-12',
    metrics: 'all',
    functionalRoadClasses: 'all',
    hours: 'all',
    geometry: boundary.features[0].geometry,
});

await analyticsModule.show(analytics);
await analyticsModule.clear(); // removes it again

Visualization modes

displayMode sets one of five modes, at create() or through updateConfig({ displayMode }):

Mode Draws
'hexgrid-3d' Hexagons coloured and raised by the metric (default)
'hexgrid-2d' Flat hexagons coloured by the metric
'square-3d' Squares coloured and raised by the metric
'square-2d' Flat squares coloured by the metric
'heatmap' A MapLibre density heatmap from the tile centres
analyticsModule.updateConfig({ displayMode: 'heatmap' });

Metrics

activeMetric picks the metric that colours and raises the cells, congestionLevel by default. Each metric's look (palette or colour stops, height, filters) sits under metrics.<metric>, so you can set up several and switch between them:

const analyticsModule = await TrafficAreaAnalyticsModule.create(map, {
    activeMetric: 'congestionLevel',
    metrics: {
        congestionLevel: { palette: 'trafficLight' },
        speed: { palette: 'heat' },
        travelTime: { height: { maxMeters: 300 } },
    },
});
analyticsModule.updateConfig({ activeMetric: 'speed' });

The metrics are areaAnalyticsMetricKeys, with the units of the service. On a tile, networkLength is the road length within that tile, a road-density indicator.

Color

A metric takes its colours from metrics.<metric>.palette, a named palette, or metrics.<metric>.colorStops, your own stops.

Palettes

areaAnalyticsPalettes lists the names (AreaAnalyticsPalette). Each runs from good traffic to bad:

analyticsModule.setPalette('trafficLight'); // green → amber → red (default)
analyticsModule.setPalette('heat');         // blue → orange → red
analyticsModule.setPalette('monochrome');   // light grey → dark grey
analyticsModule.setPalette('viridis');      // yellow → green → blue → purple
analyticsModule.setPalette('plasma');       // yellow → orange → magenta → purple

analyticsModule.setPalette('viridis', ['speed', 'freeFlowSpeed']); // only these metrics

Custom color stops

setColorStops(), or colorStops in the config, takes an AreaAnalyticsColorStopsConfig of stops, each a value and a CSS color, and the scaleMode that says what the values are.

scaleMode A stop's value is
'raw' (default) a metric value
'predefinedRange' 0–100 % of the SDK's range for the metric: congestion 0–100, speed and free-flow speed 0–120 km/h, travel time 0–20 minutes per 10 km, network length 0–5000 m
'currentRange' 0–100 % of the range in the loaded tiles, 0 % being the loaded minimum
analyticsModule.setColorStops({
    scaleMode: 'raw',
    stops: [
        { value: 0, color: '#2dc653' },   // 0 % congestion
        { value: 30, color: '#f5a623' },
        { value: 100, color: '#e03030' },
    ],
}, ['congestionLevel']);
analyticsModule.setColorStops(undefined); // back to the metric's palette
analyticsModule.setPalette(undefined);    // and with no palette, 'trafficLight'

Reading a metric's colours

resolveColorStops(metric, metricConfig) returns the stops a metric is painted with, as an AreaAnalyticsColorStopsConfig. These are its colorStops when set, otherwise its palette spread from 0 to 100 % of its range, reversed on the speed metrics. A legend or chart drawn from it follows whatever the config sets:

import { resolveColorStops } from '@tomtom-org/maps-sdk/map';

const { scaleMode, stops } = resolveColorStops('speed', analyticsModule.getConfig()?.metrics?.speed);
const legendColors = stops.map((stop) => stop.color);

Height

In the 3D modes, metrics.<metric>.height (AreaAnalyticsHeightConfig) raises the cells. setHeight() sets every metric, or only those in its second argument.

analyticsModule.setHeight({ maxMeters: 200 });                   // tallest cell 200 m
analyticsModule.setHeight({ maxMeters: 100, minMeters: 10 });    // and a floor
analyticsModule.setHeight({ scaleMode: 'currentRange', maxMeters: 150 }, ['speed']);
analyticsModule.setHeight({ scaleMode: 'raw', metersPerUnit: 10 });    // 10 m per unit of the metric

Filtering

setFilters() hides the cells outside a value range, for every metric or only those in its second argument:

analyticsModule.setFilters({ min: 50 });                    // congestion ≥ 50
analyticsModule.setFilters({ min: 20, max: 80 }, ['congestionLevel']);
analyticsModule.setFilters(undefined);                      // clears them

Only the active metric's filter hides cells, and never in the heatmap.

Region boundary

show() draws each region polygon of the response alongside the cells, as a fill and an outline. regionPolygon styles it:

const analyticsModule = await TrafficAreaAnalyticsModule.create(map, {
    regionPolygon: {
        color: '#0052a5',                // fill and line; default black on a light style, white on a dark one
        // style 'inverted' tints the world outside the region; defaults 'filled' and opacity 0
        fill: { style: 'inverted', opacity: 0.08 },
        line: { opacity: 1, width: 3 },  // defaults 0.5 and 2 px
    },
});

Layer positioning

beforeLayerConfig (AreaAnalyticsBeforeLayerConfig) takes one target for every display mode ('top' or a key of mapStyleLayerIDs), or a record keyed by display mode, plus all for the modes it leaves out:

const analyticsModule = await TrafficAreaAnalyticsModule.create(map, {
    beforeLayerConfig: { all: 'lowestLabel', 'hexgrid-3d': 'lowestPlaceLabel', 'square-3d': 'lowestPlaceLabel' },
});
analyticsModule.updateConfig({ beforeLayerConfig: { 'hexgrid-3d': 'top' } });

Layer overrides

Beyond the settings above, layers (TrafficAreaAnalyticsLayersConfig) takes a partial MapLibre layer per layer the module draws. Each display mode draws its own:

Mode Layers
heatmap heatmap
hexgrid-2d hexgridFill, hexgridOutline (the outline of a hovered or clicked cell)
hexgrid-3d hexgridExtrusion, hexgridExtrusionHighlight
square-2d, square-3d the same four square* entries
every mode regionFill, regionLine
analyticsModule.updateConfig({
    layers: {
        hexgridFill: { paint: { 'fill-opacity': 0.85 } },
        hexgridExtrusion: { paint: { 'fill-extrusion-vertical-gradient': false } },
        regionLine: { paint: { 'line-dasharray': [2, 2] } },
    },
});

Visibility

isVisible() reports the visible setting, true by default, not whether any cell is drawn. Hiding keeps the data. While hidden, show() updates the data without drawing.

const analyticsModule = await TrafficAreaAnalyticsModule.create(map, { visible: false });
analyticsModule.setVisible(true);
analyticsModule.getShown().hexgrid.features.length > 0; // whether there are cells to draw

Events

click, hover, long-hover and the other user events fire on what the active mode draws: hexagon or square cells, or the heatmap's tile-centre points. The handler receives it as an AreaAnalyticsTileFeature, with its metric values in properties:

analyticsModule.events.on('click', (cell, lngLat) => {
    console.log(cell.properties.congestionLevel, cell.properties.speed);
});
analyticsModule.events.off('click');

A hovered or clicked cell is highlighted. The module's event state methods highlight one from your own UI, or read which ones are:

analyticsModule.setEventState({ index: 0, state: 'click' });
const { click = [] } = analyticsModule.getEventStates();

Custom tooltip on hover

import { Popup } from 'maplibre-gl';

const popup = new Popup({ closeButton: false, anchor: 'left', offset: 12 });

analyticsModule.events.on('hover', (cell, lngLat) => {
    const { congestionLevel, speed } = cell.properties;
    popup
        .setLngLat(lngLat)
        .setHTML(`<strong>Congestion:</strong> ${congestionLevel ?? 0}%<br/><strong>Speed:</strong> ${speed ?? 0} km/h`)
        .addTo(map.mapLibreMap);
});

map.mapLibreMap.getCanvas().addEventListener('mouseleave', () => popup.remove());

Changing the configuration

analyticsModule.applyConfig({ displayMode: 'heatmap' }); // everything else back to its default
analyticsModule.updateConfig({ activeMetric: 'speed' }); // displayMode stays 'heatmap'
analyticsModule.setPalette('viridis', ['speed']);        // the other metrics keep their colours

Config change events

Every setter, applyConfig and updateConfig emit config-change, and each show() emits shown-features with the response (module events):

const unsubscribe = analyticsModule.events.on('config-change', (config) => {
    console.log('Active metric:', config?.activeMetric);
});

Querying shown features

getShown() returns the heatmap points and the hexgrid and square polygons the sources hold, whichever mode shows:

const { heatmap, hexgrid, square } = analyticsModule.getShown();
console.log(`Hexagonal cells: ${hexgrid.features.length}`);

Every setting as data

trafficAreaAnalyticsKnobCatalogue lists the module's plain-valued settings with their kind, range or options, and their default where one value holds everywhere. A UI can build its controls from it with no map loaded:

import { trafficAreaAnalyticsKnobCatalogue } from '@tomtom-org/maps-sdk/map';

const speedKnobs = trafficAreaAnalyticsKnobCatalogue.filter(({ id }) => id.startsWith('metrics.speed.'));
// [{ id: 'metrics.speed.palette', kind: 'enum', description: '…', options: ['trafficLight', 'heat', …], default: 'trafficLight' }, …,
//  { id: 'metrics.speed.filters.min', kind: 'number', description: '…', range: { min: 0, max: 120, bounds: 'hard-min' } }, …]

The module-wide knobs come first, then the same seven for each metric, <metric> standing for any of areaAnalyticsMetricKeys:

Id Kind
visible toggle, true by default
displayMode enum, hexgrid-3d by default
activeMetric enum, congestionLevel by default
regionPolygon.color color, by default black on a light style, white on a dark one
regionPolygon.fill.style enum: filled, inverted
regionPolygon.fill.opacity, regionPolygon.line.opacity number, 0 to 1
regionPolygon.line.width number, 0 to 10 pixels
metrics.<metric>.palette enum: areaAnalyticsPalettes, default trafficLight
metrics.<metric>.height.scaleMode enum: predefinedRange, currentRange, raw
metrics.<metric>.height.maxMeters, metrics.<metric>.height.minMeters number, 0 to 5000 metres
metrics.<metric>.height.metersPerUnit number, 0 to 100 metres per unit of the metric, in the raw scale mode
metrics.<metric>.filters.min, metrics.<metric>.filters.max number, over the metric's predefined range

API reference

TrafficAreaAnalyticsModule, TrafficAreaAnalyticsModuleConfig and AreaAnalyticsMetricConfig.

Related guides and examples

Related guides

Related examples