Back to all examples

Interactive Map POIs

This example on GitHub

Click on map POIs to see their details

Interactive Map POIs
import { TomTomConfig } from '@tomtom-org/maps-sdk/core';
import { BaseMapModule, PlacesModule, POIsModule, TomTomMap } from '@tomtom-org/maps-sdk/map';
import { createLatestRequest, getPlaceDetails } from '@tomtom-org/maps-sdk/services';
import type { Popup } from 'maplibre-gl';
import './style.css';
import { API_KEY } from './config';
import { buildPopupHtml, createPOIPopup } from './popup';

// (Set your own API key when working in your own environment)
TomTomConfig.instance.put({ apiKey: API_KEY, language: 'en-US' });

(async () => {
    const map = new TomTomMap({
        mapLibre: {
            container: 'sdk-map',
            center: [4.90435, 52.36876],
            zoom: 16,
        },
    });

    const restOfTheMapModule = await BaseMapModule.get(map, { events: { cursorOnHover: 'default' } });
    const poisModule = await POIsModule.get(map);
    const placesModule = await PlacesModule.create(map);

    let activePopup: Popup | null = null;
    // One lookup at a time: a newer click or a dismiss retires the one in flight
    const latestLookup = createLatestRequest();

    const clearSelection = async () => {
        latestLookup.cancel();
        await placesModule.clear();
        activePopup?.remove();
        activePopup = null;
    };

    poisModule.events.on('click', async (feature) => {
        const lookup = await latestLookup.run((signal) =>
            getPlaceDetails({ entityId: feature.properties.id, openingHours: 'nextSevenDays', signal }),
        );
        if (!lookup.current || !lookup.value) return;

        const details = lookup.value;
        await placesModule.show(details);

        activePopup?.remove();
        const [lng, lat] = details.geometry.coordinates;
        activePopup = createPOIPopup().setLngLat([lng, lat]).setHTML(buildPopupHtml(details)).addTo(map.mapLibreMap);
    });

    restOfTheMapModule.events.on('click', async () => {
        await clearSelection();
    });
})();

A click on a POI the base map already drew pins it and opens a popup with its category, today’s opening hours, address and phone.

  • POIsModule.events.on('click') delivers the clicked base-map feature with a real entity id on it, so there is no hit-testing to write.
  • getPlaceDetails trades that id for the full Place, which PlacesModule.show renders unchanged.
  • BaseMapModule.get gives everything else on the map its own click handler, which clears the pin and the popup.
  • createLatestRequest retires the previous click’s lookup and drops one a dismiss overtook, so a slower answer cannot pin a POI already dismissed.

Related examples