-
Notifications
You must be signed in to change notification settings - Fork 199
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
Validation::is_valid #1279
Validation::is_valid #1279
Conversation
69757d9
to
7b5225f
Compare
7f140cc
to
ed0e43a
Compare
#[cfg(debug_assertions)] | ||
if right_position != current_position { | ||
use crate::algorithm::Validation; | ||
if geometry_graph.geometry().is_valid() { |
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 should address #1268
right_position != current_position
should not happen normally, and this debug_assertion was originally intended to find bugs in geo code. However, it's also been shown to happen when given invalid geometry input.
We can now distinguish between a hypothetical bug in geo code from a bug in the caller's code, and just log in that latter case.
@mthh - since this is largely based on your implementation, and you've done some work with validation, would you mind reviewing this? |
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.
Exciting! Can't wait for this to land
Definitely not done reviewing, just did a first pass and added a few comments
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.
Another pass
/// The closure `handle_validation_error` is called for each validation error. | ||
fn visit_validation<T>( | ||
&self, | ||
handle_validation_error: Box<dyn FnMut(Self::Error) -> Result<(), T> + '_>, |
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.
Presumably something like this wasn't possible?
handle_validation_error: FnMut(Self::Error) -> Result<(), T>,
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.
Because of the potentially infinite recursion of GeometryCollection
, we cannot use a true generic impl FnMut
- it must be boxed.
Is that what you were asking about?
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.
Here's the error message when you switch to: handle_validation_error: impl FnMut(Self::Error) -> Result<(), T>,
error: reached the recursion limit while instantiating `validation::geometry_collection::<impl Validation for geo::GeometryCollection>::visit_validation::<geo::validation::InvalidGeometry, Box<{closure@validation::geometry::<impl Validation for geo::Geometry>::visit_validation<..., ...>::{closure#7}}>>`
--> /Users/mkirk/src/georust/geo/geo/src/algorithm/validation/geometry.rs:91:48
|
91 | Geometry::GeometryCollection(g) => g.visit_validation(Box::new(|err| {
| ________________________________________________^
92 | | handle_validation_error(InvalidGeometry::InvalidGeometryCollection(err))
93 | | }))?,
| |_______________^
|
note: `validation::geometry_collection::<impl Validation for geo::GeometryCollection<F>>::visit_validation` defined here
--> /Users/mkirk/src/georust/geo/geo/src/algorithm/validation/geometry_collection.rs:28:5
|
28 | / fn visit_validation<T>(
29 | | &self,
30 | | mut handle_validation_error: impl FnMut(Self::Error) -> Result<(), T>,
31 | | ) -> Result<(), T> {
| |______________________^
= note: the full type name has been written to '/Users/mkirk/src/georust/geo/target/debug/deps/jts_test_runner-201a42bc420bd6fb.long-type.txt'
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 was thinking something like handle_validation_error: impl FnMut(Box<Self::Error>) -> Result<(), T>,
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 don't think that addresses the potentially infinite recursion of the closure - without boxing the closure, the compiler must build a type which can store the state of a closure which validates a GeometryCollection
inside a GeometryCollection
inside a GeometryCollection
inside a...
I confess I'm not that confident in my analysis, but I did have a go at your suggestion here, but ultimately hit the same compiler error:
Reached the recursion limit while instantiating
validation::geometry_collection::<impl Validation for geo::GeometryCollection>::visit_validation::<geo::validation::InvalidGeometry, {closure@validation::geometry::<impl Validation for geo::Geometry>::visit_validation<geo::validation::InvalidGeometry, ...>::{closure#7}}>
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 replied with one more comment but this looks good to me! Thanks for putting the effort into making this happen
/// The closure `handle_validation_error` is called for each validation error. | ||
fn visit_validation<T>( | ||
&self, | ||
handle_validation_error: Box<dyn FnMut(Self::Error) -> Result<(), T> + '_>, |
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 was thinking something like handle_validation_error: impl FnMut(Box<Self::Error>) -> Result<(), T>,
Thanks for the review @frewsxcv! I'll give it another day before merging in case anyone else wants to look. |
@michaelkirk thank you for inviting me to review it, unfortunately I won't have the time to do so. |
See #1283 for details on the unrelated CI failure. |
325ec00e5a1207308eecfb1d8fb5456cd5d5d121 from https://github.com/michaelkirk/geo-validity-check
Note: some tests are failing
failed TestValid2.xml case "Test 740" with error: expected true, actual: false failed TestValid2.xml case "Test 749" with error: expected true, actual: false failed TestValid2.xml case "Test 752" with error: expected true, actual: false failed TestValid2.xml case "Test 753" with error: expected true, actual: false failed TestValid2.xml case "Test 754" with error: expected true, actual: false Note: this commit is empty, just a commit message.
We follow JTS/GEOS convention here.
Previously the `is_valid` vs `explain_invalidity` methods were mostly a cut/paste job. `explain_invalidity` gathered all the errors, while `is_valid` short-circuited to return early as soon as it new a geometry was invalid. Now both methods share a code path, in the new `visit_validation` method. And there's another new method: `check_validation` which, because it returns a result can be used nicely in error handling: ``` polygon.check_validation()?; ``` This also leverages generics (associated types) to return specific error cases per geometry, which replaces some runtime `unreachable` checks with compile time type checks.
Co-authored-by: Corey Farwell <coreyf@rwell.org>
Co-authored-by: Corey Farwell <coreyf@rwell.org>
b4d6493
to
2b4ded7
Compare
CHANGES.md
if knowledge of this change could be valuable to users.Fixes #123, #127, #1268
Much of the original code and test cases are courtesy of https://github.com/mthh/geo-validity-check (thanks @mthh!).
I also enabled the JTS IsValid test cases from our existing JTS test suite.
My approach:
First, I just copy/pasted the existing implementation from https://github.com/mthh/geo-validity-check in: 92d8bfc
And then wired it into our crate: 5dd1909
Most significantly, I did a lot of refactoring in pursuit of deduplicating code in b89e715, so any bugs are my own.
Note to merger: please don't squash these commits, they're somewhat meaningful.