gistools/proj/convert/
axis_swap.rs

1use crate::proj::{
2    AxisDirection, CoordinateStep, IoUnits, Proj, ProjectCoordinates, ProjectionTransform, Step,
3    TransformCoordinates,
4};
5use alloc::{rc::Rc, vec::Vec};
6use core::cell::RefCell;
7
8/// An axis swapping guide
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub struct AxisSwap {
11    /// The axis guide itself. A 4-tuple of axis directions to swap
12    pub axis: [i64; 4],
13    /// The sign guide for faster comparisons
14    pub sign: [i32; 4],
15}
16impl Default for AxisSwap {
17    fn default() -> Self {
18        Self {
19            axis: [0, 1, 2, 3], // identity mapping
20            sign: [1; 4],
21        }
22    }
23}
24impl AxisSwap {
25    /// Check if the axis swap is "empty" or in the default state
26    pub fn is_empty(&self) -> bool {
27        self.axis == [0, 1, 2, 3] && self.sign == [1; 4]
28    }
29}
30impl From<Vec<AxisDirection>> for AxisSwap {
31    fn from(directions: Vec<AxisDirection>) -> Self {
32        if directions.len() > 4 {
33            panic!("Too many axes specified");
34        }
35
36        let mut axis = [0, 1, 2, 3]; // identity mapping
37        let mut sign = [1; 4];
38
39        for (i, dir) in directions.iter().enumerate() {
40            match dir {
41                AxisDirection::West => {
42                    axis[i] = 0;
43                    sign[i] = -1;
44                }
45                AxisDirection::East => {
46                    axis[i] = 0;
47                    sign[i] = 1;
48                }
49                AxisDirection::South => {
50                    axis[i] = 1;
51                    sign[i] = -1;
52                }
53                AxisDirection::North => {
54                    axis[i] = 1;
55                    sign[i] = 1;
56                }
57                AxisDirection::Down => {
58                    axis[i] = 2;
59                    sign[i] = -1;
60                }
61                AxisDirection::Up => {
62                    axis[i] = 2;
63                    sign[i] = 1;
64                }
65                _ => {} // Do nothing
66            }
67        }
68
69        // Check for duplicates
70        for a in 0..directions.len() {
71            for b in (a + 1)..directions.len() {
72                if axis[a] == axis[b] {
73                    panic!("Duplicate axis mapping");
74                }
75            }
76        }
77
78        Self { axis, sign }
79    }
80}
81
82/// An axis swapping converter
83#[derive(Debug, Clone, PartialEq)]
84pub struct AxisSwapConverter {
85    proj: Rc<RefCell<Proj>>,
86    /// The axis swapping guide
87    pub swap: AxisSwap,
88}
89impl ProjectCoordinates for AxisSwapConverter {
90    fn code(&self) -> i64 {
91        -1
92    }
93
94    fn name(&self) -> &'static str {
95        "axis swap"
96    }
97
98    fn names() -> &'static [&'static str] {
99        &["axis swap", "axis"]
100    }
101}
102impl From<AxisSwapConverter> for ProjectionTransform {
103    fn from(asc: AxisSwapConverter) -> ProjectionTransform {
104        ProjectionTransform {
105            proj: asc.proj.clone(),
106            method: Step::AxisSwap(asc.into()),
107            ..Default::default()
108        }
109    }
110}
111impl CoordinateStep for AxisSwapConverter {
112    fn new(proj: Rc<RefCell<Proj>>) -> Self {
113        {
114            let proj = &mut proj.borrow_mut();
115            proj.left = IoUnits::RADIANS;
116            proj.right = IoUnits::RADIANS;
117            proj.is_ll = true;
118        }
119        AxisSwapConverter { proj, swap: AxisSwap::default() }
120    }
121    /// Handle the axis swap
122    fn forward<P: TransformCoordinates>(&self, coords: &mut P) {
123        if self.swap.is_empty() {
124            return;
125        }
126
127        let mut out = [0.0; 4];
128        for (i, out) in out.iter_mut().enumerate() {
129            let src_idx = self.swap.axis[i] as usize;
130            let sign = self.swap.sign[i] as f64;
131            *out = coords.get(src_idx) * sign;
132        }
133
134        for (i, out) in out.iter_mut().enumerate() {
135            coords.set(i, *out);
136        }
137    }
138    /// Handle the axis swap
139    fn inverse<P: TransformCoordinates>(&self, coords: &mut P) {
140        if self.swap.is_empty() {
141            return;
142        }
143
144        let mut out = [0.0; 4];
145        for i in 0..4 {
146            let dst_idx = self.swap.axis[i] as usize;
147            let sign = self.swap.sign[i] as f64;
148            out[dst_idx] = coords.get(i) * sign;
149        }
150
151        for (i, out) in out.iter_mut().enumerate() {
152            coords.set(i, *out);
153        }
154    }
155}