-
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
feat: make FrechetDistance
generic over Metric Spaces
#1274
base: main
Are you sure you want to change the base?
Conversation
@michaelkirk What do you think about the following approach to |
This is only a drive-by comment, but wouldn't it make sense to pass the metric as a value, e.g. |
Hi @grevgeny - this approach nicely extends frechet to other metric spaces. Thank you! I have a preference for the API to be like this:
rather than
They are equivalent at this point, but I have it in mind to one day make the metric spaces customizable (e.g. custom earth radius for Haversine) and I think that change will be a little cleaner if we approach it as suggested. |
We could go directly for |
I'm not sure what you're talking about — could you be more complete in your thought? If you're talking about (someday) customizing metric spaces, I was imaging something like:
But probably more thought is due. |
Yeah, something like that. In my example, |
Euclidean isn't a very illuminating example, because I don't think you'd ever want to customize it, so let's talk about Haversine. ... maybe this would be better: trait FrechetDistance: Distance<Point, Point> {
fn frechet_distance(&self, lhs: &LineString, rhs: &LineString) -> F {
// more or less the same impl as it exists today
}
}
pub struct Haversine;
pub struct CustomHaversine { radius_meters: f64 };
impl Haversine {
pub fn with_radius(radius_meters: f64) -> CustomHaversine {
// Is it weird that this returns something other than `Self`?
CustomHaversine { radius_meters }
}
pub fn wgs84() -> CustomHaversine {
CustomHaversine { radius_meters: 6_378_137 }
}
pub fn earth_mean_radius() -> CustomHaversine {
CustomHaversine { radius_meters: 6_371_000 }
}
}
impl Distance<Point, Point> for CustomHaversine {
fn to_radians(degrees: f64) -> f64 {
degrees * PI / 180.0
}
fn distance(&self, lhs: &Point, rhs: &Point) -> F {
let lat1 = Self::to_radians(lat1);
let lon1 = Self::to_radians(lon1);
let lat2 = Self::to_radians(lat2);
let lon2 = Self::to_radians(lon2);
let dlat = lat2 - lat1;
let dlon = lon2 - lon1;
let a = (dlat / 2.0).sin().powi(2) + lat1.cos() * lat2.cos() * (dlon / 2.0).sin().powi(2);
let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
// uses custom radius
self.radius_meters * c
}
}
impl Distance<Point, Point> for Haversine {
fn distance(&self, lhs: &Point, rhs: &Point) -> F {
// assume Earth default radius
CustomHaversine::wgs84().distance(lhs, rhs)
}
}
// stays concise for the vast majority (I think?) of people who don't want to customize anything
Haversine.frechet_distance(&line_string_1, &line_string_2)
// Offer a slightly more verbose API for the 0.5% of users who want to customize the metric space
let mars_haversine = Haversine::with_radius(3_389_500);
mars_haversine.frechet_distance(&line_string_1, &line_string_2) vs. my previous suggestion, this would avoid the weird distinction between Sorry to derail your PR review @grevgeny - it's kind of related though. 😅 |
CHANGES.md
if knowledge of this change could be valuable to users.Part of #1181