gistools/proj/project/mill.rs
1use crate::proj::{CoordinateStep, Proj, ProjectCoordinates, TransformCoordinates};
2use alloc::rc::Rc;
3use core::{cell::RefCell, f64::consts::FRAC_PI_4};
4use libm::{atan, exp, log, tan};
5
6/// # Miller Cylindrical
7///
8/// The Miller cylindrical projection is a modified Mercator projection, proposed by
9/// Osborn Maitland Miller in 1942.
10///
11/// **Classification**: Neither conformal nor equal area cylindrical
12///
13/// **Available forms**: Forward and inverse spherical
14///
15/// **Defined area**: Global, but best used near the equator
16///
17/// **Alias**: mill
18///
19/// **Domain**: 2D
20///
21/// **Input type**: Geodetic coordinates
22///
23/// **Output type**: Projected coordinates
24///
25/// ## Projection String
26/// ```ini
27/// +proj=mill
28/// ```
29///
30/// ## Required Parameters
31/// - None
32///
33/// ## Optional Parameters
34/// - `+lon_0`: Longitude of projection center. Defaults to `0`.
35/// - `+R`: Radius of the sphere.
36/// - `+x_0`: False easting. Defaults to `0`.
37/// - `+y_0`: False northing. Defaults to `0`.
38///
39/// ## Usage Example
40/// Using Central meridian 90°W:
41/// ```bash
42/// $ echo -100 35 | proj +proj=mill +lon_0=90w
43/// -1113194.91 4061217.24
44/// ```
45///
46/// ## Mathematical Definition
47/// ### Forward projection:
48/// $$x = \lambda$$
49/// $$y = 1.25 * \ln \left[ \tan \left(\frac{\pi}{4} + 0.4 * \phi \right) \right]$$
50/// ### Inverse projection:
51/// $$\lambda = x$$
52/// $$\phi = 2.5 * ( \arctan \left[ e^{0.8 * y} \right] - \frac{\pi}{4} )$$
53///
54/// ## Further reading
55/// - [Wikipedia on Miller Cylindrical](https://en.wikipedia.org/wiki/Miller_cylindrical_projection)
56/// - "New Equal-Area Map Projections for Noncircular Regions", John P. Snyder, The American Cartographer, Vol 15, No. 4, October 1988, pp. 341-355.
57///
58/// 
59#[derive(Debug, Clone, PartialEq)]
60pub struct MillerCylindricalProjection {
61 proj: Rc<RefCell<Proj>>,
62}
63impl ProjectCoordinates for MillerCylindricalProjection {
64 fn code(&self) -> i64 {
65 -1
66 }
67 fn name(&self) -> &'static str {
68 "Miller Cylindrical"
69 }
70 fn names() -> &'static [&'static str] {
71 &["MillerCylindrical", "Miller Cylindrical", "Miller_Cylindrical", "mill"]
72 }
73}
74impl CoordinateStep for MillerCylindricalProjection {
75 fn new(proj: Rc<RefCell<Proj>>) -> Self {
76 proj.borrow_mut().es = 0.;
77 MillerCylindricalProjection { proj }
78 }
79 fn forward<P: TransformCoordinates>(&self, p: &mut P) {
80 mill_s_forward(p);
81 }
82 fn inverse<P: TransformCoordinates>(&self, p: &mut P) {
83 mill_s_inverse(p);
84 }
85}
86
87/// Miller Cylindrical Forward Project
88pub fn mill_s_forward<P: TransformCoordinates>(p: &mut P) {
89 p.set_x(p.lam());
90 p.set_y(log(tan(FRAC_PI_4 + p.phi() * 0.4)) * 1.25);
91}
92
93/// Miller Cylindrical Inverse Project
94pub fn mill_s_inverse<P: TransformCoordinates>(p: &mut P) {
95 p.set_lam(p.x());
96 p.set_phi(2.5 * (atan(exp(0.8 * p.y())) - FRAC_PI_4));
97}