clickhouse_arrow/native/values/
geo.rs1use super::*;
4
5#[derive(Clone, Copy, Default, Debug, PartialEq)]
9#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
10pub struct Point(pub [f64; 2]);
11
12impl Hash for Point {
13 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
14 for x in self.0 {
15 x.to_bits().hash(state);
16 }
17 }
18}
19impl std::ops::Index<u8> for Point {
20 type Output = f64;
21
22 fn index(&self, index: u8) -> &Self::Output { &self.0[index as usize] }
23}
24impl AsRef<[f64; 2]> for Point {
25 fn as_ref(&self) -> &[f64; 2] { &self.0 }
26}
27
28#[derive(Clone, Hash, Default, Debug, PartialEq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub struct Ring(pub Vec<Point>);
34
35#[derive(Clone, Hash, Default, Debug, PartialEq)]
40#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41pub struct Polygon(pub Vec<Ring>);
42
43#[derive(Clone, Hash, Default, Debug, PartialEq)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub struct MultiPolygon(pub Vec<Polygon>);
49
50macro_rules! to_from_sql {
51 ($name:ident) => {
52 impl ToSql for $name {
53 fn to_sql(self, _type_hint: Option<&Type>) -> Result<Value> { Ok(Value::$name(self)) }
54 }
55
56 impl FromSql for $name {
57 fn from_sql(type_: &Type, value: Value) -> Result<Self> {
58 if !matches!(type_, Type::$name) {
59 return Err(unexpected_type(type_));
60 }
61 match value {
62 Value::$name(x) => Ok(x),
63 _ => unimplemented!(),
64 }
65 }
66 }
67 };
68}
69
70to_from_sql!(Point);
71to_from_sql!(Ring);
72to_from_sql!(Polygon);
73to_from_sql!(MultiPolygon);
74#[cfg(feature = "geo-types")]
75mod nav_types_conversions {
76 use super::*;
77
78 macro_rules! to_from_sql {
79 ($geo_t:path, $ch_t:ident) => {
80 impl ToSql for $geo_t {
81 fn to_sql(self, _type_hint: Option<&Type>) -> Result<Value> {
82 Ok(Value::$ch_t(self.into()))
83 }
84 }
85 impl FromSql for $geo_t {
86 fn from_sql(type_: &Type, value: Value) -> Result<Self> {
87 if !matches!(type_, Type::$ch_t) {
88 return Err(unexpected_type(type_));
89 }
90 match value {
91 Value::$ch_t(x) => Ok(x.into()),
92 _ => unimplemented!(),
93 }
94 }
95 }
96 };
97 }
98 impl From<Point> for geo_types::Coord {
100 fn from(source: Point) -> Self { Self { x: source[0], y: source[1] } }
101 }
102 impl From<geo_types::Coord> for Point {
103 fn from(source: geo_types::Coord) -> Self { Self([source.x, source.y]) }
104 }
105 to_from_sql!(geo_types::Coord, Point);
106
107 impl From<Point> for geo_types::Point {
109 fn from(source: Point) -> Self { geo_types::Point(source.into()) }
110 }
111 impl From<geo_types::Point> for Point {
112 fn from(source: geo_types::Point) -> Self { source.0.into() }
113 }
114 to_from_sql!(geo_types::Point, Point);
115 impl From<Ring> for geo_types::LineString {
117 fn from(source: Ring) -> Self {
118 Self(source.0.into_iter().map(geo_types::Coord::from).collect())
119 }
120 }
121 impl From<geo_types::LineString> for Ring {
122 fn from(source: geo_types::LineString) -> Self {
123 Self(source.0.into_iter().map(Point::from).collect())
124 }
125 }
126 to_from_sql!(geo_types::LineString, Ring);
127 impl From<Ring> for geo_types::Polygon {
130 fn from(source: Ring) -> Self { geo_types::Polygon::new(source.0.into(), vec![]) }
131 }
132 impl From<geo_types::Polygon> for Polygon {
134 fn from(source: geo_types::Polygon) -> Self {
135 Self(
136 [source.exterior().clone().into()]
137 .into_iter()
138 .chain(
139 source.interiors().iter().map(|linestring| Ring::from(linestring.clone())),
140 )
141 .collect(),
142 )
143 }
144 }
145 impl From<Polygon> for geo_types::Polygon {
146 fn from(mut source: Polygon) -> Self {
147 if source.0.is_empty() {
148 return Self::new(geo_types::LineString::new(vec![]), vec![]);
149 }
150 let exterior = source.0.remove(0);
151 geo_types::Polygon::new(
152 exterior.into(),
153 source.0.into_iter().map(geo_types::LineString::from).collect(),
154 )
155 }
156 }
157 to_from_sql!(geo_types::Polygon, Polygon);
158 impl From<MultiPolygon> for geo_types::MultiPolygon {
160 fn from(source: MultiPolygon) -> Self {
161 source.0.into_iter().map(geo_types::Polygon::from).collect()
162 }
163 }
164 impl From<geo_types::MultiPolygon> for MultiPolygon {
165 fn from(source: geo_types::MultiPolygon) -> Self {
166 Self(source.into_iter().map(Polygon::from).collect())
167 }
168 }
169 to_from_sql!(geo_types::MultiPolygon, MultiPolygon);
170 #[cfg(test)]
171 #[test]
172 fn roundtrip() {
173 let multipolygon_geo: geo_types::MultiPolygon = geo_types::wkt! {
174 MULTIPOLYGON (((40.0 40.0, 20.0 45.0, 45.0 30.0, 40.0 40.0)),
176 ((20.0 35.0, 10.0 30.0, 10.0 10.0, 30.0 5.0, 45.0 20.0, 20.0 35.0),
177 (30.0 20.0, 20.0 15.0, 20.0 25.0, 30. 20.0)))
178 };
179 let multipolygon = MultiPolygon::from(multipolygon_geo.clone());
180 let multipolygon_geo2 = geo_types::MultiPolygon::from(multipolygon);
181 assert_eq!(multipolygon_geo, multipolygon_geo2);
182 }
183}