geo_types/
wkt_macro.rs

1/// Creates a [`crate::geometry`] from a
2/// [WKT](https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry) literal.
3///
4/// This is evaluated at compile time, so you don't need to worry about runtime errors from invalid
5/// WKT syntax.
6///
7/// Note that `POINT EMPTY` is not accepted because it is not representable as a `geo_types::Point`.
8///
9/// ```
10/// use geo_types::wkt;
11/// let point = wkt! { POINT(1.0 2.0) };
12/// assert_eq!(point.x(), 1.0);
13/// assert_eq!(point.y(), 2.0);
14///
15/// let geometry_collection = wkt! {
16///     GEOMETRYCOLLECTION(
17///         POINT(1.0 2.0),
18///         LINESTRING EMPTY,
19///         POLYGON((0.0 0.0,1.0 0.0,1.0 1.0,0.0 0.0))
20///     )
21/// };
22/// assert_eq!(geometry_collection.len(), 3);
23/// ```
24#[macro_export]
25macro_rules! wkt {
26    // Hide distracting implementation details from the generated rustdoc.
27    ($($wkt:tt)+) => {
28        {
29            $crate::wkt_internal!($($wkt)+)
30        }
31    };
32}
33
34#[macro_export]
35#[doc(hidden)]
36macro_rules! wkt_internal {
37    (POINT EMPTY) => {
38        compile_error!("EMPTY points are not supported in geo-types")
39    };
40    (POINT($x: literal $y: literal)) => {
41        $crate::point!(x: $x, y: $y)
42    };
43    (POINT $($tail: tt)*) => {
44        compile_error!("Invalid POINT wkt");
45    };
46    (LINESTRING EMPTY) => {
47        $crate::line_string![]
48    };
49    (LINESTRING ($($x: literal $y: literal),+)) => {
50        $crate::line_string![
51            $($crate::coord!(x: $x, y: $y)),*
52        ]
53    };
54    (LINESTRING ()) => {
55        compile_error!("use `EMPTY` instead of () for an empty collection")
56    };
57    (LINESTRING $($tail: tt)*) => {
58        compile_error!("Invalid LINESTRING wkt");
59    };
60    (POLYGON EMPTY) => {
61        $crate::polygon![]
62    };
63    (POLYGON ( $exterior_tt: tt )) => {
64        $crate::Polygon::new($crate::wkt!(LINESTRING $exterior_tt), $crate::_alloc::vec![])
65    };
66    (POLYGON( $exterior_tt: tt, $($interiors_tt: tt),+ )) => {
67        $crate::Polygon::new(
68            $crate::wkt!(LINESTRING $exterior_tt),
69            $crate::_alloc::vec![
70               $($crate::wkt!(LINESTRING $interiors_tt)),*
71            ]
72        )
73    };
74    (POLYGON ()) => {
75        compile_error!("use `EMPTY` instead of () for an empty collection")
76    };
77    (POLYGON $($tail: tt)*) => {
78        compile_error!("Invalid POLYGON wkt");
79    };
80    (MULTIPOINT EMPTY) => {
81        $crate::MultiPoint($crate::_alloc::vec![])
82    };
83    (MULTIPOINT ()) => {
84        compile_error!("use `EMPTY` instead of () for an empty collection")
85    };
86    (MULTIPOINT ($($x: literal $y: literal),* )) => {
87        $crate::MultiPoint(
88            $crate::_alloc::vec![$($crate::point!(x: $x, y: $y)),*]
89        )
90    };
91    (MULTIPOINT $($tail: tt)*) => {
92        compile_error!("Invalid MULTIPOINT wkt");
93    };
94    (MULTILINESTRING EMPTY) => {
95        $crate::MultiLineString($crate::_alloc::vec![])
96    };
97    (MULTILINESTRING ()) => {
98        compile_error!("use `EMPTY` instead of () for an empty collection")
99    };
100    (MULTILINESTRING ( $($line_string_tt: tt),* )) => {
101        $crate::MultiLineString($crate::_alloc::vec![
102           $($crate::wkt!(LINESTRING $line_string_tt)),*
103        ])
104    };
105    (MULTILINESTRING $($tail: tt)*) => {
106        compile_error!("Invalid MULTILINESTRING wkt");
107    };
108    (MULTIPOLYGON EMPTY) => {
109        $crate::MultiPolygon($crate::_alloc::vec![])
110    };
111    (MULTIPOLYGON ()) => {
112        compile_error!("use `EMPTY` instead of () for an empty collection")
113    };
114    (MULTIPOLYGON ( $($polygon_tt: tt),* )) => {
115        $crate::MultiPolygon($crate::_alloc::vec![
116           $($crate::wkt!(POLYGON $polygon_tt)),*
117        ])
118    };
119    (MULTIPOLYGON $($tail: tt)*) => {
120        compile_error!("Invalid MULTIPOLYGON wkt");
121    };
122    (GEOMETRYCOLLECTION EMPTY) => {
123        $crate::GeometryCollection($crate::_alloc::vec![])
124    };
125    (GEOMETRYCOLLECTION ()) => {
126        compile_error!("use `EMPTY` instead of () for an empty collection")
127    };
128    (GEOMETRYCOLLECTION ( $($el_type:tt $el_tt: tt),* )) => {
129        $crate::GeometryCollection($crate::_alloc::vec![
130           $($crate::Geometry::from($crate::wkt!($el_type $el_tt))),*
131        ])
132    };
133    (GEOMETRYCOLLECTION $($tail: tt)*) => {
134        compile_error!("Invalid GEOMETRYCOLLECTION wkt");
135    };
136    ($name: ident ($($tail: tt)*)) => {
137        compile_error!("Unknown type. Must be one of POINT, LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, or GEOMETRYCOLLECTION");
138    };
139}
140
141#[cfg(test)]
142mod test {
143    use crate::geometry::*;
144    use alloc::vec;
145
146    #[test]
147    fn point() {
148        let point = wkt! { POINT(1.0 2.0) };
149        assert_eq!(point.x(), 1.0);
150        assert_eq!(point.y(), 2.0);
151
152        let point = wkt! { POINT(1.0   2.0) };
153        assert_eq!(point.x(), 1.0);
154        assert_eq!(point.y(), 2.0);
155
156        // This (rightfully) fails to compile because geo-types doesn't support "empty" points
157        // wkt! { POINT EMPTY }
158    }
159
160    #[test]
161    fn empty_line_string() {
162        let line_string: LineString<f64> = wkt! { LINESTRING EMPTY };
163        assert_eq!(line_string.0.len(), 0);
164
165        // This (rightfully) fails to compile because its invalid wkt
166        // wkt! { LINESTRING() }
167    }
168
169    #[test]
170    fn line_string() {
171        let line_string = wkt! { LINESTRING(1.0 2.0,3.0 4.0) };
172        assert_eq!(line_string.0.len(), 2);
173        assert_eq!(line_string[0], coord! { x: 1.0, y: 2.0 });
174    }
175
176    #[test]
177    fn empty_polygon() {
178        let polygon: Polygon = wkt! { POLYGON EMPTY };
179        assert_eq!(polygon.exterior().0.len(), 0);
180        assert_eq!(polygon.interiors().len(), 0);
181
182        // This (rightfully) fails to compile because its invalid wkt
183        // wkt! { POLYGON() }
184    }
185
186    #[test]
187    fn polygon() {
188        let polygon = wkt! { POLYGON((1.0 2.0)) };
189        assert_eq!(polygon.exterior().0.len(), 1);
190        assert_eq!(polygon.exterior().0[0], coord! { x: 1.0, y: 2.0 });
191
192        let polygon = wkt! { POLYGON((1.0 2.0,3.0 4.0)) };
193        // Note: an extra coord is added to close the linestring
194        assert_eq!(polygon.exterior().0.len(), 3);
195        assert_eq!(polygon.exterior().0[0], coord! { x: 1.0, y: 2.0 });
196        assert_eq!(polygon.exterior().0[1], coord! { x: 3.0, y: 4.0 });
197        assert_eq!(polygon.exterior().0[2], coord! { x: 1.0, y: 2.0 });
198
199        let polygon = wkt! { POLYGON((1.0 2.0), (1.1 2.1)) };
200        assert_eq!(polygon.exterior().0.len(), 1);
201        assert_eq!(polygon.interiors().len(), 1);
202
203        assert_eq!(polygon.exterior().0[0], coord! { x: 1.0, y: 2.0 });
204        assert_eq!(polygon.interiors()[0].0[0], coord! { x: 1.1, y: 2.1 });
205
206        let polygon = wkt! { POLYGON((1.0 2.0,3.0 4.0), (1.1 2.1,3.1 4.1), (1.2 2.2,3.2 4.2)) };
207        assert_eq!(polygon.exterior().0.len(), 3);
208        assert_eq!(polygon.interiors().len(), 2);
209        assert_eq!(polygon.interiors()[1][1], coord! { x: 3.2, y: 4.2 });
210    }
211
212    #[test]
213    fn empty_multi_point() {
214        let multipoint: MultiPoint = wkt! { MULTIPOINT EMPTY };
215        assert!(multipoint.0.is_empty());
216        // This (rightfully) fails to compile because its invalid wkt
217        // wkt! { MULTIPOINT() }
218    }
219
220    #[test]
221    fn multi_point() {
222        let multi_point = wkt! { MULTIPOINT(1.0 2.0) };
223        assert_eq!(multi_point.0, vec![point! { x: 1.0, y: 2.0}]);
224
225        let multi_point = wkt! { MULTIPOINT(1.0 2.0,3.0 4.0) };
226        assert_eq!(
227            multi_point.0,
228            vec![point! { x: 1.0, y: 2.0}, point! { x: 3.0, y: 4.0}]
229        );
230    }
231
232    #[test]
233    fn empty_multi_line_string() {
234        let multi_line_string: MultiLineString = wkt! { MULTILINESTRING EMPTY };
235        assert_eq!(multi_line_string.0, vec![]);
236        // This (rightfully) fails to compile because its invalid wkt
237        // wkt! { MULTILINESTRING() }
238    }
239    #[test]
240    fn multi_line_string() {
241        let multi_line_string = wkt! { MULTILINESTRING ((1.0 2.0,3.0 4.0)) };
242        assert_eq!(multi_line_string.0.len(), 1);
243        assert_eq!(multi_line_string.0[0].0[1], coord! { x: 3.0, y: 4.0 });
244        let multi_line_string = wkt! { MULTILINESTRING ((1.0 2.0,3.0 4.0),(5.0 6.0,7.0 8.0)) };
245        assert_eq!(multi_line_string.0.len(), 2);
246        assert_eq!(multi_line_string.0[1].0[1], coord! { x: 7.0, y: 8.0 });
247
248        let multi_line_string = wkt! { MULTILINESTRING ((1.0 2.0,3.0 4.0),EMPTY) };
249        assert_eq!(multi_line_string.0.len(), 2);
250        assert_eq!(multi_line_string.0[1].0.len(), 0);
251    }
252
253    #[test]
254    fn empty_multi_polygon() {
255        let multi_polygon: MultiPolygon = wkt! { MULTIPOLYGON EMPTY };
256        assert!(multi_polygon.0.is_empty());
257
258        // This (rightfully) fails to compile because its invalid wkt
259        // wkt! { MULTIPOLYGON() }
260    }
261
262    #[test]
263    fn multi_line_polygon() {
264        let multi_polygon = wkt! { MULTIPOLYGON (((1.0 2.0))) };
265        assert_eq!(multi_polygon.0.len(), 1);
266        assert_eq!(multi_polygon.0[0].exterior().0[0], coord! { x: 1.0, y: 2.0});
267
268        let multi_polygon = wkt! { MULTIPOLYGON (((1.0 2.0,3.0 4.0), (1.1 2.1,3.1 4.1), (1.2 2.2,3.2 4.2)),((1.0 2.0))) };
269        assert_eq!(multi_polygon.0.len(), 2);
270        assert_eq!(
271            multi_polygon.0[0].interiors()[1].0[0],
272            coord! { x: 1.2, y: 2.2}
273        );
274
275        let multi_polygon = wkt! { MULTIPOLYGON (((1.0 2.0,3.0 4.0), (1.1 2.1,3.1 4.1), (1.2 2.2,3.2 4.2)), EMPTY) };
276        assert_eq!(multi_polygon.0.len(), 2);
277        assert_eq!(
278            multi_polygon.0[0].interiors()[1].0[0],
279            coord! { x: 1.2, y: 2.2}
280        );
281        assert!(multi_polygon.0[1].exterior().0.is_empty());
282    }
283
284    #[test]
285    fn empty_geometry_collection() {
286        let geometry_collection: GeometryCollection = wkt! { GEOMETRYCOLLECTION EMPTY };
287        assert!(geometry_collection.is_empty());
288
289        // This (rightfully) fails to compile because its invalid wkt
290        // wkt! { MULTIPOLYGON() }
291    }
292
293    #[test]
294    fn geometry_collection() {
295        let geometry_collection = wkt! {
296            GEOMETRYCOLLECTION (
297                POINT (40.0 10.0),
298                LINESTRING (10.0 10.0, 20.0 20.0, 10.0 40.0),
299                POLYGON ((40.0 40.0, 20.0 45.0, 45.0 30.0, 40.0 40.0))
300            )
301        };
302        assert_eq!(geometry_collection.len(), 3);
303
304        let line_string = match &geometry_collection[1] {
305            Geometry::LineString(line_string) => line_string,
306            _ => panic!(
307                "unexpected geometry: {geometry:?}",
308                geometry = geometry_collection[1]
309            ),
310        };
311        assert_eq!(line_string.0[1], coord! {x: 20.0, y: 20.0 });
312    }
313
314    #[test]
315    fn other_numeric_types() {
316        let point: Point<i32> = wkt!(POINT(1 2));
317        assert_eq!(point.x(), 1i32);
318        assert_eq!(point.y(), 2i32);
319
320        let point: Point<u64> = wkt!(POINT(1 2));
321        assert_eq!(point.x(), 1u64);
322        assert_eq!(point.y(), 2u64);
323
324        let point: Point<f32> = wkt!(POINT(1.0 2.0));
325        assert_eq!(point.x(), 1.0f32);
326        assert_eq!(point.y(), 2.0f32);
327    }
328}