Skip to content
This repository was archived by the owner on Feb 12, 2025. It is now read-only.

Commit 4690d2b

Browse files
committed
Support for NED2LLA and ENU2LLA
Providing support for NED2LLA and ENU2LLA - given a local cartesian ENU/NED vector and an LLA reference point, plus the ellipsoid, we can convert the NED/ENU vector to LLA. This is all verified by using the same values from LLA2ENU, LLA2NED, ENU2LLA , NED2LLA -> all the test cases pass.
1 parent c85ba11 commit 4690d2b

File tree

2 files changed

+99
-23
lines changed

2 files changed

+99
-23
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "coord_transforms"
3-
version = "1.1.0"
3+
version = "1.2.0"
44
authors = ["David Kramer <[email protected]>"]
55
description = "A Rust crate use for performing coordinate transformations."
66
repository = "https://github.com/DaveKram/coord_transforms"

src/geo.rs

+98-22
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ pub fn ll2utm(ll_vec: &Vector2<f64>, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -
235235
ret_utm
236236
}
237237

238-
/// Converts 3-d LLA origin coordinates plus 3-d LLA coordinates and an ellipsoid to local NED cartesian coordinates
238+
/// Converts 3-d LLA origin coordinates plus 3-d LLA coordinates and an ellipsoid to 3-d local NED cartesian coordinates
239239
///
240240
/// # Arguments
241241
///
@@ -256,7 +256,7 @@ pub fn lla2ned(lla_origin: &Vector3<f64>, lla_vec: &Vector3<f64>, ellipsoid: &ge
256256
ned_rot * (actual - orig)
257257
}
258258

259-
/// Converts 3-d LLA origin coordinates plus 3-d LLA coordinates and an ellipsoid to local ENU cartesian coordinates
259+
/// Converts 3-d LLA origin coordinates plus 3-d LLA coordinates and an ellipsoid to 3-d local ENU cartesian coordinates
260260
///
261261
/// # Arguments
262262
///
@@ -277,6 +277,52 @@ pub fn lla2enu(lla_origin: &Vector3<f64>, lla_vec: &Vector3<f64>, ellipsoid: &ge
277277
enu_rot * (actual - orig)
278278
}
279279

280+
/// Converts 3-d local cartesian NED coordinates plus 3-d LLA origin coordinates and an ellipsoid to 3-d LLA coordinates
281+
///
282+
/// # Arguments
283+
///
284+
/// * `lla_origin` - Vector3 reference to the LLA origin vector (lat, long, alt) (radians, radians, meters)
285+
/// * `ned_vec` - Vector3 reference to the local NED cartesian vector (x, y, z)
286+
/// * `ellipsoid` - geo_ellipsoid reference to the ellipsoid
287+
///
288+
/// # Return Value
289+
///
290+
/// * `nalgebra::Vector3<f64>` - (lat, long, alt) (radians, radians, meters)
291+
///
292+
pub fn ned2lla(lla_origin: &Vector3<f64>, ned_vec: &Vector3<f64>, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -> Vector3<f64> {
293+
let orig = lla2ecef(lla_origin, ellipsoid);
294+
let ned_rot = Matrix3::new(-lla_origin.x.sin() * lla_origin.y.cos(), -lla_origin.y.sin(), -lla_origin.x.cos() * lla_origin.y.cos(),
295+
-lla_origin.x.sin() * lla_origin.y.sin(), lla_origin.y.cos(), -lla_origin.x.cos() * lla_origin.y.sin(),
296+
lla_origin.x.cos(), 0.0, -lla_origin.x.sin());
297+
let actual = ned_rot * ned_vec;
298+
let total = actual + orig;
299+
let ret_vec = ecef2lla(&total, ellipsoid);
300+
ret_vec
301+
}
302+
303+
/// Converts 3-d local cartesian ENU coordinates plus 3-d LLA origin coordinates and an ellipsoid to 3-d LLA coordinates
304+
///
305+
/// # Arguments
306+
///
307+
/// * `lla_origin` - Vector3 reference to the LLA origin vector (lat, long, alt) (radians, radians, meters)
308+
/// * `enu_vec` - Vector3 reference to the local NED cartesian vector (x, y, z)
309+
/// * `ellipsoid` - geo_ellipsoid reference to the ellipsoid
310+
///
311+
/// # Return Value
312+
///
313+
/// * `nalgebra::Vector3<f64>` - (lat, long, alt) (radians, radians, meters)
314+
///
315+
pub fn enu2lla(lla_origin: &Vector3<f64>, enu_vec: &Vector3<f64>, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -> Vector3<f64> {
316+
let orig = lla2ecef(lla_origin, ellipsoid);
317+
let enu_rot = Matrix3::new(-lla_origin.y.sin(), -lla_origin.y.cos() * lla_origin.x.sin(), lla_origin.y.cos() * lla_origin.x.cos(),
318+
lla_origin.y.cos(), -lla_origin.y.sin() * lla_origin.x.sin(), lla_origin.y.sin() * lla_origin.x.cos(),
319+
0.0, lla_origin.x.cos(), lla_origin.x.sin());
320+
let actual = enu_rot * enu_vec;
321+
let total = actual + orig;
322+
let ret_vec = ecef2lla(&total, ellipsoid);
323+
ret_vec
324+
}
325+
280326
//Unit tests
281327
#[cfg(test)]
282328
mod tests {
@@ -317,9 +363,9 @@ mod tests {
317363
let test_x = 4201570.9492264455;
318364
let test_y = 172588.3449531975;
319365
let test_z = 4780835.4317144295;
320-
assert!(ecef_vec.x.approx_eq_ratio(&test_x, 0.0000000001));
321-
assert!(ecef_vec.y.approx_eq_ratio(&test_y, 0.0000000001));
322-
assert!(ecef_vec.z.approx_eq_ratio(&test_z, 0.0000000001));
366+
assert!(ecef_vec.x.approx_eq_ratio(&test_x, 0.0000000000001));
367+
assert!(ecef_vec.y.approx_eq_ratio(&test_y, 0.0000000000001));
368+
assert!(ecef_vec.z.approx_eq_ratio(&test_z, 0.0000000000001));
323369
}
324370
#[test]
325371
fn test_ecef2lla() {
@@ -331,9 +377,9 @@ mod tests {
331377
let test_x = 0.8527087756759584;
332378
let test_y = 0.04105401863784606;
333379
let test_z = 1000.000000000;
334-
assert!(lla_vec.x.approx_eq_ratio(&test_x, 0.0000000001));
335-
assert!(lla_vec.y.approx_eq_ratio(&test_y, 0.0000000001));
336-
assert!(lla_vec.z.approx_eq_ratio(&test_z, 0.0000000001));
380+
assert!(lla_vec.x.approx_eq_ratio(&test_x, 0.0000000000001));
381+
assert!(lla_vec.y.approx_eq_ratio(&test_y, 0.0000000000001));
382+
assert!(lla_vec.z.approx_eq_ratio(&test_z, 0.00001));
337383
}
338384
#[test]
339385
fn test_ll2utm() {
@@ -351,10 +397,10 @@ mod tests {
351397

352398
assert_eq!(utm.get_zone(), test_zone);
353399
assert_eq!(utm.get_hem(), test_hem);
354-
assert!(utm.get_easting().approx_eq_ratio(&test_easting, 0.0000000001));
355-
assert!(utm.get_northing().approx_eq_ratio(&test_northing, 0.0000000001));
356-
assert!(utm.get_convergence().approx_eq_ratio(&test_convergence, 0.0000000001));
357-
assert!(utm.get_scale().approx_eq_ratio(&test_scale, 0.0000000001));
400+
assert!(utm.get_easting().approx_eq_ratio(&test_easting, 0.0000000000001));
401+
assert!(utm.get_northing().approx_eq_ratio(&test_northing, 0.0000000000001));
402+
assert!(utm.get_convergence().approx_eq_ratio(&test_convergence, 0.0000000000001));
403+
assert!(utm.get_scale().approx_eq_ratio(&test_scale, 0.0000000000001));
358404
}
359405
#[test]
360406
fn test_lla2ned() {
@@ -364,9 +410,9 @@ mod tests {
364410
let lla_vec: Vector3<f64> = Vector3::new(0.8527087756759584, 0.042799347889836060477, 1000.000000000);
365411
let ned_vec = lla2ned(&lla_orig_vec, &lla_vec, &ellipsoid);
366412

367-
let test_x = 4.823198223193799;
368-
let test_y = 7339.305041782073;
369-
let test_z = 4.2139798876589225;
413+
let test_x = 4.8231982231937990945;
414+
let test_y = 7339.3050417820732036;
415+
let test_z = 4.2139798876589225073;
370416
assert!(ned_vec.x.approx_eq_ulps(&test_x, 2));
371417
assert!(ned_vec.y.approx_eq_ulps(&test_y, 2));
372418
assert!(ned_vec.z.approx_eq_ulps(&test_z, 2));
@@ -378,12 +424,42 @@ mod tests {
378424
let lla_orig_vec: Vector3<f64> = Vector3::new(0.8527087756759584, 0.04105401863784606, 1000.000000000);
379425
let lla_vec: Vector3<f64> = Vector3::new(0.8527087756759584, 0.042799347889836060477, 1000.000000000);
380426
let enu_vec = lla2enu(&lla_orig_vec, &lla_vec, &ellipsoid);
381-
382-
let test_x = 7339.305041782073;
383-
let test_y = 4.823198223193799;
384-
let test_z = -4.2139798876589225;
385-
assert!(enu_vec.x.approx_eq_ulps(&test_x, 2));
386-
assert!(enu_vec.y.approx_eq_ulps(&test_y, 2));
387-
assert!(enu_vec.z.approx_eq_ulps(&test_z, 2));
427+
428+
let test_x = 7339.3050417820732036;
429+
let test_y = 4.8231982231937990945;
430+
let test_z = -4.2139798876589225073;
431+
assert!(enu_vec.x.approx_eq_ratio(&test_x, 0.0000000000001));
432+
assert!(enu_vec.y.approx_eq_ratio(&test_y, 0.0000000000001));
433+
assert!(enu_vec.z.approx_eq_ratio(&test_z, 0.0000000000001));
434+
}
435+
#[test]
436+
fn test_ned2lla() {
437+
let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS,
438+
geo_ellipsoid::WGS84_FLATTENING);
439+
let lla_orig_vec: Vector3<f64> = Vector3::new(0.8527087756759584, 0.04105401863784606, 1000.000000000);
440+
let ned_vec: Vector3<f64> = Vector3::new(4.8231982231937990945, 7339.3050417820732036, 4.2139798876589225073);
441+
let lla_vec = ned2lla(&lla_orig_vec, &ned_vec, &ellipsoid);
442+
443+
let test_x = 0.8527087756759584;
444+
let test_y = 0.042799347889836060477;
445+
let test_z = 1000.000000000;
446+
assert!(lla_vec.x.approx_eq_ratio(&test_x, 0.0000000000001));
447+
assert!(lla_vec.y.approx_eq_ratio(&test_y, 0.0000000000001));
448+
assert!(lla_vec.z.approx_eq_ratio(&test_z, 0.00001));
449+
}
450+
#[test]
451+
fn test_enu2lla() {
452+
let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS,
453+
geo_ellipsoid::WGS84_FLATTENING);
454+
let lla_orig_vec: Vector3<f64> = Vector3::new(0.8527087756759584, 0.04105401863784606, 1000.000000000);
455+
let enu_vec: Vector3<f64> = Vector3::new(7339.3050417820732036, 4.8231982231937990945, -4.2139798876589225073);
456+
let lla_vec = enu2lla(&lla_orig_vec, &enu_vec, &ellipsoid);
457+
458+
let test_x = 0.8527087756759584;
459+
let test_y = 0.042799347889836060477;
460+
let test_z = 1000.000000000;
461+
assert!(lla_vec.x.approx_eq_ratio(&test_x, 0.0000000000001));
462+
assert!(lla_vec.y.approx_eq_ratio(&test_y, 0.0000000000001));
463+
assert!(lla_vec.z.approx_eq_ratio(&test_z, 0.00001));
388464
}
389465
}

0 commit comments

Comments
 (0)