diff --git a/CHANGES.md b/CHANGES.md index e271d5e..1462ea8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,11 @@ # Changes ## next release -### Changed +### Added * impl `std::fmt::Display` for `Wkt`. +* impls of From for the different types in `geo_types` are added. +### Changed +* Trait ToWkt gets deprecated. ## 0.10.0 - 2022-02-24 ### Changed diff --git a/src/lib.rs b/src/lib.rs index 1b6dc01..d28ab32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,24 +22,42 @@ //! //! # Examples //! +//! Parse a WKT string to a type: +//! //! ``` -//! use std::str::FromStr; //! use wkt::Wkt; -//! let point: Wkt = Wkt::from_str("POINT(10 20)").unwrap(); +//! +//! let point: Wkt = "POINT(10 20)".parse().unwrap(); //! ``` //! -//! ```ignore -//! // Convert to a geo_types primitive from a Wkt struct -//! use std::convert::TryInto; +//! Convert to a geo_types primitive from a Wkt struct: +//! +//! ``` //! use wkt::Wkt; //! use geo_types::Point; -//! let point: Wkt = Wkt::from_str("POINT(10 20)").unwrap(); -//! let g_point: geo_types::Point = (10., 20.).into(); -// // We can attempt to directly convert the Wkt without having to access its items field +//! +//! let point: Wkt = "POINT(10 20)".parse().unwrap(); //! let converted: Point = point.try_into().unwrap(); +//! +//! let g_point: geo_types::Point = (10., 20.).into(); +//! //! assert_eq!(g_point, converted); //! ``` //! +//! Take a geo_types structure and get its WKT representation: +//! +//! ``` +//! use wkt::Wkt; +//! +//! let g_point: geo_types::Point = (10., 20.).into(); +//! let wkt_point: Wkt = g_point.into(); +//! +//! assert_eq!(wkt_point.to_string(), "POINT(10 20)"); +//! +//! // or use it in a format string +//! assert_eq!(format!("{wkt_point}"), "POINT(10 20)"); +//! ``` +//! //! ## Direct Access to the `item` Field //! If you wish to work directly with one of the WKT [`types`] you can match on the `item` field //! ``` @@ -84,9 +102,6 @@ extern crate geo_types; extern crate thiserror; -#[cfg(feature = "geo-types")] -pub use crate::towkt::ToWkt; - #[cfg(feature = "geo-types")] pub mod conversion; diff --git a/src/towkt.rs b/src/towkt.rs index 5655ab6..0533207 100644 --- a/src/towkt.rs +++ b/src/towkt.rs @@ -3,10 +3,11 @@ use crate::types::{ Polygon, }; use crate::Geometry; -use crate::Wkt; +use crate::{Wkt, WktFloat}; use geo_types::CoordFloat; +#[deprecated] /// A trait for converting values to WKT pub trait ToWkt where @@ -16,7 +17,7 @@ where fn to_wkt(&self) -> Wkt; } -fn g_point_to_w_coord(g_point: &geo_types::Coordinate) -> Coord +fn g_point_to_w_coord(g_point: geo_types::Coordinate) -> Coord where T: CoordFloat, { @@ -28,49 +29,49 @@ where } } -fn g_point_to_w_point(g_point: &geo_types::Point) -> Point +fn g_point_to_w_point(g_point: geo_types::Point) -> Point where T: CoordFloat, { - let coord = g_point_to_w_coord(&g_point.0); + let coord = g_point_to_w_coord(g_point.0); Point(Some(coord)) } -fn g_points_to_w_coords(g_points: &[geo_types::Coordinate]) -> Vec> +fn g_points_to_w_coords(g_points: Vec>) -> Vec> where T: CoordFloat, { - g_points.iter().map(g_point_to_w_coord).collect() + g_points.into_iter().map(g_point_to_w_coord).collect() } -fn g_points_to_w_points(g_points: &[geo_types::Point]) -> Vec> +fn g_points_to_w_points(g_points: Vec>) -> Vec> where T: CoordFloat, { g_points - .iter() - .map(|p| &p.0) + .into_iter() + .map(|p| p.0) .map(g_point_to_w_coord) .map(|c| Point(Some(c))) .collect() } -fn g_line_to_w_linestring(g_line: &geo_types::Line) -> LineString +fn g_line_to_w_linestring(g_line: geo_types::Line) -> Geometry where T: CoordFloat, { - g_points_to_w_linestring(&[g_line.start, g_line.end]) + g_points_to_w_linestring(vec![g_line.start, g_line.end]).as_item() } -fn g_linestring_to_w_linestring(g_linestring: &geo_types::LineString) -> LineString +fn g_linestring_to_w_linestring(g_linestring: geo_types::LineString) -> Geometry where T: CoordFloat, { - let &geo_types::LineString(ref g_points) = g_linestring; - g_points_to_w_linestring(g_points) + let geo_types::LineString(g_points) = g_linestring; + g_points_to_w_linestring(g_points).as_item() } -fn g_points_to_w_linestring(g_coords: &[geo_types::Coordinate]) -> LineString +fn g_points_to_w_linestring(g_coords: Vec>) -> LineString where T: CoordFloat, { @@ -78,44 +79,45 @@ where LineString(w_coords) } -fn g_lines_to_w_lines(g_lines: &[geo_types::LineString]) -> Vec> +fn g_lines_to_w_lines(g_lines: Vec>) -> Vec> where T: CoordFloat, { - let mut w_lines = vec![]; - for g_line in g_lines { - let &geo_types::LineString(ref g_points) = g_line; - w_lines.push(g_points_to_w_linestring(g_points)); - } - w_lines + g_lines + .into_iter() + .map(|g_line| { + let geo_types::LineString(g_points) = g_line; + + g_points_to_w_linestring(g_points) + }) + .collect() } -fn g_triangle_to_w_polygon(g_triangle: &geo_types::Triangle) -> Polygon +fn g_triangle_to_w_polygon(g_triangle: geo_types::Triangle) -> Geometry where T: CoordFloat, { let polygon = g_triangle.to_polygon(); - g_polygon_to_w_polygon(&polygon) + g_polygon_to_w_polygon(polygon).as_item() } -fn g_rect_to_w_polygon(g_rect: &geo_types::Rect) -> Polygon +fn g_rect_to_w_polygon(g_rect: geo_types::Rect) -> Geometry where T: CoordFloat, { let polygon = g_rect.to_polygon(); - g_polygon_to_w_polygon(&polygon) + g_polygon_to_w_polygon(polygon).as_item() } -fn g_polygon_to_w_polygon(g_polygon: &geo_types::Polygon) -> Polygon +fn g_polygon_to_w_polygon(g_polygon: geo_types::Polygon) -> Polygon where T: CoordFloat, { - let outer_line = g_polygon.exterior(); - let inner_lines = g_polygon.interiors(); - let mut poly_lines = vec![]; + let (outer_line, inner_lines) = g_polygon.into_inner(); + let mut poly_lines = Vec::with_capacity(inner_lines.len() + 1); // Outer - let &geo_types::LineString(ref outer_points) = outer_line; + let geo_types::LineString(outer_points) = outer_line; if !outer_points.is_empty() { poly_lines.push(g_points_to_w_linestring(outer_points)); } @@ -127,98 +129,166 @@ where Polygon(poly_lines) } -fn g_mpoint_to_w_mpoint(g_mpoint: &geo_types::MultiPoint) -> MultiPoint +fn g_polygon_to_w_polygon_geom(g_polygon: geo_types::Polygon) -> Geometry where T: CoordFloat, { - let &geo_types::MultiPoint(ref g_points) = g_mpoint; + g_polygon_to_w_polygon(g_polygon).as_item() +} + +fn g_mpoint_to_w_mpoint(g_mpoint: geo_types::MultiPoint) -> Geometry +where + T: CoordFloat, +{ + let geo_types::MultiPoint(g_points) = g_mpoint; let w_points = g_points_to_w_points(g_points); - MultiPoint(w_points) + MultiPoint(w_points).as_item() } -fn g_mline_to_w_mline(g_mline: &geo_types::MultiLineString) -> MultiLineString +fn g_mline_to_w_mline(g_mline: geo_types::MultiLineString) -> Geometry where T: CoordFloat, { - let &geo_types::MultiLineString(ref g_lines) = g_mline; + let geo_types::MultiLineString(g_lines) = g_mline; let w_lines = g_lines_to_w_lines(g_lines); - MultiLineString(w_lines) + MultiLineString(w_lines).as_item() } -fn g_polygons_to_w_polygons(g_polygons: &[geo_types::Polygon]) -> Vec> +fn g_polygons_to_w_polygons(g_polygons: Vec>) -> Vec> where T: CoordFloat, { - let mut w_polygons = vec![]; - for g_polygon in g_polygons { - w_polygons.push(g_polygon_to_w_polygon(g_polygon)); - } - w_polygons + g_polygons.into_iter().map(g_polygon_to_w_polygon).collect() } -fn g_mpolygon_to_w_mpolygon(g_mpolygon: &geo_types::MultiPolygon) -> MultiPolygon +fn g_mpolygon_to_w_mpolygon(g_mpolygon: geo_types::MultiPolygon) -> Geometry where T: CoordFloat, { - let &geo_types::MultiPolygon(ref g_polygons) = g_mpolygon; + let geo_types::MultiPolygon(g_polygons) = g_mpolygon; let w_polygons = g_polygons_to_w_polygons(g_polygons); - MultiPolygon(w_polygons) + MultiPolygon(w_polygons).as_item() } -fn g_geocol_to_w_geocol(g_geocol: &geo_types::GeometryCollection) -> GeometryCollection +fn g_geocol_to_w_geocol(g_geocol: geo_types::GeometryCollection) -> Geometry where T: CoordFloat, { - let &geo_types::GeometryCollection(ref g_geoms) = g_geocol; - let mut w_geoms = vec![]; - for g_geom in g_geoms { - let w_geom = g_geom_to_w_geom(g_geom); - w_geoms.push(w_geom); - } - GeometryCollection(w_geoms) + let geo_types::GeometryCollection(g_geoms) = g_geocol; + + GeometryCollection(g_geoms.into_iter().map(g_geom_to_w_geom).collect()).as_item() } -fn g_geom_to_w_geom(g_geom: &geo_types::Geometry) -> Geometry +fn g_geom_to_w_geom(g_geom: geo_types::Geometry) -> Geometry where T: CoordFloat, { - match *g_geom { - geo_types::Geometry::Point(ref g_point) => g_point_to_w_point(g_point).as_item(), + match g_geom { + geo_types::Geometry::Point(g_point) => g_point_to_w_point(g_point).as_item(), - geo_types::Geometry::Line(ref g_line) => g_line_to_w_linestring(g_line).as_item(), + geo_types::Geometry::Line(g_line) => g_line_to_w_linestring(g_line), - geo_types::Geometry::LineString(ref g_line) => { - g_linestring_to_w_linestring(g_line).as_item() - } + geo_types::Geometry::LineString(g_line) => g_linestring_to_w_linestring(g_line), - geo_types::Geometry::Triangle(ref g_triangle) => { - g_triangle_to_w_polygon(g_triangle).as_item() - } + geo_types::Geometry::Triangle(g_triangle) => g_triangle_to_w_polygon(g_triangle), - geo_types::Geometry::Rect(ref g_rect) => g_rect_to_w_polygon(g_rect).as_item(), + geo_types::Geometry::Rect(g_rect) => g_rect_to_w_polygon(g_rect), - geo_types::Geometry::Polygon(ref g_polygon) => g_polygon_to_w_polygon(g_polygon).as_item(), + geo_types::Geometry::Polygon(g_polygon) => g_polygon_to_w_polygon(g_polygon).as_item(), - geo_types::Geometry::MultiPoint(ref g_mpoint) => g_mpoint_to_w_mpoint(g_mpoint).as_item(), + geo_types::Geometry::MultiPoint(g_mpoint) => g_mpoint_to_w_mpoint(g_mpoint), - geo_types::Geometry::MultiLineString(ref g_mline) => g_mline_to_w_mline(g_mline).as_item(), + geo_types::Geometry::MultiLineString(g_mline) => g_mline_to_w_mline(g_mline), - geo_types::Geometry::MultiPolygon(ref g_mpolygon) => { - g_mpolygon_to_w_mpolygon(g_mpolygon).as_item() - } + geo_types::Geometry::MultiPolygon(g_mpolygon) => g_mpolygon_to_w_mpolygon(g_mpolygon), - geo_types::Geometry::GeometryCollection(ref g_geocol) => { - g_geocol_to_w_geocol(g_geocol).as_item() - } + geo_types::Geometry::GeometryCollection(g_geocol) => g_geocol_to_w_geocol(g_geocol), } } -impl ToWkt for geo_types::Geometry +impl From> for Wkt where - T: CoordFloat, + T: WktFloat, { - fn to_wkt(&self) -> Wkt { - let w_geom = g_geom_to_w_geom(self); - Wkt { item: w_geom } + fn from(p: geo_types::Point) -> Wkt { + Wkt { + item: g_point_to_w_point(p).as_item(), + } + } +} + +/// This macro allows easy implementation of From for Wkt +macro_rules! impl_from_for_wkt { + ($from:ty, $func:ident) => { + impl From<$from> for Wkt + where + T: WktFloat, + { + fn from(t: $from) -> Wkt { + Wkt { item: $func(t) } + } + } + }; +} + +impl_from_for_wkt!(geo_types::Geometry, g_geom_to_w_geom); +impl_from_for_wkt!(geo_types::Line, g_line_to_w_linestring); +impl_from_for_wkt!(geo_types::LineString, g_linestring_to_w_linestring); +impl_from_for_wkt!(geo_types::Triangle, g_triangle_to_w_polygon); +impl_from_for_wkt!(geo_types::Rect, g_rect_to_w_polygon); +impl_from_for_wkt!(geo_types::MultiPoint, g_mpoint_to_w_mpoint); +impl_from_for_wkt!(geo_types::MultiLineString, g_mline_to_w_mline); +impl_from_for_wkt!(geo_types::MultiPolygon, g_mpolygon_to_w_mpolygon); +impl_from_for_wkt!(geo_types::GeometryCollection, g_geocol_to_w_geocol); +impl_from_for_wkt!(geo_types::Polygon, g_polygon_to_w_polygon_geom); + +#[cfg(test)] +mod tests { + use geo_types::Coordinate; + + use super::*; + + #[test] + fn individual_geotypes_can_be_converted_to_wkt() { + let c = Coordinate { + x: 10.0_f64, + y: 20., + }; + + let _: Wkt = geo_types::Point(c).into(); + let _: Wkt = geo_types::Line { start: c, end: c }.into(); + let _: Wkt = geo_types::LineString(vec![]).into(); + let _: Wkt = geo_types::Triangle(c, c, c).into(); + let _: Wkt = geo_types::Rect::new(c, c).into(); + let _: Wkt = geo_types::Polygon::new(geo_types::LineString(vec![]), vec![]).into(); + let _: Wkt = geo_types::MultiPoint(vec![]).into(); + let _: Wkt = geo_types::MultiLineString(vec![]).into(); + let _: Wkt = geo_types::MultiPolygon(vec![]).into(); + let _: Wkt = geo_types::GeometryCollection(vec![]).into(); + } + + #[test] + fn geptypes_geometry_can_be_converted_to_wkt() { + let c = Coordinate { + x: 10.0_f64, + y: 20., + }; + + let _: Wkt = geo_types::Geometry::Point(geo_types::Point(c)).into(); + let _: Wkt = geo_types::Geometry::Line(geo_types::Line { start: c, end: c }).into(); + let _: Wkt = geo_types::Geometry::LineString(geo_types::LineString(vec![])).into(); + let _: Wkt = geo_types::Geometry::Triangle(geo_types::Triangle(c, c, c)).into(); + let _: Wkt = geo_types::Geometry::Rect(geo_types::Rect::new(c, c)).into(); + let _: Wkt = geo_types::Geometry::Polygon(geo_types::Polygon::new( + geo_types::LineString(vec![]), + vec![], + )) + .into(); + let _: Wkt = geo_types::Geometry::MultiPoint(geo_types::MultiPoint(vec![])).into(); + let _: Wkt = + geo_types::Geometry::MultiLineString(geo_types::MultiLineString(vec![])).into(); + let _: Wkt = geo_types::Geometry::MultiPolygon(geo_types::MultiPolygon(vec![])).into(); + let _: Wkt = + geo_types::Geometry::GeometryCollection(geo_types::GeometryCollection(vec![])).into(); } }