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
use dms_coordinates::DMS;
use map_3d::{deg2rad, ecef2geodetic, geodetic2ecef, rad2deg, Ellipsoid};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "qc")]
use maud::{html, Markup, Render};
#[derive(Default, Copy, Debug, Clone, PartialEq, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct GroundPosition(f64, f64, f64);
impl From<(f64, f64, f64)> for GroundPosition {
fn from(xyz: (f64, f64, f64)) -> Self {
Self(xyz.0, xyz.1, xyz.2)
}
}
impl From<GroundPosition> for (f64, f64, f64) {
fn from(val: GroundPosition) -> Self {
(val.0, val.1, val.2)
}
}
impl GroundPosition {
/// Builds Self from ECEF WGS84 coordinates
pub fn from_ecef_wgs84(pos: (f64, f64, f64)) -> Self {
Self(pos.0, pos.1, pos.2)
}
/// Builds Self from Geodetic coordinates in ddeg
pub fn from_geodetic(pos: (f64, f64, f64)) -> Self {
let (x, y, z) = pos;
let (x, y, z) = geodetic2ecef(deg2rad(x), deg2rad(y), deg2rad(z), Ellipsoid::WGS84);
Self(x, y, z)
}
/// Converts Self to ECEF WGS84
pub fn to_ecef_wgs84(&self) -> (f64, f64, f64) {
(self.0, self.1, self.2)
}
/// Converts Self to geodetic coordinates in ddeg
pub fn to_geodetic(&self) -> (f64, f64, f64) {
let (x, y, z) = (self.0, self.1, self.2);
let (lat, lon, alt) = ecef2geodetic(x, y, z, Ellipsoid::WGS84);
(rad2deg(lat), rad2deg(lon), alt)
}
/// Returns position altitude
pub fn altitude(&self) -> f64 {
self.to_geodetic().2
}
}
impl std::fmt::Display for GroundPosition {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "WGS84 ({}m {}m {}m)", self.0, self.1, self.2)
}
}
/*
* RINEX compatible formatting
*/
impl std::fmt::UpperHex for GroundPosition {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:14.4}{:14.4}{:14.4}", self.0, self.1, self.2)
}
}
#[cfg(feature = "qc")]
impl Render for GroundPosition {
fn render(&self) -> Markup {
let ecef = (self.0, self.1, self.2);
let geo = self.to_geodetic();
html! {
table {
tr {
th {
"ECEF (WGS84)"
}
}
tr {
th {
"X"
}
td {
(format!("{:.3} m", ecef.0))
}
th {
"Y"
}
td {
(format!("{:.3} m", ecef.1))
}
th {
"Z"
}
td {
(format!("{:.3} m", ecef.2))
}
}
tr {
th {
"GEO"
}
}
tr {
th {
"Latitude"
}
td {
(format!("{:.6}°", geo.0))
}
th {
"Longitude"
}
td {
(format!("{:.6}°", geo.1))
}
th {
"Altitude"
}
td {
(format!("{:.3} m", geo.2))
}
}
tr {
th {
"DMS"
}
td {
(DMS::from_ddeg_latitude(geo.0).to_string())
}
th {
"DMS"
}
td {
(DMS::from_ddeg_longitude(geo.1).to_string())
}
}
}
}
}
}