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

Add a simple fallback for Polygon#contains? #224

Merged
merged 1 commit into from
Oct 28, 2020

Conversation

Quiwin
Copy link
Contributor

@Quiwin Quiwin commented Oct 6, 2020

Implement default Polygon#contains? and LineString#contains? which handle a Point as input.

Summary

Add a fallback on Polygon and LineString contains so that if geos is not available, rgeo can still be used to determine if a point is inside a polygon.
Its using a raycasting algorithm (https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html) with a border check.

Other Information

Hello rgeo!
At Klaxit, we are planning to migrate from our internal geometry library to rgeo.
Since we will use rgeo in some of our open source projects we dont want to add mandatory c dependencies.

Happy to iterate if some changes are needed.

thank you!

@@ -17,4 +17,5 @@ def setup

# These tests suffer from floating point issues
undef_method :test_point_on_surface
undef_method :test_contains_point
Copy link
Contributor Author

@Quiwin Quiwin Oct 6, 2020

Choose a reason for hiding this comment

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

Undef this test for simple_mercator, since with libgeos the result is false aswell,

factory = RGeo::Geographic.simple_mercator_factory()
point1 = factory.point(0, 0)
point2 = factory.point(1, 2)
point3 = factory.point(3, 9)
line_string = factory.line_string([point1, point2, point3, point1])

point = factory.point(0.5, 1)
p line_string.projection
p point.projection

p line_string.contains?(point)

With libgeos:

#<RGeo::Geos::CAPILineStringImpl:0x3fea4a5127b8 "LINESTRING (0.0 -7.081154551613622E-10, 111319.49079327357 222684.20850554318, 333958.4723798207 1006021.0627551326, 0.0 -7.081154551613622E-10)">
#<RGeo::Geos::CAPIPointImpl:0x3fea4a512344 "POINT (55659.74539663678 111325.14286638486)">
#<RGeo::Geographic::ProjectedPointImpl:0x3fea4a512b14 "POINT (0.5 1.0)">
false

Without:

#<RGeo::Cartesian::LineStringImpl:0x3fea7b502224 "LINESTRING (0.0 -7.081154551613622E-10, 111319.49079327357 222684.20850554318, 333958.4723798207 1006021.0627551326, 0.0 -7.081154551613622E-10)">
#<RGeo::Cartesian::PointImpl:0x3fea7b507cc4 "POINT (55659.74539663678 111325.14286638486)">
#<RGeo::Geographic::ProjectedPointImpl:0x3fea7b5028b4 "POINT (0.5 1.0)">
false

@Quiwin Quiwin marked this pull request as ready for review October 6, 2020 13:45
Implement default Polygon#contains? and LineString#contains? which handle a
Point as input.
Comment on lines +101 to +107
def ring_encloses_point?(ring, point, on_border_return: false)
# This is an implementation of the ray casting algorithm, greatly inspired
# by https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
# Since this algorithm does not handle point on edge, we check first if
# the ring is on the border.
# on_border_return is used for exclusion ring
return on_border_return if ring.contains?(point)
Copy link
Member

Choose a reason for hiding this comment

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

I think that we should get rid of this optional hash parameter on_border_return and just make it false in the code. We can always change it if we determine that should be true later on or some optional parameter, but as it stands this isn't configurable by the user so I think it's cleaner just to make the statement:

return false if ring.contains?(point)

and get rid of the parameter.

Copy link
Contributor Author

@Quiwin Quiwin Oct 27, 2020

Choose a reason for hiding this comment

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

@keithdoggett This parameter is used to change the border behavior for exclusion rings. I'm not sure how we could remove it.

Copy link
Member

Choose a reason for hiding this comment

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

Apologies, you're correct. I somehow missed that it was used with both true and false.

This looks good then.

@keithdoggett
Copy link
Member

Thanks for submitting this. I think it looks great except for the one tweak that I noted above.

@BuonOmo BuonOmo added this to High priority in Prioritized issues Oct 27, 2020
@BuonOmo BuonOmo self-assigned this Oct 27, 2020
Comment on lines +101 to +107
def ring_encloses_point?(ring, point, on_border_return: false)
# This is an implementation of the ray casting algorithm, greatly inspired
# by https://wrf.ecse.rpi.edu/Research/Short_Notes/pnpoly.html
# Since this algorithm does not handle point on edge, we check first if
# the ring is on the border.
# on_border_return is used for exclusion ring
return on_border_return if ring.contains?(point)
Copy link
Member

Choose a reason for hiding this comment

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

Apologies, you're correct. I somehow missed that it was used with both true and false.

This looks good then.

Copy link
Member

@BuonOmo BuonOmo left a comment

Choose a reason for hiding this comment

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

@keithdoggett I'm merging it then !

@BuonOmo BuonOmo merged commit 1a6e730 into rgeo:master Oct 28, 2020
Prioritized issues automation moved this from High priority to Closed Oct 28, 2020
@keithdoggett keithdoggett added this to Closed in Prioritized Issues Oct 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Development

Successfully merging this pull request may close these issues.

None yet

3 participants