Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@
* Navigation
* [Bearing](https://github.com/TheAlgorithms/Rust/blob/master/src/navigation/bearing.rs)
* [Haversine](https://github.com/TheAlgorithms/Rust/blob/master/src/navigation/haversine.rs)
* [Rhumbline](https://github.com/TheAlgorithms/Rust/blob/master/src/navigation/rhumbline.rs)
* Number Theory
* [Compute Totient](https://github.com/TheAlgorithms/Rust/blob/master/src/number_theory/compute_totient.rs)
* [Euler Totient](https://github.com/TheAlgorithms/Rust/blob/master/src/number_theory/euler_totient.rs)
Expand Down
5 changes: 4 additions & 1 deletion src/navigation/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
mod bearing;
mod haversine;

mod rhumbline;
pub use self::bearing::bearing;
pub use self::haversine::haversine;
pub use self::rhumbline::rhumb_bearing;
pub use self::rhumbline::rhumb_destination;
pub use self::rhumbline::rhumb_dist;
109 changes: 109 additions & 0 deletions src/navigation/rhumbline.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use std::f64::consts::PI;

const EARTH_RADIUS: f64 = 6371000.0;

pub fn rhumb_dist(lat1: f64, long1: f64, lat2: f64, long2: f64) -> f64 {
let phi1 = lat1 * PI / 180.00;
let phi2 = lat2 * PI / 180.00;
let del_phi = phi2 - phi1;
let mut del_lambda = (long2 - long1) * PI / 180.00;

if del_lambda > PI {
del_lambda -= 2.00 * PI;
} else if del_lambda < -PI {
del_lambda += 2.00 * PI;
}

let del_psi = ((phi2 / 2.00 + PI / 4.00).tan() / (phi1 / 2.00 + PI / 4.00).tan()).ln();
let q = if del_psi.abs() > 1e-12 {
del_phi / del_psi
} else {
phi1.cos()
};

(del_phi.powf(2.00) + (q * del_lambda).powf(2.00)).sqrt() * EARTH_RADIUS
}

pub fn rhumb_bearing(lat1: f64, long1: f64, lat2: f64, long2: f64) -> f64 {
let phi1 = lat1 * PI / 180.00;
let phi2 = lat2 * PI / 180.00;
let mut del_lambda = (long2 - long1) * PI / 180.00;

if del_lambda > PI {
del_lambda -= 2.0 * PI;
} else if del_lambda < -PI {
del_lambda += 2.0 * PI;
}

let del_psi = ((phi2 / 2.00 + PI / 4.00).tan() / (phi1 / 2.00 + PI / 4.00).tan()).ln();
let bearing = del_lambda.atan2(del_psi) * 180.0 / PI;
(bearing + 360.00) % 360.00
}
pub fn rhumb_destination(lat: f64, long: f64, distance: f64, bearing: f64) -> (f64, f64) {
let del = distance / EARTH_RADIUS;
let phi1 = lat * PI / 180.00;
let lambda1 = long * PI / 180.00;
let theta = bearing * PI / 180.00;

let del_phi = del * theta.cos();
let phi2 = (phi1 + del_phi).clamp(-PI / 2.0, PI / 2.0);

let del_psi = ((phi2 / 2.00 + PI / 4.00).tan() / (phi1 / 2.0 + PI / 4.0).tan()).ln();
let q = if del_psi.abs() > 1e-12 {
del_phi / del_psi
} else {
phi1.cos()
};

let del_lambda = del * theta.sin() / q;
let lambda2 = lambda1 + del_lambda;

(phi2 * 180.00 / PI, lambda2 * 180.00 / PI)
}

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

#[test]
fn test_rhumb_distance() {
let distance = rhumb_dist(28.5416, 77.2006, 28.5457, 77.1928);
assert!(distance > 700.00 && distance < 1000.0);
}

#[test]
fn test_rhumb_bearing() {
let bearing = rhumb_bearing(28.5416, 77.2006, 28.5457, 77.1928);
assert!((bearing - 300.0).abs() < 5.0);
}

#[test]
fn test_rhumb_destination_point() {
let (lat, lng) = rhumb_destination(28.5457, 77.1928, 1000.00, 305.0);
assert!((lat - 28.550).abs() < 0.010);
assert!((lng - 77.1851).abs() < 0.010);
}
// edge cases

#[test]
fn test_rhumb_distance_cross_antimeridian() {
// Test when del_lambda > PI (line 12)
let distance = rhumb_dist(0.0, 170.0, 0.0, -170.0);
assert!(distance > 0.0);
}

#[test]
fn test_rhumb_distance_cross_antimeridian_negative() {
// Test when del_lambda < -PI (line 14)
let distance = rhumb_dist(0.0, -170.0, 0.0, 170.0);
assert!(distance > 0.0);
}

#[test]
fn test_rhumb_distance_to_equator() {
// Test when del_psi is near zero (line 21 - the else branch)
let distance = rhumb_dist(0.0, 0.0, 0.0, 1.0);
assert!(distance > 0.0);
}
}