-
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
relative_eq() #567
relative_eq() #567
Conversation
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.
Hi @martinfrances107 - this is looking pretty good!
With these traits implemented, we can now use something like:
approx::assert_relative_eq!(line_string_1, line_string_2);
Right? It'd be nice to see that macro usage in a test, since that's how we usually access these traits.
Is there a reason not to implement this on all of geo_types::Geometry
?
Other than that, I have left some (I think) small requests for changes.
geo-types/src/line.rs
Outdated
epsilon: Self::Epsilon, | ||
max_relative: Self::Epsilon, | ||
) -> bool { | ||
self.start_point() |
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.
Rather than line.start_point()/line.end_point() which construct a Point
s, can we instead compare the underlying Coordinates directly?
I'd prefer something like this:
self.0.relative_eq(other.0, epsilon, max_relative)
&& self.1.relative_eq(other.1, epsilon, max_relative)
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 fixed this in the commit, to this PR, labelled 'points.rs Simplify RelativeEq for Point'
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 want to respond to this question
With these traits implemented, we can now use something like:
approx::assert_relative_eq!(line_string_1, line_string_2);
Right?
I have added a new commit here "Added new feature relative_eq"
It does not work as I want yet... so there is more to come.
But I am at the point where I should discuss some architectural re-plumbing in public.
I actually want this to work in a module I am developing. This project rightly wants to keep approx as an internal dev-dependency. So If I want to see in in my third party module then I need to optionally expose approx when the other build environment ( Cargo.toml) enables the geo module's feature "relative_eq" This might explain some of my changes.
My current issue is that I can ONLY demonstrate approx::assert_relative_eq working in a limited number of build environments. It works in 'test' and 'doctest' ONLY and ONLY inside geo-types. When I try and demo this in geo sub-crate ( ie geo/src/algorithm/rotate.rs) or my third party module.. then it fails to compile..
geo-types/src/line.rs
Outdated
|
||
#[inline] | ||
fn abs_diff_eq(&self, other: &Line<T>, epsilon: Self::Epsilon) -> bool { | ||
self.start_point() |
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.
ditto about point vs. coords
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.
Thanks you fixed in : -
lines.rs Simplify "AbsDiffEq for Line" to operate of coords not points.
geo-types/src/line_string.rs
Outdated
where | ||
T: CoordinateType + Float + AbsDiffEq, | ||
{ | ||
// type Epsilon = 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.
Does this comment mean something or just leftover and can can be removed?
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.
Thanks .. I will fix this
geo-types/src/line_string.rs
Outdated
} | ||
|
||
#[inline] | ||
fn abs_diff_eq(&self, other: &LineString<T>, epsilon: Self::Epsilon) -> bool { |
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.
Can we do the num_coords check optimization here too like you did in relative_eq?
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.
Thanks this was fixed in the commit
line_string.rs - abs_diff_eq() Now rejects line_strings with unequal lengths. (with tests)
@@ -409,6 +414,79 @@ where | |||
} | |||
} | |||
|
|||
#[cfg(test)] | |||
impl<T> RelativeEq for Point<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.
Please put the implementation on Coordinate
and change Point
's implementation to delegate to that - like:
impl<T> RelativeEq for Point<T> {
fn relative_eq(self, other, epsilon) -> bool {
self.0.relative_eq(other.0, epsilon)
}
...
}
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.
please put the implementation on
I think when I submit a fix... there is going to be a radical change here. So I want to signal it ahead of time
in the main issue .. I talked about euclidean distance versus 'Manhattan' distance.
Coordinate{x: T, y: T} has such a similar structure to Complex{ re: T, im: T}
I think I made a over-complication when I look in approx-0.4..0 at the implementation of Complex I see this ..
The implementation forgets the euclidean distance and just ensures that the both re and im meet the constraint individually. Because I copy/modifed the default implementation .. the same infinity checks for example will be done in the same order .. etc
#[cfg(feature = "num-complex")]
impl<T: RelativeEq> RelativeEq for Complex<T>
where
T::Epsilon: Clone,
{
#[inline]
fn default_max_relative() -> T::Epsilon {
T::default_max_relative()
}
#[inline]
fn relative_eq(
&self,
other: &Complex<T>,
epsilon: T::Epsilon,
max_relative: T::Epsilon,
) -> bool {
T::relative_eq(&self.re, &other.re, epsilon.clone(), max_relative.clone())
&& T::relative_eq(&self.im, &other.im, epsilon.clone(), max_relative.clone())
}
}
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.
Honestly - I think using manhattan distance is fine — it might even be preferred because it's so simple.
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.
Great -- so this has been fixed. The implementation was not moved into coords .. it was simply dissolved away.
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'd actually still prefer if this were moved into Coordinate as described, even if the implementation is much simpler now.
Reason being that Coordinate
is kind of the currency of lots of geo algorithms, so it'd be great if we could start to leverage this functionality in those tests too.
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 .. yep I had only sorted this out for RelativeEq... While I was fixing this - I noticed that I was systematically over constraining this stuff with 'Float' ... fixed.
this commit.
Removed type constraint on RelativeEq and AbsDiffEq that the type must be Float. Also AbsDiffEq for Point now uses AbsDiffEq in Coords.
geo-types/src/multi_point.rs
Outdated
/// let multi_point: MultiPoint<f32> = coords.into_iter().collect(); | ||
/// assert_eq!(3, multi_point.num_coords()); | ||
/// ``` | ||
pub fn num_coords(&self) -> usize { |
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 helpful method.
Since @frewsxcv has a WIP implementation for the same functionality here: #563, I think it'd be better to avoid the public API churn - can you make this method private or pub(crate)
for now, and add a comment linking to the as-of-yet unmerged PR to explain why it's not public.
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.
BTW #563 has since merged.
Could you please rebase or merge against master and use the newly added CoordsIter#coords_count
method instead of this one?
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 is a question of placement, I would like to discuss..
CoordsItrer#coords_count is defined in the geo module.
And there is a security conflict. The implementation of the trait must be in the module defining the trait or the module containing the struct upon which the trait operates.
The conflict is that I can't
A) use coords_count() in geo_types.
or
B) I can't define RelatveEq for Point in geo because it it viewed as a unsafe third party module.
I will post and update when I have found it ... but I can't do what you ask.
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 I have rebased and resolved the deprection notices. here
"After rebase, drop the use of the deprecated num_coords() method."
The solution is that coords_count is a simple one-liner - so I opted to break the DRY principle and inline the function is a few places .. no biggie
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 think this will lead to frustration for our users down the road to have methods with identical functionality but different names. I don't think people will be able to remember that all geo::Geometries can use coords_count
but that only some geo::Geometries can use num_coords
.
As you rightly point out, we unfortunately can't use the geo::CoordsIter
trait in the geo-types
crate, so instead I think it's best to delete this num_coords
method and instead use the more verbose self.0.len()
in the geo-types
crate.
geo-types/src/point.rs
Outdated
epsilon: Self::Epsilon, | ||
max_relative: Self::Epsilon, | ||
) -> bool { | ||
// Handle same infinities. |
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 initially surprised that we weren't simply delegating to the approx::relative_eq
, but I think I understand why.
It might be helpful for future readers if there were a comment that explained this - something like:
// this implementation is based on the [approx crates `relative_eq`](some link)
// implementation, but rather than comparing the difference
// between two scalars, we compare the euclidean distance between two
// coordinates
(feel free to improve)
I am slowly fixing up the code in response to the review. I will post again when I think I have gotten to all the problems. |
Looks like you're making steady progress @martinfrances107! Please tag me when you're ready for another round of review. |
5c9f43b
to
e875d03
Compare
…nd MultiPoint test helpers and associated tests were added to assert that two structs were relatively equal. That is identical within the limits of numerical rounding accuracy.
…lengths. (with tests)
…t be Float. Also AbsDiffEq for Point now uses AbsDiffEq in Coords.
89eaeb1
to
bfb2746
Compare
@michaelkirk , Hi I think I have fixed all the outstanding issues - and this is PR is ready for a review again I have modified geo/src/algorithm/rotate.rs to use the new assertions and avoid hardcoding numerical rounding errors in the expected test results. I have checked cargo doc ... and looked at the HTML output .. example of how to used the new methods are documented. I wanted to be able to demonstrate these test / dependencies features being used my third party module but I think I want to leave that as a followup. Thanks for the detailed review it really did greatly simplify the code and caught some holes in the test strategy Merry Christmas! |
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 looking very close!
I had some additional feedback and am hoping to solicit some input from other georust folks in one comment in particular...
geo-types/src/line.rs
Outdated
/// let a = Line::new(Coordinate { x: 0., y: 0. }, Coordinate { x: 1., y: 1. }); | ||
/// let b = Line::new(Coordinate { x: 0., y: 0. }, Coordinate { x: 1.001, y: 1. }); | ||
/// | ||
/// approx::assert_relative_eq!(a, b, epsilon=0.1) |
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.
assert_abs_diff_eq
geo-types/src/line.rs
Outdated
T::default_epsilon() | ||
} | ||
|
||
/// Equality assertion within a relative limit. |
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.
Comment seems like its for relative_eq not abs_diff_eq
/// let mut coords_b = vec![(0., 0.), (5., 0.), (7.001, 9.)]; | ||
/// let b: LineString<f32> = coords_b.into_iter().collect(); | ||
/// | ||
/// approx::assert_relative_eq!(a, b, epsilon=0.1) |
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.
ditto about assert_abs_diff_eq and the comment
geo-types/src/multi_point.rs
Outdated
/// let multi_point: MultiPoint<f32> = coords.into_iter().collect(); | ||
/// assert_eq!(3, multi_point.num_coords()); | ||
/// ``` | ||
pub fn num_coords(&self) -> usize { |
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 think this will lead to frustration for our users down the road to have methods with identical functionality but different names. I don't think people will be able to remember that all geo::Geometries can use coords_count
but that only some geo::Geometries can use num_coords
.
As you rightly point out, we unfortunately can't use the geo::CoordsIter
trait in the geo-types
crate, so instead I think it's best to delete this num_coords
method and instead use the more verbose self.0.len()
in the geo-types
crate.
geo-types/src/multi_point.rs
Outdated
/// let a = MultiPoint(vec![point![x: 0., y: 0.], point![x: 10., y: 10.]]); | ||
/// let b = MultiPoint(vec![point![x: 0., y: 0.], point![x: 10.001, y: 10.]]); | ||
/// | ||
/// approx::assert_relative_eq!(a, b, epsilon=0.1) |
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.
ditto about assert_abs_diff_eq and the comment
geo/Cargo.toml
Outdated
@@ -22,19 +22,20 @@ geographiclib-rs = { version = "0.2" } | |||
|
|||
proj = { version = "0.20.3", optional = true } | |||
|
|||
geo-types = { version = "0.6.2", path = "../geo-types", features = ["rstar"] } | |||
geo-types = { version = "0.6.2", optional = true, path = "../geo-types", features = ["rstar"] } |
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'm not 100% I understand the changes in this file. Surely geo cannot be built without geo-types, so making it an optional dependency seems fraught.
I'm guessing this is related to enabling the relative_eq
by default only as a devDependency... Is that right? I see why that might be tricky.
A maybe radical alternative which avoids the problem is to get rid of the relative_eq
feature you've introduced and simply have the new functionality you've written enabled by default.
I appreciate the intent, because we try to keep geo-types svelte, but since geo-types already unconditionally relies on the approx
crate, including your new code for the approx impls seems like an acceptable addition to me.
Anyone else care to weigh in?
/cc @georust/geo-publishers
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.
Thanks for the review, this is a tricky one
yes to a radical alternatives... this frustrates me ..Let me state the problem in very simplistic terms just so someone who has seen this before can say .. Oh that is easy.
I want different features to be present on (A) the release build and (B) the dev build.
Making geo-types optional is bad when it must always be included. I was constrained here ... there are long threads on stackoverflow about this - and meandering discussions amongst rustlang developers with lots of well meaning comments along the line of .. we can do better but no action. I hope I am just making a silly mistake.
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.
To be clear, I'm just waiting a few days to solicit feedback from @georust/geo-publishers
The specific question is:
Since we already include approx by default in geo-types, are we ok with adding these implementations of approx traits on geo-types by default as well.
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 looks like the feature we'd want: rust-lang/cargo#7916
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 looks like the feature we'd want: rust-lang/cargo#7916
And if so, it looks like this feature should stabilize soon: rust-lang/cargo#8997
So it seems like we have a few options:
- Wait to merge this until Tracking issue for -Z features=dev_dep rust-lang/cargo#7916 lands on stable Rust
- Make geo-types optional, which is what this pull request currently does. When Tracking issue for -Z features=dev_dep rust-lang/cargo#7916 lands, we can make
geo-types
required again - Keep geo-types required, but always include the
relative_eq
feature. Even on non-test builds. When Tracking issue for -Z features=dev_dep rust-lang/cargo#7916 lands, we can turn onrelative_eq
only indev-dependencies
I'm fine with any of these options, with a slight preference for option 3. It's unfortunate we introduce a new dependency for everyone, but it'll hopefully be temporary.
If we go with options 2 or 3, we should add a comment or open a GitHub Issue saying we can improve the situation when rust-lang/cargo#7916 lands.
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'm working on a slightly different proposal... hopefully I'll have something to share in 20min.
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.
please see martinfrances107#1
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.
What do you think of this solution @frewsxcv (et al)?
Currently in master:
- geo-types dependency on approx is not optional
- the methods in geo_types::private_utils are unconditionally built, but only used within geo-types if the
rstar
feature is enabled.
After this PR:
- geo-types dependency on approx is optional
- These new implementations for RelativeEq and AbsDiffEq are enabled with the now optional
approx
feature - There is "public" API breakage, in that methods in geo_types::private_utils are now skipped unless the rstar feature which requires them is enabled (
geo
buildsgeo-types
w/rstar
feature, so this won't affectgeo
).
So technically, if some other crate is using the methods in private_utils, and not enabling the rstar
feature, this would break for them. Even though this technically breaks a public API, I think it's OK to treat this as a minor change since the changes are in a module with the word "private" in its name. WDYT?
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.
SGTM! Great solution
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.
^^ Agreed!
let line1 = Line::from([(1., 0.9999999999999999), (-1., 1.)]); | ||
assert_eq!(line0.rotate(90.), line1); | ||
let line1 = Line::from([(1., 1.), (-1., 1.)]); | ||
assert_relative_eq!(line0.rotate(90.0), line1); |
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.
🎉 yay!
} | ||
#[test] | ||
fn test_rotate_line_around_point() { | ||
let line0 = Line::new(Point::new(0., 0.), Point::new(0., 2.)); | ||
let line1 = Line::new( | ||
Point::new(0., 0.), | ||
Point::new(-2., 0.00000000000000012246467991473532), |
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.
🎉 yay! good riddance!
And thank you for all the good docs and tests! |
Thanks for the carefully review again... I have fixed all my silly mistakes. The major problem concerning how to pull in different features into different build environment persists. |
Rename `relative_eq` feature to `approx` and make optional
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.
bors r+
Ok, everything seems to be in order! Thanks for working through the lengthy review process with us @martinfrances107. I'm pretty happy with where it ended up. Thanks for your contribution!
Build succeeded: |
628: approx for all Geometry types r=frewsxcv a=michaelkirk - [x] I agree to follow the project's [code of conduct](https://github.com/georust/geo/blob/master/CODE_OF_CONDUCT.md). - [x] I added an entry to `CHANGES.md` if knowledge of this change could be valuable to users. --- Follow up to #567 which implemented for Coord, Point, LineString, and a few others, this PR implements it for the remaining types, and `Geometry` itself. FYI this is a precursor to an integration I'm working on with some of the JTS test suite. Co-authored-by: Michael Kirk <[email protected]>
Added num_coords() method to MultiPoint. To Point, Line, LineString and MultiPoint test helpers and associated tests were added to assert that two structs were relatively equal. That is identical within the limits of numerical rounding accuracy.
CHANGES.md
if knowledge of this change could be valuable to users.