Skip to main content

geometry_algorithm/
dyn_area.rs

1//! `area_dyn` — runtime-dispatched area for [`DynGeometry`].
2//!
3//! Same shape as `length_dyn`. Per Boost (`algorithms/area.hpp`) the
4//! area of a non-areal *leaf* kind (point, linestring, multi-point,
5//! multi-linestring, …) is `0`, so every leaf arm has a value and the
6//! wrapper returns a plain scalar, never an error (KC4.T1). A
7//! `GeometryCollection` is **not** a leaf: Boost's
8//! `area<geometry_collection_tag>` (`area.hpp:273-285`) recursively sums
9//! the area of every member, so this wrapper does too.
10
11use geometry_coords::CoordinateScalar;
12use geometry_cs::{CartesianFamily, CoordinateSystem};
13use geometry_model::{DynGeometry, Point, Polygon};
14use geometry_strategy::{AreaStrategy, DefaultArea, DefaultAreaStrategy, ShoelaceMultiPolygonArea};
15use geometry_tag::SameAs;
16
17use crate::area::{area, multi_polygon_area};
18
19/// Runtime-dispatched signed area.
20///
21/// Returns the signed area for areal kinds (`Polygon`, `MultiPolygon`),
22/// the recursive sum of member areas for a `GeometryCollection`, and `0`
23/// for every non-areal leaf kind — matching Boost's contract
24/// (`area.hpp`, incl. the `geometry_collection_tag` specialisation at
25/// `:273-285`).
26#[must_use]
27pub fn area_dyn<S, Cs>(g: &DynGeometry<S, Cs>) -> S
28where
29    S: CoordinateScalar,
30    Cs: CoordinateSystem,
31    Cs::Family: SameAs<CartesianFamily> + DefaultArea<Cs::Family>,
32    DefaultAreaStrategy<Polygon<Point<S, 2, Cs>>>:
33        AreaStrategy<Polygon<Point<S, 2, Cs>>, Out = S> + Default,
34    ShoelaceMultiPolygonArea:
35        AreaStrategy<geometry_model::MultiPolygon<Polygon<Point<S, 2, Cs>>>, Out = S>,
36{
37    use DynGeometry::{
38        GeometryCollection, LineString, MultiLineString, MultiPoint, MultiPolygon,
39        Point as PointArm, Polygon as PolygonArm,
40    };
41    // A collection's area is the sum of its members' areas
42    // (`area.hpp:273-285` recurses via `visit_breadth_first`). The walk is
43    // an explicit work-list rather than recursion so an adversarially deep
44    // `GeometryCollection` chain cannot overflow the native stack (an
45    // uncatchable process abort).
46    let mut total = S::ZERO;
47    let mut stack = alloc::vec![g];
48    while let Some(node) = stack.pop() {
49        match node {
50            PolygonArm(pg) => total = total + area(pg),
51            MultiPolygon(mpg) => total = total + multi_polygon_area(mpg),
52            GeometryCollection(items) => stack.extend(items.iter()),
53            // Non-areal leaf kinds have no area — Boost returns 0.
54            PointArm(_) | LineString(_) | MultiPoint(_) | MultiLineString(_) => {}
55        }
56    }
57    total
58}