Base map module

The Base Map Module shows and hides the base map (land, water, buildings, roads, borders and their labels) by layer group, and reports the user events on it. The demo below toggles each group:

The module decides whether each base-map layer group shows, 3D buildings and road shields included. It also owns the road parts within them (road arrows, exit numbers…), the user events on them, and how a hovered, clicked or coloured 3D building looks. How the base map looks (label and road sizes, the 10 map colours, projection and sky) belongs to the Styling Foundations module. See Who owns what.

Layer groups

The module sorts the base-map layers into the groups listed in baseMapLayerGroupNames, from the lowest drawn to the highest:

land, water, buildings2D, roads, railways, ferries, borders, buildings3D, natureLabels, roadLabels, roadShields, houseNumbers, smallerTownLabels, stateLabels, cityLabels, allPlaceLabels, capitalLabels, countryLabels

  • Groups can overlap. allPlaceLabels holds every place label, the other place-label groups included.
  • getLayers() returns the style layer ids of every group, and getLayerIds(group) returns those of one group.

Every setting as data

baseMapKnobCatalogue lists every setting of the module’s configuration as a knob. You can read it with no map loaded, so a UI can build its controls from it:

import { baseMapKnobCatalogue } from '@tomtom-org/maps-sdk/map';
const groupToggles = baseMapKnobCatalogue.filter(({ id }) => id.startsWith('groups.'));
// [{ id: 'groups.land.visible', kind: 'toggle', description: 'The `land` layer group: …' }, …]
  • The ids are visible, then groups.<group>.visible for each group above, in the order of baseMapLayerGroupNames, then the road parts: roads.exitNumbers.visible, roads.arrows.visible, roads.restricted.visible, roads.underConstruction.visible. Last come the hovered and clicked 3D buildings: buildings3D.hover.color, buildings3D.hover.intensity, buildings3D.click.color, buildings3D.click.intensity.
  • The visibility knobs are toggles with no default. A layer you don’t set is drawn as the style ships it, and some groups, such as buildings3D, ship hidden. The colours have no default either, and the intensities default to 0.35 and 0.7.
  • baseMapKnobIds lists the ids in catalogue order, BaseMapKnobId is their union, and BaseMapKnobValueOf<ID> is the plain value one takes. setKnob sets one by id.

Basic setup

Set up a map first with the Map quickstart.

Standard base map initialization

import { BaseMapModule } from '@tomtom-org/maps-sdk/map';
const baseMap = await BaseMapModule.get(map);

Every map has one base map module. A second get() returns the same instance (style-owned modules). A configuration you pass to it replaces the current one, as applyConfig does.

Initializing with a layer group hidden

groups sets the initial state of each layer group, and setVisible changes it later:

const baseMap = await BaseMapModule.get(map, {
groups: { buildings2D: { visible: false }, buildings3D: { visible: false } }
});
  • visible applies to every layer first, then each entry of groups in the order given, so a later entry wins where two groups share layers (allPlaceLabels and cityLabels).

  • Layers neither sets are drawn as the style ships them. So is a group whose entry leaves out visible, which keeps a few groups exactly as the style draws them:

    // Only land, water and roads, each as the style ships it
    await BaseMapModule.get(map, { visible: false, groups: { land: {}, water: {}, roads: {} } });
  • Showing a group shows every layer in it, including the ones the style ships hidden, such as detailed road markings.

Road parts

roads shows or hides four road parts separately, finer than the layer groups: exitNumbers (motorway exit number badges), arrows (one-way arrows), restricted (the dashed marking of restricted roads) and underConstruction.

import { BaseMapModule, baseMapKnobCatalogue, setKnob } from '@tomtom-org/maps-sdk/map';
const baseMap = await BaseMapModule.get(map, {
roads: { exitNumbers: { visible: false }, arrows: { visible: false } },
});
setKnob(baseMap, baseMapKnobCatalogue, 'roads.underConstruction.visible', false);

A part never shows while its layer group is hidden, and a false part stays hidden when the group shows again. A part you don’t set follows its group. While nothing sets the group or visible, it is drawn as the style ships it.

Layer group modes

setVisible, isVisible and events.where take the layer groups to act on as a { show, values } value filter:

  • show: 'only' - Acts only on the layers in the values groups.
  • show: 'all-except' - Acts on every layer except those in the values groups, which setVisible leaves as they were.
// Show only land, water and borders
baseMap.setVisible(false);
baseMap.setVisible(true, { layerGroups: { show: 'only', values: ['land', 'water', 'borders'] } });
// Hide the buildings, leaving the rest as it is
baseMap.setVisible(false, { layerGroups: { show: 'only', values: ['buildings2D', 'buildings3D'] } });
// Show everything except the labels
baseMap.setVisible(true, {
layerGroups: { show: 'all-except', values: ['allPlaceLabels', 'roadLabels', 'cityLabels'] }
});

Dynamic control

Changing visibility

baseMap.setVisible(false); // the whole base map
const layerGroups = { show: 'only', values: ['buildings3D'] };
// Until a call sets the group, this is what the style ships (buildings3D usually starts hidden)
checkbox.checked = baseMap.isVisible({ layerGroups });
checkbox.addEventListener('change', () => baseMap.setVisible(checkbox.checked, { layerGroups }));
  • isVisible() reads the settings rather than the layers. It reads visible, then the groups over it, and the later one wins. A layer none of them reaches reports what the style draws.
  • getConfig().groups keeps every setVisible call (a call without groups replaces the record with visible), so a setStyle restores them all, in order. The road parts stay set.
  • Setting a groups.<group>.visible knob moves that group last too, in the order you set them, so it wins where groups share layers.

Applying configuration updates

applyConfig replaces the whole configuration, so anything the new one leaves out goes back to how the style ships it. updateConfig merges one level deep, so spread groups to add to it:

// Hides the house numbers; every other group goes back to the style
baseMap.applyConfig({ groups: { houseNumbers: { visible: false } } });
// Also hides the ferries, keeping the house numbers hidden
baseMap.updateConfig({ groups: { ...baseMap.getConfig()?.groups, ferries: { visible: false } } });

Building a map up group by group

The demo below hides everything, then shows one layer group per second (borders, buildings, land, roads, labels), then the traffic and POI layers.

To restyle groups of layers rather than hide them, use the map colours and the styling knobs.

Event handling

The module’s events fire on the base map wherever no other module’s feature is on top. Use them to handle clicks on the rest of the map, such as clearing a selection:

const baseMap = await BaseMapModule.get(map);
baseMap.events.on('click', (feature, lngLat) => {
console.log('Base map clicked at', lngLat, 'on layer', feature.layer.id);
});
// Only the road labels and shields, with a pointer cursor
baseMap.events
.where({ layerGroups: { show: 'only', values: ['roadLabels', 'roadShields'] } }, { cursorOnHover: 'pointer' })
.on('click', (feature) => console.log(feature.properties.name));

A base-map feature a handler reaches takes the event as the feature state eventState (hover, long-hover, click or contextmenu), and loses the hover the moment the pointer leaves it. The 3D buildings react to it once you give them a colour, as the next section shows.

Hovered, clicked and coloured 3D buildings

To highlight the 3D building under the pointer or the one clicked, set the colour it turns towards in buildings3D, then listen to the buildings3D layer group:

const baseMap = await BaseMapModule.get(map, {
groups: { buildings3D: { visible: true } },
buildings3D: { hover: { color: '#f3a712' }, click: { color: '#e4572e', intensity: 0.8 } }
});
baseMap.events
.where({ layerGroups: { show: 'only', values: ['buildings3D'] } }, { cursorOnHover: 'pointer' })
.on('click', (building) => console.log(building.id, building.properties));
  • hover and click each take a color and an intensity, from 0 (not at all) to 1 (fully). If you don’t set the intensity, a hovered building turns 0.35 of the way and a clicked one 0.7.
  • A building reacts only while a handler reaches it. Hovering needs any handler on the group, and clicking needs a click one. The clicked building stays clicked until the next click lands elsewhere.
  • If you don’t set a colour, the building stays as drawn, so a base-map handler you already have doesn’t change how the buildings look.

The example below lights up the buildings of Midtown Manhattan and paints the selected one:

To give single buildings a colour of their own, call setBuildingColors with CSS colours by feature id:

baseMap.events
.where({ layerGroups: { show: 'only', values: ['buildings3D'] } })
.on('click', (building) => {
baseMap.setBuildingColors({ ...baseMap.getBuildingColors(), [building.id]: '#29335c' });
});
  • The ids are the map data’s own and stay the same across tiles and zoom levels, so you can colour a building before it loads.
  • The map data splits a building of several heights into parts, each with an id of its own. The id of any part colours the whole building, and the whole building lights up on hover and click.
  • Each call replaces the colours given before. Pass {} to give every building the style’s colour back.
  • A coloured building still turns towards the hover and click colours, and the colours stay through a setStyle that keeps state.
  • The map colours recolour the other buildings and leave these colours as you set them.
  • These looks live in the fill-extrusion-color of the style’s 3D - Building layer, so a styling.layers edit that sets that paint replaces them, and with them the hover, click and colour-by-id looks.

Highlight buildings you know by place

To highlight buildings on a map of your own, such as the offices of a campus or the venues of an event, name each one by a position inside it. getBuildingIdAt returns the id of the building standing there:

const venues = [[-73.98444, 40.75566], [-73.98685, 40.75552]];
// `once('idle')` waits for the next idle, which a settled map never fires.
if (!map.mapLibreMap.loaded()) await map.mapLibreMap.once('idle');
const colors = {};
for (const position of venues) {
const id = baseMap.getBuildingIdAt(position);
if (id !== undefined) colors[id] = '#e4572e';
}
baseMap.setBuildingColors(colors);
  • It reads the tiles the map has loaded, so call it once the map is idle with the place in view. It returns undefined where no building stands, or before its tiles arrive.
  • For a building in parts, it returns the id of the whole building, which colours every part.
  • A geocoded address can sit on the street in front of its building. Use a point inside the footprint, such as a place’s own position.
  • Store the ids it returns to colour the same buildings later, wherever the map is.

For buildings with façades, roofs and glass, use the Landmarks 3D plugin, which takes the same hover and click settings. Unlike these buildings, its own turn towards the map’s accent while you leave the colours unset.

Combining with other modules

The base map, the POIs and the traffic layers are separate modules over one style, so for a focused view, set each of them:

import { BaseMapModule, POIsModule, TrafficIncidentsModule } from '@tomtom-org/maps-sdk/map';
const baseMap = await BaseMapModule.get(map);
const poisModule = await POIsModule.get(map);
const trafficIncidentsModule = await TrafficIncidentsModule.get(map);
// A navigation view: roads and their labels, live incidents, no POIs
baseMap.setVisible(false);
baseMap.setVisible(true, {
layerGroups: { show: 'only', values: ['land', 'water', 'roads', 'roadLabels', 'roadShields'] }
});
trafficIncidentsModule.setVisible(true);
poisModule.setVisible(false);

API reference

BaseMapModule API reference