leafletpolygongeojsonturfjs

Geojson/ turf : merge multiple polygons to one polygon keeping hole


So I want to merge adjacent polygons in javascript this is what I actually have with my code:

map

I want to remove inside stroke but keep border stroke.

So I want to go from this:

map2

To this:

map3

I want to keep the hole for Paris - I can defined which polygons have to be grouped but I cannot merge them with this code:

var map = L.map('map', {
    center: [46.52863469527167, 2.43896484375],
    zoom: 6,
    maxZoom: 18,
    minZoom: 7
});
$http.get("france.json").then(function (response) {
    $scope.communeFr = response.data.features
    $http.get(apiult).then(function (response) {
        $scope.showCommune = response.data.Liste
        $scope.communeFr.map(x => {
            x.show = false
            for (let i = 0; i < $scope.showCommune .length; i++) {
                if (x.properties.insee == $scope.showCommune[i].insee) {
                    x.show = true
                    return
                }
            }                
        })
        L.geoJson($scope.communeFr, {
            filter: function (feature, latlng) {
                return feature.show 
            }
        }).addTo(map);
    });
    
});

UPDATE - I tried with turf.union but the output is not right:

map4

This is my code

var GroupPolygons = [];    
$scope.communeFr.map(x => {
    x.show = false
    for (let i = 0; i < $scope.showCommune .length; i++) {
        if (x.properties.insee == $scope.showCommune[i].insee) {
            GroupPolygons.push(x)
        }
    }                
})
var union = turf.union(...GroupPolygons)
L.geoJson(union).addTo(map)

Solution

  • There seems to be an ambiguity in the turf docs concerning union regarding the number of polygons that can be passed as arguments. See here and here. Looks like it has to be two at a time - so this will work:

    // feature ids to remove e.g. central Paris
    var removeIds = [868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887];
    
    // filter features to remove central Paris
    var hole = fc.features.filter(f => !removeIds.includes(f.properties.ID_APUR))
    
    // do the union over each feature
    var union = hole[0];
    for (let i=1; i<hole.length; i++) {
      union = turf.union(union, hole[i]);
    }
    

    I selected a bunch of central Paris to remove and then from the remaining features I start with feature 0 and then turf.join all the other features (from index 1 onward) to this one. Seems inefficient but works...

    Small gotcha here:

    // new Feature collection with unioned features
    var fc2 = {
      "type": "FeatureCollection",
      "features": [union] // note features has to be an array
    }
    

    Remember to pass a FeatureCollection to L.geoJson and that the features property needs to be an array - even though in this case it contains a single feature of the merged areas.

    Working example:

    // center map on Paris
    var map = L.map('mapid', {
        center: [48.856, 2.352],
        zoom: 9,
        maxZoom: 18,
        minZoom: 1
    });
    
    // add tiles
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(map);
    
    var url = "https://gist.githubusercontent.com/robinmackenzie/937e5bd42a0412c21281f69b8f0c8614/raw/fbed7c2783366463a250e4bb0ebcf3c5f6d54dfe/greaterParis.geo.json";
    
    // get greater Paris definition
    fetch(url)
      .then(response => response.json())
      .then(fc => { 
    
        // feature ids to remove e.g. central Paris
        var removeIds = [868, 869, 870, 871, 872, 873, 874, 875, 876, 877, 878, 879, 880, 881, 882, 883, 884, 885, 886, 887];
    
        // filter features to remove central Paris
        var hole = fc.features.filter(f => !removeIds.includes(f.properties.ID_APUR))
    
        // do the union over each feature
        var union = hole[0];
        for (let i=1; i<hole.length; i++) {
          union = turf.union(union, hole[i]);
        }
    
        // new Feature collection with unioned features
        var fc2 = {
          "type": "FeatureCollection",
          "features": [union] // note features has to be an array
        }
    
        // add to map
        L.geoJson(fc2).addTo(map);
      
      });
    #mapid { height: 200px; }
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"/>
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src='https://unpkg.com/@turf/turf@6.3.0/turf.min.js'></script>
    <div id="mapid"></div>