use geometry_tag::{
BoxTag, LinestringTag, MultiLinestringTag, MultiPointTag, MultiPolygonTag, PointTag,
PolygonTag, RingTag, SegmentTag,
};
use geometry_trait::{
Box as BoxTrait, Geometry, Linestring as LinestringTrait,
MultiLinestring as MultiLinestringTrait, MultiPoint as MultiPointTrait,
MultiPolygon as MultiPolygonTrait, Point as PointTrait, Polygon as PolygonTrait,
Ring as RingTrait, Segment as SegmentTrait,
};
#[inline]
#[must_use]
pub fn num_points<G>(g: &G) -> usize
where
G: Geometry,
G::Kind: NumPointsStrategyForKind,
<G::Kind as NumPointsStrategyForKind>::S: NumPointsStrategy<G>,
{
<<G::Kind as NumPointsStrategyForKind>::S as Default>::default().num_points(g)
}
#[doc(hidden)]
pub trait NumPointsStrategy<G> {
fn num_points(&self, g: &G) -> usize;
}
#[derive(Default)]
#[doc(hidden)]
pub struct PointNumPoints;
#[derive(Default)]
#[doc(hidden)]
pub struct SegmentNumPoints;
#[derive(Default)]
#[doc(hidden)]
pub struct BoxNumPoints;
#[derive(Default)]
#[doc(hidden)]
pub struct LinestringNumPoints;
#[derive(Default)]
#[doc(hidden)]
pub struct RingNumPoints;
#[derive(Default)]
#[doc(hidden)]
pub struct PolygonNumPoints;
#[derive(Default)]
#[doc(hidden)]
pub struct MultiPointNumPoints;
#[derive(Default)]
#[doc(hidden)]
pub struct MultiLinestringNumPoints;
#[derive(Default)]
#[doc(hidden)]
pub struct MultiPolygonNumPoints;
impl<G: PointTrait> NumPointsStrategy<G> for PointNumPoints {
fn num_points(&self, _g: &G) -> usize {
1
}
}
impl<G: SegmentTrait> NumPointsStrategy<G> for SegmentNumPoints {
fn num_points(&self, _g: &G) -> usize {
2
}
}
impl<G: BoxTrait> NumPointsStrategy<G> for BoxNumPoints {
fn num_points(&self, _g: &G) -> usize {
1usize << <G::Point as PointTrait>::DIM
}
}
impl<G: LinestringTrait> NumPointsStrategy<G> for LinestringNumPoints {
fn num_points(&self, g: &G) -> usize {
g.points().count()
}
}
impl<G: RingTrait> NumPointsStrategy<G> for RingNumPoints {
fn num_points(&self, g: &G) -> usize {
g.points().count()
}
}
impl<G: PolygonTrait> NumPointsStrategy<G> for PolygonNumPoints {
fn num_points(&self, g: &G) -> usize {
let mut n = g.exterior().points().count();
for inner in g.interiors() {
n += inner.points().count();
}
n
}
}
impl<G: MultiPointTrait> NumPointsStrategy<G> for MultiPointNumPoints {
fn num_points(&self, g: &G) -> usize {
g.points().count()
}
}
impl<G: MultiLinestringTrait> NumPointsStrategy<G> for MultiLinestringNumPoints {
fn num_points(&self, g: &G) -> usize {
g.linestrings().map(num_points).sum()
}
}
impl<G: MultiPolygonTrait> NumPointsStrategy<G> for MultiPolygonNumPoints {
fn num_points(&self, g: &G) -> usize {
g.polygons().map(num_points).sum()
}
}
#[doc(hidden)]
pub trait NumPointsStrategyForKind {
type S: Default;
}
impl NumPointsStrategyForKind for PointTag {
type S = PointNumPoints;
}
impl NumPointsStrategyForKind for SegmentTag {
type S = SegmentNumPoints;
}
impl NumPointsStrategyForKind for BoxTag {
type S = BoxNumPoints;
}
impl NumPointsStrategyForKind for LinestringTag {
type S = LinestringNumPoints;
}
impl NumPointsStrategyForKind for RingTag {
type S = RingNumPoints;
}
impl NumPointsStrategyForKind for PolygonTag {
type S = PolygonNumPoints;
}
impl NumPointsStrategyForKind for MultiPointTag {
type S = MultiPointNumPoints;
}
impl NumPointsStrategyForKind for MultiLinestringTag {
type S = MultiLinestringNumPoints;
}
impl NumPointsStrategyForKind for MultiPolygonTag {
type S = MultiPolygonNumPoints;
}
#[cfg(test)]
mod tests {
use super::num_points;
use geometry_cs::Cartesian;
use geometry_model::{
Box, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Point2D, Polygon, Segment,
linestring, polygon,
};
type Pt = Point2D<f64, Cartesian>;
type Ls = Linestring<Pt>;
type Poly = Polygon<Pt>;
#[test]
fn point_is_one() {
assert_eq!(num_points(&Pt::new(0.0, 0.0)), 1);
}
#[test]
fn segment_is_two() {
let s = Segment::new(Pt::new(0.0, 0.0), Pt::new(1.0, 1.0));
assert_eq!(num_points(&s), 2);
}
#[test]
fn box_2d_is_four() {
let b = Box::from_corners(Pt::new(0.0, 0.0), Pt::new(1.0, 1.0));
assert_eq!(num_points(&b), 4);
}
#[test]
fn linestring_three_points() {
let ls: Ls = linestring![(0.0, 0.0), (1.0, 1.0), (2.0, 2.0)];
assert_eq!(num_points(&ls), 3);
}
#[test]
fn ring_counts_stored_points() {
let r: geometry_model::Ring<Pt> = geometry_model::Ring::from_vec(vec![
Pt::new(0.0, 0.0),
Pt::new(4.0, 0.0),
Pt::new(4.0, 3.0),
Pt::new(0.0, 3.0),
Pt::new(0.0, 0.0),
]);
assert_eq!(num_points(&r), 5);
}
#[test]
fn polygon_outer_only() {
let pg: Poly = polygon![[(0.0, 0.0), (4.0, 0.0), (4.0, 3.0), (0.0, 3.0), (0.0, 0.0)]];
assert_eq!(num_points(&pg), 5);
}
#[test]
fn polygon_with_hole_adds_inner_count() {
let pg: Poly = polygon![
[
(0.0, 0.0),
(10.0, 0.0),
(10.0, 10.0),
(0.0, 10.0),
(0.0, 0.0)
],
[(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 2.0), (1.0, 1.0)],
];
assert_eq!(num_points(&pg), 10);
}
#[test]
fn multi_linestring_sums_members() {
let mls: MultiLinestring<Ls> = MultiLinestring(vec![
linestring![(0.0, 0.0), (1.0, 1.0)],
linestring![(2.0, 2.0), (3.0, 3.0), (4.0, 4.0)],
]);
assert_eq!(num_points(&mls), 5);
}
#[test]
fn multi_point_counts_members() {
let mp = MultiPoint(vec![Pt::new(0.0, 0.0), Pt::new(1.0, 1.0)]);
assert_eq!(num_points(&mp), 2);
}
#[test]
fn multi_polygon_sums_members() {
let mpg: MultiPolygon<Poly> = MultiPolygon(vec![
polygon![[
(0.0, 0.0),
(0.0, 10.0),
(10.0, 10.0),
(10.0, 0.0),
(0.0, 0.0)
]],
polygon![[(0.0, 10.0), (1.0, 10.0), (1.0, 9.0), (0.0, 10.0)]],
]);
assert_eq!(num_points(&mpg), 9);
}
}