Geometries module
GeometriesModule draws polygons on the map, each with a fill, a border and a label. They can be administrative
boundaries from the geometry data service, or any GeoJSON Polygon and
MultiPolygon features of your own.
- It owns the fills, borders and labels it draws and how they look, through
color,fill,line, the labels, the fill style and the palette. - It never restyles the base map, which belongs to the Styling Foundations module. Styling knobs never reach geometry layers, and a geometry with no colour takes the map's accent. Who owns what draws the line for every module.
Showing geometries
Initializing geometry module
import { TomTomConfig } from '@tomtom-org/maps-sdk/core';
import { TomTomMap, GeometriesModule } from '@tomtom-org/maps-sdk/map';
TomTomConfig.instance.put({ apiKey: 'YOUR_API_KEY' });
const map = new TomTomMap({ mapLibre: { container: 'map' } });
const geometryModule = await GeometriesModule.create(map);
Displaying administrative boundaries
import { geocode, geometryData } from '@tomtom-org/maps-sdk/services';
const place = (await geocode({ query: 'Amsterdam, Netherlands' })).features[0];
const geometryResponse = await geometryData({ geometries: [place] });
await geometryModule.show(geometryResponse);
show replaces the geometries shown, and clear removes them.
Custom styling configuration
One top-level color sets the fill and the border together:
const geometryModule = await GeometriesModule.create(map, { color: '#E63312' });
The colours resolve in this order, and the first match wins:
| Part | Precedence |
|---|---|
| Fill | a feature's own color property → fill.color → fill.palette → color → the map's accent → the default |
| Border | line.color → a feature's own color property → fill.color → fill.palette → color → the map's accent → the default |
- The accent applies when a theme sets one.
- The default follows the map style: navy
#0A3653on light styles, sky blue#5FA8D8on dark ones. - With
fill.style: 'outline', the border is a neutral grey,#555555, instead of the accent or the default.
fill and line configure the fill and the border separately. The fill takes fill: { color, opacity }, or a
palette instead of one colour. The border takes line: { color, width, opacity }:
const geometryModule = await GeometriesModule.create(map, {
fill: { color: '#007cbf', opacity: 0.3 },
line: { color: '#005a8b', width: 2, opacity: 0.8 },
beforeLayerConfig: 'lowestLabel' // below all the map's labels
});
For anything else, layers takes a partial MapLibre layer, without id or source, for each of the module's
layers: fill, line (the border), label (the center labels) and lineLabel (the border labels). An entry
can set any property of its layer. Its paint and layout merge per property onto the module's own and
override the curated fields:
const geometryModule = await GeometriesModule.create(map, {
line: { color: '#00A65E', width: 2 },
layers: {
line: {
paint: {
'line-offset': 2, // inset/outset border, in pixels
'line-blur': 4, // glow, in pixels
'line-dasharray': [2, 2], // dash pattern in line-width units
'line-gap-width': 3 // hollow casing
},
layout: { 'line-cap': 'round' }
},
label: { layout: { 'text-transform': 'uppercase' } }
}
});
beforeLayerConfigalone positions the layers, so abeforeIDin an entry is ignored.- A layer hidden through
layout.visibilitystays hidden while the module is shown. updateConfig({ layers })repaints the geometries shown without preparing them again.
Restyling shown geometries
A config change to the colours, fill style, labels or transformFeaturesForDisplay updates the geometries on the
map without a new show(). applyConfig replaces the whole config, updateConfig changes only what you pass,
and resetConfig restores the defaults:
await geometryModule.show(cityBoundaries);
geometryModule.updateConfig({ fill: { style: 'outline', palette: ['#e63312', '#f5a000'] } });
geometryModule.updateConfig({ label: { title: ['get', 'name'] } }); // keeps the palette and fill style
geometryModule.resetConfig(); // default colours, fill style and labels
getConfig() returns the settings to store. All of it survives JSON except the transformFeaturesForDisplay
function. GeometriesModuleSerializableConfig types what you parse back, and you add the function in code:
import type { GeometriesModuleSerializableConfig } from '@tomtom-org/maps-sdk/map';
const saved: GeometriesModuleSerializableConfig = JSON.parse(JSON.stringify(geometryModule.getConfig()));
geometryModule.applyConfig({ ...saved, transformFeaturesForDisplay: addAreaLabels });
Hovered and clicked geometries
Once a handler covers the geometries, a hovered or clicked geometry draws a denser fill and a wider border in its own colour. The module's event state methods set that state from your own UI:
geometryModule.events.on('click', (geometry) => console.log(geometry.properties.name));
geometryModule.setEventState({ index: 0, state: 'click' });
An id is the properties.id that getShown() and the handlers return. The module generates one for a
feature that has none, so a geometry data result's top-level id matches nothing.
highlight sets how much denser the fill gets and how much wider the border. Try it in the geometry config playground:
- By default, the fill opacity doubles, up to fully opaque, and the border width grows 1.75 times.
- The factors multiply the current opacity and width, including a
fill.opacityorline.widthyou set. - A factor of
1draws a hovered geometry like any other, keeping its look fixed. - A width you set through
layers.linestays the same in every state.
geometryModule.applyConfig({
fill: { opacity: 0.3 },
highlight: { fill: { opacityFactor: 3 }, line: { widthFactor: 1 } }, // 0.9 while hovered, the border as is
});
Events
events covers the geometries and all their labels, and a handler receives the feature as drawn. Two named
scopes each cover one surface, with the same handlers and event states.
events.geometry covers the fills, borders and border labels, and events.geometryLabel covers the center labels.
geometryModule.events.on('click', (geometry, lngLat) => console.log(geometry.properties.title, lngLat));
geometryModule.events.geometry.on('hover', (geometry) => showTooltip(geometry.properties.title));
The user events guide covers the event types and their timings.
Showing and hiding
setVisible hides and shows the fills, borders, center labels and border labels together. To start the module
hidden, pass visible: false at creation. show() and clear() never change visibility, so geometries shown
while hidden appear on setVisible(true). The state survives a style change.
function toggleGeometry() {
geometryModule.setVisible(!geometryModule.isVisible());
}
Layer positioning and styling
A single beforeLayerConfig positions all geometry layers together. The record form { all?, fill?, line? }
places the fill and the border separately:
- Each part uses its own entry, otherwise
all, otherwise its default place just below the geometry titles. 'top'keeps the titles above everything else.updateConfig({ beforeLayerConfig })moves the layers by the same rules, so a part with no target in the new value returns to its default place.
const geometryModule = await GeometriesModule.create(map, {
fill: { color: '#87CEEB', opacity: 0.5 },
line: { color: '#4682B4', width: 1.5 },
// Fill slips below the road network, the border below the map's labels
beforeLayerConfig: { fill: 'lowestRoadLine', line: 'lowestLabel' }
});
Labels
Each polygon whose feature has a title property gets a center label, which label styles. lineLabel
repeats it along the border, but only when you set it. Pass {} for the defaults.
- Both take a
title, a string or a MapLibre expression that defaults to the feature'stitle. - Both take the styling fields every module's labels share:
size,color,haloColor,haloWidth,opacityandfont. lineLabeladdsminZoom,symbolSpacingandoffset.- Colours you don't set follow the map style, with dark text on a white halo on light styles and the reverse on dark ones.
const module = await GeometriesModule.create(map, {
label: { title: ['get', 'name'], size: 18, color: '#FFFFFF', haloColor: '#000000' },
lineLabel: { title: ['get', 'name'], minZoom: 10, size: 13, offset: [0, 1], symbolSpacing: 350 },
});
// Later: restyle the center labels of the shown geometries, keeping their other settings
module.updateConfig({ label: { ...module.getConfig()?.label, size: 20 } });
Deriving display properties
transformFeaturesForDisplay receives what you passed to show() and returns the features to draw, so a label or a
colour can come from your own domain data. It runs on every show(), and again whenever a config change alters
what is drawn:
const module = await GeometriesModule.create(map, {
transformFeaturesForDisplay: (geometries) => ({
...geometries,
features: geometries.features.map((feature) => ({
...feature,
properties: { ...feature.properties, title: `${feature.properties?.name} (${feature.properties?.population})` },
})),
}),
});
Fill styles
| Fill style | Effect |
|---|---|
'filled' |
Semi-transparent fill (default) |
'outline' |
Transparent fill with a thick border |
'inverted' |
Semi-transparent area outside the polygon |
Setting the fill style at config level
fill.style applies one fill style to every feature, and a feature's own fillStyle property overrides it:
import { GeometriesModule } from '@tomtom-org/maps-sdk/map';
// Highlight "rest of the world" outside a country
const module = await GeometriesModule.create(map, {
fill: { style: 'inverted', color: 'black', opacity: 0.5 },
});
await module.show(countryGeometry);
Using fillStyledGeometryConfig for mixed fill styles
For features in one module with different fill styles, fillStyledGeometryConfig({ palette?, beforeLayerConfig? })
returns a config whose expressions read each feature's fillStyle property. beforeLayerConfig defaults to
'lowestLabel', and the config draws the border labels too.
import { GeometriesModule, fillStyledGeometryConfig } from '@tomtom-org/maps-sdk/map';
const module = await GeometriesModule.create(map, fillStyledGeometryConfig({ palette: ['#0077b6', '#90e0ef'] }));
await module.show({
type: 'FeatureCollection',
features: [
{ ...featureA, properties: { fillStyle: 'filled', title: 'Zone A' } },
{ ...featureB, properties: { fillStyle: 'outline', title: 'Zone B' } },
],
});
Color palettes
fill.palette is a list of CSS colours that the features take in turn, starting over after the last, so neighbouring
polygons stay distinct without a colour assigned to each.
- An empty list counts as unset. Without a palette, every feature draws in one colour, the map's accent or the default.
- The precedence table shows where
fill.palettesits among the colours. - To match the map, take the palette from the colours the map is themed from. The
map-theme plugin's
deriveMapColorsreturns them by role.
import { fillStyledGeometryConfig, GeometriesModule } from '@tomtom-org/maps-sdk/map';
import { deriveMapColors } from '@tomtom-org/maps-sdk-plugin-map-theme';
const regions = await GeometriesModule.create(map, {
fill: { palette: ['#ffadad', '#ffd6a5', '#caffbf', '#a0c4ff'], opacity: 0.3 },
});
// fillStyledGeometryConfig takes it as its `palette` option
const { accent, water, roadMajor } = deriveMapColors(['#1d3f8a', '#e63312', '#f5d000', '#f4f1ea']);
const zones = await GeometriesModule.create(map, fillStyledGeometryConfig({ palette: [accent, water, roadMajor] }));
Reachable ranges have their own reachable ranges module. It titles each band from its budget, nests each origin's bands and colours them from the map.
Every setting as data
geometriesKnobCatalogue lists every plain-valued display setting of the module with its kind, its range or
options, and its default where one value holds everywhere. Each id is the setting's path in the config, so a UI
can build its controls from it with no map loaded:
import { geometriesKnobCatalogue } from '@tomtom-org/maps-sdk/map';
const borderKnobs = geometriesKnobCatalogue.filter(({ id }) => id.startsWith('line.'));
// [{ id: 'line.color', kind: 'color', description: '…' }, …,
// { id: 'line.width', kind: 'number', description: '…', range: { min: 0, max: 20, step: 0.5, bounds: 'hard-min' } }]
| Id | Kind |
|---|---|
color, visible |
colour, toggle |
fill.style, fill.color, fill.palette, fill.opacity |
enum, colour, colours, number |
line.color, line.opacity, line.width |
colour, number, number |
highlight.fill.opacityFactor, highlight.line.widthFactor |
factor, 1 to 4 and 1 to 3 |
label.size, label.color, label.haloColor, label.haloWidth, label.opacity, label.font, label.typeface |
number, colour, colour, number, number, enums, enum |
lineLabel.size, lineLabel.color, lineLabel.haloColor, lineLabel.haloWidth, lineLabel.opacity, lineLabel.font, lineLabel.typeface, lineLabel.minZoom, lineLabel.symbolSpacing, lineLabel.offset |
the label knobs, then number, number, offset |
- Opacities and widths have no
defaultentry where the fill style decides them, and colours have none where the accent or the map style does. - Setting any
lineLabelknob draws the border labels. - A
fontknob takes its options frommapFonts. Order matters, since the first font the map has wins. Unset, the labels are inNoto-Boldset in their typeface, andgetKnobreturnsundefined. lineLabel.offsetis in ems, x along the line and y across it, and defaults to[0, 1].geometriesKnobIdslists the ids in catalogue order,GeometriesKnobIdis their union, andGeometriesKnobValueOf<ID>is the plain value one takes.- It leaves out
beforeLayerConfig,layers, the labels'titleandtransformFeaturesForDisplay, which take structured, MapLibre or function values, andevents. - Its entries share their shape,
KnobEntry, with every module's catalogue.
Multiple module instances
Each instance has its own sources, layers and configuration, so several can show different sets of geometries side by side:
const countryBorders = await GeometriesModule.create(map, {
fill: { color: '#E74C3C', opacity: 0.2 },
line: { color: '#C0392B', width: 3 }
});
const cityBounds = await GeometriesModule.create(map, {
fill: { color: '#3498DB', opacity: 0.3 },
line: { color: '#2980B9', width: 2 }
});
await countryBorders.show(countryData);
await cityBounds.show(cityData);
Reading shown data
getShown() returns what the module draws, as prepared for display:
geometry- The polygons with the colours, fill styles and titles the config gave them. Aninvertedone covers the area outside it.geometryLabel- The points where their center labels sit.
const { geometry } = geometryModule.getShown();
console.log(`Geometries: ${geometry.features.length}`);
Related guides and examples
Services integration
- Geometry data service - Boundary and shape data for administrative areas and places
- Search services - Finding places and administrative areas that have geometry data
Related examples
- Basic geometry - Simple geometry display and styling
- Multiple geometries - Several geometry layers and data sets
- Multiple geometry modules - Several modules managed side by side
- Geometry config playground - Fill and border colours, layer-stack positions, visibility and the clicked look
- Places in geometry - Geometry boundaries combined with place markers
- Geometry search playground - Searching inside a geometry
Related map modules
- Reachable ranges module - Ranges calculated by budget, drawn as bands nested around their origins
- Places module - Places and POIs within or around geometric boundaries
- Routing module - Routes through geometric areas
- User interaction events - User interactions with geometric shapes and boundaries