Geometries module
GeometriesModule draws polygons on the map: administrative boundaries from the
geometry data service, or any GeoJSON Polygon and
MultiPolygon features of your own, each with a fill, a border and a label.
What this module owns: the fills, outlines and labels it draws, and how they look — the color, fill, line, label, fill style and palette configuration below. It never restyles the base map underneath, which belongs to the Styling module, and styling knobs never touch geometry layers: geometries given no colour read the map's accent as their default. See Who owns what.
Showing geometries
Initializing geometry module
import { TomTomConfig } from '@tomtom-org/maps-sdk/core';
import { TomTomMap, GeometriesModule } from '@tomtom-org/maps-sdk/map';
// Configure SDK and create map
TomTomConfig.instance.put({ apiKey: 'YOUR_API_KEY' });
const map = new TomTomMap({ mapLibre: { container: 'map' } });
// Initialize geometry module with default styling
const geometryModule = await GeometriesModule.create(map);
Displaying administrative boundaries
import { geocode, geometryData } from '@tomtom-org/maps-sdk/services';
// Get geometry for a specific place
const place = (await geocode({ query: 'Amsterdam, Netherlands' })).features[0];
const geometryResponse = await geometryData({ geometries: [place] });
// Display the geometry on the map
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, 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 adapts to the map style: navy #0A3653 on
light styles, sky blue #5FA8D8 on dark ones. With fillStyle: 'outline', the
border ends on a neutral grey instead of the accent or the default.
Fill and border are configured independently via fill and line. Each exposes concise
curated fields for the common cases — fill: { color, opacity } (or a
palette of colours instead of one) and line: { color, width, opacity }:
// Initialize with custom styling
const geometryModule = await GeometriesModule.create(map, {
fill: {
color: '#007cbf', // Fill color for polygons
opacity: 0.3 // Fill transparency
},
line: {
color: '#005a8b', // Border color
width: 2, // Border width
opacity: 0.8 // Border transparency
},
beforeLayerConfig: 'lowestLabel' // Layer positioning (below all labels)
});
For what the curated fields don't cover, layers takes a partial MapLibre layer for each of the
module's layers — fill, line (the border), label (the center labels) and lineLabel (the
border labels) — without id/source. It can set any property of that layer: an inset/outset
border (line-offset), a soft glow (line-blur), a dashed border (line-dasharray), a hollow
casing (line-gap-width), line-cap, text-transform, and more. paint and layout merge per
property onto the module's own, and what you set there takes precedence over the curated fields:
const geometryModule = await GeometriesModule.create(map, {
line: { color: '#00A65E', width: 2 },
layers: {
line: {
paint: {
'line-offset': 2, // Lateral offset in pixels (inset/outset border)
'line-blur': 4, // Edge blur in pixels (glow/halo effect)
'line-dasharray': [2, 2], // Dash pattern in line-width units ([dash, gap, …])
'line-gap-width': 3 // Hollow casing
},
layout: { 'line-cap': 'round' }
},
label: { layout: { 'text-transform': 'uppercase' } }
}
});
Where the layers sit stays beforeLayerConfig's, so a beforeID in an entry is ignored. A layer an
entry hides through layout.visibility stays hidden while the module is shown. updateConfig({ layers })
repaints the geometries shown without preparing them again. The older line: { layer } still works, under
the curated fields rather than over them, and is deprecated in favour of layers.line.
Restyling shown geometries
A config change reaches the geometries already on the map — their colours, fill style, labels and
transformFeaturesForDisplay — without calling show() again. applyConfig replaces the whole
config, so whatever it leaves out returns to the default; updateConfig changes only what it is
given, and resetConfig returns to the defaults.
await geometryModule.show(cityBoundaries);
geometryModule.updateConfig({ fill: { palette: ['#e63312', '#f5a000'] }, fillStyle: 'outline' });
geometryModule.updateConfig({ label: { text: ['get', 'name'] } }); // keeps the palette and fill style
geometryModule.resetConfig(); // default colours, fill style and labels
getConfig() returns the settings to store. Everything in it survives JSON except
transformFeaturesForDisplay, a function, so GeometriesModuleSerializableConfig types what you
parse back, and a replay adds the function again 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, the one under the pointer draws a denser fill and a wider border, in its own colour, and so does a clicked one. Put that state from your own UI with the module's event state methods:
geometryModule.events.on('click', (geometry) => console.log(geometry.properties.name));
geometryModule.putEventState({ id: 'amsterdam', state: 'click' });
highlight sets how much denser and wider, live on the geometry config playground:
Unset, the fill opacity
doubles, at most opaque, and the border width grows 1.75 times. The factors multiply whatever
opacity and width are in force, a fill.opacity or line.width you configure included, and 1
draws a hovered geometry as any other, which is how to keep its look fixed:
geometryModule.applyConfig({
fill: { opacity: 0.3 },
highlight: { fillOpacityFactor: 3, lineWidthFactor: 1 }, // 0.9 while hovered, the border as is
});
A width you give the border through layers.line is drawn as given, in every state.
Events
events covers the geometries and their center labels; a handler receives the feature as drawn. Two
named scopes single one surface out: events.geometry for the fills and borders,
events.geometryLabel for the labels. Both take the same handlers and
event states:
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, and
visible: false at creation starts the module hidden. show() and clear() never change it:
geometries shown while hidden appear on setVisible(true), and the state survives a style change.
// Toggle geometry visibility, keeping the data
function toggleGeometry() {
geometryModule.setVisible(!geometryModule.isVisible());
}
Layer positioning and styling
A single beforeLayerConfig positions all geometry layers together. To place the fill and
border at different points in the stack, pass the record form { all?, fill?, line? }: a part
takes its own entry, else all, else 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 the new value names no target for goes back 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 while the border stays above the labels
beforeLayerConfig: { fill: 'lowestRoadLine', line: 'lowestLabel' }
});
Labels
A center label is shown for each polygon whenever the feature carries a title property. label
styles it; lineLabel repeats it along the border, and is drawn only when set ({} for the
defaults). Both take a text — a string or a MapLibre expression, the feature's title when unset —
and the same styling fields as every module's labels: size, color, haloColor, haloWidth,
opacity and font. lineLabel adds minZoom, symbolSpacing and offset:
const module = await GeometriesModule.create(map, {
label: { text: ['get', 'name'], size: 18, color: '#FFFFFF', haloColor: '#000000' },
lineLabel: { text: ['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 } });
Unset label fields keep colours that adapt to the map style: dark text with a white halo on light styles, the reverse on dark ones.
Deriving display properties
transformFeaturesForDisplay receives what show() was given and returns the features to draw, so a
label or a colour can come from your own domain data rather than from properties prepared up front.
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
The SDK supports three fill styles for polygon display:
| 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
The simplest way to apply a fill style is via the fillStyle option in GeometriesModuleConfig:
import { GeometriesModule } from '@tomtom-org/maps-sdk/map';
// Highlight "rest of the world" outside a country
const module = await GeometriesModule.create(map, {
fillStyle: 'inverted',
fill: { color: 'black', opacity: 0.5 },
});
await module.show(countryGeometry);
Individual features can override the config-level fill style by setting fillStyle in their own properties.
Using fillStyledGeometryConfig for mixed fill styles
When features in the same layer need different fill styles, use fillStyledGeometryConfig({ palette?, beforeLayerConfig? }). It returns a config with MapLibre expressions that read each feature's fillStyle property and apply the appropriate style:
import { GeometriesModule, fillStyledGeometryConfig } from '@tomtom-org/maps-sdk/map';
const module = await GeometriesModule.create(map, fillStyledGeometryConfig({ palette: ['#0077b6', '#90e0ef'] }));
// Features carry their own fill style — styling adapts per feature
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 the features take in turn, starting over after the last, so neighbouring polygons stay apart without assigning a colour to each. An empty list counts as unset, and with no palette every feature draws in one colour: the map's accent, or the default.
To match a palette to the map, take it from the same colours the map is themed from: the map-theme plugin's deriveMapColors returns 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] }));
fill.color takes a colour or an expression, fill.palette a list of colours; where each sits in the order is the precedence table above.
Reachable ranges have a module of their own, ReachableRangesModule, which titles each band from its budget, nests each origin's bands and colours them from the map. See Reachable ranges module.
Every setting as data
geometriesKnobCatalogue lists every plain-valued display setting of the module with its kind, its
range or options and, where one value holds everywhere, its default. 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, fillStyle |
colour, toggle, enum |
fill.color, fill.palette, fill.opacity |
colour, colours, number |
line.color, line.opacity, line.width |
colour, number, number |
highlight.fillOpacityFactor, highlight.lineWidthFactor |
factor, 1 to 4 and 1 to 3 |
label.size, label.color, label.haloColor, label.haloWidth, label.opacity, label.font |
number, colour, colour, number, number, enums |
lineLabel.size, lineLabel.color, lineLabel.haloColor, lineLabel.haloWidth, lineLabel.opacity, lineLabel.font, lineLabel.minZoom, lineLabel.symbolSpacing, lineLabel.offset |
the label knobs, then number, number, offset |
- Opacities and widths have no
defaultentry where the fill style decides it, and colours none where the accent or the map style does. - Setting any
lineLabelknob draws the border labels. - A
fontknob's options aremapFonts; its order matters, since the first font the map has wins. lineLabel.offsetis in ems, x along the line and y across it,[0, 1]unless set.geometriesKnobIdslists the ids in catalogue order,GeometriesKnobIdis their union, andGeometriesKnobValueOf<ID>the plain value one takes.- Not in it:
beforeLayerConfig,layersand the deprecatedline: { layer }, the labels'text, andtransformFeaturesForDisplay, 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 and layers, configuration and styling, so several can show different sets of geometries side by side.
// Create separate instances for different geometry types
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 }
});
// Each instance manages its own geometries independently
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 (an inverted one as the area outside it),
and geometryLabel, the points their center labels sit at.
const { geometry } = geometryModule.getShown();
console.log(`Geometries: ${geometry.features.length}`);
Related guides and examples
Services integration
- Geometry Data Service - Fetch detailed boundary and shape data for administrative areas and places
- Search Services - Find places and administrative areas that have associated geometry data
Related examples
- Basic Geometry - Simple geometry display and styling examples
- Multiple Geometries - Working with multiple geometry layers and data sets
- Multiple Geometry Modules - Advanced geometry module management and organization
- Geometry Config Playground - Fill and border colours, layer-stack positions, visibility and the clicked look
- Places in Geometry - Combining geometry boundaries with place markers
- Geometry Search Playground - Interactive geometry-based search and discovery
Related map modules
- Reachable ranges module - Ranges calculated by budget, drawn as bands nested around their origins
- Places Module - Display places and POIs within or related to geometric boundaries
- Routing Module - Show routes that intersect or travel through geometric areas
- User Interaction Events - Handle user interactions with geometric shapes and boundaries