gistools/proj/project/
moll.rs1use crate::proj::{CoordinateStep, Proj, ProjectCoordinates, TransformCoordinates, aasin};
2use alloc::rc::Rc;
3use core::{
4 cell::RefCell,
5 f64::consts::{FRAC_PI_2, PI, TAU},
6};
7use libm::{cos, fabs, sin, sqrt};
8
9const MAX_ITER: usize = 30;
10const LOOP_TOL: f64 = 1e-7;
11
12#[derive(Debug, Default, Clone, PartialEq)]
14pub struct MollData {
15 c_x: f64,
16 c_y: f64,
17 c_p: f64,
18}
19
20fn setup(proj: &mut Proj, p: f64) -> MollData {
21 let mut store = MollData::default();
22 let p2 = p + p;
23
24 proj.es = 0.;
25 let sp = sin(p);
26 let r = sqrt(TAU * sp / (p2 + sin(p2)));
27
28 store.c_x = 2. * r / PI;
29 store.c_y = r / sp;
30 store.c_p = p2 + sin(p2);
31
32 store
33}
34
35#[derive(Debug, Clone, PartialEq)]
70pub struct MollweideProjection {
71 proj: Rc<RefCell<Proj>>,
72 store: RefCell<MollData>,
73}
74impl ProjectCoordinates for MollweideProjection {
75 fn code(&self) -> i64 {
76 -1
77 }
78 fn name(&self) -> &'static str {
79 "Mollweide"
80 }
81 fn names() -> &'static [&'static str] {
82 &["Mollweide", "moll"]
83 }
84}
85impl CoordinateStep for MollweideProjection {
86 fn new(proj: Rc<RefCell<Proj>>) -> Self {
87 let store = setup(&mut proj.borrow_mut(), FRAC_PI_2);
88 MollweideProjection { proj, store: store.into() }
89 }
90 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
91 moll_s_forward(&self.store.borrow(), p);
92 }
93 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
94 moll_s_inverse(&self.store.borrow(), p);
95 }
96}
97
98#[derive(Debug, Clone, PartialEq)]
100pub struct WagnerIVProjection {
101 proj: Rc<RefCell<Proj>>,
102 store: RefCell<MollData>,
103}
104impl ProjectCoordinates for WagnerIVProjection {
105 fn code(&self) -> i64 {
106 -1
107 }
108 fn name(&self) -> &'static str {
109 "Wagner IV"
110 }
111 fn names() -> &'static [&'static str] {
112 &["Wagner IV", "wag4"]
113 }
114}
115impl CoordinateStep for WagnerIVProjection {
116 fn new(proj: Rc<RefCell<Proj>>) -> Self {
117 let store = setup(&mut proj.borrow_mut(), PI / 3.);
118 WagnerIVProjection { proj, store: store.into() }
119 }
120 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
121 moll_s_forward(&self.store.borrow(), p);
122 }
123 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
124 moll_s_inverse(&self.store.borrow(), p);
125 }
126}
127
128#[derive(Debug, Clone, PartialEq)]
130pub struct WagnerVProjection {
131 proj: Rc<RefCell<Proj>>,
132 store: RefCell<MollData>,
133}
134impl ProjectCoordinates for WagnerVProjection {
135 fn code(&self) -> i64 {
136 -1
137 }
138 fn name(&self) -> &'static str {
139 "Wagner V"
140 }
141 fn names() -> &'static [&'static str] {
142 &["Wagner V", "wag5"]
143 }
144}
145impl CoordinateStep for WagnerVProjection {
146 fn new(proj: Rc<RefCell<Proj>>) -> Self {
147 proj.borrow_mut().es = 0.0;
148 let store = MollData { c_x: 0.90977, c_y: 1.65014, c_p: 3.00896 };
149 WagnerVProjection { proj, store: store.into() }
150 }
151 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
152 moll_s_forward(&self.store.borrow(), p);
153 }
154 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
155 moll_s_inverse(&self.store.borrow(), p);
156 }
157}
158
159pub fn moll_s_forward<P: TransformCoordinates>(moll: &MollData, p: &mut P) {
161 let k = moll.c_p * sin(p.phi());
162 let mut i = MAX_ITER;
163 while i > 0 {
164 let v = (p.phi() + sin(p.phi()) - k) / (1. + cos(p.phi()));
165 p.set_phi(p.phi() - v);
166 if fabs(v) < LOOP_TOL {
167 break;
168 }
169 i -= 1;
170 }
171 if i != 0 {
172 p.set_phi(if p.phi() < 0. { -FRAC_PI_2 } else { FRAC_PI_2 });
173 } else {
174 p.set_phi(p.phi() * 0.5);
175 }
176 p.set_x(moll.c_x * p.lam() * cos(p.phi()));
177 p.set_y(moll.c_y * sin(p.phi()));
178}
179
180pub fn moll_s_inverse<P: TransformCoordinates>(moll: &MollData, p: &mut P) {
182 let mut phi = aasin(p.y() / moll.c_y);
183 let mut lam = p.x() / (moll.c_x * cos(phi));
184 if fabs(lam) < PI {
185 phi += phi;
186 phi = aasin((phi + sin(phi)) / moll.c_p);
187 } else {
188 phi = f64::MAX;
189 lam = f64::MAX;
190 }
191 p.set_phi(phi);
192 p.set_lam(lam);
193}