geojson/
geometry.rs

1// Copyright 2015 The GeoRust Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//  http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::str::FromStr;
16use std::{convert::TryFrom, fmt};
17
18use crate::errors::{Error, Result};
19use crate::{util, Bbox, LineStringType, PointType, PolygonType};
20use crate::{JsonObject, JsonValue};
21use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
22
23/// The underlying value for a `Geometry`.
24///
25/// # Conversion from `geo_types`
26///
27/// A `Value` can be created by using the `From` impl which is available for both `geo_types`
28/// primitives AND `geo_types::Geometry` enum members:
29///
30/// ```rust
31/// # #[cfg(feature = "geo-types")]
32/// # fn test() {
33/// let point = geo_types::Point::new(2., 9.);
34/// let genum = geo_types::Geometry::from(point);
35/// assert_eq!(
36///     geojson::Value::from(&point),
37///     geojson::Value::Point(vec![2., 9.]),
38/// );
39/// assert_eq!(
40///     geojson::Value::from(&genum),
41///     geojson::Value::Point(vec![2., 9.]),
42/// );
43/// # }
44/// # #[cfg(not(feature = "geo-types"))]
45/// # fn test() {}
46/// # test()
47/// ```
48#[derive(Clone, Debug, PartialEq)]
49pub enum Value {
50    /// Point
51    ///
52    /// [GeoJSON Format Specification § 3.1.2](https://tools.ietf.org/html/rfc7946#section-3.1.2)
53    Point(PointType),
54
55    /// MultiPoint
56    ///
57    /// [GeoJSON Format Specification § 3.1.3](https://tools.ietf.org/html/rfc7946#section-3.1.3)
58    MultiPoint(Vec<PointType>),
59
60    /// LineString
61    ///
62    /// [GeoJSON Format Specification § 3.1.4](https://tools.ietf.org/html/rfc7946#section-3.1.4)
63    LineString(LineStringType),
64
65    /// MultiLineString
66    ///
67    /// [GeoJSON Format Specification § 3.1.5](https://tools.ietf.org/html/rfc7946#section-3.1.5)
68    MultiLineString(Vec<LineStringType>),
69
70    /// Polygon
71    ///
72    /// [GeoJSON Format Specification § 3.1.6](https://tools.ietf.org/html/rfc7946#section-3.1.6)
73    Polygon(PolygonType),
74
75    /// MultiPolygon
76    ///
77    /// [GeoJSON Format Specification § 3.1.7](https://tools.ietf.org/html/rfc7946#section-3.1.7)
78    MultiPolygon(Vec<PolygonType>),
79
80    /// GeometryCollection
81    ///
82    /// [GeoJSON Format Specification § 3.1.8](https://tools.ietf.org/html/rfc7946#section-3.1.8)
83    GeometryCollection(Vec<Geometry>),
84}
85
86impl Value {
87    pub fn type_name(&self) -> &'static str {
88        match self {
89            Value::Point(..) => "Point",
90            Value::MultiPoint(..) => "MultiPoint",
91            Value::LineString(..) => "LineString",
92            Value::MultiLineString(..) => "MultiLineString",
93            Value::Polygon(..) => "Polygon",
94            Value::MultiPolygon(..) => "MultiPolygon",
95            Value::GeometryCollection(..) => "GeometryCollection",
96        }
97    }
98}
99
100impl<'a> From<&'a Value> for JsonObject {
101    fn from(value: &'a Value) -> JsonObject {
102        let mut map = JsonObject::new();
103        map.insert(
104            String::from("type"),
105            // The unwrap() should never panic, because &str always serializes to JSON
106            ::serde_json::to_value(value.type_name()).unwrap(),
107        );
108        map.insert(
109            String::from(match value {
110                Value::GeometryCollection(..) => "geometries",
111                _ => "coordinates",
112            }),
113            // The unwrap() should never panic, because Value contains only JSON-serializable types
114            ::serde_json::to_value(value).unwrap(),
115        );
116        map
117    }
118}
119
120impl Value {
121    pub fn from_json_object(object: JsonObject) -> Result<Self> {
122        Self::try_from(object)
123    }
124
125    pub fn from_json_value(value: JsonValue) -> Result<Self> {
126        Self::try_from(value)
127    }
128
129    fn serialize_to_map<SM: SerializeMap>(
130        &self,
131        map: &mut SM,
132    ) -> std::result::Result<(), SM::Error> {
133        map.serialize_entry("type", self.type_name())?;
134        map.serialize_entry(
135            match self {
136                Value::GeometryCollection(..) => "geometries",
137                _ => "coordinates",
138            },
139            self,
140        )?;
141        Ok(())
142    }
143}
144
145impl TryFrom<JsonObject> for Value {
146    type Error = Error;
147
148    fn try_from(mut object: JsonObject) -> Result<Self> {
149        util::get_value(&mut object)
150    }
151}
152
153impl TryFrom<JsonValue> for Value {
154    type Error = Error;
155
156    fn try_from(value: JsonValue) -> Result<Self> {
157        if let JsonValue::Object(obj) = value {
158            Self::try_from(obj)
159        } else {
160            Err(Error::GeoJsonExpectedObject(value))
161        }
162    }
163}
164
165impl fmt::Display for Value {
166    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167        ::serde_json::to_string(&JsonObject::from(self))
168            .map_err(|_| fmt::Error)
169            .and_then(|s| f.write_str(&s))
170    }
171}
172
173impl<'a> From<&'a Value> for JsonValue {
174    fn from(value: &'a Value) -> JsonValue {
175        ::serde_json::to_value(value).unwrap()
176    }
177}
178
179impl Serialize for Value {
180    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
181    where
182        S: Serializer,
183    {
184        match self {
185            Value::Point(x) => x.serialize(serializer),
186            Value::MultiPoint(x) => x.serialize(serializer),
187            Value::LineString(x) => x.serialize(serializer),
188            Value::MultiLineString(x) => x.serialize(serializer),
189            Value::Polygon(x) => x.serialize(serializer),
190            Value::MultiPolygon(x) => x.serialize(serializer),
191            Value::GeometryCollection(x) => x.serialize(serializer),
192        }
193    }
194}
195
196/// Geometry Objects
197///
198/// [GeoJSON Format Specification § 3.1](https://tools.ietf.org/html/rfc7946#section-3.1)
199///
200/// ## Examples
201///
202/// Constructing a `Geometry`:
203///
204/// ```
205/// use geojson::{Geometry, Value};
206///
207/// let geometry = Geometry::new(Value::Point(vec![7.428959, 1.513394]));
208/// ```
209///
210/// Geometries can be created from `Value`s.
211/// ```
212/// # use geojson::{Geometry, Value};
213/// let geometry1: Geometry = Value::Point(vec![7.428959, 1.513394]).into();
214/// ```
215///
216/// Serializing a `Geometry` to a GeoJSON string:
217///
218/// ```
219/// use geojson::{GeoJson, Geometry, Value};
220/// use serde_json;
221///
222/// let geometry = Geometry::new(Value::Point(vec![7.428959, 1.513394]));
223///
224/// let geojson_string = geometry.to_string();
225///
226/// assert_eq!(
227///     "{\"type\":\"Point\",\"coordinates\":[7.428959,1.513394]}",
228///     geojson_string,
229/// );
230/// ```
231///
232/// Deserializing a GeoJSON string into a `Geometry`:
233///
234/// ```
235/// use geojson::{GeoJson, Geometry, Value};
236///
237/// let geojson_str = "{\"coordinates\":[7.428959,1.513394],\"type\":\"Point\"}";
238///
239/// let geometry = match geojson_str.parse::<GeoJson>() {
240///     Ok(GeoJson::Geometry(g)) => g,
241///     _ => return,
242/// };
243///
244/// assert_eq!(
245///     Geometry::new(Value::Point(vec![7.428959, 1.513394]),),
246///     geometry,
247/// );
248/// ```
249///
250/// Transforming a `Geometry` into a `geo_types::Geometry<f64>` (which requires the `geo-types`
251/// feature):
252///
253/// ```
254/// use geojson::{Geometry, Value};
255/// use std::convert::TryInto;
256///
257/// let geometry = Geometry::new(Value::Point(vec![7.428959, 1.513394]));
258/// # #[cfg(feature = "geo-types")]
259/// let geom: geo_types::Geometry<f64> = geometry.try_into().unwrap();
260/// ```
261#[derive(Clone, Debug, PartialEq)]
262pub struct Geometry {
263    /// Bounding Box
264    ///
265    /// [GeoJSON Format Specification § 5](https://tools.ietf.org/html/rfc7946#section-5)
266    pub bbox: Option<Bbox>,
267    pub value: Value,
268    /// Foreign Members
269    ///
270    /// [GeoJSON Format Specification § 6](https://tools.ietf.org/html/rfc7946#section-6)
271    pub foreign_members: Option<JsonObject>,
272}
273
274impl Geometry {
275    /// Returns a new `Geometry` with the specified `value`. `bbox` and `foreign_members` will be
276    /// set to `None`.
277    pub fn new(value: Value) -> Self {
278        Geometry {
279            bbox: None,
280            value,
281            foreign_members: None,
282        }
283    }
284}
285
286impl<'a> From<&'a Geometry> for JsonObject {
287    fn from(geometry: &'a Geometry) -> JsonObject {
288        let mut map = JsonObject::from(&geometry.value);
289        if let Some(ref bbox) = geometry.bbox {
290            map.insert(String::from("bbox"), ::serde_json::to_value(bbox).unwrap());
291        }
292
293        if let Some(ref foreign_members) = geometry.foreign_members {
294            for (key, value) in foreign_members {
295                map.insert(key.to_owned(), value.to_owned());
296            }
297        }
298        map
299    }
300}
301
302impl Geometry {
303    pub fn from_json_object(object: JsonObject) -> Result<Self> {
304        Self::try_from(object)
305    }
306
307    pub fn from_json_value(value: JsonValue) -> Result<Self> {
308        Self::try_from(value)
309    }
310
311    fn serialize_to_map<SM: SerializeMap>(
312        &self,
313        map: &mut SM,
314    ) -> std::result::Result<(), SM::Error> {
315        self.value.serialize_to_map(map)?;
316        if let Some(ref bbox) = self.bbox {
317            map.serialize_entry("bbox", bbox)?;
318        }
319
320        if let Some(ref foreign_members) = self.foreign_members {
321            for (key, value) in foreign_members {
322                map.serialize_entry(key, value)?
323            }
324        }
325        Ok(())
326    }
327}
328
329impl TryFrom<JsonObject> for Geometry {
330    type Error = Error;
331
332    fn try_from(mut object: JsonObject) -> Result<Self> {
333        let bbox = util::get_bbox(&mut object)?;
334        let value = util::get_value(&mut object)?;
335        let foreign_members = util::get_foreign_members(object)?;
336        Ok(Geometry {
337            bbox,
338            value,
339            foreign_members,
340        })
341    }
342}
343
344impl TryFrom<JsonValue> for Geometry {
345    type Error = Error;
346
347    fn try_from(value: JsonValue) -> Result<Self> {
348        if let JsonValue::Object(obj) = value {
349            Self::try_from(obj)
350        } else {
351            Err(Error::GeoJsonExpectedObject(value))
352        }
353    }
354}
355
356impl FromStr for Geometry {
357    type Err = Error;
358
359    fn from_str(s: &str) -> Result<Self> {
360        Self::try_from(crate::GeoJson::from_str(s)?)
361    }
362}
363
364impl Serialize for Geometry {
365    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
366    where
367        S: Serializer,
368    {
369        let mut map = serializer.serialize_map(None)?;
370        self.serialize_to_map(&mut map)?;
371        map.end()
372    }
373}
374
375impl<'de> Deserialize<'de> for Geometry {
376    fn deserialize<D>(deserializer: D) -> std::result::Result<Geometry, D::Error>
377    where
378        D: Deserializer<'de>,
379    {
380        use serde::de::Error as SerdeError;
381
382        let val = JsonObject::deserialize(deserializer)?;
383
384        Geometry::from_json_object(val).map_err(|e| D::Error::custom(e.to_string()))
385    }
386}
387
388impl<V> From<V> for Geometry
389where
390    V: Into<Value>,
391{
392    fn from(v: V) -> Geometry {
393        Geometry::new(v.into())
394    }
395}
396
397#[cfg(test)]
398mod tests {
399    use crate::{Error, GeoJson, Geometry, JsonObject, Value};
400    use serde_json::json;
401    use std::str::FromStr;
402
403    fn encode(geometry: &Geometry) -> String {
404        serde_json::to_string(&geometry).unwrap()
405    }
406    fn decode(json_string: String) -> GeoJson {
407        json_string.parse().unwrap()
408    }
409
410    #[test]
411    fn encode_decode_geometry() {
412        let geometry_json_str = "{\"type\":\"Point\",\"coordinates\":[1.1,2.1]}";
413        let geometry = Geometry {
414            value: Value::Point(vec![1.1, 2.1]),
415            bbox: None,
416            foreign_members: None,
417        };
418
419        // Test encode
420        let json_string = encode(&geometry);
421        assert_eq!(json_string, geometry_json_str);
422
423        // Test decode
424        let decoded_geometry = match decode(json_string) {
425            GeoJson::Geometry(g) => g,
426            _ => unreachable!(),
427        };
428        assert_eq!(decoded_geometry, geometry);
429    }
430
431    #[test]
432    fn test_geometry_from_value() {
433        use serde_json::json;
434        use std::convert::TryInto;
435
436        let json_value = json!({
437            "type": "Point",
438            "coordinates": [
439                0.0, 0.1
440            ],
441        });
442        assert!(json_value.is_object());
443
444        let geometry: Geometry = json_value.try_into().unwrap();
445        assert_eq!(
446            geometry,
447            Geometry {
448                value: Value::Point(vec![0.0, 0.1]),
449                bbox: None,
450                foreign_members: None,
451            }
452        )
453    }
454
455    #[test]
456    fn test_geometry_display() {
457        let v = Value::LineString(vec![vec![0.0, 0.1], vec![0.1, 0.2], vec![0.2, 0.3]]);
458        let geometry = Geometry::new(v);
459        assert_eq!(
460            geometry.to_string(),
461            "{\"type\":\"LineString\",\"coordinates\":[[0.0,0.1],[0.1,0.2],[0.2,0.3]]}"
462        );
463    }
464
465    #[test]
466    fn test_value_display() {
467        let v = Value::LineString(vec![vec![0.0, 0.1], vec![0.1, 0.2], vec![0.2, 0.3]]);
468        assert_eq!(
469            "{\"coordinates\":[[0.0,0.1],[0.1,0.2],[0.2,0.3]],\"type\":\"LineString\"}",
470            v.to_string()
471        );
472    }
473
474    #[test]
475    fn encode_decode_geometry_with_foreign_member() {
476        let geometry_json_str =
477            "{\"type\":\"Point\",\"coordinates\":[1.1,2.1],\"other_member\":true}";
478        let mut foreign_members = JsonObject::new();
479        foreign_members.insert(
480            String::from("other_member"),
481            serde_json::to_value(true).unwrap(),
482        );
483        let geometry = Geometry {
484            value: Value::Point(vec![1.1, 2.1]),
485            bbox: None,
486            foreign_members: Some(foreign_members),
487        };
488
489        // Test encode
490        let json_string = encode(&geometry);
491        assert_eq!(json_string, geometry_json_str);
492
493        // Test decode
494        let decoded_geometry = match decode(geometry_json_str.into()) {
495            GeoJson::Geometry(g) => g,
496            _ => unreachable!(),
497        };
498        assert_eq!(decoded_geometry, geometry);
499    }
500
501    #[test]
502    fn encode_decode_geometry_collection() {
503        let geometry_collection = Geometry {
504            bbox: None,
505            value: Value::GeometryCollection(vec![
506                Geometry {
507                    bbox: None,
508                    value: Value::Point(vec![100.0, 0.0]),
509                    foreign_members: None,
510                },
511                Geometry {
512                    bbox: None,
513                    value: Value::LineString(vec![vec![101.0, 0.0], vec![102.0, 1.0]]),
514                    foreign_members: None,
515                },
516            ]),
517            foreign_members: None,
518        };
519
520        let geometry_collection_string = "{\"type\":\"GeometryCollection\",\"geometries\":[{\"type\":\"Point\",\"coordinates\":[100.0,0.0]},{\"type\":\"LineString\",\"coordinates\":[[101.0,0.0],[102.0,1.0]]}]}";
521        // Test encode
522        let json_string = encode(&geometry_collection);
523        assert_eq!(json_string, geometry_collection_string);
524
525        // Test decode
526        let decoded_geometry = match decode(geometry_collection_string.into()) {
527            GeoJson::Geometry(g) => g,
528            _ => unreachable!(),
529        };
530        assert_eq!(decoded_geometry, geometry_collection);
531    }
532
533    #[test]
534    fn test_from_str_ok() {
535        let geometry_json = json!({
536            "type": "Point",
537            "coordinates": [125.6f64, 10.1]
538        })
539        .to_string();
540
541        let geometry = Geometry::from_str(&geometry_json).unwrap();
542        assert!(matches!(geometry.value, Value::Point(_)));
543    }
544
545    #[test]
546    fn test_from_str_with_unexpected_type() {
547        let feature_json = json!({
548            "type": "Feature",
549            "geometry": {
550                "type": "Point",
551                "coordinates": [125.6, 10.1]
552            },
553            "properties": {
554                "name": "Dinagat Islands"
555            }
556        })
557        .to_string();
558
559        let actual_failure = Geometry::from_str(&feature_json).unwrap_err();
560        match actual_failure {
561            Error::ExpectedType { actual, expected } => {
562                assert_eq!(actual, "Feature");
563                assert_eq!(expected, "Geometry");
564            }
565            e => panic!("unexpected error: {}", e),
566        };
567    }
568
569    #[test]
570    fn test_reject_too_few_coordinates() {
571        let err = Geometry::from_str(r#"{"type": "Point", "coordinates": []}"#).unwrap_err();
572        assert_eq!(
573            err.to_string(),
574            "A position must contain two or more elements, but got `0`"
575        );
576
577        let err = Geometry::from_str(r#"{"type": "Point", "coordinates": [23.42]}"#).unwrap_err();
578        assert_eq!(
579            err.to_string(),
580            "A position must contain two or more elements, but got `1`"
581        );
582    }
583}