Skip to content

Commit

Permalink
introduce GeometryCow avoid clone in Geometry::from(inner.clone())
Browse files Browse the repository at this point in the history
The primary currency of the geo crate is the Geometry enum:

```
pub enum Geometry<T>
where
    T: CoordNum,
{
    Point(Point<T>),
    Line(Line<T>),
    LineString(LineString<T>),
    Polygon(Polygon<T>),
    MultiPoint(MultiPoint<T>),
    MultiLineString(MultiLineString<T>),
    MultiPolygon(MultiPolygon<T>),
    GeometryCollection(GeometryCollection<T>),
    Rect(Rect<T>),
    Triangle(Triangle<T>),
}

```

Most of our traits have impls for the individual Geometry cases:

```
impl<T> Foo for Point<T> {
    fn foo(&self) -> {
    // do something
    }
}
impl<T> Foo for Line<T> {
    fn foo(&self) -> {
    // do something
    }
}
impl<T> Foo for LineString<T> {
    fn foo(&self) -> {
    // do something
    }
}
...
```

and the impl for `Geometry` itself is a simple dispatch to the inner variants
impl.
```
impl <T> Foo for Geometry<T> {
    fn foo(&self) -> {
    match self {
        Geometry::Point(g) => g.foo(),
        Geometry::Line(g) => g.foo(),
        Geometry::LineString(g) => g.foo(),
        ... => etc.
    }
}
```

However, the new `Relate` trait, which utilizes the geometry graph I've been
working on, is implemented directly on Geometry.

Like this:

```
impl<F: 'static + GeoFloat> Relate<F, Geometry<F>> for Geometry<F> {
    fn relate(&self, other: &Geometry<F>) -> IntersectionMatrix {
        let mut relate_computer = relate_computer::RelateComputer::new(self, other);
        relate_computer.compute_intersection_matrix()
    }
}
```

So, the way to make this work, is to introduce undesirable cloning:

```
fn color(line: &Line, polygon: &Polygon) -> Color {
    # We want to avoid these clones!
    let line_geometry = Geometry::Line(line.clone());
    let polygon_geometry = Geometry::Polygon(polygon.clone());

    if polygon_geometry.relate(line_geometry).is_contains() {
        Color::Green
    } else {
        Color::Red
    }
}
```

In this commit, I've started to address this by introducing a new GeometryCow
variant, which is just like Geometry, but with a reference to the inner type.

```
pub enum GeometryCow<'a, T>
where
    T: CoordNum,
{
    Point(&'a Point<T>),
    Line(&'a Line<T>),
    LineString(&'a LineString<T>),
    Polygon(&'a Polygon<T>),
    MultiPoint(&'a MultiPoint<T>),
    MultiLineString(&'a MultiLineString<T>),
    MultiPolygon(&'a MultiPolygon<T>),
    GeometryCollection(&'a GeometryCollection<T>),
    Rect(&'a Rect<T>),
    Triangle(&'a Triangle<T>),
}

impl<F: 'static + GeoFloat> Relate<F, GeometryCow<F>> for GeometryCow<F> {
    fn relate(&self, other: &GeometryCow<F>) -> IntersectionMatrix {
        let mut relate_computer = relate_computer::RelateComputer::new(self, other);
        relate_computer.compute_intersection_matrix()
    }
}
```

Then the problematic clones go away:
```
fn color(line: &Line, polygon: &Polygon) -> Color {
    let line_geometry = GeometryCow::Line(line);
    let polygon_geometry = GeometryCow::Polygon(polygon);

    if polygon_geometry.relate(line_geometry).is_contains() {
        Color::Green
    } else {
        Color::Red
    }
}
```

So, that works... *but* `relate` in turn relies on a bunch of functionality
that's already implemented on `Geometry`. I've started down the road of more or
less copy/pasting those impls for `GeometryCow`. It's almost entirely
simple dispatch logic - e.g.  whereas before we had something like this:

```
impl<T> BoundingRect<T> for Geometry<T>
where
    T: CoordNum,
{
    type Output = Option<Rect<T>>;

    fn bounding_rect(&self) -> Self::Output {
        match self {
            Geometry::Point(g) => Some(g.bounding_rect()),
            Geometry::Line(g) => Some(g.bounding_rect()),
            Geometry::LineString(g) => g.bounding_rect(),
            Geometry::Polygon(g) => g.bounding_rect(),
            Geometry::MultiPoint(g) => g.bounding_rect(),
            Geometry::MultiLineString(g) => g.bounding_rect(),
            Geometry::MultiPolygon(g) => g.bounding_rect(),
            Geometry::GeometryCollection(g) => g.bounding_rect(),
            Geometry::Rect(g) => Some(g.bounding_rect()),
            Geometry::Triangle(g) => Some(g.bounding_rect()),
        }
    }
}
```

We now also need an almost identical impl like this:
```
impl<T> BoundingRect<T> for GeometryCow<'_, T>
where
    T: CoordNum,
{
    type Output = Option<Rect<T>>;

    fn bounding_rect(&self) -> Self::Output {
        match self {
            GeometryCow::Point(g) => Some(g.bounding_rect()),
            GeometryCow::Line(g) => Some(g.bounding_rect()),
            GeometryCow::LineString(g) => g.bounding_rect(),
            GeometryCow::Polygon(g) => g.bounding_rect(),
            GeometryCow::MultiPoint(g) => g.bounding_rect(),
            GeometryCow::MultiLineString(g) => g.bounding_rect(),
            GeometryCow::MultiPolygon(g) => g.bounding_rect(),
            GeometryCow::GeometryCollection(g) => g.bounding_rect(),
            GeometryCow::Rect(g) => Some(g.bounding_rect()),
            GeometryCow::Triangle(g) => Some(g.bounding_rect()),
        }
    }
}
```

This is doable... but is there a better way? Some way to avoid all the mostly
duplication? Or is there some more conventional way this could be done?
  • Loading branch information
michaelkirk committed Jan 26, 2021
1 parent 484f87e commit c7de1ff
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 60 deletions.
26 changes: 24 additions & 2 deletions geo/src/algorithm/bounding_rect.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::utils::{partial_max, partial_min};
use crate::{
CoordNum, Coordinate, Geometry, GeometryCollection, Line, LineString, MultiLineString,
MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle,
CoordNum, Coordinate, Geometry, GeometryCollection, GeometryCow, Line, LineString,
MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle,
};
use geo_types::private_utils::{get_bounding_rect, line_string_bounding_rect};

Expand Down Expand Up @@ -177,6 +177,28 @@ where
}
}

impl<T> BoundingRect<T> for GeometryCow<'_, T>
where
T: CoordNum,
{
type Output = Option<Rect<T>>;

fn bounding_rect(&self) -> Self::Output {
match self {
GeometryCow::Point(g) => Some(g.bounding_rect()),
GeometryCow::Line(g) => Some(g.bounding_rect()),
GeometryCow::LineString(g) => g.bounding_rect(),
GeometryCow::Polygon(g) => g.bounding_rect(),
GeometryCow::MultiPoint(g) => g.bounding_rect(),
GeometryCow::MultiLineString(g) => g.bounding_rect(),
GeometryCow::MultiPolygon(g) => g.bounding_rect(),
GeometryCow::GeometryCollection(g) => g.bounding_rect(),
GeometryCow::Rect(g) => Some(g.bounding_rect()),
GeometryCow::Triangle(g) => Some(g.bounding_rect()),
}
}
}

impl<T> BoundingRect<T> for GeometryCollection<T>
where
T: CoordNum,
Expand Down
11 changes: 4 additions & 7 deletions geo/src/algorithm/contains/polygon.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::Contains;
use crate::intersects::Intersects;
use crate::relate::Relate;
use crate::{
CoordNum, Coordinate, GeoFloat, Geometry, Line, LineString, MultiPolygon, Point, Polygon,
CoordNum, Coordinate, GeoFloat, GeometryCow, Line, LineString, MultiPolygon, Point, Polygon,
};

// ┌─────────────────────────────┐
Expand All @@ -27,17 +28,13 @@ where
}
}

// TODO: ensure DE-9IM compliance: esp., when
// line.start and line.end is on the boundaries
impl<T> Contains<Line<T>> for Polygon<T>
where
T: 'static + GeoFloat,
{
fn contains(&self, line: &Line<T>) -> bool {
use crate::algorithm::relate::Relate;
// These clones are unfortunate. Can we get something like a GeometryRef type?
let polygon = Geometry::Polygon(self.clone());
let line = Geometry::Line(line.clone());
let polygon = GeometryCow::from(self);
let line = GeometryCow::from(line);
polygon.relate(&line).is_contains()
}
}
Expand Down
36 changes: 24 additions & 12 deletions geo/src/algorithm/coordinate_position.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::algorithm::{
bounding_rect::BoundingRect, dimensions::HasDimensions, intersects::Intersects,
};
use crate::{
Coordinate, GeoNum, Geometry, GeometryCollection, Line, LineString, MultiLineString,
MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle,
Coordinate, GeoNum, Geometry, GeometryCollection, GeometryCow, Line, LineString,
MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle,
};

/// The position of a `Coordinate` relative to a `Geometry`
Expand Down Expand Up @@ -332,40 +332,52 @@ where
coord: &Coordinate<T>,
is_inside: &mut bool,
boundary_count: &mut usize,
) {
GeometryCow::from(self).calculate_coordinate_position(coord, is_inside, boundary_count)
}
}

impl<'a, T: GeoNum> CoordinatePosition for GeometryCow<'a, T> {
type Scalar = T;
fn calculate_coordinate_position(
&self,
coord: &Coordinate<Self::Scalar>,
is_inside: &mut bool,
boundary_count: &mut usize,
) {
if self.is_empty() {
return;
}

match self {
Geometry::Point(point) => {
GeometryCow::Point(point) => {
point.calculate_coordinate_position(coord, is_inside, boundary_count)
}
Geometry::Line(line) => {
GeometryCow::Line(line) => {
line.calculate_coordinate_position(coord, is_inside, boundary_count)
}
Geometry::LineString(line_string) => {
GeometryCow::LineString(line_string) => {
line_string.calculate_coordinate_position(coord, is_inside, boundary_count)
}
Geometry::Polygon(polygon) => {
GeometryCow::Polygon(polygon) => {
polygon.calculate_coordinate_position(coord, is_inside, boundary_count)
}
Geometry::Rect(rect) => {
GeometryCow::Rect(rect) => {
rect.calculate_coordinate_position(coord, is_inside, boundary_count)
}
Geometry::Triangle(triangle) => {
GeometryCow::Triangle(triangle) => {
triangle.calculate_coordinate_position(coord, is_inside, boundary_count)
}
Geometry::MultiPoint(multi_point) => {
GeometryCow::MultiPoint(multi_point) => {
multi_point.calculate_coordinate_position(coord, is_inside, boundary_count)
}
Geometry::MultiLineString(multi_line_string) => {
GeometryCow::MultiLineString(multi_line_string) => {
multi_line_string.calculate_coordinate_position(coord, is_inside, boundary_count)
}
Geometry::MultiPolygon(multi_polygon) => {
GeometryCow::MultiPolygon(multi_polygon) => {
multi_polygon.calculate_coordinate_position(coord, is_inside, boundary_count)
}
Geometry::GeometryCollection(geometry_collection) => {
GeometryCow::GeometryCollection(geometry_collection) => {
geometry_collection.calculate_coordinate_position(coord, is_inside, boundary_count)
}
}
Expand Down
51 changes: 49 additions & 2 deletions geo/src/algorithm/dimensions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
CoordNum, Geometry, GeometryCollection, Line, LineString, MultiLineString, MultiPoint,
MultiPolygon, Point, Polygon, Rect, Triangle,
CoordNum, Geometry, GeometryCollection, GeometryCow, Line, LineString, MultiLineString,
MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle,
};

/// Geometries can have 0, 1, or two dimensions. Or, in the case of an [`empty`](#is_empty)
Expand Down Expand Up @@ -178,6 +178,53 @@ impl<C: CoordNum> HasDimensions for Geometry<C> {
}
}

impl<C: CoordNum> HasDimensions for GeometryCow<'_, C> {
fn is_empty(&self) -> bool {
match self {
GeometryCow::Point(g) => g.is_empty(),
GeometryCow::Line(g) => g.is_empty(),
GeometryCow::LineString(g) => g.is_empty(),
GeometryCow::Polygon(g) => g.is_empty(),
GeometryCow::MultiPoint(g) => g.is_empty(),
GeometryCow::MultiLineString(g) => g.is_empty(),
GeometryCow::MultiPolygon(g) => g.is_empty(),
GeometryCow::GeometryCollection(g) => g.is_empty(),
GeometryCow::Rect(g) => g.is_empty(),
GeometryCow::Triangle(g) => g.is_empty(),
}
}

fn dimensions(&self) -> Dimensions {
match self {
GeometryCow::Point(g) => g.dimensions(),
GeometryCow::Line(g) => g.dimensions(),
GeometryCow::LineString(g) => g.dimensions(),
GeometryCow::Polygon(g) => g.dimensions(),
GeometryCow::MultiPoint(g) => g.dimensions(),
GeometryCow::MultiLineString(g) => g.dimensions(),
GeometryCow::MultiPolygon(g) => g.dimensions(),
GeometryCow::GeometryCollection(g) => g.dimensions(),
GeometryCow::Rect(g) => g.dimensions(),
GeometryCow::Triangle(g) => g.dimensions(),
}
}

fn boundary_dimensions(&self) -> Dimensions {
match self {
GeometryCow::Point(g) => g.boundary_dimensions(),
GeometryCow::Line(g) => g.boundary_dimensions(),
GeometryCow::LineString(g) => g.boundary_dimensions(),
GeometryCow::Polygon(g) => g.boundary_dimensions(),
GeometryCow::MultiPoint(g) => g.boundary_dimensions(),
GeometryCow::MultiLineString(g) => g.boundary_dimensions(),
GeometryCow::MultiPolygon(g) => g.boundary_dimensions(),
GeometryCow::GeometryCollection(g) => g.boundary_dimensions(),
GeometryCow::Rect(g) => g.boundary_dimensions(),
GeometryCow::Triangle(g) => g.boundary_dimensions(),
}
}
}

impl<C: CoordNum> HasDimensions for Point<C> {
fn is_empty(&self) -> bool {
false
Expand Down
6 changes: 3 additions & 3 deletions geo/src/algorithm/relate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ pub(crate) use edge_end_builder::EdgeEndBuilder;
pub(crate) use intersection_matrix::IntersectionMatrix;
pub(crate) use relate_node::RelateNodeFactory;

use crate::{GeoFloat, Geometry};
use crate::{GeoFloat, GeometryCow};

pub(crate) trait Relate<F, T> {
fn relate(&self, other: &T) -> IntersectionMatrix;
}

impl<F: 'static + GeoFloat> Relate<F, Geometry<F>> for Geometry<F> {
fn relate(&self, other: &Geometry<F>) -> IntersectionMatrix {
impl<F: 'static + GeoFloat> Relate<F, GeometryCow<'_, F>> for GeometryCow<'_, F> {
fn relate(&self, other: &GeometryCow<F>) -> IntersectionMatrix {
let mut relate_computer = relate_computer::RelateComputer::new(self, other);
relate_computer.compute_intersection_matrix()
}
Expand Down
24 changes: 16 additions & 8 deletions geo/src/algorithm/relate/relate_computer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@ use crate::geomgraph::{
algorithm::RobustLineIntersector, index::SegmentIntersector, Edge, EdgeEnd, GeometryGraph,
Location, Node, NodeMap,
};
use crate::GeoFloat;
use crate::{GeoFloat, GeometryCow};

use geo_types::Geometry;
use std::cell::RefCell;
use std::rc::Rc;

Expand Down Expand Up @@ -66,7 +65,10 @@ impl<'a, F> RelateComputer<'a, F>
where
F: 'static + GeoFloat,
{
pub fn new(geom_a: &'a Geometry<F>, geom_b: &'a Geometry<F>) -> RelateComputer<'a, F> {
pub fn new(
geom_a: &'a GeometryCow<'a, F>,
geom_b: &'a GeometryCow<'a, F>,
) -> RelateComputer<'a, F> {
Self {
graph_a: GeometryGraph::new(0, geom_a),
graph_b: GeometryGraph::new(1, geom_b),
Expand Down Expand Up @@ -659,7 +661,7 @@ where
// JTS: }
// JTS: //System.out.println(e.getLabel());
// JTS: }
fn label_isolated_edge(edge: &mut Edge<F>, target_index: usize, target: &Geometry<F>) {
fn label_isolated_edge(edge: &mut Edge<F>, target_index: usize, target: &GeometryCow<F>) {
if target.dimensions() > Dimensions::ZeroDimensional {
// REVIEW: unwrap
use crate::algorithm::coordinate_position::CoordinatePosition;
Expand Down Expand Up @@ -727,7 +729,7 @@ where
// JTS: */
// JTS: private void labelIsolatedNode(Node n, int targetIndex)
// JTS: {
fn label_isolated_node(node: &mut Node<F>, target_index: usize, geometry: &Geometry<F>) {
fn label_isolated_node(node: &mut Node<F>, target_index: usize, geometry: &GeometryCow<F>) {
// JTS: int loc = ptLocator.locate(n.getCoordinate(), arg[targetIndex].getGeometry());
use crate::algorithm::coordinate_position::CoordinatePosition;
let location = geometry.coordinate_position(node.coordinate()).into();
Expand Down Expand Up @@ -764,7 +766,9 @@ mod test {
]
.into();

let mut relate_computer = RelateComputer::new(&square_a, &square_b);
let gc1 = GeometryCow::from(&square_a);
let gc2 = GeometryCow::from(&square_b);
let mut relate_computer = RelateComputer::new(&gc1, &gc2);
let intersection_matrix = relate_computer.compute_intersection_matrix();
assert_eq!(
intersection_matrix,
Expand Down Expand Up @@ -792,7 +796,9 @@ mod test {
]
.into();

let mut relate_computer = RelateComputer::new(&square_a, &square_b);
let gca = GeometryCow::from(&square_a);
let gcb = GeometryCow::from(&square_b);
let mut relate_computer = RelateComputer::new(&gca, &gcb);
let intersection_matrix = relate_computer.compute_intersection_matrix();
assert_eq!(
intersection_matrix,
Expand Down Expand Up @@ -820,7 +826,9 @@ mod test {
]
.into();

let mut relate_computer = RelateComputer::new(&square_a, &square_b);
let gca = &GeometryCow::from(&square_a);
let gcb = &GeometryCow::from(&square_b);
let mut relate_computer = RelateComputer::new(gca, gcb);
let intersection_matrix = relate_computer.compute_intersection_matrix();
assert_eq!(
intersection_matrix,
Expand Down
99 changes: 99 additions & 0 deletions geo/src/geometry_cow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use crate::{
CoordNum, Geometry, GeometryCollection, Line, LineString, MultiLineString, MultiPoint,
MultiPolygon, Point, Polygon, Rect, Triangle,
};
use std::borrow::Cow;

#[derive(PartialEq, Debug, Hash)]
pub enum GeometryCow<'a, T>
where
T: CoordNum,
{
Point(Cow<'a, Point<T>>),
Line(Cow<'a, Line<T>>),
LineString(Cow<'a, LineString<T>>),
Polygon(Cow<'a, Polygon<T>>),
MultiPoint(Cow<'a, MultiPoint<T>>),
MultiLineString(Cow<'a, MultiLineString<T>>),
MultiPolygon(Cow<'a, MultiPolygon<T>>),
GeometryCollection(Cow<'a, GeometryCollection<T>>),
Rect(Cow<'a, Rect<T>>),
Triangle(Cow<'a, Triangle<T>>),
}

impl<'a, T: CoordNum> From<&'a Geometry<T>> for GeometryCow<'a, T> {
fn from(geometry: &'a Geometry<T>) -> Self {
match geometry {
Geometry::Point(g) => GeometryCow::Point(Cow::Borrowed(g)),
Geometry::Line(g) => GeometryCow::Line(Cow::Borrowed(g)),
Geometry::LineString(g) => GeometryCow::LineString(Cow::Borrowed(g)),
Geometry::Polygon(g) => GeometryCow::Polygon(Cow::Borrowed(g)),
Geometry::MultiPoint(g) => GeometryCow::MultiPoint(Cow::Borrowed(g)),
Geometry::MultiLineString(g) => GeometryCow::MultiLineString(Cow::Borrowed(g)),
Geometry::MultiPolygon(g) => GeometryCow::MultiPolygon(Cow::Borrowed(g)),
Geometry::GeometryCollection(g) => GeometryCow::GeometryCollection(Cow::Borrowed(g)),
Geometry::Rect(g) => GeometryCow::Rect(Cow::Borrowed(g)),
Geometry::Triangle(g) => GeometryCow::Triangle(Cow::Borrowed(g)),
}
}
}

impl<'a, T: CoordNum> From<&'a Point<T>> for GeometryCow<'a, T> {
fn from(point: &'a Point<T>) -> Self {
GeometryCow::Point(Cow::Borrowed(point))
}
}

impl<'a, T: CoordNum> From<&'a LineString<T>> for GeometryCow<'a, T> {
fn from(line_string: &'a LineString<T>) -> Self {
GeometryCow::LineString(Cow::Borrowed(line_string))
}
}

impl<'a, T: CoordNum> From<&'a Line<T>> for GeometryCow<'a, T> {
fn from(line: &'a Line<T>) -> Self {
GeometryCow::Line(Cow::Borrowed(line))
}
}

impl<'a, T: CoordNum> From<&'a Polygon<T>> for GeometryCow<'a, T> {
fn from(polygon: &'a Polygon<T>) -> Self {
GeometryCow::Polygon(Cow::Borrowed(polygon))
}
}

impl<'a, T: CoordNum> From<&'a MultiPoint<T>> for GeometryCow<'a, T> {
fn from(multi_point: &'a MultiPoint<T>) -> GeometryCow<'a, T> {
GeometryCow::MultiPoint(Cow::Borrowed(multi_point))
}
}

impl<'a, T: CoordNum> From<&'a MultiLineString<T>> for GeometryCow<'a, T> {
fn from(multi_line_string: &'a MultiLineString<T>) -> Self {
GeometryCow::MultiLineString(Cow::Borrowed(multi_line_string))
}
}

impl<'a, T: CoordNum> From<&'a MultiPolygon<T>> for GeometryCow<'a, T> {
fn from(multi_polygon: &'a MultiPolygon<T>) -> Self {
GeometryCow::MultiPolygon(Cow::Borrowed(multi_polygon))
}
}

impl<'a, T: CoordNum> From<&'a GeometryCollection<T>> for GeometryCow<'a, T> {
fn from(geometry_collection: &'a GeometryCollection<T>) -> Self {
GeometryCow::GeometryCollection(Cow::Borrowed(geometry_collection))
}
}

impl<'a, T: CoordNum> From<&'a Rect<T>> for GeometryCow<'a, T> {
fn from(rect: &'a Rect<T>) -> Self {
GeometryCow::Rect(Cow::Borrowed(rect))
}
}

impl<'a, T: CoordNum> From<&'a Triangle<T>> for GeometryCow<'a, T> {
fn from(triangle: &'a Triangle<T>) -> Self {
GeometryCow::Triangle(Cow::Borrowed(triangle))
}
}
Loading

1 comment on commit c7de1ff

@michaelkirk
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦

which is just like Geometry, but with a reference to the inner type.

Commit message is out of date - the GeometryCow actually looks like this (with a Cow not just a reference):

#[derive(PartialEq, Debug, Hash)]
 pub enum GeometryCow<'a, T>
 where
     T: CoordNum,
 {
     Point(Cow<'a, Point<T>>),
     Line(Cow<'a, Line<T>>),
     LineString(Cow<'a, LineString<T>>),
     Polygon(Cow<'a, Polygon<T>>),
     MultiPoint(Cow<'a, MultiPoint<T>>),
     MultiLineString(Cow<'a, MultiLineString<T>>),
     MultiPolygon(Cow<'a, MultiPolygon<T>>),
     GeometryCollection(Cow<'a, GeometryCollection<T>>),
     Rect(Cow<'a, Rect<T>>),
     Triangle(Cow<'a, Triangle<T>>),
 }

Please sign in to comment.