longitude

Crates.iolongitude
lib.rslongitude
version0.2.1
sourcesrc
created_at2023-08-14 02:11:07.813893
updated_at2024-04-14 04:06:19.94273
descriptionWork with real world coordinates in Rust!
homepagehttps://github.com/benfaerber/longitude
repositoryhttps://github.com/benfaerber/longitude
max_upload_size
id943806
size14,951
Ben Faerber (benfaerber)

documentation

README

Longitude crates.io

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,
}

Commit count: 7

cargo fmt