Turf.js – Using Turf Mask for Polygons with Inner Rings

geojsonmaskingpolygonturf

I am getting some weird result with turf.mask().

This is the polygon I am trying to run the mask() function with: https://gist.githubusercontent.com/Stophface/50155878441205412001247878f58b63/raw/324021868304f96c795e7d8cd18ec2c6f45ee842/polygon.geojson

Raw data

According to the docs, if the mask is undefined, the world extent is used. The result of turf.mask(polygon, undefined) is this

Result of mask

https://gist.githubusercontent.com/Stophface/50155878441205412001247878f58b63/raw/5e71fc1c4e4d6815cfa22e15eb1bf10c9208231f/mask_result.geojson

So it is filling the interior ring
Why is it doing that?

The data for the second code snippet of the accepted answere (because I deleted the original question) https://gist.github.com/Stophface/5f0cf7a28466d47f07c83d6d26eba078

Best Answer

There might be simpler way of solving this problem by using some library, but one possible way with only Turf.js would be to:

  • create array of outer liner rings and array of inner linear rings of polygon (with later reverse coordinate order);
  • create multi polygon from outer linear rings and mask it;
  • get linear rings of created masked polygon and append saved inner liner rings;
  • create multi polygon from this array of rings.

Code could then look something like this:

var coords = turf.getCoords(polygon);

var outerRings = [];
var innerRings = [];

coords.forEach(function(polyCoords) {
  polyCoords.forEach(function(linearRing, i) {
    if (i == 0)
      outerRings.push(linearRing);
    else {
      var poly = turf.rewind(turf.polygon([linearRing]));
      innerRings.push(turf.getCoords(poly)[0]);
    }
  })
});
var outerRingsPoly = turf.multiPolygon([outerRings]);

var maskedOuterRingsPoly = turf.mask(outerRingsPoly);
var maskedOuterRings = turf.getCoords(maskedOuterRingsPoly);
var allRings = maskedOuterRings.concat(innerRings);

var finalPoly = turf.multiPolygon([allRings]);

Result then looks like this:

enter image description here

EDIT: When testing again with GeoJSON with several holes, turf.js complained executing var coords = turf.getCoords(polygon), so I had to change logic a bit.

Here is modified code:

var outerRings = [];
var innerRings = [];

turf.geomEach(polygon, function(geom) {
  geom.coordinates[0].forEach(function(linearRing, i) {
    if (i == 0)
      outerRings.push(linearRing);
    else {
      var poly = turf.rewind(turf.polygon([linearRing]));
      innerRings.push(turf.getCoords(poly)[0]);
    }
  });
});

var outerRingsPoly = turf.multiPolygon([outerRings]);

var maskedOuterRingsPoly = turf.mask(outerRingsPoly);
var maskedOuterRings = turf.getCoords(maskedOuterRingsPoly);
var allRings = maskedOuterRings.concat(innerRings);

var finalPoly = turf.multiPolygon([allRings]);

And here the result:

enter image description here