Skip to main content

geometry_strategy/
lib.rs

1//! # Pluggable algorithm strategies, keyed by coordinate-system family.
2//!
3//! Mirrors `boost/geometry/strategies/` — every algorithm has a
4//! strategy trait here; concrete strategies live in submodules keyed
5//! by coordinate-system family (`cartesian`, `spherical`,
6//! `geographic`).
7//!
8//! ## Writing a new strategy
9//!
10//! Take the worked example: a Cartesian point-to-point distance
11//! strategy (`Pythagoras`). A strategy for any algorithm follows the
12//! same three steps.
13//!
14//! ### Step 1 — Pick the coordinate-system family to bind on
15//!
16//! Strategies bind on the [`CoordinateSystem::Family`](geometry_cs::CoordinateSystem::Family)
17//! — never on the concrete CS — so that one impl covers both
18//! `Spherical<Degree>` and `Spherical<Radian>` (or
19//! `Geographic<Degree>` / `Geographic<Radian>`). The bound is
20//! expressed via the [`geometry_tag::SameAs`] trait, the Rust
21//! analogue of C++'s `std::is_same`:
22//!
23//! ```ignore
24//! impl<P1, P2> DistanceStrategy<P1, P2> for Pythagoras
25//! where
26//!     P1: Point,
27//!     P2: Point<Scalar = P1::Scalar>,
28//!     <P1::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
29//!     <P2::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
30//! { /* ... */ }
31//! ```
32//!
33//! The two `SameAs<CartesianFamily>` bounds form the family fence:
34//! they refuse to monomorphise for a `Spherical` or `Geographic` point.
35//! The `#[diagnostic::on_unimplemented]` plate on
36//! [`geometry_tag::SameAs`] then redirects the resulting
37//! compile error to the correct mitigation (wrap in
38//! `geometry_adapt::WithCs<_, Geographic<_>>`, or pick a
39//! CS-appropriate strategy such as `Haversine` / `Andoyer` /
40//! `Vincenty`).
41//!
42//! ### Step 2 — Decide whether to provide a `Comparable` form
43//!
44//! A "comparable" form is a sibling strategy that returns the *same
45//! ordering* as the real strategy but skips work the ordering does
46//! not need. For Pythagoras this means returning the *squared*
47//! distance and skipping the final `sqrt`. The squared form sorts
48//! identically and is roughly twice as fast on a hot inner loop:
49//!
50//! ```ignore
51//! impl<P1, P2> DistanceStrategy<P1, P2> for Pythagoras /* ... */ {
52//!     type Out = P1::Scalar;
53//!     type Comparable = ComparablePythagoras; // <- skip-sqrt sibling
54//!     fn distance(&self, a: &P1, b: &P2) -> Self::Out {
55//!         self.comparable().distance(a, b).sqrt()
56//!     }
57//!     fn comparable(&self) -> Self::Comparable { ComparablePythagoras }
58//! }
59//! ```
60//!
61//! If the math has no equivalent shortcut (Andoyer, Vincenty,
62//! Haversine after the half-angle formula), set
63//! `type Comparable = Self;` — the optimiser collapses the
64//! indirection. The doc on [`distance::DistanceStrategy::Comparable`]
65//! warns implementers not to over-engineer this.
66//!
67//! ### Step 3 — Wire the default selection
68//!
69//! Each coordinate-system family picks one default strategy per
70//! algorithm via [`distance::DefaultDistance`]:
71//!
72//! ```ignore
73//! impl DefaultDistance<CartesianFamily>  for CartesianFamily  { type Strategy = Pythagoras; }
74//! impl DefaultDistance<SphericalFamily>  for SphericalFamily  { type Strategy = Haversine;  }
75//! impl DefaultDistance<GeographicFamily> for GeographicFamily { type Strategy = Andoyer;    }
76//! ```
77//!
78//! That is what makes the no-strategy `distance(a, b)` overload
79//! resolve to the right algorithm: the type-level walk
80//! `A → A::Point → Cs → Family → DefaultDistance<…B's family…>::Strategy`
81//! resolves to the correct concrete strategy at the call site.
82//!
83//! ## Reverse dispatch — argument symmetry
84//!
85//! For algorithms whose arguments are symmetric, write one impl per
86//! tag pair `(A, B)` and the `Reversed<S>` blanket impl in
87//! [`distance::Reversed`] picks up `(B, A)` automatically. The
88//! analogue of Boost's `core/reverse_dispatch.hpp` partial
89//! specialisation, done once at the strategy-trait layer instead of
90//! per-algorithm.
91//!
92//! ## Module layout
93//!
94//! Each algorithm has its own strategy trait module — `distance`,
95//! `area`, `length`, `envelope`, `within`, `intersects`, `disjoint`,
96//! `equals`. Concrete strategies live under `cartesian`,
97//! `spherical`, or `geographic` per coordinate-system family.
98
99#![cfg_attr(not(feature = "std"), no_std)]
100#![forbid(unsafe_code)]
101
102extern crate alloc;
103
104pub mod area;
105pub mod azimuth;
106pub mod buffer;
107pub mod cartesian;
108pub mod centroid;
109pub mod closest_points;
110pub mod compare;
111pub mod convex_hull;
112pub mod densify;
113pub mod destination;
114pub mod disjoint;
115pub mod distance;
116pub mod envelope;
117pub mod equals;
118pub mod geographic;
119pub mod intersects;
120pub mod length;
121pub mod line_interpolate;
122pub(crate) mod normalise;
123mod reversal;
124pub mod segmentize;
125pub mod simplify;
126pub mod spherical;
127pub mod transform;
128pub mod within;
129
130pub use area::{
131    AreaStrategy, DefaultArea, DefaultAreaStrategy, ShoelaceArea, ShoelaceBoxArea,
132    ShoelaceMultiPolygonArea, ShoelacePolygonArea,
133};
134pub use azimuth::{AzimuthStrategy, CartesianAzimuth, DefaultAzimuth, DefaultAzimuthStrategy};
135pub use buffer::{
136    BufferDistanceStrategy, BufferEndStrategy, BufferJoinStrategy, BufferPointStrategy,
137    BufferSettings, BufferSideStrategy, CartesianBuffer, DefaultBuffer, DefaultBufferStrategy,
138    GeographicBuffer, SphericalBuffer,
139};
140pub use cartesian::{ComparablePythagoras, PointToSegment, Pythagoras};
141pub use centroid::{
142    CartesianBoxCentroid, CartesianLinestringCentroid, CartesianMultiPointCentroid,
143    CartesianPolygonCentroid, CartesianRingCentroid, CartesianSegmentCentroid, CentroidStrategy,
144    CentroidStrategyForKind,
145};
146pub use closest_points::{CartesianClosestPoints, ClosestPointsStrategy};
147pub use compare::{ALL_DIMENSIONS, EqualTo, Greater, Less, LessExact};
148pub use convex_hull::{CollectPoints, ConvexHullStrategy, MonotoneChain};
149pub use densify::{CartesianDensify, DensifyStrategy};
150pub use destination::{DefaultDestination, DefaultDestinationStrategy, DestinationStrategy};
151pub use disjoint::{CartesianDisjoint, DisjointStrategy};
152pub use distance::{DefaultDistance, DefaultDistanceStrategy, DistanceStrategy};
153pub use envelope::{
154    EnvelopeBox, EnvelopeLinestring, EnvelopeMultiLinestring, EnvelopeMultiPoint,
155    EnvelopeMultiPolygon, EnvelopePoint, EnvelopePolygon, EnvelopeRing, EnvelopeSegment,
156    EnvelopeStrategy, EnvelopeStrategyForKind,
157};
158pub use equals::{
159    EqPointPoint, EqPolygonPolygon, EqSegmentSegment, EqualsPairStrategy, EqualsStrategy,
160};
161pub use geographic::{
162    Andoyer, DirectResult, GeographicArea, GeographicAzimuth, GeographicLength,
163    GeographicPerimeter, GeographicPolygonArea, InverseResult, Karney, KarneyDirect, KarneyInverse,
164    Rhumb, RhumbFamily, Thomas, ThomasDirect, Vincenty, VincentyDirect,
165};
166pub use intersects::{CartesianIntersects, IntersectsPairStrategy, IntersectsStrategy};
167pub use length::{
168    CartesianLength, CartesianPerimeter, DefaultLength, DefaultLengthStrategy, DefaultPerimeter,
169    DefaultPerimeterStrategy, LengthStrategy,
170};
171pub use line_interpolate::{CartesianLineInterpolate, LineInterpolateStrategy};
172pub use reversal::Reversed;
173pub use segmentize::{CartesianSegmentize, SegmentizeStrategy};
174pub use simplify::{
175    DouglasPeucker, SimplifyStrategy, VisvalingamWhyatt, VisvalingamWhyattPreserve,
176};
177pub use spherical::{
178    ChamberlainDuquetteArea, ComparableHaversine, CrossTrack, Haversine, HaversineClosestPoints,
179    SphericalArea, SphericalAzimuth, SphericalLength, SphericalPerimeter, SphericalPolygonArea,
180};
181pub use transform::{Affine2, Affine3, Rotate, Scale, Skew, TransformStrategy, Translate};
182pub use within::{WithinBox, WithinPoly, WithinRing, WithinStrategy, WithinStrategyForKind};