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

GeoSeries.contains that matches GeoPandas with the exception of cases that depend on intersection. #770

Closed
wants to merge 3 commits into from

Conversation

thomcom
Copy link
Contributor

@thomcom thomcom commented Nov 2, 2022

Description

Closes #738, #742, #743, and https://github.com/rapidsai/cuspatial/issues/744>

A feature $a$ is contained in a feature $b$ $\leftrightarrow$ all of the points in $a$ are in the inner region of $b$ and none of the exterior points of $a$ or $b$ intersect. This PR implements the first part of the .contains operation, that is, $\leftrightarrow$ all of the points in $a$ are in the inner region of $b$.

We do not have an intersection primitive available yet, so I will add the second portion of this operation when it is available. There are a small number of pytest xfails that demonstrate where we can't agree with GeoPandas yet.

This PR adds boundary exclusion to our point-in-polygon algorithm. Previously, point-in-polygon had inconsistent inclusion or exclusion of the polygon boundary, based on floating-point error and implementation details of the RayCrossings algorithm. Due to that inconsistency I've added explicit boundary exclusion.

Boundary exclusion in this context means that any point in $b$ that is colinear with a line segment in $a$ is treated as an intersection, which then violates the requirements of .contains, returning false for $a.contains(b)$.

Checklist

  • I am familiar with the Contributing Guidelines.
  • New or existing tests cover these changes.
  • The documentation is up to date with these changes.

@thomcom thomcom requested review from a team as code owners November 2, 2022 16:38
@github-actions github-actions bot added libcuspatial Relates to the cuSpatial C++ library Python Related to Python code labels Nov 2, 2022
@thomcom thomcom changed the title `Ge GeoSeries.contains that matches GeoPandas with the exception of exterior cases. Nov 2, 2022
@thomcom thomcom added 2 - In Progress Currenty a work in progress improvement Improvement / enhancement to an existing function non-breaking Non-breaking change labels Nov 2, 2022
@harrism
Copy link
Member

harrism commented Nov 2, 2022

In the description, can you explain what "with the exception of exterior cases" means, precisely?


// Points on the line segment are the same, so intersection is impossible.
// This is possible because we allow closed or unclosed polygons.
if (run == 0.0 && rise == 0.0) continue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try to replace this compare with the new utility I added in #773?
I'd use as if (float_eq_by_ulp(b.x, a.x) && float_eq_by_ulp(b.y, a.y)) continue;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(No need to push to this PR for now - just want to test how usable that utility is)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to test it. Looks like it improves our accuracy for detecting those really hard corner cases like @harrism had identified.

            [Point([3.33, 1.11]), Polygon([[6, 2], [3, 1], [3, 4], [6, 2]]), True],
        ],
    )
    def test_float_precision_limits(point, polygon, expects):
        gpdpoint = gpd.GeoSeries(point)
        gpdpolygon = gpd.GeoSeries(polygon)
        point = cuspatial.from_geopandas(gpdpoint)
        polygon = cuspatial.from_geopandas(gpdpolygon)
        result = polygon.contains(point)
>       assert gpdpolygon.contains(gpdpoint).values == result.values_host

Produces a false now, previously True. It is good for us to have improved accuracy but in this case we conflict with Pandas, who produces True.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this because GeoPandas contains uses Shapely contains and not contains_properly? By excluding boundary points, you are now implementing contains_properly, not contains. https://shapely.readthedocs.io/en/latest/reference/shapely.contains_properly.html

@thomcom thomcom changed the title GeoSeries.contains that matches GeoPandas with the exception of exterior cases. GeoSeries.contains that matches GeoPandas with the exception of cases that depend on intersection. Nov 4, 2022
@thomcom thomcom self-assigned this Nov 4, 2022
@harrism harrism removed the libcuspatial Relates to the cuSpatial C++ library label Nov 14, 2022
@thomcom
Copy link
Contributor Author

thomcom commented Nov 30, 2022

Closing in favor of #749

@thomcom thomcom closed this Nov 30, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 - In Progress Currenty a work in progress improvement Improvement / enhancement to an existing function non-breaking Non-breaking change Python Related to Python code
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

[FEA] Determine if point-in-polygon is exterior inclusive or not.
3 participants