Skip to main content

kml/
conversion.rs

1//! Module for converting KML geometry elements to and from `geo-types` primitives
2//!
3//! # Example
4//!
5//! ```
6//! use kml::types::Coord;
7//! use geo_types;
8//!
9//! let kml_coord = Coord::new(1.0, 1.0, None);
10//! let geo_coord = geo_types::Coord::from(kml_coord);
11//! let kml_coord: Coord = Coord::from(geo_coord);
12//! ```
13use std::convert::TryFrom;
14
15use crate::errors::Error;
16use crate::types::{
17    Coord, CoordType, Geometry, Kml, LineString, LinearRing, MultiGeometry, Point, Polygon,
18};
19
20#[allow(deprecated)]
21#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
22impl<T> From<geo_types::Coordinate<T>> for Coord<T>
23where
24    T: CoordType,
25{
26    fn from(val: geo_types::Coordinate<T>) -> Coord<T> {
27        Coord::from((val.x, val.y))
28    }
29}
30
31#[allow(deprecated)]
32#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
33impl<T> From<Coord<T>> for geo_types::Coordinate<T>
34where
35    T: CoordType,
36{
37    fn from(val: Coord<T>) -> geo_types::Coordinate<T> {
38        geo_types::Coordinate::from((val.x, val.y))
39    }
40}
41
42#[allow(deprecated)]
43#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
44impl<T> From<geo_types::Point<T>> for Point<T>
45where
46    T: CoordType + Default,
47{
48    fn from(val: geo_types::Point<T>) -> Point<T> {
49        Point::from(Coord::from(val.0))
50    }
51}
52
53#[allow(deprecated)]
54#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
55impl<T> From<Point<T>> for geo_types::Point<T>
56where
57    T: CoordType,
58{
59    fn from(val: Point<T>) -> geo_types::Point<T> {
60        geo_types::Point::from(geo_types::Coordinate::from(val.coord))
61    }
62}
63
64#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
65impl<T> From<geo_types::Line<T>> for LineString<T>
66where
67    T: CoordType + Default,
68{
69    fn from(val: geo_types::Line<T>) -> LineString<T> {
70        LineString::from(vec![Coord::from(val.start), Coord::from(val.end)])
71    }
72}
73
74#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
75impl<T> From<geo_types::LineString<T>> for LineString<T>
76where
77    T: CoordType + Default,
78{
79    fn from(val: geo_types::LineString<T>) -> LineString<T> {
80        LineString::from(
81            val.0
82                .into_iter()
83                .map(Coord::from)
84                .collect::<Vec<Coord<T>>>(),
85        )
86    }
87}
88
89#[allow(deprecated)]
90#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
91impl<T> From<LineString<T>> for geo_types::LineString<T>
92where
93    T: CoordType,
94{
95    fn from(val: LineString<T>) -> geo_types::LineString<T> {
96        geo_types::LineString(
97            val.coords
98                .into_iter()
99                .map(geo_types::Coordinate::from)
100                .collect(),
101        )
102    }
103}
104
105#[allow(deprecated)]
106#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
107impl<T> From<geo_types::LineString<T>> for LinearRing<T>
108where
109    T: CoordType + Default,
110{
111    fn from(val: geo_types::LineString<T>) -> LinearRing<T> {
112        LinearRing::from(
113            val.0
114                .into_iter()
115                .map(Coord::from)
116                .collect::<Vec<Coord<T>>>(),
117        )
118    }
119}
120
121#[allow(deprecated)]
122#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
123impl<T> From<LinearRing<T>> for geo_types::LineString<T>
124where
125    T: CoordType,
126{
127    fn from(val: LinearRing<T>) -> geo_types::LineString<T> {
128        geo_types::LineString(
129            val.coords
130                .into_iter()
131                .map(geo_types::Coordinate::from)
132                .collect(),
133        )
134    }
135}
136
137#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
138impl<T> From<geo_types::Polygon<T>> for Polygon<T>
139where
140    T: CoordType + Default,
141{
142    fn from(val: geo_types::Polygon<T>) -> Polygon<T> {
143        let (outer, inner) = val.into_inner();
144        Polygon::new(
145            LinearRing::from(outer),
146            inner
147                .into_iter()
148                .map(LinearRing::from)
149                .collect::<Vec<LinearRing<T>>>(),
150        )
151    }
152}
153
154#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
155impl<T> From<geo_types::Rect<T>> for Polygon<T>
156where
157    T: CoordType + Default,
158{
159    fn from(val: geo_types::Rect<T>) -> Polygon<T> {
160        Polygon::from(val.to_polygon())
161    }
162}
163
164#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
165impl<T> From<geo_types::Triangle<T>> for Polygon<T>
166where
167    T: CoordType + Default,
168{
169    fn from(val: geo_types::Triangle<T>) -> Polygon<T> {
170        Polygon::from(val.to_polygon())
171    }
172}
173
174#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
175impl<T> From<Polygon<T>> for geo_types::Polygon<T>
176where
177    T: CoordType,
178{
179    fn from(val: Polygon<T>) -> geo_types::Polygon<T> {
180        geo_types::Polygon::new(
181            geo_types::LineString::from(val.outer),
182            val.inner
183                .into_iter()
184                .map(geo_types::LineString::from)
185                .collect::<Vec<geo_types::LineString<T>>>(),
186        )
187    }
188}
189
190#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
191impl<T> From<geo_types::MultiPoint<T>> for MultiGeometry<T>
192where
193    T: CoordType + Default,
194{
195    fn from(val: geo_types::MultiPoint<T>) -> MultiGeometry<T> {
196        MultiGeometry::new(
197            val.into_iter()
198                .map(|p| Geometry::Point(Point::from(p)))
199                .collect::<Vec<Geometry<T>>>(),
200        )
201    }
202}
203
204#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
205impl<T> From<geo_types::MultiLineString<T>> for MultiGeometry<T>
206where
207    T: CoordType + Default,
208{
209    fn from(val: geo_types::MultiLineString<T>) -> MultiGeometry<T> {
210        MultiGeometry::new(
211            val.into_iter()
212                .map(|l| Geometry::LineString(LineString::from(l)))
213                .collect::<Vec<Geometry<T>>>(),
214        )
215    }
216}
217
218#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
219impl<T> From<geo_types::MultiPolygon<T>> for MultiGeometry<T>
220where
221    T: CoordType + Default,
222{
223    fn from(val: geo_types::MultiPolygon<T>) -> MultiGeometry<T> {
224        MultiGeometry::new(
225            val.into_iter()
226                .map(|p| Geometry::Polygon(Polygon::from(p)))
227                .collect::<Vec<Geometry<T>>>(),
228        )
229    }
230}
231
232#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
233impl<T> From<geo_types::GeometryCollection<T>> for MultiGeometry<T>
234where
235    T: CoordType + Default,
236{
237    fn from(val: geo_types::GeometryCollection<T>) -> MultiGeometry<T> {
238        MultiGeometry::new(
239            val.into_iter()
240                .map(Geometry::from)
241                .collect::<Vec<Geometry<T>>>(),
242        )
243    }
244}
245
246#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
247impl<T> TryFrom<MultiGeometry<T>> for geo_types::GeometryCollection<T>
248where
249    T: CoordType,
250{
251    type Error = Error;
252
253    fn try_from(val: MultiGeometry<T>) -> Result<geo_types::GeometryCollection<T>, Self::Error> {
254        Ok(geo_types::GeometryCollection(
255            val.geometries
256                .into_iter()
257                .map(geo_types::Geometry::try_from)
258                .collect::<Result<Vec<geo_types::Geometry<T>>, _>>()?,
259        ))
260    }
261}
262
263#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
264impl<T> From<geo_types::Geometry<T>> for Geometry<T>
265where
266    T: CoordType + Default,
267{
268    fn from(val: geo_types::Geometry<T>) -> Geometry<T> {
269        match val {
270            geo_types::Geometry::Point(p) => Geometry::Point(Point::from(p)),
271            geo_types::Geometry::Line(l) => Geometry::LineString(LineString::from(l)),
272            geo_types::Geometry::LineString(l) => Geometry::LineString(LineString::from(l)),
273            geo_types::Geometry::Polygon(p) => Geometry::Polygon(Polygon::from(p)),
274            geo_types::Geometry::MultiPoint(p) => Geometry::MultiGeometry(MultiGeometry::from(p)),
275            geo_types::Geometry::MultiLineString(l) => {
276                Geometry::MultiGeometry(MultiGeometry::from(l))
277            }
278            geo_types::Geometry::MultiPolygon(p) => Geometry::MultiGeometry(MultiGeometry::from(p)),
279            geo_types::Geometry::GeometryCollection(g) => {
280                Geometry::MultiGeometry(MultiGeometry::from(g))
281            }
282            geo_types::Geometry::Rect(r) => Geometry::Polygon(Polygon::from(r)),
283            geo_types::Geometry::Triangle(t) => Geometry::Polygon(Polygon::from(t)),
284        }
285    }
286}
287
288#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
289impl<T> TryFrom<Geometry<T>> for geo_types::Geometry<T>
290where
291    T: CoordType,
292{
293    type Error = Error;
294
295    fn try_from(val: Geometry<T>) -> Result<geo_types::Geometry<T>, Self::Error> {
296        match val {
297            Geometry::Point(p) => Ok(geo_types::Geometry::Point(geo_types::Point::from(p))),
298            Geometry::LineString(l) => Ok(geo_types::Geometry::LineString(
299                geo_types::LineString::from(l),
300            )),
301            Geometry::LinearRing(l) => Ok(geo_types::Geometry::LineString(
302                geo_types::LineString::from(l),
303            )),
304            Geometry::Polygon(p) => Ok(geo_types::Geometry::Polygon(geo_types::Polygon::from(p))),
305            Geometry::MultiGeometry(g) => Ok(geo_types::Geometry::GeometryCollection(
306                geo_types::GeometryCollection::try_from(g)?,
307            )),
308            _ => Err(Error::InvalidGeometry("Can't convert geometry".to_string())),
309        }
310    }
311}
312
313#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
314impl<T> TryFrom<Kml<T>> for Vec<geo_types::Geometry<T>>
315where
316    T: CoordType,
317{
318    type Error = Error;
319
320    fn try_from(k: Kml<T>) -> Result<Vec<geo_types::Geometry<T>>, Self::Error> {
321        match k {
322            Kml::KmlDocument(d) => Ok(d
323                .elements
324                .into_iter()
325                .flat_map(Vec::<geo_types::Geometry<T>>::try_from)
326                .flatten()
327                .collect::<Vec<geo_types::Geometry<T>>>()),
328            Kml::Point(p) => Ok(vec![
329                geo_types::Geometry::Point(geo_types::Point::from(p));
330                1
331            ]),
332            Kml::LineString(l) => Ok(vec![
333                geo_types::Geometry::LineString(
334                    geo_types::LineString::from(l),
335                );
336                1
337            ]),
338            Kml::LinearRing(l) => Ok(vec![
339                geo_types::Geometry::LineString(
340                    geo_types::LineString::from(l),
341                );
342                1
343            ]),
344            Kml::Polygon(p) => Ok(vec![
345                geo_types::Geometry::Polygon(geo_types::Polygon::from(
346                    p
347                ));
348                1
349            ]),
350            Kml::MultiGeometry(g) => Ok(geo_types::GeometryCollection::try_from(g)?.0),
351            Kml::Placemark(p) => Ok(if let Some(g) = p.geometry {
352                vec![geo_types::Geometry::try_from(g)?; 1]
353            } else {
354                vec![]
355            }),
356            Kml::Document { elements, .. } => Ok(elements
357                .into_iter()
358                .flat_map(Vec::<geo_types::Geometry<T>>::try_from)
359                .flatten()
360                .collect()),
361            Kml::Folder(f) => Ok(f
362                .elements
363                .into_iter()
364                .flat_map(Vec::<geo_types::Geometry<T>>::try_from)
365                .flatten()
366                .collect()),
367            _ => Ok(vec![]),
368        }
369    }
370}
371
372#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
373impl<T> TryFrom<Kml<T>> for geo_types::GeometryCollection<T>
374where
375    T: CoordType,
376{
377    type Error = Error;
378
379    fn try_from(k: Kml<T>) -> Result<geo_types::GeometryCollection<T>, Self::Error> {
380        Ok(geo_types::GeometryCollection(
381            Vec::<geo_types::Geometry<T>>::try_from(k)?,
382        ))
383    }
384}
385
386/// A shortcut for producing `geo-types` [GeometryCollection](../geo_types/struct.GeometryCollection.html)
387/// from valid KML input.
388///
389/// # Example
390///
391/// ```
392/// use geo_types::GeometryCollection;
393/// use kml::{quick_collection, Kml};
394///
395/// let kml_str = r#"
396/// <Folder>
397///   <Point>
398///     <coordinates>1,1,1</coordinates>
399///     <altitudeMode>relativeToGround</altitudeMode>
400///   </Point>
401///   <LineString>
402///     <coordinates>1,1 2,1 3,1</coordinates>
403///     <altitudeMode>relativeToGround</altitudeMode>
404///   </LineString>
405/// </Folder>"#;
406/// let k: Kml<f64> = kml_str.parse().unwrap();
407/// // Turn the KML string into a geo_types GeometryCollection
408/// let mut collection: GeometryCollection<f64> = quick_collection(k).unwrap();
409/// ```
410#[deprecated(
411    since = "0.8.7",
412    note = "use `geo_types::GeometryCollection::try_from(&k)` instead"
413)]
414#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
415pub fn quick_collection<T>(k: Kml<T>) -> Result<geo_types::GeometryCollection<T>, Error>
416where
417    T: CoordType,
418{
419    geo_types::GeometryCollection::try_from(k)
420}
421
422#[cfg(test)]
423mod tests {
424    use super::*;
425    use crate::types::Folder;
426    use crate::KmlDocument;
427
428    #[test]
429    fn test_try_from_collection() {
430        let k = KmlDocument {
431            elements: vec![
432                Kml::Point(Point::from(Coord::from((1., 1.)))),
433                Kml::Folder(Folder {
434                    elements: vec![
435                        Kml::LineString(LineString::from(vec![
436                            Coord::from((1., 1.)),
437                            Coord::from((2., 2.)),
438                        ])),
439                        Kml::Point(Point::from(Coord::from((3., 3.)))),
440                    ],
441                    ..Default::default()
442                }),
443            ],
444            ..Default::default()
445        };
446
447        let gc = geo_types::GeometryCollection(vec![
448            geo_types::Geometry::Point(geo_types::Point::from((1., 1.))),
449            geo_types::Geometry::LineString(geo_types::LineString::from(vec![(1., 1.), (2., 2.)])),
450            geo_types::Geometry::Point(geo_types::Point::from((3., 3.))),
451        ]);
452        assert_eq!(
453            geo_types::GeometryCollection::try_from(Kml::KmlDocument(k)).unwrap(),
454            gc
455        );
456    }
457}