#![allow(clippy::derived_hash_with_manual_eq)] use crate::sql::array::Array; use crate::sql::fmt::Fmt; use crate::sql::value::Value; use geo::algorithm::contains::Contains; use geo::algorithm::intersects::Intersects; use geo::{Coord, LineString, Point, Polygon}; use geo_types::{MultiLineString, MultiPoint, MultiPolygon}; use revision::revisioned; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::iter::once; use std::{fmt, hash}; pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Geometry"; #[revisioned(revision = 1)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename = "$surrealdb::private::sql::Geometry")] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[non_exhaustive] pub enum Geometry { Point(Point), Line(LineString), Polygon(Polygon), MultiPoint(MultiPoint), MultiLine(MultiLineString), MultiPolygon(MultiPolygon), Collection(Vec), // Add new variants here } impl Geometry { /// Check if this is a Point pub fn is_point(&self) -> bool { matches!(self, Self::Point(_)) } /// Check if this is a Line pub fn is_line(&self) -> bool { matches!(self, Self::Line(_)) } /// Check if this is a Polygon pub fn is_polygon(&self) -> bool { matches!(self, Self::Polygon(_)) } /// Check if this is a MultiPoint pub fn is_multipoint(&self) -> bool { matches!(self, Self::MultiPoint(_)) } /// Check if this is a MultiLine pub fn is_multiline(&self) -> bool { matches!(self, Self::MultiLine(_)) } /// Check if this is a MultiPolygon pub fn is_multipolygon(&self) -> bool { matches!(self, Self::MultiPolygon(_)) } /// Check if this is not a Collection pub fn is_geometry(&self) -> bool { !matches!(self, Self::Collection(_)) } /// Check if this is a Collection pub fn is_collection(&self) -> bool { matches!(self, Self::Collection(_)) } /// Get the type of this Geometry as text pub fn as_type(&self) -> &'static str { match self { Self::Point(_) => "Point", Self::Line(_) => "LineString", Self::Polygon(_) => "Polygon", Self::MultiPoint(_) => "MultiPoint", Self::MultiLine(_) => "MultiLineString", Self::MultiPolygon(_) => "MultiPolygon", Self::Collection(_) => "GeometryCollection", } } /// Get the raw coordinates of this Geometry as an Array pub fn as_coordinates(&self) -> Value { fn point(v: &Point) -> Value { Array::from(vec![v.x(), v.y()]).into() } fn line(v: &LineString) -> Value { v.points().map(|v| point(&v)).collect::>().into() } fn polygon(v: &Polygon) -> Value { once(v.exterior()).chain(v.interiors()).map(line).collect::>().into() } fn multipoint(v: &MultiPoint) -> Value { v.iter().map(point).collect::>().into() } fn multiline(v: &MultiLineString) -> Value { v.iter().map(line).collect::>().into() } fn multipolygon(v: &MultiPolygon) -> Value { v.iter().map(polygon).collect::>().into() } fn collection(v: &[Geometry]) -> Value { v.iter().map(Geometry::as_coordinates).collect::>().into() } match self { Self::Point(v) => point(v), Self::Line(v) => line(v), Self::Polygon(v) => polygon(v), Self::MultiPoint(v) => multipoint(v), Self::MultiLine(v) => multiline(v), Self::MultiPolygon(v) => multipolygon(v), Self::Collection(v) => collection(v), } } } impl PartialOrd for Geometry { #[rustfmt::skip] fn partial_cmp(&self, other: &Self) -> Option { fn coord(v: &Coord) -> (f64, f64) { v.x_y() } fn point(v: &Point) -> (f64, f64) { coord(&v.0) } fn line(v: &LineString) -> impl Iterator + '_ { v.into_iter().map(coord) } fn polygon(v: &Polygon) -> impl Iterator + '_ { v.interiors().iter().chain(once(v.exterior())).flat_map(line) } fn multipoint(v: &MultiPoint) -> impl Iterator + '_ { v.iter().map(point) } fn multiline(v: &MultiLineString) -> impl Iterator + '_ { v.iter().flat_map(line) } fn multipolygon(v: &MultiPolygon) -> impl Iterator + '_ { v.iter().flat_map(polygon) } match (self, other) { // (Self::Point(_), Self::Line(_)) => Some(Ordering::Less), (Self::Point(_), Self::Polygon(_)) => Some(Ordering::Less), (Self::Point(_), Self::MultiPoint(_)) => Some(Ordering::Less), (Self::Point(_), Self::MultiLine(_)) => Some(Ordering::Less), (Self::Point(_), Self::MultiPolygon(_)) => Some(Ordering::Less), (Self::Point(_), Self::Collection(_)) => Some(Ordering::Less), // (Self::Line(_), Self::Point(_)) => Some(Ordering::Greater), (Self::Line(_), Self::Polygon(_)) => Some(Ordering::Less), (Self::Line(_), Self::MultiPoint(_)) => Some(Ordering::Less), (Self::Line(_), Self::MultiLine(_)) => Some(Ordering::Less), (Self::Line(_), Self::MultiPolygon(_)) => Some(Ordering::Less), (Self::Line(_), Self::Collection(_)) => Some(Ordering::Less), // (Self::Polygon(_), Self::Point(_)) => Some(Ordering::Greater), (Self::Polygon(_), Self::Line(_)) => Some(Ordering::Greater), (Self::Polygon(_), Self::MultiPoint(_)) => Some(Ordering::Less), (Self::Polygon(_), Self::MultiLine(_)) => Some(Ordering::Less), (Self::Polygon(_), Self::MultiPolygon(_)) => Some(Ordering::Less), (Self::Polygon(_), Self::Collection(_)) => Some(Ordering::Less), // (Self::MultiPoint(_), Self::Point(_)) => Some(Ordering::Greater), (Self::MultiPoint(_), Self::Line(_)) => Some(Ordering::Greater), (Self::MultiPoint(_), Self::Polygon(_)) => Some(Ordering::Greater), (Self::MultiPoint(_), Self::MultiLine(_)) => Some(Ordering::Less), (Self::MultiPoint(_), Self::MultiPolygon(_)) => Some(Ordering::Less), (Self::MultiPoint(_), Self::Collection(_)) => Some(Ordering::Less), // (Self::MultiLine(_), Self::Point(_)) => Some(Ordering::Greater), (Self::MultiLine(_), Self::Line(_)) => Some(Ordering::Greater), (Self::MultiLine(_), Self::Polygon(_)) => Some(Ordering::Greater), (Self::MultiLine(_), Self::MultiPoint(_)) => Some(Ordering::Greater), (Self::MultiLine(_), Self::MultiPolygon(_)) => Some(Ordering::Less), (Self::MultiLine(_), Self::Collection(_)) => Some(Ordering::Less), // (Self::MultiPolygon(_), Self::Point(_)) => Some(Ordering::Greater), (Self::MultiPolygon(_), Self::Line(_)) => Some(Ordering::Greater), (Self::MultiPolygon(_), Self::Polygon(_)) => Some(Ordering::Greater), (Self::MultiPolygon(_), Self::MultiPoint(_)) => Some(Ordering::Greater), (Self::MultiPolygon(_), Self::MultiLine(_)) => Some(Ordering::Greater), (Self::MultiPolygon(_), Self::Collection(_)) => Some(Ordering::Less), // (Self::Collection(_), Self::Point(_)) => Some(Ordering::Greater), (Self::Collection(_), Self::Line(_)) => Some(Ordering::Greater), (Self::Collection(_), Self::Polygon(_)) => Some(Ordering::Greater), (Self::Collection(_), Self::MultiPoint(_)) => Some(Ordering::Greater), (Self::Collection(_), Self::MultiLine(_)) => Some(Ordering::Greater), (Self::Collection(_), Self::MultiPolygon(_)) => Some(Ordering::Greater), // (Self::Point(a), Self::Point(b)) => point(a).partial_cmp(&point(b)), (Self::Line(a), Self::Line(b)) => line(a).partial_cmp(line(b)), (Self::Polygon(a), Self::Polygon(b)) => polygon(a).partial_cmp(polygon(b)), (Self::MultiPoint(a), Self::MultiPoint(b)) => multipoint(a).partial_cmp(multipoint(b)), (Self::MultiLine(a), Self::MultiLine(b)) => multiline(a).partial_cmp(multiline(b)), (Self::MultiPolygon(a), Self::MultiPolygon(b)) => multipolygon(a).partial_cmp(multipolygon(b)), (Self::Collection(a), Self::Collection(b)) => a.partial_cmp(b), } } } impl From<(f64, f64)> for Geometry { fn from(v: (f64, f64)) -> Self { Self::Point(v.into()) } } impl From<[f64; 2]> for Geometry { fn from(v: [f64; 2]) -> Self { Self::Point(v.into()) } } impl From> for Geometry { fn from(v: Point) -> Self { Self::Point(v) } } impl From> for Geometry { fn from(v: LineString) -> Self { Self::Line(v) } } impl From> for Geometry { fn from(v: Polygon) -> Self { Self::Polygon(v) } } impl From> for Geometry { fn from(v: MultiPoint) -> Self { Self::MultiPoint(v) } } impl From> for Geometry { fn from(v: MultiLineString) -> Self { Self::MultiLine(v) } } impl From> for Geometry { fn from(v: MultiPolygon) -> Self { Self::MultiPolygon(v) } } impl From> for Geometry { fn from(v: Vec) -> Self { Self::Collection(v) } } impl From>> for Geometry { fn from(v: Vec>) -> Self { Self::MultiPoint(MultiPoint(v)) } } impl From>> for Geometry { fn from(v: Vec>) -> Self { Self::MultiLine(MultiLineString(v)) } } impl From>> for Geometry { fn from(v: Vec>) -> Self { Self::MultiPolygon(MultiPolygon(v)) } } impl From for geo::Geometry { fn from(v: Geometry) -> Self { match v { Geometry::Point(v) => v.into(), Geometry::Line(v) => v.into(), Geometry::Polygon(v) => v.into(), Geometry::MultiPoint(v) => v.into(), Geometry::MultiLine(v) => v.into(), Geometry::MultiPolygon(v) => v.into(), Geometry::Collection(v) => v.into_iter().collect::>(), } } } impl FromIterator for geo::Geometry { fn from_iter>(iter: I) -> Self { let mut c: Vec> = vec![]; for i in iter { c.push(i.into()) } geo::Geometry::GeometryCollection(geo::GeometryCollection(c)) } } impl Geometry { // ----------------------------------- // Value operations // ----------------------------------- pub fn contains(&self, other: &Self) -> bool { match self { Self::Point(v) => match other { Self::Point(w) => v.contains(w), Self::MultiPoint(w) => w.iter().all(|x| v.contains(x)), Self::Collection(w) => w.iter().all(|x| self.contains(x)), _ => false, }, Self::Line(v) => match other { Self::Point(w) => v.contains(w), Self::Line(w) => v.contains(w), Self::MultiLine(w) => w.iter().all(|x| w.contains(x)), Self::Collection(w) => w.iter().all(|x| self.contains(x)), _ => false, }, Self::Polygon(v) => match other { Self::Point(w) => v.contains(w), Self::Line(w) => v.contains(w), Self::Polygon(w) => v.contains(w), Self::MultiPolygon(w) => w.iter().all(|x| w.contains(x)), Self::Collection(w) => w.iter().all(|x| self.contains(x)), _ => false, }, Self::MultiPoint(v) => match other { Self::Point(w) => v.contains(w), Self::MultiPoint(w) => w.iter().all(|x| w.contains(x)), Self::Collection(w) => w.iter().all(|x| self.contains(x)), _ => false, }, Self::MultiLine(v) => match other { Self::Point(w) => v.contains(w), Self::Line(w) => v.contains(w), Self::MultiLine(w) => w.iter().all(|x| w.contains(x)), Self::Collection(w) => w.iter().all(|x| self.contains(x)), _ => false, }, Self::MultiPolygon(v) => match other { Self::Point(w) => v.contains(w), Self::Line(w) => v.contains(w), Self::Polygon(w) => v.contains(w), Self::MultiPoint(w) => v.contains(w), Self::MultiLine(w) => v.contains(w), Self::MultiPolygon(w) => v.contains(w), Self::Collection(w) => w.iter().all(|x| self.contains(x)), }, Self::Collection(v) => v.iter().all(|x| x.contains(other)), } } pub fn intersects(&self, other: &Self) -> bool { match self { Self::Point(v) => match other { Self::Point(w) => v.intersects(w), Self::Line(w) => v.intersects(w), Self::Polygon(w) => v.intersects(w), Self::MultiPoint(w) => v.intersects(w), Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)), Self::MultiPolygon(w) => v.intersects(w), Self::Collection(w) => w.iter().all(|x| self.intersects(x)), }, Self::Line(v) => match other { Self::Point(w) => v.intersects(w), Self::Line(w) => v.intersects(w), Self::Polygon(w) => v.intersects(w), Self::MultiPoint(w) => v.intersects(w), Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)), Self::MultiPolygon(w) => v.intersects(w), Self::Collection(w) => w.iter().all(|x| self.intersects(x)), }, Self::Polygon(v) => match other { Self::Point(w) => v.intersects(w), Self::Line(w) => v.intersects(w), Self::Polygon(w) => v.intersects(w), Self::MultiPoint(w) => v.intersects(w), Self::MultiLine(w) => v.intersects(w), Self::MultiPolygon(w) => v.intersects(w), Self::Collection(w) => w.iter().all(|x| self.intersects(x)), }, Self::MultiPoint(v) => match other { Self::Point(w) => v.intersects(w), Self::Line(w) => v.intersects(w), Self::Polygon(w) => v.intersects(w), Self::MultiPoint(w) => v.intersects(w), Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)), Self::MultiPolygon(w) => v.intersects(w), Self::Collection(w) => w.iter().all(|x| self.intersects(x)), }, Self::MultiLine(v) => match other { Self::Point(w) => v.intersects(w), Self::Line(w) => v.intersects(w), Self::Polygon(w) => v.intersects(w), Self::MultiPoint(w) => v.intersects(w), Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)), Self::MultiPolygon(w) => v.intersects(w), Self::Collection(w) => w.iter().all(|x| self.intersects(x)), }, Self::MultiPolygon(v) => match other { Self::Point(w) => v.intersects(w), Self::Line(w) => v.intersects(w), Self::Polygon(w) => v.intersects(w), Self::MultiPoint(w) => v.intersects(w), Self::MultiLine(w) => v.intersects(w), Self::MultiPolygon(w) => v.intersects(w), Self::Collection(w) => w.iter().all(|x| self.intersects(x)), }, Self::Collection(v) => v.iter().all(|x| x.intersects(other)), } } } impl fmt::Display for Geometry { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Point(v) => { write!(f, "({}, {})", v.x(), v.y()) } Self::Line(v) => write!( f, "{{ type: 'LineString', coordinates: [{}] }}", Fmt::comma_separated(v.points().map(|v| Fmt::new(v, |v, f| write!( f, "[{}, {}]", v.x(), v.y() )))) ), Self::Polygon(v) => write!( f, "{{ type: 'Polygon', coordinates: [[{}]{}] }}", Fmt::comma_separated(v.exterior().points().map(|v| Fmt::new(v, |v, f| write!( f, "[{}, {}]", v.x(), v.y() )))), Fmt::new(v.interiors(), |interiors, f| { match interiors.len() { 0 => Ok(()), _ => write!( f, ", [{}]", Fmt::comma_separated(interiors.iter().map(|i| Fmt::new(i, |i, f| { write!( f, "[{}]", Fmt::comma_separated(i.points().map(|v| Fmt::new( v, |v, f| write!(f, "[{}, {}]", v.x(), v.y()) ))) ) }))) ), } }) ), Self::MultiPoint(v) => { write!( f, "{{ type: 'MultiPoint', coordinates: [{}] }}", Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f| write!( f, "[{}, {}]", v.x(), v.y() )))) ) } Self::MultiLine(v) => write!( f, "{{ type: 'MultiLineString', coordinates: [{}] }}", Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f| write!( f, "[{}]", Fmt::comma_separated(v.points().map(|v| Fmt::new(v, |v, f| write!( f, "[{}, {}]", v.x(), v.y() )))) )))) ), Self::MultiPolygon(v) => write!( f, "{{ type: 'MultiPolygon', coordinates: [{}] }}", Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f| { write!( f, "[[{}]{}]", Fmt::comma_separated( v.exterior().points().map(|v| Fmt::new(v, |v, f| write!( f, "[{}, {}]", v.x(), v.y() ))) ), Fmt::new(v.interiors(), |interiors, f| { match interiors.len() { 0 => Ok(()), _ => write!( f, ", [{}]", Fmt::comma_separated(interiors.iter().map(|i| Fmt::new( i, |i, f| { write!( f, "[{}]", Fmt::comma_separated(i.points().map(|v| Fmt::new( v, |v, f| write!(f, "[{}, {}]", v.x(), v.y()) ))) ) } ))) ), } }) ) }))), ), Self::Collection(v) => { write!( f, "{{ type: 'GeometryCollection', geometries: [{}] }}", Fmt::comma_separated(v) ) } } } } impl hash::Hash for Geometry { fn hash(&self, state: &mut H) { match self { Geometry::Point(p) => { "Point".hash(state); p.x().to_bits().hash(state); p.y().to_bits().hash(state); } Geometry::Line(l) => { "Line".hash(state); l.points().for_each(|v| { v.x().to_bits().hash(state); v.y().to_bits().hash(state); }); } Geometry::Polygon(p) => { "Polygon".hash(state); p.exterior().points().for_each(|ext| { ext.x().to_bits().hash(state); ext.y().to_bits().hash(state); }); p.interiors().iter().for_each(|int| { int.points().for_each(|v| { v.x().to_bits().hash(state); v.y().to_bits().hash(state); }); }); } Geometry::MultiPoint(v) => { "MultiPoint".hash(state); v.0.iter().for_each(|v| { v.x().to_bits().hash(state); v.y().to_bits().hash(state); }); } Geometry::MultiLine(ml) => { "MultiLine".hash(state); ml.0.iter().for_each(|ls| { ls.points().for_each(|p| { p.x().to_bits().hash(state); p.y().to_bits().hash(state); }); }); } Geometry::MultiPolygon(mp) => { "MultiPolygon".hash(state); mp.0.iter().for_each(|p| { p.exterior().points().for_each(|ext| { ext.x().to_bits().hash(state); ext.y().to_bits().hash(state); }); p.interiors().iter().for_each(|int| { int.points().for_each(|v| { v.x().to_bits().hash(state); v.y().to_bits().hash(state); }); }); }); } Geometry::Collection(v) => { "GeometryCollection".hash(state); v.iter().for_each(|v| v.hash(state)); } } } }