gistools/proj/project/
bonne.rs1use crate::proj::{
2 BONNE, CoordinateStep, EPS10, LATITUDE_OF_FIRST_STANDARD_PARALLEL, Proj, ProjMethod,
3 ProjectCoordinates, TransformCoordinates, enfn, inv_mlfn, mlfn,
4};
5use alloc::{rc::Rc, vec::Vec};
6use core::{cell::RefCell, f64::consts::FRAC_PI_2};
7use libm::{atan2, copysign, cos, fabs, hypot, sin, sqrt, tan};
8
9#[derive(Debug, Default, Clone, PartialEq)]
11pub struct BonneData {
12 phi1: f64,
13 cphi1: f64,
14 am1: f64,
15 m1: f64,
16 en: Vec<f64>,
17}
18
19#[derive(Debug, Clone, PartialEq)]
52pub struct BonneProjection {
53 proj: Rc<RefCell<Proj>>,
54 store: RefCell<BonneData>,
55 method: ProjMethod,
56}
57impl ProjectCoordinates for BonneProjection {
58 fn code(&self) -> i64 {
59 BONNE
60 }
61 fn name(&self) -> &'static str {
62 "Bonne"
63 }
64 fn names() -> &'static [&'static str] {
65 &["Bonne (Werner lat_1=90)", "bonne_werner", "Bonne", "bonne"]
66 }
67}
68impl CoordinateStep for BonneProjection {
69 fn new(proj: Rc<RefCell<Proj>>) -> Self {
70 let mut store = BonneData::default();
71 let method: ProjMethod;
72 {
73 let proj = &mut proj.borrow_mut();
74 store.phi1 = proj.params.get(&LATITUDE_OF_FIRST_STANDARD_PARALLEL).unwrap().f64();
75 if fabs(store.phi1) < EPS10 {
76 panic!("Invalid value for lat_1: |lat_1| should be > 0");
77 }
78
79 method = if proj.es != 0.0 {
80 store.en = enfn(proj.n);
81 store.am1 = sin(store.phi1);
82 let c = cos(store.phi1);
83 store.m1 = mlfn(store.phi1, store.am1, c, &store.en);
84 store.am1 = c / (sqrt(1. - proj.es * store.am1 * store.am1) * store.am1);
85 ProjMethod::Ellipsoidal
86 } else {
87 if fabs(store.phi1) + EPS10 >= FRAC_PI_2 {
88 store.cphi1 = 0.;
89 } else {
90 store.cphi1 = 1. / tan(store.phi1);
91 }
92 ProjMethod::Spheroidal
93 };
94 }
95 BonneProjection { proj, store: store.into(), method }
96 }
97 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
98 if self.method == ProjMethod::Ellipsoidal {
99 bonne_e_forward(&mut self.store.borrow_mut(), &self.proj.borrow(), p);
100 } else {
101 bonne_s_forward(&mut self.store.borrow_mut(), &self.proj.borrow(), p);
102 }
103 }
104 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
105 if self.method == ProjMethod::Ellipsoidal {
106 bonne_e_inverse(&mut self.store.borrow_mut(), &self.proj.borrow(), p);
107 } else {
108 bonne_s_inverse(&mut self.store.borrow_mut(), &self.proj.borrow(), p);
109 }
110 }
111}
112
113pub fn bonne_e_forward<P: TransformCoordinates>(bonne: &mut BonneData, proj: &Proj, p: &mut P) {
115 let mut e = sin(p.phi());
116 let c = cos(p.phi());
117 let rh = bonne.am1 + bonne.m1 - mlfn(p.phi(), e, c, &bonne.en);
118 if fabs(rh) > EPS10 {
119 e = c * p.lam() / (rh * sqrt(1. - proj.es * e * e));
120 p.set_x(rh * sin(e));
121 p.set_y(bonne.am1 - rh * cos(e));
122 } else {
123 p.set_x(0.);
124 p.set_y(0.);
125 }
126}
127
128pub fn bonne_s_forward<P: TransformCoordinates>(bonne: &mut BonneData, _proj: &Proj, p: &mut P) {
130 let rh = bonne.cphi1 + bonne.phi1 - p.phi();
131 if fabs(rh) > EPS10 {
132 let e = p.lam() * cos(p.phi()) / rh;
133 p.set_x(rh * sin(e));
134 p.set_y(bonne.cphi1 - rh * cos(e));
135 } else {
136 p.set_x(0.);
137 p.set_y(0.);
138 }
139}
140
141pub fn bonne_s_inverse<P: TransformCoordinates>(bonne: &mut BonneData, _proj: &Proj, p: &mut P) {
143 p.set_y(bonne.cphi1 - p.y());
144 let rh = copysign(hypot(p.x(), p.y()), bonne.phi1);
145 let phi = bonne.cphi1 + bonne.phi1 - rh;
146 let lam: f64;
147 let abs_phi = fabs(phi);
148 if abs_phi > FRAC_PI_2 {
149 panic!("Coordinate outside projection domain");
150 }
151 if FRAC_PI_2 - abs_phi <= EPS10 {
152 lam = 0.;
153 } else {
154 let lm = rh / cos(phi);
155 if bonne.phi1 > 0. {
156 lam = lm * atan2(p.x(), p.y());
157 } else {
158 lam = lm * atan2(-p.x(), -p.y());
159 }
160 }
161
162 p.set_phi(phi);
163 p.set_lam(lam);
164}
165
166pub fn bonne_e_inverse<P: TransformCoordinates>(bonne: &mut BonneData, proj: &Proj, p: &mut P) {
168 p.set_y(bonne.am1 - p.y());
169 let rh = copysign(hypot(p.x(), p.y()), bonne.phi1);
170 let phi = inv_mlfn(bonne.am1 + bonne.m1 - rh, &bonne.en);
171 let lam: f64;
172 let abs_phi = fabs(phi);
173 if abs_phi < FRAC_PI_2 {
174 let sinphi = sin(phi);
175 let lm = rh * sqrt(1. - proj.es * sinphi * sinphi) / cos(phi);
176 if bonne.phi1 > 0. {
177 lam = lm * atan2(p.x(), p.y());
178 } else {
179 lam = lm * atan2(-p.x(), -p.y());
180 }
181 } else if abs_phi - FRAC_PI_2 <= EPS10 {
182 lam = 0.;
183 } else {
184 panic!("Coordinates are outside the projection domain");
185 }
186 p.set_phi(phi);
187 p.set_lam(lam);
188}