#![allow(clippy::derived_hash_with_manual_eq)]
use std::cmp::Ordering;
use std::hash;
use std::iter::once;
use geo::algorithm::contains::Contains;
use geo::algorithm::intersects::Intersects;
use geo::{Coord, LineString, LinesIter, Point, Polygon};
use geo_types::{MultiLineString, MultiPoint, MultiPolygon};
use revision::revisioned;
use storekey::{BorrowDecode, Encode};
use surrealdb_types::{SqlFormat, ToSql, write_sql};
use super::Object;
use crate::fmt::Fmt;
use crate::val::{Array, Value};
#[revisioned(revision = 1)]
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub(crate) enum Geometry {
Point(Point<f64>),
Line(LineString<f64>),
Polygon(Polygon<f64>),
MultiPoint(MultiPoint<f64>),
MultiLine(MultiLineString<f64>),
MultiPolygon(MultiPolygon<f64>),
Collection(Vec<Geometry>),
}
impl Geometry {
pub fn is_geometry(&self) -> bool {
!matches!(self, Self::Collection(_))
}
pub fn is_collection(&self) -> bool {
matches!(self, Self::Collection(_))
}
pub fn is_valid(&self) -> bool {
match self {
Geometry::Point(p) => {
(-90.0..=90.0).contains(&p.0.y) && (-180.0..=180.0).contains(&p.0.x)
}
Geometry::MultiPoint(v) => v
.iter()
.all(|p| (-90.0..=90.0).contains(&p.0.y) && (-180.0..=180.0).contains(&p.0.x)),
Geometry::Line(v) => v.lines_iter().all(|l| {
(-90.0..=90.0).contains(&l.start.y)
&& (-180.0..=180.0).contains(&l.start.x)
&& (-90.0..=90.0).contains(&l.end.y)
&& (-180.0..=180.0).contains(&l.end.x)
}),
Geometry::Polygon(v) => v.lines_iter().all(|l| {
(-90.0..=90.0).contains(&l.start.y)
&& (-180.0..=180.0).contains(&l.start.x)
&& (-90.0..=90.0).contains(&l.end.y)
&& (-180.0..=180.0).contains(&l.end.x)
}),
Geometry::MultiLine(v) => v.iter().all(|l| {
l.lines_iter().all(|l| {
(-90.0..=90.0).contains(&l.start.y)
&& (-180.0..=180.0).contains(&l.start.x)
&& (-90.0..=90.0).contains(&l.end.y)
&& (-180.0..=180.0).contains(&l.end.x)
})
}),
Geometry::MultiPolygon(v) => v.iter().all(|p| {
p.lines_iter().all(|l| {
(-90.0..=90.0).contains(&l.start.y)
&& (-180.0..=180.0).contains(&l.start.x)
&& (-90.0..=90.0).contains(&l.end.y)
&& (-180.0..=180.0).contains(&l.end.x)
})
}),
Geometry::Collection(v) => v.iter().all(Geometry::is_valid),
}
}
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",
}
}
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::<Vec<Value>>().into()
}
fn polygon(v: &Polygon) -> Value {
once(v.exterior()).chain(v.interiors()).map(line).collect::<Vec<Value>>().into()
}
fn multipoint(v: &MultiPoint) -> Value {
v.iter().map(point).collect::<Vec<Value>>().into()
}
fn multiline(v: &MultiLineString) -> Value {
v.iter().map(line).collect::<Vec<Value>>().into()
}
fn multipolygon(v: &MultiPolygon) -> Value {
v.iter().map(polygon).collect::<Vec<Value>>().into()
}
fn collection(v: &[Geometry]) -> Value {
v.iter().map(Geometry::as_coordinates).collect::<Vec<Value>>().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),
}
}
pub fn as_object(&self) -> Object {
let mut obj = Object::default();
obj.insert("type", self.as_type().into());
obj.insert(
match self {
Self::Collection(_) => "geometries",
_ => "coordinates",
},
self.as_coordinates(),
);
obj
}
#[cfg_attr(not(feature = "scripting"), expect(dead_code))]
pub(crate) fn try_from_object(object: &Object) -> Option<Geometry> {
if object.len() != 2 {
return None;
}
let Some(Value::String(key)) = object.get("type") else {
return None;
};
match key.as_str() {
"Point" => {
object.get("coordinates").and_then(Geometry::array_to_point).map(Geometry::Point)
}
"LineString" => {
object.get("coordinates").and_then(Geometry::array_to_line).map(Geometry::Line)
}
"Polygon" => object
.get("coordinates")
.and_then(Geometry::array_to_polygon)
.map(Geometry::Polygon),
"MultiPoint" => object
.get("coordinates")
.and_then(Geometry::array_to_multipoint)
.map(Geometry::MultiPoint),
"MultiLineString" => object
.get("coordinates")
.and_then(Geometry::array_to_multiline)
.map(Geometry::MultiLine),
"MultiPolygon" => object
.get("coordinates")
.and_then(Geometry::array_to_multipolygon)
.map(Geometry::MultiPolygon),
"GeometryCollection" => {
let Some(Value::Array(x)) = object.get("geometries") else {
return None;
};
let mut res = Vec::with_capacity(x.len());
for x in x.iter() {
let Value::Geometry(x) = x else {
return None;
};
res.push(x.clone());
}
Some(Geometry::Collection(res))
}
_ => None,
}
}
pub(crate) fn array_to_multipolygon(v: &Value) -> Option<MultiPolygon<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::array_to_polygon(x)?);
}
Some(MultiPolygon::new(res))
}
pub(crate) fn array_to_multiline(v: &Value) -> Option<MultiLineString<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::array_to_line(x)?);
}
Some(MultiLineString::new(res))
}
pub(crate) fn array_to_multipoint(v: &Value) -> Option<MultiPoint<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::array_to_point(x)?);
}
Some(MultiPoint::new(res))
}
pub(crate) fn array_to_polygon(v: &Value) -> Option<Polygon<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
if v.is_empty() {
return None;
}
let first = Self::array_to_line(&v[0])?;
for x in &v[1..] {
res.push(Self::array_to_line(x)?);
}
Some(Polygon::new(first, res))
}
pub(crate) fn array_to_line(v: &Value) -> Option<LineString<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::array_to_point(x)?);
}
Some(LineString::from(res))
}
pub(crate) fn array_to_point(v: &Value) -> Option<Point<f64>> {
let Value::Array(v) = v else {
return None;
};
if v.len() != 2 {
return None;
}
let Value::Number(ref a) = v.0[0] else {
return None;
};
let Value::Number(ref b) = v.0[1] else {
return None;
};
Some(Point::from(((*a).try_into().ok()?, (*b).try_into().ok()?)))
}
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 PartialOrd for Geometry {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
fn coord(v: &Coord) -> (f64, f64) {
v.x_y()
}
fn point(v: &Point) -> (f64, f64) {
coord(&v.0)
}
fn line(v: &LineString) -> impl Iterator<Item = (f64, f64)> + '_ {
v.into_iter().map(coord)
}
fn polygon(v: &Polygon) -> impl Iterator<Item = (f64, f64)> + '_ {
v.interiors().iter().chain(once(v.exterior())).flat_map(line)
}
fn multipoint(v: &MultiPoint) -> impl Iterator<Item = (f64, f64)> + '_ {
v.iter().map(point)
}
fn multiline(v: &MultiLineString) -> impl Iterator<Item = (f64, f64)> + '_ {
v.iter().flat_map(line)
}
fn multipolygon(v: &MultiPolygon) -> impl Iterator<Item = (f64, f64)> + '_ {
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<Point<f64>> for Geometry {
fn from(v: Point<f64>) -> Self {
Self::Point(v)
}
}
impl From<LineString<f64>> for Geometry {
fn from(v: LineString<f64>) -> Self {
Self::Line(v)
}
}
impl From<Polygon<f64>> for Geometry {
fn from(v: Polygon<f64>) -> Self {
Self::Polygon(v)
}
}
impl From<MultiPoint<f64>> for Geometry {
fn from(v: MultiPoint<f64>) -> Self {
Self::MultiPoint(v)
}
}
impl From<MultiLineString<f64>> for Geometry {
fn from(v: MultiLineString<f64>) -> Self {
Self::MultiLine(v)
}
}
impl From<MultiPolygon<f64>> for Geometry {
fn from(v: MultiPolygon<f64>) -> Self {
Self::MultiPolygon(v)
}
}
impl From<Vec<Geometry>> for Geometry {
fn from(v: Vec<Geometry>) -> Self {
Self::Collection(v)
}
}
impl From<Vec<Point<f64>>> for Geometry {
fn from(v: Vec<Point<f64>>) -> Self {
Self::MultiPoint(MultiPoint(v))
}
}
impl From<Vec<LineString<f64>>> for Geometry {
fn from(v: Vec<LineString<f64>>) -> Self {
Self::MultiLine(MultiLineString(v))
}
}
impl From<Vec<Polygon<f64>>> for Geometry {
fn from(v: Vec<Polygon<f64>>) -> Self {
Self::MultiPolygon(MultiPolygon(v))
}
}
impl From<Geometry> for geo::Geometry<f64> {
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::<geo::Geometry<f64>>(),
}
}
}
impl FromIterator<Geometry> for geo::Geometry<f64> {
fn from_iter<I: IntoIterator<Item = Geometry>>(iter: I) -> Self {
let mut c: Vec<geo::Geometry<f64>> = vec![];
for i in iter {
c.push(i.into())
}
geo::Geometry::GeometryCollection(geo::GeometryCollection(c))
}
}
impl From<Geometry> for surrealdb_types::Geometry {
fn from(v: Geometry) -> Self {
match v {
Geometry::Point(v) => surrealdb_types::Geometry::Point(v),
Geometry::Line(v) => surrealdb_types::Geometry::Line(v),
Geometry::Polygon(v) => surrealdb_types::Geometry::Polygon(v),
Geometry::MultiPoint(v) => surrealdb_types::Geometry::MultiPoint(v),
Geometry::MultiLine(v) => surrealdb_types::Geometry::MultiLine(v),
Geometry::MultiPolygon(v) => surrealdb_types::Geometry::MultiPolygon(v),
Geometry::Collection(v) => {
surrealdb_types::Geometry::Collection(v.into_iter().map(Into::into).collect())
}
}
}
}
impl From<surrealdb_types::Geometry> for Geometry {
fn from(v: surrealdb_types::Geometry) -> Self {
match v {
surrealdb_types::Geometry::Point(v) => Geometry::Point(v),
surrealdb_types::Geometry::Line(v) => Geometry::Line(v),
surrealdb_types::Geometry::Polygon(v) => Geometry::Polygon(v),
surrealdb_types::Geometry::MultiPoint(v) => Geometry::MultiPoint(v),
surrealdb_types::Geometry::MultiLine(v) => Geometry::MultiLine(v),
surrealdb_types::Geometry::MultiPolygon(v) => Geometry::MultiPolygon(v),
surrealdb_types::Geometry::Collection(v) => {
Geometry::Collection(v.into_iter().map(Into::into).collect())
}
}
}
}
impl ToSql for Geometry {
fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
match self {
Self::Point(v) => {
write_sql!(f, sql_fmt, "({}, {})", v.x(), v.y())
}
Self::Line(v) => write_sql!(
f,
sql_fmt,
"{{ type: 'LineString', coordinates: [{}] }}",
Fmt::comma_separated(v.points().map(|v| Fmt::new(v, |v, f, fmt| write_sql!(
f,
fmt,
"[{}, {}]",
v.x(),
v.y()
))))
),
Self::Polygon(v) => write_sql!(
f,
sql_fmt,
"{{ type: 'Polygon', coordinates: [{}] }}",
Fmt::comma_separated(once(v.exterior()).chain(v.interiors()).map(|v| Fmt::new(
v,
|v, f, fmt| write_sql!(
f,
fmt,
"[{}]",
Fmt::comma_separated(v.points().map(|v| Fmt::new(
v,
|v, f, fmt| write_sql!(f, fmt, "[{}, {}]", v.x(), v.y())
)))
)
)))
),
Self::MultiPoint(v) => {
write_sql!(
f,
sql_fmt,
"{{ type: 'MultiPoint', coordinates: [{}] }}",
Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f, fmt| write_sql!(
f,
fmt,
"[{}, {}]",
v.x(),
v.y()
))))
)
}
Self::MultiLine(v) => {
write_sql!(
f,
sql_fmt,
"{{ type: 'MultiLineString', coordinates: [{}] }}",
Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f, fmt| write_sql!(
f,
fmt,
"[{}]",
Fmt::comma_separated(v.points().map(|v| Fmt::new(
v,
|v, f, fmt| write_sql!(f, fmt, "[{}, {}]", v.x(), v.y())
)))
))))
)
}
Self::MultiPolygon(v) => {
write_sql!(
f,
sql_fmt,
"{{ type: 'MultiPolygon', coordinates: [{}] }}",
Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f, fmt| {
write_sql!(
f,
fmt,
"[{}]",
Fmt::comma_separated(once(v.exterior()).chain(v.interiors()).map(
|v| Fmt::new(v, |v, f, fmt| write_sql!(
f,
fmt,
"[{}]",
Fmt::comma_separated(v.points().map(|v| Fmt::new(
v,
|v, f, fmt| write_sql!(f, fmt, "[{}, {}]", v.x(), v.y())
)))
))
))
)
}))),
)
}
Self::Collection(v) => {
write_sql!(
f,
sql_fmt,
"{{ type: 'GeometryCollection', geometries: [{}] }}",
Fmt::comma_separated(v)
)
}
}
}
}
impl hash::Hash for Geometry {
fn hash<H: hash::Hasher>(&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));
}
}
}
}
impl<F> Encode<F> for Geometry {
fn encode<W: std::io::Write>(
&self,
w: &mut storekey::Writer<W>,
) -> Result<(), storekey::EncodeError> {
match self {
Geometry::Point(point) => {
w.write_u8(2)?;
Encode::<F>::encode(&point.x(), w)?;
Encode::<F>::encode(&point.y(), w)?;
}
Geometry::Line(line_string) => {
w.write_u8(3)?;
for p in line_string.points() {
w.mark_terminator();
Encode::<F>::encode(&p.x(), w)?;
Encode::<F>::encode(&p.y(), w)?;
}
w.write_terminator()?;
}
Geometry::Polygon(polygon) => {
w.write_u8(4)?;
for p in polygon.exterior().points() {
w.mark_terminator();
Encode::<F>::encode(&p.x(), w)?;
Encode::<F>::encode(&p.y(), w)?;
}
w.write_terminator()?;
for l in polygon.interiors() {
for p in l.points() {
w.mark_terminator();
Encode::<F>::encode(&p.x(), w)?;
Encode::<F>::encode(&p.y(), w)?;
}
w.write_terminator()?;
}
w.write_terminator()?;
}
Geometry::MultiPoint(multi_point) => {
w.write_u8(5)?;
for p in multi_point.iter() {
w.mark_terminator();
Encode::<F>::encode(&p.x(), w)?;
Encode::<F>::encode(&p.y(), w)?;
}
w.write_terminator()?;
}
Geometry::MultiLine(multi_line_string) => {
w.write_u8(6)?;
for l in multi_line_string.iter() {
for p in l.points() {
w.mark_terminator();
Encode::<F>::encode(&p.x(), w)?;
Encode::<F>::encode(&p.y(), w)?;
}
w.write_terminator()?;
}
w.write_terminator()?;
}
Geometry::MultiPolygon(multi_polygon) => {
w.write_u8(7)?;
for p in multi_polygon.iter() {
for p in p.exterior().points() {
w.mark_terminator();
Encode::<F>::encode(&p.x(), w)?;
Encode::<F>::encode(&p.y(), w)?;
}
w.write_terminator()?;
for l in p.interiors() {
for p in l.points() {
w.mark_terminator();
Encode::<F>::encode(&p.x(), w)?;
Encode::<F>::encode(&p.y(), w)?;
}
w.write_terminator()?;
}
w.write_terminator()?;
}
w.write_terminator()?;
}
Geometry::Collection(items) => {
w.write_u8(8)?;
for g in items.iter() {
w.mark_terminator();
Encode::<F>::encode(g, w)?;
}
w.write_terminator()?;
}
}
Ok(())
}
}
impl<'de, F> BorrowDecode<'de, F> for Geometry {
fn borrow_decode(r: &mut storekey::BorrowReader<'de>) -> Result<Self, storekey::DecodeError> {
match r.read_u8()? {
2 => {
let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
Ok(Geometry::Point(Point::from(point)))
}
3 => {
let mut res = Vec::new();
while !r.read_terminal()? {
let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
res.push(Coord::from(point))
}
Ok(Geometry::Line(LineString::new(res)))
}
4 => {
let mut ext = Vec::new();
while !r.read_terminal()? {
let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
ext.push(Coord::from(point))
}
let ext = LineString::new(ext);
let mut int = Vec::new();
while !r.read_terminal()? {
let mut line = Vec::new();
while !r.read_terminal()? {
let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
line.push(Coord::from(point))
}
int.push(LineString::new(line))
}
Ok(Geometry::Polygon(Polygon::new(ext, int)))
}
5 => {
let mut res = Vec::new();
while !r.read_terminal()? {
let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
res.push(Point::from(point))
}
Ok(Geometry::MultiPoint(MultiPoint::new(res)))
}
6 => {
let mut lines = Vec::new();
while !r.read_terminal()? {
let mut res = Vec::new();
while !r.read_terminal()? {
let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
res.push(Coord::from(point))
}
lines.push(LineString::new(res));
}
Ok(Geometry::MultiLine(MultiLineString::new(lines)))
}
7 => {
let mut polygons = Vec::new();
while !r.read_terminal()? {
let mut ext = Vec::new();
while !r.read_terminal()? {
let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
ext.push(Coord::from(point))
}
let ext = LineString::new(ext);
let mut int = Vec::new();
while !r.read_terminal()? {
let mut line = Vec::new();
while !r.read_terminal()? {
let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
line.push(Coord::from(point))
}
int.push(LineString::new(line))
}
polygons.push(Polygon::new(ext, int));
}
Ok(Geometry::MultiPolygon(MultiPolygon::new(polygons)))
}
8 => {
let mut geoms = Vec::new();
while !r.read_terminal()? {
geoms.push(BorrowDecode::<'de, F>::borrow_decode(r)?)
}
Ok(Geometry::Collection(geoms))
}
_ => Err(storekey::DecodeError::InvalidFormat),
}
}
}
#[cfg(test)]
mod test {
use core::f64;
use geo::{Coord, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon};
use super::Geometry;
fn round_trip(geom: &Geometry) {
let enc = storekey::encode_vec(geom).unwrap();
let dec = storekey::decode_borrow(&enc).unwrap();
assert_eq!(geom, &dec)
}
#[test]
fn encode_decode() {
round_trip(&Geometry::Point(Point::new(0.0, f64::INFINITY)));
round_trip(&Geometry::Line(LineString::new(vec![
Coord::from((0.0, f64::INFINITY)),
Coord::from((f64::NEG_INFINITY, 1.0)),
])));
round_trip(&Geometry::Polygon(Polygon::new(
LineString::new(vec![
Coord::from((0.0, f64::INFINITY)),
Coord::from((f64::NEG_INFINITY, 1.0)),
]),
vec![
LineString::new(vec![Coord::from((1.0, 2.0)), Coord::from((3.0, 4.0))]),
LineString::new(vec![Coord::from((5.0, 6.0)), Coord::from((7.0, 8.0))]),
],
)));
round_trip(&Geometry::MultiPoint(MultiPoint::new(vec![
Point::from((0.0, f64::INFINITY)),
Point::from((f64::NEG_INFINITY, 1.0)),
])));
round_trip(&Geometry::MultiLine(MultiLineString::new(vec![
LineString::new(vec![
Coord::from((0.0, f64::INFINITY)),
Coord::from((f64::NEG_INFINITY, 1.0)),
]),
LineString::new(vec![Coord::from((1.0, 2.0)), Coord::from((3.0, 4.0))]),
])));
round_trip(&Geometry::MultiPolygon(MultiPolygon::new(vec![
Polygon::new(
LineString::new(vec![
Coord::from((0.0, f64::INFINITY)),
Coord::from((f64::NEG_INFINITY, 1.0)),
]),
vec![
LineString::new(vec![Coord::from((1.0, 2.0)), Coord::from((3.0, 4.0))]),
LineString::new(vec![Coord::from((5.0, 6.0)), Coord::from((7.0, 8.0))]),
],
),
Polygon::new(
LineString::new(vec![Coord::from((9.0, 10.0)), Coord::from((11.0, 12.0))]),
vec![
LineString::new(vec![Coord::from((13.0, 14.0)), Coord::from((15.0, 16.0))]),
LineString::new(vec![Coord::from((17.0, 18.0)), Coord::from((19.0, 20.0))]),
],
),
])));
}
}