Traffic incident details module
TrafficIncidentDetailsModule draws the incidents a trafficIncidentDetails call returned — no more, no less — and highlights a subset of them. Use it when your app holds the typed incidents too, for popups, lists or route correlation, and decides when they render.
What this module owns: the incident lines and icons it draws from the details you fetched, and how they look — including the focus treatment below. The live incident tiles and their colours belong to the Traffic Incidents Module. The module borrows those colours, so that module's colors.* knobs recolour it too (Borrowed looks). No other knob reaches it. See Who owns what.
When to use the details module vs TrafficIncidentsModule
| You need… | Use |
|---|---|
| Live, full-coverage incidents with no fetch | TrafficIncidentsModule |
| Typed incident objects in your app code | TrafficIncidentDetailsModule |
| Render exactly the result of a single REST call | TrafficIncidentDetailsModule |
| Per-feature focus / highlight a subset | TrafficIncidentDetailsModule |
| Maximum visual fidelity (per-road-class styling, declutter) | TrafficIncidentsModule |
The tile incidents are hidden until shown, so the two don't collide by default. If your app shows TrafficIncidentsModule, hide it while this module draws, or each incident renders twice.
Initialization
import { TomTomMap, TrafficIncidentDetailsModule } from '@tomtom-org/maps-sdk/map';
const map = new TomTomMap({ mapLibre: { container: 'map' } });
const incidentDetailsModule = await TrafficIncidentDetailsModule.create(map);
It is data-owned: every create() returns a new, independent instance.
Rendering incidents
Pass the service result to show():
import { trafficIncidentDetails } from '@tomtom-org/maps-sdk/services';
const result = await trafficIncidentDetails({ bbox: map.getBBox() });
await incidentDetailsModule.show(result);
show()replaces what was shown and clears the focus;clear()removes every incident.getShown().incidentsis the collection last passed toshow(), not a viewport query.- The
shown-featuresmodule event fires on eachshow().
Focusing a subset
setFocus(ids) marks the incidents with those ids through MapLibre feature state, and setFocus(null) or setFocus([]) clears it:
incidentDetailsModule.setFocus([result.features[0].properties.id]);
incidentDetailsModule.setFocus(null);
- The default highlight widens the focused incidents and paints an outline beneath them, black on a light style and white on a dark one. The others stay as they are: the highlight adds emphasis, it does not dim.
setFocusdoes nothing beforeshow(). A style change that keeps state keeps the focus.- A hovered or clicked incident draws the same highlight once a handler covers the module. Its state is the module's event state, which
setEventStateputs from your own UI.
Customising or disabling the highlight
highlight takes an IncidentHighlightConfig, whose unset fields keep their defaults:
const incidentDetailsModule = await TrafficIncidentDetailsModule.create(map, {
highlight: { outline: { color: '#1976d2' }, line: { widthFactor: 1.8 } },
});
line.widthFactormultiplies the width of the highlighted incidents:1.6by default, clamped to1–1.8.1keeps the outline without widening; a wider stripe would cover the outline at high zoom.highlight: falseturns the highlight off for focused, hovered and clicked incidents alike.setFocusstill writesfeature-state.focused, and user events theireventState, for your own layers to draw.highlightchanges on a module already showing incidents too, the highlighted ones repainting in place:
incidentDetailsModule.updateConfig({ highlight: { outline: { color: '#c2185b' }, line: { widthFactor: 1.2 } } });
incidentDetailsModule.updateConfig({ highlight: false });
Events
The handlers of click, hover and the other user events receive the typed TrafficIncident, its Date and array properties intact:
incidentDetailsModule.events.on('click', (incident) => {
incidentDetailsModule.setFocus([incident.properties.id]);
});
The handler's third argument, allEventFeatures, holds every incident under the pointer once, however many of its layers were hit.
Visibility and layer ordering
isVisible() reports the visible setting, true by default, not whether any incident is drawn. While hidden, show() updates the data without drawing it.
incidentDetailsModule.setVisible(false);
incidentDetailsModule.getShown().incidents.features.length > 0; // whether there are incidents to draw
// Above every other layer (default: below the labels, 'lowestLabel')
incidentDetailsModule.updateConfig({ beforeLayerConfig: 'top' });
beforeLayerConfig takes one target, 'top' or a key of mapStyleLayerIDs, for every layer of the module (Layer position); there are no parts to position apart.
Layer overrides
For what the settings above don't cover, layers (TrafficIncidentDetailsLayersConfig) takes a partial MapLibre layer for each layer the module draws, bottom to top: highlightHalo, outline, innerSolid, innerChevron, innerPattern, then the incidentMarker, jamMarker and closedRoadMarker icons.
incidentDetailsModule.updateConfig({
layers: {
incidentMarker: { minzoom: 11 }, // the category icons from zoom 11
jamMarker: { layout: { 'text-field': '' } }, // jam icons without their delay
innerChevron: { layout: { visibility: 'none' } }, // no direction chevrons
},
});
paintandlayoutmerge per property onto the module's own, and win over the incident colours and the highlight.- A layer an entry hides through
layout.visibilitystays hidden while the module is shown. - A
beforeIDis ignored:beforeLayerConfigplaces the layers.
Every setting as data
trafficIncidentDetailsKnobCatalogue lists the module's plain-valued settings with their kind,
range and, where one value holds everywhere, their default, readable with no map loaded:
import { knobEntryOf, trafficIncidentDetailsKnobCatalogue } from '@tomtom-org/maps-sdk/map';
const widthFactor = knobEntryOf(trafficIncidentDetailsKnobCatalogue, 'highlight.line.widthFactor');
// { id: 'highlight.line.widthFactor', kind: 'factor', description: '…', range: { min: 1, max: 1.8, step: 0.1, bounds: 'hard' }, default: 1.6 }
| Id | Kind |
|---|---|
visible |
toggle, true by default |
highlight.outline.color |
colour; unset, it follows the map's light/dark theme |
highlight.line.widthFactor |
factor 1–1.8, 1.6 by default |
trafficIncidentDetailsKnobIdslists the ids,TrafficIncidentDetailsKnobIdis their union, andTrafficIncidentDetailsKnobValueOf<ID>the plain value one takes.- Not in it:
beforeLayerConfig,layers,highlight: false, which turns the highlight off, andevents. The incident lines take the style's incident colours, which the Traffic Incidents Module'scolors.*knobs set. - The demo above builds its panel from the catalogue: click an incident to focus it, then turn the highlight knobs.
Limitations
The Incident Details API returns a feature list, not rendering-ready tiles, so the tags the tile pipeline adds per feature are missing:
- No road-class-aware width or offset: every incident is a uniform stripe centred on the road.
- No declutter: every incident shows above the layer's minimum zoom.
- No secondary cause icon: only the primary category icon renders.
For full visual fidelity, use TrafficIncidentsModule.
Overlapping incidents: where incidents share a point or stack along one road, MapLibre's symbol collision keeps only the highest-sort-key icon, and queryRenderedFeatures cannot reach the culled ones. To surface every incident at a location, read the FeatureCollection you passed to show().
API reference
TrafficIncidentDetailsModule, TrafficIncidentDetailsModuleConfig and IncidentHighlightConfig.
Related guides and examples
Related guides
- Incident details service - The query this module draws
- Traffic incidents module - The live incident tiles, and the colours this module borrows
- Traffic overview - Every way the SDK draws traffic
Related examples
- Traffic incident details - A city's incidents clipped to its boundary, a clicked one focused
- Traffic incident focus - A city's incidents brought forward through
layers, and the rest of the map pushed back