geometry_cs/system.rs
1//! The [`CoordinateSystem`] trait and its four concrete types.
2//!
3//! Mirrors the `cs::{cartesian,spherical,geographic,polar}` empty /
4//! `<Unit>`-parameterised tag types in
5//! `boost/geometry/core/cs.hpp:85-165`, together with the
6//! `traits::cs_tag<Cs>` family classifier at `cs.hpp:194-225`.
7//!
8//! The `Family` associated type is the Rust analogue of Boost's
9//! `cs_tag<Cs>::type`. Algorithm bounds key on the family
10//! (`<P::Cs as CoordinateSystem>::Family`) so a strategy written for
11//! `SphericalFamily` automatically applies to both
12//! `Spherical<Degree>` and `Spherical<Radian>`.
13
14use core::marker::PhantomData;
15
16use crate::family::{CartesianFamily, GeographicFamily, PolarFamily, SphericalFamily};
17use crate::unit::AngleUnit;
18
19/// A coordinate system.
20///
21/// Mirrors `traits::coordinate_system<Point>::type` from
22/// `boost/geometry/core/coordinate_system.hpp:43-49`: the typedef every
23/// point exposes that identifies *which* coordinate system its values
24/// live in. The `Family` associated type is the analogue of Boost's
25/// `traits::cs_tag<Cs>::type` (`cs.hpp:186-225`), the family-level
26/// classifier that strategies actually dispatch on.
27///
28/// # Examples
29///
30/// ```
31/// use geometry_cs::{Cartesian, CartesianFamily, CoordinateSystem};
32/// // Every CS exposes its `Family`; strategies bind on the family.
33/// fn _cartesian_family<C: CoordinateSystem<Family = CartesianFamily>>() {}
34/// _cartesian_family::<Cartesian>();
35/// ```
36pub trait CoordinateSystem {
37 /// The CS family this concrete system belongs to.
38 ///
39 /// Mirrors `boost::geometry::traits::cs_tag<Cs>::type`
40 /// (`boost/geometry/core/cs.hpp:194-225`).
41 type Family;
42}
43
44/// Cartesian / rectangular coordinates.
45///
46/// Mirrors `boost::geometry::cs::cartesian`
47/// (`boost/geometry/core/cs.hpp:85-93`).
48///
49/// # Examples
50///
51/// ```
52/// use geometry_cs::{Cartesian, CartesianFamily, CoordinateSystem};
53/// let _: <Cartesian as CoordinateSystem>::Family = CartesianFamily;
54/// ```
55#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
56pub struct Cartesian;
57
58impl CoordinateSystem for Cartesian {
59 type Family = CartesianFamily;
60}
61
62/// Spherical coordinates in unit `U`.
63///
64/// Mirrors `boost::geometry::cs::spherical<DegreeOrRadian>`
65/// (`boost/geometry/core/cs.hpp:115-135`).
66///
67/// # Convention: latitude measured from the equator
68///
69/// Boost has *two* spherical tags
70/// (`boost/geometry/core/tags.hpp:38-41`):
71///
72/// * `spherical_polar_tag` — colatitude. The second coordinate is
73/// the angle from the **pole**, ranging `[0, π]`.
74/// * `spherical_equatorial_tag` — latitude. The second coordinate
75/// is the angle from the **equator**, ranging `[-π/2, π/2]`.
76///
77/// v1 collapses both into one [`Spherical<U>`] family and picks the
78/// **equatorial** convention because that is what the OGC standard
79/// and the Boost.Geometry quickstart use
80/// (`doc/src/examples/quick_start.cpp` Amsterdam → Paris example).
81///
82/// In coordinate order:
83///
84/// ```text
85/// get::<0>(p) → longitude (azimuth, around the polar axis)
86/// get::<1>(p) → latitude (measured from the equator)
87/// ```
88///
89/// In a `Spherical<Degree>` point, longitude ranges `-180.0..=180.0`
90/// (east of Greenwich is positive) and latitude ranges
91/// `-90.0..=90.0` (north of the equator is positive). Amsterdam
92/// (≈ 4.90° E, 52.37° N) is `(4.90, 52.37)` — the "north of the
93/// equator" component is the *second* coordinate.
94///
95/// # If you have colatitude data
96///
97/// Convert to latitude first: `latitude = 90° − colatitude`
98/// (degrees) or `π/2 − colatitude` (radians). The dedicated
99/// `SphericalPolar` / `SphericalEquatorial` split is deferred to a future iteration.
100///
101/// # Examples
102///
103/// ```
104/// use geometry_cs::{CoordinateSystem, Degree, Spherical, SphericalFamily};
105/// fn _spherical<C: CoordinateSystem<Family = SphericalFamily>>() {}
106/// _spherical::<Spherical<Degree>>();
107/// ```
108#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
109pub struct Spherical<U: AngleUnit>(PhantomData<U>);
110
111impl<U: AngleUnit> CoordinateSystem for Spherical<U> {
112 type Family = SphericalFamily;
113}
114
115/// Geographic (lat / lon) coordinates on a spheroid in unit `U`.
116///
117/// Mirrors `boost::geometry::cs::geographic<DegreeOrRadian>`
118/// (`boost/geometry/core/cs.hpp:98-110`).
119///
120/// # Convention: latitude measured from the equator
121///
122/// Same equatorial convention as [`Spherical<U>`] — the difference
123/// is the *earth model*: `Geographic` distances run on a spheroid
124/// (Andoyer / Vincenty / Thomas), `Spherical` on a perfect sphere
125/// (Haversine). In coordinate order:
126///
127/// ```text
128/// get::<0>(p) → longitude (east of Greenwich positive)
129/// get::<1>(p) → latitude (north of the equator positive)
130/// ```
131///
132/// In a `Geographic<Degree>` point, longitude ranges `-180.0..=180.0`
133/// and latitude ranges `-90.0..=90.0`.
134///
135/// # Examples
136///
137/// ```
138/// use geometry_cs::{CoordinateSystem, Degree, Geographic, GeographicFamily};
139/// fn _geo<C: CoordinateSystem<Family = GeographicFamily>>() {}
140/// _geo::<Geographic<Degree>>();
141/// ```
142#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
143pub struct Geographic<U: AngleUnit>(PhantomData<U>);
144
145impl<U: AngleUnit> CoordinateSystem for Geographic<U> {
146 type Family = GeographicFamily;
147}
148
149/// Polar coordinates in unit `U`.
150///
151/// Mirrors `boost::geometry::cs::polar<DegreeOrRadian>`
152/// (`boost/geometry/core/cs.hpp:155-165`).
153///
154/// # Examples
155///
156/// ```
157/// use geometry_cs::{CoordinateSystem, Polar, PolarFamily, Radian};
158/// fn _polar<C: CoordinateSystem<Family = PolarFamily>>() {}
159/// _polar::<Polar<Radian>>();
160/// ```
161#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
162pub struct Polar<U: AngleUnit>(PhantomData<U>);
163
164impl<U: AngleUnit> CoordinateSystem for Polar<U> {
165 type Family = PolarFamily;
166}