geojson/conversion/
from_geo_types.rs

1use geo_types::{self, CoordFloat};
2
3use crate::{geometry, Feature, FeatureCollection};
4
5use crate::{LineStringType, PointType, PolygonType};
6use std::convert::From;
7
8#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
9impl<T> From<&geo_types::Point<T>> for geometry::Value
10where
11    T: CoordFloat,
12{
13    fn from(point: &geo_types::Point<T>) -> Self {
14        let coords = create_point_type(point);
15
16        geometry::Value::Point(coords)
17    }
18}
19
20#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
21impl<T> From<&geo_types::MultiPoint<T>> for geometry::Value
22where
23    T: CoordFloat,
24{
25    fn from(multi_point: &geo_types::MultiPoint<T>) -> Self {
26        let coords = multi_point
27            .0
28            .iter()
29            .map(|point| create_point_type(point))
30            .collect();
31
32        geometry::Value::MultiPoint(coords)
33    }
34}
35
36#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
37impl<T> From<&geo_types::LineString<T>> for geometry::Value
38where
39    T: CoordFloat,
40{
41    fn from(line_string: &geo_types::LineString<T>) -> Self {
42        let coords = create_line_string_type(line_string);
43
44        geometry::Value::LineString(coords)
45    }
46}
47
48#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
49impl<T> From<&geo_types::Line<T>> for geometry::Value
50where
51    T: CoordFloat,
52{
53    fn from(line: &geo_types::Line<T>) -> Self {
54        let coords = create_from_line_type(line);
55
56        geometry::Value::LineString(coords)
57    }
58}
59
60#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
61impl<T> From<&geo_types::Triangle<T>> for geometry::Value
62where
63    T: CoordFloat,
64{
65    fn from(triangle: &geo_types::Triangle<T>) -> Self {
66        let coords = create_from_triangle_type(triangle);
67
68        geometry::Value::Polygon(coords)
69    }
70}
71
72#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
73impl<T> From<&geo_types::Rect<T>> for geometry::Value
74where
75    T: CoordFloat,
76{
77    fn from(rect: &geo_types::Rect<T>) -> Self {
78        let coords = create_from_rect_type(rect);
79
80        geometry::Value::Polygon(coords)
81    }
82}
83
84#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
85impl<T> From<&geo_types::MultiLineString<T>> for geometry::Value
86where
87    T: CoordFloat,
88{
89    fn from(multi_line_string: &geo_types::MultiLineString<T>) -> Self {
90        let coords = create_multi_line_string_type(multi_line_string);
91
92        geometry::Value::MultiLineString(coords)
93    }
94}
95
96#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
97impl<T> From<&geo_types::Polygon<T>> for geometry::Value
98where
99    T: CoordFloat,
100{
101    fn from(polygon: &geo_types::Polygon<T>) -> Self {
102        let coords = create_polygon_type(polygon);
103
104        geometry::Value::Polygon(coords)
105    }
106}
107
108#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
109impl<T> From<&geo_types::MultiPolygon<T>> for geometry::Value
110where
111    T: CoordFloat,
112{
113    fn from(multi_polygon: &geo_types::MultiPolygon<T>) -> Self {
114        let coords = create_multi_polygon_type(multi_polygon);
115
116        geometry::Value::MultiPolygon(coords)
117    }
118}
119
120#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
121impl<T> From<&geo_types::GeometryCollection<T>> for geometry::Value
122where
123    T: CoordFloat,
124{
125    fn from(geometry_collection: &geo_types::GeometryCollection<T>) -> Self {
126        let values = geometry_collection
127            .0
128            .iter()
129            .map(|geometry| geometry::Geometry::new(geometry::Value::from(geometry)))
130            .collect();
131
132        geometry::Value::GeometryCollection(values)
133    }
134}
135
136#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
137impl<T> From<&geo_types::GeometryCollection<T>> for FeatureCollection
138where
139    T: CoordFloat,
140{
141    fn from(geometry_collection: &geo_types::GeometryCollection<T>) -> Self {
142        let values: Vec<Feature> = geometry_collection
143            .0
144            .iter()
145            .map(|geometry| geometry::Geometry::new(geometry::Value::from(geometry)).into())
146            .collect();
147
148        FeatureCollection {
149            bbox: None,
150            features: values,
151            foreign_members: None,
152        }
153    }
154}
155
156#[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))]
157impl<'a, T> From<&'a geo_types::Geometry<T>> for geometry::Value
158where
159    T: CoordFloat,
160{
161    /// Convert from `geo_types::Geometry` enums
162    fn from(geometry: &'a geo_types::Geometry<T>) -> Self {
163        match *geometry {
164            geo_types::Geometry::Point(ref point) => geometry::Value::from(point),
165            geo_types::Geometry::MultiPoint(ref multi_point) => geometry::Value::from(multi_point),
166            geo_types::Geometry::LineString(ref line_string) => geometry::Value::from(line_string),
167            geo_types::Geometry::Line(ref line) => geometry::Value::from(line),
168            geo_types::Geometry::Triangle(ref triangle) => geometry::Value::from(triangle),
169            geo_types::Geometry::Rect(ref rect) => geometry::Value::from(rect),
170            geo_types::Geometry::GeometryCollection(ref gc) => geometry::Value::from(gc),
171            geo_types::Geometry::MultiLineString(ref multi_line_string) => {
172                geometry::Value::from(multi_line_string)
173            }
174            geo_types::Geometry::Polygon(ref polygon) => geometry::Value::from(polygon),
175            geo_types::Geometry::MultiPolygon(ref multi_polygon) => {
176                geometry::Value::from(multi_polygon)
177            }
178        }
179    }
180}
181
182fn create_point_type<T>(point: &geo_types::Point<T>) -> PointType
183where
184    T: CoordFloat,
185{
186    let x: f64 = point.x().to_f64().unwrap();
187    let y: f64 = point.y().to_f64().unwrap();
188
189    vec![x, y]
190}
191
192fn create_line_string_type<T>(line_string: &geo_types::LineString<T>) -> LineStringType
193where
194    T: CoordFloat,
195{
196    line_string
197        .points()
198        .map(|point| create_point_type(&point))
199        .collect()
200}
201
202fn create_from_line_type<T>(line_string: &geo_types::Line<T>) -> LineStringType
203where
204    T: CoordFloat,
205{
206    vec![
207        create_point_type(&line_string.start_point()),
208        create_point_type(&line_string.end_point()),
209    ]
210}
211
212fn create_from_triangle_type<T>(triangle: &geo_types::Triangle<T>) -> PolygonType
213where
214    T: CoordFloat,
215{
216    create_polygon_type(&triangle.to_polygon())
217}
218
219fn create_from_rect_type<T>(rect: &geo_types::Rect<T>) -> PolygonType
220where
221    T: CoordFloat,
222{
223    create_polygon_type(&rect.to_polygon())
224}
225
226fn create_multi_line_string_type<T>(
227    multi_line_string: &geo_types::MultiLineString<T>,
228) -> Vec<LineStringType>
229where
230    T: CoordFloat,
231{
232    multi_line_string
233        .0
234        .iter()
235        .map(|line_string| create_line_string_type(line_string))
236        .collect()
237}
238
239fn create_polygon_type<T>(polygon: &geo_types::Polygon<T>) -> PolygonType
240where
241    T: CoordFloat,
242{
243    let mut coords = vec![polygon
244        .exterior()
245        .points()
246        .map(|point| create_point_type(&point))
247        .collect()];
248
249    coords.extend(
250        polygon
251            .interiors()
252            .iter()
253            .map(|line_string| create_line_string_type(line_string)),
254    );
255
256    coords
257}
258
259fn create_multi_polygon_type<T>(multi_polygon: &geo_types::MultiPolygon<T>) -> Vec<PolygonType>
260where
261    T: CoordFloat,
262{
263    multi_polygon
264        .0
265        .iter()
266        .map(|polygon| create_polygon_type(polygon))
267        .collect()
268}
269
270#[cfg(test)]
271mod tests {
272    use crate::{GeoJson, Geometry, Value};
273    use geo_types::{
274        Coord, GeometryCollection, Line, LineString, MultiLineString, MultiPoint, MultiPolygon,
275        Point, Polygon, Rect, Triangle,
276    };
277
278    #[test]
279    fn geo_point_conversion_test() {
280        // Test with f32 coordinates
281        let geo_point = Point::new(40.02f32, 116.34f32);
282        let geojson_point = Value::from(&geo_point);
283
284        if let Value::Point(c) = geojson_point {
285            assert_almost_eq!(geo_point.x(), c[0] as f32, 1e-6);
286            assert_almost_eq!(geo_point.y(), c[1] as f32, 1e-6);
287        } else {
288            panic!("Not valid geometry {:?}", geojson_point);
289        }
290
291        // Test with f64 coordinates.
292        let geo_point = Point::new(40.02f64, 116.34f64);
293        let geojson_point = Value::from(&geo_point);
294
295        if let Value::Point(c) = geojson_point {
296            assert_almost_eq!(geo_point.x(), c[0], 1e-6);
297            assert_almost_eq!(geo_point.y(), c[1], 1e-6);
298        } else {
299            panic!("Not valid geometry {:?}", geojson_point);
300        }
301    }
302
303    #[test]
304    fn geo_multi_point_conversion_test() {
305        let p1 = Point::new(40.02f64, 116.34f64);
306        let p2 = Point::new(13.02f64, 24.34f64);
307
308        let geo_multi_point = MultiPoint(vec![p1, p2]);
309        let geojson_multi_point = Value::from(&geo_multi_point);
310
311        if let Value::MultiPoint(c) = geojson_multi_point {
312            assert_almost_eq!(p1.x(), c[0][0], 1e-6);
313            assert_almost_eq!(p1.y(), c[0][1], 1e-6);
314            assert_almost_eq!(p2.x(), c[1][0], 1e-6);
315            assert_almost_eq!(p2.y(), c[1][1], 1e-6);
316        } else {
317            panic!("Not valid geometry {:?}", geojson_multi_point);
318        }
319    }
320
321    #[test]
322    fn geo_line_string_conversion_test() {
323        let p1 = Point::new(40.02f64, 116.34f64);
324        let p2 = Point::new(13.02f64, 24.34f64);
325
326        let geo_line_string = LineString::from(vec![p1, p2]);
327        let geojson_line_point = Value::from(&geo_line_string);
328
329        if let Value::LineString(c) = geojson_line_point {
330            assert_almost_eq!(p1.x(), c[0][0], 1e-6);
331            assert_almost_eq!(p1.y(), c[0][1], 1e-6);
332            assert_almost_eq!(p2.x(), c[1][0], 1e-6);
333            assert_almost_eq!(p2.y(), c[1][1], 1e-6);
334        } else {
335            panic!("Not valid geometry {:?}", geojson_line_point);
336        }
337    }
338
339    #[test]
340    fn geo_line_conversion_test() {
341        let p1 = Point::new(40.02f64, 116.34f64);
342        let p2 = Point::new(13.02f64, 24.34f64);
343
344        let geo_line = Line::new(p1, p2);
345        let geojson_line_point = Value::from(&geo_line);
346
347        if let Value::LineString(c) = geojson_line_point {
348            assert_almost_eq!(p1.x(), c[0][0], 1e-6);
349            assert_almost_eq!(p1.y(), c[0][1], 1e-6);
350            assert_almost_eq!(p2.x(), c[1][0], 1e-6);
351            assert_almost_eq!(p2.y(), c[1][1], 1e-6);
352        } else {
353            panic!("Not valid geometry {:?}", geojson_line_point);
354        }
355    }
356
357    #[test]
358    fn geo_triangle_conversion_test() {
359        let c1: Coord<f64> = Coord { x: 0., y: 0. };
360        let c2: Coord<f64> = Coord { x: 10., y: 20. };
361        let c3: Coord<f64> = Coord { x: 20., y: -10. };
362
363        let triangle = Triangle(c1, c2, c3);
364
365        let geojson_polygon = Value::from(&triangle);
366
367        // Geo-types Polygon construction introduces an extra vertex: let's check it!
368        if let Value::Polygon(c) = geojson_polygon {
369            assert_almost_eq!(c1.x, c[0][0][0], 1e-6);
370            assert_almost_eq!(c1.y, c[0][0][1], 1e-6);
371            assert_almost_eq!(c2.x, c[0][1][0], 1e-6);
372            assert_almost_eq!(c2.y, c[0][1][1], 1e-6);
373            assert_almost_eq!(c3.x, c[0][2][0], 1e-6);
374            assert_almost_eq!(c3.y, c[0][2][1], 1e-6);
375            assert_almost_eq!(c1.x, c[0][3][0], 1e-6);
376            assert_almost_eq!(c1.y, c[0][3][1], 1e-6);
377        } else {
378            panic!("Not valid geometry {:?}", geojson_polygon);
379        }
380    }
381
382    #[test]
383    fn geo_rect_conversion_test() {
384        let c1: Coord<f64> = Coord { x: 0., y: 0. };
385        let c2: Coord<f64> = Coord { x: 10., y: 20. };
386
387        let rect = Rect::new(c1, c2);
388
389        let geojson_polygon = Value::from(&rect);
390
391        // Geo-types Polygon construction introduces an extra vertex: let's check it!
392        if let Value::Polygon(c) = geojson_polygon {
393            assert_almost_eq!(c1.x, c[0][0][0], 1e-6);
394            assert_almost_eq!(c1.y, c[0][0][1], 1e-6);
395            assert_almost_eq!(c1.x, c[0][1][0], 1e-6);
396            assert_almost_eq!(c2.y, c[0][1][1], 1e-6);
397            assert_almost_eq!(c2.x, c[0][2][0], 1e-6);
398            assert_almost_eq!(c2.y, c[0][2][1], 1e-6);
399            assert_almost_eq!(c2.x, c[0][3][0], 1e-6);
400            assert_almost_eq!(c1.y, c[0][3][1], 1e-6);
401            assert_almost_eq!(c1.x, c[0][4][0], 1e-6);
402            assert_almost_eq!(c1.y, c[0][4][1], 1e-6);
403        } else {
404            panic!("Not valid geometry {:?}", geojson_polygon);
405        }
406    }
407
408    #[test]
409    fn geo_multi_line_string_conversion_test() {
410        let p1 = Point::new(40.02f64, 116.34f64);
411        let p2 = Point::new(13.02f64, 24.34f64);
412        let p3 = Point::new(46.84f64, 160.95f64);
413        let p4 = Point::new(42.02f64, 96.34f64);
414
415        let geo_line_string1 = LineString::from(vec![p1, p2]);
416        let geo_line_string2 = LineString::from(vec![p3, p4]);
417
418        let geo_multi_line_string = MultiLineString(vec![geo_line_string1, geo_line_string2]);
419        let geojson_multi_line_point = Value::from(&geo_multi_line_string);
420
421        if let Value::MultiLineString(c) = geojson_multi_line_point {
422            assert_almost_eq!(p1.x(), c[0][0][0], 1e-6);
423            assert_almost_eq!(p1.y(), c[0][0][1], 1e-6);
424            assert_almost_eq!(p2.x(), c[0][1][0], 1e-6);
425            assert_almost_eq!(p2.y(), c[0][1][1], 1e-6);
426            assert_almost_eq!(p3.x(), c[1][0][0], 1e-6);
427            assert_almost_eq!(p3.y(), c[1][0][1], 1e-6);
428            assert_almost_eq!(p4.x(), c[1][1][0], 1e-6);
429            assert_almost_eq!(p4.y(), c[1][1][1], 1e-6);
430        } else {
431            panic!("Not valid geometry {:?}", geojson_multi_line_point);
432        }
433    }
434
435    #[test]
436    fn geo_polygon_conversion_test() {
437        let p1 = Point::new(100.0f64, 0.0f64);
438        let p2 = Point::new(101.0f64, 0.0f64);
439        let p3 = Point::new(101.0f64, 1.0f64);
440        let p4 = Point::new(104.0f64, 0.2f64);
441        let p5 = Point::new(100.9f64, 0.2f64);
442        let p6 = Point::new(100.9f64, 0.7f64);
443
444        let geo_line_string1 = LineString::from(vec![p1, p2, p3, p1]);
445        let geo_line_string2 = LineString::from(vec![p4, p5, p6, p4]);
446
447        let geo_polygon = Polygon::new(geo_line_string1, vec![geo_line_string2]);
448        let geojson_polygon = Value::from(&geo_polygon);
449
450        if let Value::Polygon(c) = geojson_polygon {
451            assert_almost_eq!(p1.x(), c[0][0][0], 1e-6);
452            assert_almost_eq!(p1.y(), c[0][0][1], 1e-6);
453            assert_almost_eq!(p2.x(), c[0][1][0], 1e-6);
454            assert_almost_eq!(p2.y(), c[0][1][1], 1e-6);
455            assert_almost_eq!(p3.x(), c[0][2][0], 1e-6);
456            assert_almost_eq!(p3.y(), c[0][2][1], 1e-6);
457            assert_almost_eq!(p4.x(), c[1][0][0], 1e-6);
458            assert_almost_eq!(p4.y(), c[1][0][1], 1e-6);
459            assert_almost_eq!(p5.x(), c[1][1][0], 1e-6);
460            assert_almost_eq!(p5.y(), c[1][1][1], 1e-6);
461            assert_almost_eq!(p6.x(), c[1][2][0], 1e-6);
462            assert_almost_eq!(p6.y(), c[1][2][1], 1e-6);
463        } else {
464            panic!("Not valid geometry {:?}", geojson_polygon);
465        }
466    }
467
468    #[test]
469    fn geo_multi_polygon_conversion_test() {
470        let p1 = Point::new(102.0f64, 2.0f64);
471        let p2 = Point::new(103.0f64, 2.0f64);
472        let p3 = Point::new(103.0f64, 3.0f64);
473        let p4 = Point::new(100.0f64, 0.0f64);
474        let p5 = Point::new(101.0f64, 0.0f64);
475        let p6 = Point::new(101.0f64, 1.0f64);
476
477        let geo_line_string1 = LineString::from(vec![p1, p2, p3, p1]);
478        let geo_line_string2 = LineString::from(vec![p4, p5, p6, p4]);
479
480        let geo_polygon1 = Polygon::new(geo_line_string1, vec![]);
481        let geo_polygon2 = Polygon::new(geo_line_string2, vec![]);
482        let geo_multi_polygon = MultiPolygon(vec![geo_polygon1, geo_polygon2]);
483        let geojson_multi_polygon = Value::from(&geo_multi_polygon);
484
485        if let Value::MultiPolygon(c) = geojson_multi_polygon {
486            assert_almost_eq!(p1.x(), c[0][0][0][0], 1e-6);
487            assert_almost_eq!(p1.y(), c[0][0][0][1], 1e-6);
488            assert_almost_eq!(p2.x(), c[0][0][1][0], 1e-6);
489            assert_almost_eq!(p2.y(), c[0][0][1][1], 1e-6);
490            assert_almost_eq!(p3.x(), c[0][0][2][0], 1e-6);
491            assert_almost_eq!(p3.y(), c[0][0][2][1], 1e-6);
492            assert_almost_eq!(p4.x(), c[1][0][0][0], 1e-6);
493            assert_almost_eq!(p4.y(), c[1][0][0][1], 1e-6);
494            assert_almost_eq!(p5.x(), c[1][0][1][0], 1e-6);
495            assert_almost_eq!(p5.y(), c[1][0][1][1], 1e-6);
496            assert_almost_eq!(p6.x(), c[1][0][2][0], 1e-6);
497            assert_almost_eq!(p6.y(), c[1][0][2][1], 1e-6);
498        } else {
499            panic!("Not valid geometry {:?}", geojson_multi_polygon);
500        }
501    }
502
503    #[test]
504    fn geo_geometry_collection_conversion_test() {
505        let p1 = Point::new(100.0f64, 0.0f64);
506        let p2 = Point::new(100.0f64, 1.0f64);
507        let p3 = Point::new(101.0f64, 1.0f64);
508        let p4 = Point::new(102.0f64, 0.0f64);
509        let p5 = Point::new(101.0f64, 0.0f64);
510        let geo_multi_point = MultiPoint(vec![p1, p2]);
511        let geo_multi_line_string = MultiLineString(vec![
512            LineString::from(vec![p1, p2]),
513            LineString::from(vec![p2, p3]),
514        ]);
515        let geo_multi_polygon = MultiPolygon(vec![
516            Polygon::new(LineString::from(vec![p3, p4, p5, p3]), vec![]),
517            Polygon::new(LineString::from(vec![p1, p5, p3, p1]), vec![]),
518        ]);
519        let geo_geometry_collection = GeometryCollection(vec![
520            geo_types::Geometry::MultiPoint(geo_multi_point),
521            geo_types::Geometry::MultiLineString(geo_multi_line_string),
522            geo_types::Geometry::MultiPolygon(geo_multi_polygon),
523        ]);
524
525        let geojson_geometry_collection = Value::from(&geo_geometry_collection);
526
527        if let Value::GeometryCollection(geometries) = geojson_geometry_collection {
528            let geometry_type = |geometry: &Geometry| match geometry.value {
529                Value::Point(..) => "Point",
530                Value::MultiPoint(..) => "MultiPoint",
531                Value::LineString(..) => "LineString",
532                Value::MultiLineString(..) => "MultiLineString",
533                Value::Polygon(..) => "Polygon",
534                Value::MultiPolygon(..) => "MultiPolygon",
535                Value::GeometryCollection(..) => "GeometryCollection",
536            };
537
538            assert_eq!(3, geometries.len());
539            assert_eq!(geometry_type(&geometries[0]), "MultiPoint");
540            assert_eq!(geometry_type(&geometries[1]), "MultiLineString");
541            assert_eq!(geometry_type(&geometries[2]), "MultiPolygon");
542        } else {
543            panic!("Not valid geometry {:?}", geojson_geometry_collection);
544        }
545    }
546
547    #[test]
548    fn test_from_geo_type_to_geojson() {
549        let p1 = geo_types::Point::new(100.0f64, 0.0f64);
550        let actual = serde_json::Value::from(GeoJson::from(&p1));
551        let expected: serde_json::Value =
552            serde_json::json!({"coordinates": [100.0, 0.0], "type": "Point"});
553        assert_eq!(expected, actual);
554    }
555
556    #[test]
557    fn test_from_iter_geo_type_to_geojson() {
558        let p1 = geo_types::Point::new(100.0f64, 0.0f64);
559        let p2 = geo_types::Point::new(200.0f64, 0.0f64);
560        let points: Vec<_> = vec![p1, p2];
561
562        use std::iter::FromIterator;
563
564        let actual = GeoJson::from_iter(points.iter());
565        let actual2 = points.iter().collect::<GeoJson>();
566        assert_eq!(actual, actual2);
567
568        let expected: serde_json::Value = serde_json::json!({
569            "type": "GeometryCollection",
570            "geometries": [
571                {"coordinates": [100.0, 0.0], "type": "Point"},
572                {"coordinates": [200.0, 0.0], "type": "Point"},
573            ]
574        });
575        assert_eq!(expected, serde_json::Value::from(actual));
576    }
577}