gistools/proj/
internal.rs

1use super::{DatumParams, DatumType, ParameterValue};
2use crate::proj::{
3    AZIMUTH_PROJECTION_CENTRE, FALSE_EASTING, FALSE_NORTHING, LATITUDE_OF_FALSE_ORIGIN,
4    LATITUDE_OF_NATURAL_ORIGIN, LATITUDE_OF_PROJECTION_CENTRE, LONGITUDE_OF_FALSE_ORIGIN,
5    LONGITUDE_OF_NATURAL_ORIGIN, LONGITUDE_OF_PROJECTION_CENTRE, ProjValue,
6    SCALE_FACTOR_AT_NATURAL_ORIGIN, name_to_param_id,
7};
8use alloc::{collections::BTreeMap, string::String};
9use s2json::{GetXY, GetZ, NewXY, NewXYZ, SetXY, SetZ};
10
11/// A generic 4-dimensional point/vector
12#[repr(C)]
13#[derive(Debug, Default, Copy, Clone, PartialEq)]
14pub struct Coords(pub f64, pub f64, pub f64, pub f64);
15impl Coords {
16    /// Create a new Coords
17    pub fn new(x: f64, y: f64, z: f64, t: f64) -> Coords {
18        Coords(x, y, z, t)
19    }
20    /// Create a new Coords from xy
21    pub fn new_xy(x: f64, y: f64) -> Coords {
22        Coords(x, y, 0.0, 0.0)
23    }
24}
25impl GetXY for Coords {
26    fn x(&self) -> f64 {
27        self.0
28    }
29    fn y(&self) -> f64 {
30        self.1
31    }
32}
33impl GetZ for Coords {
34    fn z(&self) -> Option<f64> {
35        Some(self.2)
36    }
37}
38impl NewXY for Coords {
39    fn new_xy(x: f64, y: f64) -> Self {
40        Coords(x, y, 0.0, 0.0)
41    }
42}
43impl NewXYZ for Coords {
44    fn new_xyz(x: f64, y: f64, z: f64) -> Self {
45        Coords(x, y, z, 0.0)
46    }
47}
48impl SetXY for Coords {
49    fn set_x(&mut self, x: f64) {
50        self.0 = x;
51    }
52    fn set_y(&mut self, y: f64) {
53        self.1 = y;
54    }
55}
56impl SetZ for Coords {
57    fn set_z(&mut self, z: f64) {
58        self.2 = z;
59    }
60}
61
62/// A complex number container
63#[repr(C)]
64#[derive(Debug, Default, Copy, Clone, PartialEq)]
65pub struct Complex {
66    /// Real part
67    pub r: f64,
68    /// Imaginary part
69    pub i: f64,
70}
71
72/// Projection datum methods
73#[derive(Debug, Default, Clone, Copy, PartialEq)]
74pub enum ProjMethod {
75    /// Ellipsoidal
76    #[default]
77    Ellipsoidal = 0,
78    /// Spheroidal
79    Spheroidal = 1,
80}
81
82/// Airy projection modes
83#[derive(Debug, Default, Clone, Copy, PartialEq)]
84pub enum ProjMode {
85    /// North Pole
86    #[default]
87    NPole = 0,
88    /// South Pole
89    SPole = 1,
90    /// Equatorial
91    Equit = 2,
92    /// Oblique
93    Obliq = 3,
94}
95
96/// Generic Projection Container
97#[derive(Debug, Clone, PartialEq)]
98#[allow(non_snake_case)]
99pub struct Proj {
100    // PARAMETERS
101    /// The name of the projection
102    pub name: String,
103    /// Projection conversion params
104    pub params: BTreeMap<i64, ProjValue>,
105
106    // ELLIPSOID PARAMETERS
107
108    // The linear parameters
109    /// The name of the ellipsoid
110    pub ellps: String,
111    /// semimajor axis (radius if eccentricity==0)
112    pub a: f64,
113    /// semiminor axis
114    pub b: f64,
115    /// 1 / a
116    pub ra: f64,
117    /// 1 / b
118    pub rb: f64,
119    /// If the ellipsoid is a sphere
120    pub sphere: bool,
121    // The eccentricities
122    /// angular eccentricity
123    pub alpha: f64,
124    /// first eccentricity
125    pub e: f64,
126    /// first eccentricity squared
127    pub es: f64,
128    /// second eccentricity
129    pub e2: f64,
130    /// second eccentricity squared
131    pub e2s: f64,
132    /// third eccentricity
133    pub e3: f64,
134    /// third eccentricity squared
135    pub e3s: f64,
136    /// 1 - e^2
137    pub one_es: f64,
138    /// 1 / one_es
139    pub rone_es: f64,
140    // The flattenings
141    /// first flattening [the flattening of the ellipsoid]
142    pub f: f64,
143    /// second flattening
144    pub f2: f64,
145    /// third flattening
146    pub n: f64,
147    /// The inverse flattening (1/f) [the reverse flattening of the ellipsoid]
148    pub rf: f64,
149    /// 1/f2
150    pub rf2: f64,
151    /// 1/n
152    pub rn: f64,
153    /// This one's for GRS80 Datum (Dynamic form factor)
154    pub J: f64,
155
156    /// es and a before any +proj related adjustment
157    pub es_orig: f64,
158    /// a before any +proj related adjustment
159    pub a_orig: f64,
160
161    // COORDINATE HANDLING
162    /// Over-range flag
163    pub over: bool,
164    /// Geocentric latitude flag
165    pub geoc: bool,
166    /// proj=latlong ... not really a projection at all
167    pub is_ll: bool,
168    /// proj=geocent ... not really a projection at all
169    pub is_geocent: bool,
170    /// Left flag for input/output coordinate types
171    pub left: IoUnits,
172    /// Right flag for input/output coordinate types
173    pub right: IoUnits,
174
175    // CARTOGRAPHIC OFFSETS
176    /// central meridian
177    pub lam0: f64,
178    /// central parallel
179    pub phi0: f64,
180    /// false easting
181    pub x0: f64,
182    /// false northing
183    pub y0: f64,
184    /// height origin
185    pub z0: f64,
186    /// time origin
187    pub t0: f64,
188
189    // SCALING
190    /// General scaling factor - e.g. the 0.9996 of UTM
191    pub k0: f64,
192    /// Plane coordinate scaling TO meter
193    pub to_meter: f64,
194    /// Plane coordinate scaling FROM meter
195    pub fr_meter: f64,
196    /// Vertical scaling TO meter
197    pub vto_meter: f64,
198    /// Vertical scaling FROM meter
199    pub vfr_meter: f64,
200
201    // DATUMS AND HEIGHT SYSTEMS
202    /// Datum type (None, Param3, Param7, GridShift, WGS84)
203    pub datum_type: DatumType,
204    /// Parameters for 3PARAM and 7PARAM
205    pub datum_params: DatumParams,
206
207    /// prime meridian offset (in radians)
208    pub from_greenwich: f64,
209}
210impl Default for Proj {
211    fn default() -> Self {
212        Self {
213            name: "".into(),
214            params: BTreeMap::new(),
215            ellps: "".into(),
216            a: 0.,
217            b: 0.,
218            ra: 0.,
219            rb: 0.,
220            sphere: false,
221            alpha: 0.,
222            e: 0.,
223            es: 0.,
224            e2: 0.,
225            e2s: 0.,
226            e3: 0.,
227            e3s: 0.,
228            one_es: 0.,
229            rone_es: 0.,
230            f: 0.,
231            f2: 0.,
232            n: 0.,
233            rf: 0.,
234            rf2: 0.,
235            rn: 0.,
236            J: 0.,
237            es_orig: 0.,
238            a_orig: 0.,
239            over: false,
240            geoc: false,
241            is_ll: false,
242            is_geocent: false,
243            left: IoUnits::RADIANS,
244            right: IoUnits::CLASSIC,
245            lam0: 0.,
246            phi0: 0.,
247            x0: 0.,
248            y0: 0.,
249            z0: 0.,
250            t0: 0.,
251            k0: 1.,
252            to_meter: 1.,
253            fr_meter: 1.,
254            vto_meter: 1.,
255            vfr_meter: 1.,
256            datum_type: DatumType::NoDatum,
257            datum_params: DatumParams::default(),
258            from_greenwich: 0.,
259        }
260    }
261}
262impl Proj {
263    /// Add a parameter to the proj object
264    pub fn add_param(&mut self, param: &ParameterValue) {
265        // add via id
266        if let Some(id) = &param.id {
267            self.insert_param(id.code.i64(), param.into());
268        } else if !param.ids.is_empty() {
269            for id in &param.ids {
270                self.insert_param(id.code.i64(), param.into());
271            }
272        } else {
273            // add via name
274            self.insert_param(name_to_param_id(&param.name), param.into());
275        }
276    }
277    /// Set an f64 parameter
278    pub fn set_f64(&mut self, id: i64, value: f64) {
279        self.insert_param(id, value.into());
280    }
281    // /// Set a variable from user input (usually used by the API / TUI)
282    // pub fn set_var(&mut self, name: &str, value: &str) {
283    //     let name_id = name_to_param_id(name);
284    //     self.insert_param(name_id, value.into());
285    // }
286    /// Insert a param given code and value
287    fn insert_param(&mut self, id: i64, value: ProjValue) {
288        self.add_to_params(id, &value);
289        self.params.insert(id, value);
290    }
291    /// Apply directly to easy access params:
292    fn add_to_params(&mut self, id: i64, value: &ProjValue) {
293        match id {
294            LONGITUDE_OF_FALSE_ORIGIN
295            | LONGITUDE_OF_NATURAL_ORIGIN
296            | LONGITUDE_OF_PROJECTION_CENTRE => self.lam0 = value.f64(),
297            LATITUDE_OF_FALSE_ORIGIN
298            | LATITUDE_OF_NATURAL_ORIGIN
299            | LATITUDE_OF_PROJECTION_CENTRE => self.phi0 = value.f64(),
300            SCALE_FACTOR_AT_NATURAL_ORIGIN => self.k0 = value.f64(),
301            AZIMUTH_PROJECTION_CENTRE => self.alpha = value.f64(),
302            FALSE_EASTING => self.x0 = value.f64(),
303            FALSE_NORTHING => self.y0 = value.f64(),
304            _ => {}
305        }
306    }
307}
308
309/// Apply transformation to observation - in forward or inverse direction
310#[derive(Debug, Default, Copy, Clone, PartialEq)]
311pub enum Direction {
312    /// Forward
313    FWD = 1,
314    /// Do Nothing
315    #[default]
316    IDENT = 0,
317    /// Inverse
318    INV = -1,
319}
320
321/// IO Units Type
322#[derive(Debug, Default, Clone, PartialEq)]
323pub enum IoUnits {
324    /// Doesn't matter (or depends on pipeline neighbours)
325    #[default]
326    WHATEVER = 0,
327    /// Scaled meters (right), projected system
328    CLASSIC = 1,
329    /// Meters, projected system
330    PROJECTED = 2,
331    /// Meters, 3D cartesian system
332    CARTESIAN = 3,
333    /// Radians
334    RADIANS = 4,
335    /// Degrees
336    DEGREES = 5,
337}