geodesy/inner_op/
geodesic.rs1use crate::authoring::*;
3
4fn fwd(op: &Op, _ctx: &dyn Context, operands: &mut dyn CoordinateSet) -> usize {
7 let ellps = op.params.ellps(0);
8
9 let n = operands.len();
10 let sliced = 0..n;
11
12 let mut successes = 0_usize;
13 for i in sliced {
14 let args = operands.get_coord(i);
15 let origin = Coor2D::geo(args[0], args[1]);
16 let azimuth = args[2].to_radians();
17 let distance = args[3];
18
19 let destination = ellps.geodesic_fwd(&origin, azimuth, distance).to_degrees();
20
21 if destination[3] > 990.0 {
23 operands.set_coord(i, &Coor4D::nan());
24 continue;
25 }
26
27 let result = Coor4D([destination[1], destination[0], args[0], args[1]]);
28 operands.set_coord(i, &result);
29 successes += 1;
30 }
31
32 successes
33}
34
35fn inv(op: &Op, _ctx: &dyn Context, operands: &mut dyn CoordinateSet) -> usize {
38 let ellps = op.params.ellps(0);
39 let reversible = op.params.boolean("reversible");
40
41 let n = operands.len();
42 let sliced = 0..n;
43
44 let mut successes = 0_usize;
45 for i in sliced {
46 let mut from = Coor2D::origin();
47 let mut to = Coor2D::origin();
48
49 let coord = operands.get_coord(i);
50 from[0] = coord[1].to_radians();
51 from[1] = coord[0].to_radians();
52 to[0] = coord[3].to_radians();
53 to[1] = coord[2].to_radians();
54
55 let mut geodesic = ellps.geodesic_inv(&from, &to).to_degrees();
56
57 if geodesic[3] > 990.0 {
59 operands.set_coord(i, &Coor4D::nan());
60 continue;
61 }
62 geodesic[3] = (geodesic[1] + 180.0) % 360.0;
63
64 if reversible {
65 let distance = geodesic[2];
66 let return_azi = geodesic[3];
67 operands.set_coord(i, &Coor4D::raw(coord[2], coord[3], return_azi, distance));
68 continue;
69 }
70
71 operands.set_coord(i, &geodesic);
72 successes += 1;
73 }
74
75 successes
76}
77
78#[rustfmt::skip]
82pub const GAMUT: [OpParameter; 3] = [
83 OpParameter::Flag { key: "inv" },
84 OpParameter::Flag { key: "reversible" },
85 OpParameter::Text { key: "ellps", default: Some("GRS80") }
86];
87
88pub fn new(parameters: &RawParameters, _ctx: &dyn Context) -> Result<Op, Error> {
89 let op = Op::basic(parameters, InnerOp(fwd), Some(InnerOp(inv)), &GAMUT)?;
90 Ok(op)
91}
92
93#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test]
100 fn geodesic() -> Result<(), Error> {
101 let mut ctx = Minimal::default();
102
103 let cph_cdg = Coor4D::raw(55., 12., 49., 2.);
105
106 let op = ctx.op("geodesic")?;
108 let mut operands = [cph_cdg];
109 ctx.apply(op, Inv, &mut operands)?;
110
111 let expected = Coor4D([
112 -130.1540604203936,
113 -138.05257941840648,
114 956066.2319619625,
115 41.94742058159352,
116 ]);
117
118 assert!((operands[0][0] - expected[0]).abs() < 1e-9);
119 assert!((operands[0][1] - expected[1]).abs() < 1e-9);
120 assert!((operands[0][2] - expected[2]).abs() < 1e-9);
121 assert!((operands[0][3] - expected[3]).abs() < 1e-9);
122
123 let op = ctx.op("geodesic reversible")?;
125 let mut operands = [cph_cdg];
126 ctx.apply(op, Inv, &mut operands)?;
127
128 let expected = Coor4D([49.0, 2.0, 41.94742058159352, 956066.2319619625]);
129
130 assert!((operands[0][0] - expected[0]).abs() < 1e-9);
131 assert!((operands[0][1] - expected[1]).abs() < 1e-9);
132 assert!((operands[0][2] - expected[2]).abs() < 1e-9);
133 assert!((operands[0][3] - expected[3]).abs() < 1e-9);
134
135 ctx.apply(op, Fwd, &mut operands)?;
137
138 assert!((operands[0][0] - cph_cdg[0]).abs() < 1e-10);
139 assert!((operands[0][1] - cph_cdg[1]).abs() < 1e-10);
140 assert!((operands[0][2] - cph_cdg[2]).abs() < 1e-10);
141 assert!((operands[0][3] - cph_cdg[3]).abs() < 1e-10);
142
143 Ok(())
144 }
145}