Skip to main content

geodesy/coordinate/
set.rs

1/// `CoordinateSet` is the fundamental coordinate access interface in ISO-19111.
2/// Strictly speaking, it is not a set, but (in abstract terms) rather an
3/// indexed list, or (in more concrete terms): An array.
4///
5/// Here it is implemented simply as an accessor trait, that allows us to
6/// access any user provided data model by iterating over its elements,
7/// represented as a `Coor4D`
8pub trait CoordinateSet: CoordinateMetadata {
9    /// Number of coordinate tuples in the set
10    fn len(&self) -> usize;
11
12    /// Native dimension of the underlying coordinates (they will always be
13    /// returned by [`Self::get_coord()`] as converted to [`Coor4D`](super::Coor4D))
14    fn dim(&self) -> usize;
15
16    /// Access the `index`th coordinate tuple
17    fn get_coord(&self, index: usize) -> Coor4D;
18
19    /// Overwrite the `index`th coordinate tuple
20    fn set_coord(&mut self, index: usize, value: &Coor4D);
21
22    /// Companion to `len()`
23    fn is_empty(&self) -> bool {
24        self.len() == 0
25    }
26
27    /// Replace the two first elements of the `index`th `CoordinateTuple`
28    /// with `x` and `y`.
29    /// Consider providing a type specific version, when implementing
30    /// the CoordinateSet trait for a concrete data type: The default
31    ///  version is straightforward, but not necessarily efficient
32    fn set_xy(&mut self, index: usize, x: f64, y: f64) {
33        let mut coord = self.get_coord(index);
34        coord[0] = x;
35        coord[1] = y;
36        self.set_coord(index, &coord);
37    }
38
39    /// Access the two first elements of the `index`th `CoordinateTuple`.
40    /// Consider providing a type specific version, when implementing
41    /// the CoordinateSet trait for a concrete data type: The default
42    /// version is straightforward, but not necessarily efficient
43    fn xy(&self, index: usize) -> (f64, f64) {
44        self.get_coord(index).xy()
45    }
46
47    /// Replace the three first elements of the `index`th `CoordinateTuple`
48    /// with `x`, `y` and `z`.
49    /// Consider providing a type specific version, when implementing
50    /// the CoordinateSet trait for a concrete data type: The default
51    ///  version is straightforward, but not necessarily efficient
52    fn set_xyz(&mut self, index: usize, x: f64, y: f64, z: f64) {
53        let mut coord = self.get_coord(index);
54        coord[0] = x;
55        coord[1] = y;
56        coord[2] = z;
57        self.set_coord(index, &coord);
58    }
59
60    /// Access the three first elements of the `index`th `CoordinateTuple`.
61    /// Consider providing a type specific version, when implementing
62    /// the CoordinateSet trait for a concrete data type: The default
63    /// version is straightforward, but not necessarily efficient
64    fn xyz(&self, index: usize) -> (f64, f64, f64) {
65        self.get_coord(index).xyz()
66    }
67
68    /// Replace the four elements of the `index`th `CoordinateTuple`
69    /// with `x`, `y`, `z` and `t`. Syntactic sugar for [`Self::set_coord`]
70    fn set_xyzt(&mut self, index: usize, x: f64, y: f64, z: f64, t: f64) {
71        self.set_coord(index, &Coor4D([x, y, z, t]));
72    }
73
74    /// Access the four elements of the `index`th `CoordinateTuple`.
75    /// Syntactic sugar for [`Self::get_coord`]
76    fn xyzt(&self, index: usize) -> (f64, f64, f64, f64) {
77        self.get_coord(index).xyzt()
78    }
79
80    /// Set all coordinate tuples in the set to NaN
81    fn stomp(&mut self) {
82        let nanny = Coor4D::nan();
83        for i in 0..self.len() {
84            self.set_coord(i, &nanny);
85        }
86    }
87}
88
89use super::*;
90
91// Some helper macros, simplifying the macros for the actual data types
92
93// Produce the correct len() method for arrays, slices, and vecs
94macro_rules! length {
95    (array) => {
96        fn len(&self) -> usize {
97            N
98        }
99    };
100
101    (slice) => {
102        fn len(&self) -> usize {
103            (**self).len()
104        }
105    };
106
107    (vec) => {
108        fn len(&self) -> usize {
109            self.len()
110        }
111    };
112}
113
114macro_rules! coordinate_set_impl_2d_subset {
115    ($dim:expr, $len:ident) => {
116        length!($len);
117
118        fn dim(&self) -> usize {
119            $dim
120        }
121
122        fn xy(&self, index: usize) -> (f64, f64) {
123            self[index].xy()
124        }
125
126        fn set_xy(&mut self, index: usize, x: f64, y: f64) {
127            self[index].set_xy(x, y);
128        }
129    };
130}
131
132macro_rules! coordinate_set_impl_3d_subset {
133    ($dim:expr, $len:ident) => {
134        coordinate_set_impl_2d_subset!($dim, $len);
135
136        fn xyz(&self, index: usize) -> (f64, f64, f64) {
137            self[index].xyz()
138        }
139        fn set_xyz(&mut self, index: usize, x: f64, y: f64, z: f64) {
140            self[index].set_xyz(x, y, z);
141        }
142    };
143}
144
145// ----- CoordinateSet implementations for some Coor2D containers ------------
146
147/// By default, the CoordinateSet implementations for Coor2D return `0` and `f64::NAN`
148/// as third and fourth coordinate value in `get_coord()`. In the common use case of
149/// handling 2D geographical or projected coordinates in a static reference frame,
150/// this will usually be what you need:
151///
152/// - The `0` as the third coordinate will make transformations behave as if the points
153///   are placed immediately on the reference ellipsoid, `h==0`
154///
155/// - The `f64::NAN` as the fourth coordinate will spill into the plane coordinate
156///   values if passing these static coordinates through any dynamic transformations,
157///   requiring a proper time coordinate, hence giving a very noisy debugging signal
158///
159/// If other fixed values for third and fourth coordinate are needed, the
160/// `CoordinateSet` trait is also blanket-implemented for the tuple
161/// `(T, f64, f64) where T: CoordinateSet`, so any data structure implementing the
162/// `CoordinateSet` trait can be combined with two fixed values for third and fourth
163/// coordinate dimension.
164macro_rules! coordinate_set_impl_for_coor2d {
165    ($kind:ident) => {
166        coordinate_set_impl_2d_subset!(2, $kind);
167
168        fn get_coord(&self, index: usize) -> Coor4D {
169            Coor4D([self[index][0], self[index][1], 0., f64::NAN])
170        }
171
172        fn set_coord(&mut self, index: usize, value: &Coor4D) {
173            self[index] = Coor2D([value[0], value[1]]);
174        }
175    };
176}
177
178impl<const N: usize> CoordinateSet for [Coor2D; N] {
179    coordinate_set_impl_for_coor2d!(array);
180}
181
182impl CoordinateSet for &mut [Coor2D] {
183    coordinate_set_impl_for_coor2d!(slice);
184}
185
186impl CoordinateSet for Vec<Coor2D> {
187    coordinate_set_impl_for_coor2d!(vec);
188}
189
190// ----- CoordinateSet implementations for some Coor32 containers ------------
191
192macro_rules! coordinate_set_impl_for_coor32 {
193    ($kind:ident) => {
194        coordinate_set_impl_2d_subset!(2, $kind);
195
196        fn get_coord(&self, index: usize) -> Coor4D {
197            Coor4D([self[index][0] as f64, self[index][1] as f64, 0., f64::NAN])
198        }
199
200        fn set_coord(&mut self, index: usize, value: &Coor4D) {
201            self[index] = Coor32([value[0] as f32, value[1] as f32]);
202        }
203    };
204}
205
206impl<const N: usize> CoordinateSet for [Coor32; N] {
207    coordinate_set_impl_for_coor32!(array);
208}
209
210impl CoordinateSet for &mut [Coor32] {
211    coordinate_set_impl_for_coor32!(slice);
212}
213
214impl CoordinateSet for Vec<Coor32> {
215    coordinate_set_impl_for_coor32!(vec);
216}
217
218// ----- CoordinateSet implementations for some Coor3D containers ------------
219
220macro_rules! coordinate_set_impl_for_coor3d {
221    ($kind:ident) => {
222        coordinate_set_impl_3d_subset!(3, $kind);
223
224        fn get_coord(&self, index: usize) -> Coor4D {
225            Coor4D([self[index][0], self[index][1], self[index][2], f64::NAN])
226        }
227
228        fn set_coord(&mut self, index: usize, value: &Coor4D) {
229            self[index] = Coor3D([value[0], value[1], value[2]]);
230        }
231    };
232}
233
234impl<const N: usize> CoordinateSet for [Coor3D; N] {
235    coordinate_set_impl_for_coor3d!(array);
236}
237
238impl CoordinateSet for &mut [Coor3D] {
239    coordinate_set_impl_for_coor3d!(slice);
240}
241
242impl CoordinateSet for Vec<Coor3D> {
243    coordinate_set_impl_for_coor3d!(vec);
244}
245
246// ----- CoordinateSet implementations for some Coor4D containers ------------
247
248macro_rules! coordinate_set_impl_for_coor4d {
249    ($kind:ident) => {
250        coordinate_set_impl_3d_subset!(4, $kind);
251
252        fn get_coord(&self, index: usize) -> Coor4D {
253            self[index]
254        }
255
256        fn set_coord(&mut self, index: usize, value: &Coor4D) {
257            self[index] = *value;
258        }
259
260        fn xyzt(&self, index: usize) -> (f64, f64, f64, f64) {
261            self[index].xyzt()
262        }
263
264        fn set_xyzt(&mut self, index: usize, x: f64, y: f64, z: f64, t: f64) {
265            self[index].set_xyzt(x, y, z, t);
266        }
267    };
268}
269
270impl<const N: usize> CoordinateSet for [Coor4D; N] {
271    coordinate_set_impl_for_coor4d!(array);
272}
273
274impl CoordinateSet for &mut [Coor4D] {
275    coordinate_set_impl_for_coor4d!(slice);
276}
277
278impl CoordinateSet for Vec<Coor4D> {
279    coordinate_set_impl_for_coor4d!(vec);
280}
281
282/// User defined values for third and fourth coordinate dimension.
283/// Intended as a way to supply a fixed height and epoch to a set
284/// of 2D coordinates
285impl<T> CoordinateSet for (T, f64, f64)
286where
287    T: CoordinateSet,
288{
289    fn len(&self) -> usize {
290        self.0.len()
291    }
292    fn dim(&self) -> usize {
293        4
294    }
295    fn get_coord(&self, index: usize) -> Coor4D {
296        let c = self.0.get_coord(index);
297        Coor4D([c[0], c[1], self.1, self.2])
298    }
299    fn set_coord(&mut self, index: usize, value: &Coor4D) {
300        self.0.set_coord(index, value);
301    }
302}
303
304/// User defined values for fourth coordinate dimension.
305/// Intended as a way to supply a fixed epoch to a set
306/// of 3D coordinates
307impl<T> CoordinateSet for (T, f64)
308where
309    T: CoordinateSet,
310{
311    fn len(&self) -> usize {
312        self.0.len()
313    }
314    fn dim(&self) -> usize {
315        4
316    }
317    fn get_coord(&self, index: usize) -> Coor4D {
318        let c = self.0.get_coord(index);
319        Coor4D([c[0], c[1], c[2], self.1])
320    }
321    fn set_coord(&mut self, index: usize, value: &Coor4D) {
322        self.0.set_coord(index, value);
323    }
324}
325
326// ----- Implementations: Coordinate Metadata ---------------------------------
327impl MdIdentifier {
328    pub fn new() -> Self {
329        MdIdentifier(uuid::Uuid::new_v4())
330    }
331}
332impl Default for MdIdentifier {
333    fn default() -> Self {
334        MdIdentifier(uuid::Uuid::new_v4())
335    }
336}
337
338impl DataEpoch {
339    pub fn new() -> Self {
340        DataEpoch(f64::NAN)
341    }
342}
343
344// ----- T E S T S ---------------------------------------------------
345
346#[cfg(test)]
347mod tests {
348    // Also see the "coor2d" test in tmerc, where 2D and 4D operands are
349    // used in otherwise identical setups.
350    use super::*;
351    // Test the "impl<const N: usize> CoordinateSet for [Coor4D; N]"
352    #[test]
353    fn array() {
354        let mut operands = crate::test_data::coor4d();
355        assert_eq!(operands.len(), 2);
356        assert!(!operands.is_empty());
357
358        let cph = operands.get_coord(0);
359        assert_eq!(cph[0], 55.);
360        assert_eq!(cph[1], 12.);
361
362        let sth = operands.get_coord(1);
363        assert_eq!(sth[0], 59.);
364        assert_eq!(sth[1], 18.);
365
366        // Turn Copenhagen into Stockholm
367        operands.set_coord(0, &sth);
368        let cph = operands.get_coord(0);
369        assert_eq!(cph[0], 59.);
370        assert_eq!(cph[1], 18.);
371    }
372
373    // Test the "impl CoordinateSet for Vec<Coor4D>"
374    #[test]
375    fn vector() {
376        let mut operands = Vec::from(crate::test_data::coor2d());
377        assert_eq!(operands.len(), 2);
378        assert!(!operands.is_empty());
379
380        let cph = operands.get_coord(0);
381        assert_eq!(cph[0], 55.);
382        assert_eq!(cph[1], 12.);
383
384        let sth = operands.get_coord(1);
385        assert_eq!(sth[0], 59.);
386        assert_eq!(sth[1], 18.);
387
388        // Turn Copenhagen into Stockholm
389        operands.set_coord(0, &sth);
390        let cph = operands.get_coord(0);
391        assert_eq!(cph[0], 59.);
392        assert_eq!(cph[1], 18.);
393    }
394
395    // Test the "AngularUnits" conversion trait
396    #[test]
397    fn angular() {
398        let operands = crate::test_data::coor2d();
399        let cph = operands.get_coord(0);
400
401        // Note the different usage patterns when using the AngularUnits trait with
402        // a Coor4D and a CoordinateSet: For the latter, the blanket implementation
403        // is for an `&mut T where T: CoordinateSet`, and we just mutate the contents
404        // in situ. For the former, we return a newly computed `Coor4D`.
405        let cph = cph.to_radians();
406        assert_eq!(cph[0], operands.get_coord(0).to_radians()[0]);
407        assert_eq!(cph[1], operands.get_coord(0).to_radians()[1]);
408
409        assert_eq!(
410            cph[0].to_degrees() * 3600.,
411            operands.get_coord(0).to_radians().to_arcsec()[0]
412        );
413        assert_eq!(
414            cph[1].to_degrees() * 3600.,
415            operands.get_coord(0).to_radians().to_arcsec()[1]
416        );
417    }
418
419    #[test]
420    fn setting_and_getting_as_f64() {
421        let first = Coor4D([11., 12., 13., 14.]);
422        let second = Coor4D([21., 22., 23., 24.]);
423        let mut operands = Vec::from([first, second]);
424        let (x, y) = operands.xy(0);
425        assert_eq!((x, y), (11., 12.));
426        let (x, y) = operands.xy(1);
427        assert_eq!((x, y), (21., 22.));
428        operands.set_xy(0, x, y);
429        let (x, y) = operands.xy(0);
430        assert_eq!((x, y), (21., 22.));
431
432        let mut operands = Vec::from([first, second]);
433        let (x, y, z) = operands.xyz(0);
434        assert_eq!((x, y, z), (11., 12., 13.));
435        let (x, y, z) = operands.xyz(1);
436        assert_eq!((x, y, z), (21., 22., 23.));
437        operands.set_xyz(0, x, y, z);
438        let (x, y, z) = operands.xyz(0);
439        assert_eq!((x, y, z), (21., 22., 23.));
440
441        let mut operands = Vec::from([first, second]);
442        let (x, y, z, t) = operands.xyzt(0);
443        assert_eq!((x, y, z, t), (11., 12., 13., 14.));
444        let (x, y, z, t) = operands.xyzt(1);
445        assert_eq!((x, y, z, t), (21., 22., 23., 24.));
446        operands.set_xyzt(0, x, y, z, t);
447        let (x, y, z, t) = operands.xyzt(0);
448        assert_eq!((x, y, z, t), (21., 22., 23., 24.));
449    }
450}