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

boolean-contains: incorrectly returns true for Polygon with bite taken out of the side and the polygon's boundary box #1897

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mglasgow
Copy link

@mglasgow mglasgow commented May 5, 2020

  • Use a meaningful title for the pull request. Include the name of the package modified.
  • Have read How To Contribute.
  • Run npm test at the sub modules where changes have occurred.
  • Run npm run lint to ensure code style at the turf module level.

When using Turfjs to generate quadtiles from OpenStreetMap country boundaries, I ran into scenario where boolean-contains returns true when it should not have. The polygons in question are:

screenshot

I am checking if the polygon with the "bite" taken out the side contains the rectangular tile that clipped it. boolean-contains returns true, when it should return false.

The current logic does a check that all the points of the contained polygon lie within the containing polygon. This is obviously a good short-cut test to perform, but cannot give a definitive yes answer.

I looked at the JTS implementation of cover() to get an idea of how they are doing it. They have some point-in-poly shortcut tests, then have to do a full topological comparison. I used cover() rather than contains() because it matches the implementation of how turfjs has defined contains, in regards to polygon boundaries. The comments on JTS contains() vs compare() code do a good job outlining the difference.

My PR to fix this adds another check to the logic. Only return true if the difference between the polygon being contained and the containing polygon is null. IE, check that there is no part of the contained polygon outside of the containing polygon.

I have added equivalent logic to boolean-within, as well as test cases to both.

On the topic of boolean-within, the logic is slightly different to boolean-contains, specifically it's handling of features with null geometries. If this is meant to be an exact opposite of boolean-contains, perhaps it can simply import the boolean-contains package and call it with swapped parameters, rather than duplicating the code in both packages?

…out the side (but not the corner) and the polygon boundary, the function currently returns true. It should return false.

same logic appled to boolean-within
@rowanwins
Copy link
Member

Hey @mglasgow

Thanks for the thoughtful PR - these boolean modules probably need a bit of rework in general. Ideally I think we want to avoid having to do a difference operation because that's quite expensive and these boolean modules are supposed to be fast - that said I probably broke that rule because I didn't know much about algorithms when I wrote them :)

Given the changes at this stage are relatively minor I'm probably happy to merge and tackle a bigger overhaul down the track, but I'd like a few days to ponder just in case anything comes to mind

Cheers
Rowan

PS Greetings from Canberra!

@mglasgow
Copy link
Author

mglasgow commented May 5, 2020

Hi Rowan, I am up in Sydney!

I did give some thought to the implementation. I agree, difference is definitely a heavier operation than the current point-in-poly comparisons. I erred on the side of correctness over performance in this instance. It would be nice if the Martinez implementation had a "short-circuiting" ability: return true or false if any of the polygons have a difference, rather than computing the entire geometry (which we throw away).

The other option I considered was to check that none of the containing polygon points in the boundary lie in the interior (but not exterior) of the polygon that is to be contained. This seems to be going down the path of the segment intersection logic that JTS implements in it's PreparedGeometry implementation.

The simpler solution for my needs was definitely using a difference operator.

I've just considered another general optimisation that could be made in regards to the bounding boxes: rather than checking for overlap, the function could check that the bounding box is contained as well?

@rowanwins
Copy link
Member

I wonder if we could use something like https://github.com/rowanwins/shamos-hoey or https://github.com/rowanwins/sweepline-intersections ?

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

Successfully merging this pull request may close these issues.

None yet

2 participants