This is the documentation of version 0.58.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 theme, icon and text configuration below. It never restyles the base map underneath, which belongs to the Styling module, and styling knobs never touch these markers. See Who owns what.

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',
    position: [4.9041, 52.3676], // Amsterdam
    limit: 10
});

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

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

Custom styling

Theme

Themes control the overall look of place markers. Choose one at initialization or swap at runtime:

// Initialize with a specific theme
const placesModule = await PlacesModule.create(map, { theme: 'base-map' });

// Or swap at runtime
placesModule.applyTheme('circle-icon');

// Micro-only rendering: combine `base-map` with a hidden `main` layer
const placesModule = await PlacesModule.create(map, {
    theme: 'base-map',
    layers: { main: { layout: { visibility: 'none' } } },
});

Text styling

Customize labels for places using text configuration:

// Make POI names red and other places blue
const customTextConfig: PlaceTextConfig = {
    color: ['match', ['get', 'type'], 'POI', '#D32F2F', '#1976D2']
};

// Initialize with custom text styling
const placesModule = await PlacesModule.create(map, {
    text: customTextConfig,
});

// Or apply after initialization
placesModule.applyTextConfig(customTextConfig);

Icon styling

Customize place icons and markers. Custom icons work under every theme: when a category is mapped, features in that category use the provided sprite; otherwise the theme's default icon (the base-map style's category/group expression or the default pin) still renders.

// Per-category custom images — layered on top of any theme
const customIconConfig: PlaceIconConfig = {
  categoryIcons: [
    { id: 'RESTAURANT', image: '/icons/food.png', pixelRatio: 2 },
    { id: 'HOTEL_OR_MOTEL', image: '/icons/hotel.png', pixelRatio: 2 }
  ]
};

// Initialize with custom icon styling
const placesModule = await PlacesModule.create(map, {
  theme: 'pin',
  icon: customIconConfig
});

// Or apply after initialization
placesModule.applyIconConfig(customIconConfig);

Use offsetX/offsetY (pixels) on a categoryIcons entry to shift a custom icon from its coordinate — useful when the icon's visual tip isn't at its own geometric bottom or center:

const customIconConfig: PlaceIconConfig = {
  categoryIcons: [
    { id: 'RESTAURANT', image: '/icons/food.png', pixelRatio: 2, offsetX: 0, offsetY: -10 }
  ]
};

offsetX/offsetY only apply to categoryIcons entries that also provide an image — an entry that references an existing sprite icon by id alone ignores them. Setting them on PlaceIconConfig.default.image has no effect. RoutingModule charging stop icons support the same two fields; see the routes guide.

Extra properties

Add custom properties to places for advanced styling or filtering:

placesModule.applyExtraFeatureProps({
    category: (place) => place.properties.poi?.categories?.[0],
    rating: (place) => place.properties.poi?.rating || 0,
    isOpen: true
});

A callback receives a Place and must return a JSON value. When the places you show carry a narrower shape — EV charging stations with availability, say — name it on the config and the callbacks are typed for it:

const config: PlacesModuleConfig<EVChargingStationWithAvailabilityPlaceProps> = {
    extraFeatureProps: {
        availabilityRatio: (place) => getChargingPointAvailability(place)?.ratio ?? 0,
    },
};

Entry points

Search, geocoding and reverse geocoding fill a place's entrances in properties.entryPoints. Set entryPoints on the module config to draw them: a dot per entrance, larger for the main one, with a dotted line back to the place.

const placesModule = await PlacesModule.create(map, {
    entryPoints: {
        showFor: 'clicked', // 'all' | 'selected' (hovered or clicked, the default) | 'clicked'
        minZoom: 16,        // default 15
        layers: { line: { layout: { visibility: 'none' } } }, // MapLibre overrides for `point` and `line`
    },
});

// Only places with an event handler reach the hover and click states
placesModule.events.on('click', (feature) => console.log(feature.properties.title));

// `entryPoints: undefined` stops drawing them
placesModule.updateConfig({ entryPoints: { showFor: 'all' } });

A reverse geocoding result's geometry is the queried point; move it to properties.originalPosition, the matched address, before showing it:

const address = await reverseGeocode({ position: clickedPosition });
await placesModule.show({
    ...address,
    geometry: { ...address.geometry, coordinates: address.properties.originalPosition },
});

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] });
});

Programmatic event states

Manually control visual states of places:

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

// Remove event state from specific place
placesModule.cleanEventState({ index: 0 });

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

// Remove all event states
placesModule.cleanEventStates();

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, {
    theme: 'pin',
    icon: {
        categoryIcons: [{ id: 'RESTAURANT', image: '/icons/restaurant.png', pixelRatio: 2 }]
    }
});

const hotelPlaces = await PlacesModule.create(map, {
    theme: '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. This returns the exact data previously passed to show():

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

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