├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── examples ├── benchmarking.rs ├── geo.rs └── visual.rs ├── logo.png └── src ├── d2.rs ├── d3.rs ├── geo.rs ├── lib.rs └── structs ├── geo_ellipsoid.rs ├── mod.rs └── utm_grid.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | matrix: 7 | allow_failures: 8 | - rust: nightly 9 | before_install: 10 | - sudo apt-get install -y libx11-dev 11 | - sudo apt-get install -y xorg-dev -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "coord_transforms" 3 | version = "1.4.0" 4 | authors = ["David Kramer "] 5 | description = "A Rust crate use for performing coordinate transformations." 6 | repository = "https://github.com/DaveKram/coord_transforms" 7 | readme = "README.md" 8 | keywords = ["coordinates", "geospatial", "transforms", "transformations"] 9 | categories = ["algorithms", "embedded", "science", "parsing"] 10 | license = "MIT" 11 | 12 | [dependencies] 13 | nalgebra = "0.31.0" 14 | float-cmp = "0.9.0" 15 | 16 | [dev-dependencies] 17 | chrono = "0.4.19" 18 | rayon = "1.5.3" 19 | minifb = "0.23.0" 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Synopsis 3 | 4 | 5 | 6 | A Rust crate use for performing coordinate transformations. The crate relies on nalgebra vectors to perform the coordinate transformations. 7 | 8 | [![Build Status](https://travis-ci.org/DaveKram/coord_transforms.svg?branch=master)](https://travis-ci.org/DaveKram/coord_transforms) 9 | 10 |
11 | 12 | ## Code Example 13 | 14 | ``` 15 | let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 16 | geo_ellipsoid::WGS84_FLATTENING); 17 | let lla_vec: Vector3 = Vector3::new(3.0, 4.0, 5.0); 18 | let ecef_vec = lla2ecef(&lla_vec, &ellipsoid); 19 | assert_approx_eq!(ecef_vec.x, 4127585.379918784); 20 | assert_approx_eq!(ecef_vec.y, 4779006.1975849345); 21 | assert_approx_eq!(ecef_vec.z, 894117.5572814466); 22 | ``` 23 | 24 | ## Features 25 | 26 | * Simple, one line function calls to convert from one coordinate system to another 27 | * Common geodetic models provided for geodetic coordinate transformations 28 | * Uses nalgebra (widely used linear algebra crate) 29 | 30 | ## Suggestions for use 31 | 32 | * Use [Rayon!](https://github.com/rayon-rs/rayon "Rayon Github") Rayon allows for easy parallelization - which coordinate transformations are an ideal candidate for. Take a look at examples/benchmarking for an example. 33 | 34 | ## Roadmap / Ideas 35 | 36 | * Asserts/Option return for invalid input data 37 | 38 | ## Examples 39 | 40 | There are very early work in progress examples being built. These contain basic examples, benchmarking, and other types of examples to help aid in use of the crate. To run an example from the /examples directory: 41 | 42 | ``` 43 | cargo run --release --example 44 | ``` 45 | 46 | ## License 47 | 48 | Copyright (c) 2017 David Kramer 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 51 | 52 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 53 | 54 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 55 | -------------------------------------------------------------------------------- /examples/benchmarking.rs: -------------------------------------------------------------------------------- 1 | extern crate coord_transforms; 2 | extern crate chrono; 3 | extern crate rayon; 4 | use coord_transforms::prelude::*; 5 | use chrono::prelude::*; 6 | use rayon::prelude::*; 7 | 8 | fn main() { 9 | let num_iters = 1000000; 10 | test_lla2ecef(num_iters); 11 | test_cartesian2spherical(num_iters); 12 | test_ll2utm(num_iters); 13 | } 14 | 15 | #[allow(unused_variables)] 16 | fn test_cartesian2spherical(num_iters: u32) { 17 | //Test normal loop 18 | let mut d1: DateTime = Local::now(); 19 | for _ in 0..num_iters { 20 | let cart_vec: Vector3 = Vector3::new(3.0, 4.0, 5.0); 21 | d3::cartesian2spherical(&cart_vec); 22 | } 23 | let mut d2: DateTime = Local::now(); 24 | let mut dur = d2.signed_duration_since(d1); 25 | println!("(cartesian2spherical): Basic loop - num_iters: {} took {:?} microseconds", num_iters, dur.num_microseconds().unwrap()); 26 | 27 | //Test rayon loop 28 | d1 = Local::now(); 29 | (0..num_iters).into_par_iter().map(|i| { 30 | let cart_vec: Vector3 = Vector3::new(3.0, 4.0, 5.0); 31 | d3::cartesian2spherical(&cart_vec) 32 | }).count(); 33 | d2 = Local::now(); 34 | dur = d2.signed_duration_since(d1); 35 | println!("(cartesian2spherical): Rayon loop - num_iters: {} took {:?} microseconds", num_iters, dur.num_microseconds().unwrap()); 36 | } 37 | 38 | #[allow(unused_variables)] 39 | fn test_lla2ecef(num_iters: u32) { 40 | //Test normal loop 41 | let mut d1: DateTime = Local::now(); 42 | for _ in 0..num_iters { 43 | let ellipsoid = structs::geo_ellipsoid::geo_ellipsoid::new(structs::geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 44 | structs::geo_ellipsoid::WGS84_FLATTENING); 45 | let lla_vec: Vector3 = Vector3::new(0.8527087756759584, 0.04105401863784606, 1000.000000000); 46 | geo::lla2ecef(&lla_vec, &ellipsoid); 47 | } 48 | let mut d2: DateTime = Local::now(); 49 | let mut dur = d2.signed_duration_since(d1); 50 | println!("(lla2ecef): Basic loop - num_iters: {} took {:?} microseconds", num_iters, dur.num_microseconds().unwrap()); 51 | 52 | //Test rayon loop 53 | d1 = Local::now(); 54 | (0..num_iters).into_par_iter().map(|i| { 55 | let ellipsoid = structs::geo_ellipsoid::geo_ellipsoid::new(structs::geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 56 | structs::geo_ellipsoid::WGS84_FLATTENING); 57 | let lla_vec: Vector3 = Vector3::new(0.8527087756759584, 0.04105401863784606, 1000.000000000); 58 | geo::lla2ecef(&lla_vec, &ellipsoid) 59 | }).count(); 60 | d2 = Local::now(); 61 | dur = d2.signed_duration_since(d1); 62 | println!("(lla2ecef): Rayon loop - num_iters: {} took {:?} microseconds", num_iters, dur.num_microseconds().unwrap()); 63 | } 64 | 65 | #[allow(unused_variables)] 66 | fn test_ll2utm(num_iters: u32) { 67 | //Test normal loop 68 | let mut d1: DateTime = Local::now(); 69 | for _ in 0..num_iters { 70 | let ellipsoid = structs::geo_ellipsoid::geo_ellipsoid::new(structs::geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 71 | structs::geo_ellipsoid::WGS84_FLATTENING); 72 | let ll_vec: Vector2 = Vector2::new(1.3804121468, 0.20555336013); 73 | geo::ll2utm(&ll_vec, &ellipsoid); 74 | } 75 | let mut d2: DateTime = Local::now(); 76 | let mut dur = d2.signed_duration_since(d1); 77 | println!("(ll2utm): Basic loop - num_iters: {} took {:?} microseconds", num_iters, dur.num_microseconds().unwrap()); 78 | 79 | //Test rayon loop 80 | d1 = Local::now(); 81 | (0..num_iters).into_par_iter().map(|i| { 82 | let ellipsoid = structs::geo_ellipsoid::geo_ellipsoid::new(structs::geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 83 | structs::geo_ellipsoid::WGS84_FLATTENING); 84 | let ll_vec: Vector2 = Vector2::new(1.3804121468, 0.20555336013); 85 | geo::ll2utm(&ll_vec, &ellipsoid); 86 | }).count(); 87 | d2 = Local::now(); 88 | dur = d2.signed_duration_since(d1); 89 | println!("(ll2utm): Rayon loop - num_iters: {} took {:?} microseconds", num_iters, dur.num_microseconds().unwrap()); 90 | } -------------------------------------------------------------------------------- /examples/geo.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Note: This example "cascades" from top to bottom 3 | It attempts to use the previous converted version of the LLA vec to show 4 | that there are no errors converting between coordinate systems 5 | */ 6 | 7 | extern crate coord_transforms; 8 | use coord_transforms::prelude::*; 9 | 10 | fn main() { 11 | let lat_deg: f64 = 57.77348022; 12 | let lon_deg: f64 = 157.37338121; 13 | let alt_m: f64 = 1000.0; 14 | 15 | let lla_vec = Vector3::new(lat_deg.to_radians(), lon_deg.to_radians(), alt_m); 16 | println!("-----LLA (Base)-----"); 17 | println!("Lat: {}", lla_vec.x.to_degrees()); 18 | println!("Lon: {}", lla_vec.y.to_degrees()); 19 | println!("Alt: {}", lla_vec.z); 20 | 21 | //Define ellipsoid for the Earth 22 | let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 23 | geo_ellipsoid::WGS84_FLATTENING); 24 | 25 | //Convert to Earth-Centered Earth-Fixed (ECEF) 26 | let ecef_vec = geo::lla2ecef(&lla_vec, &ellipsoid); 27 | println!("-----ECEF-----"); 28 | println!("x: {}", ecef_vec.x); 29 | println!("y: {}", ecef_vec.y); 30 | println!("z: {}", ecef_vec.z); 31 | 32 | //Convert it back to LLA 33 | let lla_again_vec = geo::ecef2lla(&ecef_vec, &ellipsoid); 34 | println!("-----LLA Again-----"); 35 | println!("Lat: {}", lla_again_vec.x.to_degrees()); 36 | println!("Lon: {}", lla_again_vec.y.to_degrees()); 37 | println!("Alt: {}", lla_again_vec.z); 38 | 39 | //Convert it to UTM 40 | let ll_vec = Vector2::new(lla_again_vec.x, lla_again_vec.y); 41 | let utm = geo::ll2utm(&ll_vec, &ellipsoid); 42 | println!("-----UTM-----"); 43 | println!("Zone: {}", utm.get_zone()); 44 | println!("Hemisphere: {:?}", utm.get_hem()); 45 | println!("Easting: {}", utm.get_easting()); 46 | println!("Northing: {}", utm.get_northing()); 47 | println!("Convergence: {}", utm.get_convergence()); 48 | println!("Scale: {}", utm.get_scale()); 49 | 50 | //Convert UTM to LL 51 | let llfromutm_vec = geo::utm2ll(&utm, &ellipsoid); 52 | println!("-----LL from UTM-----"); 53 | println!("Lat: {}", llfromutm_vec.x.to_degrees()); 54 | println!("Lon: {}", llfromutm_vec.y.to_degrees()); 55 | 56 | 57 | //Convert it to NED and back 58 | let lla_orig_vec = Vector3::new((lat_deg + 1.0).to_radians(), (lon_deg + 1.0).to_radians(), alt_m); 59 | let ned_vec = geo::lla2ned(&lla_orig_vec, &lla_again_vec, &ellipsoid); 60 | println!("-----NED-----"); 61 | println!("LLA Origin: {} - {} - {}", lla_orig_vec.x.to_degrees(), lla_orig_vec.y.to_degrees(), lla_orig_vec.z); 62 | println!("N: {}", ned_vec.x); 63 | println!("E: {}", ned_vec.y); 64 | println!("D: {}", ned_vec.z); 65 | 66 | //Simple NED-ENU 67 | let simple_enu = geo::ned2enu(&ned_vec); 68 | println!("-----ENU-----"); 69 | println!("LLA Origin: {} - {} - {}", lla_orig_vec.x.to_degrees(), lla_orig_vec.y.to_degrees(), lla_orig_vec.z); 70 | println!("E: {}", simple_enu.x); 71 | println!("N: {}", simple_enu.y); 72 | println!("U: {}", simple_enu.z); 73 | 74 | //Convert NED back into LLA 75 | let lla_revert_ned = geo::ned2lla(&lla_orig_vec, &ned_vec, &ellipsoid); 76 | println!("-----LLA (from NED)-----"); 77 | println!("LLA Origin: {} - {} - {}", lla_orig_vec.x.to_degrees(), lla_orig_vec.y.to_degrees(), lla_orig_vec.z); 78 | println!("Lat: {}", lla_revert_ned.x.to_degrees()); 79 | println!("Lon: {}", lla_revert_ned.y.to_degrees()); 80 | println!("Alt: {}", lla_revert_ned.z); 81 | 82 | //Convert ENU back into LLA 83 | let lla_revert_enu = geo::enu2lla(&lla_orig_vec, &simple_enu, &ellipsoid); 84 | println!("-----LLA (from ENU)-----"); 85 | println!("LLA Origin: {} - {} - {}", lla_orig_vec.x.to_degrees(), lla_orig_vec.y.to_degrees(), lla_orig_vec.z); 86 | println!("Lat: {}", lla_revert_enu.x.to_degrees()); 87 | println!("Lon: {}", lla_revert_enu.y.to_degrees()); 88 | println!("Alt: {}", lla_revert_enu.z); 89 | 90 | } -------------------------------------------------------------------------------- /examples/visual.rs: -------------------------------------------------------------------------------- 1 | extern crate coord_transforms; 2 | extern crate nalgebra as na; 3 | extern crate minifb; 4 | use coord_transforms::prelude::*; 5 | use minifb::{Key, WindowOptions, Window}; 6 | 7 | const WIDTH: usize = 800; 8 | const HEIGHT: usize = 600; 9 | 10 | fn main() { 11 | let mut buffer: Vec = vec![0; WIDTH * HEIGHT]; 12 | 13 | let mut window = Window::new("Test - Visual - coord_transforms", 14 | WIDTH, 15 | HEIGHT, 16 | WindowOptions::default()).unwrap_or_else(|e| { 17 | panic!("{}", e); 18 | }); 19 | 20 | let mut line_angle_rads = 0.0; 21 | while window.is_open() && !window.is_key_down(Key::Escape) { 22 | //Circle code 23 | let radius: i64 = 200; 24 | let x0: i64 = WIDTH as i64 / 2; 25 | let y0: i64 = HEIGHT as i64 / 2; 26 | let mut x: i64 = radius - 1; 27 | let mut y: i64 = 0; 28 | let mut dx: i64 = 1; 29 | let mut dy: i64 = 1; 30 | let mut err: i64 = dx - (radius << 1); 31 | let colorPix = 0xFFFFFF; 32 | 33 | //Line code 34 | let mut p1x: i64 = WIDTH as i64 / 2; 35 | let mut p1y: i64 = HEIGHT as i64 / 2; 36 | let pol_vec: Vector2 = Vector2::new(radius as f64, line_angle_rads); 37 | let cart_vec = d2::polar2cartesian(&pol_vec); 38 | let mut p2x: i64 = p1x + cart_vec.x as i64; 39 | let mut p2y: i64 = p1y + cart_vec.y as i64; 40 | let mut psteep = false; 41 | if (p2y - p1y).abs() > (p2x - p1x).abs() { 42 | psteep = true; 43 | } 44 | if psteep { 45 | let tempx = p1x; 46 | p1x = p1y; 47 | p1y = tempx; 48 | let tempx2 = p2x; 49 | p2x = p2y; 50 | p2y = tempx2; 51 | } 52 | if p1x > p2x { 53 | let tempx = p1x; 54 | p1x = p2x; 55 | p2x = tempx; 56 | let tempy = p1y; 57 | p1y = p2y; 58 | p2y = tempy; 59 | } 60 | let pdx = p2x - p1x; 61 | let pdy = (p2y - p1y).abs(); 62 | let mut perror = pdx / 2; 63 | let mut pystep = 0; 64 | if p1y < p2y { 65 | pystep = 1; 66 | }else{ 67 | pystep = -1; 68 | } 69 | let mut py0 = p1y; 70 | let pmaxx = p2x; 71 | 72 | //Draw circle 73 | while x >= y { 74 | buffer[xy2lin(x0 + x, y0 + y) as usize] = colorPix; 75 | buffer[xy2lin(x0 + y, y0 + x) as usize] = colorPix; 76 | buffer[xy2lin(x0 - y, y0 + x) as usize] = colorPix; 77 | buffer[xy2lin(x0 - x, y0 + y) as usize] = colorPix; 78 | buffer[xy2lin(x0 - x, y0 - y) as usize] = colorPix; 79 | buffer[xy2lin(x0 - y, y0 - x) as usize] = colorPix; 80 | buffer[xy2lin(x0 + y, y0 - x) as usize] = colorPix; 81 | buffer[xy2lin(x0 + x, y0 - y) as usize] = colorPix; 82 | if err <= 0 { 83 | y += 1; 84 | err += dy; 85 | dy += 2; 86 | } 87 | if err > 0 { 88 | x -= 1; 89 | dx += 2; 90 | err += (-radius << 1) + dx; 91 | } 92 | } 93 | 94 | //Draw line 95 | for x in p1x..pmaxx { 96 | if psteep { 97 | buffer[xy2lin(py0, x) as usize] = colorPix; 98 | }else{ 99 | buffer[xy2lin(x, py0) as usize] = colorPix; 100 | } 101 | perror -= pdy; 102 | if perror < 0 { 103 | py0 += pystep; 104 | perror += pdx; 105 | } 106 | } 107 | 108 | window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap(); 109 | 110 | //Wrap radians 111 | if line_angle_rads >= 2.0 * 3.14 { 112 | line_angle_rads = 0.0; 113 | }else{ 114 | line_angle_rads += 0.001; 115 | } 116 | 117 | //Clear 118 | for x in 0..WIDTH * HEIGHT { 119 | buffer[x] = 0x0; 120 | } 121 | } 122 | } 123 | 124 | fn xy2lin(x: i64, y: i64) -> i64 { 125 | WIDTH as i64 * y + x 126 | } 127 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveKram/coord_transforms/327c598472ea89b32c7c0f92815f36d012dd2d8e/logo.png -------------------------------------------------------------------------------- /src/d2.rs: -------------------------------------------------------------------------------- 1 | use ::std; 2 | use na::Vector2; 3 | 4 | /// Converts 2-d polar coordinates to 2-d cartesian coordinates 5 | /// 6 | /// # Arguments 7 | /// 8 | /// * `pol_vec` - Vector2 reference to the polar vector (rho, theta) - theta in radians 9 | /// 10 | /// # Return Value 11 | /// 12 | /// * `nalgebra::Vector2` - x, y 13 | /// 14 | /// # Formula 15 | /// 16 | /// * x = rho * cos(theta) 17 | /// * y = rho * sin(theta) 18 | pub fn polar2cartesian(pol_vec: &Vector2) -> Vector2 { 19 | let mut ret_vec: Vector2 = Vector2::new(0.0, 0.0); 20 | ret_vec.x = pol_vec.x * pol_vec.y.cos(); 21 | ret_vec.y = pol_vec.x * pol_vec.y.sin(); 22 | ret_vec 23 | } 24 | 25 | /// Converts 2-d log polar coordinates to 2-d cartesian coordinates 26 | /// 27 | /// # Arguments 28 | /// 29 | /// * `logpol_vec` - Vector2 reference to the log polar vector (rho, theta) in radians 30 | /// 31 | /// # Return Value 32 | /// 33 | /// * `nalgebra::Vector2` - x, y 34 | /// 35 | /// # Formula 36 | /// 37 | /// * x = e^rho * cos(theta) 38 | /// * y = e^rho * sin(theta) 39 | pub fn logpolar2cartesian(logpol_vec: &Vector2) -> Vector2 { 40 | let mut ret_vec: Vector2 = Vector2::new(0.0, 0.0); 41 | ret_vec.x = std::f64::consts::E.powf(logpol_vec.x) * logpol_vec.y.cos(); 42 | ret_vec.y = std::f64::consts::E.powf(logpol_vec.x) * logpol_vec.y.sin(); 43 | ret_vec 44 | } 45 | 46 | /// Converts 2-d bipolar coordinates to 2-d cartesian coordinates 47 | /// 48 | /// # Arguments 49 | /// 50 | /// * `bipol_vec` - Vector2 reference to the bipolar vector (sigma, tau) in radians 51 | /// * `a` - f64 value for foci points (-a, 0) and (a, 0) 52 | /// 53 | /// # Return Value 54 | /// 55 | /// * `nalgebra::Vector2` - x, y 56 | /// 57 | /// # Formula 58 | /// 59 | /// * x = a * ((sinh(tau)) / (cosh(tau) - cos(sigma))) 60 | /// * y = a * ((sin(sigma)) / (cosh(tau) - cos(sigma))) 61 | pub fn bipolar2cartesian(bipol_vec: &Vector2, a: f64) -> Vector2 { 62 | let mut ret_vec: Vector2 = Vector2::new(0.0, 0.0); 63 | ret_vec.x = a * ((bipol_vec.y.sinh()) / (bipol_vec.y.cosh() - bipol_vec.x.cos())); 64 | ret_vec.y = a * ((bipol_vec.x.sin()) / (bipol_vec.y.cosh() - bipol_vec.x.cos())); 65 | ret_vec 66 | } 67 | 68 | 69 | /// Converts 2-d cartesian coordinates to 2-d polar coordinates 70 | /// 71 | /// # Arguments 72 | /// 73 | /// * `cart_vec` - Vector2 reference to the cartesian vector (x, y) 74 | /// 75 | /// # Return Value 76 | /// 77 | /// * `nalgebra::Vector2` - rho, theta (in radians) 78 | /// 79 | /// # Formula 80 | /// 81 | /// * r = sqrt( x^2 + y^2 ) 82 | /// * theta = arctan(y / x) 83 | pub fn cartesian2polar(cart_vec: &Vector2) -> Vector2 { 84 | let mut ret_vec: Vector2 = Vector2::new(0.0, 0.0); 85 | ret_vec.x = (cart_vec.x.powi(2) + cart_vec.y.powi(2)).sqrt(); 86 | ret_vec.y = cart_vec.y.atan2(cart_vec.x); 87 | ret_vec 88 | } 89 | 90 | /// Converts 2-d cartesian coordinates to 2-d log polar coordinates 91 | /// 92 | /// # Arguments 93 | /// 94 | /// * `cart_vec` - Vector2 reference to the cartesian vector (x, y) 95 | /// 96 | /// # Return Value 97 | /// 98 | /// * `nalgebra::Vector2` - rho, theta (in radians) 99 | /// 100 | /// # Formula 101 | /// 102 | /// * r = log(sqrt( x^2 + y^2 )) 103 | /// * theta = arctan(y / x) 104 | pub fn cartesian2logpolar(cart_vec: &Vector2) -> Vector2 { 105 | let mut ret_vec: Vector2 = Vector2::new(0.0, 0.0); 106 | ret_vec.x = ((cart_vec.x.powi(2) + cart_vec.y.powi(2)).sqrt()).ln(); 107 | ret_vec.y = cart_vec.y.atan2(cart_vec.x); 108 | ret_vec 109 | } 110 | 111 | //Unit tests 112 | #[cfg(test)] 113 | mod tests { 114 | use super::*; 115 | use float_cmp::ApproxEqUlps; 116 | #[test] 117 | fn test_polar2cartesian() { 118 | let pol_vec: Vector2 = Vector2::new(3.0, 4.0); 119 | let cart_vec = polar2cartesian(&pol_vec); 120 | 121 | let test_x = -1.960930862590836; 122 | let test_y = -2.2704074859237844; 123 | assert!(cart_vec.x.approx_eq_ulps(&test_x, 2)); 124 | assert!(cart_vec.y.approx_eq_ulps(&test_y, 2)); 125 | } 126 | #[test] 127 | fn test_logpolar2cartesian() { 128 | let logpol_vec: Vector2 = Vector2::new(3.0, 4.0); 129 | let cart_vec = logpolar2cartesian(&logpol_vec); 130 | 131 | let test_x = -13.128783081462156; 132 | let test_y = -15.20078446306795; 133 | assert!(cart_vec.x.approx_eq_ulps(&test_x, 2)); 134 | assert!(cart_vec.y.approx_eq_ulps(&test_y, 2)); 135 | } 136 | #[test] 137 | fn test_bipolar2cartesian() { 138 | let bipol_vec: Vector2 = Vector2::new(3.0, 4.0); 139 | let cart_vec = bipolar2cartesian(&bipol_vec, 1.0); 140 | 141 | let test_x = 0.9643685028429331; 142 | let test_y = 0.004986885446035738; 143 | assert!(cart_vec.x.approx_eq_ulps(&test_x, 2)); 144 | assert!(cart_vec.y.approx_eq_ulps(&test_y, 2)); 145 | } 146 | #[test] 147 | fn test_cartesian2polar() { 148 | let cart_vec: Vector2 = Vector2::new(3.0, 4.0); 149 | let polar_vec = cartesian2polar(&cart_vec); 150 | 151 | let test_x = 5.0; 152 | let test_y = 0.9272952180016122; 153 | assert!(polar_vec.x.approx_eq_ulps(&test_x, 2)); 154 | assert!(polar_vec.y.approx_eq_ulps(&test_y, 2)); 155 | } 156 | #[test] 157 | fn test_cartesian2logpolar() { 158 | let cart_vec: Vector2 = Vector2::new(3.0, 4.0); 159 | let logpolar_vec = cartesian2logpolar(&cart_vec); 160 | 161 | let test_x = 1.6094379124341003; 162 | let test_y = 0.9272952180016122; 163 | assert!(logpolar_vec.x.approx_eq_ulps(&test_x, 2)); 164 | assert!(logpolar_vec.y.approx_eq_ulps(&test_y, 2)); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/d3.rs: -------------------------------------------------------------------------------- 1 | use na::Vector3; 2 | 3 | /// Converts 3-d spherical coordinates to 3-d cartesian coordinates 4 | /// 5 | /// # Arguments 6 | /// 7 | /// * `sphere_vec` - Vector3 reference to the spherical vector (rho, theta, phi) (r, el, az) in radians 8 | /// 9 | /// # Return Value 10 | /// 11 | /// * `nalgebra::Vector3` - x, y, z 12 | /// 13 | /// # Formula 14 | /// 15 | /// * x = rho * sin(theta) * cos(phi) 16 | /// * y = rho * sin(theta) * sin(phi) 17 | /// * z = rho * cos(theta) 18 | pub fn spherical2cartesian(sphere_vec: &Vector3) -> Vector3 { 19 | let mut ret_vec: Vector3 = Vector3::new(0.0, 0.0, 0.0); 20 | ret_vec.x = sphere_vec.x * sphere_vec.y.sin() * sphere_vec.z.cos(); 21 | ret_vec.y = sphere_vec.x * sphere_vec.y.sin() * sphere_vec.z.sin(); 22 | ret_vec.z = sphere_vec.x * sphere_vec.y.cos(); 23 | ret_vec 24 | } 25 | 26 | /// Converts 3-d cylindrical coordinates to 3-d cartesian coordinates 27 | /// 28 | /// # Arguments 29 | /// 30 | /// * `cyl_vec` - Vector3 reference to the cylindrical vector (rho, theta, z) in radians 31 | /// 32 | /// # Return Value 33 | /// 34 | /// * `nalgebra::Vector3` - x, y, z 35 | /// 36 | /// # Formula 37 | /// 38 | /// * x = rho * cos(theta) 39 | /// * y = rho * sin(theta) 40 | /// * z = z 41 | pub fn cylindrical2cartesian(cyl_vec: &Vector3) -> Vector3 { 42 | let mut ret_vec: Vector3 = Vector3::new(0.0, 0.0, 0.0); 43 | ret_vec.x = cyl_vec.x * cyl_vec.y.cos(); 44 | ret_vec.y = cyl_vec.x * cyl_vec.y.sin(); 45 | ret_vec.z = cyl_vec.z; 46 | ret_vec 47 | } 48 | 49 | /// Converts 3-d cartesian coordinates to 3-d spherical coordinates 50 | /// 51 | /// # Arguments 52 | /// 53 | /// * `cart_vec` - Vector3 reference to the cartesian vector (x, y, z) 54 | /// 55 | /// # Return Value 56 | /// 57 | /// * `nalgebra::Vector3` - rho, theta, phi (in radians) 58 | /// 59 | /// # Formula 60 | /// 61 | /// * rho = sqrt( x^2 + y^2 + z^2 ) 62 | /// * theta = arctan((sqrt( x2 + y^2 )) / (z)) 63 | /// * phi = arctan(y / x) 64 | pub fn cartesian2spherical(cart_vec: &Vector3) -> Vector3 { 65 | let mut ret_vec: Vector3 = Vector3::new(0.0, 0.0, 0.0); 66 | ret_vec.x = (cart_vec.x.powi(2) + cart_vec.y.powi(2) + cart_vec.z.powi(2)).sqrt(); 67 | ret_vec.y = ((cart_vec.x.powi(2) + cart_vec.y.powi(2)).sqrt()).atan2(cart_vec.z); 68 | ret_vec.z = cart_vec.y.atan2(cart_vec.x); 69 | ret_vec 70 | } 71 | 72 | /// Converts 3-d cartesian coordinates to 3-d cylindrical coordinates 73 | /// 74 | /// # Arguments 75 | /// 76 | /// * `cart_vec` - Vector3 reference to the cartesian vector (x, y, z) 77 | /// 78 | /// # Return Value 79 | /// 80 | /// * `nalgebra::Vector3` - rho, theta, z (in radians) 81 | /// 82 | /// # Formula 83 | /// 84 | /// * rho = sqrt( x^2 + y^2 ) 85 | /// * theta = arctan(y / x) 86 | /// * z = z 87 | pub fn cartesian2cylindrical(cart_vec: &Vector3) -> Vector3 { 88 | let mut ret_vec: Vector3 = Vector3::new(0.0, 0.0, 0.0); 89 | ret_vec.x = (cart_vec.x.powi(2) + cart_vec.y.powi(2)).sqrt(); 90 | ret_vec.y = cart_vec.y.atan2(cart_vec.x); 91 | ret_vec.z = cart_vec.z; 92 | ret_vec 93 | } 94 | 95 | //Unit tests 96 | #[cfg(test)] 97 | mod tests { 98 | use super::*; 99 | use float_cmp::ApproxEqUlps; 100 | #[test] 101 | fn test_spherical2cartesian() { 102 | let sphere_vec: Vector3 = Vector3::new(3.0, 4.0, 5.0); 103 | let cart_vec = spherical2cartesian(&sphere_vec); 104 | 105 | let test_x = -0.6440287493492097; 106 | let test_y = 2.177148851629225; 107 | let test_z = -1.960930862590836; 108 | assert!(cart_vec.x.approx_eq_ulps(&test_x, 2)); 109 | assert!(cart_vec.y.approx_eq_ulps(&test_y, 2)); 110 | assert!(cart_vec.z.approx_eq_ulps(&test_z, 2)); 111 | } 112 | #[test] 113 | fn test_cylindrical2cartesian() { 114 | let cyl_vec: Vector3 = Vector3::new(3.0, 4.0, 5.0); 115 | let cart_vec = cylindrical2cartesian(&cyl_vec); 116 | 117 | let test_x = -1.960930862590836; 118 | let test_y = -2.2704074859237844; 119 | let test_z = 5.0; 120 | assert!(cart_vec.x.approx_eq_ulps(&test_x, 2)); 121 | assert!(cart_vec.y.approx_eq_ulps(&test_y, 2)); 122 | assert!(cart_vec.z.approx_eq_ulps(&test_z, 2)); 123 | } 124 | #[test] 125 | fn test_cartesian2spherical() { 126 | let cart_vec: Vector3 = Vector3::new(3.0, 4.0, 5.0); 127 | let sphere_vec = cartesian2spherical(&cart_vec); 128 | 129 | let test_x = 7.0710678118654755; 130 | let test_y = 0.7853981633974483; 131 | let test_z = 0.9272952180016122; 132 | assert!(sphere_vec.x.approx_eq_ulps(&test_x, 2)); 133 | assert!(sphere_vec.y.approx_eq_ulps(&test_y, 2)); 134 | assert!(sphere_vec.z.approx_eq_ulps(&test_z, 2)); 135 | } 136 | #[test] 137 | fn test_cartesian2cylindrical() { 138 | let cart_vec: Vector3 = Vector3::new(3.0, 4.0, 5.0); 139 | let cyl_vec = cartesian2cylindrical(&cart_vec); 140 | 141 | let test_x = 5.0; 142 | let test_y = 0.9272952180016122; 143 | let test_z = 5.0; 144 | assert!(cyl_vec.x.approx_eq_ulps(&test_x, 2)); 145 | assert!(cyl_vec.y.approx_eq_ulps(&test_y, 2)); 146 | assert!(cyl_vec.z.approx_eq_ulps(&test_z, 2)); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/geo.rs: -------------------------------------------------------------------------------- 1 | use na::Vector3; 2 | use na::Vector2; 3 | use na::Matrix3; 4 | use structs::geo_ellipsoid; 5 | use structs::utm_grid; 6 | 7 | /// Converts 3-d ENU coordinates to 3-d NED coordinates 8 | /// 9 | /// # Arguments 10 | /// 11 | /// * `enu_vec` - Vector3 reference to the ENU vector (x, y, z) 12 | /// 13 | /// # Return Value 14 | /// 15 | /// * `nalgebra::Vector3` - x, y, z 16 | /// 17 | /// # Formula 18 | /// 19 | /// * x = y 20 | /// * y = x 21 | /// * z = -z 22 | pub fn enu2ned(enu_vec: &Vector3) -> Vector3 { 23 | let mut ret_vec: Vector3 = Vector3::new(0.0, 0.0, 0.0); 24 | ret_vec.x = enu_vec.y; 25 | ret_vec.y = enu_vec.x; 26 | ret_vec.z = -enu_vec.z; 27 | ret_vec 28 | } 29 | 30 | /// Converts 3-d NED coordinates to 3-d ENU coordinates 31 | /// 32 | /// # Arguments 33 | /// 34 | /// * `ned_vec` - Vector3 reference to the NED vector (x, y, z) 35 | /// 36 | /// # Return Value 37 | /// 38 | /// * `nalgebra::Vector3` - x, y, z 39 | /// 40 | /// # Formula 41 | /// 42 | /// * x = y 43 | /// * y = x 44 | /// * z = -z 45 | pub fn ned2enu(ned_vec: &Vector3) -> Vector3 { 46 | let mut ret_vec: Vector3 = Vector3::new(0.0, 0.0, 0.0); 47 | ret_vec.x = ned_vec.y; 48 | ret_vec.y = ned_vec.x; 49 | ret_vec.z = -ned_vec.z; 50 | ret_vec 51 | } 52 | 53 | 54 | /// Converts 3-d LLA coordinates to 3-d ECEF coordinates 55 | /// 56 | /// # Arguments 57 | /// 58 | /// * `lla_vec` - Vector3 reference to the LLA vector (latitude, longitude, altitude) (radians, radians, meters) 59 | /// * `ellipsoid` - geo_ellipsoid reference to the ellipsoid 60 | /// 61 | /// # Return Value 62 | /// 63 | /// * `nalgebra::Vector3` - x, y, z 64 | /// 65 | /// # Formula 66 | /// 67 | /// * x = (N + h) * cos(lat) * cos(lon) 68 | /// * y = (N + h) * cos(lat) * sin(lon) 69 | /// * z = (( b^2 / a^2 ) * N + h) * sin(lat) 70 | pub fn lla2ecef(lla_vec: &Vector3, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -> Vector3 { 71 | let mut ret_vec: Vector3 = Vector3::new(0.0, 0.0, 0.0); 72 | let n = ellipsoid.get_semi_major_axis() / (1.0 - ellipsoid.get_first_ecc().powi(2) * lla_vec.x.sin().powi(2)).sqrt(); 73 | ret_vec.x = (n + lla_vec.z) * lla_vec.x.cos() * lla_vec.y.cos(); 74 | ret_vec.y = (n + lla_vec.z) * lla_vec.x.cos() * lla_vec.y.sin(); 75 | ret_vec.z = ((ellipsoid.get_semi_minor_axis().powi(2) / ellipsoid.get_semi_major_axis().powi(2)) * n + lla_vec.z) * lla_vec.x.sin(); 76 | ret_vec 77 | } 78 | 79 | 80 | /// Converts 3-d ECEF coordinates to 3-d LLA coordinates 81 | /// 82 | /// # Arguments 83 | /// 84 | /// * `ecef_vec` - Vector3 reference to the ECEF vector (x, y, z) 85 | /// * `ellipsoid` - geo_ellipsoid reference to the ellipsoid 86 | /// 87 | /// # Return Value 88 | /// 89 | /// * `nalgebra::Vector3` - lat, long, alt (radians, radians, meters) 90 | /// 91 | /// # Formula 92 | /// 93 | /// * x = arctan((z + e'^2 * b * sin^3 (theta)) / (p - e^2 * a * cos^3 (theta))) 94 | /// * y = arctan(y / x) 95 | /// * z = (p / cos(lat)) - N 96 | pub fn ecef2lla(ecef_vec: &Vector3, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -> Vector3 { 97 | let mut ret_vec: Vector3 = Vector3::new(0.0, 0.0, 0.0); 98 | let p = (ecef_vec.x.powi(2) + ecef_vec.y.powi(2)).sqrt(); 99 | let theta = (ecef_vec.z * ellipsoid.get_semi_major_axis()).atan2(p * ellipsoid.get_semi_minor_axis()); 100 | let x_top = ecef_vec.z + ellipsoid.get_second_ecc().powi(2) * ellipsoid.get_semi_minor_axis() * theta.sin().powi(3); 101 | let x_bot = p - ellipsoid.get_first_ecc().powi(2) * ellipsoid.get_semi_major_axis() * theta.cos().powi(3); 102 | ret_vec.x = x_top.atan2(x_bot); 103 | ret_vec.y = ecef_vec.y.atan2(ecef_vec.x); 104 | let n = ellipsoid.get_semi_major_axis() / (1.0 - ellipsoid.get_first_ecc().powi(2) * (ret_vec.x.sin() * ret_vec.x.sin())).sqrt(); 105 | ret_vec.z = (p / ret_vec.x.cos()) - n; 106 | ret_vec 107 | } 108 | 109 | /// Converts 2-d LL coordinates to UTM Grid (using Karney method) - accruacy to within a few nanometers within 3900km of the central meridian 110 | /// 111 | /// # Arguments 112 | /// 113 | /// * `ll_vec` - Vector2 reference to the LL vector (latitude, longitude) (radians, radians) 114 | /// * `ellipsoid` - geo_ellipsoid reference to the ellipsoid 115 | /// 116 | /// # Return Value 117 | /// 118 | /// * `utm_grid::utm_grid` - UTM Grid the latitude and longitude belong in 119 | /// 120 | /// # Notes 121 | /// 122 | /// * Based on code from here: http://www.movable-type.co.uk/scripts/latlong-utm-mgrs.html 123 | /// * Based on white paper from here: https://arxiv.org/abs/1002.1417 124 | /// * (c) Chris Veness 2014-2017 MIT Licence 125 | pub fn ll2utm(ll_vec: &Vector2, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -> utm_grid::utm_grid { 126 | let mut ret_utm = utm_grid::utm_grid::new(0, utm_grid::hemisphere::NORTH, 0.0, 0.0, 0.0, 0.0); 127 | let mut zone = ((ll_vec.y.to_degrees() + 180.0) / 6.0).floor() + 1.0; 128 | let mut lon_cent_mer = ((zone - 1.0) * 6.0 - 180.0 + 3.0).to_radians(); 129 | 130 | //Handle Norway/Svalbard exceptions 131 | let mgrs_lat_bands = ['C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'X']; 132 | let lat_band = mgrs_lat_bands[(ll_vec.x.to_degrees() / 8.0 + 10.0).floor() as usize]; 133 | let loncentmer_shift_rads = 0.10472; 134 | if zone == 31.0 && lat_band == 'V' && ll_vec.y >= 0.0523599 { 135 | zone += 1.0; lon_cent_mer += loncentmer_shift_rads; 136 | } 137 | if zone == 32.0 && lat_band == 'X' && ll_vec.y < 0.15708 { 138 | zone -= 1.0; lon_cent_mer -= loncentmer_shift_rads; 139 | } 140 | if zone == 32.0 && lat_band == 'X' && ll_vec.y >= 0.15708 { 141 | zone += 1.0; lon_cent_mer += loncentmer_shift_rads; 142 | } 143 | if zone == 34.0 && lat_band == 'X' && ll_vec.y < 0.366519 { 144 | zone -= 1.0; lon_cent_mer -= loncentmer_shift_rads; 145 | } 146 | if zone == 34.0 && lat_band == 'X' && ll_vec.y >= 0.366519 { 147 | zone += 1.0; lon_cent_mer += loncentmer_shift_rads; 148 | } 149 | if zone == 36.0 && lat_band == 'X' && ll_vec.y < 0.575959 { 150 | zone -= 1.0; lon_cent_mer -= loncentmer_shift_rads; 151 | } 152 | if zone == 36.0 && lat_band == 'X' && ll_vec.y >= 0.575959 { 153 | zone += 1.0; lon_cent_mer += loncentmer_shift_rads; 154 | } 155 | 156 | //Determine easting/northing 157 | let lambda = ll_vec.y - lon_cent_mer; 158 | let c_lambda = lambda.cos(); 159 | let s_lambda = lambda.sin(); 160 | let t_lambda = lambda.tan(); 161 | let e = (ellipsoid.get_flattening() * (2.0 - ellipsoid.get_flattening())).sqrt(); 162 | let n = ellipsoid.get_flattening() / (2.0 - ellipsoid.get_flattening()); 163 | let n2 = n * n; 164 | let n3 = n * n * n; 165 | let n4 = n * n * n * n; 166 | let n5 = n * n * n * n * n; 167 | let n6 = n * n * n * n * n * n; 168 | let sigma = (e * ((e * ll_vec.x.tan()) / (1.0 + ll_vec.x.tan().powi(2)).sqrt()).atanh()).sinh(); 169 | let taup = ll_vec.x.tan() * (1.0 + sigma.powi(2)).sqrt() - sigma * (1.0 + ll_vec.x.tan().powi(2)).sqrt(); 170 | let xip = taup.atan2(c_lambda); 171 | let etap = (s_lambda / (taup.powi(2) + c_lambda.powi(2)).sqrt()).asinh(); 172 | let a_maj = ellipsoid.get_semi_major_axis() / (1.0 + n) * (1.0 + (1.0 / 4.0) * n2 + (1.0 / 64.0) * n4 + (1.0 / 256.0) * n6); 173 | 174 | let alpha = 175 | [0.0, 176 | 1.0 / 2.0 * n - 2.0 / 3.0 * n2 + 5.0 / 16.0 * n3 + 41.0 / 180.0 * n4 - 127.0 / 288.0 * n5 + 7891.0 / 37800.0 * n6, 177 | 13.0 / 48.0 * n2 - 3.0 / 5.0 * n3 + 557.0 / 1440.0 * n4 + 281.0 / 630.0 * n5 - 1983433.0 / 1935360.0 * n6, 178 | 61.0 / 240.0 * n3 - 103.0 / 140.0 * n4 + 15061.0 / 26880.0 * n5 + 167603.0 / 181440.0 * n6, 179 | 4956.01 / 161280.0 * n4 - 179.0 / 168.0 * n5 + 6601661.0 / 7257600.0 * n6, 180 | 4729.0 / 80640.0 * n5 - 3418889.0 / 1995840.0 * n6, 181 | 212378941.0 / 319334400.0 * n6]; 182 | 183 | let mut xi = xip; 184 | for j in 1..7 { 185 | xi += alpha[j] * (2.0 * (j as f64) * xip).sin() * (2.0 * (j as f64) * etap).cosh(); 186 | } 187 | let mut eta = etap; 188 | for j in 1..7 { 189 | eta += alpha[j] * (2.0 * (j as f64) * xip).cos() * (2.0 * (j as f64) * etap).sinh(); 190 | } 191 | let mut easting = utm_grid::SCALE_FACTOR_CENTERAL_MERIDIAN * a_maj * eta; 192 | let mut northing = utm_grid::SCALE_FACTOR_CENTERAL_MERIDIAN * a_maj * xi; 193 | 194 | //Determine convergence 195 | let mut pp = 1.0; 196 | for j in 1..7 { 197 | pp += (2.0 * (j as f64) * alpha[j]) * (2.0 * (j as f64) * xip).cos() * (2.0 * (j as f64) * etap).cosh(); 198 | } 199 | let mut qp = 0.0; 200 | for j in 1..7 { 201 | qp += (2.0 * (j as f64) * alpha[j]) * (2.0 * (j as f64) * xip).sin() * (2.0 * (j as f64) * etap).sinh(); 202 | } 203 | 204 | let yp = (taup / (1.0 + taup.powi(2)).sqrt() * t_lambda).atan(); 205 | let ypp = qp.atan2(pp); 206 | let mut y = yp + ypp; 207 | 208 | //Determine scale 209 | let sphi = ll_vec.x.sin(); 210 | let kp = (1.0 - e.powi(2) * sphi.powi(2)).sqrt() * (1.0 + ll_vec.x.tan().powi(2)).sqrt() / (taup.powi(2) + c_lambda.powi(2)).sqrt(); 211 | let kpp = a_maj / ellipsoid.get_semi_major_axis() * (pp.powi(2) + qp.powi(2)).sqrt(); 212 | let mut k = utm_grid::SCALE_FACTOR_CENTERAL_MERIDIAN * kp * kpp; 213 | 214 | //Shift northing and easting to false origins 215 | easting += utm_grid::FALSE_EASTING; 216 | if northing < 0.0 { 217 | northing += utm_grid::FALSE_NORTHING; 218 | } 219 | 220 | //Round to correct precision - easting and northing are nanometer precision 221 | easting = (easting * 1000000.0).round() / 1000000.0; 222 | northing = (northing * 1000000.0).round() / 1000000.0; 223 | y = (y * 1000000000.0).round() / 1000000000.0; 224 | k = (k * 1000000000000.0).round() / 1000000000000.0; 225 | 226 | //Set values in structure 227 | ret_utm.set_zone(zone as u32); 228 | ret_utm.set_easting(easting); 229 | ret_utm.set_northing(northing); 230 | ret_utm.set_convergence(y); 231 | ret_utm.set_scale(k); 232 | if ll_vec.x >= 0.0 { 233 | ret_utm.set_hem(utm_grid::hemisphere::NORTH); 234 | }else{ 235 | ret_utm.set_hem(utm_grid::hemisphere::SOUTH); 236 | } 237 | 238 | ret_utm 239 | } 240 | 241 | /// Converts UTM Grid coordinates to 2-d LL (using Karney method) - accruacy to within a few nanometers within 3900km of the central meridian 242 | /// 243 | /// # Arguments 244 | /// 245 | /// * `utm` - utm_grid reference to the UTM grid 246 | /// * `ellipsoid` - geo_ellipsoid reference to the ellipsoid 247 | /// 248 | /// # Return Value 249 | /// 250 | /// * `nalgebra::Vector2` - Lat, Lon (radians, radians) 251 | /// 252 | /// # Notes 253 | /// 254 | /// * Based on code from here: http://www.movable-type.co.uk/scripts/latlong-utm-mgrs.html 255 | /// * Based on white paper from here: https://arxiv.org/abs/1002.1417 256 | /// * (c) Chris Veness 2014-2017 MIT Licence 257 | pub fn utm2ll(utm: &utm_grid::utm_grid, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -> Vector2 { 258 | let mut ret_val: Vector2 = Vector2::new(0.0, 0.0); 259 | let z = utm.get_zone(); 260 | let h = utm.get_hem(); 261 | let mut x = utm.get_easting(); 262 | let mut y = utm.get_northing(); 263 | 264 | let a = ellipsoid.get_semi_major_axis(); 265 | let f = ellipsoid.get_flattening(); 266 | let e = ellipsoid.get_first_ecc(); 267 | 268 | //Make x relative to central meridian 269 | x = x - utm_grid::FALSE_EASTING; 270 | 271 | //Make y relative to equator 272 | if h == utm_grid::hemisphere::SOUTH { 273 | y = y - utm_grid::FALSE_NORTHING; 274 | } 275 | 276 | let n = f / (2.0 - f); 277 | let n2 = n * n; 278 | let n3 = n * n2; 279 | let n4 = n * n3; 280 | let n5 = n * n4; 281 | let n6 = n * n5; 282 | 283 | let A = a / (1.0 + n) * (1.0 + 1.0/4.0 * n2 + 1.0 / 64.0 * n4 + 1.0 / 256.0 * n6); 284 | let eta = x / (utm_grid::SCALE_FACTOR_CENTERAL_MERIDIAN * A); 285 | let xi = y / (utm_grid::SCALE_FACTOR_CENTERAL_MERIDIAN * A); 286 | 287 | let beta = 288 | [0.0, 289 | 1.0 / 2.0 * n - 2.0 / 3.0 * n2 + 37.0 / 96.0 * n3 - 1.0 / 360.0 * n4 - 81.0 / 512.0 * n5 + 96199.0 / 604800.0 * n6, 290 | 1.0 / 48.0 * n2 + 1.0 / 15.0 * n3 - 437.0 / 440.0 * n4 + 46.0 / 105.0 * n5 - 1118711.0 / 3870720.0 * n6, 291 | 17.0 / 480.0 * n3 - 37.0 / 840.0 * n4 - 209.0 / 4480.0 * n5 + 5569.0 / 90720.0 * n6, 292 | 4397.0 / 161280.0 * n4 - 11.0 / 504.0 * n5 - 830251.0 / 7257600.0 * n6, 293 | 4583.0 / 161280.0 * n5 - 108847.0 / 3991680.0 * n6, 294 | 20648693.0 / 638668800.0 * n6]; 295 | 296 | let mut xip = xi; 297 | for j in 1..7 { 298 | xip -= beta[j] * (2.0 * (j as f64) * xi).sin() * (2.0 * (j as f64) * eta).cosh(); 299 | } 300 | 301 | let mut etap = eta; 302 | for j in 1..7 { 303 | etap -= beta[j] * (2.0 * (j as f64) * xi).cos() * (2.0 * (j as f64) * eta).sinh(); 304 | } 305 | 306 | let sinetap = etap.sinh(); 307 | let sinxip = xip.sin(); 308 | let cosxip = xip.cos(); 309 | 310 | let taup = sinxip / (sinetap * sinetap + cosxip * cosxip).sqrt(); 311 | let mut taui = taup; 312 | while { 313 | let sigmai = (e * (e * taui / (1.0 + taui * taui).sqrt()).atanh()).sinh(); 314 | let tauip = taui * (1.0 + sigmai * sigmai).sqrt() - sigmai * (1.0 + taui * taui).sqrt(); 315 | let delataui = (taup - tauip) / (1.0 + tauip * tauip).sqrt() * (1.0 + (1.0 - e * e) * taui * taui) / ((1.0 - e * e) * (1.0 + taui * taui).sqrt()); 316 | taui += delataui; 317 | delataui.abs() > 1e-12 318 | } {} 319 | 320 | let phi = taui.atan(); 321 | let mut lambda = sinetap.atan2(cosxip); 322 | 323 | let lambda0 = (((z as i32 - 1) * 6 - 180 + 3) as f64).to_radians(); 324 | lambda += lambda0; 325 | 326 | ret_val.x = phi; 327 | ret_val.y = lambda; 328 | 329 | ret_val 330 | } 331 | 332 | /// Converts 3-d LLA origin coordinates plus 3-d LLA coordinates and an ellipsoid to 3-d local NED cartesian coordinates 333 | /// 334 | /// # Arguments 335 | /// 336 | /// * `lla_origin` - Vector3 reference to the LLA origin vector (lat, long, alt) (radians, radians, meters) 337 | /// * `lla_vec` - Vector3 reference to the LLA vector (lat, long, alt) (radians, radians, meters) 338 | /// * `ellipsoid` - geo_ellipsoid reference to the ellipsoid 339 | /// 340 | /// # Return Value 341 | /// 342 | /// * `nalgebra::Vector3` - x, y, z 343 | /// 344 | pub fn lla2ned(lla_origin: &Vector3, lla_vec: &Vector3, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -> Vector3 { 345 | let orig = lla2ecef(lla_origin, ellipsoid); 346 | let actual = lla2ecef(lla_vec, ellipsoid); 347 | let ned_rot = Matrix3::new(-lla_origin.x.sin() * lla_origin.y.cos(), -lla_origin.x.sin() * lla_origin.y.sin(), lla_origin.x.cos(), 348 | -lla_origin.y.sin(), lla_origin.y.cos(), 0.0, 349 | -lla_origin.x.cos() * lla_origin.y.cos(), -lla_origin.x.cos() * lla_origin.y.sin(), -lla_origin.x.sin()); 350 | ned_rot * (actual - orig) 351 | } 352 | 353 | /// Converts 3-d LLA origin coordinates plus 3-d LLA coordinates and an ellipsoid to 3-d local ENU cartesian coordinates 354 | /// 355 | /// # Arguments 356 | /// 357 | /// * `lla_origin` - Vector3 reference to the LLA origin vector (lat, long, alt) (radians, radians, meters) 358 | /// * `lla_vec` - Vector3 reference to the LLA vector (lat, long, alt) (radians, radians, meters) 359 | /// * `ellipsoid` - geo_ellipsoid reference to the ellipsoid 360 | /// 361 | /// # Return Value 362 | /// 363 | /// * `nalgebra::Vector3` - x, y, z 364 | /// 365 | pub fn lla2enu(lla_origin: &Vector3, lla_vec: &Vector3, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -> Vector3 { 366 | let orig = lla2ecef(lla_origin, ellipsoid); 367 | let actual = lla2ecef(lla_vec, ellipsoid); 368 | let enu_rot = Matrix3::new(-lla_origin.y.sin(), lla_origin.y.cos(), 0.0, 369 | -lla_origin.y.cos() * lla_origin.x.sin(), -lla_origin.y.sin() * lla_origin.x.sin(), lla_origin.x.cos(), 370 | lla_origin.y.cos() * lla_origin.x.cos(), lla_origin.y.sin() * lla_origin.x.cos(), lla_origin.x.sin()); 371 | enu_rot * (actual - orig) 372 | } 373 | 374 | /// Converts 3-d local cartesian NED coordinates plus 3-d LLA origin coordinates and an ellipsoid to 3-d LLA coordinates 375 | /// 376 | /// # Arguments 377 | /// 378 | /// * `lla_origin` - Vector3 reference to the LLA origin vector (lat, long, alt) (radians, radians, meters) 379 | /// * `ned_vec` - Vector3 reference to the local NED cartesian vector (x, y, z) 380 | /// * `ellipsoid` - geo_ellipsoid reference to the ellipsoid 381 | /// 382 | /// # Return Value 383 | /// 384 | /// * `nalgebra::Vector3` - (lat, long, alt) (radians, radians, meters) 385 | /// 386 | pub fn ned2lla(lla_origin: &Vector3, ned_vec: &Vector3, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -> Vector3 { 387 | let orig = lla2ecef(lla_origin, ellipsoid); 388 | 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(), 389 | -lla_origin.x.sin() * lla_origin.y.sin(), lla_origin.y.cos(), -lla_origin.x.cos() * lla_origin.y.sin(), 390 | lla_origin.x.cos(), 0.0, -lla_origin.x.sin()); 391 | let actual = ned_rot * ned_vec; 392 | let total = actual + orig; 393 | let ret_vec = ecef2lla(&total, ellipsoid); 394 | ret_vec 395 | } 396 | 397 | /// Converts 3-d local cartesian ENU coordinates plus 3-d LLA origin coordinates and an ellipsoid to 3-d LLA coordinates 398 | /// 399 | /// # Arguments 400 | /// 401 | /// * `lla_origin` - Vector3 reference to the LLA origin vector (lat, long, alt) (radians, radians, meters) 402 | /// * `enu_vec` - Vector3 reference to the local NED cartesian vector (x, y, z) 403 | /// * `ellipsoid` - geo_ellipsoid reference to the ellipsoid 404 | /// 405 | /// # Return Value 406 | /// 407 | /// * `nalgebra::Vector3` - (lat, long, alt) (radians, radians, meters) 408 | /// 409 | pub fn enu2lla(lla_origin: &Vector3, enu_vec: &Vector3, ellipsoid: &geo_ellipsoid::geo_ellipsoid) -> Vector3 { 410 | let orig = lla2ecef(lla_origin, ellipsoid); 411 | 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(), 412 | lla_origin.y.cos(), -lla_origin.y.sin() * lla_origin.x.sin(), lla_origin.y.sin() * lla_origin.x.cos(), 413 | 0.0, lla_origin.x.cos(), lla_origin.x.sin()); 414 | let actual = enu_rot * enu_vec; 415 | let total = actual + orig; 416 | let ret_vec = ecef2lla(&total, ellipsoid); 417 | ret_vec 418 | } 419 | 420 | //Unit tests 421 | #[cfg(test)] 422 | mod tests { 423 | use super::*; 424 | use float_cmp::ApproxEqRatio; 425 | #[test] 426 | fn test_enu2ned() { 427 | let enu_vec: Vector3 = Vector3::new(3.0, 4.0, 5.0); 428 | let ned_vec = enu2ned(&enu_vec); 429 | 430 | let test_x = 4.0; 431 | let test_y = 3.0; 432 | let test_z = -5.0; 433 | assert!(ned_vec.x.approx_eq_ratio(&test_x, 0.00025)); 434 | assert!(ned_vec.y.approx_eq_ratio(&test_y, 0.00025)); 435 | assert!(ned_vec.z.approx_eq_ratio(&test_z, 0.00025)); 436 | } 437 | #[test] 438 | fn test_ned2enu() { 439 | let ned_vec: Vector3 = Vector3::new(3.0, 4.0, 5.0); 440 | let enu_vec = ned2enu(&ned_vec); 441 | 442 | let test_x = 4.0; 443 | let test_y = 3.0; 444 | let test_z = -5.0; 445 | assert!(enu_vec.x.approx_eq_ratio(&test_x, 0.00025)); 446 | assert!(enu_vec.y.approx_eq_ratio(&test_y, 0.00025)); 447 | assert!(enu_vec.z.approx_eq_ratio(&test_z, 0.00025)); 448 | } 449 | #[test] 450 | fn test_lla2ecef() { 451 | let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 452 | geo_ellipsoid::WGS84_FLATTENING); 453 | let lla_vec: Vector3 = Vector3::new(0.8527087756759584, 0.04105401863784606, 1000.000000000); 454 | let ecef_vec = lla2ecef(&lla_vec, &ellipsoid); 455 | 456 | let test_x = 4201570.9492264455; 457 | let test_y = 172588.3449531975; 458 | let test_z = 4780835.4317144295; 459 | assert!(ecef_vec.x.approx_eq_ratio(&test_x, 0.00025)); 460 | assert!(ecef_vec.y.approx_eq_ratio(&test_y, 0.00025)); 461 | assert!(ecef_vec.z.approx_eq_ratio(&test_z, 0.00025)); 462 | } 463 | #[test] 464 | fn test_ecef2lla() { 465 | let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 466 | geo_ellipsoid::WGS84_FLATTENING); 467 | let ecef_vec: Vector3 = Vector3::new(4201570.9492264455, 172588.3449531975, 4780835.4317144295); 468 | let lla_vec = ecef2lla(&ecef_vec, &ellipsoid); 469 | 470 | let test_x = 0.8527087756759584; 471 | let test_y = 0.04105401863784606; 472 | let test_z = 1000.000000000; 473 | assert!(lla_vec.x.approx_eq_ratio(&test_x, 0.00025)); 474 | assert!(lla_vec.y.approx_eq_ratio(&test_y, 0.00025)); 475 | assert!(lla_vec.z.approx_eq_ratio(&test_z, 0.00025)); 476 | } 477 | #[test] 478 | fn test_ll2utm() { 479 | let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 480 | geo_ellipsoid::WGS84_FLATTENING); 481 | let ll_vec: Vector2 = Vector2::new(1.3804121468, 0.20555336013); 482 | let utm = ll2utm(&ll_vec, &ellipsoid); 483 | 484 | let test_zone = 33; 485 | let test_hem = utm_grid::hemisphere::NORTH; 486 | let test_easting = 431952.612166; 487 | let test_northing = 8782098.22289; 488 | let test_convergence = -0.055232079; 489 | let test_scale = 0.999656581563; 490 | 491 | assert_eq!(utm.get_zone(), test_zone); 492 | assert_eq!(utm.get_hem(), test_hem); 493 | assert!(utm.get_easting().approx_eq_ratio(&test_easting, 0.00025)); 494 | assert!(utm.get_northing().approx_eq_ratio(&test_northing, 0.00025)); 495 | assert!(utm.get_convergence().approx_eq_ratio(&test_convergence, 0.00025)); 496 | assert!(utm.get_scale().approx_eq_ratio(&test_scale, 0.00025)); 497 | } 498 | #[test] 499 | fn test_utm2ll() { 500 | let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 501 | geo_ellipsoid::WGS84_FLATTENING); 502 | let mut utm = utm_grid::utm_grid::new(0, utm_grid::hemisphere::NORTH, 0.0, 0.0, 0.0, 0.0); 503 | utm.set_zone(33); 504 | utm.set_hem(utm_grid::hemisphere::NORTH); 505 | utm.set_easting(431952.612166); 506 | utm.set_northing(8782098.22289); 507 | utm.set_convergence(-0.055232079); 508 | utm.set_scale(0.999656581563); 509 | let ll_vec = utm2ll(&utm, &ellipsoid); 510 | 511 | let test_lat = 1.3804121468; 512 | let test_lon = 0.20555336013; 513 | assert!(ll_vec.x.approx_eq_ratio(&test_lat, 0.00025)); 514 | assert!(ll_vec.y.approx_eq_ratio(&test_lon, 0.00025)); 515 | } 516 | #[test] 517 | fn test_lla2ned() { 518 | let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 519 | geo_ellipsoid::WGS84_FLATTENING); 520 | let lla_orig_vec: Vector3 = Vector3::new(0.8527087756759584, 0.04105401863784606, 1000.000000000); 521 | let lla_vec: Vector3 = Vector3::new(0.8527087756759584, 0.042799347889836060477, 1000.000000000); 522 | let ned_vec = lla2ned(&lla_orig_vec, &lla_vec, &ellipsoid); 523 | 524 | let test_x = 4.8231982231937990945; 525 | let test_y = 7339.3050417820732036; 526 | let test_z = 4.2139798876589225073; 527 | assert!(ned_vec.x.approx_eq_ratio(&test_x, 0.00025)); 528 | assert!(ned_vec.y.approx_eq_ratio(&test_y, 0.00025)); 529 | assert!(ned_vec.z.approx_eq_ratio(&test_z, 0.00025)); 530 | } 531 | #[test] 532 | fn test_lla2enu() { 533 | let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 534 | geo_ellipsoid::WGS84_FLATTENING); 535 | let lla_orig_vec: Vector3 = Vector3::new(0.8527087756759584, 0.04105401863784606, 1000.000000000); 536 | let lla_vec: Vector3 = Vector3::new(0.8527087756759584, 0.042799347889836060477, 1000.000000000); 537 | let enu_vec = lla2enu(&lla_orig_vec, &lla_vec, &ellipsoid); 538 | 539 | let test_x = 7339.3050417820732036; 540 | let test_y = 4.8231982231937990945; 541 | let test_z = -4.2139798876589225073; 542 | assert!(enu_vec.x.approx_eq_ratio(&test_x, 0.00025)); 543 | assert!(enu_vec.y.approx_eq_ratio(&test_y, 0.00025)); 544 | assert!(enu_vec.z.approx_eq_ratio(&test_z, 0.00025)); 545 | } 546 | #[test] 547 | fn test_ned2lla() { 548 | let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 549 | geo_ellipsoid::WGS84_FLATTENING); 550 | let lla_orig_vec: Vector3 = Vector3::new(0.8527087756759584, 0.04105401863784606, 1000.000000000); 551 | let ned_vec: Vector3 = Vector3::new(4.8231982231937990945, 7339.3050417820732036, 4.2139798876589225073); 552 | let lla_vec = ned2lla(&lla_orig_vec, &ned_vec, &ellipsoid); 553 | 554 | let test_x = 0.8527087756759584; 555 | let test_y = 0.042799347889836060477; 556 | let test_z = 1000.000000000; 557 | assert!(lla_vec.x.approx_eq_ratio(&test_x, 0.00025)); 558 | assert!(lla_vec.y.approx_eq_ratio(&test_y, 0.00025)); 559 | assert!(lla_vec.z.approx_eq_ratio(&test_z, 0.00025)); 560 | } 561 | #[test] 562 | fn test_enu2lla() { 563 | let ellipsoid = geo_ellipsoid::geo_ellipsoid::new(geo_ellipsoid::WGS84_SEMI_MAJOR_AXIS_METERS, 564 | geo_ellipsoid::WGS84_FLATTENING); 565 | let lla_orig_vec: Vector3 = Vector3::new(0.8527087756759584, 0.04105401863784606, 1000.000000000); 566 | let enu_vec: Vector3 = Vector3::new(7339.3050417820732036, 4.8231982231937990945, -4.2139798876589225073); 567 | let lla_vec = enu2lla(&lla_orig_vec, &enu_vec, &ellipsoid); 568 | 569 | let test_x = 0.8527087756759584; 570 | let test_y = 0.042799347889836060477; 571 | let test_z = 1000.000000000; 572 | assert!(lla_vec.x.approx_eq_ratio(&test_x, 0.00025)); 573 | assert!(lla_vec.y.approx_eq_ratio(&test_y, 0.00025)); 574 | assert!(lla_vec.z.approx_eq_ratio(&test_z, 0.00025)); 575 | } 576 | } 577 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #[allow(unused_imports)] 3 | #[macro_use] 4 | extern crate float_cmp; 5 | extern crate nalgebra as na; 6 | 7 | /// Module for 3-dimensional coordinate transformations 8 | pub mod d3; 9 | /// Module for 2-dimensional coordinate transformations 10 | pub mod d2; 11 | /// Module for geographical coordinate transformations 12 | pub mod geo; 13 | /// Module for constants used in the coordinate transformations 14 | pub mod structs; 15 | 16 | /// Prelude module for ease of use 17 | pub mod prelude { 18 | pub use na::Vector3; 19 | pub use na::Vector2; 20 | pub use d3; 21 | pub use d2; 22 | pub use geo; 23 | pub use structs; 24 | pub use structs::*; 25 | } -------------------------------------------------------------------------------- /src/structs/geo_ellipsoid.rs: -------------------------------------------------------------------------------- 1 | pub const WGS84_SEMI_MAJOR_AXIS_METERS: f64 = 6378137.0; 2 | pub const WGS84_FLATTENING: f64 = 298.257223563; 3 | pub const WGS72_SEMI_MAJOR_AXIS_METERS: f64 = 6378135.0; 4 | pub const WGS72_FLATTENING: f64 = 298.26; 5 | pub const WGS66_SEMI_MAJOR_AXIS_METERS: f64 = 6378145.0; 6 | pub const WGS66_FLATTENING: f64 = 298.25; 7 | pub const WGS60_SEMI_MAJOR_AXIS_METERS: f64 = 6378165.0; 8 | pub const WGS60_FLATTENING: f64 = 298.3; 9 | pub const SOUTHAMERICAN_1969_SEMI_MAJOR_AXIS_METERS: f64 = 6378160.0; 10 | pub const SOUTHAMERICAN_1969_FLATTENING: f64 = 298.25; 11 | pub const FISCHER_1969_MODIFIED_SEMI_MAJOR_AXIS_METERS: f64 = 6378155.0; 12 | pub const FISCHER_1969_MODIFIED_FLATTENING: f64 = 298.3; 13 | pub const EVEREST_MODIFIED_SEMI_MAJOR_AXIS_METERS: f64 = 6377304.063; 14 | pub const EVEREST_MODIFIED_FLATTENING: f64 = 300.8017; 15 | pub const AIRY_MODIFIED_SEMI_MAJOR_AXIS_METERS: f64 = 6377340.189; 16 | pub const AIRY_MODIFIED_FLATTENING: f64 = 299.3249646; 17 | pub const KRASSOVSKY_SEMI_MAJOR_AXIS_METERS: f64 = 6378245.0; 18 | pub const KRASSOVSKY_FLATTENING: f64 = 298.3; 19 | pub const INTERNATIONAL_SEMI_MAJOR_AXIS_METERS: f64 = 6378388.0; 20 | pub const INTERNATIONAL_FLATTENING: f64 = 297.0; 21 | pub const HOUGH_SEMI_MAJOR_AXIS_METERS: f64 = 6378270.0; 22 | pub const HOUGH_FLATTENING: f64 = 297.0; 23 | pub const HELMERT_1906_SEMI_MAJOR_AXIS_METERS: f64 = 6378200.0; 24 | pub const HELMERT_1906_FLATTENING: f64 = 298.3; 25 | pub const GRS_1980_SEMI_MAJOR_AXIS_METERS: f64 = 6378137.0; 26 | pub const GRS_1980_FLATTENING: f64 = 298.257222101; 27 | pub const GRS_1967_SEMI_MAJOR_AXIS_METERS: f64 = 6378160.0; 28 | pub const GRS_1967_FLATTENING: f64 = 298.247167427; 29 | pub const FISCHER_1968_SEMI_MAJOR_AXIS_METERS: f64 = 6378150.0; 30 | pub const FISCHER_1968_FLATTENING: f64 = 298.3; 31 | pub const FISCHER_1960_MERCURY_SEMI_MAJOR_AXIS_METERS: f64 = 6378166.0; 32 | pub const FISCHER_1960_MERCURY_FLATTENING: f64 = 298.3; 33 | pub const EVEREST_SEMI_MAJOR_AXIS_METERS: f64 = 6377276.345; 34 | pub const EVEREST_FLATTENING: f64 = 300.8017; 35 | pub const CLARKE_1880_SEMI_MAJOR_AXIS_METERS: f64 = 6378249.145; 36 | pub const CLARKE_1880_FLATTENING: f64 = 293.465; 37 | pub const CLARKE_1866_SEMI_MAJOR_AXIS_METERS: f64 = 6378206.4; 38 | pub const CLARKE_1866_FLATTENING: f64 = 294.9786982; 39 | pub const BESSEL_1841_NAMBIA_SEMI_MAJOR_AXIS_METERS: f64 = 6377483.865; 40 | pub const BESSEL_1841_NAMBIA_FLATTENING: f64 = 299.1528128; 41 | pub const BESSEL_1841_SEMI_MAJOR_AXIS_METERS: f64 = 6377397.155; 42 | pub const BESSEL_1841_FLATTENING: f64 = 299.1528128; 43 | pub const AUSTRALIAN_NATIONAL_SEMI_MAJOR_AXIS_METERS: f64 = 6378160.0; 44 | pub const AUSTRALIAN_NATIONAL_FLATTENING: f64 = 298.25; 45 | pub const AIRY_SEMI_MAJOR_AXIS_METERS: f64 = 6377563.396; 46 | pub const AIRY_FLATTENING: f64 = 299.3249646; 47 | 48 | #[allow(non_camel_case_types)] 49 | pub struct geo_ellipsoid { 50 | semi_major_axis: f64, 51 | flattening: f64, 52 | semi_minor_axis: f64, 53 | first_ecc: f64, 54 | second_ecc: f64 55 | } 56 | 57 | impl geo_ellipsoid { 58 | pub fn new(sma: f64, f: f64) -> geo_ellipsoid { 59 | let smia = sma * (1.0 - (1.0 / f)); 60 | geo_ellipsoid { 61 | semi_major_axis: sma, 62 | flattening: 1.0 / f, 63 | semi_minor_axis: smia, 64 | first_ecc: ((sma.powi(2) - smia.powi(2)) / (sma.powi(2))).sqrt(), 65 | second_ecc: ((sma.powi(2) - smia.powi(2)) / (smia.powi(2))).sqrt(), 66 | } 67 | } 68 | 69 | pub fn get_semi_major_axis(&self) -> f64 { 70 | self.semi_major_axis 71 | } 72 | 73 | pub fn get_flattening(&self) -> f64 { 74 | self.flattening 75 | } 76 | 77 | pub fn get_semi_minor_axis(&self) -> f64 { 78 | self.semi_minor_axis 79 | } 80 | 81 | pub fn get_first_ecc(&self) -> f64 { 82 | self.first_ecc 83 | } 84 | 85 | pub fn get_second_ecc(&self) -> f64 { 86 | self.second_ecc 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/structs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod geo_ellipsoid; 2 | pub mod utm_grid; -------------------------------------------------------------------------------- /src/structs/utm_grid.rs: -------------------------------------------------------------------------------- 1 | pub const SCALE_FACTOR_CENTERAL_MERIDIAN: f64 = 0.9996; 2 | pub const FALSE_EASTING: f64 = 500000.0; 3 | pub const FALSE_NORTHING: f64 = 10000000.0; 4 | 5 | #[derive(Clone, Debug, PartialEq)] 6 | #[allow(non_camel_case_types)] 7 | pub enum hemisphere { 8 | NORTH, 9 | SOUTH 10 | } 11 | 12 | #[allow(non_camel_case_types)] 13 | pub struct utm_grid { 14 | zone: u32, 15 | hem: hemisphere, 16 | easting: f64, 17 | northing: f64, 18 | convergence: f64, 19 | scale: f64 20 | } 21 | 22 | impl utm_grid { 23 | pub fn new(p_z: u32, p_h: hemisphere, p_e: f64, p_n: f64, p_c: f64, p_s: f64) -> utm_grid { 24 | utm_grid { 25 | zone: p_z, 26 | hem: p_h, 27 | easting: p_e, 28 | northing: p_n, 29 | convergence: p_c, 30 | scale: p_s 31 | } 32 | } 33 | 34 | pub fn set_zone(&mut self, z: u32) { 35 | self.zone = z; 36 | } 37 | 38 | pub fn set_hem(&mut self, h: hemisphere) { 39 | self.hem = h; 40 | } 41 | 42 | pub fn set_easting(&mut self, e: f64) { 43 | self.easting = e; 44 | } 45 | 46 | pub fn set_northing(&mut self, n: f64) { 47 | self.northing = n; 48 | } 49 | 50 | pub fn set_convergence(&mut self, n: f64) { 51 | self.convergence = n; 52 | } 53 | 54 | pub fn set_scale(&mut self, n: f64) { 55 | self.scale = n; 56 | } 57 | 58 | pub fn get_zone(&self) -> u32 { 59 | self.zone 60 | } 61 | 62 | pub fn get_hem(&self) -> hemisphere { 63 | self.hem.to_owned() 64 | } 65 | 66 | pub fn get_easting(&self) -> f64 { 67 | self.easting 68 | } 69 | 70 | pub fn get_northing(&self) -> f64 { 71 | self.northing 72 | } 73 | 74 | pub fn get_convergence(&self) -> f64 { 75 | self.convergence 76 | } 77 | 78 | pub fn get_scale(&self) -> f64 { 79 | self.scale 80 | } 81 | } --------------------------------------------------------------------------------