diff --git a/Cargo.toml b/Cargo.toml index 2da6b5f..01f09e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ keywords = ["geo", "geospatial", "wkt"] [dependencies] geo-types = {version = "0.4", optional = true} +num-traits = "0.2" [dev-dependencies] criterion = { version = "0.2" } diff --git a/src/conversion.rs b/src/conversion.rs index e069632..8c075a5 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -13,6 +13,7 @@ // limitations under the License. extern crate geo_types; +extern crate num_traits; use std::fmt; use types::*; @@ -33,17 +34,33 @@ impl fmt::Display for Error { } } -fn try_into_point(point: &Point) -> Result, Error> { +fn create_geo_coordinate(coord: &Coord) -> geo_types::Coordinate +where + T: num_traits::Float, +{ + geo_types::Coordinate { + x: T::from(coord.x).unwrap(), + y: T::from(coord.y).unwrap(), + } +} + +fn try_into_point(point: &Point) -> Result, Error> +where + T: num_traits::Float, +{ match point.0 { Some(ref c) => { - let geo_point: geo_types::Point = (c.x, c.y).into(); + let geo_point: geo_types::Point = (c.x, c.y).into(); Ok(geo_point.into()) } None => Err(Error::PointConversionError), } } -pub fn try_into_geometry(geometry: &Geometry) -> Result, Error> { +pub fn try_into_geometry(geometry: &Geometry) -> Result, Error> +where + T: num_traits::Float, +{ match geometry { Geometry::Point(point) => try_into_point(point), Geometry::LineString(linestring) => Ok(linestring.into()), @@ -57,32 +74,40 @@ pub fn try_into_geometry(geometry: &Geometry) -> Result } } -impl<'a> From<&'a LineString> for geo_types::Geometry { - fn from(linestring: &LineString) -> Self { - let geo_linestring: geo_types::LineString = - linestring.0.iter().map(|c| (c.x, c.y)).collect(); +impl<'a, T> Into> for &'a LineString +where + T: num_traits::Float, +{ + fn into(self) -> geo_types::Geometry { + let geo_linestring: geo_types::LineString = + self.0.iter().map(|c| create_geo_coordinate(&c)).collect(); geo_linestring.into() } } -impl<'a> From<&'a MultiLineString> for geo_types::Geometry { - fn from(multilinestring: &MultiLineString) -> Self { - let geo_multilinestring: geo_types::MultiLineString = multilinestring +impl<'a, T> Into> for &'a MultiLineString +where + T: num_traits::Float, +{ + fn into(self) -> geo_types::Geometry { + let geo_multilinestring: geo_types::MultiLineString = self .0 .iter() - .map(|l| l.0.iter().map(|c| (c.x, c.y)).collect::>()) + .map(|l| l.0.iter().map(|c| create_geo_coordinate(&c)).collect::>()) .collect(); geo_multilinestring.into() } } -fn w_polygon_to_g_polygon(polygon: &Polygon) -> geo_types::Polygon { +fn w_polygon_to_g_polygon(polygon: &Polygon) -> geo_types::Polygon +where + T: num_traits::Float, +{ let mut iter = polygon .0 - .iter() - .map(|l| l.0.iter().map(|c| (c.x, c.y)).collect::>().into()); + .iter().map(|l| l.0.iter().map(|c| create_geo_coordinate(c)).collect::>().into()); match iter.next() { Some(interior) => geo_types::Polygon::new(interior, iter.collect()), @@ -90,28 +115,37 @@ fn w_polygon_to_g_polygon(polygon: &Polygon) -> geo_types::Polygon { } } -impl<'a> From<&'a Polygon> for geo_types::Geometry { - fn from(polygon: &Polygon) -> Self { - w_polygon_to_g_polygon(polygon).into() +impl<'a, T> Into> for &'a Polygon +where + T: num_traits::Float, +{ + fn into(self) -> geo_types::Geometry { + w_polygon_to_g_polygon(self).into() } } -impl<'a> From<&'a MultiPoint> for geo_types::Geometry { - fn from(multipoint: &MultiPoint) -> Self { - let geo_multipoint: geo_types::MultiPoint = multipoint +impl<'a, T> Into> for &'a MultiPoint +where + T: num_traits::Float, +{ + fn into(self) -> geo_types::Geometry { + let geo_multipoint: geo_types::MultiPoint = self .0 .iter() .filter_map(|p| p.0.as_ref()) - .map(|c| (c.x, c.y)) + .map(|c| create_geo_coordinate(c)) .collect(); geo_multipoint.into() } } -impl<'a> From<&'a MultiPolygon> for geo_types::Geometry { - fn from(multipolygon: &MultiPolygon) -> Self { - let geo_multipolygon: geo_types::MultiPolygon = multipolygon +impl<'a, T> Into> for &'a MultiPolygon +where + T: num_traits::Float, +{ + fn into(self) -> geo_types::Geometry { + let geo_multipolygon: geo_types::MultiPolygon = self .0 .iter() .map(|p| w_polygon_to_g_polygon(p)) @@ -121,10 +155,13 @@ impl<'a> From<&'a MultiPolygon> for geo_types::Geometry { } } -pub fn try_into_geometry_collection( - geometrycollection: &GeometryCollection, -) -> Result, Error> { - let geo_geometrycollection: geo_types::GeometryCollection = geometrycollection +pub fn try_into_geometry_collection( + geometrycollection: &GeometryCollection, +) -> Result, Error> +where + T: num_traits::Float, +{ + let geo_geometrycollection: geo_types::GeometryCollection = geometrycollection .0 .iter() .map(|g| try_into_geometry(g)) @@ -142,7 +179,8 @@ mod tests { #[test] fn convert_empty_point() { let point = Point(None).as_item(); - assert!(try_into_geometry(&point).is_err()); + let res: Result, Error> = try_into_geometry(&point); + assert!(res.is_err()); } #[test] diff --git a/src/lib.rs b/src/lib.rs index a843345..e03ac66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate num_traits; + use std::default::Default; use std::fmt; +use std::str::FromStr; use tokenizer::{PeekableTokens, Token, Tokens}; use types::GeometryCollection; @@ -37,45 +40,51 @@ pub use towkt::ToWkt; #[cfg(feature = "geo-types")] pub mod conversion; -pub enum Geometry { - Point(Point), - LineString(LineString), - Polygon(Polygon), - MultiPoint(MultiPoint), - MultiLineString(MultiLineString), - MultiPolygon(MultiPolygon), - GeometryCollection(GeometryCollection), +pub enum Geometry +where + T: num_traits::Float +{ + Point(Point), + LineString(LineString), + Polygon(Polygon), + MultiPoint(MultiPoint), + MultiLineString(MultiLineString), + MultiPolygon(MultiPolygon), + GeometryCollection(GeometryCollection), } -impl Geometry { - fn from_word_and_tokens(word: &str, tokens: &mut PeekableTokens) -> Result { +impl Geometry +where + T: num_traits::Float + FromStr + Default +{ + fn from_word_and_tokens(word: &str, tokens: &mut PeekableTokens) -> Result { match word { w if w.eq_ignore_ascii_case("POINT") => { - let x = ::from_tokens_with_parens(tokens); + let x = as FromTokens>::from_tokens_with_parens(tokens); x.map(|y| y.as_item()) } w if w.eq_ignore_ascii_case("LINESTRING") => { - let x = ::from_tokens_with_parens(tokens); + let x = as FromTokens>::from_tokens_with_parens(tokens); x.map(|y| y.as_item()) } w if w.eq_ignore_ascii_case("POLYGON") => { - let x = ::from_tokens_with_parens(tokens); + let x = as FromTokens>::from_tokens_with_parens(tokens); x.map(|y| y.as_item()) } w if w.eq_ignore_ascii_case("MULTIPOINT") => { - let x = ::from_tokens_with_parens(tokens); + let x = as FromTokens>::from_tokens_with_parens(tokens); x.map(|y| y.as_item()) } w if w.eq_ignore_ascii_case("MULTILINESTRING") => { - let x = ::from_tokens_with_parens(tokens); + let x = as FromTokens>::from_tokens_with_parens(tokens); x.map(|y| y.as_item()) } w if w.eq_ignore_ascii_case("MULTIPOLYGON") => { - let x = ::from_tokens_with_parens(tokens); + let x = as FromTokens>::from_tokens_with_parens(tokens); x.map(|y| y.as_item()) } w if w.eq_ignore_ascii_case("GEOMETRYCOLLECTION") => { - let x = ::from_tokens_with_parens(tokens); + let x = as FromTokens>::from_tokens_with_parens(tokens); x.map(|y| y.as_item()) } _ => Err("Invalid type encountered"), @@ -83,7 +92,10 @@ impl Geometry { } } -impl fmt::Display for Geometry { +impl fmt::Display for Geometry +where + T: num_traits::Float + fmt::Display +{ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { Geometry::Point(point) => point.fmt(f), @@ -97,16 +109,22 @@ impl fmt::Display for Geometry { } } -pub struct Wkt { - pub items: Vec, +pub struct Wkt +where + T: num_traits::Float +{ + pub items: Vec>, } -impl Wkt { +impl Wkt +where + T: num_traits::Float + FromStr + Default +{ pub fn new() -> Self { Wkt { items: vec![] } } - pub fn add_item(&mut self, item: Geometry) { + pub fn add_item(&mut self, item: Geometry) { self.items.push(item); } @@ -115,7 +133,7 @@ impl Wkt { Wkt::from_tokens(tokens) } - fn from_tokens(tokens: Tokens) -> Result { + fn from_tokens(tokens: Tokens) -> Result { let mut wkt = Wkt::new(); let mut tokens = tokens.peekable(); let word = match tokens.next() { @@ -136,10 +154,13 @@ impl Wkt { } } -trait FromTokens: Sized + Default { - fn from_tokens(tokens: &mut PeekableTokens) -> Result; +trait FromTokens: Sized + Default +where + T: num_traits::Float + FromStr + Default +{ + fn from_tokens(tokens: &mut PeekableTokens) -> Result; - fn from_tokens_with_parens(tokens: &mut PeekableTokens) -> Result { + fn from_tokens_with_parens(tokens: &mut PeekableTokens) -> Result { match tokens.next() { Some(Token::ParenOpen) => (), Some(Token::Word(ref s)) if s.eq_ignore_ascii_case("EMPTY") => { @@ -155,9 +176,9 @@ trait FromTokens: Sized + Default { result } - fn comma_many(f: F, tokens: &mut PeekableTokens) -> Result, &'static str> + fn comma_many(f: F, tokens: &mut PeekableTokens) -> Result, &'static str> where - F: Fn(&mut PeekableTokens) -> Result, + F: Fn(&mut PeekableTokens) -> Result, { let mut items = Vec::new(); @@ -182,20 +203,20 @@ mod tests { #[test] fn empty_string() { - let wkt = Wkt::from_str("").ok().unwrap(); + let wkt: Wkt = Wkt::from_str("").ok().unwrap(); assert_eq!(0, wkt.items.len()); } #[test] fn empty_items() { - let mut wkt = Wkt::from_str("POINT EMPTY").ok().unwrap(); + let mut wkt: Wkt = Wkt::from_str("POINT EMPTY").ok().unwrap(); assert_eq!(1, wkt.items.len()); match wkt.items.pop().unwrap() { Geometry::Point(Point(None)) => (), _ => unreachable!(), }; - let mut wkt = Wkt::from_str("MULTIPOLYGON EMPTY").ok().unwrap(); + let mut wkt: Wkt = Wkt::from_str("MULTIPOLYGON EMPTY").ok().unwrap(); assert_eq!(1, wkt.items.len()); match wkt.items.pop().unwrap() { Geometry::MultiPolygon(MultiPolygon(polygons)) => assert_eq!(polygons.len(), 0), @@ -205,7 +226,7 @@ mod tests { #[test] fn lowercase_point() { - let mut wkt = Wkt::from_str("point EMPTY").ok().unwrap(); + let mut wkt: Wkt = Wkt::from_str("point EMPTY").ok().unwrap(); assert_eq!(1, wkt.items.len()); match wkt.items.pop().unwrap() { Geometry::Point(Point(None)) => (), @@ -215,7 +236,7 @@ mod tests { #[test] fn invalid_number() { - if let Err(msg) = Wkt::from_str("POINT (10 20.1A)") { + if let Err(msg) = >::from_str("POINT (10 20.1A)") { assert_eq!("Expected a number for the Y coordinate", msg); } else { panic!("Should not have parsed"); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 492c857..3ab43d3 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -12,13 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate num_traits; + use std::iter::Peekable; +use std::marker::PhantomData; use std::str; #[derive(PartialEq, Debug)] -pub enum Token { +pub enum Token +where + T: num_traits::Float +{ Comma, - Number(f64), + Number(T), ParenClose, ParenOpen, Word(String), @@ -39,24 +45,32 @@ fn is_numberlike(c: char) -> bool { } } -pub type PeekableTokens<'a> = Peekable>; +pub type PeekableTokens<'a, T> = Peekable>; -pub struct Tokens<'a> { +pub struct Tokens<'a, T> { chars: Peekable>, + phantom: PhantomData, } -impl<'a> Tokens<'a> { +impl<'a, T> Tokens<'a, T> +where + T: num_traits::Float +{ pub fn from_str(input: &'a str) -> Self { Tokens { chars: input.chars().peekable(), + phantom: PhantomData, } } } -impl<'a> Iterator for Tokens<'a> { - type Item = Token; +impl<'a, T> Iterator for Tokens<'a, T> +where + T: num_traits::Float + str::FromStr + Default +{ + type Item = Token; - fn next(&mut self) -> Option { + fn next(&mut self) -> Option> { // TODO: should this return Result? let mut next_char = self.chars.next()?; @@ -72,7 +86,7 @@ impl<'a> Iterator for Tokens<'a> { ',' => Some(Token::Comma), c if is_numberlike(c) => { let mut number = c.to_string() + &self.read_until_whitespace().unwrap_or_default(); - match number.trim_left_matches('+').parse::() { + match number.trim_left_matches('+').parse::() { Ok(parsed_num) => Some(Token::Number(parsed_num)), Err(_) => None } @@ -85,7 +99,10 @@ impl<'a> Iterator for Tokens<'a> { } } -impl<'a> Tokens<'a> { +impl<'a, T> Tokens<'a, T> +where + T: num_traits::Float + str::FromStr + Default +{ fn read_until_whitespace(&mut self) -> Option { let mut result = String::new(); @@ -124,21 +141,21 @@ impl<'a> Tokens<'a> { #[test] fn test_tokenizer_empty() { let test_str = ""; - let tokens: Vec = Tokens::from_str(test_str).collect(); + let tokens: Vec> = Tokens::from_str(test_str).collect(); assert_eq!(tokens, vec![]); } #[test] fn test_tokenizer_1word() { let test_str = "hello"; - let tokens: Vec = Tokens::from_str(test_str).collect(); + let tokens: Vec> = Tokens::from_str(test_str).collect(); assert_eq!(tokens, vec![Token::Word("hello".to_string())]); } #[test] fn test_tokenizer_2words() { let test_str = "hello world"; - let tokens: Vec = Tokens::from_str(test_str).collect(); + let tokens: Vec> = Tokens::from_str(test_str).collect(); assert_eq!( tokens, vec![ @@ -151,28 +168,28 @@ fn test_tokenizer_2words() { #[test] fn test_tokenizer_1number() { let test_str = "4.2"; - let tokens: Vec = Tokens::from_str(test_str).collect(); + let tokens: Vec> = Tokens::from_str(test_str).collect(); assert_eq!(tokens, vec![Token::Number(4.2)]); } #[test] fn test_tokenizer_1number_plus() { let test_str = "+4.2"; - let tokens: Vec = Tokens::from_str(test_str).collect(); + let tokens: Vec> = Tokens::from_str(test_str).collect(); assert_eq!(tokens, vec![Token::Number(4.2)]); } #[test] fn test_tokenizer_invalid_number() { let test_str = "4.2p"; - let tokens: Vec = Tokens::from_str(test_str).collect(); + let tokens: Vec> = Tokens::from_str(test_str).collect(); assert_eq!(tokens, vec![]); } #[test] fn test_tokenizer_2numbers() { let test_str = ".4 -2"; - let tokens: Vec = Tokens::from_str(test_str).collect(); + let tokens: Vec> = Tokens::from_str(test_str).collect(); assert_eq!(tokens, vec![Token::Number(0.4), Token::Number(-2.0)]); } @@ -180,7 +197,7 @@ fn test_tokenizer_2numbers() { fn test_no_stack_overflow() { fn check(c: &str, count: usize, expected: usize) { let test_str = c.repeat(count); - let tokens : Vec= Tokens::from_str(&test_str).collect(); + let tokens : Vec>= Tokens::from_str(&test_str).collect(); assert_eq!(expected, tokens.len()); } @@ -197,7 +214,7 @@ fn test_no_stack_overflow() { #[test] fn test_tokenizer_point() { let test_str = "POINT (10 -20)"; - let tokens: Vec = Tokens::from_str(test_str).collect(); + let tokens: Vec> = Tokens::from_str(test_str).collect(); assert_eq!( tokens, vec![ diff --git a/src/towkt.rs b/src/towkt.rs index cb005be..940220c 100644 --- a/src/towkt.rs +++ b/src/towkt.rs @@ -1,4 +1,5 @@ extern crate geo_types; +extern crate num_traits; use types::Coord; use types::GeometryCollection; @@ -12,12 +13,18 @@ use Geometry; use Wkt; /// A trait for converting values to WKT -pub trait ToWkt { +pub trait ToWkt +where + T: num_traits::Float +{ /// Converts the value of `self` to an instance of WKT - fn to_wkt(&self) -> Wkt; + 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: num_traits::Float +{ Coord { x: g_point.x, y: g_point.y, @@ -26,16 +33,25 @@ fn g_point_to_w_coord(g_point: &geo_types::Coordinate) -> Coord { } } -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: num_traits::Float +{ 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: &[geo_types::Coordinate]) -> Vec> +where + T: num_traits::Float +{ g_points.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: &[geo_types::Point]) -> Vec> +where + T: num_traits::Float +{ g_points .iter() .map(|p|&p.0) @@ -44,21 +60,33 @@ fn g_points_to_w_points(g_points: &[geo_types::Point]) -> Vec { .collect() } -fn g_line_to_w_linestring(g_line: &geo_types::Line) -> LineString { +fn g_line_to_w_linestring(g_line: &geo_types::Line) -> LineString +where + T: num_traits::Float +{ g_points_to_w_linestring(&vec![g_line.start, g_line.end]) } -fn g_linestring_to_w_linestring(g_linestring: &geo_types::LineString) -> LineString { +fn g_linestring_to_w_linestring(g_linestring: &geo_types::LineString) -> LineString +where + T: num_traits::Float +{ let &geo_types::LineString(ref g_points) = g_linestring; g_points_to_w_linestring(g_points) } -fn g_points_to_w_linestring(g_coords: &[geo_types::Coordinate]) -> LineString { +fn g_points_to_w_linestring(g_coords: &[geo_types::Coordinate]) -> LineString +where + T: num_traits::Float +{ let w_coords = g_points_to_w_coords(g_coords); LineString(w_coords) } -fn g_lines_to_w_lines(g_lines: &[geo_types::LineString]) -> Vec { +fn g_lines_to_w_lines(g_lines: &[geo_types::LineString]) -> Vec> +where + T: num_traits::Float +{ let mut w_lines = vec![]; for g_line in g_lines { let &geo_types::LineString(ref g_points) = g_line; @@ -67,7 +95,10 @@ fn g_lines_to_w_lines(g_lines: &[geo_types::LineString]) -> Vec w_lines } -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: num_traits::Float +{ let outer_line = g_polygon.exterior(); let inner_lines = g_polygon.interiors(); let mut poly_lines = vec![]; @@ -85,19 +116,28 @@ fn g_polygon_to_w_polygon(g_polygon: &geo_types::Polygon) -> Polygon { Polygon(poly_lines) } -fn g_mpoint_to_w_mpoint(g_mpoint: &geo_types::MultiPoint) -> MultiPoint { +fn g_mpoint_to_w_mpoint(g_mpoint: &geo_types::MultiPoint) -> MultiPoint +where + T: num_traits::Float +{ let &geo_types::MultiPoint(ref g_points) = g_mpoint; let w_points = g_points_to_w_points(g_points); MultiPoint(w_points) } -fn g_mline_to_w_mline(g_mline: &geo_types::MultiLineString) -> MultiLineString { +fn g_mline_to_w_mline(g_mline: &geo_types::MultiLineString) -> MultiLineString +where + T: num_traits::Float +{ let &geo_types::MultiLineString(ref g_lines) = g_mline; let w_lines = g_lines_to_w_lines(g_lines); MultiLineString(w_lines) } -fn g_polygons_to_w_polygons(g_polygons: &[geo_types::Polygon]) -> Vec { +fn g_polygons_to_w_polygons(g_polygons: &[geo_types::Polygon]) -> Vec> +where + T: num_traits::Float +{ let mut w_polygons = vec![]; for g_polygon in g_polygons { w_polygons.push(g_polygon_to_w_polygon(g_polygon)); @@ -105,13 +145,19 @@ fn g_polygons_to_w_polygons(g_polygons: &[geo_types::Polygon]) -> Vec) -> MultiPolygon { +fn g_mpolygon_to_w_mpolygon(g_mpolygon: &geo_types::MultiPolygon) -> MultiPolygon +where + T: num_traits::Float +{ let &geo_types::MultiPolygon(ref g_polygons) = g_mpolygon; let w_polygons = g_polygons_to_w_polygons(g_polygons); MultiPolygon(w_polygons) } -fn g_geocol_to_w_geocol(g_geocol: &geo_types::GeometryCollection) -> GeometryCollection { +fn g_geocol_to_w_geocol(g_geocol: &geo_types::GeometryCollection) -> GeometryCollection +where + T: num_traits::Float +{ let &geo_types::GeometryCollection(ref g_geoms) = g_geocol; let mut w_geoms = vec![]; for g_geom in g_geoms { @@ -121,7 +167,10 @@ fn g_geocol_to_w_geocol(g_geocol: &geo_types::GeometryCollection) -> Geomet GeometryCollection(w_geoms) } -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: num_traits::Float +{ match g_geom { &geo_types::Geometry::Point(ref g_point) => g_point_to_w_point(g_point).as_item(), @@ -147,8 +196,11 @@ fn g_geom_to_w_geom(g_geom: &geo_types::Geometry) -> Geometry { } } -impl ToWkt for geo_types::Geometry { - fn to_wkt(&self) -> Wkt { +impl ToWkt for geo_types::Geometry +where + T: num_traits::Float +{ + fn to_wkt(&self) -> Wkt { let w_geom = g_geom_to_w_geom(&self); Wkt { items: vec![w_geom], diff --git a/src/types/coord.rs b/src/types/coord.rs index f3c8d76..d692314 100644 --- a/src/types/coord.rs +++ b/src/types/coord.rs @@ -12,19 +12,28 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate num_traits; + use std::fmt; +use std::str::FromStr; use tokenizer::{PeekableTokens, Token}; use FromTokens; #[derive(Default)] -pub struct Coord { - pub x: f64, - pub y: f64, - pub z: Option, - pub m: Option, +pub struct Coord +where + T: num_traits::Float +{ + pub x: T, + pub y: T, + pub z: Option, + pub m: Option, } -impl fmt::Display for Coord { +impl fmt::Display for Coord +where + T: num_traits::Float + fmt::Display +{ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(f, "{} {}", self.x, self.y)?; if let Some(z) = self.z { @@ -37,8 +46,11 @@ impl fmt::Display for Coord { } } -impl FromTokens for Coord { - fn from_tokens(tokens: &mut PeekableTokens) -> Result { +impl FromTokens for Coord +where + T: num_traits::Float + FromStr + Default +{ + fn from_tokens(tokens: &mut PeekableTokens) -> Result { let x = match tokens.next() { Some(Token::Number(n)) => n, _ => return Err("Expected a number for the X coordinate"), diff --git a/src/types/geometrycollection.rs b/src/types/geometrycollection.rs index bd62922..4bae6ce 100644 --- a/src/types/geometrycollection.rs +++ b/src/types/geometrycollection.rs @@ -12,21 +12,30 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate num_traits; + use std::fmt; +use std::str::FromStr; use tokenizer::{PeekableTokens, Token}; use FromTokens; use Geometry; #[derive(Default)] -pub struct GeometryCollection(pub Vec); +pub struct GeometryCollection(pub Vec>); -impl GeometryCollection { - pub fn as_item(self) -> Geometry { +impl GeometryCollection +where + T: num_traits::Float +{ + pub fn as_item(self) -> Geometry { Geometry::GeometryCollection(self) } } -impl fmt::Display for GeometryCollection { +impl fmt::Display for GeometryCollection +where + T: num_traits::Float + fmt::Display +{ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { if self.0.is_empty() { f.write_str("GEOMETRYCOLLECTION EMPTY") @@ -43,8 +52,11 @@ impl fmt::Display for GeometryCollection { } } -impl FromTokens for GeometryCollection { - fn from_tokens(tokens: &mut PeekableTokens) -> Result { +impl FromTokens for GeometryCollection +where + T: num_traits::Float + FromStr + Default +{ + fn from_tokens(tokens: &mut PeekableTokens) -> Result { let mut items = Vec::new(); let word = match tokens.next() { @@ -79,7 +91,7 @@ mod tests { #[test] fn basic_geometrycollection() { - let mut wkt = Wkt::from_str("GEOMETRYCOLLECTION (POINT (8 4)))") + let mut wkt: Wkt = Wkt::from_str("GEOMETRYCOLLECTION (POINT (8 4)))") .ok() .unwrap(); assert_eq!(1, wkt.items.len()); @@ -92,7 +104,7 @@ mod tests { #[test] fn complex_geometrycollection() { - let mut wkt = Wkt::from_str("GEOMETRYCOLLECTION (POINT (8 4),LINESTRING(4 6,7 10)))") + let mut wkt: Wkt = Wkt::from_str("GEOMETRYCOLLECTION (POINT (8 4),LINESTRING(4 6,7 10)))") .ok() .unwrap(); assert_eq!(1, wkt.items.len()); @@ -105,7 +117,7 @@ mod tests { #[test] fn write_empty_geometry_collection() { - let geometry_collection = GeometryCollection(vec![]); + let geometry_collection: GeometryCollection = GeometryCollection(vec![]); assert_eq!( "GEOMETRYCOLLECTION EMPTY", diff --git a/src/types/linestring.rs b/src/types/linestring.rs index f08149b..d2b10f0 100644 --- a/src/types/linestring.rs +++ b/src/types/linestring.rs @@ -12,29 +12,41 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate num_traits; + use std::fmt; +use std::str::FromStr; use tokenizer::PeekableTokens; use types::coord::Coord; use FromTokens; use Geometry; #[derive(Default)] -pub struct LineString(pub Vec); +pub struct LineString(pub Vec>); -impl LineString { - pub fn as_item(self) -> Geometry { +impl LineString +where + T: num_traits::Float +{ + pub fn as_item(self) -> Geometry { Geometry::LineString(self) } } -impl FromTokens for LineString { - fn from_tokens(tokens: &mut PeekableTokens) -> Result { - let result = FromTokens::comma_many(::from_tokens, tokens); +impl FromTokens for LineString +where + T: num_traits::Float + FromStr + Default +{ + fn from_tokens(tokens: &mut PeekableTokens) -> Result { + let result = FromTokens::comma_many( as FromTokens>::from_tokens, tokens); result.map(LineString) } } -impl fmt::Display for LineString { +impl fmt::Display for LineString +where + T: num_traits::Float + fmt::Display +{ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { if self.0.is_empty() { f.write_str("LINESTRING EMPTY") @@ -79,7 +91,7 @@ mod tests { #[test] fn write_empty_linestring() { - let linestring = LineString(vec![]); + let linestring: LineString = LineString(vec![]); assert_eq!("LINESTRING EMPTY", format!("{}", linestring)); } diff --git a/src/types/multilinestring.rs b/src/types/multilinestring.rs index 8cf3b74..0a32895 100644 --- a/src/types/multilinestring.rs +++ b/src/types/multilinestring.rs @@ -12,22 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate num_traits; + use std::fmt; +use std::str::FromStr; use tokenizer::PeekableTokens; use types::linestring::LineString; use FromTokens; use Geometry; #[derive(Default)] -pub struct MultiLineString(pub Vec); +pub struct MultiLineString(pub Vec>); -impl MultiLineString { - pub fn as_item(self) -> Geometry { +impl MultiLineString +where + T: num_traits::Float +{ + pub fn as_item(self) -> Geometry { Geometry::MultiLineString(self) } } -impl fmt::Display for MultiLineString { +impl fmt::Display for MultiLineString +where + T: num_traits::Float + fmt::Display +{ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { if self.0.is_empty() { f.write_str("MULTILINESTRING EMPTY") @@ -48,10 +57,13 @@ impl fmt::Display for MultiLineString { } } -impl FromTokens for MultiLineString { - fn from_tokens(tokens: &mut PeekableTokens) -> Result { +impl FromTokens for MultiLineString +where + T: num_traits::Float + FromStr + Default +{ + fn from_tokens(tokens: &mut PeekableTokens) -> Result { let result = - FromTokens::comma_many(::from_tokens_with_parens, tokens); + FromTokens::comma_many( as FromTokens>::from_tokens_with_parens, tokens); result.map(MultiLineString) } } @@ -64,7 +76,7 @@ mod tests { #[test] fn basic_multilinestring() { - let mut wkt = Wkt::from_str("MULTILINESTRING ((8 4, -3 0), (4 0, 6 -10))") + let mut wkt: Wkt = Wkt::from_str("MULTILINESTRING ((8 4, -3 0), (4 0, 6 -10))") .ok() .unwrap(); assert_eq!(1, wkt.items.len()); @@ -77,7 +89,7 @@ mod tests { #[test] fn write_empty_multilinestring() { - let multilinestring = MultiLineString(vec![]); + let multilinestring: MultiLineString = MultiLineString(vec![]); assert_eq!("MULTILINESTRING EMPTY", format!("{}", multilinestring)); } diff --git a/src/types/multipoint.rs b/src/types/multipoint.rs index b85d5ab..191d5b5 100644 --- a/src/types/multipoint.rs +++ b/src/types/multipoint.rs @@ -12,22 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate num_traits; + use std::fmt; +use std::str::FromStr; use tokenizer::PeekableTokens; use types::point::Point; use FromTokens; use Geometry; #[derive(Default)] -pub struct MultiPoint(pub Vec); +pub struct MultiPoint(pub Vec>); -impl MultiPoint { - pub fn as_item(self) -> Geometry { +impl MultiPoint +where + T: num_traits::Float +{ + pub fn as_item(self) -> Geometry { Geometry::MultiPoint(self) } } -impl fmt::Display for MultiPoint { +impl fmt::Display for MultiPoint +where + T: num_traits::Float + fmt::Display +{ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { if self.0.is_empty() { f.write_str("MULTIPOINT EMPTY") @@ -45,9 +54,12 @@ impl fmt::Display for MultiPoint { } } -impl FromTokens for MultiPoint { - fn from_tokens(tokens: &mut PeekableTokens) -> Result { - let result = FromTokens::comma_many(::from_tokens_with_parens, tokens); +impl FromTokens for MultiPoint +where + T: num_traits::Float + FromStr + Default +{ + fn from_tokens(tokens: &mut PeekableTokens) -> Result { + let result = FromTokens::comma_many( as FromTokens>::from_tokens_with_parens, tokens); result.map(MultiPoint) } } @@ -60,7 +72,7 @@ mod tests { #[test] fn basic_multipoint() { - let mut wkt = Wkt::from_str("MULTIPOINT ((8 4), (4 0))").ok().unwrap(); + let mut wkt: Wkt = Wkt::from_str("MULTIPOINT ((8 4), (4 0))").ok().unwrap(); assert_eq!(1, wkt.items.len()); let points = match wkt.items.pop().unwrap() { Geometry::MultiPoint(MultiPoint(points)) => points, @@ -71,7 +83,7 @@ mod tests { #[test] fn write_empty_multipoint() { - let multipoint = MultiPoint(vec![]); + let multipoint: MultiPoint = MultiPoint(vec![]); assert_eq!("MULTIPOINT EMPTY", format!("{}", multipoint)); } diff --git a/src/types/multipolygon.rs b/src/types/multipolygon.rs index 62723c9..1642d21 100644 --- a/src/types/multipolygon.rs +++ b/src/types/multipolygon.rs @@ -12,22 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate num_traits; + use std::fmt; +use std::str::FromStr; use tokenizer::PeekableTokens; use types::polygon::Polygon; use FromTokens; use Geometry; #[derive(Default)] -pub struct MultiPolygon(pub Vec); +pub struct MultiPolygon(pub Vec>); -impl MultiPolygon { - pub fn as_item(self) -> Geometry { +impl MultiPolygon +where + T: num_traits::Float +{ + pub fn as_item(self) -> Geometry { Geometry::MultiPolygon(self) } } -impl fmt::Display for MultiPolygon { +impl fmt::Display for MultiPolygon +where + T: num_traits::Float + fmt::Display +{ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { if self.0.is_empty() { f.write_str("MULTIPOLYGON EMPTY") @@ -52,10 +61,13 @@ impl fmt::Display for MultiPolygon { } } -impl FromTokens for MultiPolygon { - fn from_tokens(tokens: &mut PeekableTokens) -> Result { +impl FromTokens for MultiPolygon +where + T: num_traits::Float + FromStr + Default +{ + fn from_tokens(tokens: &mut PeekableTokens) -> Result { let result = - FromTokens::comma_many(::from_tokens_with_parens, tokens); + FromTokens::comma_many( as FromTokens>::from_tokens_with_parens, tokens); result.map(MultiPolygon) } } @@ -68,7 +80,7 @@ mod tests { #[test] fn basic_multipolygon() { - let mut wkt = Wkt::from_str("MULTIPOLYGON (((8 4)), ((4 0)))") + let mut wkt: Wkt = Wkt::from_str("MULTIPOLYGON (((8 4)), ((4 0)))") .ok() .unwrap(); assert_eq!(1, wkt.items.len()); @@ -81,7 +93,7 @@ mod tests { #[test] fn write_empty_multipolygon() { - let multipolygon = MultiPolygon(vec![]); + let multipolygon: MultiPolygon = MultiPolygon(vec![]); assert_eq!("MULTIPOLYGON EMPTY", format!("{}", multipolygon)); } diff --git a/src/types/point.rs b/src/types/point.rs index 0b8bbca..02deced 100644 --- a/src/types/point.rs +++ b/src/types/point.rs @@ -12,22 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate num_traits; + use std::fmt; +use std::str::FromStr; use tokenizer::PeekableTokens; use types::coord::Coord; use FromTokens; use Geometry; #[derive(Default)] -pub struct Point(pub Option); +pub struct Point(pub Option>); -impl Point { - pub fn as_item(self) -> Geometry { +impl Point +where + T: num_traits::Float +{ + pub fn as_item(self) -> Geometry { Geometry::Point(self) } } -impl fmt::Display for Point { +impl fmt::Display for Point +where + T: num_traits::Float + fmt::Display +{ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self.0 { Some(ref coord) => { @@ -49,9 +58,12 @@ impl fmt::Display for Point { } } -impl FromTokens for Point { - fn from_tokens(tokens: &mut PeekableTokens) -> Result { - let result = ::from_tokens(tokens); +impl FromTokens for Point +where + T: num_traits::Float + FromStr + Default +{ + fn from_tokens(tokens: &mut PeekableTokens) -> Result { + let result = as FromTokens>::from_tokens(tokens); result.map(|coord| Point(Some(coord))) } } @@ -77,7 +89,7 @@ mod tests { #[test] fn basic_point_whitespace() { - let mut wkt = Wkt::from_str(" \n\t\rPOINT \n\t\r( \n\r\t10 \n\t\r-20 \n\t\r) \n\t\r") + let mut wkt: Wkt = Wkt::from_str(" \n\t\rPOINT \n\t\r( \n\r\t10 \n\t\r-20 \n\t\r) \n\t\r") .ok() .unwrap(); assert_eq!(1, wkt.items.len()); @@ -93,15 +105,15 @@ mod tests { #[test] fn invalid_points() { - Wkt::from_str("POINT ()").err().unwrap(); - Wkt::from_str("POINT (10)").err().unwrap(); - Wkt::from_str("POINT 10").err().unwrap(); - Wkt::from_str("POINT (10 -20 40)").err().unwrap(); + >::from_str("POINT ()").err().unwrap(); + >::from_str("POINT (10)").err().unwrap(); + >::from_str("POINT 10").err().unwrap(); + >::from_str("POINT (10 -20 40)").err().unwrap(); } #[test] fn write_empty_point() { - let point = Point(None); + let point: Point = Point(None); assert_eq!("POINT EMPTY", format!("{}", point)); } diff --git a/src/types/polygon.rs b/src/types/polygon.rs index 804ff63..5a5d7c4 100644 --- a/src/types/polygon.rs +++ b/src/types/polygon.rs @@ -12,22 +12,31 @@ // See the License for the specific language governing permissions and // limitations under the License. +extern crate num_traits; + use std::fmt; +use std::str::FromStr; use tokenizer::PeekableTokens; use types::linestring::LineString; use FromTokens; use Geometry; #[derive(Default)] -pub struct Polygon(pub Vec); +pub struct Polygon(pub Vec>); -impl Polygon { - pub fn as_item(self) -> Geometry { +impl Polygon +where + T: num_traits::Float +{ + pub fn as_item(self) -> Geometry { Geometry::Polygon(self) } } -impl fmt::Display for Polygon { +impl fmt::Display for Polygon +where + T: num_traits::Float + fmt::Display +{ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { if self.0.is_empty() { f.write_str("POLYGON EMPTY") @@ -48,10 +57,13 @@ impl fmt::Display for Polygon { } } -impl FromTokens for Polygon { - fn from_tokens(tokens: &mut PeekableTokens) -> Result { +impl FromTokens for Polygon +where + T: num_traits::Float + FromStr + Default +{ + fn from_tokens(tokens: &mut PeekableTokens) -> Result { let result = - FromTokens::comma_many(::from_tokens_with_parens, tokens); + FromTokens::comma_many( as FromTokens>::from_tokens_with_parens, tokens); result.map(Polygon) } } @@ -64,7 +76,7 @@ mod tests { #[test] fn basic_polygon() { - let mut wkt = Wkt::from_str("POLYGON ((8 4, 4 0, 0 4, 8 4), (7 3, 4 1, 1 4, 7 3))") + let mut wkt: Wkt = Wkt::from_str("POLYGON ((8 4, 4 0, 0 4, 8 4), (7 3, 4 1, 1 4, 7 3))") .ok() .unwrap(); assert_eq!(1, wkt.items.len()); @@ -77,7 +89,7 @@ mod tests { #[test] fn write_empty_polygon() { - let polygon = Polygon(vec![]); + let polygon: Polygon = Polygon(vec![]); assert_eq!("POLYGON EMPTY", format!("{}", polygon)); }