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/// ![Miller Cylindrical](https://github.com/Open-S2/gis-tools/blob/master/assets/proj4/projections/images/mill.png?raw=true)
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}