Expand description
§Adapting your own types
Three paths get a user type into the geometry kernel, ordered from least to most indirect. Pick the first one that applies.
§Path 1 — Direct impl Point for MyPoint
If you own the type, implement geometry_trait::Geometry and
geometry_trait::Point directly (add geometry_trait::PointMut
too if you need to write coordinates). This is the most explicit
form and the one with the smallest compile-time footprint — no
wrapper types, no macros.
use geometry_cs::Cartesian;
use geometry_tag::PointTag;
use geometry_trait::{Geometry, Point, PointMut};
#[derive(Default)]
struct MyPoint { x: f64, y: f64 }
impl Geometry for MyPoint {
type Kind = PointTag;
type Point = Self;
}
impl Point for MyPoint {
type Scalar = f64;
type Cs = Cartesian;
const DIM: usize = 2;
fn get<const D: usize>(&self) -> f64 {
if D == 0 { self.x } else { self.y }
}
}
impl PointMut for MyPoint {
fn set<const D: usize>(&mut self, v: f64) {
if D == 0 { self.x = v } else { self.y = v }
}
}§Path 2 — #[derive(Point)]
For the common “struct with named coordinate fields” case the
Point proc-macro generates the impl. Mirrors the C++
BOOST_GEOMETRY_REGISTER_POINT_2D macro from
boost/geometry/geometries/register/point.hpp. The derive lives
in geometry-derive and is re-exported by the geometry facade
so downstream users only need a single dependency:
use geometry::Point; // the derive macro
use geometry::prelude::*; // brings in the `Point` trait + algorithms
#[derive(Default, Point)]
#[geometry(cs = "Cartesian", scalar = "f64")]
struct MyPoint { x: f64, y: f64 }
let d = distance(&MyPoint { x: 0.0, y: 0.0 }, &MyPoint { x: 3.0, y: 4.0 });
assert_eq!(d, 5.0);§Path 3 — Adapt<T> + optional WithCs<T, Cs>
When you do not own the type (a [T; N], a (T, T), a foreign
crate’s point type), wrap it in Adapt<T>. Adapt is a
#[repr(transparent)] newtype that forwards coordinate access
into the foreign storage layout. Coherence forbids a blanket impl
on the foreign type directly; the wrapper sidesteps the orphan
rule with zero runtime cost.
use geometry_adapt::Adapt;
use geometry_trait::Point;
let p = Adapt([3.0_f64, 4.0]);
assert_eq!(p.get::<0>(), 3.0);
assert_eq!(p.get::<1>(), 4.0);Adapt<T> is shape-only and defaults the coordinate system
to Cartesian. For any other CS (latitude /
longitude in degrees, radians, …) layer WithCs<T, Cs> on top.
This is the orthogonality from proposal §3.7: Adapt answers
“how do I read coordinates out of this foreign data layout?”,
while WithCs answers “what does that coordinate pair mean?”.
use geometry_adapt::{Adapt, WithCs};
use geometry_algorithm::distance;
use geometry_cs::{Degree, Geographic};
// [lon, lat] degrees on WGS84.
let ams = WithCs::<_, Geographic<Degree>>::new(Adapt([4.90_f64, 52.37]));
let par = WithCs::<_, Geographic<Degree>>::new(Adapt([2.35_f64, 48.86]));
let _ = distance(&ams, &par); // picks Andoyer for the geographic familyEither wrapper can also re-tag a user type that already
implements Point, so a MyPoint from
Path 1 can be reused as a geographic point by writing
WithCs::<MyPoint, Geographic<Degree>>::new(p) instead of
defining a second adapter.
§Container adaptation — the register_*! macros
Coherence also forbids a blanket impl on foreign sequence types
(impl<P: Point, C: AsRef<[P]>> Linestring for C). For that case,
geometry-adapt ships declarative macros register_linestring!,
register_ring!, register_polygon!, and the three register_multi_*!
forms, mirroring
BOOST_GEOMETRY_REGISTER_LINESTRING and siblings
(boost/geometry/geometries/register/linestring.hpp and co.).
Module layout:
Adapt<T>— shape-only wrapper. Forwards coordinate access into a foreign storage layout (array, tuple, third-party point). Defaults toCartesian; layerWithCson top for other systems.WithCs<T, Cs>— coordinate-system re-tagging wrapper.
Mirrors the role of boost/geometry/geometries/adapted/*.hpp
(c_array.hpp, std_array.hpp, boost_tuple.hpp,
boost_polygon.hpp, …).
Macros§
- register_
linestring - Register a user-owned linestring type whose storage is iterable as
&Pointvia a user-supplied expression. - register_
multi_ linestring - Register a user-owned multi-linestring container.
- register_
multi_ point - Register a user-owned multi-point container.
- register_
multi_ polygon - Register a user-owned multi-polygon container.
- register_
polygon - Register a user-owned polygon type whose interior decomposes as
{ outer: $Ring, inners: <iterable of $Ring> }. - register_
ring - Register a user-owned ring type whose storage is iterable as
&Pointvia a user-supplied expression.