use crate::errors::Error;
use crate::json::{self, Deserialize, Deserializer, JsonObject, JsonValue, Serialize, Serializer};
use crate::serde;
use crate::{Feature, FeatureCollection, Geometry};
use std::convert::TryFrom;
use std::fmt;
use std::iter::FromIterator;
use std::str::FromStr;
#[derive(Clone, Debug, PartialEq)]
pub enum GeoJson {
Geometry(Geometry),
Feature(Feature),
FeatureCollection(FeatureCollection),
}
impl<'a> From<&'a GeoJson> for JsonObject {
fn from(geojson: &'a GeoJson) -> JsonObject {
match *geojson {
GeoJson::Geometry(ref geometry) => geometry.into(),
GeoJson::Feature(ref feature) => feature.into(),
GeoJson::FeatureCollection(ref fc) => fc.into(),
}
}
}
impl From<GeoJson> for JsonValue {
fn from(geojson: GeoJson) -> JsonValue {
match geojson {
GeoJson::Geometry(geometry) => JsonValue::Object(JsonObject::from(&geometry)),
GeoJson::Feature(feature) => JsonValue::Object(JsonObject::from(&feature)),
GeoJson::FeatureCollection(fc) => JsonValue::Object(JsonObject::from(&fc)),
}
}
}
impl<G: Into<Geometry>> From<G> for GeoJson {
fn from(geometry: G) -> Self {
GeoJson::Geometry(geometry.into())
}
}
impl<G: Into<Geometry>> FromIterator<G> for GeoJson {
fn from_iter<I: IntoIterator<Item = G>>(iter: I) -> Self {
use crate::Value;
let geometries = iter.into_iter().map(|g| g.into()).collect();
let collection = Value::GeometryCollection(geometries);
GeoJson::Geometry(Geometry::new(collection))
}
}
impl From<Feature> for GeoJson {
fn from(feature: Feature) -> Self {
GeoJson::Feature(feature)
}
}
impl From<FeatureCollection> for GeoJson {
fn from(feature_collection: FeatureCollection) -> GeoJson {
GeoJson::FeatureCollection(feature_collection)
}
}
impl TryFrom<GeoJson> for Geometry {
type Error = Error;
fn try_from(value: GeoJson) -> Result<Self, Self::Error> {
match value {
GeoJson::Geometry(g) => Ok(g),
GeoJson::Feature(_) => Err(Error::ExpectedType {
expected: "Geometry".to_string(),
actual: "Feature".to_string(),
}),
GeoJson::FeatureCollection(_) => Err(Error::ExpectedType {
expected: "Geometry".to_string(),
actual: "FeatureCollection".to_string(),
}),
}
}
}
impl TryFrom<GeoJson> for Feature {
type Error = Error;
fn try_from(value: GeoJson) -> Result<Self, Self::Error> {
match value {
GeoJson::Geometry(_) => Err(Error::ExpectedType {
expected: "Feature".to_string(),
actual: "Geometry".to_string(),
}),
GeoJson::Feature(f) => Ok(f),
GeoJson::FeatureCollection(_) => Err(Error::ExpectedType {
expected: "Feature".to_string(),
actual: "FeatureCollection".to_string(),
}),
}
}
}
impl TryFrom<GeoJson> for FeatureCollection {
type Error = Error;
fn try_from(value: GeoJson) -> Result<Self, Self::Error> {
match value {
GeoJson::Geometry(_) => Err(Error::ExpectedType {
expected: "FeatureCollection".to_string(),
actual: "Geometry".to_string(),
}),
GeoJson::Feature(_) => Err(Error::ExpectedType {
expected: "FeatureCollection".to_string(),
actual: "Feature".to_string(),
}),
GeoJson::FeatureCollection(f) => Ok(f),
}
}
}
impl GeoJson {
pub fn from_json_object(object: JsonObject) -> Result<Self, Error> {
Self::try_from(object)
}
pub fn from_json_value(value: JsonValue) -> Result<Self, Error> {
Self::try_from(value)
}
pub fn to_json_value(self) -> JsonValue {
JsonValue::from(self)
}
pub fn from_reader<R>(rdr: R) -> Result<Self, serde_json::Error>
where
R: std::io::Read,
{
serde_json::from_reader(rdr)
}
}
impl TryFrom<JsonObject> for GeoJson {
type Error = Error;
fn try_from(object: JsonObject) -> Result<Self, Self::Error> {
let type_ = match object.get("type") {
Some(json::JsonValue::String(t)) => Type::from_str(t),
_ => return Err(Error::GeometryUnknownType("type".to_owned())),
};
let type_ = type_.ok_or(Error::EmptyType)?;
match type_ {
Type::Feature => Feature::try_from(object).map(GeoJson::Feature),
Type::FeatureCollection => {
FeatureCollection::try_from(object).map(GeoJson::FeatureCollection)
}
_ => Geometry::try_from(object).map(GeoJson::Geometry),
}
}
}
impl TryFrom<JsonValue> for GeoJson {
type Error = Error;
fn try_from(value: JsonValue) -> Result<Self, Self::Error> {
if let JsonValue::Object(obj) = value {
Self::try_from(obj)
} else {
Err(Error::GeoJsonExpectedObject(value))
}
}
}
#[derive(PartialEq, Clone, Copy)]
enum Type {
Point,
MultiPoint,
LineString,
MultiLineString,
Polygon,
MultiPolygon,
GeometryCollection,
Feature,
FeatureCollection,
}
impl Type {
fn from_str(s: &str) -> Option<Self> {
match s {
"Point" => Some(Type::Point),
"MultiPoint" => Some(Type::MultiPoint),
"LineString" => Some(Type::LineString),
"MultiLineString" => Some(Type::MultiLineString),
"Polygon" => Some(Type::Polygon),
"MultiPolygon" => Some(Type::MultiPolygon),
"GeometryCollection" => Some(Type::GeometryCollection),
"Feature" => Some(Type::Feature),
"FeatureCollection" => Some(Type::FeatureCollection),
_ => None,
}
}
}
impl Serialize for GeoJson {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
JsonObject::from(self).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for GeoJson {
fn deserialize<D>(deserializer: D) -> Result<GeoJson, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error as SerdeError;
let val = JsonObject::deserialize(deserializer)?;
GeoJson::from_json_object(val).map_err(|e| D::Error::custom(e.to_string()))
}
}
impl FromStr for GeoJson {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let object = get_object(s)?;
GeoJson::from_json_object(object)
}
}
fn get_object(s: &str) -> Result<json::JsonObject, Error> {
match ::serde_json::from_str(s) {
Ok(json::JsonValue::Object(object)) => Ok(object),
Ok(other) => Err(Error::ExpectedObjectValue(other)),
Err(serde_error) => Err(Error::MalformedJson(serde_error)),
}
}
impl fmt::Display for GeoJson {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
::serde_json::to_string(self)
.map_err(|_| fmt::Error)
.and_then(|s| f.write_str(&s))
}
}
impl fmt::Display for Feature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
::serde_json::to_string(self)
.map_err(|_| fmt::Error)
.and_then(|s| f.write_str(&s))
}
}
impl fmt::Display for Geometry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
::serde_json::to_string(self)
.map_err(|_| fmt::Error)
.and_then(|s| f.write_str(&s))
}
}
impl fmt::Display for FeatureCollection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
::serde_json::to_string(self)
.map_err(|_| fmt::Error)
.and_then(|s| f.write_str(&s))
}
}
#[cfg(test)]
mod tests {
use crate::{Error, Feature, GeoJson, Geometry, Value};
use serde_json::json;
use std::convert::TryInto;
use std::str::FromStr;
#[test]
fn test_geojson_from_reader() {
let json_str = r#"{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [102.0, 0.5]
},
"properties": null
}"#;
let g1 = GeoJson::from_reader(json_str.as_bytes()).unwrap();
let json_value = json!({
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [102.0, 0.5]
},
"properties": null,
});
let g2: GeoJson = json_value.try_into().unwrap();
assert_eq!(g1, g2);
}
#[test]
fn test_geojson_from_value() {
let json_value = json!({
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [102.0, 0.5]
},
"properties": null,
});
assert!(json_value.is_object());
let geojson: GeoJson = json_value.try_into().unwrap();
assert_eq!(
geojson,
GeoJson::Feature(Feature {
bbox: None,
geometry: Some(Geometry::new(Value::Point(vec![102.0, 0.5]))),
id: None,
properties: None,
foreign_members: None,
})
);
}
#[test]
fn test_invalid_json() {
let geojson_str = r#"{
"type": "FeatureCollection",
"features": [
!INTENTIONAL_TYPO! {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-0.13583511114120483,
51.5218870403801
]
}
}
]
}"#;
assert!(matches!(
GeoJson::from_str(geojson_str),
Err(Error::MalformedJson(_))
))
}
}