#[link(name = "numkong")]
extern "C" {
fn nk_haversine_f32(
a_lats: *const f32,
a_lons: *const f32,
b_lats: *const f32,
b_lons: *const f32,
n: usize,
results: *mut f32,
);
fn nk_haversine_f64(
a_lats: *const f64,
a_lons: *const f64,
b_lats: *const f64,
b_lons: *const f64,
n: usize,
results: *mut f64,
);
fn nk_vincenty_f32(
a_lats: *const f32,
a_lons: *const f32,
b_lats: *const f32,
b_lons: *const f32,
n: usize,
results: *mut f32,
);
fn nk_vincenty_f64(
a_lats: *const f64,
a_lons: *const f64,
b_lats: *const f64,
b_lons: *const f64,
n: usize,
results: *mut f64,
);
}
pub trait Haversine: Sized {
fn haversine(
a_lat: &[Self],
a_lon: &[Self],
b_lat: &[Self],
b_lon: &[Self],
result: &mut [Self],
) -> Option<()>;
}
pub trait Vincenty: Sized {
fn vincenty(
a_lat: &[Self],
a_lon: &[Self],
b_lat: &[Self],
b_lon: &[Self],
result: &mut [Self],
) -> Option<()>;
}
pub trait Geospatial: Haversine + Vincenty {}
impl Haversine for f64 {
fn haversine(
a_lat: &[Self],
a_lon: &[Self],
b_lat: &[Self],
b_lon: &[Self],
result: &mut [Self],
) -> Option<()> {
let n = a_lat.len();
if a_lon.len() != n || b_lat.len() != n || b_lon.len() != n || result.len() != n {
return None;
}
unsafe {
nk_haversine_f64(
a_lat.as_ptr(),
a_lon.as_ptr(),
b_lat.as_ptr(),
b_lon.as_ptr(),
n,
result.as_mut_ptr(),
)
};
Some(())
}
}
impl Vincenty for f64 {
fn vincenty(
a_lat: &[Self],
a_lon: &[Self],
b_lat: &[Self],
b_lon: &[Self],
result: &mut [Self],
) -> Option<()> {
let n = a_lat.len();
if a_lon.len() != n || b_lat.len() != n || b_lon.len() != n || result.len() != n {
return None;
}
unsafe {
nk_vincenty_f64(
a_lat.as_ptr(),
a_lon.as_ptr(),
b_lat.as_ptr(),
b_lon.as_ptr(),
n,
result.as_mut_ptr(),
)
};
Some(())
}
}
impl Geospatial for f64 {}
impl Haversine for f32 {
fn haversine(
a_lat: &[Self],
a_lon: &[Self],
b_lat: &[Self],
b_lon: &[Self],
result: &mut [Self],
) -> Option<()> {
let n = a_lat.len();
if a_lon.len() != n || b_lat.len() != n || b_lon.len() != n || result.len() != n {
return None;
}
unsafe {
nk_haversine_f32(
a_lat.as_ptr(),
a_lon.as_ptr(),
b_lat.as_ptr(),
b_lon.as_ptr(),
n,
result.as_mut_ptr(),
)
};
Some(())
}
}
impl Vincenty for f32 {
fn vincenty(
a_lat: &[Self],
a_lon: &[Self],
b_lat: &[Self],
b_lon: &[Self],
result: &mut [Self],
) -> Option<()> {
let n = a_lat.len();
if a_lon.len() != n || b_lat.len() != n || b_lon.len() != n || result.len() != n {
return None;
}
unsafe {
nk_vincenty_f32(
a_lat.as_ptr(),
a_lon.as_ptr(),
b_lat.as_ptr(),
b_lon.as_ptr(),
n,
result.as_mut_ptr(),
)
};
Some(())
}
}
impl Geospatial for f32 {}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{assert_close, FloatLike, TestableType};
fn check_haversine<T>(
a_lat_deg: f64,
a_lon_deg: f64,
b_lat_deg: f64,
b_lon_deg: f64,
expected_meters: f64,
tolerance: f64,
) where
T: FloatLike + TestableType + Haversine,
{
let a_lat = [T::from_f32(a_lat_deg.to_radians() as f32)];
let a_lon = [T::from_f32(a_lon_deg.to_radians() as f32)];
let b_lat = [T::from_f32(b_lat_deg.to_radians() as f32)];
let b_lon = [T::from_f32(b_lon_deg.to_radians() as f32)];
let mut result = [T::zero()];
T::haversine(&a_lat, &a_lon, &b_lat, &b_lon, &mut result).unwrap();
assert_close(
result[0].to_f64(),
expected_meters,
tolerance,
0.0,
&format!("haversine<{}>", core::any::type_name::<T>()),
);
}
fn check_vincenty<T>(
a_lat_deg: f64,
a_lon_deg: f64,
b_lat_deg: f64,
b_lon_deg: f64,
expected_meters: f64,
tolerance: f64,
) where
T: FloatLike + TestableType + Vincenty,
{
let a_lat = [T::from_f32(a_lat_deg.to_radians() as f32)];
let a_lon = [T::from_f32(a_lon_deg.to_radians() as f32)];
let b_lat = [T::from_f32(b_lat_deg.to_radians() as f32)];
let b_lon = [T::from_f32(b_lon_deg.to_radians() as f32)];
let mut result = [T::zero()];
T::vincenty(&a_lat, &a_lon, &b_lat, &b_lon, &mut result).unwrap();
assert_close(
result[0].to_f64(),
expected_meters,
tolerance,
0.0,
&format!("vincenty<{}>", core::any::type_name::<T>()),
);
}
#[test]
fn geospatial() {
let hav_expected = 3_914_000.0;
let vin_expected = 3_944_000.0;
check_haversine::<f64>(
40.7128,
-74.0060,
34.0522,
-118.2437,
hav_expected,
20_000.0,
);
check_haversine::<f32>(
40.7128,
-74.0060,
34.0522,
-118.2437,
hav_expected,
50_000.0,
);
check_vincenty::<f64>(
40.7128,
-74.0060,
34.0522,
-118.2437,
vin_expected,
20_000.0,
);
check_vincenty::<f32>(
40.7128,
-74.0060,
34.0522,
-118.2437,
vin_expected,
50_000.0,
);
}
}