1use serde::ser::SerializeMap;
16use std::convert::TryFrom;
17use std::iter::FromIterator;
18use std::str::FromStr;
19
20use crate::errors::{Error, Result};
21use crate::{util, Bbox, Feature};
22use crate::{JsonObject, JsonValue};
23use serde::{Deserialize, Deserializer, Serialize, Serializer};
24
25#[derive(Clone, Debug, Default, PartialEq)]
65pub struct FeatureCollection {
66 pub bbox: Option<Bbox>,
70 pub features: Vec<Feature>,
71 pub foreign_members: Option<JsonObject>,
75}
76
77impl IntoIterator for FeatureCollection {
78 type Item = Feature;
79 type IntoIter = std::vec::IntoIter<Feature>;
80
81 fn into_iter(self) -> Self::IntoIter {
82 self.features.into_iter()
83 }
84}
85
86impl<'a> IntoIterator for &'a FeatureCollection {
87 type Item = &'a Feature;
88 type IntoIter = std::slice::Iter<'a, Feature>;
89
90 fn into_iter(self) -> Self::IntoIter {
91 IntoIterator::into_iter(&self.features)
92 }
93}
94
95impl<'a> From<&'a FeatureCollection> for JsonObject {
96 fn from(fc: &'a FeatureCollection) -> JsonObject {
97 match serde_json::to_value(fc).unwrap() {
99 serde_json::Value::Object(obj) => obj,
100 value => {
101 panic!("serializing FeatureCollection should result in an Object, but got something {:?}", value)
104 }
105 }
106 }
107}
108
109impl FeatureCollection {
110 pub fn from_json_object(object: JsonObject) -> Result<Self> {
111 Self::try_from(object)
112 }
113
114 pub fn from_json_value(value: JsonValue) -> Result<Self> {
115 Self::try_from(value)
116 }
117}
118
119impl TryFrom<JsonObject> for FeatureCollection {
120 type Error = Error;
121
122 fn try_from(mut object: JsonObject) -> Result<Self> {
123 match util::expect_type(&mut object)? {
124 ref type_ if type_ == "FeatureCollection" => Ok(FeatureCollection {
125 bbox: util::get_bbox(&mut object)?,
126 features: util::get_features(&mut object)?,
127 foreign_members: util::get_foreign_members(object)?,
128 }),
129 type_ => Err(Error::ExpectedType {
130 expected: "FeatureCollection".to_owned(),
131 actual: type_,
132 }),
133 }
134 }
135}
136
137impl TryFrom<JsonValue> for FeatureCollection {
138 type Error = Error;
139
140 fn try_from(value: JsonValue) -> Result<Self> {
141 if let JsonValue::Object(obj) = value {
142 Self::try_from(obj)
143 } else {
144 Err(Error::GeoJsonExpectedObject(value))
145 }
146 }
147}
148
149impl FromStr for FeatureCollection {
150 type Err = Error;
151
152 fn from_str(s: &str) -> Result<Self> {
153 Self::try_from(crate::GeoJson::from_str(s)?)
154 }
155}
156
157impl Serialize for FeatureCollection {
158 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
159 where
160 S: Serializer,
161 {
162 let mut map = serializer.serialize_map(None)?;
163 map.serialize_entry("type", "FeatureCollection")?;
164 map.serialize_entry("features", &self.features)?;
165
166 if let Some(ref bbox) = self.bbox {
167 map.serialize_entry("bbox", bbox)?;
168 }
169
170 if let Some(ref foreign_members) = self.foreign_members {
171 for (key, value) in foreign_members {
172 map.serialize_entry(key, value)?;
173 }
174 }
175
176 map.end()
177 }
178}
179
180impl<'de> Deserialize<'de> for FeatureCollection {
181 fn deserialize<D>(deserializer: D) -> std::result::Result<FeatureCollection, D::Error>
182 where
183 D: Deserializer<'de>,
184 {
185 use serde::de::Error as SerdeError;
186
187 let val = JsonObject::deserialize(deserializer)?;
188
189 FeatureCollection::from_json_object(val).map_err(|e| D::Error::custom(e.to_string()))
190 }
191}
192
193impl FromIterator<Feature> for FeatureCollection {
201 fn from_iter<T: IntoIterator<Item = Feature>>(iter: T) -> Self {
202 let mut bbox = Some(vec![]);
203
204 let features = iter
205 .into_iter()
206 .inspect(|feat| {
207 let (curr_bbox, curr_len) = match &mut bbox {
210 Some(curr_bbox) => {
211 let curr_len = curr_bbox.len();
212 (curr_bbox, curr_len)
213 }
214 None => {
215 return;
218 }
219 };
220
221 match &feat.bbox {
222 None => {
223 bbox = None;
224 }
225 Some(fbox) if fbox.is_empty() || fbox.len() % 2 != 0 => {
226 bbox = None;
227 }
228 Some(fbox) if curr_len == 0 => {
229 curr_bbox.clone_from(fbox);
231 }
232 Some(fbox) if curr_len != fbox.len() => {
233 bbox = None;
234 }
235 Some(fbox) => {
236 curr_bbox.iter_mut().zip(fbox.iter()).enumerate().for_each(
238 |(idx, (bc, fc))| {
239 if idx < curr_len / 2 {
240 *bc = fc.min(*bc);
242 } else {
243 *bc = fc.max(*bc);
244 }
245 },
246 );
247 }
248 };
249 })
250 .collect();
251 FeatureCollection {
252 bbox,
253 features,
254 foreign_members: None,
255 }
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use crate::{Error, Feature, FeatureCollection, Value};
262 use serde_json::json;
263
264 use std::str::FromStr;
265
266 #[test]
267 fn test_fc_from_iterator() {
268 let features: Vec<Feature> = vec![
269 {
270 let mut feat: Feature = Value::Point(vec![0., 0., 0.]).into();
271 feat.bbox = Some(vec![-1., -1., -1., 1., 1., 1.]);
272 feat
273 },
274 {
275 let mut feat: Feature =
276 Value::MultiPoint(vec![vec![10., 10., 10.], vec![11., 11., 11.]]).into();
277 feat.bbox = Some(vec![10., 10., 10., 11., 11., 11.]);
278 feat
279 },
280 ];
281
282 let fc: FeatureCollection = features.into_iter().collect();
283 assert_eq!(fc.features.len(), 2);
284 assert_eq!(fc.bbox, Some(vec![-1., -1., -1., 11., 11., 11.]));
285 }
286
287 fn feature_collection_json() -> String {
288 json!({ "type": "FeatureCollection", "features": [
289 {
290 "type": "Feature",
291 "geometry": {
292 "type": "Point",
293 "coordinates": [11.1, 22.2]
294 },
295 "properties": {
296 "name": "Downtown"
297 }
298 },
299 {
300 "type": "Feature",
301 "geometry": {
302 "type": "Point",
303 "coordinates": [33.3, 44.4]
304 },
305 "properties": {
306 "name": "Uptown"
307 }
308 },
309 ]})
310 .to_string()
311 }
312
313 #[test]
314 fn test_from_str_ok() {
315 let feature_collection = FeatureCollection::from_str(&feature_collection_json()).unwrap();
316 assert_eq!(2, feature_collection.features.len());
317 }
318
319 #[test]
320 fn iter_features() {
321 let feature_collection = FeatureCollection::from_str(&feature_collection_json()).unwrap();
322
323 let mut names: Vec<String> = vec![];
324 for feature in &feature_collection {
325 let name = feature
326 .property("name")
327 .unwrap()
328 .as_str()
329 .unwrap()
330 .to_string();
331 names.push(name);
332 }
333
334 assert_eq!(names, vec!["Downtown", "Uptown"]);
335 }
336
337 #[test]
338 fn test_from_str_with_unexpected_type() {
339 let geometry_json = json!({
340 "type": "Point",
341 "coordinates": [125.6, 10.1]
342 })
343 .to_string();
344
345 let actual_failure = FeatureCollection::from_str(&geometry_json).unwrap_err();
346 match actual_failure {
347 Error::ExpectedType { actual, expected } => {
348 assert_eq!(actual, "Geometry");
349 assert_eq!(expected, "FeatureCollection");
350 }
351 e => panic!("unexpected error: {}", e),
352 };
353 }
354}