Skip to content

Commit

Permalink
Implement 3D and Measure support for geo-types only
Browse files Browse the repository at this point in the history
This PR includes #772

---

This PR focuses on `geo-types` only. This is a part of #742.

This PR changes the underlying geo-type data structures to support 3D data and measurement values (M and Z values). The PR attempts to cause relatively minor disruptions to the existing users (see breaking changes below). My knowledge of the actual geo algorithms is limited, so please ping me for any specific algo change.

All geo type structs have been renamed from `Foo<T>(...)` to `Foo<T, Z=NoValue, M=NoValue>(...)`, and several type aliases were added:

```rust
// old
pub struct LineString<T: CoordNum>(pub Vec<Coordinate<T>>);

// new
pub struct LineString<T: CoordNum, Z: ZCoord=NoValue, M: Measure=NoValue>(pub Vec<Coordinate<T, Z, M>>);

pub type LineStringM<T, M=T> = LineString<T, NoValue, M>;
pub type LineString3D<T> = LineString<T, T, NoValue>;
pub type LineString3DM<T, M=T> = LineString<T, T, M>;
```

`NoValue` is an empty struct that behaves just like a number. It supports all math and comparison operations. This means that a `Z` or `M` value can be manipulated without checking if it is actually there.  This code works for Z and M being either a number or a NoValue:

```rust
pub struct NoValue;

impl<T: CoordNum, Z: ZCoord, M: Measure> Sub for Coordinate<T, Z, M> {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self {
        coord! {
            x: self.x - rhs.x,
            y: self.y - rhs.y,
            z: self.z - rhs.z,
            m: self.m - rhs.m,
        }
    }
}
```

Function implementations can keep just the original 2D `<T>` variant, or add support for 3D `<Z>` and/or the Measure `<M>`. The above example works for all combinations of objects. This function only works for 2D objects with and without the Measure.

```rust
impl<T: CoordNum, M: Measure> Line<T, NoValue, M> {
    /// Calculate the slope (Δy/Δx).
    pub fn slope(&self) -> T {
        self.dy() / self.dx()
    }
}
```

* It is no longer possible to create Coordinate with just `x` and `y` values using implicit tuple constructor. Use `coord!` instead.
  • Loading branch information
nyurik authored and urschrei committed Aug 26, 2022
1 parent 0df7ec4 commit f916944
Show file tree
Hide file tree
Showing 18 changed files with 1,071 additions and 607 deletions.
24 changes: 24 additions & 0 deletions geo-types/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,29 @@

* Fixed: using empty `polygon![]` macro no longer requires including `line_string!` macro

### Breaking changes
* All geo types now support optional 3D (z coordinate) and M (measure) values. For example, `LineM` contains `x`, `y`, and `m` values, whereas `Line3D` has `x,y,z`. `Line3DM` has both `z` and `m`. When not used, `z` and `m` values are represented by the `NoValue` empty struct. `NoValue` behaves like a number.
* Remove deprecated functions on `Geometry<T>`:
* `into_point` - Switch to `std::convert::TryInto<Point>`
* `into_line_string` - Switch to `std::convert::TryInto<LineString>`
* `into_line` - Switch to `std::convert::TryInto<Line>`
* `into_polygon` - Switch to `std::convert::TryInto<Polygon>`
* `into_multi_point` - Switch to `std::convert::TryInto<MultiPoint>`
* `into_multi_line_string` - Switch to `std::convert::TryInto<MultiLineString>`
* `into_multi_polygon` - Switch to `std::convert::TryInto<MultiPolygon>`
* Remove deprecated `CoordinateType` trait. Use `CoordFloat` or `CoordNum` instead.
* Remove deprecated functions from `LineString<T>`
* Remove `points_iter()` -- use `points()` instead.
* Remove `num_coords()` -- use `geo::algorithm::coords_iter::CoordsIter::coords_count` instead.
* Remove deprecated functions from `Point<T>`
* Remove `lng()` -- use `x()` instead.
* Remove `set_lng()` -- use `set_x()` instead.
* Remove `lat()` -- use `y()` instead.
* Remove `set_lat()` -- use `set_y()` instead.
* Remove deprecated `Polygon<T>::is_convex()` -- use `geo::is_convex` on `poly.exterior()` instead.
* Remove deprecated `Rect<T>::try_new()` -- use `Rect::new` instead, since `Rect::try_new` will never Error. Also removes corresponding `InvalidRectCoordinatesError`.
* Replace deprecated `GeometryCollection::new()` with `GeometryCollection::new(value)`, and remove deprecated `GeometryCollection::new_from(value)`.

## 0.7.6

* You may now specify `Geometry` rather than `Geometry<f64>` since we've added
Expand All @@ -25,6 +48,7 @@
* Deprecate `GeometryCollection::from(single_geom)` in favor of `GeometryCollection::from(vec![single_geom])`
* <https://github.com/georust/geo/pull/821>


## 0.7.4

* BREAKING: Make `Rect::to_lines` return lines in winding order for `Rect::to_polygon`.
Expand Down
79 changes: 53 additions & 26 deletions geo-types/src/arbitrary.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
use crate::{
CoordFloat, Coordinate, Geometry, GeometryCollection, LineString, MultiLineString, MultiPoint,
MultiPolygon, Point, Polygon, Rect, Triangle,
CoordFloat, Coordinate, Geometry, GeometryCollection, LineString, Measure, MultiLineString,
MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle, ZCoord,
};
use std::mem;

impl<'a, T> arbitrary::Arbitrary<'a> for Coordinate<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Coordinate<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(coord! {
x: u.arbitrary::<T>()?,
y: u.arbitrary::<T>()?,
z: u.arbitrary::<Z>()?,
m: u.arbitrary::<M>()?,
})
}
}

impl<'a, T> arbitrary::Arbitrary<'a> for Point<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Point<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
u.arbitrary::<Coordinate<T>>().map(Self)
u.arbitrary::<Coordinate<T, Z, M>>().map(Self)
}
}

impl<'a, T> arbitrary::Arbitrary<'a> for LineString<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for LineString<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let coords = u.arbitrary::<Vec<Coordinate<T>>>()?;
let coords = u.arbitrary::<Vec<Coordinate<T, Z, M>>>()?;
if coords.len() < 2 {
Err(arbitrary::Error::IncorrectFormat)
} else {
Expand All @@ -39,86 +47,105 @@ where
}

fn size_hint(_depth: usize) -> (usize, Option<usize>) {
(mem::size_of::<T>() * 2, None)
(
mem::size_of::<T>() * 2 + mem::size_of::<Z>() + mem::size_of::<M>(),
None,
)
}
}

impl<'a, T> arbitrary::Arbitrary<'a> for Polygon<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Polygon<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self::new(
u.arbitrary::<LineString<T>>()?,
u.arbitrary::<Vec<LineString<T>>>()?,
u.arbitrary::<LineString<T, Z, M>>()?,
u.arbitrary::<Vec<LineString<T, Z, M>>>()?,
))
}
}

impl<'a, T> arbitrary::Arbitrary<'a> for MultiPoint<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for MultiPoint<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
u.arbitrary::<Vec<Point<T>>>().map(Self)
u.arbitrary::<Vec<Point<T, Z, M>>>().map(Self)
}
}

impl<'a, T> arbitrary::Arbitrary<'a> for MultiLineString<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for MultiLineString<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
u.arbitrary::<Vec<LineString<T>>>().map(Self)
u.arbitrary::<Vec<LineString<T, Z, M>>>().map(Self)
}
}

impl<'a, T> arbitrary::Arbitrary<'a> for MultiPolygon<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for MultiPolygon<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
u.arbitrary::<Vec<Polygon<T>>>().map(Self)
u.arbitrary::<Vec<Polygon<T, Z, M>>>().map(Self)
}
}

impl<'a, T> arbitrary::Arbitrary<'a> for GeometryCollection<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for GeometryCollection<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
u.arbitrary()
}
}

impl<'a, T> arbitrary::Arbitrary<'a> for Rect<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Rect<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self::new(
u.arbitrary::<Coordinate<T>>()?,
u.arbitrary::<Coordinate<T>>()?,
u.arbitrary::<Coordinate<T, Z, M>>()?,
u.arbitrary::<Coordinate<T, Z, M>>()?,
))
}
}

impl<'a, T> arbitrary::Arbitrary<'a> for Triangle<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Triangle<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(Self(
u.arbitrary::<Coordinate<T>>()?,
u.arbitrary::<Coordinate<T>>()?,
u.arbitrary::<Coordinate<T>>()?,
u.arbitrary::<Coordinate<T, Z, M>>()?,
u.arbitrary::<Coordinate<T, Z, M>>()?,
u.arbitrary::<Coordinate<T, Z, M>>()?,
))
}
}

impl<'a, T> arbitrary::Arbitrary<'a> for Geometry<T>
impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Geometry<T, Z, M>
where
T: arbitrary::Arbitrary<'a> + CoordFloat,
Z: arbitrary::Arbitrary<'a> + ZCoord,
M: arbitrary::Arbitrary<'a> + Measure,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let n = u.int_in_range(0..=8)?;
Expand Down
Loading

0 comments on commit f916944

Please sign in to comment.