diff --git a/CHANGELOG.md b/CHANGELOG.md index 82d0bb1..4aa23fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Don't panic when parsing invalid WKB (#74). - Fix CI by removing georust container & fix clippy lint (#78) +- Remove trait wrappers to work around Rust 1.90 compiler regression (#77) - Add lint warning for missing docs #80 - Remove unnecessary `unwrap`s #79 - Expose `Dimension` publicly #82 diff --git a/src/writer/line.rs b/src/writer/line.rs index 582a765..72d7d7c 100644 --- a/src/writer/line.rs +++ b/src/writer/line.rs @@ -1,40 +1,18 @@ -use std::io::Write; - -use geo_traits::{ - GeometryTrait, LineStringTrait, LineTrait, UnimplementedGeometryCollection, UnimplementedLine, - UnimplementedMultiLineString, UnimplementedMultiPoint, UnimplementedMultiPolygon, - UnimplementedPoint, UnimplementedPolygon, UnimplementedRect, UnimplementedTriangle, -}; - +use crate::common::WkbType; use crate::error::WkbResult; -use crate::writer::{line_string_wkb_size, write_line_string, WriteOptions}; - -/// A wrapper around an impl LineTrait to provide LineStringTrait -struct LineWrapper<'a, G: LineTrait>(&'a G); - -impl<'a, G: LineTrait> LineStringTrait for LineWrapper<'a, G> { - type CoordType<'b> - = G::CoordType<'a> - where - G: 'b, - Self: 'b; - - fn num_coords(&self) -> usize { - 2 - } - - unsafe fn coord_unchecked(&self, i: usize) -> Self::CoordType<'_> { - match i { - 0 => self.0.start(), - 1 => self.0.end(), - _ => unreachable!(), - } - } -} +use crate::writer::coord::write_coord; +use crate::writer::WriteOptions; +use crate::Endianness; +use byteorder::{BigEndian, ByteOrder, LittleEndian, WriteBytesExt}; +use geo_traits::LineTrait; +use std::io::Write; /// The number of bytes this Line will take up when encoded as WKB pub fn line_wkb_size(geom: &impl LineTrait) -> usize { - line_string_wkb_size(&LineWrapper(geom)) + let header = 1 + 4 + 4; + let each_coord = geom.dim().size() * 8; + let all_coords = 2 * each_coord; + header + all_coords } /// Write a Line geometry to a Writer encoded as WKB @@ -43,71 +21,29 @@ pub fn write_line( geom: &impl LineTrait, options: &WriteOptions, ) -> WkbResult<()> { - write_line_string(writer, &LineWrapper(geom), options) + // Byte order + writer.write_u8(options.endianness.into()).unwrap(); + + // Content + match options.endianness { + Endianness::LittleEndian => write_line_content::(writer, geom), + Endianness::BigEndian => write_line_content::(writer, geom), + } } -impl> GeometryTrait for LineWrapper<'_, G> { - type T = f64; - type PointType<'b> - = UnimplementedPoint - where - Self: 'b; - type LineStringType<'b> - = Self - where - Self: 'b; - type PolygonType<'b> - = UnimplementedPolygon - where - Self: 'b; - type MultiPointType<'b> - = UnimplementedMultiPoint - where - Self: 'b; - type MultiLineStringType<'b> - = UnimplementedMultiLineString - where - Self: 'b; - type MultiPolygonType<'b> - = UnimplementedMultiPolygon - where - Self: 'b; - type GeometryCollectionType<'b> - = UnimplementedGeometryCollection - where - Self: 'b; - type RectType<'b> - = UnimplementedRect - where - Self: 'b; - type LineType<'b> - = UnimplementedLine - where - Self: 'b; - type TriangleType<'b> - = UnimplementedTriangle - where - Self: 'b; +fn write_line_content( + writer: &mut impl Write, + geom: &impl LineTrait, +) -> WkbResult<()> { + let wkb_type = WkbType::LineString(geom.dim().try_into()?); + writer.write_u32::(wkb_type.into())?; - fn dim(&self) -> geo_traits::Dimensions { - self.0.dim() - } + // numPoints + writer.write_u32::(2).unwrap(); - fn as_type( - &self, - ) -> geo_traits::GeometryType< - '_, - Self::PointType<'_>, - Self::LineStringType<'_>, - Self::PolygonType<'_>, - Self::MultiPointType<'_>, - Self::MultiLineStringType<'_>, - Self::MultiPolygonType<'_>, - Self::GeometryCollectionType<'_>, - Self::RectType<'_>, - Self::TriangleType<'_>, - Self::LineType<'_>, - > { - geo_traits::GeometryType::LineString(self) + for coord in geom.coords() { + write_coord::(writer, &coord)?; } + + Ok(()) } diff --git a/src/writer/rect.rs b/src/writer/rect.rs index d92c7ba..6d113a4 100644 --- a/src/writer/rect.rs +++ b/src/writer/rect.rs @@ -1,250 +1,92 @@ -use std::io::Write; - -use geo_traits::{ - CoordTrait, GeometryTrait, LineStringTrait, PolygonTrait, RectTrait, - UnimplementedGeometryCollection, UnimplementedLine, UnimplementedLineString, - UnimplementedMultiLineString, UnimplementedMultiPoint, UnimplementedMultiPolygon, - UnimplementedPoint, UnimplementedPolygon, UnimplementedRect, UnimplementedTriangle, -}; - +use crate::common::WkbType; use crate::error::WkbResult; -use crate::writer::{polygon_wkb_size, write_polygon, WriteOptions}; - -struct Coord2D { - x: f64, - y: f64, -} - -impl CoordTrait for Coord2D { - type T = f64; - - fn x(&self) -> Self::T { - self.x - } - - fn y(&self) -> Self::T { - self.y - } - - fn dim(&self) -> geo_traits::Dimensions { - geo_traits::Dimensions::Xy - } - - fn nth_or_panic(&self, n: usize) -> Self::T { - match n { - 0 => self.x, - 1 => self.y, - _ => panic!(), - } - } -} - -/// A wrapper around an impl RectTrait to provide LineStringTrait and PolygonTrait -struct RectWrapper<'a, G: RectTrait>(&'a G); - -impl<'a, G: RectTrait> LineStringTrait for &'a RectWrapper<'a, G> { - type CoordType<'b> - = Coord2D - where - G: 'b, - Self: 'b; - - fn num_coords(&self) -> usize { - 5 - } - - unsafe fn coord_unchecked(&self, i: usize) -> Self::CoordType<'_> { - let min_coord = self.0.min(); - let max_coord = self.0.max(); - - match i { - 0 => Coord2D { - x: min_coord.x(), - y: min_coord.y(), - }, - 1 => Coord2D { - x: min_coord.x(), - y: max_coord.y(), - }, - 2 => Coord2D { - x: max_coord.x(), - y: max_coord.y(), - }, - 3 => Coord2D { - x: max_coord.x(), - y: min_coord.y(), - }, - 4 => Coord2D { - x: min_coord.x(), - y: min_coord.y(), - }, - _ => unreachable!(), - } - } -} - -impl> PolygonTrait for RectWrapper<'_, G> { - type RingType<'b> - = &'b RectWrapper<'b, G> - where - G: 'b, - Self: 'b; - - fn exterior(&self) -> Option> { - Some(self) - } - - fn num_interiors(&self) -> usize { - 0 - } - - unsafe fn interior_unchecked(&self, _i: usize) -> Self::RingType<'_> { - unreachable!() - } -} +use crate::writer::WriteOptions; +use crate::Endianness; +use byteorder::{BigEndian, ByteOrder, LittleEndian, WriteBytesExt}; +use geo_traits::{CoordTrait, RectTrait}; +use std::io::Write; /// The number of bytes this Rect will take up when encoded as WKB +/// +/// Note that only 2D Rects are supported. Even if the input Rect has more than 2 dimensions, only +/// the X and Y dimensions will be written. pub fn rect_wkb_size(geom: &impl RectTrait) -> usize { - polygon_wkb_size(&RectWrapper(geom)) + let header = 1 + 4 + 4; + let each_coord = geom.dim().size() * 8; + let all_coords = 5 * each_coord; + header + all_coords } /// Write a Rect geometry to a Writer encoded as WKB +/// +/// Note that only 2D Rects are supported. Even if the input Rect has more than 2 dimensions, only +/// the X and Y dimensions will be written. pub fn write_rect( writer: &mut impl Write, geom: &impl RectTrait, options: &WriteOptions, ) -> WkbResult<()> { - write_polygon(writer, &RectWrapper(geom), options) -} - -impl<'a, G: RectTrait> GeometryTrait for RectWrapper<'a, G> { - type T = f64; - type PointType<'b> - = UnimplementedPoint - where - Self: 'b; - type LineStringType<'b> - = UnimplementedLineString - where - Self: 'b; - type PolygonType<'b> - = Self - where - Self: 'b; - type MultiPointType<'b> - = UnimplementedMultiPoint - where - Self: 'b; - type MultiLineStringType<'b> - = UnimplementedMultiLineString - where - Self: 'b; - type MultiPolygonType<'b> - = UnimplementedMultiPolygon - where - Self: 'b; - type GeometryCollectionType<'b> - = UnimplementedGeometryCollection - where - Self: 'b; - type RectType<'b> - = UnimplementedRect - where - Self: 'b; - type LineType<'b> - = UnimplementedLine - where - Self: 'b; - type TriangleType<'b> - = UnimplementedTriangle - where - Self: 'b; + // Byte order + writer.write_u8(options.endianness.into())?; - fn dim(&self) -> geo_traits::Dimensions { - geo_traits::Dimensions::Xy - } - - fn as_type( - &self, - ) -> geo_traits::GeometryType< - '_, - Self::PointType<'a>, - Self::LineStringType<'a>, - Self::PolygonType<'a>, - Self::MultiPointType<'a>, - Self::MultiLineStringType<'a>, - Self::MultiPolygonType<'a>, - Self::GeometryCollectionType<'a>, - Self::RectType<'_>, - Self::TriangleType<'_>, - Self::LineType<'_>, - > { - geo_traits::GeometryType::Polygon(self) + // Content + match options.endianness { + Endianness::LittleEndian => write_rect_content::(writer, geom), + Endianness::BigEndian => write_rect_content::(writer, geom), } } -impl<'a, G: RectTrait> GeometryTrait for &'a RectWrapper<'a, G> { - type T = f64; - type PointType<'b> - = UnimplementedPoint - where - Self: 'b; - type LineStringType<'b> - = Self - where - Self: 'b; - type PolygonType<'b> - = UnimplementedPolygon - where - Self: 'b; - type MultiPointType<'b> - = UnimplementedMultiPoint - where - Self: 'b; - type MultiLineStringType<'b> - = UnimplementedMultiLineString - where - Self: 'b; - type MultiPolygonType<'b> - = UnimplementedMultiPolygon - where - Self: 'b; - type GeometryCollectionType<'b> - = UnimplementedGeometryCollection - where - Self: 'b; - type RectType<'b> - = UnimplementedRect - where - Self: 'b; - type LineType<'b> - = UnimplementedLine - where - Self: 'b; - type TriangleType<'b> - = UnimplementedTriangle - where - Self: 'b; - - fn dim(&self) -> geo_traits::Dimensions { - geo_traits::Dimensions::Xy - } +/// Minimal struct to hold a named coordinate pair +struct Coord { + x: f64, + y: f64, +} - fn as_type( - &self, - ) -> geo_traits::GeometryType< - '_, - Self::PointType<'a>, - Self::LineStringType<'a>, - Self::PolygonType<'a>, - Self::MultiPointType<'a>, - Self::MultiLineStringType<'a>, - Self::MultiPolygonType<'a>, - Self::GeometryCollectionType<'a>, - Self::RectType<'_>, - Self::TriangleType<'_>, - Self::LineType<'_>, - > { - geo_traits::GeometryType::LineString(self) - } +fn write_rect_content( + writer: &mut impl Write, + geom: &impl RectTrait, +) -> WkbResult<()> { + let wkb_type = WkbType::Polygon(geom.dim().try_into()?); + writer.write_u32::(wkb_type.into())?; + + // numRings + let num_rings = 1; + writer.write_u32::(num_rings)?; + + let min_coord = geom.min(); + let max_coord = geom.max(); + + let ll = Coord { + x: min_coord.x(), + y: min_coord.y(), + }; + let ul = Coord { + x: min_coord.x(), + y: max_coord.y(), + }; + let ur = Coord { + x: max_coord.x(), + y: max_coord.y(), + }; + let lr = Coord { + x: max_coord.x(), + y: min_coord.y(), + }; + + writer.write_f64::(ll.x)?; + writer.write_f64::(ll.y)?; + + writer.write_f64::(ul.x)?; + writer.write_f64::(ul.y)?; + + writer.write_f64::(ur.x)?; + writer.write_f64::(ur.y)?; + + writer.write_f64::(lr.x)?; + writer.write_f64::(lr.y)?; + + writer.write_f64::(ll.x)?; + writer.write_f64::(ll.y)?; + + Ok(()) } diff --git a/src/writer/triangle.rs b/src/writer/triangle.rs index a96ffa5..42def5f 100644 --- a/src/writer/triangle.rs +++ b/src/writer/triangle.rs @@ -1,62 +1,18 @@ -use std::io::Write; - -use geo_traits::{ - GeometryTrait, LineStringTrait, PolygonTrait, TriangleTrait, UnimplementedGeometryCollection, - UnimplementedLine, UnimplementedLineString, UnimplementedMultiLineString, - UnimplementedMultiPoint, UnimplementedMultiPolygon, UnimplementedPoint, UnimplementedPolygon, - UnimplementedRect, UnimplementedTriangle, -}; - +use crate::common::WkbType; use crate::error::WkbResult; -use crate::writer::{polygon_wkb_size, write_polygon, WriteOptions}; - -/// A wrapper around an impl TriangleTrait to provide LineStringTrait and PolygonTrait -struct TriangleWrapper<'a, G: TriangleTrait>(&'a G); - -impl<'a, G: TriangleTrait> LineStringTrait for &'a TriangleWrapper<'a, G> { - type CoordType<'b> - = G::CoordType<'a> - where - G: 'b, - Self: 'b; - - fn num_coords(&self) -> usize { - 3 - } - - unsafe fn coord_unchecked(&self, i: usize) -> Self::CoordType<'_> { - match i { - 0 => self.0.first(), - 1 => self.0.second(), - 2 => self.0.third(), - _ => unreachable!(), - } - } -} - -impl> PolygonTrait for TriangleWrapper<'_, G> { - type RingType<'b> - = &'b TriangleWrapper<'b, G> - where - G: 'b, - Self: 'b; - - fn exterior(&self) -> Option> { - Some(self) - } - - fn num_interiors(&self) -> usize { - 0 - } - - unsafe fn interior_unchecked(&self, _i: usize) -> Self::RingType<'_> { - unreachable!() - } -} +use crate::writer::coord::write_coord; +use crate::writer::WriteOptions; +use crate::Endianness; +use byteorder::{BigEndian, ByteOrder, LittleEndian, WriteBytesExt}; +use geo_traits::TriangleTrait; +use std::io::Write; /// The number of bytes this Triangle will take up when encoded as WKB pub fn triangle_wkb_size(geom: &impl TriangleTrait) -> usize { - polygon_wkb_size(&TriangleWrapper(geom)) + let header = 1 + 4 + 4; + let each_coord = geom.dim().size() * 8; + let all_coords = 4 * each_coord; + header + all_coords } /// Write a Triangle geometry to a Writer encoded as WKB @@ -65,137 +21,34 @@ pub fn write_triangle( geom: &impl TriangleTrait, options: &WriteOptions, ) -> WkbResult<()> { - write_polygon(writer, &TriangleWrapper(geom), options) -} - -impl> GeometryTrait for TriangleWrapper<'_, G> { - type T = f64; - type PointType<'b> - = UnimplementedPoint - where - Self: 'b; - type LineStringType<'b> - = UnimplementedLineString - where - Self: 'b; - type PolygonType<'b> - = Self - where - Self: 'b; - type MultiPointType<'b> - = UnimplementedMultiPoint - where - Self: 'b; - type MultiLineStringType<'b> - = UnimplementedMultiLineString - where - Self: 'b; - type MultiPolygonType<'b> - = UnimplementedMultiPolygon - where - Self: 'b; - type GeometryCollectionType<'b> - = UnimplementedGeometryCollection - where - Self: 'b; - type RectType<'b> - = UnimplementedRect - where - Self: 'b; - type LineType<'b> - = UnimplementedLine - where - Self: 'b; - type TriangleType<'b> - = UnimplementedTriangle - where - Self: 'b; - - fn dim(&self) -> geo_traits::Dimensions { - geo_traits::Dimensions::Xy - } + // Byte order + writer.write_u8(options.endianness.into())?; - fn as_type( - &self, - ) -> geo_traits::GeometryType< - '_, - Self::PointType<'_>, - Self::LineStringType<'_>, - Self::PolygonType<'_>, - Self::MultiPointType<'_>, - Self::MultiLineStringType<'_>, - Self::MultiPolygonType<'_>, - Self::GeometryCollectionType<'_>, - Self::RectType<'_>, - Self::TriangleType<'_>, - Self::LineType<'_>, - > { - geo_traits::GeometryType::Polygon(self) + // Content + match options.endianness { + Endianness::LittleEndian => write_triangle_content::(writer, geom), + Endianness::BigEndian => write_triangle_content::(writer, geom), } } -impl<'a, G: TriangleTrait> GeometryTrait for &'a TriangleWrapper<'a, G> { - type T = f64; - type PointType<'b> - = UnimplementedPoint - where - Self: 'b; - type LineStringType<'b> - = Self - where - Self: 'b; - type PolygonType<'b> - = UnimplementedPolygon - where - Self: 'b; - type MultiPointType<'b> - = UnimplementedMultiPoint - where - Self: 'b; - type MultiLineStringType<'b> - = UnimplementedMultiLineString - where - Self: 'b; - type MultiPolygonType<'b> - = UnimplementedMultiPolygon - where - Self: 'b; - type GeometryCollectionType<'b> - = UnimplementedGeometryCollection - where - Self: 'b; - type RectType<'b> - = UnimplementedRect - where - Self: 'b; - type LineType<'b> - = UnimplementedLine - where - Self: 'b; - type TriangleType<'b> - = UnimplementedTriangle - where - Self: 'b; +fn write_triangle_content( + writer: &mut impl Write, + geom: &impl TriangleTrait, +) -> WkbResult<()> { + let wkb_type = WkbType::Polygon(geom.dim().try_into()?); + writer.write_u32::(wkb_type.into())?; - fn dim(&self) -> geo_traits::Dimensions { - geo_traits::Dimensions::Xy - } + // numRings + let num_rings = 1; + writer.write_u32::(num_rings)?; - fn as_type( - &self, - ) -> geo_traits::GeometryType< - '_, - Self::PointType<'_>, - Self::LineStringType<'_>, - Self::PolygonType<'_>, - Self::MultiPointType<'_>, - Self::MultiLineStringType<'_>, - Self::MultiPolygonType<'_>, - Self::GeometryCollectionType<'_>, - Self::RectType<'_>, - Self::TriangleType<'_>, - Self::LineType<'_>, - > { - geo_traits::GeometryType::LineString(self) - } + let num_coords = 4; + writer.write_u32::(num_coords)?; + + write_coord::(writer, &geom.first())?; + write_coord::(writer, &geom.second())?; + write_coord::(writer, &geom.third())?; + write_coord::(writer, &geom.first())?; + + Ok(()) }