Как кластеризировать пользовательские значки, добавленные маркером

#javascript #mapbox-gl-js

Вопрос:

версия mapbox-gl-js: ^2.0.1

Я добавил пользовательские значки, следуя этому примеру: https://docs.mapbox.com/mapbox-gl-js/example/custom-marker-icons

Как я могу сгруппировать эти значки ?

Ссылки на соответствующую документацию

  1. https://docs.mapbox.com/mapbox-gl-js/example/cluster
  2. https://docs.mapbox.com/mapbox-gl-js/example/cluster-html

Что я пробовал

     <script>
        mapboxgl.accessToken = 'super secret';
        var map = new mapboxgl.Map({
            container: 'map',
            zoom: 0.3,
            center: [0, 20],
            style: 'mapbox://styles/mapbox/light-v10'
        });

        map.addControl(new mapboxgl.NavigationControl());

        // filters for classifying earthquakes into five categories based on magnitude
        var mag1 = ['<', ['get', 'mag'], 2];
        var mag2 = ['all', ['>=', ['get', 'mag'], 2], ['<', ['get', 'mag'], 3]];
        var mag3 = ['all', ['>=', ['get', 'mag'], 3], ['<', ['get', 'mag'], 4]];
        var mag4 = ['all', ['>=', ['get', 'mag'], 4], ['<', ['get', 'mag'], 5]];
        var mag5 = ['>=', ['get', 'mag'], 5];

        // colors to use for the categories
        var colors = ['#fed976', '#feb24c', '#fd8d3c', '#fc4e2a', '#e31a1c'];

        var geojson = {
            'type': 'FeatureCollection',
            'features': [
                {
                    'type': 'Feature',
                    'properties': {
                        'message': 'Foo',
                        "mag": 5,
                        'iconSize': [60, 60],
                        'id': 1
                    },
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [-66.324462, -16.024695]
                    }
                },
                {
                    'type': 'Feature',
                    'properties': {
                        'message': 'Bar',
                        "mag": 10,
                        'iconSize': [50, 50],
                        'id': 2
                    },
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [-61.21582, -15.971891]
                    }
                },
                {
                    'type': 'Feature',
                    'properties': {
                        'message': 'Baz',
                        "mag": 1.5,
                        'iconSize': [40, 40],
                        'id': 3
                    },
                    'geometry': {
                        'type': 'Point',
                        'coordinates': [-63.292236, -18.281518]
                    }
                }
            ]
        };

        geojson.features.forEach(function (marker) {
            // Create a DOM element for each marker.
            var el = document.createElement('div');
            el.className = 'marker';
            el.id = marker.properties.id;
            el.style.backgroundImage =
                'url(https://placekitten.com/g/'  
                marker.properties.iconSize.join('/')  
                '/)';
            el.style.width = marker.properties.iconSize[0]   'px';
            el.style.height = marker.properties.iconSize[1]   'px';
            el.style.backgroundSize = '100%';
            el.style.display = 'none';

            el.addEventListener('click', function () {
                window.alert(marker.properties.message);
            });

            // Add markers to the map.
            new mapboxgl.Marker(el)
                .setLngLat(marker.geometry.coordinates)
                .addTo(map);
        });

        map.on('load', function () {
            // add a clustered GeoJSON source for a sample set of earthquakes
            map.addSource('earthquakes', {
                'type': 'geojson',
                'data': geojson, //'https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson',
                'cluster': true,
                'clusterRadius': 80,
                'clusterProperties': {
                    // keep separate counts for each magnitude category in a cluster
                    'mag1': [' ', ['case', mag1, 1, 0]],
                    'mag2': [' ', ['case', mag2, 1, 0]],
                    'mag3': [' ', ['case', mag3, 1, 0]],
                    'mag4': [' ', ['case', mag4, 1, 0]],
                    'mag5': [' ', ['case', mag5, 1, 0]]
                }
            });
            // circle and symbol layers for rendering individual earthquakes (unclustered points)
            map.addLayer({
                'id': 'earthquake_circle',
                'type': 'circle',
                'source': 'earthquakes',
                'filter': ['!=', 'cluster', true],
                'paint': {
                    'circle-color': [
                        'case',
                        mag1,
                        colors[0],
                        mag2,
                        colors[1],
                        mag3,
                        colors[2],
                        mag4,
                        colors[3],
                        colors[4]
                    ],
                    'circle-opacity': 0.6,
                    'circle-radius': 10
                }
            });

            // objects for caching and keeping track of HTML marker objects (for performance)
            var markers = {};
            var markersOnScreen = {};

            function updateMarkers() {
                var newMarkers = {};
                var features = map.querySourceFeatures('earthquakes');
                // for every cluster on the screen, create an HTML marker for it (if we didn't yet),
                // and add it to the map if it's not there already
                for (var i = 0; i < features.length; i  ) {
                    var coords = features[i].geometry.coordinates;

                    var props = features[i].properties;
                    if (!props.cluster) {
                        document.getElementById(props.id).style.display = 'block'
                        continue;
                    }
                    var id = props.cluster_id;
                    var marker = markers[id];
                    if (!marker) {
                        var el = createDonutChart(props);
                        marker = markers[id] = new mapboxgl.Marker({
                            element: el
                        }).setLngLat(coords);
                    }
                    newMarkers[id] = marker;
                    if (!markersOnScreen[id]) {
                        marker.addTo(map);
                    }
                }
                // for every marker we've added previously, remove those that are no longer visible
                for (id in markersOnScreen) {
                    if (!newMarkers[id]) markersOnScreen[id].remove();
                }
                markersOnScreen = newMarkers;
            }

            // after the GeoJSON data is loaded, update markers on the screen on every frame
            map.on('render', function () {
                if (!map.isSourceLoaded('earthquakes')) return;
                updateMarkers();
            });
        });

        // code for creating an SVG donut chart from feature properties
        function createDonutChart(props) {
            var offsets = [];
            var counts = [
                props.mag1,
                props.mag2,
                props.mag3,
                props.mag4,
                props.mag5
            ];
            var total = 0;
            for (var i = 0; i < counts.length; i  ) {
                offsets.push(total);
                total  = counts[i];
            }
            var fontSize =
                total >= 1000 ? 22 : total >= 100 ? 20 : total >= 10 ? 18 : 16;
            var r = total >= 1000 ? 50 : total >= 100 ? 32 : total >= 10 ? 24 : 18;
            var r0 = Math.round(r * 0.6);
            var w = r * 2;

            var html =
                '<div><svg width="'  
                w  
                '" height="'  
                w  
                '" viewbox="0 0 '  
                w  
                ' '  
                w  
                '" text-anchor="middle" style="font: '  
                fontSize  
                'px sans-serif; display: block">';

            for (i = 0; i < counts.length; i  ) {
                html  = donutSegment(
                    offsets[i] / total,
                    (offsets[i]   counts[i]) / total,
                    r,
                    r0,
                    colors[i]
                );
            }
            html  =
                '<circle cx="'  
                r  
                '" cy="'  
                r  
                '" r="'  
                r0  
                '" fill="white" /><text dominant-baseline="central" transform="translate('  
                r  
                ', '  
                r  
                ')">'  
                total.toLocaleString()  
                '</text></svg></div>';
            var el = document.createElement('div');
            el.innerHTML = html;
            return el.firstChild;
        }

        function donutSegment(start, end, r, r0, color) {
            if (end - start === 1) end -= 0.00001;
            var a0 = 2 * Math.PI * (start - 0.25);
            var a1 = 2 * Math.PI * (end - 0.25);
            var x0 = Math.cos(a0),
                y0 = Math.sin(a0);
            var x1 = Math.cos(a1),
                y1 = Math.sin(a1);
            var largeArc = end - start > 0.5 ? 1 : 0;

            return [
                '<path d="M',
                r   r0 * x0,
                r   r0 * y0,
                'L',
                r   r * x0,
                r   r * y0,
                'A',
                r,
                r,
                0,
                largeArc,
                1,
                r   r * x1,
                r   r * y1,
                'L',
                r   r0 * x1,
                r   r0 * y1,
                'A',
                r0,
                r0,
                0,
                largeArc,
                0,
                r   r0 * x0,
                r   r0 * y0,
                '" fill="'   color   '" />'
            ].join(' ');
        }
    </script>