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

Map modules

A map module adds one capability to a TomTomMap, such as showing places or routes, filtering traffic, shading terrain or restyling the whole map. Every module follows the contract this page describes:

Types of modules

Who owns the sources and layers a module controls decides the factory you call and how many instances make sense on one map.

Style-owned modules: get(map, config?)

Data-owned modules: create(map, config?)

Per-module reference

Module Kind What it draws or controls
BaseMapModule Style-owned Which base-map layer groups and road parts show, and the events on them
POIsModule Style-owned The style's POIs: visibility, category filters, size, labels, category icons
TrafficFlowModule Style-owned The live traffic flow tiles
TrafficIncidentsModule Style-owned The live traffic incident tiles
TerrainModule Style-owned Hillshade shading and the 3D surface
StylingFoundationsModule Style-owned Sizes, map colours, projection and sky across the whole map
PlacesModule Data-owned Places you give it, as pins, circles or base-map-looking icons
RoutingModule Data-owned Routes, waypoints and route sections
GeometriesModule Data-owned Polygons and lines, such as administrative areas
ReachableRangesModule Data-owned The bands calculateReachableRanges returns
CustomGeoJSONModule Data-owned Your own GeoJSON, with your own MapLibre layer specs
TrafficIncidentDetailsModule Data-owned Incidents you fetched with trafficIncidentDetails
TrafficAreaAnalyticsModule Data-owned Area analytics as a hexgrid, square grid or heatmap

Who owns what

Everything visible on the map has exactly one owner, whoever owns its layers. Every module has knobs of its own. Each style-owned module decides whether and how its part of the loaded style shows. The Styling Foundations module sets the foundations that reach across every part: sizes, the map colours, projection and sky. Each data-owned instance decides everything about the layers it adds itself. In the diagram, the dashed line marks the only place a knob reaches past its module. There, the data-owned modules read the accent as their default colour.

Style-owned modules: get(), one per mapStylingFoundationsModule: get(), one per mapData-owned modules: create(), many per mapMapLibre mapBaseMapModule · POIsModuleTrafficFlowModule · TrafficIncidentsModuleTerrainModuleTheir knobs: what is shown, and howvisible · filters · road partsPOI, traffic and hillshade looksIts knobs: the foundations of the whole mapsizes · map colours · projection · skycolors.accentPlacesModule · RoutingModule · GeometriesModuleReachableRangesModule · CustomGeoJSONModuleTrafficIncidentDetailsModule · TrafficAreaAnalyticsModuleEach instance's own knobscolor · label · width · markerType · …The loaded style's layersbase map · POIs · traffic tiles · hillshadeMap viewprojection · skySources and layers each instance adds(ids suffixed per instance) show, hide, filter, restylerestyle every partview.* knobs default colour, readdraw and style

In detail:

You want to change… Owner How
Whether something the loaded style draws is shown — a base-map layer group or road part, the traffic tiles, a POI category, the hillshade, the 3D surface The style-owned module for that part setVisible, updateConfig({ filters }), setHillshadeVisible, setElevationVisible, setKnob
How one part looks — POI size and label colours, traffic colours and widths, hillshade lighting The style-owned module for that part: POIs, traffic flow, traffic incidents, terrain setKnob, updateConfig
The foundations across every part — label, icon and road sizes, the 10 map colours, projection and sky The Styling Foundations module setKnob, setMapColors
A named bundle of looks across those modules applyMapPreset applyMapPreset(map, id)
Anything a data-owned module draws itself — routes, places, geometries, your GeoJSON, fetched traffic incidents, area analytics That module, through its own configuration create(map, config), applyConfig / updateConfig, setKnob and setVisible
The map's colour identity — the accent routes, places and geometries default to The Styling Foundations module publishes it; each data-owned module reads it setMapColors, the colors.accent knob; a colour set on the module always wins
A look a data-owned module borrows from the style, such as POI-looking places or the colours of fetched traffic incidents The module whose knob restyles it sets it on the style; the data-owned module reads it and redraws its own layers The knobs that restyle what it borrows; a setting given to the module always wins

Two consequences:

Within a style-owned module, the finer settings only narrow what its coarser ones show. A road part or the POI micro markers never show while their layer group or the POIs are hidden. Showing those again keeps a part's false. The POIs' zoomShift rewrites the same layer filters that filters.categories narrows, in either order.

To find the layers a module owns, read its sourceAndLayerIDs: the MapLibre source id and layer ids of each of its sources. Every module has it, style-owned or data-owned. Use it when your own code writes MapLibre layer properties and should leave a module's layers alone. On the base map, getLayerIds(group) narrows the list to one layer group. A road part, such as the arrows, has no layer list of its own. The Styling Foundations module owns no layers, so its list is empty.

const pois = await POIsModule.get(map);
pois.sourceAndLayerIDs.pois.layerIDs; // the POI layers, micro markers included

Read it again after a style switch or a configuration change, since a data-owned module can add or remove layers, such as a route's section layers.

Each module guide opens with a What this module owns line that applies this rule.

Writing to a module's layers yourself

Changing a module's layers with map.mapLibreMap is a fallback for visual tweaks no knob offers. It doesn't combine cleanly with the modules, so expect to set your values again:

Changing a setting

A module takes its settings in two ways, and which one you use depends on where the values come from:

Both write the same configuration, which getConfig() reads back. A knob id is the setting's path in it, so setKnob(placesModule, placesKnobCatalogue, 'label.color', '#c2185b') changes the label.color that updateConfig({ label: { color: '#c2185b' } }) writes:

Your codesettings it writes, typedSettings held as dataa control · storage · a URL · a language modelConfiguration methodsget / create(map, config)applyConfig(config)updateConfig(partial)Knob functionssetKnob(module, catalogue, id, value)setKnobs(module, catalogue, values)The module's knob cataloguean entry per plain-valued settingits id is the setting's pathRangeErrornothing changes, not even the valid onesThe module's configurationone object, read with getConfig()What the module drawsSettings with a knobvisible · color · label.color · widthFactor …Settings only code can writelayer specs · callbacks · MapLibre expressions checks each value a value refused any settingunchecked at run time at each id's paththe rest keptredraw, and config-change
Configuration methods Knob functions
A value of the wrong type Caught by TypeScript, not at run time Refused at run time, with a RangeError
A value out of range Applied as given Refused, and nothing changes
A nested setting updateConfig replaces the nested object you give it Set by its path, keeping the settings beside it

Note that the configuration methods don't check values at run time. A value from outside your code, such as JSON.parse output or a model's answer, can reach the map unchecked, so pass it to the knob functions instead.

updateConfig merges one level deep. A nested object you give it replaces the current one. To change one nested field, spread the current part or set its knob:

import { placesKnobCatalogue, setKnob, setKnobs } from '@tomtom-org/maps-sdk/map';

// Settings your code writes: typed, and any setting the module takes
placesModule.updateConfig({ label: { ...placesModule.getConfig()?.label, color: '#c2185b' } });

// Settings held as data: checked against the catalogue, set by path
setKnob(placesModule, placesKnobCatalogue, 'label.color', '#c2185b');
setKnobs(placesModule, placesKnobCatalogue, { 'label.color': '#c2185b', 'label.size': 14 });

Saving and replaying a configuration

getConfig() exports a configuration and applyConfig() imports it. A configuration that went through JSON and back into applyConfig holds the same settings and draws the same layers. Keep one to restore a user's map, attach it to a support report, or replay it in a test.

localStorage.setItem('places', JSON.stringify(placesModule.getConfig()));

// Later, on the same module or a new one
placesModule.applyConfig(JSON.parse(localStorage.getItem('places')));

applyConfig doesn't check what it reads back. If the saved settings can come from anywhere other than your own getConfig(), for example an older release or a user's edit, save knob values by id instead and restore them with setKnobs. It checks each value against its catalogue entry and sets none if one is refused:

import { placesKnobCatalogue, setKnobs } from '@tomtom-org/maps-sdk/map';

localStorage.setItem('places', JSON.stringify({ 'label.color': '#c2185b', 'label.size': 14 }));

setKnobs(placesModule, placesKnobCatalogue, JSON.parse(localStorage.getItem('places'))); // RangeError if one is refused

The map effects playground saves its panel this way.

Most module configurations, styling knobs included, are fully JSON. Five modules also take functions or image elements, which JSON can't carry. Each has a type for the part that survives, detailed on its reference page. When you replay, add the rest back in code:

Module Serializable part Left out
PlacesModule PlacesModuleSerializableConfig label.title as a function, connections.label.title, icon.mapping, extraFeatureProps functions, image elements in icon
RoutingModule RoutingModuleSerializableConfig waypoints.icon.mapping, waypoints.label.title as a function, image elements in waypoints.icon
GeometriesModule GeometriesModuleSerializableConfig transformFeaturesForDisplay
ReachableRangesModule ReachableRangesModuleSerializableConfig lineLabel.title
CustomGeoJSONModule CustomGeoJSONModuleSerializableConfig images

JSON.stringify silently drops a function, but turns an image element into {}, which doesn't replay. To keep the whole configuration serializable, give icon images as URLs, data URIs or SVG text, and a place or waypoint title as a MapLibre expression. POIsModule's icon.categoryIcons also takes image elements, with no type that leaves them out.

const state = JSON.parse(saved); // PlacesModuleSerializableConfig
placesModule.applyConfig({
    ...state,
    connections: { ...state.connections, label: { ...state.connections?.label, title: (connection) => connection.name } },
});

The configuration is what a module draws with, not the data it shows, so replay show() with the data too.

Appearance vocabulary

A concept has the same name on every module that has it. The table lists the shared appearance terms, the rule each follows, and the exact properties that carry it. A module missing from a row doesn't have that concept.

Concept The rule Modules and properties
The module's colour A top-level color is the colour of what the module draws. If unset, it is the map's accent (colors.accent, published as map.mapColors.accent), then the module's own default. A part-level colour overrides it. RoutingModule: color, under layers.* paint overrides · GeometriesModule: color, under fill.palette, fill.color, line.color and a feature's color property (order) · PlacesModule: color, under icon.default.style.fillColor, label.color and layers.*
Labels One label shape, LabelConfig: size, color, haloColor, haloWidth, opacity, font, typeface, customTypeface. Each module adds the label's content and its placement: a distance from the icon, or an offset as [x, y]; FeatureLabelConfig adds a title, a function or a MapLibre expression. PlacesModule: label (PlaceLabelConfig, adds title, and distance as one number of ems away from the icon) · RoutingModule: waypoints.label (WaypointLabelConfig, adds title and visible) · GeometriesModule: label (GeometryLabelConfig, adds title, a string or expression that defaults to the feature's own title), and lineLabel (GeometryLineLabelConfig, the same title plus minZoom, symbolSpacing, and offset as [x, y] ems) · ReachableRangesModule: lineLabel (ReachableRangeLineLabelConfig, adds a title function)
Typeface A label's typeface is one of the style's faces, mapTypefaces (sans, sans-italic, serif, serif-italic), and each label keeps its weight. A customTypeface of your own font files wins over it, and font, MapLibre's list of faces, wins over both. Leave both out and a module's labels follow the map's typeface. Map-wide: the Styling Foundations knob labels.typeface, and labels.customTypeface · PlacesModule: label.typeface, label.customTypeface · RoutingModule: waypoints.label.typeface, waypoints.label.customTypeface · GeometriesModule: label.* and lineLabel.* · ReachableRangesModule: lineLabel.*
Multipliers A *Factor multiplies a default and is clamped to its catalogue entry's range, which knobEntryOf(catalogue, id) reads. Where a size also has presets ('s', 'm', 'l'), a *Preset picks the base and the factor multiplies it. RoutingModule: widthPreset and widthFactor (0.5 to 2), waypoints.sizePreset and waypoints.sizeFactor (0.5 to 2), sections.<type>.widthPreset and sections.<type>.widthFactor, sections.<type>.icon.sizeFactor (0.25 to 2), highlight.outline.widthFactor (1 to 1.25), highlight.section.outline.widthFactor (0.5 to 2), waypoints.highlight.sizeFactor (1 to 1.5) · GeometriesModule: highlight.fill.opacityFactor (1 to 4) and highlight.line.widthFactor (1 to 3) · TrafficIncidentDetailsModule: highlight.line.widthFactor (1 to 1.8) · POIsModule: sizeFactor (0.5 to 1.5) · TrafficFlowModule and TrafficIncidentsModule: widthFactor (0.5 to 2) · Styling Foundations knobs: labels.sizeFactor, symbols.sizeFactor, roads.widthFactor (0.5 to 1.5)
Hovered and clicked look A feature a handler covers, or one setEventState puts a state on, draws one look whether hovered or clicked. highlight holds the factors that look multiplies by; a factor of 1 draws the feature as any other. A MapLibre layer you give in place of the default draws as given. RoutingModule: highlight.outline.widthFactor, waypoints.highlight.sizeFactor, and highlight.section.outline.color and highlight.section.outline.widthFactor on the sections setSectionFocus marks · GeometriesModule: highlight.fill.opacityFactor, highlight.line.widthFactor · TrafficIncidentDetailsModule: highlight.outline.color, highlight.line.widthFactor, also drawn on the incidents setFocus marks, highlight: false to turn it off · PlacesModule: layers.selected
Absolute units A value that mirrors a MapLibre property keeps MapLibre's name, unless the SDK uses that word for another concept, and is in pixels. Any other unit is in the name. Opacities run from 0 to 1, label offsets are in ems. Pixels: LabelConfig size and haloWidth, GeometriesModule line.width and lineLabel.symbolSpacing, PlacesModule cluster.badge.outlineWidth and cluster.badge.radius · Metres: TrafficAreaAnalyticsModule height.maxMeters, height.minMeters and, per unit of the metric, height.metersPerUnit · Minutes: TrafficIncidentsModule filters.delays.minMinutes · Strengths from 0 to 1: TerrainModule hillshade.intensity, which raises nothing; its elevation.exaggeration scales the raised surface instead
Marker type, fill style, map theme Theme means only the map-wide look. How one module draws its own features has a name of its own. PlacesModule: markerType (PlacesMarkerType; placesMarkerTypes lists pin, circle-icon, base-map, pin-clustered) · GeometriesModule: fill.style (GeometryFillStyle; geometryFillStyles lists filled, outline, inverted), per feature properties.fillStyle · ReachableRangesModule: fill.style, the same values · TrafficAreaAnalyticsModule: regionPolygon.fill.style, filled or inverted · Map theme: StylingFoundationsModule.setMapColors, fed by the Map Theme plugin's deriveMapColors, deriveMapColorsFromCss, deriveMapColorsFromImage and mapColorsLightDark
Fill colour or palette A colour field takes a colour or a MapLibre expression, never a palette name. A palette is its own field: on geometries and reachable ranges a list of your own CSS colours, which the features or bands take one after another; on area analytics a name, ramped over the metric's value. GeometriesModule: fill.color, fill.palette, unset drawing one colour, the accent · ReachableRangesModule: fill.palette, unset ramping from the accent toward the map's land colour · TrafficAreaAnalyticsModule: metrics.<metric>.palette (AreaAnalyticsPalette; areaAnalyticsPalettes lists the names), and metrics.<metric>.colorStops, which wins over it
Zoom thresholds minZoom and maxZoom. PlacesModule: entryPoints.minZoom · GeometriesModule: lineLabel.minZoom · RoutingModule: sections.<type>.sign.minZoom, countryCrossings.minZoom · POIsModule: minZoom · TerrainModule: hillshade.maxZoom
Borrowed looks A part drawn to look like the style follows the knobs that restyle what it borrows. A look set on the module wins. PlacesModule with markerType base-map, circle-icon or pin-clustered · RoutingModule sections.traffic · TrafficIncidentDetailsModule line colours. The knobs each one follows: Borrowed looks
Visibility visible in the config, setVisible(visible) to change it, isVisible() to read it back. isVisible() returns the setting, the same value as the module's visible knob. On a data-owned module, hiding keeps the data, show() and clear() leave it hidden, and getShown() returns what is shown. On a style-owned module that nothing has set, isVisible() reads what the style draws, and the traffic modules are hidden until shown; getRenderedFeatures() returns what its tiles render in view. A part that hides on its own has its own visible. All three on PlacesModule, RoutingModule, GeometriesModule, ReachableRangesModule, CustomGeoJSONModule, TrafficIncidentDetailsModule, TrafficAreaAnalyticsModule, POIsModule, TrafficFlowModule and TrafficIncidentsModule (plus icons.visible with setIconsVisible / isIconsVisible) · BaseMapModule: visible, groups.<group>.visible, setVisible and isVisible with { layerGroups } · RoutingModule per part: summaryBubbles.visible, countryCrossings.visible, sections.<type>.visible · TerrainModule: hillshade.visible with setHillshadeVisible / isHillshadeVisible, elevation.visible with setElevationVisible / isElevationVisible · Styling Foundations knobs: view.sky.visible
Layer position beforeLayerConfig takes 'top' or a named style layer such as 'lowestLabel' (BeforeLayerConfig) for all of a module's layers. A module drawing several parts also takes a record keyed by its part names, all covering the parts it leaves out (PartsBeforeLayerConfig). GeometriesModule and ReachableRangesModule: GeometryBeforeLayerConfig, parts fill, line · RoutingModule: RoutingBeforeLayerConfig, parts mainLines, instructionLines, icons · PlacesModule: PlacesBeforeLayerConfig, parts places, connections · TrafficAreaAnalyticsModule: AreaAnalyticsBeforeLayerConfig, one part per display mode · TrafficIncidentDetailsModule: BeforeLayerConfig alone · CustomGeoJSONModule: CustomGeoJSONBeforeLayerConfig, for all its layers and on each layer, which also takes { layerID } to go below a layer outside the style
Value filters One filter shape, ValuesFilter: { show, values }, where show is one of filterShowModes, 'only' (just the values) or 'all-except' (everything but them). POIsModule: filters.categories · TrafficFlowModule and TrafficIncidentsModule: filters fields such as roadCategories and, on incidents, magnitudes · BaseMapModule: the layerGroups that setVisible, isVisible and events.where take

Every setting as data

Each module publishes its display settings as a knob catalogue, static data you can read with no map loaded. A control panel builds its controls from it, and an agent checks values against it. An entry's id is the setting's path in the module's configuration, and never points into a list. A list is one knob of kind colors, enums or offset, or no knob. The kind says which value the entry takes and which fields come with it. The entry type is KnobEntry, and the kinds are KnobKind:

Kind Value Fields
toggle true or false
factor a multiplier, 1 keeping what the style does range
number a number range
offset an [x, y] displacement, x to the right and y down, in the unit the description names range, for x and y each
color a CSS colour
enum one of the options options
enums a list of the options; the description says when their order matters options
colors a list of CSS colours or of the names in options options, where there are names
image the id of an image in the style's sprite
text any string

Setting a knob by its id

These functions work with a knob by its catalogue id on every module, including the Styling Foundations module and the map effects plugin's MapEffects:

The path is the same for every module, and a knob only ever redraws what its own module draws:

One catalogue per modulestatic data, no map neededA valuefrom a control, saved settings or an agentSet knobs by idvalidateKnobValueevery value against its entryRangeErrornothing changesThat module's configurationthe settings at their paths changed, the rest keptWhat that module draws, and nothing elseevents: 'config-change'placesKnobCatalogueroutingKnobCataloguetrafficFlowKnobCatalogue…stylingFoundationsKnobCatalogueeffectKnobCatalogue (map effects plugin)Each entry: id · kind · range or options · defaultkinds shared by every cataloguesetKnob(module, catalogue, id, value)setKnobs(module, catalogue, values) the entry for the id invalidvalidredraw
import { getKnob, knobEntryOf, resetKnob, setKnob, trafficFlowKnobCatalogue, validateKnobValue } from '@tomtom-org/maps-sdk/map';

setKnob(trafficFlow, trafficFlowKnobCatalogue, 'filters.roadCategories.values', ['motorway', 'trunk']);
getKnob(trafficFlow, trafficFlowKnobCatalogue, 'visible'); // false until set: the entry's default
resetKnob(trafficFlow, trafficFlowKnobCatalogue, 'filters.roadCategories.values');

const knob = knobEntryOf(trafficFlowKnobCatalogue, 'filters.roadCategories.values'); // kind, range or options, default
validateKnobValue(knob, ['motorway', 'cart track']); // RangeError: … 'cart track' is not

The traffic config playground sets every flow and incidents knob this way, with controls built from the two catalogues.