miniproj_ops/ops/
popvis_pseudo_mercator.rs1use std::f64::consts::{FRAC_PI_2, FRAC_PI_4};
4
5use crate::{
6 ellipsoid::Ellipsoid, traits::GetterContstruct, DbContstruct, Projection, PseudoSerialize,
7};
8
9#[derive(Copy, Clone, Debug)]
10pub struct PopVisPseudoMercatorParams {
11 lon_orig: f64,
13 lat_orig: f64,
15 false_e: f64,
17 false_n: f64,
19}
20
21impl PopVisPseudoMercatorParams {
22 pub const fn new(lon_orig: f64, lat_orig: f64, false_e: f64, false_n: f64) -> Self {
23 Self {
24 lat_orig,
25 lon_orig,
26 false_e,
27 false_n,
28 }
29 }
30
31 pub fn lon_orig(&self) -> f64 {
33 self.lon_orig
34 }
35
36 pub fn lat_orig(&self) -> f64 {
38 self.lat_orig
39 }
40
41 pub fn false_e(&self) -> f64 {
43 self.false_e
44 }
45
46 pub fn false_n(&self) -> f64 {
48 self.false_n
49 }
50}
51
52#[allow(non_snake_case)]
54#[derive(Copy, Clone, Debug)]
55pub struct PopVisPseudoMercatorProjection {
56 pub false_e: f64,
57 pub false_n: f64,
58 pub ellipsoid_a: f64,
59 pub lon_orig: f64,
60}
61
62impl PopVisPseudoMercatorProjection {
63 #[allow(non_snake_case)]
64 pub fn new(ell: &Ellipsoid, params: &PopVisPseudoMercatorParams) -> Self {
65 Self {
66 ellipsoid_a: ell.a(),
67 lon_orig: params.lon_orig(),
68 false_e: params.false_e(),
69 false_n: params.false_n(),
70 }
71 }
72}
73
74impl Projection for PopVisPseudoMercatorProjection {
75 #[allow(non_snake_case)]
78 fn rad_to_projected(&self, longitude: f64, latitude: f64) -> (f64, f64) {
79 (
80 self.false_e + self.ellipsoid_a * (longitude - self.lon_orig),
81 self.false_n + self.ellipsoid_a * (FRAC_PI_4 + latitude / 2f64).tan().ln(),
82 )
83 }
84
85 #[allow(non_snake_case)]
88 fn projected_to_rad(&self, easting: f64, northing: f64) -> (f64, f64) {
89 let D = (self.false_n - northing) / self.ellipsoid_a;
90 (
91 ((easting - self.false_e) / self.ellipsoid_a) + self.lon_orig,
92 FRAC_PI_2 - 2.0 * D.exp().atan(),
93 )
94 }
95}
96
97impl PseudoSerialize for PopVisPseudoMercatorProjection {
98 fn to_constructed(&self) -> String {
99 format!(
100 r"PopVisPseudoMercatorProjection{{
101 ellipsoid_a: {}f64,
102 lon_orig: {}f64,
103 false_e: {}f64,
104 false_n: {}f64,
105}}",
106 self.ellipsoid_a, self.lon_orig, self.false_e, self.false_n,
107 )
108 }
109}
110
111impl DbContstruct for PopVisPseudoMercatorProjection {
112 fn from_database_params(params: &[(u32, f64)], ellipsoid: &Ellipsoid) -> Self {
113 let params = PopVisPseudoMercatorParams::new(
114 params
115 .iter()
116 .find_map(|(c, v)| if *c == 8802 { Some(*v) } else { None })
117 .unwrap(),
118 params
119 .iter()
120 .find_map(|(c, v)| if *c == 8801 { Some(*v) } else { None })
121 .unwrap(),
122 params
123 .iter()
124 .find_map(|(c, v)| if *c == 8806 { Some(*v) } else { None })
125 .unwrap(),
126 params
127 .iter()
128 .find_map(|(c, v)| if *c == 8807 { Some(*v) } else { None })
129 .unwrap(),
130 );
131 Self::new(ellipsoid, ¶ms)
132 }
133}
134
135impl GetterContstruct for PopVisPseudoMercatorProjection {
136 fn with_db_getter<G>(mut getter: G, ellipsoid: &Ellipsoid) -> Option<Self>
137 where
138 G: FnMut(u32) -> Option<f64>,
139 {
140 let params = PopVisPseudoMercatorParams::new(
141 getter(8802)?,
142 getter(8801)?,
143 getter(8806)?,
144 getter(8807)?,
145 );
146 Some(Self::new(ellipsoid, ¶ms))
147 }
148}
149
150pub fn direct_projection(params: &[(u32, f64)], ell: Ellipsoid) -> String {
151 PopVisPseudoMercatorProjection::from_database_params(params, &ell).to_constructed()
152}
153#[cfg(test)]
154mod tests {
155
156 use crate::ellipsoid::Ellipsoid;
157 use crate::traits::*;
158
159 use super::PopVisPseudoMercatorParams;
160 use super::PopVisPseudoMercatorProjection;
161
162 #[test]
163 fn popvis_mercator_consistency() {
164 let ell = Ellipsoid::from_a_f_inv(6378137.0, 298.257223563);
165 let params =
166 PopVisPseudoMercatorParams::new(0.0f64.to_radians(), 0.0f64.to_radians(), 0.0, 0.0);
167
168 let projection = PopVisPseudoMercatorProjection::new(&ell, ¶ms);
169 let easting_goal = -11169055.58;
170 let northing_goal = 2800000.00;
171 let (lon, lat) = projection.projected_to_deg(easting_goal, northing_goal);
172 let (easting, northing) = projection.deg_to_projected(lon, lat);
173
174 eprintln!("easting: {easting_goal} - {easting}");
175 eprintln!("northing: {northing_goal} - {northing}");
176
177 assert!((easting - easting_goal).abs() < 0.01);
178
179 assert!((northing - northing_goal).abs() < 0.01);
180 }
181}