use crate::json::{Deserialize, Deserializer, JsonObject, JsonValue, Serialize, Serializer};
use crate::serde;
use crate::{util, Bbox, Error, LineStringType, PointType, PolygonType};
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Point(PointType),
MultiPoint(Vec<PointType>),
LineString(LineStringType),
MultiLineString(Vec<LineStringType>),
Polygon(PolygonType),
MultiPolygon(Vec<PolygonType>),
GeometryCollection(Vec<Geometry>),
}
impl<'a> From<&'a Value> for JsonValue {
fn from(value: &'a Value) -> JsonValue {
match *value {
Value::Point(ref x) => ::serde_json::to_value(x),
Value::MultiPoint(ref x) => ::serde_json::to_value(x),
Value::LineString(ref x) => ::serde_json::to_value(x),
Value::MultiLineString(ref x) => ::serde_json::to_value(x),
Value::Polygon(ref x) => ::serde_json::to_value(x),
Value::MultiPolygon(ref x) => ::serde_json::to_value(x),
Value::GeometryCollection(ref x) => ::serde_json::to_value(x),
}
.unwrap()
}
}
impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
JsonValue::from(self).serialize(serializer)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Geometry {
pub bbox: Option<Bbox>,
pub value: Value,
pub foreign_members: Option<JsonObject>,
}
impl Geometry {
pub fn new(value: Value) -> Self {
Geometry {
bbox: None,
value,
foreign_members: None,
}
}
}
impl<'a> From<&'a Geometry> for JsonObject {
fn from(geometry: &'a Geometry) -> JsonObject {
let mut map = JsonObject::new();
if let Some(ref bbox) = geometry.bbox {
map.insert(String::from("bbox"), ::serde_json::to_value(bbox).unwrap());
}
let ty = String::from(match geometry.value {
Value::Point(..) => "Point",
Value::MultiPoint(..) => "MultiPoint",
Value::LineString(..) => "LineString",
Value::MultiLineString(..) => "MultiLineString",
Value::Polygon(..) => "Polygon",
Value::MultiPolygon(..) => "MultiPolygon",
Value::GeometryCollection(..) => "GeometryCollection",
});
map.insert(String::from("type"), ::serde_json::to_value(&ty).unwrap());
map.insert(
String::from(match geometry.value {
Value::GeometryCollection(..) => "geometries",
_ => "coordinates",
}),
::serde_json::to_value(&geometry.value).unwrap(),
);
if let Some(ref foreign_members) = geometry.foreign_members {
for (key, value) in foreign_members {
map.insert(key.to_owned(), value.to_owned());
}
}
map
}
}
impl Geometry {
pub fn from_json_object(mut object: JsonObject) -> Result<Self, Error> {
let value = match &*util::expect_type(&mut object)? {
"Point" => Value::Point(util::get_coords_one_pos(&mut object)?),
"MultiPoint" => Value::MultiPoint(util::get_coords_1d_pos(&mut object)?),
"LineString" => Value::LineString(util::get_coords_1d_pos(&mut object)?),
"MultiLineString" => Value::MultiLineString(util::get_coords_2d_pos(&mut object)?),
"Polygon" => Value::Polygon(util::get_coords_2d_pos(&mut object)?),
"MultiPolygon" => Value::MultiPolygon(util::get_coords_3d_pos(&mut object)?),
"GeometryCollection" => Value::GeometryCollection(util::get_geometries(&mut object)?),
_ => return Err(Error::GeometryUnknownType),
};
let bbox = util::get_bbox(&mut object)?;
let foreign_members = util::get_foreign_members(object)?;
Ok(Geometry {
bbox,
value,
foreign_members,
})
}
}
impl Serialize for Geometry {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
JsonObject::from(self).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Geometry {
fn deserialize<D>(deserializer: D) -> Result<Geometry, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error as SerdeError;
use std::error::Error as StdError;
let val = JsonObject::deserialize(deserializer)?;
Geometry::from_json_object(val).map_err(|e| D::Error::custom(e.description()))
}
}
#[cfg(test)]
mod tests {
use crate::json::JsonObject;
use crate::{GeoJson, Geometry, Value};
fn encode(geometry: &Geometry) -> String {
serde_json::to_string(&geometry).unwrap()
}
fn decode(json_string: String) -> GeoJson {
json_string.parse().unwrap()
}
#[test]
fn encode_decode_geometry() {
let geometry_json_str = "{\"coordinates\":[1.1,2.1],\"type\":\"Point\"}";
let geometry = Geometry {
value: Value::Point(vec![1.1, 2.1]),
bbox: None,
foreign_members: None,
};
let json_string = encode(&geometry);
assert_eq!(json_string, geometry_json_str);
let decoded_geometry = match decode(json_string) {
GeoJson::Geometry(g) => g,
_ => unreachable!(),
};
assert_eq!(decoded_geometry, geometry);
}
#[test]
fn test_geometry_display() {
let v = Value::LineString(vec![vec![0.0, 0.1], vec![0.1, 0.2], vec![0.2, 0.3]]);
let geometry = Geometry::new(v);
assert_eq!(
"{\"coordinates\":[[0.0,0.1],[0.1,0.2],[0.2,0.3]],\"type\":\"LineString\"}",
geometry.to_string()
);
}
#[test]
fn encode_decode_geometry_with_foreign_member() {
let geometry_json_str =
"{\"coordinates\":[1.1,2.1],\"other_member\":true,\"type\":\"Point\"}";
let mut foreign_members = JsonObject::new();
foreign_members.insert(
String::from("other_member"),
serde_json::to_value(true).unwrap(),
);
let geometry = Geometry {
value: Value::Point(vec![1.1, 2.1]),
bbox: None,
foreign_members: Some(foreign_members),
};
let json_string = encode(&geometry);
assert_eq!(json_string, geometry_json_str);
let decoded_geometry = match decode(geometry_json_str.into()) {
GeoJson::Geometry(g) => g,
_ => unreachable!(),
};
assert_eq!(decoded_geometry, geometry);
}
#[test]
fn encode_decode_geometry_collection() {
let geometry_collection = Geometry {
bbox: None,
value: Value::GeometryCollection(vec![
Geometry {
bbox: None,
value: Value::Point(vec![100.0, 0.0]),
foreign_members: None,
},
Geometry {
bbox: None,
value: Value::LineString(vec![vec![101.0, 0.0], vec![102.0, 1.0]]),
foreign_members: None,
},
]),
foreign_members: None,
};
let geometry_collection_string = "{\"geometries\":[{\"coordinates\":[100.0,0.0],\"type\":\"Point\"},{\"coordinates\":[[101.0,0.0],[102.0,1.0]],\"type\":\"LineString\"}],\"type\":\"GeometryCollection\"}";
let json_string = encode(&geometry_collection);
assert_eq!(json_string, geometry_collection_string);
let decoded_geometry = match decode(geometry_collection_string.into()) {
GeoJson::Geometry(g) => g,
_ => unreachable!(),
};
assert_eq!(decoded_geometry, geometry_collection);
}
}