|
96 | 96 | //! ...
|
97 | 97 | //! }
|
98 | 98 | //! ```
|
99 |
| -use crate::{JsonObject, JsonValue, Result}; |
| 99 | +use crate::{Feature, JsonObject, JsonValue, Result}; |
100 | 100 |
|
101 | 101 | use serde::{ser::Error, Serialize, Serializer};
|
102 | 102 |
|
| 103 | +use crate::util::expect_owned_object; |
| 104 | +use std::convert::TryFrom; |
103 | 105 | use std::{convert::TryInto, io};
|
104 | 106 |
|
105 | 107 | /// Serialize a single data structure to a GeoJSON Feature string.
|
@@ -198,6 +200,66 @@ where
|
198 | 200 | Ok(())
|
199 | 201 | }
|
200 | 202 |
|
| 203 | +/// Convert a `T` into a [`Feature`]. |
| 204 | +/// |
| 205 | +/// This is analogous to [`serde_json::to_value`](https://docs.rs/serde_json/latest/serde_json/fn.to_value.html) |
| 206 | +/// |
| 207 | +/// Note that if (and only if) `T` has a field named `geometry`, it will be serialized to |
| 208 | +/// `feature.geometry`. |
| 209 | +/// |
| 210 | +/// All other fields will be serialized to `feature.properties`. |
| 211 | +/// |
| 212 | +/// # Examples |
| 213 | +#[cfg_attr(feature = "geo-types", doc = "```")] |
| 214 | +#[cfg_attr(not(feature = "geo-types"), doc = "```ignore")] |
| 215 | +/// use serde::Serialize; |
| 216 | +/// use geojson::{Feature, Value, Geometry}; |
| 217 | +/// use geojson::ser::{to_feature, serialize_geometry}; |
| 218 | +/// |
| 219 | +/// #[derive(Serialize)] |
| 220 | +/// struct MyStruct { |
| 221 | +/// // Serialize `geometry` as geojson, rather than using the type's default serialization |
| 222 | +/// #[serde(serialize_with = "serialize_geometry")] |
| 223 | +/// geometry: geo_types::Point, |
| 224 | +/// name: String, |
| 225 | +/// } |
| 226 | +/// |
| 227 | +/// let my_struct = MyStruct { |
| 228 | +/// geometry: geo_types::Point::new(1.0, 2.0), |
| 229 | +/// name: "My Name".to_string() |
| 230 | +/// }; |
| 231 | +/// |
| 232 | +/// let feature: Feature = to_feature(my_struct).unwrap(); |
| 233 | +/// assert_eq!("My Name", feature.properties.unwrap()["name"]); |
| 234 | +/// assert_eq!(feature.geometry.unwrap(), Geometry::new(Value::Point(vec![1.0, 2.0]))); |
| 235 | +/// ``` |
| 236 | +/// |
| 237 | +/// # Errors |
| 238 | +/// |
| 239 | +/// Serialization can fail if `T`'s implementation of `Serialize` decides to |
| 240 | +/// fail, or if `T` contains a map with non-string keys. |
| 241 | +pub fn to_feature<T>(value: T) -> Result<Feature> |
| 242 | +where |
| 243 | + T: Serialize, |
| 244 | +{ |
| 245 | + let js_value = serde_json::to_value(value)?; |
| 246 | + let mut js_object = expect_owned_object(js_value)?; |
| 247 | + |
| 248 | + let geometry = if let Some(geometry_value) = js_object.remove("geometry") { |
| 249 | + Some(crate::Geometry::try_from(geometry_value)?) |
| 250 | + } else { |
| 251 | + None |
| 252 | + }; |
| 253 | + |
| 254 | + Ok(Feature { |
| 255 | + bbox: None, |
| 256 | + geometry, |
| 257 | + id: None, |
| 258 | + properties: Some(js_object), |
| 259 | + foreign_members: None, |
| 260 | + }) |
| 261 | +} |
| 262 | + |
201 | 263 | /// Serialize elements as a GeoJSON FeatureCollection into the IO stream.
|
202 | 264 | ///
|
203 | 265 | /// Note that `T` must have a column called `geometry`.
|
@@ -569,6 +631,7 @@ mod tests {
|
569 | 631 | mod geo_types_tests {
|
570 | 632 | use super::*;
|
571 | 633 | use crate::de::tests::feature_collection;
|
| 634 | + use crate::Geometry; |
572 | 635 |
|
573 | 636 | #[test]
|
574 | 637 | fn serializes_optional_point() {
|
@@ -677,6 +740,42 @@ mod tests {
|
677 | 740 | assert_eq!(actual_output, expected_output);
|
678 | 741 | }
|
679 | 742 |
|
| 743 | + #[test] |
| 744 | + fn test_to_feature() { |
| 745 | + #[derive(Serialize)] |
| 746 | + struct MyStruct { |
| 747 | + #[serde(serialize_with = "serialize_geometry")] |
| 748 | + geometry: geo_types::Point<f64>, |
| 749 | + name: String, |
| 750 | + age: u64, |
| 751 | + } |
| 752 | + |
| 753 | + let my_struct = MyStruct { |
| 754 | + geometry: geo_types::point!(x: 125.6, y: 10.1), |
| 755 | + name: "Dinagat Islands".to_string(), |
| 756 | + age: 123, |
| 757 | + }; |
| 758 | + |
| 759 | + let actual = to_feature(&my_struct).unwrap(); |
| 760 | + let expected = Feature { |
| 761 | + bbox: None, |
| 762 | + geometry: Some(Geometry::new(crate::Value::Point(vec![125.6, 10.1]))), |
| 763 | + id: None, |
| 764 | + properties: Some( |
| 765 | + json!({ |
| 766 | + "name": "Dinagat Islands", |
| 767 | + "age": 123 |
| 768 | + }) |
| 769 | + .as_object() |
| 770 | + .unwrap() |
| 771 | + .clone(), |
| 772 | + ), |
| 773 | + foreign_members: None, |
| 774 | + }; |
| 775 | + |
| 776 | + assert_eq!(actual, expected) |
| 777 | + } |
| 778 | + |
680 | 779 | #[test]
|
681 | 780 | fn serialize_feature_collection() {
|
682 | 781 | #[derive(Serialize)]
|
|
0 commit comments