Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mask - option for islands in holes to be pulled out as separate MultiPolygon exterior rings #1305

Open
andrewharvey opened this issue Mar 10, 2018 · 6 comments

Comments

@andrewharvey
Copy link
Contributor

The mask function takes an input polygon and inverts it such all areas outside the input polygon now form the inside of the output.

When you pass mask a Polygon with a hole (an exterior ring and an interior ring) like this:

polygon with a hole

https://gist.github.com/andrewharvey/971b9db1900a62efa7635f6a6d337ae7

It will simply add a new global exterior ring and make the input rings interior rings.

Although I'm not 100% sure if supported by the GeoJSON spec many applications which support GeoJSON allow for a GeoJSON Polygon with an interior ring inside another interior ring to be interpreted as an island inside a hole inside a polygon.

I think the winding order of the interior rings should be used to spell out if the ring should be a hole or island, although this seems to make no difference in Leaflet.

So in Leaflet this works and visually creates the mask you expect.

island in a hole in a polygon

https://gist.github.com/andrewharvey/c092a6c4932a4d94ecb6cd59dc935b90

However not all applications support this for example earcut does not: mapbox/earcut#94.

I propose mask should have an option for these islands within holes to be pulled out as separate exterior rings as part of a MultiPolygon geometry type rather than inside the Polygon type which is what earcut asks for.

I'm working on a PR at the moment to do this, appreciate any thoughts on the issue.

It should also fix the issue where you can't round trip that second example back to the first by again applying the mask.

@andrewharvey andrewharvey changed the title mask - option for islands in holes pulled out as separate features mask - option for islands in holes to be pulled out as separate MultiPolygon exterior rings Mar 10, 2018
@stebogit
Copy link
Collaborator

Hey @andrewharvey thanks for the interesting topic. Here my two cents.
Reading the specs looks like actually islands inside a polygon are not part of a polygon itself as it says:

For Polygons with more than one of these rings, the first MUST be the exterior ring, and any others MUST be interior rings. The exterior ring bounds the surface, and the interior rings (if present) bound holes within the surface

They should be part of a MuliPolygon instead, like earcut is doing if I understand correctly.
So I'm not sure if we should allow that option in Turf.

@andrewharvey
Copy link
Contributor Author

They should be part of a MuliPolygon instead, like earcut is doing if I understand correctly.

If that's the case then the current output of turf.mask is wrong and it looks okay in Leaflet by luck, in which case what I propose shouldn't be an option it should be the default behaviour.

@andrewharvey
Copy link
Contributor Author

They should be part of a MuliPolygon instead, like earcut is doing if I understand correctly.

I checked on the GeoJSON spec mailing list and this is correct, the islands should be part of a MultiPolygon.

Checking the source code I realised the mask function is defined at a very low level in terms of operations in the exterior and interior rings, so technically what it currently does is correct, just not what I as a user would expect. So I'm thinking we should change the definition to be higher level and change the behaviour so input Polygon's which have inners are spit out as new Polygons part of a MultiPolygon. I'm sure there's a lot of edge cases though where this won't work, am I opening a can of worms?

turf.difference works as expected for this use case, but it doesn't handle all the edge cases like the overlapping polygon test, it errors with "side location conflict".

/cc @rowanwins

@andrewharvey
Copy link
Contributor Author

also related turf.mask seems to get stuck in an infinite loop on this https://gist.github.com/andrewharvey/7cf52badbe77e9843096a882a5e93c3e. I've since migrated to using turf.difference instead of mask.

@waissbluth
Copy link

waissbluth commented Feb 13, 2020

They should be part of a MultiPolygon instead, like earcut is doing if I understand correctly.

If that's the case then the current output of turf.mask is wrong and it looks okay in Leaflet by luck, in which case what I propose shouldn't be an option it should be the default behaviour.

Agree -- @andrewharvey did you figure this out? I am getting the same invalid Polygon using difference: a "Polygon" with two concentric interior rings, the second of which is an island in the hole.

UPDATE: I get an invalid Polygon using "@turf/difference": "6.0.1" but the correct MultiPolygon on "@turf/difference": "5.1.5". What version are you using @andrewharvey?

@waissbluth
Copy link

This is a simple test case that fails to produce a valid MultiPolygon for both difference @ 6.0.1 and mask @ 5.1.5, and instead produce an invalid Polygon. difference @ 5.1.5 produces proper output.

const world = {
  "type": "Polygon",
  "coordinates": [ [ [ -180, -90 ], [  180, -90 ], [  180,  90 ], [ -180,  90 ], [ -180, -90 ] ] ]
};

const hole = {
  "type": "Polygon",
  "coordinates": [
    [ [ -10,  50 ], [ -40,  20 ], [  70, -30 ],  [ -10,  50 ] ],
    [ [ -5,  30 ], [ -20, 20 ], [  35, 3  ], [ -5,  30 ] ]
  ]
};

console.log(turf.difference(world, hole));
console.log(turf.mask(hole));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants