1use crate::search::*;
2
3#[derive(Debug, Clone, PartialEq, Serialize)]
8#[serde(tag = "type")]
9pub enum GeoShape {
10 #[serde(rename = "point")]
14 Point {
15 coordinates: GeoLocation,
17 },
18
19 #[serde(rename = "linestring")]
21 LineString {
22 coordinates: Vec<GeoLocation>,
24 },
25
26 #[serde(rename = "polygon")]
30 Polygon {
31 coordinates: Vec<Vec<GeoLocation>>,
33 },
34
35 #[serde(rename = "multipoint")]
37 MultiPoint {
38 coordinates: Vec<GeoLocation>,
40 },
41
42 #[serde(rename = "multilinestring")]
44 MultiLineString {
45 coordinates: Vec<Vec<GeoLocation>>,
47 },
48
49 #[serde(rename = "multipolygon")]
51 MultiPolygon {
52 coordinates: Vec<Vec<Vec<GeoLocation>>>,
54 },
55
56 #[serde(rename = "envelope")]
59 Envelope {
60 coordinates: (GeoLocation, GeoLocation),
62 },
63
64 #[serde(rename = "circle")]
67 Circle {
68 coordinates: GeoLocation,
70
71 radius: Distance,
73 },
74
75 #[serde(rename = "geometrycollection")]
78 GeometryCollection {
79 geometries: Vec<GeoShape>,
81 },
82}
83
84impl GeoShape {
85 pub fn point<T>(coordinates: T) -> Self
87 where
88 T: Into<GeoLocation>,
89 {
90 Self::Point {
91 coordinates: coordinates.into(),
92 }
93 }
94
95 pub fn line_string<T>(coordinates: T) -> Self
97 where
98 T: IntoIterator,
99 T::Item: Into<GeoLocation>,
100 {
101 Self::LineString {
102 coordinates: coordinates.into_iter().map(Into::into).collect(),
103 }
104 }
105
106 pub fn polygon<T>(coordinates: T) -> Self
108 where
109 T: IntoIterator,
110 T::Item: IntoIterator,
111 <T::Item as IntoIterator>::Item: Into<GeoLocation>,
112 {
113 Self::Polygon {
114 coordinates: coordinates
115 .into_iter()
116 .map(|x| x.into_iter().map(Into::into).collect())
117 .collect(),
118 }
119 }
120
121 pub fn multi_point<T>(coordinates: T) -> Self
123 where
124 T: IntoIterator,
125 T::Item: Into<GeoLocation>,
126 {
127 Self::MultiPoint {
128 coordinates: coordinates.into_iter().map(Into::into).collect(),
129 }
130 }
131
132 pub fn multi_line_string<T>(coordinates: T) -> Self
134 where
135 T: IntoIterator,
136 T::Item: IntoIterator,
137 <T::Item as IntoIterator>::Item: Into<GeoLocation>,
138 {
139 Self::MultiLineString {
140 coordinates: coordinates
141 .into_iter()
142 .map(|x| x.into_iter().map(Into::into).collect())
143 .collect(),
144 }
145 }
146
147 pub fn multi_polygon<T>(coordinates: T) -> Self
149 where
150 T: IntoIterator,
151 T::Item: IntoIterator,
152 <T::Item as IntoIterator>::Item: IntoIterator,
153 <<T::Item as IntoIterator>::Item as IntoIterator>::Item: Into<GeoLocation>,
154 {
155 Self::MultiPolygon {
156 coordinates: coordinates
157 .into_iter()
158 .map(|x| {
159 x.into_iter()
160 .map(|y| y.into_iter().map(Into::into).collect())
161 .collect()
162 })
163 .collect(),
164 }
165 }
166
167 pub fn envelope<T>(top_left: T, bottom_right: T) -> Self
169 where
170 T: Into<GeoLocation>,
171 {
172 Self::Envelope {
173 coordinates: (top_left.into(), bottom_right.into()),
174 }
175 }
176
177 pub fn circle<T, R>(coordinates: T, radius: R) -> Self
179 where
180 T: Into<GeoLocation>,
181 R: Into<Distance>,
182 {
183 Self::Circle {
184 coordinates: coordinates.into(),
185 radius: radius.into(),
186 }
187 }
188
189 pub fn geometry_collection<T>(geometries: T) -> Self
191 where
192 T: IntoIterator,
193 T::Item: Into<Self>,
194 {
195 Self::GeometryCollection {
196 geometries: geometries.into_iter().map(Into::into).collect(),
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204 use crate::util::*;
205
206 #[test]
207 fn serialization() {
208 assert_serialize(
209 GeoShape::point([-77.0, 38.0]),
210 json!({
211 "type": "point",
212 "coordinates": [-77.0, 38.0]
213 }),
214 );
215
216 assert_serialize(
217 GeoShape::line_string([[-77.0, 38.0], [-77.0, 38.0]]),
218 json!({
219 "type": "linestring",
220 "coordinates": [[-77.0, 38.0], [-77.0, 38.0]]
221 }),
222 );
223
224 assert_serialize(
225 GeoShape::polygon([
226 vec![
227 [-17.0, 10.0],
228 [16.0, 15.0],
229 [12.0, 0.0],
230 [16.0, -15.0],
231 [-17.0, -10.0],
232 [-17.0, 10.0],
233 ],
234 vec![[18.2, 8.2], [-18.8, 8.2], [-10.8, -8.8], [18.2, 8.2]],
235 ]),
236 json!({
237 "type": "polygon",
238 "coordinates": [
239 [
240 [-17.0, 10.0],
241 [16.0, 15.0],
242 [12.0, 0.0],
243 [16.0, -15.0],
244 [-17.0, -10.0],
245 [-17.0, 10.0],
246 ],
247 [
248 [18.2, 8.2],
249 [-18.8, 8.2],
250 [-10.8, -8.8],
251 [18.2, 8.2],
252 ],
253 ]
254 }),
255 );
256
257 assert_serialize(
258 GeoShape::multi_point([[-77.0, 38.0], [-77.0, 38.0]]),
259 json!({
260 "type": "multipoint",
261 "coordinates": [[-77.0, 38.0], [-77.0, 38.0]]
262 }),
263 );
264
265 assert_serialize(
266 GeoShape::multi_line_string([
267 [[12.0, 2.0], [13.0, 2.0], [13.0, 3.0], [12.0, 3.0]],
268 [[10.0, 0.0], [11.0, 0.0], [11.0, 1.0], [10.0, 1.0]],
269 [[10.2, 0.2], [10.8, 0.2], [10.8, 0.8], [12.0, 0.8]],
270 ]),
271 json!({
272 "type": "multilinestring",
273 "coordinates": [
274 [[12.0, 2.0], [13.0, 2.0], [13.0, 3.0], [12.0, 3.0]],
275 [[10.0, 0.0], [11.0, 0.0], [11.0, 1.0], [10.0, 1.0]],
276 [[10.2, 0.2], [10.8, 0.2], [10.8, 0.8], [12.0, 0.8]],
277 ]
278 }),
279 );
280
281 assert_serialize(
282 GeoShape::multi_polygon([
283 vec![
284 vec![
285 [-17.0, 10.0],
286 [16.0, 15.0],
287 [12.0, 0.0],
288 [16.0, -15.0],
289 [-17.0, -10.0],
290 [-17.0, 10.0],
291 ],
292 vec![[18.2, 8.2], [-18.8, 8.2], [-10.8, -8.8], [18.2, 8.2]],
293 ],
294 vec![vec![
295 [-15.0, 8.0],
296 [16.0, 15.0],
297 [12.0, 0.0],
298 [16.0, -15.0],
299 [-17.0, -10.0],
300 [-15.0, 8.0],
301 ]],
302 ]),
303 json!({
304 "type": "multipolygon",
305 "coordinates": [
306 [
307 [
308 [-17.0, 10.0],
309 [16.0, 15.0],
310 [12.0, 0.0],
311 [16.0, -15.0],
312 [-17.0, -10.0],
313 [-17.0, 10.0],
314 ],
315 [
316 [18.2, 8.2],
317 [-18.8, 8.2],
318 [-10.8, -8.8],
319 [18.2, 8.2],
320 ],
321 ],
322 [
323 [
324 [-15.0, 8.0],
325 [16.0, 15.0],
326 [12.0, 0.0],
327 [16.0, -15.0],
328 [-17.0, -10.0],
329 [-15.0, 8.0],
330 ]
331 ],
332 ]
333 }),
334 );
335
336 assert_serialize(
337 GeoShape::envelope([-77.0, 38.0], [-77.0, 38.0]),
338 json!({
339 "type": "envelope",
340 "coordinates": [[-77.0, 38.0], [-77.0, 38.0]]
341 }),
342 );
343
344 assert_serialize(
345 GeoShape::circle([-45.0, 45.0], Distance::Meters(100)),
346 json!({
347 "type": "circle",
348 "radius": "100m",
349 "coordinates": [-45.0, 45.0]
350 }),
351 );
352
353 assert_serialize(
354 GeoShape::geometry_collection([
355 GeoShape::envelope([-77.0, 38.0], [-77.0, 38.0]),
356 GeoShape::point([-77.0, 38.0]),
357 ]),
358 json!({
359 "type": "geometrycollection",
360 "geometries": [
361 {
362 "type": "envelope",
363 "coordinates": [[-77.0, 38.0], [-77.0, 38.0]]
364 },
365 {
366 "type": "point",
367 "coordinates": [-77.0, 38.0]
368 },
369 ]
370 }),
371 );
372 }
373}