1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Geoutils is a evolving crate to provide several geological computations and utilities.
//! Most computations are based off methods on the [Location](struct.Location.html) struct.
//!
//! Find the full API reference at [docs.rs](http://docs.rs/geoutils).
//!
//! # Examples
//!
//! * Get the distance between two points using [Vincenty's Inverse Formula](https://en.wikipedia.org/wiki/Vincenty%27s_formulae).
//! ```rust
//! use geoutils::Location;
//!
//! let berlin = Location::new(52.518611, 13.408056);
//! let moscow = Location::new(55.751667, 37.617778);
//! let distance = berlin.distance_to(&moscow).unwrap();
//!
//! println!("Distance = {}", distance.meters());
//! ```
//!
//! * Get the distance between two points using the [Haversine Formula](https://en.wikipedia.org/wiki/Haversine_formula).
//! ```rust
//! use geoutils::Location;
//!
//! let berlin = Location::new(52.518611, 13.408056);
//! let moscow = Location::new(55.751667, 37.617778);
//! let distance = berlin.haversine_distance_to(&moscow);
//!
//! println!("Distance = {}", distance.meters());
//! ```
//!
//! * Get the center of a list of coordinates.
//! ```rust
//! use geoutils::Location;
//!
//! let berlin = Location::new(52.518611, 13.408056);
//! let moscow = Location::new(55.751667, 37.617778);
//! let center = Location::center(&vec![&berlin, &moscow]);
//!
//! println!("Center {}, {}", center.latitude(), center.longitude());
//! ```
//!
//! * Check if a point falls in a certain radius of another point.
//! ```rust
//! use geoutils::{Location, Distance};
//!
//! let berlin = Location::new(52.518611, 13.408056);
//! let moscow = Location::new(55.751667, 37.617778);
//! let is_in_radius = berlin.is_in_circle(&moscow, Distance::from_meters(2000.0)).unwrap();
//!
//! println!("Is Berlin in 2000m of Moscow? {}", is_in_radius);
//! ```
//!

#![deny(missing_docs)]
mod formula;

pub use formula::Distance;

/// Location defines a point using it's latitude and longitude.
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Location(f64, f64);

impl Location {
    /// Create a new Location with it's degree values of latitude and longitude.
    pub fn new<T: Into<f64>>(lat: T, lon: T) -> Self {
        Location(lat.into(), lon.into())
    }

    /// Get the latitude.
    pub fn latitude(&self) -> f64 {
        self.0
    }

    /// Get the longitude.
    pub fn longitude(&self) -> f64 {
        self.1
    }

    /// Find the distance from itself to another point. Internally uses Vincenty's inverse formula.
    /// For better performance and lesser accuracy, consider [haversine_distance_to](struct.Location.html#method.haversine_distance_to).
    /// This method returns Err if the formula fails to converge within 100 iterations.
    pub fn distance_to(&self, to: &Location) -> Result<Distance, String> {
        match formula::vincenty_inverse(self, to, 0.00001, 0.0) {
            Ok(res) => Ok(res.distance),
            Err(e) => Err(e),
        }
    }

    /// Find the distance from itself to another point using Haversine formula.
    /// This is usually computationally less intensive than [distance_to](struct.Location.html#method.distance_to) but
    /// is generally not as accurate.
    pub fn haversine_distance_to(&self, to: &Location) -> Distance {
        formula::haversine_distance_to(self, to)
    }

    /// Check if the point is within a fixed radius of another point.
    pub fn is_in_circle(&self, center: &Location, radius: Distance) -> Result<bool, String> {
        match formula::vincenty_inverse(self, center, 0.00001, 0.0) {
            Ok(res) => Ok(res.distance.meters() < radius.meters()),
            Err(e) => Err(e),
        }
    }

    /// Find the center of given locations.
    pub fn center(coords: &[&Location]) -> Location {
        formula::center_of_coords(coords)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_get_distance() {
        let l1 = Location::new(27.740068, 85.337576);
        let l2 = Location::new(27.740286, 85.337059);

        match l1.distance_to(&l2) {
            Ok(distance) => {
                assert_eq!(distance.meters(), 56.409);
            }
            Err(e) => panic!("Failed: {:?}", e),
        }
    }

    #[test]
    fn test_get_distance_haversine() {
        let l1 = Location::new(27.740068, 85.337576);
        let l2 = Location::new(27.740286, 85.337059);

        let distance = l1.haversine_distance_to(&l2);
        assert_eq!(distance.meters(), 56.36);
    }

    #[test]
    fn test_get_center() {
        let l1 = Location::new(52.518611, 13.408056);
        let l2 = Location::new(55.751667, 37.617778);

        let l = Location::center(&[&l1, &l2]);

        assert_eq!(l.0, 54.743683);
        assert_eq!(l.1, 25.033239);
    }
}