elasticsearch_dsl/search/params/
geo_shape.rs

1use crate::search::*;
2
3/// The `geo_shape` data type facilitates the indexing of and searching with
4/// arbitrary geo shapes such as rectangles and polygons. It should be used
5/// when either the data being indexed or the queries being executed contain
6/// shapes other than just points.
7#[derive(Debug, Clone, PartialEq, Serialize)]
8#[serde(tag = "type")]
9pub enum GeoShape {
10    /// A single geographic coordinate
11    ///
12    /// Note: Elasticsearch uses WGS-84 coordinates only
13    #[serde(rename = "point")]
14    Point {
15        /// Coordinates
16        coordinates: GeoLocation,
17    },
18
19    /// An arbitrary line given two or more points
20    #[serde(rename = "linestring")]
21    LineString {
22        /// Coordinates
23        coordinates: Vec<GeoLocation>,
24    },
25
26    /// A closed polygon whose first and last point must match, thus requiring
27    /// `n + 1` vertices to create an `n-sided` polygon and a minimum of `4`
28    /// vertices
29    #[serde(rename = "polygon")]
30    Polygon {
31        /// Coordinates
32        coordinates: Vec<Vec<GeoLocation>>,
33    },
34
35    /// An array of unconnected, but likely related points
36    #[serde(rename = "multipoint")]
37    MultiPoint {
38        /// Coordinates
39        coordinates: Vec<GeoLocation>,
40    },
41
42    /// An array of separate linestrings
43    #[serde(rename = "multilinestring")]
44    MultiLineString {
45        /// Coordinates
46        coordinates: Vec<Vec<GeoLocation>>,
47    },
48
49    /// An array of separate polygons
50    #[serde(rename = "multipolygon")]
51    MultiPolygon {
52        /// Coordinates
53        coordinates: Vec<Vec<Vec<GeoLocation>>>,
54    },
55
56    /// A bounding rectangle, or envelope, specified by specifying only
57    /// the top left and bottom right points.
58    #[serde(rename = "envelope")]
59    Envelope {
60        /// Coordinates
61        coordinates: (GeoLocation, GeoLocation),
62    },
63
64    /// A circle specified by a center point and radius with units,
65    /// which default to `METERS`
66    #[serde(rename = "circle")]
67    Circle {
68        /// Coordinates
69        coordinates: GeoLocation,
70
71        /// Circle radius
72        radius: Distance,
73    },
74
75    /// A GeoJSON shape similar to the `multi*` shapes except that multiple
76    /// types can coexist (e.g., a Point and a LineString)
77    #[serde(rename = "geometrycollection")]
78    GeometryCollection {
79        /// A collection of geo shapes
80        geometries: Vec<GeoShape>,
81    },
82}
83
84impl GeoShape {
85    /// Creates an instance of [`GeoShape::Point`]
86    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    /// Creates an instance of [`GeoShape::LineString`]
96    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    /// Creates an instance of [`GeoShape::Polygon`]
107    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    /// Creates an instance of [`GeoShape::MultiPoint`]
122    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    /// Creates an instance of [`GeoShape::MultiLineString`]
133    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    /// Creates an instance of [`GeoShape::MultiPolygon`]
148    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    /// Creates an instance of [`GeoShape::Envelope`]
168    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    /// Creates an instance of [`GeoShape::Circle`]
178    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    /// Creates an instance of [`GeoShape::GeometryCollection`]
190    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}