1use geo_types::{Coordinate, Geometry, GeometryCollection, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle};
2use std::fmt;
3use geo::algorithm::orient::{Direction, Orient};
4
5pub trait ToWkt {
6 fn to_wkt(&self) -> String;
31}
32
33impl<T: num_traits::Float + fmt::Display> ToWkt for GeometryCollection<T> {
36 fn to_wkt(&self) -> String {
37 if self.is_empty() {
38 "GEOMETRYCOLLECTION EMPTY".into()
39 } else {
40 format!(
41 "GEOMETRYCOLLECTION({})",
42 self.0
43 .iter()
44 .map(|p| p.to_wkt())
45 .collect::<Vec<String>>()
46 .join(",")
47 )
48 }
49 }
50}
51
52impl<T: num_traits::Float + fmt::Display> ToWkt for Geometry<T> {
53 fn to_wkt(&self) -> String {
54 match self {
55 Geometry::MultiPolygon { .. } => self.clone().into_multi_polygon().unwrap().to_wkt(),
56 Geometry::Polygon { .. } => self.clone().into_polygon().unwrap().to_wkt(),
57 Geometry::MultiLineString { .. } => {
58 self.clone().into_multi_line_string().unwrap().to_wkt()
59 }
60 Geometry::LineString { .. } => self.clone().into_line_string().unwrap().to_wkt(),
61 Geometry::Point { .. } => self.clone().into_point().unwrap().to_wkt(),
62 _ => "GEOMETRYCOLLECTION EMPTY".into(),
63 }
64 }
65}
66
67impl<T: num_traits::Float + fmt::Display> ToWkt for MultiPolygon<T> {
70 fn to_wkt(&self) -> String {
71 multi_polygon_to_wkt(self)
72 }
73}
74
75fn multi_polygon_to_wkt<T: num_traits::Float + fmt::Display>(poly: &MultiPolygon<T>) -> String {
76 if poly.0.is_empty() {
77 "MULTIPOLYGON EMPTY".into()
78 } else {
79 format!(
80 "MULTIPOLYGON((({})))",
81 poly.0
82 .iter()
83 .map(|p| polygon_linestrings_to_wkt(&p))
84 .collect::<Vec<String>>()
85 .join(")),((")
86 )
87 }
88}
89
90impl<T: num_traits::Float + fmt::Display> ToWkt for Polygon<T> {
91 fn to_wkt(&self) -> String {
92 polygon_to_wkt(self)
93 }
94}
95
96fn polygon_to_wkt<T: num_traits::Float + fmt::Display>(poly: &Polygon<T>) -> String {
97 if poly.exterior().0.is_empty() {
98 "POLYGON EMPTY".into()
99 } else {
100 format!("POLYGON(({}))", polygon_linestrings_to_wkt(poly))
101 }
102}
103
104fn polygon_linestrings_to_wkt<T: num_traits::Float + fmt::Display>(poly: &Polygon<T>) -> String {
105 let norm_poly = poly.orient(Direction::Default);
106 let mut lines: Vec<LineString<T>> = norm_poly.interiors().into();
107 let exterior: &LineString<T> = norm_poly.exterior();
108 lines.insert(0, exterior.clone());
109
110 lines
111 .iter()
112 .map(|l| line_to_wkt(&l))
113 .collect::<Vec<String>>()
114 .join("),(")
115}
116
117impl<T: num_traits::Float + fmt::Display> ToWkt for Rect<T> {
120 fn to_wkt(&self) -> String {
121 format!("POLYGON(({} {},{} {},{} {},{} {},{} {}))",
122 self.min.x, self.min.y,
123 self.min.x, self.max.y,
124 self.max.x, self.max.y,
125 self.max.x, self.min.y,
126 self.min.x, self.min.y)
127 }
128}
129
130impl<T: num_traits::Float + fmt::Display> ToWkt for Triangle<T> {
133 fn to_wkt(&self) -> String {
134 format!("POLYGON(({} {},{} {},{} {},{} {}))",
135 self.0.x, self.0.y,
136 self.1.x, self.1.y,
137 self.2.x, self.2.y,
138 self.0.x, self.0.y)
139 }
140}
141
142impl<T: num_traits::Float + fmt::Display> ToWkt for MultiLineString<T> {
145 fn to_wkt(&self) -> String {
146 multi_linestring_to_wkt(self)
147 }
148}
149
150fn multi_linestring_to_wkt<T: num_traits::Float + fmt::Display>(
151 multi_line: &MultiLineString<T>,
152) -> String {
153 if multi_line.0.is_empty() {
154 "MULTILINESTRING EMPTY".into()
155 } else {
156 format!(
157 "MULTILINESTRING(({}))",
158 multi_line
159 .0
160 .iter()
161 .map(|l| line_to_wkt(&l))
162 .collect::<Vec<String>>()
163 .join("),(")
164 )
165 }
166}
167
168impl<T: num_traits::Float + fmt::Display> ToWkt for LineString<T> {
169 fn to_wkt(&self) -> String {
170 linestring_to_wkt(self)
171 }
172}
173
174impl<T: num_traits::Float + fmt::Display> ToWkt for Line<T> {
175 fn to_wkt(&self) -> String {
176 linestring_to_wkt(&LineString(vec![self.start, self.end]))
177 }
178}
179
180fn linestring_to_wkt<T: num_traits::Float + fmt::Display>(line: &LineString<T>) -> String {
181 if line.0.is_empty() {
182 "LINESTRING EMPTY".into()
183 } else {
184 format!("LINESTRING({})", line_to_wkt(line))
185 }
186}
187
188fn line_to_wkt<T: num_traits::Float + fmt::Display>(line: &LineString<T>) -> String {
189 line.0
190 .iter()
191 .map(|c| coord_to_wkt(&c))
192 .collect::<Vec<String>>()
193 .join(",")
194}
195
196impl<T: num_traits::Float + fmt::Display> ToWkt for MultiPoint<T> {
199 fn to_wkt(&self) -> String {
200 multi_point_to_wkt(self)
201 }
202}
203
204fn multi_point_to_wkt<T: num_traits::Float + fmt::Display>(multi_point: &MultiPoint<T>) -> String {
205 if multi_point.0.is_empty() {
206 "MULTIPOINT EMPTY".into()
207 } else {
208 format!(
209 "MULTIPOINT({})",
210 multi_point
211 .0
212 .iter()
213 .map(|p| point_to_string(&p))
214 .collect::<Vec<String>>()
215 .join(",")
216 )
217 }
218}
219
220impl<T: num_traits::Float + fmt::Display> ToWkt for Point<T> {
221 fn to_wkt(&self) -> String {
222 point_to_wkt(self)
223 }
224}
225
226fn point_to_wkt<T: num_traits::Float + fmt::Display>(point: &Point<T>) -> String {
227 format!("POINT({})", point_to_string(point))
228}
229
230fn point_to_string<T: num_traits::Float + fmt::Display>(point: &Point<T>) -> String {
231 coord_to_wkt(&point.0)
232}
233
234fn coord_to_wkt<T: num_traits::Float + fmt::Display>(coord: &Coordinate<T>) -> String {
235 format!("{} {}", coord.x, coord.y)
236}
237
238#[cfg(test)]
241mod tests {
242 use super::*;
243 use geo_types::{line_string, point, polygon};
244
245 #[test]
246 fn can_format_geom_collection() {
247 let poly = Geometry::Polygon(polygon![
248 (x: 1.0, y: 1.0),
249 (x: 4.0, y: 1.0),
250 (x: 4.0, y: 4.0),
251 (x: 1.0, y: 4.0),
252 (x: 1.0, y: 1.0),
253 ]);
254 let pe = Geometry::Point(point!(x: 1.0, y: 1.0));
255 let gc = GeometryCollection(vec![pe, poly]);
256 let wkt_out = gc.to_wkt();
257 let expected =
258 String::from("GEOMETRYCOLLECTION(POINT(1 1),POLYGON((1 1,4 1,4 4,1 4,1 1)))");
259 assert_eq!(wkt_out, expected);
260 }
261
262 #[test]
263 fn can_format_empty_geom_collection() {
264 let gc = GeometryCollection(vec![] as Vec<Geometry<f64>>);
265 let wkt_out = gc.to_wkt();
266 let expected = String::from("GEOMETRYCOLLECTION EMPTY");
267 assert_eq!(wkt_out, expected);
268 }
269
270 #[test]
271 fn can_format_multi_polygon() {
272 let poly1 = polygon![
273 (x: 1.0, y: 1.0),
274 (x: 4.0, y: 1.0),
275 (x: 4.0, y: 4.0),
276 (x: 1.0, y: 4.0),
277 (x: 1.0, y: 1.0),
278 ];
279 let poly2 = polygon!(
280 exterior: [
281 (x: 0.0, y: 0.0),
282 (x: 6.0, y: 0.0),
283 (x: 6.0, y: 6.0),
284 (x: 0.0, y: 6.0),
285 (x: 0.0, y: 0.0),],
286 interiors:[[
287 (x: 1.0, y: 1.0),
288 (x: 4.0, y: 1.0),
289 (x: 4.0, y: 4.0),
290 (x: 1.50, y: 4.0),
291 (x: 1.0, y: 1.0),]
292 ]
293 );
294 let mp = MultiPolygon(vec![poly1, poly2]);
295 let wkt_out = mp.to_wkt();
296 let expected = String::from(
297 "MULTIPOLYGON(((1 1,4 1,4 4,1 4,1 1)),((0 0,6 0,6 6,0 6,0 0),(1 1,1.5 4,4 4,4 1,1 1)))",
298 );
299 assert_eq!(wkt_out, expected);
300 }
301
302 #[test]
303 fn can_format_empty_multi_polygon() {
304 let mp = MultiPolygon(vec![] as Vec<Polygon<f64>>);
305 let wkt_out = mp.to_wkt();
306 let expected = String::from("MULTIPOLYGON EMPTY");
307 assert_eq!(wkt_out, expected);
308 }
309
310 #[test]
311 fn can_format_polygon() {
312 let poly = polygon![
313 (x: 1.0, y: 1.0),
314 (x: 4.0, y: 1.0),
315 (x: 4.0, y: 4.0),
316 (x: 1.0, y: 4.0),
317 (x: 1.0, y: 1.0),
318 ];
319 let wkt_out = poly.to_wkt();
320 let expected = String::from("POLYGON((1 1,4 1,4 4,1 4,1 1))");
321 assert_eq!(wkt_out, expected);
322 }
323
324 #[test]
325 fn can_format_empty_polygon() {
326 let poly: Polygon<f64> =
327 Polygon::new(LineString::from(vec![] as Vec<Coordinate<f64>>), vec![]);
328 let wkt_out = poly.to_wkt();
329 let expected = String::from("POLYGON EMPTY");
330 assert_eq!(wkt_out, expected);
331 }
332
333 #[test]
334 fn can_format_polygon_with_hole() {
335 let poly = polygon!(
336 exterior: [
337 (x: 0.0, y: 0.0),
338 (x: 6.0, y: 0.0),
339 (x: 6.0, y: 6.0),
340 (x: 0.0, y: 6.0),
341 (x: 0.0, y: 0.0),],
342 interiors:[[
343 (x: 1.0, y: 1.0),
344 (x: 4.0, y: 1.0),
345 (x: 4.0, y: 4.0),
346 (x: 1.50, y: 4.0),
347 (x: 1.0, y: 1.0),]
348 ]
349 );
350 let wkt_out = poly.to_wkt();
351 let expected = String::from("POLYGON((0 0,6 0,6 6,0 6,0 0),(1 1,1.5 4,4 4,4 1,1 1))");
352 assert_eq!(wkt_out, expected);
353 }
354
355 #[test]
356 fn can_format_multi_line_string() {
357 let line1 = line_string![
358 (x: 1.0, y: 1.0),
359 (x: 4.0, y: 1.0),
360 (x: 4.0, y: 4.0),
361 (x: 1.50, y: 4.0),
362 ];
363 let line2 = line_string![
364 (x: 11.0, y: 21.0),
365 (x: 34.0, y: 21.0),
366 (x: 24.0, y: 54.0),
367 (x: 31.50, y: 34.0),
368 ];
369 let ml = MultiLineString(vec![line1, line2]);
370 let wkt_out = ml.to_wkt();
371 let expected =
372 String::from("MULTILINESTRING((1 1,4 1,4 4,1.5 4),(11 21,34 21,24 54,31.5 34))");
373 assert_eq!(wkt_out, expected);
374 }
375
376 #[test]
377 fn can_format_empty_multi_line_string() {
378 let ml = MultiLineString(vec![] as Vec<LineString<f64>>);
379 let wkt_out = ml.to_wkt();
380 let expected = String::from("MULTILINESTRING EMPTY");
381 assert_eq!(wkt_out, expected);
382 }
383
384 #[test]
385 fn can_format_line_string() {
386 let line = line_string![
387 (x: 1.0, y: 1.0),
388 (x: 4.0, y: 1.0),
389 (x: 4.0, y: 4.0),
390 (x: 1.50, y: 4.0),
391 (x: 1.0, y: 1.0),
392 ];
393 let wkt_out = line.to_wkt();
394 let expected = String::from("LINESTRING(1 1,4 1,4 4,1.5 4,1 1)");
395 assert_eq!(wkt_out, expected);
396 }
397
398 #[test]
399 fn can_format_empty_line_string() {
400 let line = LineString::from(vec![] as Vec<Coordinate<f64>>);
401 let wkt_out = line.to_wkt();
402 let expected = String::from("LINESTRING EMPTY");
403 assert_eq!(wkt_out, expected);
404 }
405
406 #[test]
407 fn can_format_multi_point() {
408 let point1 = point!(x: 22.200, y: 31.0);
409 let point2 = point!(x: 4356.0, y: 1002.345);
410 let mp = MultiPoint(vec![point1, point2]);
411 let wkt_out = mp.to_wkt();
412 let expected = String::from("MULTIPOINT(22.2 31,4356 1002.345)");
413 assert_eq!(wkt_out, expected);
414 }
415
416 #[test]
417 fn can_format_empty_multi_point() {
418 let mp = MultiPoint(vec![] as Vec<Point<f64>>);
419 let wkt_out = mp.to_wkt();
420 let expected = String::from("MULTIPOINT EMPTY");
421 assert_eq!(wkt_out, expected);
422 }
423
424 #[test]
425 fn can_format_point() {
426 let point = point!(x: 22.200, y: 31.0);
427 let wkt_out = point.to_wkt();
428 let expected = String::from("POINT(22.2 31)");
429 assert_eq!(wkt_out, expected);
430 }
431}