1use crate::errors::{Error, Result};
16use crate::{Feature, FeatureCollection, Geometry};
17use serde::{Deserialize, Serialize};
18use std::convert::TryFrom;
19use std::fmt;
20use std::iter::FromIterator;
21use std::str::FromStr;
22
23#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
80#[serde(untagged, try_from = "deserialize::RawGeoJson")]
81pub enum GeoJson {
82 Geometry(Geometry),
83 Feature(Feature),
84 FeatureCollection(FeatureCollection),
85}
86
87impl<G: Into<Geometry>> From<G> for GeoJson {
88 fn from(geometry: G) -> Self {
89 GeoJson::Geometry(geometry.into())
90 }
91}
92
93impl<G: Into<Geometry>> FromIterator<G> for GeoJson {
94 fn from_iter<I: IntoIterator<Item = G>>(iter: I) -> Self {
95 let geometry_collection = Geometry::new_geometry_collection(iter);
96 GeoJson::Geometry(geometry_collection)
97 }
98}
99
100impl From<Feature> for GeoJson {
101 fn from(feature: Feature) -> Self {
102 GeoJson::Feature(feature)
103 }
104}
105
106impl From<FeatureCollection> for GeoJson {
107 fn from(feature_collection: FeatureCollection) -> GeoJson {
108 GeoJson::FeatureCollection(feature_collection)
109 }
110}
111
112impl From<Vec<Feature>> for GeoJson {
113 fn from(features: Vec<Feature>) -> GeoJson {
114 GeoJson::from(features.into_iter().collect::<FeatureCollection>())
115 }
116}
117
118impl TryFrom<GeoJson> for Geometry {
119 type Error = Error;
120 fn try_from(value: GeoJson) -> Result<Self> {
121 match value {
122 GeoJson::Geometry(g) => Ok(g),
123 GeoJson::Feature(_) => Err(Error::ExpectedType {
124 expected: "Geometry".to_string(),
125 actual: "Feature".to_string(),
126 }),
127 GeoJson::FeatureCollection(_) => Err(Error::ExpectedType {
128 expected: "Geometry".to_string(),
129 actual: "FeatureCollection".to_string(),
130 }),
131 }
132 }
133}
134
135impl TryFrom<GeoJson> for Feature {
136 type Error = Error;
137 fn try_from(value: GeoJson) -> Result<Self> {
138 match value {
139 GeoJson::Geometry(_) => Err(Error::ExpectedType {
140 expected: "Feature".to_string(),
141 actual: "Geometry".to_string(),
142 }),
143 GeoJson::Feature(f) => Ok(f),
144 GeoJson::FeatureCollection(_) => Err(Error::ExpectedType {
145 expected: "Feature".to_string(),
146 actual: "FeatureCollection".to_string(),
147 }),
148 }
149 }
150}
151
152impl TryFrom<GeoJson> for FeatureCollection {
153 type Error = Error;
154 fn try_from(value: GeoJson) -> Result<Self> {
155 match value {
156 GeoJson::Geometry(_) => Err(Error::ExpectedType {
157 expected: "FeatureCollection".to_string(),
158 actual: "Geometry".to_string(),
159 }),
160 GeoJson::Feature(_) => Err(Error::ExpectedType {
161 expected: "FeatureCollection".to_string(),
162 actual: "Feature".to_string(),
163 }),
164 GeoJson::FeatureCollection(f) => Ok(f),
165 }
166 }
167}
168
169impl GeoJson {
170 pub fn from_reader<R>(rdr: R) -> serde_json::Result<Self>
172 where
173 R: std::io::Read,
174 {
175 serde_json::from_reader(rdr)
176 }
177
178 pub fn to_string_pretty(self) -> Result<String> {
180 ::serde_json::to_string_pretty(&self)
181 .map_err(Error::MalformedGeoJson)
182 .map(|s| s.to_string())
183 }
184}
185
186impl FromStr for GeoJson {
216 type Err = Error;
217
218 fn from_str(s: &str) -> Result<Self> {
219 Ok(serde_json::from_str(s)?)
220 }
221}
222
223impl fmt::Display for GeoJson {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 ::serde_json::to_string(self)
226 .map_err(|_| fmt::Error)
227 .and_then(|s| f.write_str(&s))
228 }
229}
230
231impl fmt::Display for Feature {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 ::serde_json::to_string(self)
234 .map_err(|_| fmt::Error)
235 .and_then(|s| f.write_str(&s))
236 }
237}
238
239impl fmt::Display for Geometry {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 ::serde_json::to_string(self)
242 .map_err(|_| fmt::Error)
243 .and_then(|s| f.write_str(&s))
244 }
245}
246
247impl fmt::Display for FeatureCollection {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 ::serde_json::to_string(self)
250 .map_err(|_| fmt::Error)
251 .and_then(|s| f.write_str(&s))
252 }
253}
254
255mod deserialize {
256 use crate::geometry::deserialize::{Coordinates, GeometryType, RawGeometry};
257 use crate::util::normalize_foreign_members;
258 use crate::{Bbox, Error, Feature, FeatureCollection, GeoJson, Geometry, JsonObject, feature};
259 use serde::Deserialize;
260 use std::convert::TryFrom;
261
262 #[derive(Debug, Clone, PartialEq, Deserialize)]
263 enum GeoJsonType {
264 Feature,
265 FeatureCollection,
266 Point,
267 LineString,
268 Polygon,
269 MultiPoint,
270 MultiLineString,
271 MultiPolygon,
272 GeometryCollection,
273 }
274
275 impl GeoJsonType {
276 fn as_geometry_type(&self) -> Option<GeometryType> {
278 match self {
279 GeoJsonType::Point => Some(GeometryType::Point),
280 GeoJsonType::LineString => Some(GeometryType::LineString),
281 GeoJsonType::Polygon => Some(GeometryType::Polygon),
282 GeoJsonType::MultiPoint => Some(GeometryType::MultiPoint),
283 GeoJsonType::MultiLineString => Some(GeometryType::MultiLineString),
284 GeoJsonType::MultiPolygon => Some(GeometryType::MultiPolygon),
285 GeoJsonType::GeometryCollection => Some(GeometryType::GeometryCollection),
286 GeoJsonType::Feature | GeoJsonType::FeatureCollection => None,
287 }
288 }
289 }
290
291 #[derive(Debug, Clone, Deserialize)]
294 #[serde(expecting = "GeoJson object")]
295 pub(crate) struct RawGeoJson {
296 r#type: GeoJsonType,
297
298 bbox: Option<Bbox>,
300
301 coordinates: Option<Coordinates>,
303
304 geometries: Option<Vec<Geometry>>,
306
307 features: Option<Vec<Feature>>,
309
310 id: Option<feature::Id>,
312 geometry: Option<Geometry>,
313 properties: Option<JsonObject>,
314
315 #[serde(flatten)]
317 foreign_members: Option<JsonObject>,
318 }
319
320 impl TryFrom<RawGeoJson> for GeoJson {
321 type Error = Error;
322
323 fn try_from(mut raw: RawGeoJson) -> crate::Result<Self> {
324 normalize_foreign_members(&mut raw.foreign_members);
325
326 match raw.r#type {
327 GeoJsonType::FeatureCollection => {
328 let features = raw.features.ok_or_else(|| {
329 use serde::de::Error as _;
330 Error::MalformedGeoJson(serde_json::Error::missing_field("features"))
331 })?;
332 Ok(GeoJson::FeatureCollection(FeatureCollection {
333 bbox: raw.bbox,
334 features,
335 foreign_members: raw.foreign_members,
336 }))
337 }
338
339 GeoJsonType::Feature => Ok(GeoJson::Feature(Feature {
340 bbox: raw.bbox,
341 geometry: raw.geometry,
342 id: raw.id,
343 properties: raw.properties,
344 foreign_members: raw.foreign_members,
345 })),
346
347 geojson_type => {
349 let geometry_type = geojson_type.as_geometry_type().expect(
350 "as_geometry_type returns Some for all variants except Feature/FeatureCollection",
351 );
352 let raw_geom = RawGeometry {
353 r#type: geometry_type,
354 coordinates: raw.coordinates,
355 geometries: raw.geometries,
356 bbox: raw.bbox,
357 foreign_members: raw.foreign_members,
358 };
359 Ok(GeoJson::Geometry(Geometry::try_from(raw_geom)?))
360 }
361 }
362 }
363 }
364}
365
366#[cfg(test)]
367mod tests {
368 use crate::{Error, Feature, FeatureCollection, GeoJson, Geometry};
369 use serde_json::json;
370 use std::str::FromStr;
371
372 #[test]
373 fn test_geojson_from_reader() {
374 let json_str = r#"{
375 "type": "Feature",
376 "geometry": {
377 "type": "Point",
378 "coordinates": [102.0, 0.5]
379 },
380 "properties": null
381 }"#;
382
383 let actual = GeoJson::from_reader(json_str.as_bytes()).unwrap();
384 let actual_as_json = serde_json::to_value(actual).unwrap();
385 let expected = json!({
386 "type": "Feature",
387 "geometry": {
388 "type": "Point",
389 "coordinates": [102.0, 0.5]
390 },
391 "properties": null
392 });
393 assert_eq!(actual_as_json, expected);
394 }
395
396 #[test]
397 fn test_geojson_from_features() {
398 let features: Vec<Feature> = vec![
399 Geometry::new_point([0., 0., 0.]).into(),
400 Geometry::new_point([1., 1., 1.]).into(),
401 ];
402
403 let geojson: GeoJson = features.into();
404 assert_eq!(
405 geojson,
406 GeoJson::FeatureCollection(FeatureCollection {
407 features: vec![
408 Feature {
409 bbox: None,
410 geometry: Some(Geometry::new_point([0., 0., 0.])),
411 id: None,
412 properties: None,
413 foreign_members: None,
414 },
415 Feature {
416 bbox: None,
417 geometry: Some(Geometry::new_point([1., 1., 1.])),
418 id: None,
419 properties: None,
420 foreign_members: None,
421 },
422 ],
423 bbox: None,
424 foreign_members: None,
425 })
426 );
427 }
428
429 #[test]
430 fn test_missing_properties_key() {
431 let json_str = json!({
432 "type": "Feature",
433 "geometry": {
434 "type": "Point",
435 "coordinates": [102.0, 0.5]
436 },
437 })
438 .to_string();
439
440 let geojson = GeoJson::from_str(&json_str).unwrap();
441 assert_eq!(
442 geojson,
443 GeoJson::Feature(Feature {
444 bbox: None,
445 geometry: Some(Geometry::new_point([102.0, 0.5])),
446 id: None,
447 properties: None,
448 foreign_members: None,
449 })
450 );
451 }
452
453 #[test]
454 fn test_invalid_json() {
455 let geojson_str = r#"{
456 "type": "FeatureCollection",
457 "features": [
458 !INTENTIONAL_TYPO! {
459 "type": "Feature",
460 "properties": {},
461 "geometry": {
462 "type": "Point",
463 "coordinates": [
464 -0.13583511114120483,
465 51.5218870403801
466 ]
467 }
468 }
469 ]
470 }"#;
471 assert!(matches!(
472 GeoJson::from_str(geojson_str),
473 Err(Error::MalformedGeoJson(_))
474 ))
475 }
476
477 #[test]
478 fn geojson_with_invalid_type() {
479 let geojson_str = json!({
480 "type": "TYPO-FeatureCollection",
481 "features": []
482 })
483 .to_string();
484
485 let err = GeoJson::from_str(&geojson_str).unwrap_err();
486 assert!(matches!(err, Error::MalformedGeoJson(_)));
487 }
488
489 #[test]
490 fn geojson_with_missing_type() {
491 let geojson_str = json!({
492 "features": []
493 })
494 .to_string();
495
496 let err = GeoJson::from_str(&geojson_str).unwrap_err();
497 assert!(matches!(err, Error::MalformedGeoJson(_)));
498 }
499
500 #[test]
501 fn not_an_object() {
502 let geojson_str = "[]";
503 let err = GeoJson::from_str(geojson_str).unwrap_err();
504 assert!(matches!(err, Error::MalformedGeoJson(_)));
505 assert!(format!("{err}").contains("expected GeoJson object"));
506 }
507}