Search code examples
javascriptleafletlodash

Combining tooltips for multiple Leaflet markers of the same location when using GeoJSON FeatureCollections?


We've got a Leaflet map containing markers and some of them have the same location, and we want to combine tooltips for markers of the same location. I asked about this, providing a simplified reproducible eg, and a comment linked to an answer on a different thread which elegantly solved the issue with JS. Our problem now is that we barely know JS and don't know how to adapt this to our oversimplified eg.

Our previous reproducible eg. built markers individually (eg. L.marker([51.5, -0.4]).bindPopup("single popup").addTo(myMap);), but we did this for question simplicity and our real code uses GeoJSON FeatureCollection. Adapting our previous eg to use this:

<!DOCTYPE html>
    <style>
        html,
        body {
            margin: 0px;
            height: 100%;
        }

        body {
            display: flex;
        }
    </style>
<body>
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css"
        integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
        crossorigin="" />
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js"
        integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
        crossorigin=""></script>
    <script src="https://unpkg.com/[email protected]/lodash.js"></script>

    <div id="mapid" style="flex: 1"></div>
    <script>var myMap = L.map("mapid");
        L.tileLayer('https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png', { attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', }).addTo(myMap);
        myMap.setView([51.5, -0.09], 10);

        var geojsonFeatureCollection =
        {
            "type": "FeatureCollection",
            "features": [{
                "type": "Feature",
                "geometry": {
                    "type": "Point",
                    "coordinates": [-0.09,51.5]
                },
                "properties": {
                    "prop0": "text to show in combined pop up",
                    "ignore": "ignore this text"
                }
            },
            {
                "type": "Feature",
                "geometry": {
                    "type": "Point",
                    "coordinates": [-0.09,51.5]
                },
                "properties": {
                    "prop0": "more text to show in combined pop up",
                    "ignore": "ignore this text"
                }
            },
            {
                "type": "Feature",
                "geometry": {
                    "type": "Point",
                    "coordinates": [-0.4,51.5]
                },
                "properties": {
                    "prop0": "single popup",
                    "ignore": "ignore this text"
                }
            }]
        }

        L.geoJSON(geojsonFeatureCollection, {
    pointToLayer: function (feature, coordinates) {
        return L.marker(coordinates);
    }
})
.bindPopup((layer) => layer.feature.properties.prop0)
.addTo(myMap);
    </script>
</body>
</html>

The answer that comment linked to a demo which used JSON data for the points which is close to but not quite the same as FeatureCollection. It used Lodash to group items up by "name", which seems a property of each JSON item: var groupedData = _.groupBy(data, "name");, but we don't know how to do the same with the "features.geometry.coordinates" property of our data.

How can we group GeoJSON FeatureCollection data up like this to combine markers of the same location and then customize tooltips (eg. use the text from "prop0" and other fields)?


Solution

  • You should be able to rework (possibly in runtime) your GeoJSON data with a very similar method as proposed in Bind more popups to the same marker or merge popups content

    If I understand correctly, in your case you do not have a property that you can use to identify same locations (was "name" in the linked post), so you can only use the coordinates to determine if Point features should be merged together?

    In that case, you could have something in the lines of:

    const groupedByCoords = _.groupBy(
      geojsonFeatureCollection.features,
      (feature) => feature.geometry.coordinates
    );
    
    const features = [];
    
         // Rework the Features to merge content
         for (const key in groupedByCoords){
           const items = groupedByCoords[key];
           const mergedContent = items.map((item) => item.properties.prop0).join("<br/>");
           const feature = _.cloneDeep(items[0]);
           feature.properties.prop0 = mergedContent;
           features.push(feature);
         }
    
    // Replace your features
    geojsonFeatureCollection.features = features;
    

    Screenshot result Marker with popup and combined text

    Live demo: https://plnkr.co/edit/G4Laa9MIvTLkEFlo