Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add geo-traits crate #1157

Merged
merged 40 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
e337d8a
Add geo-traits crate
kylebarron Feb 26, 2024
7f68df2
Update doc
kylebarron Feb 26, 2024
34ed5be
Update geo-traits/Cargo.toml
kylebarron Feb 27, 2024
d57995e
Merge branch 'main' into kyle/geo-traits-crate
kylebarron Oct 10, 2024
7308df3
Merge branch 'kyle/geo-traits-crate' of github.com:kylebarron/geo int…
kylebarron Oct 10, 2024
ee04201
Update docker images
kylebarron Oct 10, 2024
ad48ee1
Remove CoordTrait and add multiple dimensions
kylebarron Oct 10, 2024
4261836
Module docs
kylebarron Oct 10, 2024
449b39f
rename
kylebarron Oct 10, 2024
f54fffc
Add logical Dimension
kylebarron Oct 11, 2024
db08c55
partialeq and hash
kylebarron Oct 11, 2024
7ecdb7e
return impl Iterator
kylebarron Oct 12, 2024
03f6615
rename to min/max
kylebarron Oct 12, 2024
31435e8
Merge branch 'main' into kyle/geo-traits-crate
kylebarron Oct 12, 2024
1d30066
Specialized implementations of GeometryTrait
kylebarron Oct 13, 2024
ab0bf7b
clearer GeometryTrait naming
kylebarron Oct 13, 2024
33c1ef9
Rename ItemType
kylebarron Oct 13, 2024
6363ad8
Unimplemented versions
kylebarron Oct 14, 2024
9421e05
Add LineTrait and TriangleTrait and improve trait docs
kylebarron Oct 14, 2024
9552705
Fix geo-types empty Polygon
kylebarron Oct 14, 2024
3725844
Merge branch 'main' into kyle/geo-traits-crate
kylebarron Oct 14, 2024
7a2effd
Make iterators private
kylebarron Oct 14, 2024
8dcbe50
Rename Dimension -> Dimensions
kylebarron Oct 14, 2024
6889cae
No default x() and y()
kylebarron Oct 14, 2024
de7ca8a
More descriptive point panics
kylebarron Oct 14, 2024
0fb5ab8
Change to DoubleEndedIterator + ExactSizeIterator
kylebarron Oct 15, 2024
58117ce
change dimensions case
kylebarron Oct 15, 2024
6b697dd
Update geo-traits/src/point.rs
kylebarron Oct 16, 2024
2eb3b03
reorder unchecked
kylebarron Oct 16, 2024
1f68cc3
`PointTrait::is_empty`
kylebarron Oct 16, 2024
b1364ef
Merge branch 'main' into kyle/geo-traits-crate
kylebarron Oct 16, 2024
d49873b
Add sentence to is_empty
kylebarron Oct 16, 2024
7c0e90f
fix doc link
kylebarron Oct 16, 2024
09de278
Use array
kylebarron Oct 18, 2024
8418780
Update geo-traits/src/line.rs
kylebarron Oct 18, 2024
4a9b3cc
typo
kylebarron Oct 22, 2024
ba9bf98
Merge branch 'main' into kyle/geo-traits-crate
kylebarron Oct 22, 2024
4764343
Restore CoordTrait, PointTrait yields Option<CoordTrait>
kylebarron Oct 24, 2024
2b77691
Merge branch 'main' into kyle/geo-traits-crate
kylebarron Oct 24, 2024
711dfac
Update geo-traits/src/coord.rs
kylebarron Oct 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,32 @@ jobs:
# we don't want to test `proj-network` because it only enables the `proj` feature
- run: cargo test --features "use-proj use-serde"

geo_traits:
name: geo-traits
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
defaults:
run:
working-directory: geo-traits
strategy:
matrix:
container_image:
# We aim to support rust-stable plus (at least) the prior 3 releases,
# giving us about 6 months of coverage.
#
# Minimum supported rust version (MSRV)
- "georust/geo-ci:proj-9.3.1-rust-1.70"
# Two most recent releases - we omit older ones for expedient CI
- "georust/geo-ci:proj-9.3.1-rust-1.74"
- "georust/geo-ci:proj-9.3.1-rust-1.75"
container:
image: ${{ matrix.container_image }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
- run: cargo check --all-targets
- run: cargo test

geo_postgis:
name: geo-postgis
runs-on: ubuntu-latest
Expand Down Expand Up @@ -184,4 +210,3 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v3
- run: RUSTDOCFLAGS="-D warnings" cargo doc --all-features --no-deps

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
resolver = "2"
members = [
"geo",
"geo-types",
"geo-bool-ops-benches",
"geo-postgis",
"geo-test-fixtures",
"geo-traits",
"geo-types",
"jts-test-runner",
"geo-bool-ops-benches",
]

[patch.crates-io]
Expand Down
17 changes: 17 additions & 0 deletions geo-traits/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "geo-traits"
version = "0.1.0"
license = "MIT OR Apache-2.0"
repository = "https://github.com/georust/geo"
documentation = "https://docs.rs/geo-traits/"
readme = "../README.md"
keywords = ["gis", "geo", "geography", "geospatial"]
description = "Geospatial traits"
rust-version = "1.65"
edition = "2021"

[dependencies]
geo-types = "0.7"

[dev-dependencies]
approx = ">= 0.4.0, < 0.6.0"
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
77 changes: 77 additions & 0 deletions geo-traits/src/coord.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use geo_types::{Coord, CoordNum, Point};

/// A trait for accessing data from a generic Coord.
pub trait CoordTrait {
type T: CoordNum;

/// x component of this coord
fn x(&self) -> Self::T;

/// y component of this coord
fn y(&self) -> Self::T;

/// Returns a tuple that contains the x/horizontal & y/vertical component of the coord.
fn x_y(&self) -> (Self::T, Self::T) {
(self.x(), self.y())
}
}

impl<T: CoordNum> CoordTrait for Point<T> {
type T = T;

fn x(&self) -> Self::T {
self.0.x
}

fn y(&self) -> Self::T {
self.0.y
}
}

impl<T: CoordNum> CoordTrait for &Point<T> {
type T = T;

fn x(&self) -> Self::T {
self.0.x
}

fn y(&self) -> Self::T {
self.0.y
}
}

impl<T: CoordNum> CoordTrait for Coord<T> {
type T = T;

fn x(&self) -> Self::T {
self.x
}

fn y(&self) -> Self::T {
self.y
}
}

impl<T: CoordNum> CoordTrait for &Coord<T> {
type T = T;

fn x(&self) -> Self::T {
self.x
}

fn y(&self) -> Self::T {
self.y
}
}

impl<T: CoordNum> CoordTrait for (T, T) {
type T = T;

fn x(&self) -> Self::T {
self.0
}

fn y(&self) -> Self::T {
self.1
}
}
153 changes: 153 additions & 0 deletions geo-traits/src/geometry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
use geo_types::{
CoordNum, Geometry, GeometryCollection, LineString, MultiLineString, MultiPoint, MultiPolygon,
Point, Polygon, Rect,
};

use super::{
GeometryCollectionTrait, LineStringTrait, MultiLineStringTrait, MultiPointTrait,
MultiPolygonTrait, PointTrait, PolygonTrait, RectTrait,
};

/// A trait for accessing data from a generic Geometry.
#[allow(clippy::type_complexity)]
pub trait GeometryTrait {
type T: CoordNum;
type Point<'a>: 'a + PointTrait<T = Self::T>
where
Self: 'a;
type LineString<'a>: 'a + LineStringTrait<T = Self::T>
where
Self: 'a;
type Polygon<'a>: 'a + PolygonTrait<T = Self::T>
where
Self: 'a;
type MultiPoint<'a>: 'a + MultiPointTrait<T = Self::T>
where
Self: 'a;
type MultiLineString<'a>: 'a + MultiLineStringTrait<T = Self::T>
where
Self: 'a;
type MultiPolygon<'a>: 'a + MultiPolygonTrait<T = Self::T>
where
Self: 'a;
type GeometryCollection<'a>: 'a + GeometryCollectionTrait<T = Self::T>
where
Self: 'a;
type Rect<'a>: 'a + RectTrait<T = Self::T>
where
Self: 'a;

fn as_type(
&self,
) -> GeometryType<
'_,
Self::Point<'_>,
Self::LineString<'_>,
Self::Polygon<'_>,
Self::MultiPoint<'_>,
Self::MultiLineString<'_>,
Self::MultiPolygon<'_>,
Self::GeometryCollection<'_>,
Self::Rect<'_>,
>;
}

/// An enumeration of all geometry types that can be contained inside a [GeometryTrait]. This is
/// used for extracting concrete geometry types out of a [GeometryTrait].
#[derive(Debug)]
pub enum GeometryType<'a, P, L, Y, MP, ML, MY, GC, R>
where
P: PointTrait,
L: LineStringTrait,
Y: PolygonTrait,
MP: MultiPointTrait,
ML: MultiLineStringTrait,
MY: MultiPolygonTrait,
GC: GeometryCollectionTrait,
R: RectTrait,
{
Point(&'a P),
LineString(&'a L),
Polygon(&'a Y),
MultiPoint(&'a MP),
MultiLineString(&'a ML),
MultiPolygon(&'a MY),
GeometryCollection(&'a GC),
Rect(&'a R),
}

impl<'a, T: CoordNum + 'a> GeometryTrait for Geometry<T> {
type T = T;
type Point<'b> = Point<Self::T> where Self: 'b;
type LineString<'b> = LineString<Self::T> where Self: 'b;
type Polygon<'b> = Polygon<Self::T> where Self: 'b;
type MultiPoint<'b> = MultiPoint<Self::T> where Self: 'b;
type MultiLineString<'b> = MultiLineString<Self::T> where Self: 'b;
type MultiPolygon<'b> = MultiPolygon<Self::T> where Self: 'b;
type GeometryCollection<'b> = GeometryCollection<Self::T> where Self: 'b;
type Rect<'b> = Rect<Self::T> where Self: 'b;

fn as_type(
&self,
) -> GeometryType<
'_,
Point<T>,
LineString<T>,
Polygon<T>,
MultiPoint<T>,
MultiLineString<T>,
MultiPolygon<T>,
GeometryCollection<T>,
Rect<T>,
> {
match self {
Geometry::Point(p) => GeometryType::Point(p),
Geometry::LineString(p) => GeometryType::LineString(p),
Geometry::Polygon(p) => GeometryType::Polygon(p),
Geometry::MultiPoint(p) => GeometryType::MultiPoint(p),
Geometry::MultiLineString(p) => GeometryType::MultiLineString(p),
Geometry::MultiPolygon(p) => GeometryType::MultiPolygon(p),
Geometry::GeometryCollection(p) => GeometryType::GeometryCollection(p),
Geometry::Rect(p) => GeometryType::Rect(p),
_ => todo!(),
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

impl<'a, T: CoordNum + 'a> GeometryTrait for &'a Geometry<T> {
type T = T;
type Point<'b> = Point<Self::T> where Self: 'b;
type LineString<'b> = LineString<Self::T> where Self: 'b;
type Polygon<'b> = Polygon<Self::T> where Self: 'b;
type MultiPoint<'b> = MultiPoint<Self::T> where Self: 'b;
type MultiLineString<'b> = MultiLineString<Self::T> where Self: 'b;
type MultiPolygon<'b> = MultiPolygon<Self::T> where Self: 'b;
type GeometryCollection<'b> = GeometryCollection<Self::T> where Self: 'b;
type Rect<'b> = Rect<Self::T> where Self: 'b;

fn as_type(
&self,
) -> GeometryType<
'_,
Point<T>,
LineString<T>,
Polygon<T>,
MultiPoint<T>,
MultiLineString<T>,
MultiPolygon<T>,
GeometryCollection<T>,
Rect<T>,
> {
match self {
Geometry::Point(p) => GeometryType::Point(p),
Geometry::LineString(p) => GeometryType::LineString(p),
Geometry::Polygon(p) => GeometryType::Polygon(p),
Geometry::MultiPoint(p) => GeometryType::MultiPoint(p),
Geometry::MultiLineString(p) => GeometryType::MultiLineString(p),
Geometry::MultiPolygon(p) => GeometryType::MultiPolygon(p),
Geometry::GeometryCollection(p) => GeometryType::GeometryCollection(p),
Geometry::Rect(p) => GeometryType::Rect(p),
_ => todo!(),
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
64 changes: 64 additions & 0 deletions geo-traits/src/geometry_collection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use super::{GeometryCollectionIterator, GeometryTrait};
use geo_types::{CoordNum, Geometry, GeometryCollection};

/// A trait for accessing data from a generic GeometryCollection.
pub trait GeometryCollectionTrait: Sized {
type T: CoordNum;
type ItemType<'a>: 'a + GeometryTrait<T = Self::T>
where
Self: 'a;

/// An iterator over the geometries in this GeometryCollection
fn geometries(&self) -> GeometryCollectionIterator<'_, Self::T, Self::ItemType<'_>, Self> {
GeometryCollectionIterator::new(self, 0, self.num_geometries())
}

/// The number of geometries in this GeometryCollection
fn num_geometries(&self) -> usize;
Comment on lines +27 to +28
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm more of an n() fella myself, but this is much more explicit. I would be just fine with this. Though an n() alias would also be nice :)

Copy link
Member

Choose a reason for hiding this comment

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

I feel like I don't commonly see n as a shorthand for "number of things" in rust libraries. Can you point to any examples?

Even num is a little out there AFAIK - usually it's len in rust. But at least num is long-form enough to be clear even if it's not idiomatic.

(@JosiahParry perfect time for us to pull out that "bikeshedding" definition 😆)

Copy link
Member Author

Choose a reason for hiding this comment

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

I think of len as "how large is this collection", but it should be 100% obvious what "this collection" means. Maybe len is descriptive enough for GeometryCollection, but it isn't for Polygon, because does len() mean the total number of rings or just the number of interiors? For that reason I like having the trait methods be very direct and descriptive, even if slightly more verbose


/// Access to a specified geometry in this GeometryCollection
/// Will return None if the provided index is out of bounds
fn geometry(&self, i: usize) -> Option<Self::ItemType<'_>> {
if i >= self.num_geometries() {
None
} else {
unsafe { Some(self.geometry_unchecked(i)) }
}
}

/// Access to a specified geometry in this GeometryCollection
///
/// # Safety
///
/// Accessing an index out of bounds is UB.
unsafe fn geometry_unchecked(&self, i: usize) -> Self::ItemType<'_>;
}

impl<T: CoordNum> GeometryCollectionTrait for GeometryCollection<T> {
type T = T;
type ItemType<'a> = &'a Geometry<Self::T>
where
Self: 'a;

fn num_geometries(&self) -> usize {
self.0.len()
}

unsafe fn geometry_unchecked(&self, i: usize) -> Self::ItemType<'_> {
self.0.get_unchecked(i)
}
}

impl<'a, T: CoordNum> GeometryCollectionTrait for &'a GeometryCollection<T> {
type T = T;
type ItemType<'b> = &'a Geometry<Self::T> where
Self: 'b;

fn num_geometries(&self) -> usize {
self.0.len()
}

unsafe fn geometry_unchecked(&self, i: usize) -> Self::ItemType<'_> {
self.0.get_unchecked(i)
}
}
Loading
Loading