diff --git a/src/algorithm/native/type_id.rs b/src/algorithm/native/type_id.rs index 206032178..ffe9f361e 100644 --- a/src/algorithm/native/type_id.rs +++ b/src/algorithm/native/type_id.rs @@ -1,9 +1,8 @@ use crate::array::*; -use crate::io::wkb::reader::WKBGeometryType; use crate::trait_::GeometryArrayAccessor; use crate::GeometryArrayTrait; -use arrow_array::builder::Int8Builder; -use arrow_array::{Int8Array, OffsetSizeTrait}; +use arrow::array::Int16Builder; +use arrow_array::{Int16Array, OffsetSizeTrait}; use std::collections::HashSet; /// Calculation of the geometry types within a GeometryArray @@ -21,7 +20,7 @@ pub trait TypeIds { /// - MULTILINESTRING is 5 /// - MULTIPOLYGON is 6 /// - GEOMETRYCOLLECTION is 7 - fn get_type_ids(&self) -> Int8Array; + fn get_type_ids(&self) -> Int16Array; /// Return the unique geometry types stored in this array /// @@ -36,16 +35,16 @@ pub trait TypeIds { /// - MULTILINESTRING is 5 /// - MULTIPOLYGON is 6 /// - GEOMETRYCOLLECTION is 7 - fn get_unique_type_ids(&self) -> HashSet; + fn get_unique_type_ids(&self) -> HashSet; } impl TypeIds for PointArray<2> { - fn get_type_ids(&self) -> Int8Array { - let values = vec![0i8; self.len()]; - Int8Array::new(values.into(), self.nulls().cloned()) + fn get_type_ids(&self) -> Int16Array { + let values = vec![0i16; self.len()]; + Int16Array::new(values.into(), self.nulls().cloned()) } - fn get_unique_type_ids(&self) -> HashSet { + fn get_unique_type_ids(&self) -> HashSet { let mut values = HashSet::with_capacity(1); values.insert(0); values @@ -55,12 +54,12 @@ impl TypeIds for PointArray<2> { macro_rules! constant_impl { ($type:ty, $value:expr) => { impl TypeIds for $type { - fn get_type_ids(&self) -> Int8Array { + fn get_type_ids(&self) -> Int16Array { let values = vec![$value; self.len()]; - Int8Array::new(values.into(), self.nulls().cloned()) + Int16Array::new(values.into(), self.nulls().cloned()) } - fn get_unique_type_ids(&self) -> HashSet { + fn get_unique_type_ids(&self) -> HashSet { let mut values = HashSet::with_capacity(1); values.insert($value); values @@ -76,10 +75,10 @@ constant_impl!(MultiLineStringArray, 5); constant_impl!(MultiPolygonArray, 6); impl TypeIds for MixedGeometryArray { - fn get_type_ids(&self) -> Int8Array { + fn get_type_ids(&self) -> Int16Array { use crate::scalar::Geometry::*; - let mut output_array = Int8Builder::with_capacity(self.len()); + let mut output_array = Int16Builder::with_capacity(self.len()); self.iter().for_each(|maybe_g| { output_array.append_option(maybe_g.map(|g| match g { Point(_) => 0, @@ -95,7 +94,7 @@ impl TypeIds for MixedGeometryArray { output_array.finish() } - fn get_unique_type_ids(&self) -> HashSet { + fn get_unique_type_ids(&self) -> HashSet { use crate::scalar::Geometry::*; let mut values = HashSet::new(); @@ -118,41 +117,24 @@ impl TypeIds for MixedGeometryArray { } impl TypeIds for WKBArray { - fn get_type_ids(&self) -> Int8Array { - use WKBGeometryType::*; - - let mut output_array = Int8Builder::with_capacity(self.len()); + fn get_type_ids(&self) -> Int16Array { + let mut output_array = Int16Builder::with_capacity(self.len()); self.iter().for_each(|maybe_wkb| { - output_array.append_option(maybe_wkb.map(|wkb| match wkb.get_wkb_geometry_type() { - Point => 0, - LineString => 1, - Polygon => 3, - MultiPoint => 4, - MultiLineString => 5, - MultiPolygon => 6, - GeometryCollection => 7, + output_array.append_option(maybe_wkb.map(|wkb| { + let type_id = u32::from(wkb.get_wkb_geometry_type()); + type_id.try_into().unwrap() })) }); output_array.finish() } - fn get_unique_type_ids(&self) -> HashSet { - use WKBGeometryType::*; - + fn get_unique_type_ids(&self) -> HashSet { let mut values = HashSet::new(); self.iter().flatten().for_each(|wkb| { - let type_id = match wkb.get_wkb_geometry_type() { - Point => 0, - LineString => 1, - Polygon => 3, - MultiPoint => 4, - MultiLineString => 5, - MultiPolygon => 6, - GeometryCollection => 7, - }; - values.insert(type_id); + let type_id = u32::from(wkb.get_wkb_geometry_type()); + values.insert(type_id.try_into().unwrap()); }); values diff --git a/src/array/binary/builder.rs b/src/array/binary/builder.rs index 0be718577..65396eb72 100644 --- a/src/array/binary/builder.rs +++ b/src/array/binary/builder.rs @@ -9,10 +9,9 @@ use crate::geo_traits::{ }; use crate::io::wkb::writer::{ geometry_collection_wkb_size, line_string_wkb_size, multi_line_string_wkb_size, - multi_point_wkb_size, multi_polygon_wkb_size, polygon_wkb_size, + multi_point_wkb_size, multi_polygon_wkb_size, point_wkb_size, polygon_wkb_size, write_geometry_collection_as_wkb, write_line_string_as_wkb, write_multi_line_string_as_wkb, write_multi_point_as_wkb, write_multi_polygon_as_wkb, write_point_as_wkb, write_polygon_as_wkb, - POINT_WKB_SIZE, }; use arrow_array::builder::GenericBinaryBuilder; use arrow_array::OffsetSizeTrait; @@ -82,7 +81,7 @@ impl WKBBuilder { pub fn push_point(&mut self, geom: Option<&impl PointTrait>) { if let Some(geom) = geom { // TODO: figure out how to write directly to the underlying vec without a copy - let mut buf = Vec::with_capacity(POINT_WKB_SIZE); + let mut buf = Vec::with_capacity(point_wkb_size(geom.dim())); write_point_as_wkb(&mut buf, geom).unwrap(); self.0.append_value(&buf) } else { diff --git a/src/array/binary/capacity.rs b/src/array/binary/capacity.rs index decbe6625..e7f0c98d6 100644 --- a/src/array/binary/capacity.rs +++ b/src/array/binary/capacity.rs @@ -8,7 +8,7 @@ use crate::geo_traits::{ }; use crate::io::wkb::writer::{ geometry_collection_wkb_size, line_string_wkb_size, multi_line_string_wkb_size, - multi_point_wkb_size, multi_polygon_wkb_size, polygon_wkb_size, POINT_WKB_SIZE, + multi_point_wkb_size, multi_polygon_wkb_size, point_wkb_size, polygon_wkb_size, }; /// A counter for the buffer sizes of a [`WKBArray`][crate::array::WKBArray]. @@ -49,9 +49,9 @@ impl WKBCapacity { /// Add a Point to this capacity counter. #[inline] - pub fn add_point(&mut self, is_valid: bool) { - if is_valid { - self.buffer_capacity += POINT_WKB_SIZE; + pub fn add_point<'a>(&mut self, point: Option<&'a (impl PointTrait + 'a)>) { + if let Some(point) = point { + self.buffer_capacity += point_wkb_size(point.dim()); } self.offsets_capacity += 1; } @@ -112,7 +112,7 @@ impl WKBCapacity { pub fn add_geometry<'a>(&mut self, geom: Option<&'a (impl GeometryTrait + 'a)>) { if let Some(geom) = geom { match geom.as_type() { - crate::geo_traits::GeometryType::Point(_) => self.add_point(true), + crate::geo_traits::GeometryType::Point(g) => self.add_point(Some(g)), crate::geo_traits::GeometryType::LineString(g) => self.add_line_string(Some(g)), crate::geo_traits::GeometryType::Polygon(g) => self.add_polygon(Some(g)), crate::geo_traits::GeometryType::MultiPoint(p) => self.add_multi_point(Some(p)), @@ -148,7 +148,7 @@ impl WKBCapacity { ) -> Self { let mut counter = Self::new_empty(); for maybe_geom in geoms.into_iter() { - counter.add_point(maybe_geom.is_some()); + counter.add_point(maybe_geom); } counter } diff --git a/src/io/wkb/common.rs b/src/io/wkb/common.rs new file mode 100644 index 000000000..3241fceaf --- /dev/null +++ b/src/io/wkb/common.rs @@ -0,0 +1,20 @@ +use num_enum::{IntoPrimitive, TryFromPrimitive}; + +#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)] +#[repr(u32)] +pub enum WKBType { + Point = 1, + LineString = 2, + Polygon = 3, + MultiPoint = 4, + MultiLineString = 5, + MultiPolygon = 6, + GeometryCollection = 7, + PointZ = 1001, + LineStringZ = 1002, + PolygonZ = 1003, + MultiPointZ = 1004, + MultiLineStringZ = 1005, + MultiPolygonZ = 1006, + GeometryCollectionZ = 1007, +} diff --git a/src/io/wkb/mod.rs b/src/io/wkb/mod.rs index b66f0b1e4..c4a21e757 100644 --- a/src/io/wkb/mod.rs +++ b/src/io/wkb/mod.rs @@ -1,6 +1,7 @@ //! An optimized implementation of reading and writing ISO-flavored WKB-encoded geometries. mod api; +pub(crate) mod common; pub(crate) mod reader; pub(crate) mod writer; diff --git a/src/io/wkb/reader/geometry.rs b/src/io/wkb/reader/geometry.rs index 5b841c634..94d1c10f5 100644 --- a/src/io/wkb/reader/geometry.rs +++ b/src/io/wkb/reader/geometry.rs @@ -8,11 +8,12 @@ use crate::geo_traits::{ GeometryCollectionTrait, GeometryTrait, LineStringTrait, MultiLineStringTrait, MultiPointTrait, MultiPolygonTrait, PointTrait, PolygonTrait, }; +use crate::io::wkb::common::WKBType; use crate::io::wkb::reader::geometry_collection::WKBGeometryCollection; use crate::io::wkb::reader::rect::WKBRect; use crate::io::wkb::reader::{ - WKBGeometryType, WKBLineString, WKBMaybeMultiLineString, WKBMaybeMultiPoint, - WKBMaybeMultiPolygon, WKBMultiLineString, WKBMultiPoint, WKBMultiPolygon, WKBPoint, WKBPolygon, + WKBLineString, WKBMaybeMultiLineString, WKBMaybeMultiPoint, WKBMaybeMultiPolygon, + WKBMultiLineString, WKBMultiPoint, WKBMultiPolygon, WKBPoint, WKBPolygon, }; use crate::scalar::WKB; @@ -21,70 +22,74 @@ impl<'a, O: OffsetSizeTrait> WKB<'a, O> { let buf = self.arr.value(self.geom_index); let mut reader = Cursor::new(buf); let byte_order = reader.read_u8().unwrap(); - let geometry_type = match byte_order { + let geometry_type_u32 = match byte_order { 0 => reader.read_u32::().unwrap(), 1 => reader.read_u32::().unwrap(), _ => panic!("Unexpected byte order."), }; + let geometry_type = WKBType::try_from(geometry_type_u32).unwrap(); match geometry_type { - 1 => WKBGeometry::Point(WKBPoint::new(buf, byte_order.into(), 0, Dimension::XY)), - 2 => WKBGeometry::LineString(WKBLineString::new( + WKBType::Point => { + WKBGeometry::Point(WKBPoint::new(buf, byte_order.into(), 0, Dimension::XY)) + } + WKBType::LineString => WKBGeometry::LineString(WKBLineString::new( buf, byte_order.into(), 0, Dimension::XY, )), - 3 => WKBGeometry::Polygon(WKBPolygon::new(buf, byte_order.into(), 0, Dimension::XY)), - 4 => WKBGeometry::MultiPoint(WKBMultiPoint::new(buf, byte_order.into(), Dimension::XY)), - 5 => WKBGeometry::MultiLineString(WKBMultiLineString::new( - buf, - byte_order.into(), - Dimension::XY, - )), - 6 => WKBGeometry::MultiPolygon(WKBMultiPolygon::new( + WKBType::Polygon => { + WKBGeometry::Polygon(WKBPolygon::new(buf, byte_order.into(), 0, Dimension::XY)) + } + WKBType::MultiPoint => { + WKBGeometry::MultiPoint(WKBMultiPoint::new(buf, byte_order.into(), Dimension::XY)) + } + WKBType::MultiLineString => WKBGeometry::MultiLineString(WKBMultiLineString::new( buf, byte_order.into(), Dimension::XY, )), - 7 => WKBGeometry::GeometryCollection(WKBGeometryCollection::new( + WKBType::MultiPolygon => WKBGeometry::MultiPolygon(WKBMultiPolygon::new( buf, byte_order.into(), Dimension::XY, )), - 1001 => WKBGeometry::Point(WKBPoint::new(buf, byte_order.into(), 0, Dimension::XYZ)), - 1002 => WKBGeometry::LineString(WKBLineString::new( + WKBType::GeometryCollection => WKBGeometry::GeometryCollection( + WKBGeometryCollection::new(buf, byte_order.into(), Dimension::XY), + ), + WKBType::PointZ => { + WKBGeometry::Point(WKBPoint::new(buf, byte_order.into(), 0, Dimension::XYZ)) + } + WKBType::LineStringZ => WKBGeometry::LineString(WKBLineString::new( buf, byte_order.into(), 0, Dimension::XYZ, )), - 1003 => { + WKBType::PolygonZ => { WKBGeometry::Polygon(WKBPolygon::new(buf, byte_order.into(), 0, Dimension::XYZ)) } - 1004 => { + WKBType::MultiPointZ => { WKBGeometry::MultiPoint(WKBMultiPoint::new(buf, byte_order.into(), Dimension::XYZ)) } - 1005 => WKBGeometry::MultiLineString(WKBMultiLineString::new( - buf, - byte_order.into(), - Dimension::XYZ, - )), - 1006 => WKBGeometry::MultiPolygon(WKBMultiPolygon::new( + WKBType::MultiLineStringZ => WKBGeometry::MultiLineString(WKBMultiLineString::new( buf, byte_order.into(), Dimension::XYZ, )), - 1007 => WKBGeometry::GeometryCollection(WKBGeometryCollection::new( + WKBType::MultiPolygonZ => WKBGeometry::MultiPolygon(WKBMultiPolygon::new( buf, byte_order.into(), Dimension::XYZ, )), - _ => panic!("Unsupported geometry type"), + WKBType::GeometryCollectionZ => WKBGeometry::GeometryCollection( + WKBGeometryCollection::new(buf, byte_order.into(), Dimension::XYZ), + ), } } - pub fn get_wkb_geometry_type(&'a self) -> WKBGeometryType { + pub fn get_wkb_geometry_type(&'a self) -> WKBType { let buf = self.arr.value(self.geom_index); let mut reader = Cursor::new(buf); let byte_order = reader.read_u8().unwrap(); diff --git a/src/io/wkb/reader/mod.rs b/src/io/wkb/reader/mod.rs index 1d4f7fb5f..f6a7c9427 100644 --- a/src/io/wkb/reader/mod.rs +++ b/src/io/wkb/reader/mod.rs @@ -31,4 +31,3 @@ pub use multipoint::WKBMultiPoint; pub use multipolygon::WKBMultiPolygon; pub use point::WKBPoint; pub use polygon::WKBPolygon; -pub use r#type::WKBGeometryType; diff --git a/src/io/wkb/reader/type.rs b/src/io/wkb/reader/type.rs index 23e802b6f..584531fe2 100644 --- a/src/io/wkb/reader/type.rs +++ b/src/io/wkb/reader/type.rs @@ -7,6 +7,7 @@ use num_enum::TryFromPrimitive; use crate::array::CoordType; use crate::datatypes::{Dimension, GeoDataType}; use crate::error::{GeoArrowError, Result}; +use crate::io::wkb::common::WKBType; use crate::scalar::WKB; #[derive(Debug, Eq, PartialEq, TryFromPrimitive)] @@ -174,13 +175,14 @@ pub(crate) fn infer_geometry_type<'a, O: OffsetSizeTrait>( let mut available_type = AvailableTypes::new(); for geom in geoms { match geom.get_wkb_geometry_type() { - WKBGeometryType::Point => available_type.add_point(), - WKBGeometryType::LineString => available_type.add_line_string(), - WKBGeometryType::Polygon => available_type.add_polygon(), - WKBGeometryType::MultiPoint => available_type.add_multi_point(), - WKBGeometryType::MultiLineString => available_type.add_multi_line_string(), - WKBGeometryType::MultiPolygon => available_type.add_multi_polygon(), - WKBGeometryType::GeometryCollection => available_type.add_geometry_collection(), + WKBType::Point => available_type.add_point(), + WKBType::LineString => available_type.add_line_string(), + WKBType::Polygon => available_type.add_polygon(), + WKBType::MultiPoint => available_type.add_multi_point(), + WKBType::MultiLineString => available_type.add_multi_line_string(), + WKBType::MultiPolygon => available_type.add_multi_polygon(), + WKBType::GeometryCollection => available_type.add_geometry_collection(), + _ => todo!("3d support"), } } available_type.resolve_type(large_type, coord_type) diff --git a/src/io/wkb/writer/geometry.rs b/src/io/wkb/writer/geometry.rs index 337ecf55e..e41319e82 100644 --- a/src/io/wkb/writer/geometry.rs +++ b/src/io/wkb/writer/geometry.rs @@ -6,9 +6,9 @@ use crate::error::Result; use crate::geo_traits::{GeometryTrait, GeometryType}; use crate::io::wkb::writer::{ geometry_collection_wkb_size, line_string_wkb_size, multi_line_string_wkb_size, - multi_point_wkb_size, multi_polygon_wkb_size, polygon_wkb_size, write_line_string_as_wkb, - write_multi_line_string_as_wkb, write_multi_point_as_wkb, write_multi_polygon_as_wkb, - write_point_as_wkb, write_polygon_as_wkb, POINT_WKB_SIZE, + multi_point_wkb_size, multi_polygon_wkb_size, point_wkb_size, polygon_wkb_size, + write_line_string_as_wkb, write_multi_line_string_as_wkb, write_multi_point_as_wkb, + write_multi_polygon_as_wkb, write_point_as_wkb, write_polygon_as_wkb, }; use crate::trait_::GeometryArrayAccessor; use crate::trait_::GeometryArrayTrait; @@ -18,7 +18,7 @@ use std::io::{Cursor, Write}; pub fn geometry_wkb_size(geom: &impl GeometryTrait) -> usize { use GeometryType::*; match geom.as_type() { - Point(_) => POINT_WKB_SIZE, + Point(_) => point_wkb_size(geom.dim()), LineString(ls) => line_string_wkb_size(ls), Polygon(p) => polygon_wkb_size(p), MultiPoint(mp) => multi_point_wkb_size(mp), @@ -53,8 +53,10 @@ pub fn write_geometry_as_wkb( } } -impl From<&MixedGeometryArray> for WKBArray { - fn from(value: &MixedGeometryArray) -> Self { +impl From<&MixedGeometryArray> + for WKBArray +{ + fn from(value: &MixedGeometryArray) -> Self { let mut offsets: OffsetsBuilder = OffsetsBuilder::with_capacity(value.len()); // First pass: calculate binary array offsets diff --git a/src/io/wkb/writer/geometrycollection.rs b/src/io/wkb/writer/geometrycollection.rs index 20bb4fed8..86c08e992 100644 --- a/src/io/wkb/writer/geometrycollection.rs +++ b/src/io/wkb/writer/geometrycollection.rs @@ -2,6 +2,7 @@ use crate::array::offset_builder::OffsetsBuilder; use crate::array::{GeometryCollectionArray, WKBArray}; use crate::error::Result; use crate::geo_traits::GeometryCollectionTrait; +use crate::io::wkb::common::WKBType; use crate::io::wkb::reader::Endianness; use crate::io::wkb::writer::geometry::{geometry_wkb_size, write_geometry_as_wkb}; use crate::trait_::GeometryArrayAccessor; @@ -29,8 +30,19 @@ pub fn write_geometry_collection_as_wkb( // Byte order writer.write_u8(Endianness::LittleEndian.into()).unwrap(); - // wkbType = 7 - writer.write_u32::(7).unwrap(); + match geom.dim() { + 2 => { + writer + .write_u32::(WKBType::GeometryCollection.into()) + .unwrap(); + } + 3 => { + writer + .write_u32::(WKBType::GeometryCollectionZ.into()) + .unwrap(); + } + _ => panic!(), + } // numGeometries writer @@ -44,8 +56,10 @@ pub fn write_geometry_collection_as_wkb( Ok(()) } -impl From<&GeometryCollectionArray> for WKBArray { - fn from(value: &GeometryCollectionArray) -> Self { +impl From<&GeometryCollectionArray> + for WKBArray +{ + fn from(value: &GeometryCollectionArray) -> Self { let mut offsets: OffsetsBuilder = OffsetsBuilder::with_capacity(value.len()); // First pass: calculate binary array offsets diff --git a/src/io/wkb/writer/linestring.rs b/src/io/wkb/writer/linestring.rs index fa8cfaab2..00a6eb9ee 100644 --- a/src/io/wkb/writer/linestring.rs +++ b/src/io/wkb/writer/linestring.rs @@ -2,6 +2,7 @@ use crate::array::offset_builder::OffsetsBuilder; use crate::array::{LineStringArray, WKBArray}; use crate::error::Result; use crate::geo_traits::{CoordTrait, LineStringTrait}; +use crate::io::wkb::common::WKBType; use crate::io::wkb::reader::Endianness; use crate::trait_::GeometryArrayAccessor; use crate::trait_::GeometryArrayTrait; @@ -11,7 +12,10 @@ use std::io::{Cursor, Write}; /// The byte length of a WKBLineString pub fn line_string_wkb_size(geom: &impl LineStringTrait) -> usize { - 1 + 4 + 4 + (geom.num_coords() * 16) + let header = 1 + 4 + 4; + let each_coord = geom.dim() * 8; + let all_coords = geom.num_coords() * each_coord; + header + all_coords } /// Write a LineString geometry to a Writer encoded as WKB @@ -22,8 +26,19 @@ pub fn write_line_string_as_wkb( // Byte order writer.write_u8(Endianness::LittleEndian.into()).unwrap(); - // wkbType = 2 - writer.write_u32::(2).unwrap(); + match geom.dim() { + 2 => { + writer + .write_u32::(WKBType::LineString.into()) + .unwrap(); + } + 3 => { + writer + .write_u32::(WKBType::LineStringZ.into()) + .unwrap(); + } + _ => panic!(), + } // numPoints writer @@ -33,13 +48,21 @@ pub fn write_line_string_as_wkb( for coord in geom.coords() { writer.write_f64::(coord.x()).unwrap(); writer.write_f64::(coord.y()).unwrap(); + + if geom.dim() == 3 { + writer + .write_f64::(coord.nth_unchecked(2)) + .unwrap(); + } } Ok(()) } -impl From<&LineStringArray> for WKBArray { - fn from(value: &LineStringArray) -> Self { +impl From<&LineStringArray> + for WKBArray +{ + fn from(value: &LineStringArray) -> Self { let mut offsets: OffsetsBuilder = OffsetsBuilder::with_capacity(value.len()); // First pass: calculate binary array offsets diff --git a/src/io/wkb/writer/mod.rs b/src/io/wkb/writer/mod.rs index df3534098..af933abee 100644 --- a/src/io/wkb/writer/mod.rs +++ b/src/io/wkb/writer/mod.rs @@ -14,5 +14,5 @@ pub use linestring::{line_string_wkb_size, write_line_string_as_wkb}; pub use multilinestring::{multi_line_string_wkb_size, write_multi_line_string_as_wkb}; pub use multipoint::{multi_point_wkb_size, write_multi_point_as_wkb}; pub use multipolygon::{multi_polygon_wkb_size, write_multi_polygon_as_wkb}; -pub use point::{write_point_as_wkb, POINT_WKB_SIZE}; +pub use point::{point_wkb_size, write_point_as_wkb}; pub use polygon::{polygon_wkb_size, write_polygon_as_wkb}; diff --git a/src/io/wkb/writer/multilinestring.rs b/src/io/wkb/writer/multilinestring.rs index a139b7958..bda388650 100644 --- a/src/io/wkb/writer/multilinestring.rs +++ b/src/io/wkb/writer/multilinestring.rs @@ -2,6 +2,7 @@ use crate::array::offset_builder::OffsetsBuilder; use crate::array::{MultiLineStringArray, WKBArray}; use crate::error::Result; use crate::geo_traits::MultiLineStringTrait; +use crate::io::wkb::common::WKBType; use crate::io::wkb::reader::Endianness; use crate::io::wkb::writer::linestring::{line_string_wkb_size, write_line_string_as_wkb}; use crate::trait_::GeometryArrayAccessor; @@ -28,8 +29,19 @@ pub fn write_multi_line_string_as_wkb( // Byte order writer.write_u8(Endianness::LittleEndian.into()).unwrap(); - // wkbType = 5 - writer.write_u32::(5).unwrap(); + match geom.dim() { + 2 => { + writer + .write_u32::(WKBType::MultiLineString.into()) + .unwrap(); + } + 3 => { + writer + .write_u32::(WKBType::MultiLineStringZ.into()) + .unwrap(); + } + _ => panic!(), + } // numPoints writer @@ -43,8 +55,10 @@ pub fn write_multi_line_string_as_wkb( Ok(()) } -impl From<&MultiLineStringArray> for WKBArray { - fn from(value: &MultiLineStringArray) -> Self { +impl From<&MultiLineStringArray> + for WKBArray +{ + fn from(value: &MultiLineStringArray) -> Self { let mut offsets: OffsetsBuilder = OffsetsBuilder::with_capacity(value.len()); // First pass: calculate binary array offsets diff --git a/src/io/wkb/writer/multipoint.rs b/src/io/wkb/writer/multipoint.rs index b4d1ad274..e334b8be4 100644 --- a/src/io/wkb/writer/multipoint.rs +++ b/src/io/wkb/writer/multipoint.rs @@ -2,8 +2,9 @@ use crate::array::offset_builder::OffsetsBuilder; use crate::array::{MultiPointArray, WKBArray}; use crate::error::Result; use crate::geo_traits::MultiPointTrait; +use crate::io::wkb::common::WKBType; use crate::io::wkb::reader::Endianness; -use crate::io::wkb::writer::point::{write_point_as_wkb, POINT_WKB_SIZE}; +use crate::io::wkb::writer::point::{point_wkb_size, write_point_as_wkb}; use crate::trait_::GeometryArrayAccessor; use crate::trait_::GeometryArrayTrait; use arrow_array::{GenericBinaryArray, OffsetSizeTrait}; @@ -12,7 +13,7 @@ use std::io::{Cursor, Write}; /// The byte length of a WKBMultiPoint pub fn multi_point_wkb_size(geom: &impl MultiPointTrait) -> usize { - 1 + 4 + 4 + (geom.num_points() * POINT_WKB_SIZE) + 1 + 4 + 4 + (geom.num_points() * point_wkb_size(geom.dim())) } /// Write a MultiPoint geometry to a Writer encoded as WKB @@ -23,8 +24,19 @@ pub fn write_multi_point_as_wkb( // Byte order writer.write_u8(Endianness::LittleEndian.into()).unwrap(); - // wkbType = 4 - writer.write_u32::(4).unwrap(); + match geom.dim() { + 2 => { + writer + .write_u32::(WKBType::MultiPoint.into()) + .unwrap(); + } + 3 => { + writer + .write_u32::(WKBType::MultiPointZ.into()) + .unwrap(); + } + _ => panic!(), + } // numPoints writer @@ -38,8 +50,10 @@ pub fn write_multi_point_as_wkb( Ok(()) } -impl From<&MultiPointArray> for WKBArray { - fn from(value: &MultiPointArray) -> Self { +impl From<&MultiPointArray> + for WKBArray +{ + fn from(value: &MultiPointArray) -> Self { let mut offsets: OffsetsBuilder = OffsetsBuilder::with_capacity(value.len()); // First pass: calculate binary array offsets diff --git a/src/io/wkb/writer/multipolygon.rs b/src/io/wkb/writer/multipolygon.rs index 761d0829f..5fd521108 100644 --- a/src/io/wkb/writer/multipolygon.rs +++ b/src/io/wkb/writer/multipolygon.rs @@ -2,6 +2,7 @@ use crate::array::offset_builder::OffsetsBuilder; use crate::array::{MultiPolygonArray, WKBArray}; use crate::error::Result; use crate::geo_traits::MultiPolygonTrait; +use crate::io::wkb::common::WKBType; use crate::io::wkb::reader::Endianness; use crate::io::wkb::writer::polygon::{polygon_wkb_size, write_polygon_as_wkb}; use crate::trait_::GeometryArrayAccessor; @@ -28,8 +29,19 @@ pub fn write_multi_polygon_as_wkb( // Byte order writer.write_u8(Endianness::LittleEndian.into()).unwrap(); - // wkbType = 6 - writer.write_u32::(6).unwrap(); + match geom.dim() { + 2 => { + writer + .write_u32::(WKBType::MultiPolygon.into()) + .unwrap(); + } + 3 => { + writer + .write_u32::(WKBType::MultiPolygonZ.into()) + .unwrap(); + } + _ => panic!(), + } // numPolygons writer @@ -43,8 +55,10 @@ pub fn write_multi_polygon_as_wkb( Ok(()) } -impl From<&MultiPolygonArray> for WKBArray { - fn from(value: &MultiPolygonArray) -> Self { +impl From<&MultiPolygonArray> + for WKBArray +{ + fn from(value: &MultiPolygonArray) -> Self { let mut offsets: OffsetsBuilder = OffsetsBuilder::with_capacity(value.len()); // First pass: calculate binary array offsets diff --git a/src/io/wkb/writer/point.rs b/src/io/wkb/writer/point.rs index 971bb0804..6c9413cec 100644 --- a/src/io/wkb/writer/point.rs +++ b/src/io/wkb/writer/point.rs @@ -2,6 +2,7 @@ use crate::array::offset_builder::OffsetsBuilder; use crate::array::{PointArray, WKBArray}; use crate::error::Result; use crate::geo_traits::PointTrait; +use crate::io::wkb::common::WKBType; use crate::io::wkb::reader::Endianness; use crate::trait_::GeometryArrayAccessor; use crate::trait_::GeometryArrayTrait; @@ -10,31 +11,59 @@ use byteorder::{LittleEndian, WriteBytesExt}; use std::io::{Cursor, Write}; /// The byte length of a WKBPoint -pub const POINT_WKB_SIZE: usize = 1 + 4 + 8 + 8; +pub fn point_wkb_size(dim: usize) -> usize { + let header = 1 + 4; + let coords = dim * 8; + header + coords +} + +/// The byte length of a WKBPoint +pub fn point_wkb_size_const() -> usize { + let header = 1 + 4; + let coords = D * 8; + header + coords +} /// Write a Point geometry to a Writer encoded as WKB pub fn write_point_as_wkb(mut writer: W, geom: &impl PointTrait) -> Result<()> { // Byte order writer.write_u8(Endianness::LittleEndian.into()).unwrap(); - // wkbType = 1 - writer.write_u32::(1).unwrap(); + match geom.dim() { + 2 => { + writer + .write_u32::(WKBType::Point.into()) + .unwrap(); + } + 3 => { + writer + .write_u32::(WKBType::PointZ.into()) + .unwrap(); + } + _ => panic!(), + } writer.write_f64::(geom.x()).unwrap(); writer.write_f64::(geom.y()).unwrap(); + if geom.dim() == 3 { + writer + .write_f64::(geom.nth_unchecked(2)) + .unwrap(); + } + Ok(()) } -impl From<&PointArray<2>> for WKBArray { - fn from(value: &PointArray<2>) -> Self { +impl From<&PointArray> for WKBArray { + fn from(value: &PointArray) -> Self { let non_null_count = value .nulls() .map_or(value.len(), |validity| value.len() - validity.null_count()); let validity = value.nulls().cloned(); // only allocate space for a WKBPoint for non-null items - let values_len = non_null_count * POINT_WKB_SIZE; + let values_len = non_null_count * point_wkb_size_const::(); let mut offsets: OffsetsBuilder = OffsetsBuilder::with_capacity(value.len()); let values = { @@ -44,7 +73,7 @@ impl From<&PointArray<2>> for WKBArray { for maybe_geom in value.iter() { if let Some(geom) = maybe_geom { write_point_as_wkb(&mut writer, &geom).unwrap(); - offsets.try_push_usize(POINT_WKB_SIZE).unwrap(); + offsets.try_push_usize(point_wkb_size_const::()).unwrap(); } else { offsets.extend_constant(1); } diff --git a/src/io/wkb/writer/polygon.rs b/src/io/wkb/writer/polygon.rs index 5cbfc4236..0293b642e 100644 --- a/src/io/wkb/writer/polygon.rs +++ b/src/io/wkb/writer/polygon.rs @@ -2,6 +2,7 @@ use crate::array::offset_builder::OffsetsBuilder; use crate::array::{PolygonArray, WKBArray}; use crate::error::Result; use crate::geo_traits::{CoordTrait, LineStringTrait, PolygonTrait}; +use crate::io::wkb::common::WKBType; use crate::io::wkb::reader::Endianness; use crate::trait_::GeometryArrayAccessor; use crate::trait_::GeometryArrayTrait; @@ -13,12 +14,14 @@ use std::io::{Cursor, Write}; pub fn polygon_wkb_size(geom: &impl PolygonTrait) -> usize { let mut sum = 1 + 4 + 4; + let each_coord = geom.dim() * 8; + // TODO: support empty polygons where this will panic let ext_ring = geom.exterior().unwrap(); - sum += 4 + (ext_ring.num_coords() * 16); + sum += 4 + (ext_ring.num_coords() * each_coord); for int_ring in geom.interiors() { - sum += 4 + (int_ring.num_coords() * 16); + sum += 4 + (int_ring.num_coords() * each_coord); } sum @@ -32,8 +35,19 @@ pub fn write_polygon_as_wkb( // Byte order writer.write_u8(Endianness::LittleEndian.into()).unwrap(); - // wkbType = 3 - writer.write_u32::(3).unwrap(); + match geom.dim() { + 2 => { + writer + .write_u32::(WKBType::Polygon.into()) + .unwrap(); + } + 3 => { + writer + .write_u32::(WKBType::PolygonZ.into()) + .unwrap(); + } + _ => panic!(), + } // numRings // TODO: support empty polygons where this will panic @@ -50,6 +64,11 @@ pub fn write_polygon_as_wkb( for coord in ext_ring.coords() { writer.write_f64::(coord.x()).unwrap(); writer.write_f64::(coord.y()).unwrap(); + if geom.dim() == 3 { + writer + .write_f64::(coord.nth_unchecked(2)) + .unwrap(); + } } for int_ring in geom.interiors() { @@ -60,14 +79,21 @@ pub fn write_polygon_as_wkb( for coord in int_ring.coords() { writer.write_f64::(coord.x()).unwrap(); writer.write_f64::(coord.y()).unwrap(); + if geom.dim() == 3 { + writer + .write_f64::(coord.nth_unchecked(2)) + .unwrap(); + } } } Ok(()) } -impl From<&PolygonArray> for WKBArray { - fn from(value: &PolygonArray) -> Self { +impl From<&PolygonArray> + for WKBArray +{ + fn from(value: &PolygonArray) -> Self { let mut offsets: OffsetsBuilder = OffsetsBuilder::with_capacity(value.len()); // First pass: calculate binary array offsets