3 unstable releases
0.2.1 | Apr 14, 2024 |
---|---|
0.2.0 | Apr 14, 2024 |
0.1.0 | Aug 14, 2023 |
#274 in Math
3,299 downloads per month
15KB
288 lines
Longitude
A coordinate library in Rust. Dealing with coordinate math is tricky because you are trying to move a point on a spherical object (the earth). Manipulating the longitude is pretty straight forward as it is just the length from the North Pole to the South Pole. Latitude is a bit more complicated because the circumference of the earth varies based on how close you are to the equator. I use this library whenever I need to deal with interactive maps, triangulation, etc. I've used this in my app sardines and another app called Batch Watch.
Dealing with Units of Distance
Create a distance which is a value associated with a unit.
use longitude::{Distance, DistanceUnit};
let distance_a = Distance::from_kilometers(10.);
let distance_b = Distance::from_miles(6.213712);
You can now perform operations on these measurements including conversion, addition, subtraction and multiplying by a scalar.
println!("{:?}", distance_a.convert_to(DistanceUnit::Kilometers));
Performing operators on coordinates
// Add a distance to a coordinate point:
let location_a = Location::from(40.7885447, -111.7656248);
let distance_a = Distance::from_kilometers(8.2);
let location_b = location_a.add(&distance_a, Direction::North);
let location_result = Location::from(40.8622065532978, -111.7656248);
assert!(location_b == location_result)
// Measure the distance between 2 coordinate points:
let location_a = Location::from(40.7885447, -111.7656248);
let location_b = Location::from(40.7945846, -111.6950349);
let distance_a = location_a.distance(&location_b);
let distance_b = Distance::from_kilometers(5.9868);
assert!(distance_a == distance_b);
How does it work?
First it uses the Distance struct for all measurements. This makes conversion easy and ensures you never get confused about units. The location struct stores longitude and latitude. This is how the distance of 2 points is calculated:
impl Location {
pub fn distance(&self, other: &Location) -> Distance {
let (lat1, lng1) = (self.latitude, self.longitude);
let (lat2, lng2) = (other.latitude, other.longitude);
let pi_180 = |x: f64| (x * PI) / 180.;
let d_lat = pi_180(lat2) - pi_180(lat1);
let d_lng = pi_180(lng2) - pi_180(lng1);
let a = (d_lat / 2.).sin().powf(2.) +
pi_180(lat2).cos().powf(2.) *
(d_lng / 2.).sin().powf(2.);
let c = 2. * atan2(a.sqrt(), (1. - a).sqrt());
RADIUS_OF_EARTH.clone() * c
}
}
This function uses the Haversine Formula. (Read more here: https://en.wikipedia.org/wiki/Haversine_formula)
You can also add some distance to coordinate point.
pub fn add(&self, distance: &Distance, direction: Direction) -> Self
The function add takes a distance and direction. The direction is North, South, East of West. If it East or West we can use this formula:
let d = distance.kilometers() / RADIUS_OF_EARTH.kilometers();
let c = 180. / PI;
let offset = d * c / (self.latitude * PI / 180.).cos();
let scalar = if direction == Direction::East { 1. } else { -1. };
Self {
latitude: self.latitude,
longitude: self.longitude + (offset * scalar),
}
We need the trig because the latitude will vary based on distance from the equator.
Adding to the North or South is a bit more straight forward:
let d = distance.kilometers() / RADIUS_OF_EARTH.kilometers();
let c = 180. / PI;
let offset = d * c;
let scalar = if direction == Direction::North { 1. } else { -1. };
Self {
latitude: self.latitude + (offset * scalar),
longitude: self.longitude,
}
Dependencies
~370–700KB
~15K SLoC