-
Notifications
You must be signed in to change notification settings - Fork 200
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
Robust Intersect and Contains #511
Conversation
+ restructure: `intersects/mod.rs` and `contains/mod.rs` + robust intersection + macro for symmetric intersection implementations + robust contains
Fix some tests
A couple of clarifications needed:
Also checked against shapely/geos which seems to concur with our definition (so points on edges / vertices of the polygon are not considered to be "contained"). Shall we go with the definition (so fix |
Yes. Apparently I wrote that broken triangle implementation 😞: b3944ca For my own benefit, I've just now referred to http://portal.opengeospatial.org/files/?artifact_id=25355 (thanks @urschrei 😉)
So it's defined in terms of
And from the jts impl:
So for In the future, I'll be careful to be rigorous about the cases represented in DE-9IM. |
Yeah, DE-9IM based rigorous definitions of our geometry types, and traits definitely seems useful. I believe jts, geos (hence postgis), shapely all use the same definition of Intersects, and Contains so makes sense for us to with the same for interop with the community. Will add some doc to the types trying to formalize the definitions. Aside: why didn't they just name it denim? Possible trademark issues? I'm so tempted to use it as it is so much simpler to type (and fashionable) |
+ define interior, boundary for geometry types + define `Intersects` and `Contains` as per DE-9IM
c84a5ab
to
98b3058
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have yet to review the Contains
portion, but I ran out of time for now.
I have reviewed the Intersecs
and other changes, and they look great! Just some optional feedback for clarity.
I'll finish reviewing up hopefully yet tonight, or if not, tomorrow.
Co-authored-by: Corey Farwell <[email protected]>
+ remove unnecessary PhantomData in Kernel defn. + cargo fmt
Thanks for the comments @frewsxcv @michaelkirk ; I have addressed them, also added a few lines in closes #415 |
@@ -1,5 +1,10 @@ | |||
# Changes | |||
|
|||
## Master (not released) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, thank you for doing this! Tracking as we go will make the eventual release easier.
{ | ||
fn contains(&self, coord: &Coordinate<T>) -> bool { | ||
if self.start == self.end { | ||
&self.start == coord |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a surprising consequence.
Given a coordinate: (0,0)
And a line Line((0,0), (0,1))
The line does not contain the coordinate, since the coordinate is only on the boundary of the line, not in the line's interior.
This continues to be true as we shrink the line — the line Line((0,0), (0,0.5))
doesn't contain the coordinate.
...until the line is shortened all the way to degeneracy - Line((0,0), (0,0))
now contains the coordinate.
Is that right? 🤯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it is confusing, but that is the precise expected behavior. I cross verified with shapely (geos), and it does the same.
} | ||
|
||
// TODO: ensure DE-9IM compliance: esp., when | ||
// line.start and line.end is on the boundaries |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. Also see the test line_in_polygon_edgecases
in contains/mod.rs
. We might be able to use the winding order trait and a few additions to contains to solve these cases, but wanted to attempt this in a different PR.
} | ||
} | ||
|
||
// TODO: also check interiors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, this one also looks wrong. I wouldn't want to include new broken implementations, but I believe you are just moving these, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, these implementations were just moved into this file. We should fix the two traits for de9im compliance in another pr
impl<G, T> Contains<G> for MultiPolygon<T> | ||
where | ||
T: CoordinateType, | ||
Polygon<T>: Contains<G>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like we're newly accepting any type which implements the Contains trait, whereas before MultiPolygon only supported Coordinate and Point, is that right?
Does this existing implementation work for types with more dimensionality than a point?
e.g. If the two squares are part of a single multipolygon, even though the black line is contained by neither square individually, is it contained by the multipolygon?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, MultiPolygon should not contain overlapping constituent polys:
As per the OGC SFS specification, the Polygons in a MultiPolygon may not overlap, and may only touch at single points. This allows the topological point-set semantics to be well-defined.
With the above defn., I think we can lift any Polygon: Contains to work on MultiPolygon
geo/src/algorithm/contains/rect.rs
Outdated
{ | ||
fn contains(&self, bounding_rect: &Rect<T>) -> bool { | ||
// All points of LineString must be in the polygon ? | ||
self.min().x <= bounding_rect.min().x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So a degenerate Rect which is either a Line or a Point will be contained
if it's on the boundary - I don't think that's correct, is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! The previous implementation was the same with strict checking, which would also miss some edge cases. Want to address these in a different PR (needs a few extra impls, utils). Let me add a todo comment here so we get back to it.
fn contains(&self, coord: &Coordinate<T>) -> bool { | ||
let ls = LineString(vec![self.0, self.1, self.2, self.0]); | ||
use utils::*; | ||
coord_pos_relative_to_ring(*coord, &ls) == CoordPos::Inside |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for fixing this!
Ok, this LGTM.
I agree it’s fine to address the pre-existing issues in a follow up PR.
Thanks for making this better! I’d say merge this whenever you’re ready.
… On Sep 19, 2020, at 21:57, rmanoka ***@***.***> wrote:
@rmanoka commented on this pull request.
In geo/src/algorithm/contains/rect.rs:
> +impl<T> Contains<Point<T>> for Rect<T>
+where
+ T: CoordinateType,
+{
+ fn contains(&self, p: &Point<T>) -> bool {
+ self.contains(&p.0)
+ }
+}
+
+impl<T> Contains<Rect<T>> for Rect<T>
+where
+ T: CoordinateType,
+{
+ fn contains(&self, bounding_rect: &Rect<T>) -> bool {
+ // All points of LineString must be in the polygon ?
+ self.min().x <= bounding_rect.min().x
Good catch! The previous implementation was the same with strict checking, which would also miss some edge cases. Want to address these in a different PR (needs a few extra impls, utils). Let me add a todo comment here so we get back to it.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
Ah, I didn’t realize polygons in a multi-polygon couldn’t overlap. Thank you for explaining.
… On Sep 19, 2020, at 21:53, rmanoka ***@***.***> wrote:
@rmanoka commented on this pull request.
In geo/src/algorithm/contains/polygon.rs:
> + .interiors()
+ .iter()
+ .any(|ring| ring.intersects(linestring))
+ } else {
+ false
+ }
+ }
+}
+
+// ┌──────────────────────────────────┐
+// │ Implementations for MultiPolygon │
+// └──────────────────────────────────┘
+impl<G, T> Contains<G> for MultiPolygon<T>
+where
+ T: CoordinateType,
+ Polygon<T>: Contains<G>,
So, MultiPolygon should not contain overlapping constituent polys:
As per the OGC SFS specification, the Polygons in a MultiPolygon may not overlap, and may only touch at single points. This allows the topological point-set semantics to be well-defined.
With the above defn., I think we can lift any Polygon: Contains to work on MultiPolygon
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
Actually, you're right. The polygons can touch at a single point which introduces some edge cases. For eg. two rectangles touching at a single point, and a line passing through that point. It won't be in any of the individual polygons but is contained in the multipolygon. Will leave a comment, and we can get to this when we ensure correctness of these two traits. |
+ update CHANGES.md + add TODO comments for DE-9IM compliance
719131e
to
b93fd10
Compare
bors r=michaelkirk |
bors r=michaelkirk |
Build succeeded: |
Use robust predicates in Intersects and Contains traits
Future work:
The polygon contains line / polygon logic is not yet fully correct. I've added a few TODO comments, and an ignored test for cases where our predicate doesn't match the DE-9IM definition. Unfortunately, it doesn't seem to be a simple change to fix these corner cases. For instance, geos seems to use a concept called
GeometryGraph
for these cases which I'm yet to understand.