Skip to main content

surrealdb_expr/val/
geometry.rs

1#![allow(clippy::derived_hash_with_manual_eq)]
2use std::cmp::Ordering;
3use std::hash;
4use std::iter::once;
5
6use common::fmt::Fmt;
7use geo::algorithm::contains::Contains;
8use geo::algorithm::intersects::Intersects;
9use geo::{Coord, LineString, LinesIter, Point, Polygon};
10use geo_types::{MultiLineString, MultiPoint, MultiPolygon};
11use revision::revisioned;
12use storekey::{BorrowDecode, Encode};
13use surrealdb_types::{SqlFormat, ToSql, write_sql};
14
15use super::Object;
16use crate::val::{Array, Value};
17
18#[revisioned(revision = 1)]
19#[derive(Clone, Debug, PartialEq)]
20#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
21pub enum Geometry {
22	Point(Point<f64>),
23	Line(LineString<f64>),
24	Polygon(Polygon<f64>),
25	MultiPoint(MultiPoint<f64>),
26	MultiLine(MultiLineString<f64>),
27	MultiPolygon(MultiPolygon<f64>),
28	Collection(Vec<Geometry>),
29	// Add new variants here
30}
31
32impl Geometry {
33	/// Check if this is not a Collection
34	pub fn is_geometry(&self) -> bool {
35		!matches!(self, Self::Collection(_))
36	}
37	/// Check if this is a Collection
38	pub fn is_collection(&self) -> bool {
39		matches!(self, Self::Collection(_))
40	}
41
42	/// Check if this has valid latitude and longitude points:
43	/// * -90 <= lat <= 90
44	/// * -180 <= lng <= 180
45	pub fn is_valid(&self) -> bool {
46		match self {
47			Geometry::Point(p) => {
48				(-90.0..=90.0).contains(&p.0.y) && (-180.0..=180.0).contains(&p.0.x)
49			}
50			Geometry::MultiPoint(v) => v
51				.iter()
52				.all(|p| (-90.0..=90.0).contains(&p.0.y) && (-180.0..=180.0).contains(&p.0.x)),
53			Geometry::Line(v) => v.lines_iter().all(|l| {
54				(-90.0..=90.0).contains(&l.start.y)
55					&& (-180.0..=180.0).contains(&l.start.x)
56					&& (-90.0..=90.0).contains(&l.end.y)
57					&& (-180.0..=180.0).contains(&l.end.x)
58			}),
59			Geometry::Polygon(v) => v.lines_iter().all(|l| {
60				(-90.0..=90.0).contains(&l.start.y)
61					&& (-180.0..=180.0).contains(&l.start.x)
62					&& (-90.0..=90.0).contains(&l.end.y)
63					&& (-180.0..=180.0).contains(&l.end.x)
64			}),
65			Geometry::MultiLine(v) => v.iter().all(|l| {
66				l.lines_iter().all(|l| {
67					(-90.0..=90.0).contains(&l.start.y)
68						&& (-180.0..=180.0).contains(&l.start.x)
69						&& (-90.0..=90.0).contains(&l.end.y)
70						&& (-180.0..=180.0).contains(&l.end.x)
71				})
72			}),
73			Geometry::MultiPolygon(v) => v.iter().all(|p| {
74				p.lines_iter().all(|l| {
75					(-90.0..=90.0).contains(&l.start.y)
76						&& (-180.0..=180.0).contains(&l.start.x)
77						&& (-90.0..=90.0).contains(&l.end.y)
78						&& (-180.0..=180.0).contains(&l.end.x)
79				})
80			}),
81			Geometry::Collection(v) => v.iter().all(Geometry::is_valid),
82		}
83	}
84	/// Get the type of this Geometry as text
85	pub fn as_type(&self) -> &'static str {
86		match self {
87			Self::Point(_) => "Point",
88			Self::Line(_) => "LineString",
89			Self::Polygon(_) => "Polygon",
90			Self::MultiPoint(_) => "MultiPoint",
91			Self::MultiLine(_) => "MultiLineString",
92			Self::MultiPolygon(_) => "MultiPolygon",
93			Self::Collection(_) => "GeometryCollection",
94		}
95	}
96	/// Get the raw coordinates of this Geometry as an Array
97	pub fn as_coordinates(&self) -> Value {
98		fn point(v: &Point) -> Value {
99			Array::from(vec![v.x(), v.y()]).into()
100		}
101
102		fn line(v: &LineString) -> Value {
103			v.points().map(|v| point(&v)).collect::<Vec<Value>>().into()
104		}
105
106		fn polygon(v: &Polygon) -> Value {
107			once(v.exterior()).chain(v.interiors()).map(line).collect::<Vec<Value>>().into()
108		}
109
110		fn multipoint(v: &MultiPoint) -> Value {
111			v.iter().map(point).collect::<Vec<Value>>().into()
112		}
113
114		fn multiline(v: &MultiLineString) -> Value {
115			v.iter().map(line).collect::<Vec<Value>>().into()
116		}
117
118		fn multipolygon(v: &MultiPolygon) -> Value {
119			v.iter().map(polygon).collect::<Vec<Value>>().into()
120		}
121
122		fn collection(v: &[Geometry]) -> Value {
123			v.iter().map(Geometry::as_coordinates).collect::<Vec<Value>>().into()
124		}
125
126		match self {
127			Self::Point(v) => point(v),
128			Self::Line(v) => line(v),
129			Self::Polygon(v) => polygon(v),
130			Self::MultiPoint(v) => multipoint(v),
131			Self::MultiLine(v) => multiline(v),
132			Self::MultiPolygon(v) => multipolygon(v),
133			Self::Collection(v) => collection(v),
134		}
135	}
136
137	/// Get the GeoJSON object representation for this geometry
138	pub fn as_object(&self) -> Object {
139		let mut obj = Object::default();
140		obj.insert("type", self.as_type().into());
141		obj.insert(
142			match self {
143				Self::Collection(_) => "geometries",
144				_ => "coordinates",
145			},
146			self.as_coordinates(),
147		);
148		obj
149	}
150
151	pub fn try_from_object(object: &Object) -> Option<Geometry> {
152		if object.len() != 2 {
153			return None;
154		}
155
156		let Some(Value::String(key)) = object.get("type") else {
157			return None;
158		};
159
160		match key.as_str() {
161			"Point" => {
162				object.get("coordinates").and_then(Geometry::array_to_point).map(Geometry::Point)
163			}
164			"LineString" => {
165				object.get("coordinates").and_then(Geometry::array_to_line).map(Geometry::Line)
166			}
167			"Polygon" => object
168				.get("coordinates")
169				.and_then(Geometry::array_to_polygon)
170				.map(Geometry::Polygon),
171			"MultiPoint" => object
172				.get("coordinates")
173				.and_then(Geometry::array_to_multipoint)
174				.map(Geometry::MultiPoint),
175			"MultiLineString" => object
176				.get("coordinates")
177				.and_then(Geometry::array_to_multiline)
178				.map(Geometry::MultiLine),
179			"MultiPolygon" => object
180				.get("coordinates")
181				.and_then(Geometry::array_to_multipolygon)
182				.map(Geometry::MultiPolygon),
183			"GeometryCollection" => {
184				let Some(Value::Array(x)) = object.get("geometries") else {
185					return None;
186				};
187
188				let mut res = Vec::with_capacity(x.len());
189
190				for x in x.iter() {
191					let Value::Geometry(x) = x else {
192						return None;
193					};
194					res.push(x.clone());
195				}
196
197				Some(Geometry::Collection(res))
198			}
199
200			_ => None,
201		}
202	}
203
204	/// Converts a surreal value to a MultiPolygon if the array matches to a
205	/// MultiPolygon.
206	pub fn array_to_multipolygon(v: &Value) -> Option<MultiPolygon<f64>> {
207		let mut res = Vec::new();
208		let Value::Array(v) = v else {
209			return None;
210		};
211		for x in v.iter() {
212			res.push(Self::array_to_polygon(x)?);
213		}
214		Some(MultiPolygon::new(res))
215	}
216
217	/// Converts a surreal value to a MultiLine if the array matches to a
218	/// MultiLine.
219	pub fn array_to_multiline(v: &Value) -> Option<MultiLineString<f64>> {
220		let mut res = Vec::new();
221		let Value::Array(v) = v else {
222			return None;
223		};
224		for x in v.iter() {
225			res.push(Self::array_to_line(x)?);
226		}
227		Some(MultiLineString::new(res))
228	}
229
230	/// Converts a surreal value to a MultiPoint if the array matches to a
231	/// MultiPoint.
232	pub fn array_to_multipoint(v: &Value) -> Option<MultiPoint<f64>> {
233		let mut res = Vec::new();
234		let Value::Array(v) = v else {
235			return None;
236		};
237		for x in v.iter() {
238			res.push(Self::array_to_point(x)?);
239		}
240		Some(MultiPoint::new(res))
241	}
242
243	/// Converts a surreal value to a Polygon if the array matches to a Polygon.
244	pub fn array_to_polygon(v: &Value) -> Option<Polygon<f64>> {
245		let mut res = Vec::new();
246		let Value::Array(v) = v else {
247			return None;
248		};
249		if v.is_empty() {
250			return None;
251		}
252		let first = Self::array_to_line(&v[0])?;
253		for x in &v[1..] {
254			res.push(Self::array_to_line(x)?);
255		}
256		Some(Polygon::new(first, res))
257	}
258
259	/// Converts a surreal value to a LineString if the array matches to a
260	/// LineString.
261	pub fn array_to_line(v: &Value) -> Option<LineString<f64>> {
262		let mut res = Vec::new();
263		let Value::Array(v) = v else {
264			return None;
265		};
266		for x in v.iter() {
267			res.push(Self::array_to_point(x)?);
268		}
269		Some(LineString::from(res))
270	}
271
272	/// Converts a surreal value to a Point if the array matches to a point.
273	pub fn array_to_point(v: &Value) -> Option<Point<f64>> {
274		let Value::Array(v) = v else {
275			return None;
276		};
277		if v.len() != 2 {
278			return None;
279		}
280		// FIXME: This truncates decimals and large integers into a f64.
281		let Value::Number(ref a) = v.0[0] else {
282			return None;
283		};
284		let Value::Number(ref b) = v.0[1] else {
285			return None;
286		};
287		Some(Point::from(((*a).try_into().ok()?, (*b).try_into().ok()?)))
288	}
289
290	// -----------------------------------
291	// Value operations
292	// -----------------------------------
293
294	pub fn contains(&self, other: &Self) -> bool {
295		match self {
296			Self::Point(v) => match other {
297				Self::Point(w) => v.contains(w),
298				Self::MultiPoint(w) => w.iter().all(|x| v.contains(x)),
299				Self::Collection(w) => w.iter().all(|x| self.contains(x)),
300				_ => false,
301			},
302			Self::Line(v) => match other {
303				Self::Point(w) => v.contains(w),
304				Self::Line(w) => v.contains(w),
305				Self::MultiLine(w) => w.iter().all(|x| w.contains(x)),
306				Self::Collection(w) => w.iter().all(|x| self.contains(x)),
307				_ => false,
308			},
309			Self::Polygon(v) => match other {
310				Self::Point(w) => v.contains(w),
311				Self::Line(w) => v.contains(w),
312				Self::Polygon(w) => v.contains(w),
313				Self::MultiPolygon(w) => w.iter().all(|x| w.contains(x)),
314				Self::Collection(w) => w.iter().all(|x| self.contains(x)),
315				_ => false,
316			},
317			Self::MultiPoint(v) => match other {
318				Self::Point(w) => v.contains(w),
319				Self::MultiPoint(w) => w.iter().all(|x| w.contains(x)),
320				Self::Collection(w) => w.iter().all(|x| self.contains(x)),
321				_ => false,
322			},
323			Self::MultiLine(v) => match other {
324				Self::Point(w) => v.contains(w),
325				Self::Line(w) => v.contains(w),
326				Self::MultiLine(w) => w.iter().all(|x| w.contains(x)),
327				Self::Collection(w) => w.iter().all(|x| self.contains(x)),
328				_ => false,
329			},
330			Self::MultiPolygon(v) => match other {
331				Self::Point(w) => v.contains(w),
332				Self::Line(w) => v.contains(w),
333				Self::Polygon(w) => v.contains(w),
334				Self::MultiPoint(w) => v.contains(w),
335				Self::MultiLine(w) => v.contains(w),
336				Self::MultiPolygon(w) => v.contains(w),
337				Self::Collection(w) => w.iter().all(|x| self.contains(x)),
338			},
339			Self::Collection(v) => v.iter().all(|x| x.contains(other)),
340		}
341	}
342
343	pub fn intersects(&self, other: &Self) -> bool {
344		match self {
345			Self::Point(v) => match other {
346				Self::Point(w) => v.intersects(w),
347				Self::Line(w) => v.intersects(w),
348				Self::Polygon(w) => v.intersects(w),
349				Self::MultiPoint(w) => v.intersects(w),
350				Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)),
351				Self::MultiPolygon(w) => v.intersects(w),
352				Self::Collection(w) => w.iter().all(|x| self.intersects(x)),
353			},
354			Self::Line(v) => match other {
355				Self::Point(w) => v.intersects(w),
356				Self::Line(w) => v.intersects(w),
357				Self::Polygon(w) => v.intersects(w),
358				Self::MultiPoint(w) => v.intersects(w),
359				Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)),
360				Self::MultiPolygon(w) => v.intersects(w),
361				Self::Collection(w) => w.iter().all(|x| self.intersects(x)),
362			},
363			Self::Polygon(v) => match other {
364				Self::Point(w) => v.intersects(w),
365				Self::Line(w) => v.intersects(w),
366				Self::Polygon(w) => v.intersects(w),
367				Self::MultiPoint(w) => v.intersects(w),
368				Self::MultiLine(w) => v.intersects(w),
369				Self::MultiPolygon(w) => v.intersects(w),
370				Self::Collection(w) => w.iter().all(|x| self.intersects(x)),
371			},
372			Self::MultiPoint(v) => match other {
373				Self::Point(w) => v.intersects(w),
374				Self::Line(w) => v.intersects(w),
375				Self::Polygon(w) => v.intersects(w),
376				Self::MultiPoint(w) => v.intersects(w),
377				Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)),
378				Self::MultiPolygon(w) => v.intersects(w),
379				Self::Collection(w) => w.iter().all(|x| self.intersects(x)),
380			},
381			Self::MultiLine(v) => match other {
382				Self::Point(w) => v.intersects(w),
383				Self::Line(w) => v.intersects(w),
384				Self::Polygon(w) => v.intersects(w),
385				Self::MultiPoint(w) => v.intersects(w),
386				Self::MultiLine(w) => w.iter().any(|x| v.intersects(x)),
387				Self::MultiPolygon(w) => v.intersects(w),
388				Self::Collection(w) => w.iter().all(|x| self.intersects(x)),
389			},
390			Self::MultiPolygon(v) => match other {
391				Self::Point(w) => v.intersects(w),
392				Self::Line(w) => v.intersects(w),
393				Self::Polygon(w) => v.intersects(w),
394				Self::MultiPoint(w) => v.intersects(w),
395				Self::MultiLine(w) => v.intersects(w),
396				Self::MultiPolygon(w) => v.intersects(w),
397				Self::Collection(w) => w.iter().all(|x| self.intersects(x)),
398			},
399			Self::Collection(v) => v.iter().all(|x| x.intersects(other)),
400		}
401	}
402}
403
404impl PartialOrd for Geometry {
405	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
406		fn coord(v: &Coord) -> (f64, f64) {
407			v.x_y()
408		}
409
410		fn point(v: &Point) -> (f64, f64) {
411			coord(&v.0)
412		}
413
414		fn line(v: &LineString) -> impl Iterator<Item = (f64, f64)> + '_ {
415			v.into_iter().map(coord)
416		}
417
418		fn polygon(v: &Polygon) -> impl Iterator<Item = (f64, f64)> + '_ {
419			v.interiors().iter().chain(once(v.exterior())).flat_map(line)
420		}
421
422		fn multipoint(v: &MultiPoint) -> impl Iterator<Item = (f64, f64)> + '_ {
423			v.iter().map(point)
424		}
425
426		fn multiline(v: &MultiLineString) -> impl Iterator<Item = (f64, f64)> + '_ {
427			v.iter().flat_map(line)
428		}
429
430		fn multipolygon(v: &MultiPolygon) -> impl Iterator<Item = (f64, f64)> + '_ {
431			v.iter().flat_map(polygon)
432		}
433
434		match (self, other) {
435			//
436			(Self::Point(_), Self::Line(_)) => Some(Ordering::Less),
437			(Self::Point(_), Self::Polygon(_)) => Some(Ordering::Less),
438			(Self::Point(_), Self::MultiPoint(_)) => Some(Ordering::Less),
439			(Self::Point(_), Self::MultiLine(_)) => Some(Ordering::Less),
440			(Self::Point(_), Self::MultiPolygon(_)) => Some(Ordering::Less),
441			(Self::Point(_), Self::Collection(_)) => Some(Ordering::Less),
442			//
443			(Self::Line(_), Self::Point(_)) => Some(Ordering::Greater),
444			(Self::Line(_), Self::Polygon(_)) => Some(Ordering::Less),
445			(Self::Line(_), Self::MultiPoint(_)) => Some(Ordering::Less),
446			(Self::Line(_), Self::MultiLine(_)) => Some(Ordering::Less),
447			(Self::Line(_), Self::MultiPolygon(_)) => Some(Ordering::Less),
448			(Self::Line(_), Self::Collection(_)) => Some(Ordering::Less),
449			//
450			(Self::Polygon(_), Self::Point(_)) => Some(Ordering::Greater),
451			(Self::Polygon(_), Self::Line(_)) => Some(Ordering::Greater),
452			(Self::Polygon(_), Self::MultiPoint(_)) => Some(Ordering::Less),
453			(Self::Polygon(_), Self::MultiLine(_)) => Some(Ordering::Less),
454			(Self::Polygon(_), Self::MultiPolygon(_)) => Some(Ordering::Less),
455			(Self::Polygon(_), Self::Collection(_)) => Some(Ordering::Less),
456			//
457			(Self::MultiPoint(_), Self::Point(_)) => Some(Ordering::Greater),
458			(Self::MultiPoint(_), Self::Line(_)) => Some(Ordering::Greater),
459			(Self::MultiPoint(_), Self::Polygon(_)) => Some(Ordering::Greater),
460			(Self::MultiPoint(_), Self::MultiLine(_)) => Some(Ordering::Less),
461			(Self::MultiPoint(_), Self::MultiPolygon(_)) => Some(Ordering::Less),
462			(Self::MultiPoint(_), Self::Collection(_)) => Some(Ordering::Less),
463			//
464			(Self::MultiLine(_), Self::Point(_)) => Some(Ordering::Greater),
465			(Self::MultiLine(_), Self::Line(_)) => Some(Ordering::Greater),
466			(Self::MultiLine(_), Self::Polygon(_)) => Some(Ordering::Greater),
467			(Self::MultiLine(_), Self::MultiPoint(_)) => Some(Ordering::Greater),
468			(Self::MultiLine(_), Self::MultiPolygon(_)) => Some(Ordering::Less),
469			(Self::MultiLine(_), Self::Collection(_)) => Some(Ordering::Less),
470			//
471			(Self::MultiPolygon(_), Self::Point(_)) => Some(Ordering::Greater),
472			(Self::MultiPolygon(_), Self::Line(_)) => Some(Ordering::Greater),
473			(Self::MultiPolygon(_), Self::Polygon(_)) => Some(Ordering::Greater),
474			(Self::MultiPolygon(_), Self::MultiPoint(_)) => Some(Ordering::Greater),
475			(Self::MultiPolygon(_), Self::MultiLine(_)) => Some(Ordering::Greater),
476			(Self::MultiPolygon(_), Self::Collection(_)) => Some(Ordering::Less),
477			//
478			(Self::Collection(_), Self::Point(_)) => Some(Ordering::Greater),
479			(Self::Collection(_), Self::Line(_)) => Some(Ordering::Greater),
480			(Self::Collection(_), Self::Polygon(_)) => Some(Ordering::Greater),
481			(Self::Collection(_), Self::MultiPoint(_)) => Some(Ordering::Greater),
482			(Self::Collection(_), Self::MultiLine(_)) => Some(Ordering::Greater),
483			(Self::Collection(_), Self::MultiPolygon(_)) => Some(Ordering::Greater),
484			//
485			(Self::Point(a), Self::Point(b)) => point(a).partial_cmp(&point(b)),
486			(Self::Line(a), Self::Line(b)) => line(a).partial_cmp(line(b)),
487			(Self::Polygon(a), Self::Polygon(b)) => polygon(a).partial_cmp(polygon(b)),
488			(Self::MultiPoint(a), Self::MultiPoint(b)) => multipoint(a).partial_cmp(multipoint(b)),
489			(Self::MultiLine(a), Self::MultiLine(b)) => multiline(a).partial_cmp(multiline(b)),
490			(Self::MultiPolygon(a), Self::MultiPolygon(b)) => {
491				multipolygon(a).partial_cmp(multipolygon(b))
492			}
493			(Self::Collection(a), Self::Collection(b)) => a.partial_cmp(b),
494		}
495	}
496}
497
498impl From<(f64, f64)> for Geometry {
499	fn from(v: (f64, f64)) -> Self {
500		Self::Point(v.into())
501	}
502}
503
504impl From<[f64; 2]> for Geometry {
505	fn from(v: [f64; 2]) -> Self {
506		Self::Point(v.into())
507	}
508}
509
510impl From<Point<f64>> for Geometry {
511	fn from(v: Point<f64>) -> Self {
512		Self::Point(v)
513	}
514}
515
516impl From<LineString<f64>> for Geometry {
517	fn from(v: LineString<f64>) -> Self {
518		Self::Line(v)
519	}
520}
521
522impl From<Polygon<f64>> for Geometry {
523	fn from(v: Polygon<f64>) -> Self {
524		Self::Polygon(v)
525	}
526}
527
528impl From<MultiPoint<f64>> for Geometry {
529	fn from(v: MultiPoint<f64>) -> Self {
530		Self::MultiPoint(v)
531	}
532}
533
534impl From<MultiLineString<f64>> for Geometry {
535	fn from(v: MultiLineString<f64>) -> Self {
536		Self::MultiLine(v)
537	}
538}
539
540impl From<MultiPolygon<f64>> for Geometry {
541	fn from(v: MultiPolygon<f64>) -> Self {
542		Self::MultiPolygon(v)
543	}
544}
545
546impl From<Vec<Geometry>> for Geometry {
547	fn from(v: Vec<Geometry>) -> Self {
548		Self::Collection(v)
549	}
550}
551
552impl From<Vec<Point<f64>>> for Geometry {
553	fn from(v: Vec<Point<f64>>) -> Self {
554		Self::MultiPoint(MultiPoint(v))
555	}
556}
557
558impl From<Vec<LineString<f64>>> for Geometry {
559	fn from(v: Vec<LineString<f64>>) -> Self {
560		Self::MultiLine(MultiLineString(v))
561	}
562}
563
564impl From<Vec<Polygon<f64>>> for Geometry {
565	fn from(v: Vec<Polygon<f64>>) -> Self {
566		Self::MultiPolygon(MultiPolygon(v))
567	}
568}
569
570impl From<Geometry> for geo::Geometry<f64> {
571	fn from(v: Geometry) -> Self {
572		match v {
573			Geometry::Point(v) => v.into(),
574			Geometry::Line(v) => v.into(),
575			Geometry::Polygon(v) => v.into(),
576			Geometry::MultiPoint(v) => v.into(),
577			Geometry::MultiLine(v) => v.into(),
578			Geometry::MultiPolygon(v) => v.into(),
579			Geometry::Collection(v) => v.into_iter().collect::<geo::Geometry<f64>>(),
580		}
581	}
582}
583
584impl From<geo::Geometry<f64>> for Geometry {
585	/// Convert from the upstream geometry enum.
586	///
587	/// `geo`'s enum is a superset of SurrealQL's geometry variants: `Rect` and
588	/// `Triangle` convert to their equivalent polygons, and a bare `Line`
589	/// (segment) becomes a two-point line string.
590	fn from(v: geo::Geometry<f64>) -> Self {
591		match v {
592			geo::Geometry::Point(v) => Geometry::Point(v),
593			geo::Geometry::Line(v) => Geometry::Line(LineString::from(vec![v.start, v.end])),
594			geo::Geometry::LineString(v) => Geometry::Line(v),
595			geo::Geometry::Polygon(v) => Geometry::Polygon(v),
596			geo::Geometry::MultiPoint(v) => Geometry::MultiPoint(v),
597			geo::Geometry::MultiLineString(v) => Geometry::MultiLine(v),
598			geo::Geometry::MultiPolygon(v) => Geometry::MultiPolygon(v),
599			geo::Geometry::GeometryCollection(v) => {
600				Geometry::Collection(v.0.into_iter().map(Geometry::from).collect())
601			}
602			geo::Geometry::Rect(v) => Geometry::Polygon(v.to_polygon()),
603			geo::Geometry::Triangle(v) => Geometry::Polygon(v.to_polygon()),
604		}
605	}
606}
607
608impl FromIterator<Geometry> for geo::Geometry<f64> {
609	fn from_iter<I: IntoIterator<Item = Geometry>>(iter: I) -> Self {
610		let mut c: Vec<geo::Geometry<f64>> = vec![];
611		for i in iter {
612			c.push(i.into())
613		}
614		geo::Geometry::GeometryCollection(geo::GeometryCollection(c))
615	}
616}
617
618impl From<Geometry> for surrealdb_types::Geometry {
619	fn from(v: Geometry) -> Self {
620		match v {
621			Geometry::Point(v) => surrealdb_types::Geometry::Point(v),
622			Geometry::Line(v) => surrealdb_types::Geometry::Line(v),
623			Geometry::Polygon(v) => surrealdb_types::Geometry::Polygon(v),
624			Geometry::MultiPoint(v) => surrealdb_types::Geometry::MultiPoint(v),
625			Geometry::MultiLine(v) => surrealdb_types::Geometry::MultiLine(v),
626			Geometry::MultiPolygon(v) => surrealdb_types::Geometry::MultiPolygon(v),
627			Geometry::Collection(v) => {
628				surrealdb_types::Geometry::Collection(v.into_iter().map(Into::into).collect())
629			}
630		}
631	}
632}
633
634impl From<surrealdb_types::Geometry> for Geometry {
635	fn from(v: surrealdb_types::Geometry) -> Self {
636		match v {
637			surrealdb_types::Geometry::Point(v) => Geometry::Point(v),
638			surrealdb_types::Geometry::Line(v) => Geometry::Line(v),
639			surrealdb_types::Geometry::Polygon(v) => Geometry::Polygon(v),
640			surrealdb_types::Geometry::MultiPoint(v) => Geometry::MultiPoint(v),
641			surrealdb_types::Geometry::MultiLine(v) => Geometry::MultiLine(v),
642			surrealdb_types::Geometry::MultiPolygon(v) => Geometry::MultiPolygon(v),
643			surrealdb_types::Geometry::Collection(v) => {
644				Geometry::Collection(v.into_iter().map(Into::into).collect())
645			}
646		}
647	}
648}
649
650impl ToSql for Geometry {
651	fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
652		match self {
653			Self::Point(v) => {
654				write_sql!(f, sql_fmt, "({}, {})", v.x(), v.y())
655			}
656			Self::Line(v) => write_sql!(
657				f,
658				sql_fmt,
659				"{{ type: 'LineString', coordinates: [{}] }}",
660				Fmt::comma_separated(v.points().map(|v| Fmt::new(v, |v, f, fmt| write_sql!(
661					f,
662					fmt,
663					"[{}, {}]",
664					v.x(),
665					v.y()
666				))))
667			),
668			Self::Polygon(v) => write_sql!(
669				f,
670				sql_fmt,
671				"{{ type: 'Polygon', coordinates: [{}] }}",
672				Fmt::comma_separated(once(v.exterior()).chain(v.interiors()).map(|v| Fmt::new(
673					v,
674					|v, f, fmt| write_sql!(
675						f,
676						fmt,
677						"[{}]",
678						Fmt::comma_separated(v.points().map(|v| Fmt::new(
679							v,
680							|v, f, fmt| write_sql!(f, fmt, "[{}, {}]", v.x(), v.y())
681						)))
682					)
683				)))
684			),
685			Self::MultiPoint(v) => {
686				write_sql!(
687					f,
688					sql_fmt,
689					"{{ type: 'MultiPoint', coordinates: [{}] }}",
690					Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f, fmt| write_sql!(
691						f,
692						fmt,
693						"[{}, {}]",
694						v.x(),
695						v.y()
696					))))
697				)
698			}
699			Self::MultiLine(v) => {
700				write_sql!(
701					f,
702					sql_fmt,
703					"{{ type: 'MultiLineString', coordinates: [{}] }}",
704					Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f, fmt| write_sql!(
705						f,
706						fmt,
707						"[{}]",
708						Fmt::comma_separated(v.points().map(|v| Fmt::new(
709							v,
710							|v, f, fmt| write_sql!(f, fmt, "[{}, {}]", v.x(), v.y())
711						)))
712					))))
713				)
714			}
715			Self::MultiPolygon(v) => {
716				write_sql!(
717					f,
718					sql_fmt,
719					"{{ type: 'MultiPolygon', coordinates: [{}] }}",
720					Fmt::comma_separated(v.iter().map(|v| Fmt::new(v, |v, f, fmt| {
721						write_sql!(
722							f,
723							fmt,
724							"[{}]",
725							Fmt::comma_separated(once(v.exterior()).chain(v.interiors()).map(
726								|v| Fmt::new(v, |v, f, fmt| write_sql!(
727									f,
728									fmt,
729									"[{}]",
730									Fmt::comma_separated(v.points().map(|v| Fmt::new(
731										v,
732										|v, f, fmt| write_sql!(f, fmt, "[{}, {}]", v.x(), v.y())
733									)))
734								))
735							))
736						)
737					}))),
738				)
739			}
740			Self::Collection(v) => {
741				write_sql!(
742					f,
743					sql_fmt,
744					"{{ type: 'GeometryCollection', geometries: [{}] }}",
745					Fmt::comma_separated(v)
746				)
747			}
748		}
749	}
750}
751
752impl hash::Hash for Geometry {
753	fn hash<H: hash::Hasher>(&self, state: &mut H) {
754		match self {
755			Geometry::Point(p) => {
756				"Point".hash(state);
757				p.x().to_bits().hash(state);
758				p.y().to_bits().hash(state);
759			}
760			Geometry::Line(l) => {
761				"Line".hash(state);
762				l.points().for_each(|v| {
763					v.x().to_bits().hash(state);
764					v.y().to_bits().hash(state);
765				});
766			}
767			Geometry::Polygon(p) => {
768				"Polygon".hash(state);
769				p.exterior().points().for_each(|ext| {
770					ext.x().to_bits().hash(state);
771					ext.y().to_bits().hash(state);
772				});
773				p.interiors().iter().for_each(|int| {
774					int.points().for_each(|v| {
775						v.x().to_bits().hash(state);
776						v.y().to_bits().hash(state);
777					});
778				});
779			}
780			Geometry::MultiPoint(v) => {
781				"MultiPoint".hash(state);
782				v.0.iter().for_each(|v| {
783					v.x().to_bits().hash(state);
784					v.y().to_bits().hash(state);
785				});
786			}
787			Geometry::MultiLine(ml) => {
788				"MultiLine".hash(state);
789				ml.0.iter().for_each(|ls| {
790					ls.points().for_each(|p| {
791						p.x().to_bits().hash(state);
792						p.y().to_bits().hash(state);
793					});
794				});
795			}
796			Geometry::MultiPolygon(mp) => {
797				"MultiPolygon".hash(state);
798				mp.0.iter().for_each(|p| {
799					p.exterior().points().for_each(|ext| {
800						ext.x().to_bits().hash(state);
801						ext.y().to_bits().hash(state);
802					});
803					p.interiors().iter().for_each(|int| {
804						int.points().for_each(|v| {
805							v.x().to_bits().hash(state);
806							v.y().to_bits().hash(state);
807						});
808					});
809				});
810			}
811			Geometry::Collection(v) => {
812				"GeometryCollection".hash(state);
813				v.iter().for_each(|v| v.hash(state));
814			}
815		}
816	}
817}
818
819impl<F> Encode<F> for Geometry {
820	fn encode<W: std::io::Write>(
821		&self,
822		w: &mut storekey::Writer<W>,
823	) -> Result<(), storekey::EncodeError> {
824		match self {
825			Geometry::Point(point) => {
826				w.write_u8(2)?;
827				Encode::<F>::encode(&point.x(), w)?;
828				Encode::<F>::encode(&point.y(), w)?;
829			}
830			Geometry::Line(line_string) => {
831				w.write_u8(3)?;
832				for p in line_string.points() {
833					w.mark_terminator();
834					Encode::<F>::encode(&p.x(), w)?;
835					Encode::<F>::encode(&p.y(), w)?;
836				}
837				w.write_terminator()?;
838			}
839			Geometry::Polygon(polygon) => {
840				w.write_u8(4)?;
841				for p in polygon.exterior().points() {
842					w.mark_terminator();
843					Encode::<F>::encode(&p.x(), w)?;
844					Encode::<F>::encode(&p.y(), w)?;
845				}
846				w.write_terminator()?;
847				for l in polygon.interiors() {
848					for p in l.points() {
849						w.mark_terminator();
850						Encode::<F>::encode(&p.x(), w)?;
851						Encode::<F>::encode(&p.y(), w)?;
852					}
853					w.write_terminator()?;
854				}
855				w.write_terminator()?;
856			}
857			Geometry::MultiPoint(multi_point) => {
858				w.write_u8(5)?;
859				for p in multi_point.iter() {
860					w.mark_terminator();
861					Encode::<F>::encode(&p.x(), w)?;
862					Encode::<F>::encode(&p.y(), w)?;
863				}
864				w.write_terminator()?;
865			}
866			Geometry::MultiLine(multi_line_string) => {
867				w.write_u8(6)?;
868				for l in multi_line_string.iter() {
869					for p in l.points() {
870						w.mark_terminator();
871						Encode::<F>::encode(&p.x(), w)?;
872						Encode::<F>::encode(&p.y(), w)?;
873					}
874					w.write_terminator()?;
875				}
876				w.write_terminator()?;
877			}
878			Geometry::MultiPolygon(multi_polygon) => {
879				w.write_u8(7)?;
880				for p in multi_polygon.iter() {
881					for p in p.exterior().points() {
882						w.mark_terminator();
883						Encode::<F>::encode(&p.x(), w)?;
884						Encode::<F>::encode(&p.y(), w)?;
885					}
886					w.write_terminator()?;
887					for l in p.interiors() {
888						for p in l.points() {
889							w.mark_terminator();
890							Encode::<F>::encode(&p.x(), w)?;
891							Encode::<F>::encode(&p.y(), w)?;
892						}
893						w.write_terminator()?;
894					}
895					w.write_terminator()?;
896				}
897				w.write_terminator()?;
898			}
899			Geometry::Collection(items) => {
900				w.write_u8(8)?;
901				for g in items.iter() {
902					w.mark_terminator();
903					Encode::<F>::encode(g, w)?;
904				}
905				w.write_terminator()?;
906			}
907		}
908		Ok(())
909	}
910}
911
912impl<'de, F> BorrowDecode<'de, F> for Geometry {
913	fn borrow_decode(r: &mut storekey::BorrowReader<'de>) -> Result<Self, storekey::DecodeError> {
914		match r.read_u8()? {
915			2 => {
916				let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
917				Ok(Geometry::Point(Point::from(point)))
918			}
919			3 => {
920				let mut res = Vec::new();
921				while !r.read_terminal()? {
922					let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
923					res.push(Coord::from(point))
924				}
925				Ok(Geometry::Line(LineString::new(res)))
926			}
927			4 => {
928				let mut ext = Vec::new();
929				while !r.read_terminal()? {
930					let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
931					ext.push(Coord::from(point))
932				}
933				let ext = LineString::new(ext);
934				let mut int = Vec::new();
935				while !r.read_terminal()? {
936					let mut line = Vec::new();
937					while !r.read_terminal()? {
938						let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
939						line.push(Coord::from(point))
940					}
941					int.push(LineString::new(line))
942				}
943				Ok(Geometry::Polygon(Polygon::new(ext, int)))
944			}
945			5 => {
946				let mut res = Vec::new();
947				while !r.read_terminal()? {
948					let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
949					res.push(Point::from(point))
950				}
951				Ok(Geometry::MultiPoint(MultiPoint::new(res)))
952			}
953			6 => {
954				let mut lines = Vec::new();
955				while !r.read_terminal()? {
956					let mut res = Vec::new();
957					while !r.read_terminal()? {
958						let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
959						res.push(Coord::from(point))
960					}
961					lines.push(LineString::new(res));
962				}
963				Ok(Geometry::MultiLine(MultiLineString::new(lines)))
964			}
965			7 => {
966				let mut polygons = Vec::new();
967				while !r.read_terminal()? {
968					let mut ext = Vec::new();
969					while !r.read_terminal()? {
970						let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
971						ext.push(Coord::from(point))
972					}
973					let ext = LineString::new(ext);
974					let mut int = Vec::new();
975					while !r.read_terminal()? {
976						let mut line = Vec::new();
977						while !r.read_terminal()? {
978							let point = <(f64, f64) as BorrowDecode<'de, F>>::borrow_decode(r)?;
979							line.push(Coord::from(point))
980						}
981						int.push(LineString::new(line))
982					}
983					polygons.push(Polygon::new(ext, int));
984				}
985				Ok(Geometry::MultiPolygon(MultiPolygon::new(polygons)))
986			}
987			8 => {
988				let mut geoms = Vec::new();
989				while !r.read_terminal()? {
990					geoms.push(BorrowDecode::<'de, F>::borrow_decode(r)?)
991				}
992				Ok(Geometry::Collection(geoms))
993			}
994			_ => Err(storekey::DecodeError::InvalidFormat),
995		}
996	}
997}
998
999#[cfg(test)]
1000mod test {
1001	use core::f64;
1002
1003	use geo::{Coord, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon};
1004
1005	use super::Geometry;
1006
1007	fn round_trip(geom: &Geometry) {
1008		let enc = storekey::encode_vec(geom).unwrap();
1009		let dec = storekey::decode_borrow(&enc).unwrap();
1010		assert_eq!(geom, &dec)
1011	}
1012
1013	#[test]
1014	fn encode_decode() {
1015		round_trip(&Geometry::Point(Point::new(0.0, f64::INFINITY)));
1016		round_trip(&Geometry::Line(LineString::new(vec![
1017			Coord::from((0.0, f64::INFINITY)),
1018			Coord::from((f64::NEG_INFINITY, 1.0)),
1019		])));
1020		round_trip(&Geometry::Polygon(Polygon::new(
1021			LineString::new(vec![
1022				Coord::from((0.0, f64::INFINITY)),
1023				Coord::from((f64::NEG_INFINITY, 1.0)),
1024			]),
1025			vec![
1026				LineString::new(vec![Coord::from((1.0, 2.0)), Coord::from((3.0, 4.0))]),
1027				LineString::new(vec![Coord::from((5.0, 6.0)), Coord::from((7.0, 8.0))]),
1028			],
1029		)));
1030		round_trip(&Geometry::MultiPoint(MultiPoint::new(vec![
1031			Point::from((0.0, f64::INFINITY)),
1032			Point::from((f64::NEG_INFINITY, 1.0)),
1033		])));
1034		round_trip(&Geometry::MultiLine(MultiLineString::new(vec![
1035			LineString::new(vec![
1036				Coord::from((0.0, f64::INFINITY)),
1037				Coord::from((f64::NEG_INFINITY, 1.0)),
1038			]),
1039			LineString::new(vec![Coord::from((1.0, 2.0)), Coord::from((3.0, 4.0))]),
1040		])));
1041		round_trip(&Geometry::MultiPolygon(MultiPolygon::new(vec![
1042			Polygon::new(
1043				LineString::new(vec![
1044					Coord::from((0.0, f64::INFINITY)),
1045					Coord::from((f64::NEG_INFINITY, 1.0)),
1046				]),
1047				vec![
1048					LineString::new(vec![Coord::from((1.0, 2.0)), Coord::from((3.0, 4.0))]),
1049					LineString::new(vec![Coord::from((5.0, 6.0)), Coord::from((7.0, 8.0))]),
1050				],
1051			),
1052			Polygon::new(
1053				LineString::new(vec![Coord::from((9.0, 10.0)), Coord::from((11.0, 12.0))]),
1054				vec![
1055					LineString::new(vec![Coord::from((13.0, 14.0)), Coord::from((15.0, 16.0))]),
1056					LineString::new(vec![Coord::from((17.0, 18.0)), Coord::from((19.0, 20.0))]),
1057				],
1058			),
1059		])));
1060	}
1061}