diff --git a/CHANGES.md b/CHANGES.md index e8d4a10..9cb1430 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,12 @@ ## UNRELEASED +* Added `GeometryCollection::try_from(&GeoJson)` and deprecated + `quick_collection` for conventional naming and simpler docs. + * + +## 0.24.0 + * Fix: FeatureIterator errors when reading "features" field before "type" field. * * Added `geojson::{ser, de}` helpers to convert your custom struct to and from GeoJSON. diff --git a/src/conversion/mod.rs b/src/conversion/mod.rs index 9c3f4cf..d44e576 100644 --- a/src/conversion/mod.rs +++ b/src/conversion/mod.rs @@ -12,13 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use geo_types::{self, CoordFloat, GeometryCollection}; +use geo_types::CoordFloat; use crate::geojson::GeoJson; -use crate::geojson::GeoJson::{Feature, FeatureCollection, Geometry}; use crate::Result; -use std::convert::TryInto; +use std::convert::TryFrom; #[cfg(test)] macro_rules! assert_almost_eq { @@ -79,32 +78,6 @@ macro_rules! try_from_owned_value { pub(crate) mod from_geo_types; pub(crate) mod to_geo_types; -// Process top-level `GeoJSON` items, returning a geo_types::GeometryCollection or an Error -fn process_geojson(gj: &GeoJson) -> Result> -where - T: CoordFloat, -{ - match gj { - FeatureCollection(collection) => Ok(GeometryCollection( - collection - .features - .iter() - // Only pass on non-empty geometries - .filter_map(|feature| feature.geometry.as_ref()) - .map(|geometry| geometry.clone().try_into()) - .collect::>()?, - )), - Feature(feature) => { - if let Some(geometry) = &feature.geometry { - Ok(GeometryCollection(vec![geometry.clone().try_into()?])) - } else { - Ok(GeometryCollection(vec![])) - } - } - Geometry(geometry) => Ok(GeometryCollection(vec![geometry.clone().try_into()?])), - } -} - /// A shortcut for producing `geo_types` [GeometryCollection](../geo_types/struct.GeometryCollection.html) objects /// from arbitrary valid GeoJSON input. /// @@ -113,7 +86,7 @@ where /// # Example /// /// ``` -/// use geo_types::GeometryCollection; +/// use geo_types::{Geometry, GeometryCollection, Point}; /// use geojson::{quick_collection, GeoJson}; /// /// let geojson_str = r#" @@ -125,10 +98,7 @@ where /// "properties": {}, /// "geometry": { /// "type": "Point", -/// "coordinates": [ -/// -0.13583511114120483, -/// 51.5218870403801 -/// ] +/// "coordinates": [-1.0, 2.0] /// } /// } /// ] @@ -137,11 +107,16 @@ where /// let geojson = geojson_str.parse::().unwrap(); /// // Turn the GeoJSON string into a geo_types GeometryCollection /// let mut collection: GeometryCollection = quick_collection(&geojson).unwrap(); +/// assert_eq!(collection[0], Geometry::Point(Point::new(-1.0, 2.0))) /// ``` +#[deprecated( + since = "0.24.1", + note = "use `geo_types::GeometryCollection::try_from(&geojson)` instead" +)] #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] pub fn quick_collection(gj: &GeoJson) -> Result> where T: CoordFloat, { - process_geojson(gj) + geo_types::GeometryCollection::try_from(gj) } diff --git a/src/conversion/to_geo_types.rs b/src/conversion/to_geo_types.rs index 996a730..5536c60 100644 --- a/src/conversion/to_geo_types.rs +++ b/src/conversion/to_geo_types.rs @@ -243,6 +243,40 @@ impl_try_from_geom_value![ GeometryCollection ]; +impl TryFrom<&GeoJson> for geo_types::GeometryCollection { + type Error = Error; + + // Process top-level `GeoJSON` items, returning a geo_types::GeometryCollection or an Error + fn try_from(gj: &GeoJson) -> Result> + where + T: CoordFloat, + { + match gj { + GeoJson::FeatureCollection(collection) => Ok(geo_types::GeometryCollection( + collection + .features + .iter() + // Only pass on non-empty geometries + .filter_map(|feature| feature.geometry.as_ref()) + .map(|geometry| geometry.clone().try_into()) + .collect::>()?, + )), + GeoJson::Feature(feature) => { + if let Some(geometry) = &feature.geometry { + Ok(geo_types::GeometryCollection(vec![geometry + .clone() + .try_into()?])) + } else { + Ok(geo_types::GeometryCollection(vec![])) + } + } + GeoJson::Geometry(geometry) => Ok(geo_types::GeometryCollection(vec![geometry + .clone() + .try_into()?])), + } + } +} + #[cfg_attr(docsrs, doc(cfg(feature = "geo-types")))] impl TryFrom for geo_types::Geometry where diff --git a/src/lib.rs b/src/lib.rs index 045faa0..7124f3f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -245,7 +245,7 @@ //! [`geo-types`](../geo_types/index.html#structs) are a common geometry format used across many //! geospatial processing crates. The `geo-types` feature is enabled by default. //! -//! ### From geo-types to geojson +//! ### Convert `geo-types` to `geojson` //! //! [`From`] is implemented on the [`Value`] enum variants to allow conversion _from_ [`geo-types` //! Geometries](../geo_types/index.html#structs). @@ -295,67 +295,21 @@ //! # } //! ``` //! -//! ### From geojson to geo-types +//! ### Convert `geojson` to `geo-types` //! -//! The optional `geo-types` feature implements the [`TryFrom`](../std/convert/trait.TryFrom.html) -//! trait, providing **fallible** conversions _to_ [geo-types Geometries](../geo_types/index.html#structs) -//! from [GeoJSON `Value`](enum.Value.html) enums. +//! The `geo-types` feature implements the [`TryFrom`](../std/convert/trait.TryFrom.html) trait, +//! providing **fallible** conversions _to_ [geo-types Geometries](../geo_types/index.html#structs) +//! from [`GeoJson`], [`Value`], or [`Geometry`] types. //! -//! **In most cases it is assumed that you want to convert GeoJSON into `geo` primitive types in -//! order to process, transform, or measure them:** -//! - `match` on `geojson`, iterating over its `features` field, yielding `Option`. -//! - process each `Feature`, accessing its `Value` field, yielding `Option`. -//! -//! Each [`Value`](enum.Value.html) represents a primitive type, such as a coordinate, point, -//! linestring, polygon, or its multi- equivalent, **and each of these has an equivalent `geo` -//! primitive type**, which you can convert to using the `std::convert::TryFrom` trait. -//! -//! #### GeoJSON to geo_types::GeometryCollection -//! -//! Unifying these features, the [`quick_collection`](fn.quick_collection.html) function accepts a [`GeoJson`](enum.GeoJson.html) enum -//! and processes it, producing a [`GeometryCollection`](../geo_types/struct.GeometryCollection.html) -//! whose members can be transformed, measured, rotated, etc using the algorithms and functions in -//! the [`geo`](https://docs.rs/geo) crate: +//! #### Convert `geojson` to `geo_types::Geometry` //! //! ``` //! # #[cfg(feature = "geo-types")] //! # { -//! // requires enabling the `geo-types` feature -//! use geo_types::GeometryCollection; -//! use geojson::{quick_collection, GeoJson}; -//! let geojson_str = r#" -//! { -//! "type": "FeatureCollection", -//! "features": [ -//! { -//! "type": "Feature", -//! "properties": {}, -//! "geometry": { -//! "type": "Point", -//! "coordinates": [ -//! -0.13583511114120483, -//! 51.5218870403801 -//! ] -//! } -//! } -//! ] -//! } -//! "#; -//! let geojson = geojson_str.parse::().unwrap(); -//! // Turn the GeoJSON string into a geo_types GeometryCollection -//! let mut collection: GeometryCollection = quick_collection(&geojson).unwrap(); -//! # } -//! ``` -//! -//! #### Convert `GeoJson` to `geo_types::Geometry` -//! -//! ``` -//! # #[cfg(feature = "geo-types")] -//! # { -//! // requires enabling the `geo-types` feature +//! // This example requires the `geo-types` feature //! use geo_types::Geometry; //! use geojson::GeoJson; -//! use std::convert::TryInto; +//! use std::convert::TryFrom; //! use std::str::FromStr; //! //! let geojson_str = r#" @@ -373,7 +327,7 @@ //! "#; //! let geojson = GeoJson::from_str(geojson_str).unwrap(); //! // Turn the GeoJSON string into a geo_types Geometry -//! let geom: geo_types::Geometry = geojson.try_into().unwrap(); +//! let geom = geo_types::Geometry::::try_from(geojson).unwrap(); //! # } //! ``` //!