1use crate::proj::{
2 ALGO, APPROX, AuxLat, CoordinateStep, EPS10, Proj, ProjValue, ProjectCoordinates, SOUTH,
3 TRANSVERSE_MERCATOR, TRANSVERSE_MERCATOR_SOUTH_ORIENTATED, TransformCoordinates, ZONE, adjlon,
4 auxlat_coeffs, auxlat_convert, auxlat_convert_mid, enfn, inv_mlfn, mlfn, rectifying_radius,
5};
6use alloc::{rc::Rc, vec::Vec};
7use core::{
8 cell::RefCell,
9 f64::consts::{FRAC_PI_2, PI},
10};
11use libm::{
12 acos, asin, asinh, atan2, copysign, cos, exp, fabs, floor, hypot, log, round, sin, sinh, sqrt,
13};
14
15#[derive(Debug, Default, Clone, PartialEq)]
27pub enum TMercAlgo {
28 Auto,
30 EvendenSnyder,
32 #[default]
34 PoderEngsager,
35}
36
37#[derive(Debug, Default, Clone, PartialEq)]
39pub struct EvendenSnyder {
40 esp: f64,
41 ml0: f64,
42 en: Vec<f64>,
43}
44
45#[derive(Debug, Default, Clone, PartialEq)]
47pub struct PoderEngsager {
48 qn: f64,
50 zb: f64,
52 cgb: [f64; 6],
54 cbg: [f64; 6],
56 utg: [f64; 6],
58 gtu: [f64; 6],
60}
61
62#[derive(Debug, Default, Clone, PartialEq)]
64pub struct TmercData {
65 approx: EvendenSnyder,
66 exact: PoderEngsager,
67}
68
69#[derive(Debug, Default, Clone, PartialEq)]
71pub enum TMercMode {
72 #[default]
74 Spherical,
75 ApproxEllipsoidal,
77 ExactEllipsoidal,
79 AutoEllipsoidal,
81}
82
83const FC1: f64 = 1.0;
85const FC2: f64 = 0.5;
86const FC3: f64 = 0.166_666_666_666_666_66;
87const FC4: f64 = 0.083_333_333_333_333_33;
88const FC5: f64 = 0.05;
89const FC6: f64 = 0.033_333_333_333_333_33;
90const FC7: f64 = 0.023_809_523_809_523_808;
91const FC8: f64 = 0.017_857_142_857_142_856;
92
93const PROJ_ETMERC_ORDER: i32 = 6;
95
96pub fn tmerc_approx_e_fwd<P: TransformCoordinates>(tmerc: &mut TmercData, proj: &Proj, p: &mut P) {
98 let evenden_snyder = &tmerc.approx;
99 if p.lam() < -FRAC_PI_2 || p.lam() > FRAC_PI_2 {
105 panic!("Longitude out of range");
106 }
107
108 let sinphi = sin(p.phi());
109 let cosphi = cos(p.phi());
110 let mut t = if fabs(cosphi) > 1e-10 { sinphi / cosphi } else { 0. };
111 t *= t;
112 let mut al = cosphi * p.lam();
113 let als = al * al;
114 al /= sqrt(1. - proj.es * sinphi * sinphi);
115 let n = evenden_snyder.esp * cosphi * cosphi;
116 p.set_x(
117 proj.k0
118 * al
119 * (FC1
120 + FC3
121 * als
122 * (1. - t
123 + n
124 + FC5
125 * als
126 * (5.
127 + t * (t - 18.)
128 + n * (14. - 58. * t)
129 + FC7 * als * (61. + t * (t * (179. - t) - 479.))))),
130 );
131 p.set_y(
132 proj.k0
133 * (mlfn(p.phi(), sinphi, cosphi, &evenden_snyder.en) - evenden_snyder.ml0
134 + sinphi
135 * al
136 * p.lam()
137 * FC2
138 * (1.
139 + FC4
140 * als
141 * (5. - t
142 + n * (9. + 4. * n)
143 + FC6
144 * als
145 * (61.
146 + t * (t - 58.)
147 + n * (270. - 330. * t)
148 + FC8 * als * (1385. + t * (t * (543. - t) - 3111.)))))),
149 );
150}
151
152pub fn tmerc_spherical_fwd<P: TransformCoordinates>(tmerc: &mut TmercData, proj: &Proj, p: &mut P) {
154 let evenden_snyder = &tmerc.approx;
155
156 let cosphi = cos(p.phi());
157 let mut b = cosphi * sin(p.lam());
158 if fabs(fabs(b) - 1.) <= EPS10 {
159 panic!("Coordinate outside projection domain");
160 }
161
162 let x = evenden_snyder.ml0 * log((1. + b) / (1. - b));
163 let mut y = cosphi * cos(p.lam()) / sqrt(1. - b * b);
164
165 b = fabs(y);
166 if cosphi == 1. && (p.lam() < -FRAC_PI_2 || p.lam() > FRAC_PI_2) {
167 y = PI;
170 } else if b >= 1. {
171 if (b - 1.) > EPS10 {
172 panic!("Coordinate outside projection domain");
173 } else {
174 y = 0.;
175 }
176 } else {
177 y = acos(y);
178 }
179
180 if p.phi() < 0. {
181 y = -y;
182 }
183 y = evenden_snyder.esp * (y - proj.phi0);
184
185 p.set_x(x);
186 p.set_y(y);
187}
188
189pub fn tmerc_approx_e_inv<P: TransformCoordinates>(tmerc: &mut TmercData, proj: &Proj, p: &mut P) {
191 let evenden_snyder = &tmerc.approx;
192
193 let mut phi = inv_mlfn(evenden_snyder.ml0 + p.y() / proj.k0, &evenden_snyder.en);
194 let lam: f64;
195 if fabs(phi) >= FRAC_PI_2 {
196 phi = if p.y() < 0. { -FRAC_PI_2 } else { FRAC_PI_2 };
197 lam = 0.;
198 } else {
199 let sinphi = sin(phi);
200 let cosphi = cos(phi);
201 let mut t = if fabs(cosphi) > 1e-10 { sinphi / cosphi } else { 0. };
202 let n = evenden_snyder.esp * cosphi * cosphi;
203 let mut con = 1. - proj.es * sinphi * sinphi;
204 let d = p.x() * sqrt(con) / proj.k0;
205 con *= t;
206 t *= t;
207 let ds = d * d;
208 phi -= (con * ds / (1. - proj.es))
209 * FC2
210 * (1.
211 - ds * FC4
212 * (5. + t * (3. - 9. * n) + n * (1. - 4. * n)
213 - ds * FC6
214 * (61. + t * (90. - 252. * n + 45. * t) + 46. * n
215 - ds * FC8 * (1385. + t * (3633. + t * (4095. + 1575. * t))))));
216 lam = d
217 * (FC1
218 - ds * FC3
219 * (1. + 2. * t + n
220 - ds * FC5
221 * (5. + t * (28. + 24. * t + 8. * n) + 6. * n
222 - ds * FC7 * (61. + t * (662. + t * (1320. + 720. * t))))))
223 / cosphi;
224 }
225 p.set_phi(phi);
226 p.set_lam(lam);
227}
228
229pub fn tmerc_spherical_inv<P: TransformCoordinates>(tmerc: &mut TmercData, proj: &Proj, p: &mut P) {
231 let evenden_snyder = &tmerc.approx;
232 let mut h = exp(p.x() / evenden_snyder.esp);
233 if h == 0. {
234 panic!("Coordinate outside projection domain");
235 }
236 let g = 0.5 * (h - 1. / h);
237 let d = proj.phi0 + p.y() / evenden_snyder.esp;
239 h = cos(d);
240 p.set_phi(asin(sqrt((1. - h * h) / (1. + g * g))));
241 p.set_phi(copysign(p.phi(), d));
243 p.set_lam(if g != 0.0 || h != 0.0 { atan2(g, h) } else { 0. });
244}
245
246fn setup_approx(tmerc: &mut TmercData, proj: &Proj) {
247 let evenden_snyder = &mut tmerc.approx;
248
249 if proj.es != 0.0 {
250 evenden_snyder.en = enfn(proj.n);
251 if evenden_snyder.en.is_empty() {
252 panic!("Projection setup failed");
253 }
254
255 evenden_snyder.ml0 = mlfn(proj.phi0, sin(proj.phi0), cos(proj.phi0), &evenden_snyder.en);
256 evenden_snyder.esp = proj.es / (1. - proj.es);
257 } else {
258 evenden_snyder.esp = proj.k0;
259 evenden_snyder.ml0 = 0.5 * evenden_snyder.esp;
260 }
261}
262
263#[inline]
279fn clen_s(
280 a: &[f64],
281 sin_arg_r: f64,
282 cos_arg_r: f64,
283 sinh_arg_i: f64,
284 cosh_arg_i: f64,
285) -> (f64, f64) {
286 let mut r: f64;
287 let mut i: f64;
288 let mut hr: f64;
289 let mut hr1: f64 = 0.0;
290 let mut hr2: f64;
291 let mut hi: f64 = 0.0;
292 let mut hi1: f64 = 0.0;
293 let mut hi2: f64;
294
295 let mut p = a.len();
297 r = 2.0 * cos_arg_r * cosh_arg_i;
298 i = -2.0 * sin_arg_r * sinh_arg_i;
299
300 p -= 1;
302 hr = a[p];
303 while p > 0 {
304 p -= 1;
305 hr2 = hr1;
306 hi2 = hi1;
307 hr1 = hr;
308 hi1 = hi;
309 hr = -hr2 + r * hr1 - i * hi1 + a[p];
310 hi = -hi2 + i * hr1 + r * hi1;
311 }
312
313 r = sin_arg_r * cosh_arg_i;
314 i = cos_arg_r * sinh_arg_i;
315 let real = r * hr - i * hi;
316 let imag = r * hi + i * hr;
317 (real, imag)
318}
319
320pub fn tmerc_exact_e_fwd<P: TransformCoordinates>(tmerc: &mut TmercData, p: &mut P) {
322 let poder_engsager = &tmerc.exact;
323
324 let mut cn = auxlat_convert(p.phi(), &poder_engsager.cbg, PROJ_ETMERC_ORDER);
326 let sin_cn = sin(cn);
328 let cos_cn = cos(cn);
329 let sin_ce = sin(p.lam());
330 let cos_ce = cos(p.lam());
331
332 let cos_cn_cos_ce = cos_cn * cos_ce;
333 cn = atan2(sin_cn, cos_cn_cos_ce);
334
335 let inv_denom_tan_ce = 1. / hypot(sin_cn, cos_cn_cos_ce);
336 let tan_ce = sin_ce * cos_cn * inv_denom_tan_ce;
337 let mut ce = asinh(tan_ce); let two_inv_denom_tan_ce = 2. * inv_denom_tan_ce;
357 let two_inv_denom_tan_ce_square = two_inv_denom_tan_ce * inv_denom_tan_ce;
358 let tmp_r = cos_cn_cos_ce * two_inv_denom_tan_ce_square;
359 let sin_arg_r = sin_cn * tmp_r;
360 let cos_arg_r = cos_cn_cos_ce * tmp_r - 1.;
361
362 let sinh_arg_i = tan_ce * two_inv_denom_tan_ce;
381 let cosh_arg_i = two_inv_denom_tan_ce_square - 1.;
382 let (d_cn, d_ce) = clen_s(&poder_engsager.gtu, sin_arg_r, cos_arg_r, sinh_arg_i, cosh_arg_i);
383 cn += d_cn;
384 ce += d_ce;
385 if fabs(ce) <= 2.623395162778 {
386 p.set_y(poder_engsager.qn * cn + poder_engsager.zb);
388 p.set_x(poder_engsager.qn * ce);
390 } else {
391 panic!("Coordinate outside projection domain");
392 }
393}
394
395pub fn tmerc_exact_e_inv<P: TransformCoordinates>(tmerc: &mut TmercData, p: &mut P) {
397 let poder_engsager = &tmerc.exact;
398
399 let mut cn = (p.y() - poder_engsager.zb) / poder_engsager.qn;
401 let mut ce = p.x() / poder_engsager.qn;
402
403 if fabs(ce) <= 2.623395162778 {
404 let sin_arg_r = sin(2. * cn);
407 let cos_arg_r = cos(2. * cn);
408
409 let exp_2_ce = exp(2. * ce);
412 let half_inv_exp_2_ce = 0.5 / exp_2_ce;
413 let sinh_arg_i = 0.5 * exp_2_ce - half_inv_exp_2_ce;
414 let cosh_arg_i = 0.5 * exp_2_ce + half_inv_exp_2_ce;
415 let (d_cn, d_ce) =
416 clen_s(&poder_engsager.utg, sin_arg_r, cos_arg_r, sinh_arg_i, cosh_arg_i);
417 cn += d_cn;
418 ce += d_ce;
419
420 let sin_cn = sin(cn);
422 let cos_cn = cos(cn);
423
424 let sinh_ce = sinh(ce);
444 ce = atan2(sinh_ce, cos_cn);
445 let modulus_ce = hypot(sinh_ce, cos_cn);
446 let rr = hypot(sin_cn, modulus_ce);
447 cn = atan2(sin_cn, modulus_ce);
448 p.set_phi(auxlat_convert_mid(
452 cn,
453 sin_cn / rr,
454 modulus_ce / rr,
455 &poder_engsager.cgb,
456 PROJ_ETMERC_ORDER,
457 ));
458 p.set_lam(ce);
459 } else {
460 panic!("Coordinate outside projection domain");
461 }
462}
463
464fn setup_exact(tmerc: &mut TmercData, proj: &Proj) {
465 let poder_engsager = &mut tmerc.exact;
466 assert!(proj.es > 0., "Eccentricity must be greater than zero");
467 assert!(PROJ_ETMERC_ORDER == AuxLat::ORDER as i32, "Inconsistent orders etmerc vs auxorder");
468 let n = proj.n;
470
471 auxlat_coeffs(n, AuxLat::CONFORMAL, AuxLat::GEOGRAPHIC, &mut poder_engsager.cgb);
481 auxlat_coeffs(n, AuxLat::GEOGRAPHIC, AuxLat::CONFORMAL, &mut poder_engsager.cbg);
482 poder_engsager.qn = proj.k0 * rectifying_radius(n);
486 auxlat_coeffs(n, AuxLat::RECTIFYING, AuxLat::CONFORMAL, &mut poder_engsager.utg);
490 auxlat_coeffs(n, AuxLat::CONFORMAL, AuxLat::RECTIFYING, &mut poder_engsager.gtu);
491 let z = auxlat_convert(proj.phi0, &poder_engsager.cbg, PROJ_ETMERC_ORDER);
493
494 poder_engsager.zb =
497 -poder_engsager.qn * auxlat_convert(z, &poder_engsager.gtu, PROJ_ETMERC_ORDER);
498}
499
500pub fn tmerc_auto_e_fwd<P: TransformCoordinates>(tmerc: &mut TmercData, proj: &Proj, p: &mut P) {
502 if fabs(p.lam()) > 3.0_f64.to_radians() {
503 tmerc_exact_e_fwd(tmerc, p);
504 } else {
505 tmerc_approx_e_fwd(tmerc, proj, p);
506 }
507}
508
509pub fn tmerc_auto_e_inv<P: TransformCoordinates>(tmerc: &mut TmercData, proj: &Proj, p: &mut P) {
511 if fabs(p.x()) > 0.053 - 0.022 * p.y() * p.y() {
518 tmerc_exact_e_inv(tmerc, p);
519 } else {
520 tmerc_approx_e_inv(tmerc, proj, p);
521 }
522}
523
524fn get_algo_from_params(proj: &Proj, algo: &mut TMercAlgo) -> bool {
525 let approx = proj.params.get(&APPROX).unwrap_or(&ProjValue::default()).bool();
526 if approx {
527 *algo = TMercAlgo::EvendenSnyder;
528 return true;
529 }
530
531 let algo_str = proj.params.get(&ALGO).unwrap_or(&ProjValue::default()).string();
532 if !algo_str.is_empty() {
533 if algo_str == "evenden_snyder" {
534 *algo = TMercAlgo::EvendenSnyder;
535 return true;
536 }
537 if algo_str == "poder_engsager" {
538 *algo = TMercAlgo::PoderEngsager;
539 return true;
540 }
541 if algo_str == "auto" {
542 *algo = TMercAlgo::Auto;
543 } else {
545 panic!("unknown value for +algo");
546 }
547 }
548
549 if *algo == TMercAlgo::Auto && (proj.es > 0.1 || proj.phi0 != 0. || fabs(proj.k0 - 1.) > 0.01) {
553 *algo = TMercAlgo::PoderEngsager;
554 }
555
556 true
557}
558
559fn setup(proj: &mut Proj, e_alg: &mut TMercAlgo) -> (TmercData, TMercMode) {
560 let mut tmerc = TmercData::default();
561
562 if proj.es == 0. {
563 *e_alg = TMercAlgo::EvendenSnyder;
564 }
565 let mode = match *e_alg {
566 TMercAlgo::EvendenSnyder => {
567 setup_approx(&mut tmerc, proj);
568 if proj.es == 0. { TMercMode::Spherical } else { TMercMode::ApproxEllipsoidal }
569 }
570 TMercAlgo::PoderEngsager => {
571 setup_exact(&mut tmerc, proj);
572 TMercMode::ExactEllipsoidal
573 }
574 TMercAlgo::Auto => {
575 setup_approx(&mut tmerc, proj);
576 setup_exact(&mut tmerc, proj);
577 TMercMode::AutoEllipsoidal
578 }
579 };
580
581 (tmerc, mode)
582}
583
584pub type TransverseMercatorProjection = TransverseMercatorBaseProjection<TRANSVERSE_MERCATOR>;
588pub type TransverseMercatorSouthOrientedProjection =
592 TransverseMercatorBaseProjection<TRANSVERSE_MERCATOR_SOUTH_ORIENTATED>;
593
594#[derive(Debug, Clone, PartialEq)]
632pub struct TransverseMercatorBaseProjection<const C: i64> {
633 proj: Rc<RefCell<Proj>>,
634 mode: TMercMode,
635 store: RefCell<TmercData>,
636 algo: RefCell<TMercAlgo>,
637}
638impl<const C: i64> ProjectCoordinates for TransverseMercatorBaseProjection<C> {
639 fn code(&self) -> i64 {
640 C
641 }
642 fn name(&self) -> &'static str {
643 "Transverse Mercator"
644 }
645 fn names() -> &'static [&'static str] {
646 &[
647 "Transverse Mercator",
648 "Transverse_Mercator",
649 "Transverse Mercator (South Oriented)",
650 "tmerc",
651 ]
652 }
653}
654impl<const C: i64> CoordinateStep for TransverseMercatorBaseProjection<C> {
655 fn new(proj: Rc<RefCell<Proj>>) -> Self {
656 let mut algo = TMercAlgo::default();
657 get_algo_from_params(&proj.borrow(), &mut algo);
658 let (store, mode) = setup(&mut proj.borrow_mut(), &mut algo);
659 TransverseMercatorBaseProjection { proj, mode, store: store.into(), algo: algo.into() }
660 }
661 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
662 match self.mode {
663 TMercMode::Spherical => {
664 tmerc_spherical_fwd(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
665 }
666 TMercMode::ApproxEllipsoidal => {
667 tmerc_approx_e_fwd(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
668 }
669 TMercMode::ExactEllipsoidal => tmerc_exact_e_fwd(&mut self.store.borrow_mut(), p),
670 TMercMode::AutoEllipsoidal => {
671 tmerc_auto_e_fwd(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
672 }
673 }
674 }
675 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
676 match self.mode {
677 TMercMode::Spherical => {
678 tmerc_spherical_inv(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
679 }
680 TMercMode::ApproxEllipsoidal => {
681 tmerc_approx_e_inv(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
682 }
683 TMercMode::ExactEllipsoidal => tmerc_exact_e_inv(&mut self.store.borrow_mut(), p),
684 TMercMode::AutoEllipsoidal => {
685 tmerc_auto_e_inv(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
686 }
687 }
688 }
689}
690
691#[derive(Debug, Clone, PartialEq)]
725pub struct ExtendedTransverseMercatorProjection {
726 proj: Rc<RefCell<Proj>>,
727 mode: TMercMode,
728 store: RefCell<TmercData>,
729 algo: RefCell<TMercAlgo>,
730}
731impl ProjectCoordinates for ExtendedTransverseMercatorProjection {
732 fn code(&self) -> i64 {
733 -1
734 }
735 fn name(&self) -> &'static str {
736 "Extended Transverse Mercator"
737 }
738 fn names() -> &'static [&'static str] {
739 &["Extended Transverse Mercator", "Extended_Transverse_Mercator", "etmerc"]
740 }
741}
742impl CoordinateStep for ExtendedTransverseMercatorProjection {
743 fn new(proj: Rc<RefCell<Proj>>) -> Self {
744 if proj.borrow().es == 0.0 {
745 panic!("Invalid value for eccentricity: it should not be zero");
746 }
747 let mut algo = TMercAlgo::PoderEngsager;
748 get_algo_from_params(&proj.borrow(), &mut algo);
749 let (store, mode) = setup(&mut proj.borrow_mut(), &mut algo);
750 ExtendedTransverseMercatorProjection { proj, mode, store: store.into(), algo: algo.into() }
751 }
752 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
753 match self.mode {
754 TMercMode::Spherical => {
755 tmerc_spherical_fwd(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
756 }
757 TMercMode::ApproxEllipsoidal => {
758 tmerc_approx_e_fwd(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
759 }
760 TMercMode::ExactEllipsoidal => tmerc_exact_e_fwd(&mut self.store.borrow_mut(), p),
761 TMercMode::AutoEllipsoidal => {
762 tmerc_auto_e_fwd(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
763 }
764 }
765 }
766 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
767 match self.mode {
768 TMercMode::Spherical => {
769 tmerc_spherical_inv(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
770 }
771 TMercMode::ApproxEllipsoidal => {
772 tmerc_approx_e_inv(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
773 }
774 TMercMode::ExactEllipsoidal => tmerc_exact_e_inv(&mut self.store.borrow_mut(), p),
775 TMercMode::AutoEllipsoidal => {
776 tmerc_auto_e_inv(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
777 }
778 }
779 }
780}
781
782#[derive(Debug, Clone, PartialEq)]
841pub struct UniversalTransverseMercatorProjection {
842 proj: Rc<RefCell<Proj>>,
843 mode: TMercMode,
844 store: RefCell<TmercData>,
845 algo: RefCell<TMercAlgo>,
846}
847impl ProjectCoordinates for UniversalTransverseMercatorProjection {
848 fn code(&self) -> i64 {
849 -1
850 }
851 fn name(&self) -> &'static str {
852 "Universal Transverse Mercator"
853 }
854 fn names() -> &'static [&'static str] {
855 &["Universal Transverse Mercator", "Universal Transverse Mercator (UTM)", "utm"]
856 }
857}
858impl CoordinateStep for UniversalTransverseMercatorProjection {
859 fn new(proj: Rc<RefCell<Proj>>) -> Self {
860 {
861 let proj = &mut proj.borrow_mut();
862 if proj.es == 0.0 {
863 panic!("Invalid value for eccentricity: it should not be zero");
864 }
865
866 if proj.lam0 < -1000.0 || proj.lam0 > 1000.0 {
867 panic!("Invalid value for lon_0");
868 }
869
870 if proj.params.contains_key(&SOUTH) {
871 proj.y0 = 10000000.
872 } else {
873 proj.y0 = 0.
874 }
875 proj.x0 = 500000.;
876 let zone = if let Some(zone) = proj.params.get(&ZONE) {
878 let mut zone = zone.i64();
879 if zone > 0 && zone <= 60 {
880 zone -= 1;
881 } else {
882 panic!("Invalid value for zone");
883 }
884 zone
885 } else {
886 let mut zone = round(floor((adjlon(proj.lam0) + PI) * 30. / PI)) as i64;
888 if zone < 0 {
889 zone = 0;
890 } else if zone >= 60 {
891 zone = 59;
892 }
893 zone
894 };
895 proj.lam0 = ((zone as f64) + 0.5) * PI / 30. - PI;
896 proj.k0 = 0.9996;
897 proj.phi0 = 0.;
898 }
899 let mut algo = TMercAlgo::PoderEngsager;
900 get_algo_from_params(&proj.borrow(), &mut algo);
901 let (store, mode) = setup(&mut proj.borrow_mut(), &mut algo);
902 UniversalTransverseMercatorProjection { proj, mode, store: store.into(), algo: algo.into() }
903 }
904 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
905 match self.mode {
906 TMercMode::Spherical => {
907 tmerc_spherical_fwd(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
908 }
909 TMercMode::ApproxEllipsoidal => {
910 tmerc_approx_e_fwd(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
911 }
912 TMercMode::ExactEllipsoidal => tmerc_exact_e_fwd(&mut self.store.borrow_mut(), p),
913 TMercMode::AutoEllipsoidal => {
914 tmerc_auto_e_fwd(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
915 }
916 }
917 }
918 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
919 match self.mode {
920 TMercMode::Spherical => {
921 tmerc_spherical_inv(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
922 }
923 TMercMode::ApproxEllipsoidal => {
924 tmerc_approx_e_inv(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
925 }
926 TMercMode::ExactEllipsoidal => tmerc_exact_e_inv(&mut self.store.borrow_mut(), p),
927 TMercMode::AutoEllipsoidal => {
928 tmerc_auto_e_inv(&mut self.store.borrow_mut(), &self.proj.borrow(), p)
929 }
930 }
931 }
932}