elasticsearch_dsl/search/params/
shape.rs

1use crate::search::*;
2
3/// The `shape` data type facilitates the indexing of and searching with
4/// arbitrary `x, y` cartesian shapes such as rectangles and polygons. It can
5/// be used to index and query geometries whose coordinates fall in a
6/// 2-dimensional planar coordinate system.
7#[derive(Debug, Clone, PartialEq, Serialize)]
8#[serde(tag = "type")]
9pub enum Shape {
10    /// A single `x, y` coordinate
11    #[serde(rename = "point")]
12    Point {
13        /// Coordinates
14        coordinates: Coordinate,
15    },
16
17    /// An arbitrary line given two or more points
18    #[serde(rename = "linestring")]
19    LineString {
20        /// Coordinates
21        coordinates: Vec<Coordinate>,
22    },
23
24    /// A closed polygon whose first and last point must match, thus requiring
25    /// `n + 1` vertices to create an `n-sided` polygon and a minimum of `4`
26    /// vertices
27    #[serde(rename = "polygon")]
28    Polygon {
29        /// Coordinates
30        coordinates: Vec<Vec<Coordinate>>,
31    },
32
33    /// An array of unconnected, but likely related points
34    #[serde(rename = "multipoint")]
35    MultiPoint {
36        /// Coordinates
37        coordinates: Vec<Coordinate>,
38    },
39
40    /// An array of separate linestrings
41    #[serde(rename = "multilinestring")]
42    MultiLineString {
43        /// Coordinates
44        coordinates: Vec<Vec<Coordinate>>,
45    },
46
47    /// An array of separate polygons
48    #[serde(rename = "multipolygon")]
49    MultiPolygon {
50        /// Coordinates
51        coordinates: Vec<Vec<Vec<Coordinate>>>,
52    },
53
54    /// A bounding rectangle, or envelope, specified by specifying only
55    /// the top left and bottom right points.
56    #[serde(rename = "envelope")]
57    Envelope {
58        /// Coordinates
59        coordinates: (Coordinate, Coordinate),
60    },
61
62    /// A GeoJSON shape similar to the `multi*` shapes except that multiple
63    /// types can coexist (e.g., a Point and a LineString)
64    #[serde(rename = "geometrycollection")]
65    GeometryCollection {
66        /// A collection of shapes
67        geometries: Vec<Shape>,
68    },
69}
70
71impl Shape {
72    /// Creates an instance of [`Shape::Point`]
73    pub fn point<T>(coordinates: T) -> Self
74    where
75        T: Into<Coordinate>,
76    {
77        Self::Point {
78            coordinates: coordinates.into(),
79        }
80    }
81
82    /// Creates an instance of [`Shape::LineString`]
83    pub fn line_string<T>(coordinates: T) -> Self
84    where
85        T: IntoIterator,
86        T::Item: Into<Coordinate>,
87    {
88        Self::LineString {
89            coordinates: coordinates.into_iter().map(Into::into).collect(),
90        }
91    }
92
93    /// Creates an instance of [`Shape::Polygon`]
94    pub fn polygon<T>(coordinates: T) -> Self
95    where
96        T: IntoIterator,
97        T::Item: IntoIterator,
98        <T::Item as IntoIterator>::Item: Into<Coordinate>,
99    {
100        Self::Polygon {
101            coordinates: coordinates
102                .into_iter()
103                .map(|x| x.into_iter().map(Into::into).collect())
104                .collect(),
105        }
106    }
107
108    /// Creates an instance of [`Shape::MultiPoint`]
109    pub fn multi_point<T>(coordinates: T) -> Self
110    where
111        T: IntoIterator,
112        T::Item: Into<Coordinate>,
113    {
114        Self::MultiPoint {
115            coordinates: coordinates.into_iter().map(Into::into).collect(),
116        }
117    }
118
119    /// Creates an instance of [`Shape::MultiLineString`]
120    pub fn multi_line_string<T>(coordinates: T) -> Self
121    where
122        T: IntoIterator,
123        T::Item: IntoIterator,
124        <T::Item as IntoIterator>::Item: Into<Coordinate>,
125    {
126        Self::MultiLineString {
127            coordinates: coordinates
128                .into_iter()
129                .map(|x| x.into_iter().map(Into::into).collect())
130                .collect(),
131        }
132    }
133
134    /// Creates an instance of [`Shape::MultiPolygon`]
135    pub fn multi_polygon<T>(coordinates: T) -> Self
136    where
137        T: IntoIterator,
138        T::Item: IntoIterator,
139        <T::Item as IntoIterator>::Item: IntoIterator,
140        <<T::Item as IntoIterator>::Item as IntoIterator>::Item: Into<Coordinate>,
141    {
142        Self::MultiPolygon {
143            coordinates: coordinates
144                .into_iter()
145                .map(|x| {
146                    x.into_iter()
147                        .map(|y| y.into_iter().map(Into::into).collect())
148                        .collect()
149                })
150                .collect(),
151        }
152    }
153
154    /// Creates an instance of [`Shape::Envelope`]
155    pub fn envelope<T>(top_left: T, bottom_right: T) -> Self
156    where
157        T: Into<Coordinate>,
158    {
159        Self::Envelope {
160            coordinates: (top_left.into(), bottom_right.into()),
161        }
162    }
163
164    /// Creates an instance of [`Shape::GeometryCollection`]
165    pub fn geometry_collection<T>(geometries: T) -> Self
166    where
167        T: IntoIterator,
168        T::Item: Into<Self>,
169    {
170        Self::GeometryCollection {
171            geometries: geometries.into_iter().map(Into::into).collect(),
172        }
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179    use crate::util::*;
180
181    #[test]
182    fn serialization() {
183        assert_serialize(
184            Shape::point([-77.0, 38.0]),
185            json!({
186                "type": "point",
187                "coordinates": [-77.0, 38.0]
188            }),
189        );
190
191        assert_serialize(
192            Shape::line_string([[-77.0, 38.0], [-77.0, 38.0]]),
193            json!({
194                "type": "linestring",
195                "coordinates": [[-77.0, 38.0], [-77.0, 38.0]]
196            }),
197        );
198
199        assert_serialize(
200            Shape::polygon([
201                vec![
202                    [-17.0, 10.0],
203                    [16.0, 15.0],
204                    [12.0, 0.0],
205                    [16.0, -15.0],
206                    [-17.0, -10.0],
207                    [-17.0, 10.0],
208                ],
209                vec![[18.2, 8.2], [-18.8, 8.2], [-10.8, -8.8], [18.2, 8.2]],
210            ]),
211            json!({
212                "type": "polygon",
213                "coordinates": [
214                    [
215                        [-17.0, 10.0],
216                        [16.0, 15.0],
217                        [12.0, 0.0],
218                        [16.0, -15.0],
219                        [-17.0, -10.0],
220                        [-17.0, 10.0],
221                    ],
222                    [
223                        [18.2, 8.2],
224                        [-18.8, 8.2],
225                        [-10.8, -8.8],
226                        [18.2, 8.2],
227                    ],
228                ]
229            }),
230        );
231
232        assert_serialize(
233            Shape::multi_point([[-77.0, 38.0], [-77.0, 38.0]]),
234            json!({
235                "type": "multipoint",
236                "coordinates": [[-77.0, 38.0], [-77.0, 38.0]]
237            }),
238        );
239
240        assert_serialize(
241            Shape::multi_line_string([
242                [[12.0, 2.0], [13.0, 2.0], [13.0, 3.0], [12.0, 3.0]],
243                [[10.0, 0.0], [11.0, 0.0], [11.0, 1.0], [10.0, 1.0]],
244                [[10.2, 0.2], [10.8, 0.2], [10.8, 0.8], [12.0, 0.8]],
245            ]),
246            json!({
247                "type": "multilinestring",
248                "coordinates": [
249                    [[12.0, 2.0], [13.0, 2.0], [13.0, 3.0], [12.0, 3.0]],
250                    [[10.0, 0.0], [11.0, 0.0], [11.0, 1.0], [10.0, 1.0]],
251                    [[10.2, 0.2], [10.8, 0.2], [10.8, 0.8], [12.0, 0.8]],
252                ]
253            }),
254        );
255
256        assert_serialize(
257            Shape::multi_polygon([
258                vec![
259                    vec![
260                        [-17.0, 10.0],
261                        [16.0, 15.0],
262                        [12.0, 0.0],
263                        [16.0, -15.0],
264                        [-17.0, -10.0],
265                        [-17.0, 10.0],
266                    ],
267                    vec![[18.2, 8.2], [-18.8, 8.2], [-10.8, -8.8], [18.2, 8.2]],
268                ],
269                vec![vec![
270                    [-15.0, 8.0],
271                    [16.0, 15.0],
272                    [12.0, 0.0],
273                    [16.0, -15.0],
274                    [-17.0, -10.0],
275                    [-15.0, 8.0],
276                ]],
277            ]),
278            json!({
279                "type": "multipolygon",
280                "coordinates": [
281                    [
282                        [
283                            [-17.0, 10.0],
284                            [16.0, 15.0],
285                            [12.0, 0.0],
286                            [16.0, -15.0],
287                            [-17.0, -10.0],
288                            [-17.0, 10.0],
289                        ],
290                        [
291                            [18.2, 8.2],
292                            [-18.8, 8.2],
293                            [-10.8, -8.8],
294                            [18.2, 8.2],
295                        ],
296                    ],
297                    [
298                        [
299                            [-15.0, 8.0],
300                            [16.0, 15.0],
301                            [12.0, 0.0],
302                            [16.0, -15.0],
303                            [-17.0, -10.0],
304                            [-15.0, 8.0],
305                        ]
306                    ],
307                ]
308            }),
309        );
310
311        assert_serialize(
312            Shape::envelope([-77.0, 38.0], [-77.0, 38.0]),
313            json!({
314                "type": "envelope",
315                "coordinates": [[-77.0, 38.0], [-77.0, 38.0]]
316            }),
317        );
318
319        assert_serialize(
320            Shape::geometry_collection([
321                Shape::envelope([-77.0, 38.0], [-77.0, 38.0]),
322                Shape::point([-77.0, 38.0]),
323            ]),
324            json!({
325                "type": "geometrycollection",
326                "geometries": [
327                    {
328                        "type": "envelope",
329                        "coordinates": [[-77.0, 38.0], [-77.0, 38.0]]
330                    },
331                    {
332                        "type": "point",
333                        "coordinates": [-77.0, 38.0]
334                    },
335                ]
336            }),
337        );
338    }
339}