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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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!(),
}
}
}

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!(),
Copy link
Member

Choose a reason for hiding this comment

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

Bloop

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is because I didn't add Line and Triangle traits yet, but we can do that before merging if you'd like

}
}
}
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;

/// 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