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

Places module

The Places Module allows you to display and interact with places on your TomTom map. You can add search results, custom locations, and handle user interactions.

What this module owns: the place markers and labels it draws, and how they look — the marker type, colour, icon and label configuration in Place styling. A place that joins a route becomes one of the route's waypoints, drawn by the Routing module instead. It never restyles the base map underneath, which belongs to the Styling Foundations module. Markers given no colour read the map's accent as their default, and the marker types that borrow the look of the style's own POIs follow the knobs that restyle them (Borrowed looks). See Who owns what.

This guide covers showing places, events and the data shown. Two features of the module have a guide of their own:

Guide Covers
Place styling Marker types and clusters, colour, labels, icons, extra properties, layer positioning and the knob catalogue
Connections and entry points Labelled lines between places, and the entrances a place's data carries

Initialization

import { TomTomMap, PlacesModule } from '@tomtom-org/maps-sdk/map';

// Initialize map
const map = new TomTomMap({ mapLibre: { container: 'map' } });

// Initialize Places module with default configuration
const placesModule = await PlacesModule.create(map);

Displaying places

Display one or multiple places on the map:

import { discoverPlaces } from '@tomtom-org/maps-sdk/services';

// Show single place
await placesModule.show({
  id: 'custom-place-1',
  type: 'Feature',
  geometry: { type: 'Point', coordinates: [4.9041, 52.3676] },
  properties: {
    type: 'POI',
    address: { freeformAddress: 'Custom Place 1, Amsterdam' },
    poi: { name: 'Custom Place' }
  }
});

// Show multiple places
await placesModule.show([place1, place2, place3]);

const searchResults = await discoverPlaces({
    query: 'restaurants',
    geoBias: { position: [4.9041, 52.3676] }, // Amsterdam
    limit: 10
});

// Show search results
await placesModule.show(searchResults);

// Remove all places from the map:
await placesModule.clear();

What show() takes

show() takes a place, an array of places or a Places collection as the service returns it: search, geocoding, reverse geocoding, place details and EV availability results, or your own GeoJSON points. There are no fields to map first. Three places services return something else:

Result Not showable because Show instead
getSearchSuggestions Suggestions to choose from, without geometry A place suggestion's resolve(), or a brand or category suggestion's refine()
geometryData Polygons The Geometries module
evChargingStationsAvailability Availability alone, no place getPlacesWithEVAvailability, over the stations a search found

preparePlacesForDisplay is what show() does to each place before drawing it. Call it to see the properties your layer overrides, extra properties and event handlers read:

import { preparePlacesForDisplay } from '@tomtom-org/maps-sdk/map';

const [first] = preparePlacesForDisplay(searchResults, { markerType: 'base-map' }).features;
console.log(first.properties.title, first.properties.iconID, first.properties.category);

Your own data

Your own GeoJSON points go to show() as they are, and draw like the places a search returns. Each keeps its own properties, which label.title, layer overrides and click handlers read back. Name their shape on create to have the label.title and icon.mapping callbacks typed for it:

type Store = CommonPlaceProps & { storeName: string };
const stores: Places<Store> = await (await fetch('https://your-api.com/stores.json')).json();

const placesModule = await PlacesModule.create<Store>(map, {
    markerType: 'base-map', // drawn like the map style's own POIs
    icon: { mapping: { to: 'poiCategory', fn: () => 'COFFEE_SHOP' } },
    label: { title: (place) => place.properties.storeName },
});

await placesModule.show(stores);

Showing and hiding

setVisible hides and shows everything the module draws — places, connections and entry points — while keeping its data. visible: false at creation starts it hidden.

const placesModule = await PlacesModule.create(map, { visible: false });
await placesModule.show(searchResults); // loaded, not drawn

placesModule.setVisible(true);
placesModule.isVisible(); // true

Events

User events

Handle user interactions with places:

// Click events
placesModule.events.on('click', (feature, lngLat) => {
    console.log('Click coordinates:', lngLat);
    console.log('Place title:', feature.properties.title);
    console.log('Place coordinates:', feature.geometry.coordinates);
});

// Hover events
placesModule.events.on('hover', (feature, lngLat) => {
    console.log('Hovering over:', feature.properties.poi?.name);
});

// Remove event listeners
placesModule.events.off('click', handler);

events covers every surface the module draws. Three named scopes narrow it: events.places for the pins alone — typed as Place, which is what you want in TypeScript — events.connections for the lines and labels drawn by showConnections, and events.entryPoints for the entry points:

placesModule.events.places.on('click', (place) => console.log(place.properties.poi?.name));
placesModule.events.connections.on('click', () => console.log('connection line clicked'));
placesModule.events.entryPoints.on('click', (entryPoint) => {
    // A point at the entrance, whether its dot or its line was hit, with its place attached
    console.log(entryPoint.properties.type, entryPoint.properties.place?.properties.title);
    // It is a valid route location
    calculateRoute({ locations: [origin, entryPoint] });
});

Result cards and popups

The module tells you which place was clicked or hovered, and does the hit-testing behind it. It draws no result card, popup or info window for a place: open your own from the event.

import { Popup } from 'maplibre-gl';

const popup = new Popup({ closeButton: false, offset: 35 });

placesModule.events.places.on('click', (place) => {
    popup
        .setLngLat(place.geometry.coordinates)
        .setText(place.properties.poi?.name ?? place.properties.address.freeformAddress)
        .addTo(map.mapLibreMap);
});

// The popup is yours, so close it when the places change
popup.remove();
await placesModule.show(newResults);

Programmatic event states

Put, clean and read the states of places the way you do on every module that draws your data — see event states. They act on the places, whichever marker type draws them, clustered or not; events.connections and events.entryPoints carry their own.

// Highlight the first place as if clicked
placesModule.putEventState({ index: 0, state: 'click' });

const { click: [selectedID] = [], hover = [] } = placesModule.getEventStates();

placesModule.cleanEventStates({ states: ['hover'] });

A pin draws its state, and so do its entry points. A show() of new results drops the states; to keep a selection across a re-show, read it first and put it back:

const [selectedID] = placesModule.getEventStates().click ?? [];
await placesModule.show(morePlaces);
if (selectedID) placesModule.putEventState({ id: selectedID, state: 'click' });

Multiple module instances

You can create multiple independent instances of the Places module, each with its own configuration and data. This is useful for displaying different sets of places with distinct styling or behavior.

// Create separate instances for different purposes
const restaurantPlaces = await PlacesModule.create(map, {
    markerType: 'pin',
    icon: {
        categoryIcons: [{ id: 'RESTAURANT', image: '/icons/restaurant.png', pixelRatio: 2 }]
    }
});

const hotelPlaces = await PlacesModule.create(map, {
    markerType: 'circle-icon',
    icon: {
        categoryIcons: [{ id: 'HOTEL_OR_MOTEL', image: '/icons/hotel.png', pixelRatio: 2 }]
    }
});

// Each instance manages its own places independently
await restaurantPlaces.show(restaurantResults);
await hotelPlaces.show(hotelResults);

Reading shown data

Use getShown() to retrieve the places currently displayed on the map: the places passed to show() as drawn, each with its display properties (title, iconID…) and current eventState, and the connections passed to showConnections():

const shown = placesModule.getShown();
console.log(`Showing ${shown.places.features.length} places`);

Identity across show, clear and style changes

What happens to the shown places, their event states and their connections when you show again, clear or change the style:

Call Places Event states Connections
show(results) Replaced, never merged Dropped, even for a place shown again under the same id Dropped
show(placesModule.getShown().places) Shown again Kept: they travel on each feature's properties.eventState Dropped
clear() Removed Removed Dropped
applyConfig, updateConfig, setKnob, resetKnob Kept Kept Kept, ids resolved again
setStyle Kept Kept Kept, ids resolved again
setStyle(style, { resetState: true }) Removed Removed Removed

So in a search, select, search again loop, put the selection back by id after the new show():

// `selectedID` is the id your app kept when the place was clicked
await placesModule.show(await discoverPlaces({ query: 'coffee', geoBias: { position } }));
placesModule.putEventState({ id: selectedID, state: 'click' }); // does nothing if the id is gone

API reference

For complete documentation of all Places Module properties, methods, and types, see the PlacesModule API Reference.

Related guides and examples

Services integration

Related examples

Related map modules