gistools/proj/project/
somerc.rs1use crate::proj::{CoordinateStep, Proj, ProjectCoordinates, SOMERC, TransformCoordinates, aasin};
2use alloc::rc::Rc;
3use core::{
4 cell::RefCell,
5 f64::consts::{FRAC_PI_2, FRAC_PI_4},
6};
7use libm::{atan, cos, exp, fabs, log, sin, sqrt, tan};
8
9#[derive(Debug, Default, Clone, PartialEq)]
11pub struct SomercData {
12 k: f64,
13 c: f64,
14 hlf_e: f64,
15 k_r: f64,
16 cosp0: f64,
17 sinp0: f64,
18}
19
20const EPS: f64 = 1.0e-10;
21const NITER: usize = 6;
22
23#[derive(Debug, Clone, PartialEq)]
64pub struct SwissOblMercatorProjection {
65 proj: Rc<RefCell<Proj>>,
66 store: RefCell<SomercData>,
67}
68impl ProjectCoordinates for SwissOblMercatorProjection {
69 fn code(&self) -> i64 {
70 SOMERC
71 }
72 fn name(&self) -> &'static str {
73 "Swiss. Obl. Mercator"
74 }
75 fn names() -> &'static [&'static str] {
76 &["Swiss. Obl. Mercator", "somerc"]
77 }
78}
79impl CoordinateStep for SwissOblMercatorProjection {
80 fn new(proj: Rc<RefCell<Proj>>) -> Self {
81 let mut store = SomercData::default();
82 {
83 let proj = &mut proj.borrow_mut();
84 store.hlf_e = 0.5 * proj.e;
85 let mut cp = cos(proj.phi0);
86 cp *= cp;
87 store.c = sqrt(1. + proj.es * cp * cp * proj.rone_es);
88 let mut sp = sin(proj.phi0);
89 store.sinp0 = sp / store.c;
90 let phip0 = aasin(store.sinp0);
91 store.cosp0 = cos(phip0);
92 sp *= proj.e;
93 store.k = log(tan(FRAC_PI_4 + 0.5 * phip0))
94 - store.c
95 * (log(tan(FRAC_PI_4 + 0.5 * proj.phi0))
96 - store.hlf_e * log((1. + sp) / (1. - sp)));
97 store.k_r = proj.k0 * sqrt(proj.one_es) / (1. - sp * sp);
98 }
99 SwissOblMercatorProjection { proj, store: store.into() }
100 }
101 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
102 somerc_e_forward(&self.store.borrow(), &self.proj.borrow(), p);
103 }
104 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
105 somerc_e_inverse(&self.store.borrow(), &self.proj.borrow(), p);
106 }
107}
108
109pub fn somerc_e_forward<P: TransformCoordinates>(somerc: &SomercData, proj: &Proj, p: &mut P) {
111 let sp = proj.e * sin(p.phi());
112 let phip = 2.
113 * atan(exp(somerc.c
114 * (log(tan(FRAC_PI_4 + 0.5 * p.phi())) - somerc.hlf_e * log((1. + sp) / (1. - sp)))
115 + somerc.k))
116 - FRAC_PI_2;
117 let lamp = somerc.c * p.lam();
118 let cp = cos(phip);
119 let phipp = aasin(somerc.cosp0 * sin(phip) - somerc.sinp0 * cp * cos(lamp));
120 let lampp = aasin(cp * sin(lamp) / cos(phipp));
121 p.set_x(somerc.k_r * lampp);
122 p.set_y(somerc.k_r * log(tan(FRAC_PI_4 + 0.5 * phipp)));
123}
124
125pub fn somerc_e_inverse<P: TransformCoordinates>(somerc: &SomercData, proj: &Proj, p: &mut P) {
127 let phipp = 2. * (atan(exp(p.y() / somerc.k_r)) - FRAC_PI_4);
128 let lampp = p.x() / somerc.k_r;
129 let cp = cos(phipp);
130 let mut phip = aasin(somerc.cosp0 * sin(phipp) + somerc.sinp0 * cp * cos(lampp));
131 let lamp = aasin(cp * sin(lampp) / cos(phip));
132 let con = (somerc.k - log(tan(FRAC_PI_4 + 0.5 * phip))) / somerc.c;
133 let mut i = NITER;
134 while i > 0 {
135 let esp = proj.e * sin(phip);
136 let delp = (con + log(tan(FRAC_PI_4 + 0.5 * phip))
137 - somerc.hlf_e * log((1. + esp) / (1. - esp)))
138 * (1. - esp * esp)
139 * cos(phip)
140 * proj.rone_es;
141 phip -= delp;
142 if fabs(delp) < EPS {
143 break;
144 }
145 i -= 1;
146 }
147 if i != 0 {
148 p.set_phi(phip);
149 p.set_lam(lamp / somerc.c);
150 } else {
151 panic!("Coordinate outside projection domain");
152 }
153}