Klasteryzacja znaczników

Wybierz platformę: Android iOS JavaScript

Przegląd

Z tego samouczka dowiesz się, jak wyświetlać dużą liczbę znaczników na mapie za pomocą rozwiązania do łączenia znaczników w klastry. Aby łączyć blisko położone znaczniki w klastry i uprościć wyświetlanie znaczników na mapie, możesz użyć biblioteki @googlemaps/markerclusterer w połączeniu z Maps JavaScript API.

Aby zobaczyć, jak działa grupowanie znaczników, wyświetl tę mapę.

Liczba na klastrze wskazuje, ile znaczników zawiera. Zauważ, że gdy powiększysz dowolną lokalizację klastra, liczba na klastrze zmniejszy się i zaczniesz widzieć poszczególne znaczniki na mapie. Pomniejszenie mapy spowoduje ponowne połączenie znaczników w klastry.

Poniższy przykład pokazuje pełny kod potrzebny do utworzenia tej mapy.

TypeScript

import { MarkerClusterer } from '@googlemaps/markerclusterer';

async function init() {
    // Request needed libraries.
    const [{ InfoWindow }, { AdvancedMarkerElement, PinElement }] =
        await Promise.all([
            google.maps.importLibrary('maps'),
            google.maps.importLibrary('marker'),
        ]);

    const mapElement =
        document.querySelector<google.maps.MapElement>('gmp-map')!;
    const innerMap = mapElement.innerMap;

    const infoWindow = new InfoWindow({
        content: '',
        disableAutoPan: true,
    });

    // Create an array of alphabetical characters used to label the markers.
    const labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    // Add some markers to the map.
    const markers = locations.map((position, i) => {
        const label = labels[i % labels.length];
        const pinGlyph = new PinElement({
            glyphText: label,
            glyphColor: 'white',
        });

        const marker = new AdvancedMarkerElement({
            position,
            content: pinGlyph,
            gmpClickable: true,
        });

        // markers can only be keyboard focusable when they have click listeners
        // open info window when marker is clicked
        marker.addEventListener('gmp-click', () => {
            const content = document.createElement('div');
            content.textContent = `${String(position.lat)}, ${String(position.lng)}`;
            infoWindow.setContent(content);

            infoWindow.open({
                anchor: marker,
                map: innerMap,
            });
        });
        return marker;
    });

    // Add a marker clusterer to manage the markers.
    new MarkerClusterer({ markers, map: innerMap });
}

const locations = [
    { lat: -31.56391, lng: 147.154312 },
    { lat: -33.718234, lng: 150.363181 },
    { lat: -33.727111, lng: 150.371124 },
    { lat: -33.848588, lng: 151.209834 },
    { lat: -33.851702, lng: 151.216968 },
    { lat: -34.671264, lng: 150.863657 },
    { lat: -35.304724, lng: 148.662905 },
    { lat: -36.817685, lng: 175.699196 },
    { lat: -36.828611, lng: 175.790222 },
    { lat: -37.75, lng: 145.116667 },
    { lat: -37.759859, lng: 145.128708 },
    { lat: -37.765015, lng: 145.133858 },
    { lat: -37.770104, lng: 145.143299 },
    { lat: -37.7737, lng: 145.145187 },
    { lat: -37.774785, lng: 145.137978 },
    { lat: -37.819616, lng: 144.968119 },
    { lat: -38.330766, lng: 144.695692 },
    { lat: -39.927193, lng: 175.053218 },
    { lat: -41.330162, lng: 174.865694 },
    { lat: -42.734358, lng: 147.439506 },
    { lat: -42.734358, lng: 147.501315 },
    { lat: -42.735258, lng: 147.438 },
    { lat: -43.999792, lng: 170.463352 },
];

void init();

JavaScript

import { MarkerClusterer } from '@googlemaps/markerclusterer';

async function init() {
    // Request needed libraries.
    const [{ InfoWindow }, { AdvancedMarkerElement, PinElement }] =
        await Promise.all([
            google.maps.importLibrary('maps'),
            google.maps.importLibrary('marker'),
        ]);

    const mapElement = document.querySelector('gmp-map');
    const innerMap = mapElement.innerMap;

    const infoWindow = new InfoWindow({
        content: '',
        disableAutoPan: true,
    });

    // Create an array of alphabetical characters used to label the markers.
    const labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    // Add some markers to the map.
    const markers = locations.map((position, i) => {
        const label = labels[i % labels.length];
        const pinGlyph = new PinElement({
            glyphText: label,
            glyphColor: 'white',
        });

        const marker = new AdvancedMarkerElement({
            position,
            content: pinGlyph,
            gmpClickable: true,
        });

        // markers can only be keyboard focusable when they have click listeners
        // open info window when marker is clicked
        marker.addEventListener('gmp-click', () => {
            const content = document.createElement('div');
            content.textContent = `${String(position.lat)}, ${String(position.lng)}`;
            infoWindow.setContent(content);

            infoWindow.open({
                anchor: marker,
                map: innerMap,
            });
        });
        return marker;
    });

    // Add a marker clusterer to manage the markers.
    new MarkerClusterer({ markers, map: innerMap });
}

const locations = [
    { lat: -31.56391, lng: 147.154312 },
    { lat: -33.718234, lng: 150.363181 },
    { lat: -33.727111, lng: 150.371124 },
    { lat: -33.848588, lng: 151.209834 },
    { lat: -33.851702, lng: 151.216968 },
    { lat: -34.671264, lng: 150.863657 },
    { lat: -35.304724, lng: 148.662905 },
    { lat: -36.817685, lng: 175.699196 },
    { lat: -36.828611, lng: 175.790222 },
    { lat: -37.75, lng: 145.116667 },
    { lat: -37.759859, lng: 145.128708 },
    { lat: -37.765015, lng: 145.133858 },
    { lat: -37.770104, lng: 145.143299 },
    { lat: -37.7737, lng: 145.145187 },
    { lat: -37.774785, lng: 145.137978 },
    { lat: -37.819616, lng: 144.968119 },
    { lat: -38.330766, lng: 144.695692 },
    { lat: -39.927193, lng: 175.053218 },
    { lat: -41.330162, lng: 174.865694 },
    { lat: -42.734358, lng: 147.439506 },
    { lat: -42.734358, lng: 147.501315 },
    { lat: -42.735258, lng: 147.438 },
    { lat: -43.999792, lng: 170.463352 },
];

void init();

CSS

html,
body {
    height: 100%;
    margin: 0;
    padding: 0;
}

HTML

<html>
    <head>
        <title>Marker Clustering</title>

        <link rel="stylesheet" type="text/css" href="./style.css" />
        <script type="module" src="./index.js"></script>
        <script>
            // prettier-ignore
            (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
                key: "GOOGLE_MAPS_API_KEY",
            });
        </script>
    </head>
    <body>
        <gmp-map
            center="-28.024,140.887"
            zoom="3"
            map-id="DEMO_MAP_ID"></gmp-map>
    </body>
</html>

W tym samouczku dodamy do mapy zestaw znaczników za pomocą tablicy locations. Do pobierania znaczników na mapę możesz używać innych źródeł. Więcej informacji znajdziesz w przewodniku po tworzeniu znaczników.

Dodawanie rozwiązania do łączenia znaczników w klastry

Aby dodać rozwiązanie do łączenia znaczników w klastry:

  1. Dodaj bibliotekę grupowania znaczników do strony lub aplikacji. Biblioteka jest dostępna w NPM pod adresem @googlemaps/markerclusterer oraz w repozytorium w GitHubie. Aby zainstalować bibliotekę, wybierz jedną z tych opcji:

    NPM

    Zainstaluj bibliotekę @googlemaps/markerclusterer za pomocą NPM.

    npm install @googlemaps/markerclusterer

    CDN

    Ten skrypt wczytuje bibliotekę z CDN unpkg.com.

    <script src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js"></script>
  2. Dodaj rozwiązanie do łączenia znaczników w klastry w aplikacji.

    Ten kod dodaje do mapy rozwiązanie do łączenia znaczników w klastry. Wybierz jedną z tych opcji:

    NPM

    import { MarkerClusterer } from "@googlemaps/markerclusterer";
    
    const markerCluster = new MarkerClusterer({ markers, map });

    CDN

    Gdy biblioteka jest dostępna przez CDN, jest dostępna w globalnym obiekcie markerClusterer.

    const markerCluster = new markerClusterer.MarkerClusterer({ markers, map });

    Ten przykład przekazuje tablicę markers do MarkerClusterer.

  3. (Opcjonalnie) Dostosuj klaster znaczników, aby zmienić wygląd ikony klastra, algorytm grupowania lub zachowanie po kliknięciu.

Dostosowywanie rozwiązania do łączenia znaczników w klastry

Możesz dostosować MarkerClusterer, przekazując dodatkowe opcje w MarkerClustererOptions obiekcie podczas tworzenia instancji klastra:

  • renderer: określa, jak znaczniki klastra są renderowane na mapie.
  • algorithm i algorithmOptions: określają, jak znaczniki są grupowane w klastry na podstawie odległości i poziomu powiększenia.
  • onClusterClick: Zastępuje działanie domyślne, gdy użytkownik kliknie znacznik klastra.

Na tej mapie widać znaczniki z niestandardowymi ikonami klastrów.

Dostosowywanie ikony klastra

Domyślnie MarkerClusterer używa DefaultRenderer do tworzenia ikon klastrów SVG. Aby dostosować wygląd znaczników klastra, przekaż do opcji renderer obiekt, który implementuje interfejs Renderer.

Interfejs Renderer wymaga metody render(cluster, stats, map), która zwraca AdvancedMarkerElement (lub starszy google.maps.Marker). Metoda render przyjmuje te argumenty:

  • cluster (Cluster): reprezentuje grupę znaczników, w tym:
    • count: liczba widocznych znaczników w klastrze.
    • position: pozycja środka klastra google.maps.LatLng.
    • bounds: google.maps.LatLngBounds klastra.
    • markers: tablica znaczników zawartych w klastrze.
  • stats (ClusterStats): zawiera statystyki dotyczące wszystkich klastrów w bieżącym cyklu renderowania, takie jak stats.clusters.markers.mean, min, max, i sum. Możesz użyć tych wartości, aby dynamicznie stylizować klastry na podstawie ich względnego rozmiaru.
  • map (google.maps.Map): instancja mapy.

Ten przykład definiuje niestandardowy renderer który tworzy okrągłą odznakę HTML dla każdego AdvancedMarkerElement i zmienia jej kolor tła, gdy liczba znaczników klastra przekracza średni rozmiar klastra.

TypeScript

// Customize the cluster icons using a custom Renderer
    const renderer = {
        render: (
            {
                count,
                position,
            }: { count: number; position: google.maps.LatLng },
            stats: ClusterStats
        ) => {
            // Change color based on the number of markers in the cluster relative to the mean
            const isLargeCluster =
                count > Math.max(10, stats.clusters.markers.mean);
            const clusterClass = isLargeCluster
                ? 'large-cluster'
                : 'small-cluster';

            // Build the HTML content for the Advanced Marker
            const content = document.createElement('div');
            content.classList.add('custom-cluster-icon', clusterClass);
            content.textContent = String(count);

            return new AdvancedMarkerElement({
                position,
                content,
                // Adjust zIndex to be above other markers
                zIndex: 1000 + count,
            });
        },
    };

    // Add a marker clusterer to manage the markers.
    new MarkerClusterer({ markers, map: innerMap, renderer });
}

JavaScript

// Customize the cluster icons using a custom Renderer
    const renderer = {
        render: ({ count, position }, stats) => {
            // Change color based on the number of markers in the cluster relative to the mean
            const isLargeCluster =
                count > Math.max(10, stats.clusters.markers.mean);
            const clusterClass = isLargeCluster
                ? 'large-cluster'
                : 'small-cluster';

            // Build the HTML content for the Advanced Marker
            const content = document.createElement('div');
            content.classList.add('custom-cluster-icon', clusterClass);
            content.textContent = String(count);

            return new AdvancedMarkerElement({
                position,
                content,
                // Adjust zIndex to be above other markers
                zIndex: 1000 + count,
            });
        },
    };

    // Add a marker clusterer to manage the markers.
    new MarkerClusterer({ markers, map: innerMap, renderer });
}

Zobacz pełny przykład niestandardowego renderera

TypeScript

import { MarkerClusterer, ClusterStats } from '@googlemaps/markerclusterer';

async function init() {
    // Request needed libraries.
    const [{ InfoWindow }, { AdvancedMarkerElement, PinElement }] =
        await Promise.all([
            google.maps.importLibrary('maps'),
            google.maps.importLibrary('marker'),
        ]);

    await customElements.whenDefined('gmp-map');

    const mapElement =
        document.querySelector<google.maps.MapElement>('gmp-map')!;
    const innerMap = mapElement.innerMap;

    const infoWindow = new InfoWindow({
        content: '',
        disableAutoPan: true,
    });

    // Create an array of alphabetical characters used to label the markers.
    const labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    // Add some markers to the map.
    const markers = locations.map((position, i) => {
        const label = labels[i % labels.length];
        const pinGlyph = new PinElement({
            glyphText: label,
            glyphColor: 'white',
        });

        const marker = new AdvancedMarkerElement({
            position,
            content: pinGlyph,
            gmpClickable: true,
        });

        marker.addEventListener('gmp-click', () => {
            const content = document.createElement('div');
            content.textContent = `${String(position.lat)}, ${String(position.lng)}`;
            infoWindow.setContent(content);

            infoWindow.open({
                anchor: marker,
                map: innerMap,
            });
        });
        return marker;
    });

    // Customize the cluster icons using a custom Renderer
    const renderer = {
        render: (
            {
                count,
                position,
            }: { count: number; position: google.maps.LatLng },
            stats: ClusterStats
        ) => {
            // Change color based on the number of markers in the cluster relative to the mean
            const isLargeCluster =
                count > Math.max(10, stats.clusters.markers.mean);
            const clusterClass = isLargeCluster
                ? 'large-cluster'
                : 'small-cluster';

            // Build the HTML content for the Advanced Marker
            const content = document.createElement('div');
            content.classList.add('custom-cluster-icon', clusterClass);
            content.textContent = String(count);

            return new AdvancedMarkerElement({
                position,
                content,
                // Adjust zIndex to be above other markers
                zIndex: 1000 + count,
            });
        },
    };

    // Add a marker clusterer to manage the markers.
    new MarkerClusterer({ markers, map: innerMap, renderer });
}

const locations = [
    { lat: -31.56391, lng: 147.154312 },
    { lat: -33.718234, lng: 150.363181 },
    { lat: -33.727111, lng: 150.371124 },
    { lat: -33.848588, lng: 151.209834 },
    { lat: -33.851702, lng: 151.216968 },
    { lat: -34.671264, lng: 150.863657 },
    { lat: -35.304724, lng: 148.662905 },
    { lat: -36.817685, lng: 175.699196 },
    { lat: -36.828611, lng: 175.790222 },
    { lat: -37.75, lng: 145.116667 },
    { lat: -37.759859, lng: 145.128708 },
    { lat: -37.765015, lng: 145.133858 },
    { lat: -37.770104, lng: 145.143299 },
    { lat: -37.7737, lng: 145.145187 },
    { lat: -37.774785, lng: 145.137978 },
    { lat: -37.819616, lng: 144.968119 },
    { lat: -38.330766, lng: 144.695692 },
    { lat: -39.927193, lng: 175.053218 },
    { lat: -41.330162, lng: 174.865694 },
    { lat: -42.734358, lng: 147.439506 },
    { lat: -42.734358, lng: 147.501315 },
    { lat: -42.735258, lng: 147.438 },
    { lat: -43.999792, lng: 170.463352 },
];

void init();

JavaScript

import { MarkerClusterer } from '@googlemaps/markerclusterer';

async function init() {
    // Request needed libraries.
    const [{ InfoWindow }, { AdvancedMarkerElement, PinElement }] =
        await Promise.all([
            google.maps.importLibrary('maps'),
            google.maps.importLibrary('marker'),
        ]);

    await customElements.whenDefined('gmp-map');

    const mapElement = document.querySelector('gmp-map');
    const innerMap = mapElement.innerMap;

    const infoWindow = new InfoWindow({
        content: '',
        disableAutoPan: true,
    });

    // Create an array of alphabetical characters used to label the markers.
    const labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

    // Add some markers to the map.
    const markers = locations.map((position, i) => {
        const label = labels[i % labels.length];
        const pinGlyph = new PinElement({
            glyphText: label,
            glyphColor: 'white',
        });

        const marker = new AdvancedMarkerElement({
            position,
            content: pinGlyph,
            gmpClickable: true,
        });

        marker.addEventListener('gmp-click', () => {
            const content = document.createElement('div');
            content.textContent = `${String(position.lat)}, ${String(position.lng)}`;
            infoWindow.setContent(content);

            infoWindow.open({
                anchor: marker,
                map: innerMap,
            });
        });
        return marker;
    });

    // Customize the cluster icons using a custom Renderer
    const renderer = {
        render: ({ count, position }, stats) => {
            // Change color based on the number of markers in the cluster relative to the mean
            const isLargeCluster =
                count > Math.max(10, stats.clusters.markers.mean);
            const clusterClass = isLargeCluster
                ? 'large-cluster'
                : 'small-cluster';

            // Build the HTML content for the Advanced Marker
            const content = document.createElement('div');
            content.classList.add('custom-cluster-icon', clusterClass);
            content.textContent = String(count);

            return new AdvancedMarkerElement({
                position,
                content,
                // Adjust zIndex to be above other markers
                zIndex: 1000 + count,
            });
        },
    };

    // Add a marker clusterer to manage the markers.
    new MarkerClusterer({ markers, map: innerMap, renderer });
}

const locations = [
    { lat: -31.56391, lng: 147.154312 },
    { lat: -33.718234, lng: 150.363181 },
    { lat: -33.727111, lng: 150.371124 },
    { lat: -33.848588, lng: 151.209834 },
    { lat: -33.851702, lng: 151.216968 },
    { lat: -34.671264, lng: 150.863657 },
    { lat: -35.304724, lng: 148.662905 },
    { lat: -36.817685, lng: 175.699196 },
    { lat: -36.828611, lng: 175.790222 },
    { lat: -37.75, lng: 145.116667 },
    { lat: -37.759859, lng: 145.128708 },
    { lat: -37.765015, lng: 145.133858 },
    { lat: -37.770104, lng: 145.143299 },
    { lat: -37.7737, lng: 145.145187 },
    { lat: -37.774785, lng: 145.137978 },
    { lat: -37.819616, lng: 144.968119 },
    { lat: -38.330766, lng: 144.695692 },
    { lat: -39.927193, lng: 175.053218 },
    { lat: -41.330162, lng: 174.865694 },
    { lat: -42.734358, lng: 147.439506 },
    { lat: -42.734358, lng: 147.501315 },
    { lat: -42.735258, lng: 147.438 },
    { lat: -43.999792, lng: 170.463352 },
];

void init();

CSS

html,
body,
gmp-map {
    height: 100%;
    margin: 0;
    padding: 0;
}

.custom-cluster-icon {
    width: 45px;
    height: 45px;
    border-radius: 50%;
    border: 2px solid white;
    display: flex;
    justify-content: center;
    align-items: center;
    color: white;
    font-weight: bold;
    font-size: 14px;
    font-family: sans-serif;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

.large-cluster {
    background-color: #ff0000;
}

.small-cluster {
    background-color: #0000ff;
}

HTML

<html>
    <head>
        <title>marker-clustering-custom</title>

        <link rel="stylesheet" type="text/css" href="./style.css" />
        <script type="module" src="./index.js"></script>
        <script>
            // prettier-ignore
            (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
                key: "GOOGLE_MAPS_API_KEY"
            });
        </script>
    </head>
    <body>
        <gmp-map
            center="-28.024, 140.887"
            zoom="3"
            map-id="DEMO_MAP_ID"></gmp-map>
    </body>
</html>

Więcej przykładów renderera znajdziesz w wersji demonstracyjnej użycia renderera.

Modyfikowanie algorytmu grupowania

Domyślnie MarkerClusterer używa SuperClusterAlgorithm, który grupuje znaczniki za pomocą drzew k-d. Możesz dostosować zachowanie grupowania, przekazując instancję algorytmu do opcji algorithm lub przekazując opcje algorytmu domyślnego do algorithmOptions.

Biblioteka zawiera te wbudowane algorytmy:

  • SuperClusterAlgorithm (domyślny): szybkie grupowanie punktów geoprzestrzennych za pomocą drzew k-d. Typowe opcje:
    • maxZoom: maksymalny poziom powiększenia, przy którym generowane są klastry (domyślnie: 16). Przy poziomach powiększenia powyżej maxZoom poszczególne znaczniki są wyświetlane bez grupowania.
    • radius: promień klastra w pikselach (domyślnie: 60).
    • minPoints: minimalna liczba znaczników wymagana do utworzenia klastra (domyślnie: 2).
  • SuperClusterViewportAlgorithm: Wariant SuperClusterAlgorithm filtrowany według widocznego obszaru, który grupuje tylko znaczniki w bieżącym widocznym obszarze mapy oraz dopełnienie w pikselach (viewportPadding, domyślnie: 60).
  • GridAlgorithm: algorytm grupowania oparty na siatce. Typowe opcje to maxZoom (domyślnie: 16), gridSize (rozmiar siatki w pikselach, domyślnie: 40) i maxDistance (maksymalna odległość w metrach między środkiem klastra a znacznikiem, domyślnie: 40000).
  • NoopAlgorithm: renderuje wszystkie znaczniki bez grupowania.

Ten przykład konfiguruje SuperClusterAlgorithm z niestandardowymi wartościami radius i maxZoom:

import {MarkerClusterer, SuperClusterAlgorithm} from '@googlemaps/markerclusterer';

const markerCluster = new MarkerClusterer({
  map,
  markers,
  algorithm: new SuperClusterAlgorithm({
    radius: 80,
    maxZoom: 15,
  }),
});

Aby porównać zachowanie różnych algorytmów, zobacz wersję demonstracyjną porównania algorytmów.

Dostosowywanie zachowania po kliknięciu klastra

Domyślnie kliknięcie znacznika klastra powoduje dopasowanie widocznego obszaru mapy do granic klikniętego klastra (map.fitBounds(cluster.bounds)). Aby dostosować zachowanie po kliknięciu klastra przez użytkownika, przekaż funkcję wywołania zwrotnego do onClusterClick opcji:

const markerCluster = new MarkerClusterer({
  map,
  markers,
  onClusterClick: (event, cluster, map) => {
    // Zoom in by 2 levels centered on the cluster position
    map.setCenter(cluster.position);
    map.setZoom((map.getZoom() ?? 3) + 2);
  },
});

Więcej informacji

Bardziej złożone przykłady grupowania znaczników znajdziesz w repozytorium w GitHubie. Możesz też przeczytać dokumentację referencyjną biblioteki.