diff --git a/rust/geoarrow/src/algorithm/native/downcast.rs b/rust/geoarrow/src/algorithm/native/downcast.rs index 9006f37e..cad3c47a 100644 --- a/rust/geoarrow/src/algorithm/native/downcast.rs +++ b/rust/geoarrow/src/algorithm/native/downcast.rs @@ -17,11 +17,12 @@ use crate::schema::GeoSchemaExt; use crate::table::Table; use crate::NativeArray; +/// Downcast will change between geometry types but will not affect the dimension of the data. pub trait Downcast { type Output; /// The data type that downcasting would result in. - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType; + fn downcasted_data_type(&self) -> NativeType; /// If possible, convert this array to a simpler and/or smaller data type /// @@ -33,19 +34,17 @@ pub trait Downcast { /// - MixedGeometry -> any of the 6 concrete types /// - GeometryCollection -> MixedGeometry or any of the 6 concrete types /// - /// If small_offsets is `true`, it will additionally try to convert `i64` offset buffers to - /// `i32` if the offsets would not overflow. - fn downcast(&self, small_offsets: bool) -> Self::Output; + fn downcast(&self) -> Self::Output; } impl Downcast for PointArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { self.data_type() } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { Arc::new(self.clone()) } } @@ -93,19 +92,16 @@ pub(crate) fn can_downcast_multi(buffer: &OffsetBuffer) - impl Downcast for LineStringArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { match self.data_type() { NativeType::LineString(ct, dim) => NativeType::LineString(ct, dim), _ => unreachable!(), } } - fn downcast(&self, small_offsets: bool) -> Self::Output { - match (self.data_type(), self.downcasted_data_type(small_offsets)) { - ( - NativeType::LineString(_, Dimension::XY), - NativeType::LineString(_, Dimension::XY), - ) => Arc::new(self.clone()), + fn downcast(&self) -> Self::Output { + match (self.data_type(), self.downcasted_data_type()) { + (NativeType::LineString(_, _), NativeType::LineString(_, _)) => Arc::new(self.clone()), _ => unreachable!(), } } @@ -114,14 +110,14 @@ impl Downcast for LineStringArray { impl Downcast for PolygonArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { match self.data_type() { NativeType::Polygon(ct, dim) => NativeType::Polygon(ct, dim), _ => unreachable!(), } } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { Arc::new(self.clone()) } } @@ -129,7 +125,7 @@ impl Downcast for PolygonArray { impl Downcast for MultiPointArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { match self.data_type() { NativeType::MultiPoint(ct, dim) => { if can_downcast_multi(&self.geom_offsets) { @@ -141,7 +137,7 @@ impl Downcast for MultiPointArray { _ => unreachable!(), } } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { // Note: this won't allow a downcast for empty MultiPoints if *self.geom_offsets.last() as usize == self.len() { return Arc::new(PointArray::new( @@ -158,7 +154,7 @@ impl Downcast for MultiPointArray { impl Downcast for MultiLineStringArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { match self.data_type() { NativeType::MultiLineString(ct, dim) => { if can_downcast_multi(&self.geom_offsets) { @@ -171,7 +167,7 @@ impl Downcast for MultiLineStringArray { } } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { if *self.geom_offsets.last() as usize == self.len() { return Arc::new(LineStringArray::new( self.coords.clone(), @@ -188,7 +184,7 @@ impl Downcast for MultiLineStringArray { impl Downcast for MultiPolygonArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { match self.data_type() { NativeType::MultiPolygon(ct, dim) => { if can_downcast_multi(&self.geom_offsets) { @@ -201,7 +197,7 @@ impl Downcast for MultiPolygonArray { } } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { if *self.geom_offsets.last() as usize == self.len() { return Arc::new(PolygonArray::new( self.coords.clone(), @@ -219,7 +215,7 @@ impl Downcast for MultiPolygonArray { impl Downcast for MixedGeometryArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { let coord_type = self.coord_type(); if self.has_points() @@ -239,7 +235,7 @@ impl Downcast for MixedGeometryArray { && !self.has_multi_line_strings() && !self.has_multi_polygons() { - return self.line_strings.downcasted_data_type(small_offsets); + return self.line_strings.downcasted_data_type(); } if !self.has_points() @@ -249,7 +245,7 @@ impl Downcast for MixedGeometryArray { && !self.has_multi_line_strings() && !self.has_multi_polygons() { - return self.polygons.downcasted_data_type(small_offsets); + return self.polygons.downcasted_data_type(); } if !self.has_points() @@ -259,7 +255,7 @@ impl Downcast for MixedGeometryArray { && !self.has_multi_line_strings() && !self.has_multi_polygons() { - return self.multi_points.downcasted_data_type(small_offsets); + return self.multi_points.downcasted_data_type(); } if !self.has_points() @@ -269,7 +265,7 @@ impl Downcast for MixedGeometryArray { && self.has_multi_line_strings() && !self.has_multi_polygons() { - return self.multi_line_strings.downcasted_data_type(small_offsets); + return self.multi_line_strings.downcasted_data_type(); } if !self.has_points() @@ -279,13 +275,13 @@ impl Downcast for MixedGeometryArray { && !self.has_multi_line_strings() && self.has_multi_polygons() { - return self.multi_polygons.downcasted_data_type(small_offsets); + return self.multi_polygons.downcasted_data_type(); } self.data_type() } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { // TODO: do I need to handle the slice offset? if self.has_points() && !self.has_line_strings() @@ -304,7 +300,7 @@ impl Downcast for MixedGeometryArray { && !self.has_multi_line_strings() && !self.has_multi_polygons() { - return self.line_strings.downcast(small_offsets); + return self.line_strings.downcast(); } if !self.has_points() @@ -314,7 +310,7 @@ impl Downcast for MixedGeometryArray { && !self.has_multi_line_strings() && !self.has_multi_polygons() { - return self.polygons.downcast(small_offsets); + return self.polygons.downcast(); } if !self.has_points() @@ -324,7 +320,7 @@ impl Downcast for MixedGeometryArray { && !self.has_multi_line_strings() && !self.has_multi_polygons() { - return self.multi_points.downcast(small_offsets); + return self.multi_points.downcast(); } if !self.has_points() @@ -334,7 +330,7 @@ impl Downcast for MixedGeometryArray { && self.has_multi_line_strings() && !self.has_multi_polygons() { - return self.multi_line_strings.downcast(small_offsets); + return self.multi_line_strings.downcast(); } if !self.has_points() @@ -344,7 +340,7 @@ impl Downcast for MixedGeometryArray { && !self.has_multi_line_strings() && self.has_multi_polygons() { - return self.multi_polygons.downcast(small_offsets); + return self.multi_polygons.downcast(); } Arc::new(self.clone()) @@ -354,14 +350,14 @@ impl Downcast for MixedGeometryArray { impl Downcast for GeometryCollectionArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { todo!() } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { // TODO: support downcasting with null elements if *self.geom_offsets.last() as usize == self.len() && self.null_count() == 0 { // Call downcast on the mixed array - return self.array.downcast(small_offsets); + return self.array.downcast(); } Arc::new(self.clone()) @@ -371,10 +367,10 @@ impl Downcast for GeometryCollectionArray { impl Downcast for RectArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { self.data_type() } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { Arc::new(self.clone()) } } @@ -382,42 +378,36 @@ impl Downcast for RectArray { impl Downcast for &dyn NativeArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { - use Dimension::*; + fn downcasted_data_type(&self) -> NativeType { use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().downcasted_data_type(small_offsets), - LineString(_, XY) => self.as_line_string().downcasted_data_type(small_offsets), - Polygon(_, XY) => self.as_polygon().downcasted_data_type(small_offsets), - MultiPoint(_, XY) => self.as_multi_point().downcasted_data_type(small_offsets), - MultiLineString(_, XY) => self - .as_multi_line_string() - .downcasted_data_type(small_offsets), - MultiPolygon(_, XY) => self.as_multi_polygon().downcasted_data_type(small_offsets), - Mixed(_, XY) => self.as_mixed().downcasted_data_type(small_offsets), - GeometryCollection(_, XY) => self - .as_geometry_collection() - .downcasted_data_type(small_offsets), - Rect(XY) => self.as_rect().downcasted_data_type(small_offsets), + Point(_, _) => self.as_point().downcasted_data_type(), + LineString(_, _) => self.as_line_string().downcasted_data_type(), + Polygon(_, _) => self.as_polygon().downcasted_data_type(), + MultiPoint(_, _) => self.as_multi_point().downcasted_data_type(), + MultiLineString(_, _) => self.as_multi_line_string().downcasted_data_type(), + MultiPolygon(_, _) => self.as_multi_polygon().downcasted_data_type(), + Mixed(_, _) => self.as_mixed().downcasted_data_type(), + GeometryCollection(_, _) => self.as_geometry_collection().downcasted_data_type(), + Rect(_) => self.as_rect().downcasted_data_type(), _ => todo!("3d support"), } } - fn downcast(&self, small_offsets: bool) -> Self::Output { - use Dimension::*; + fn downcast(&self) -> Self::Output { use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().downcast(small_offsets), - LineString(_, XY) => self.as_line_string().downcast(small_offsets), - Polygon(_, XY) => self.as_polygon().downcast(small_offsets), - MultiPoint(_, XY) => self.as_multi_point().downcast(small_offsets), - MultiLineString(_, XY) => self.as_multi_line_string().downcast(small_offsets), - MultiPolygon(_, XY) => self.as_multi_polygon().downcast(small_offsets), - Mixed(_, XY) => self.as_mixed().downcast(small_offsets), - GeometryCollection(_, XY) => self.as_geometry_collection().downcast(small_offsets), - Rect(XY) => self.as_rect().downcast(small_offsets), + Point(_, _) => self.as_point().downcast(), + LineString(_, _) => self.as_line_string().downcast(), + Polygon(_, _) => self.as_polygon().downcast(), + MultiPoint(_, _) => self.as_multi_point().downcast(), + MultiLineString(_, _) => self.as_multi_line_string().downcast(), + MultiPolygon(_, _) => self.as_multi_polygon().downcast(), + Mixed(_, _) => self.as_mixed().downcast(), + GeometryCollection(_, _) => self.as_geometry_collection().downcast(), + Rect(_) => self.as_rect().downcast(), _ => todo!("3d support"), } } @@ -460,10 +450,10 @@ fn resolve_types(types: &HashSet) -> NativeType { impl Downcast for ChunkedPointArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { self.data_type() } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { Arc::new(self.clone()) } } @@ -473,15 +463,15 @@ macro_rules! impl_chunked_downcast { impl Downcast for $chunked_array { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { let mut types = HashSet::new(); self.chunks.iter().for_each(|chunk| { - types.insert(chunk.downcasted_data_type(small_offsets)); + types.insert(chunk.downcasted_data_type()); }); resolve_types(&types) } - fn downcast(&self, small_offsets: bool) -> Self::Output { - let to_data_type = self.downcasted_data_type(small_offsets); + fn downcast(&self) -> Self::Output { + let to_data_type = self.downcasted_data_type(); if to_data_type == self.data_type() { return Arc::new(self.clone()); @@ -504,10 +494,10 @@ impl_chunked_downcast!(ChunkedGeometryCollectionArray); impl Downcast for ChunkedRectArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { self.data_type() } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { Arc::new(self.clone()) } } @@ -515,42 +505,38 @@ impl Downcast for ChunkedRectArray { impl Downcast for &dyn ChunkedNativeArray { type Output = Arc; - fn downcasted_data_type(&self, small_offsets: bool) -> NativeType { + fn downcasted_data_type(&self) -> NativeType { use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().downcasted_data_type(small_offsets), - LineString(_, XY) => self.as_line_string().downcasted_data_type(small_offsets), - Polygon(_, XY) => self.as_polygon().downcasted_data_type(small_offsets), - MultiPoint(_, XY) => self.as_multi_point().downcasted_data_type(small_offsets), - MultiLineString(_, XY) => self - .as_multi_line_string() - .downcasted_data_type(small_offsets), - MultiPolygon(_, XY) => self.as_multi_polygon().downcasted_data_type(small_offsets), - Mixed(_, XY) => self.as_mixed().downcasted_data_type(small_offsets), - GeometryCollection(_, XY) => self - .as_geometry_collection() - .downcasted_data_type(small_offsets), - Rect(XY) => self.as_rect().downcasted_data_type(small_offsets), + Point(_, XY) => self.as_point().downcasted_data_type(), + LineString(_, XY) => self.as_line_string().downcasted_data_type(), + Polygon(_, XY) => self.as_polygon().downcasted_data_type(), + MultiPoint(_, XY) => self.as_multi_point().downcasted_data_type(), + MultiLineString(_, XY) => self.as_multi_line_string().downcasted_data_type(), + MultiPolygon(_, XY) => self.as_multi_polygon().downcasted_data_type(), + Mixed(_, XY) => self.as_mixed().downcasted_data_type(), + GeometryCollection(_, XY) => self.as_geometry_collection().downcasted_data_type(), + Rect(XY) => self.as_rect().downcasted_data_type(), _ => todo!("3d support"), } } - fn downcast(&self, small_offsets: bool) -> Self::Output { + fn downcast(&self) -> Self::Output { use Dimension::*; use NativeType::*; match self.data_type() { - Point(_, XY) => self.as_point().downcast(small_offsets), - LineString(_, XY) => self.as_line_string().downcast(small_offsets), - Polygon(_, XY) => self.as_polygon().downcast(small_offsets), - MultiPoint(_, XY) => self.as_multi_point().downcast(small_offsets), - MultiLineString(_, XY) => self.as_multi_line_string().downcast(small_offsets), - MultiPolygon(_, XY) => self.as_multi_polygon().downcast(small_offsets), - Mixed(_, XY) => self.as_mixed().downcast(small_offsets), - GeometryCollection(_, XY) => self.as_geometry_collection().downcast(small_offsets), - Rect(XY) => self.as_rect().downcast(small_offsets), + Point(_, XY) => self.as_point().downcast(), + LineString(_, XY) => self.as_line_string().downcast(), + Polygon(_, XY) => self.as_polygon().downcast(), + MultiPoint(_, XY) => self.as_multi_point().downcast(), + MultiLineString(_, XY) => self.as_multi_line_string().downcast(), + MultiPolygon(_, XY) => self.as_multi_polygon().downcast(), + Mixed(_, XY) => self.as_mixed().downcast(), + GeometryCollection(_, XY) => self.as_geometry_collection().downcast(), + Rect(XY) => self.as_rect().downcast(), _ => todo!("3d support"), } } @@ -569,11 +555,11 @@ pub trait DowncastTable { /// /// If small_offsets is `true`, it will additionally try to convert `i64` offset buffers to /// `i32` if the offsets would not overflow. - fn downcast(&self, small_offsets: bool) -> Result; + fn downcast(&self) -> Result
; } impl DowncastTable for Table { - fn downcast(&self, small_offsets: bool) -> Result
{ + fn downcast(&self) -> Result
{ let downcasted_columns = self .schema() .as_ref() @@ -581,7 +567,7 @@ impl DowncastTable for Table { .iter() .map(|idx| { let geometry = self.geometry_column(Some(*idx))?; - Ok((*idx, geometry.as_ref().downcast(small_offsets))) + Ok((*idx, geometry.as_ref().downcast())) }) .collect::>>()?; diff --git a/rust/geoarrow/src/array/geometrycollection/array.rs b/rust/geoarrow/src/array/geometrycollection/array.rs index 2445de82..4a37452e 100644 --- a/rust/geoarrow/src/array/geometrycollection/array.rs +++ b/rust/geoarrow/src/array/geometrycollection/array.rs @@ -253,11 +253,11 @@ impl IntoArrow for GeometryCollectionArray { } } -impl TryFrom<&GenericListArray> for GeometryCollectionArray { +impl TryFrom<(&GenericListArray, Dimension)> for GeometryCollectionArray { type Error = GeoArrowError; - fn try_from(value: &GenericListArray) -> Result { - let geoms: MixedGeometryArray = value.values().as_ref().try_into()?; + fn try_from((value, dim): (&GenericListArray, Dimension)) -> Result { + let geoms: MixedGeometryArray = (value.values().as_ref(), dim).try_into()?; let geom_offsets = value.offsets(); let validity = value.nulls(); @@ -270,11 +270,11 @@ impl TryFrom<&GenericListArray> for GeometryCollectionArray { } } -impl TryFrom<&GenericListArray> for GeometryCollectionArray { +impl TryFrom<(&GenericListArray, Dimension)> for GeometryCollectionArray { type Error = GeoArrowError; - fn try_from(value: &GenericListArray) -> Result { - let geoms: MixedGeometryArray = value.values().as_ref().try_into()?; + fn try_from((value, dim): (&GenericListArray, Dimension)) -> Result { + let geoms: MixedGeometryArray = (value.values().as_ref(), dim).try_into()?; let geom_offsets = offsets_buffer_i64_to_i32(value.offsets())?; let validity = value.nulls(); @@ -287,18 +287,18 @@ impl TryFrom<&GenericListArray> for GeometryCollectionArray { } } -impl TryFrom<&dyn Array> for GeometryCollectionArray { +impl TryFrom<(&dyn Array, Dimension)> for GeometryCollectionArray { type Error = GeoArrowError; - fn try_from(value: &dyn Array) -> Result { + fn try_from((value, dim): (&dyn Array, Dimension)) -> Result { match value.data_type() { DataType::List(_) => { let downcasted = value.as_list::(); - downcasted.try_into() + (downcasted, dim).try_into() } DataType::LargeList(_) => { let downcasted = value.as_list::(); - downcasted.try_into() + (downcasted, dim).try_into() } _ => Err(GeoArrowError::General(format!( "Unexpected type: {:?}", @@ -312,7 +312,11 @@ impl TryFrom<(&dyn Array, &Field)> for GeometryCollectionArray { type Error = GeoArrowError; fn try_from((arr, field): (&dyn Array, &Field)) -> Result { - let mut arr: Self = arr.try_into()?; + let geom_type = NativeType::try_from(field)?; + let dim = geom_type + .dimension() + .ok_or(GeoArrowError::General("Expected dimension".to_string()))?; + let mut arr: Self = (arr, dim).try_into()?; arr.metadata = Arc::new(ArrayMetadata::try_from(field)?); Ok(arr) } diff --git a/rust/geoarrow/src/array/mixed/array.rs b/rust/geoarrow/src/array/mixed/array.rs index c5397748..f4972406 100644 --- a/rust/geoarrow/src/array/mixed/array.rs +++ b/rust/geoarrow/src/array/mixed/array.rs @@ -484,16 +484,17 @@ impl IntoArrow for MixedGeometryArray { } } -impl TryFrom<&UnionArray> for MixedGeometryArray { +impl TryFrom<(&UnionArray, Dimension)> for MixedGeometryArray { type Error = GeoArrowError; - fn try_from(value: &UnionArray) -> std::result::Result { - let mut points: Vec = vec![]; - let mut line_strings: Vec = vec![]; - let mut polygons: Vec = vec![]; - let mut multi_points: Vec = vec![]; - let mut multi_line_strings: Vec = vec![]; - let mut multi_polygons: Vec = vec![]; + fn try_from((value, dim): (&UnionArray, Dimension)) -> std::result::Result { + let mut points: Option = None; + let mut line_strings: Option = None; + let mut polygons: Option = None; + let mut multi_points: Option = None; + let mut multi_line_strings: Option = None; + let mut multi_polygons: Option = None; + match value.data_type() { DataType::Union(fields, mode) => { if !matches!(mode, UnionMode::Dense) { @@ -501,7 +502,7 @@ impl TryFrom<&UnionArray> for MixedGeometryArray { } for (type_id, _field) in fields.iter() { - let dimension = if type_id < 10 { + let found_dimension = if type_id < 10 { Dimension::XY } else if type_id < 20 { Dimension::XYZ @@ -512,48 +513,33 @@ impl TryFrom<&UnionArray> for MixedGeometryArray { ))); }; + if dim != found_dimension { + return Err( GeoArrowError::General(format!("expected dimension: {:?}, found child array with dimension {:?} and type_id: {}", dim, found_dimension, type_id ))); + } + match type_id { 1 | 11 => { - points.push( - (value.child(type_id).as_ref(), dimension) - .try_into() - .unwrap(), - ); + points = Some((value.child(type_id).as_ref(), dim).try_into().unwrap()); } 2 | 12 => { - line_strings.push( - (value.child(type_id).as_ref(), dimension) - .try_into() - .unwrap(), - ); + line_strings = + Some((value.child(type_id).as_ref(), dim).try_into().unwrap()); } 3 | 13 => { - polygons.push( - (value.child(type_id).as_ref(), dimension) - .try_into() - .unwrap(), - ); + polygons = + Some((value.child(type_id).as_ref(), dim).try_into().unwrap()); } 4 | 14 => { - multi_points.push( - (value.child(type_id).as_ref(), dimension) - .try_into() - .unwrap(), - ); + multi_points = + Some((value.child(type_id).as_ref(), dim).try_into().unwrap()); } 5 | 15 => { - multi_line_strings.push( - (value.child(type_id).as_ref(), dimension) - .try_into() - .unwrap(), - ); + multi_line_strings = + Some((value.child(type_id).as_ref(), dim).try_into().unwrap()); } 6 | 16 => { - multi_polygons.push( - (value.child(type_id).as_ref(), dimension) - .try_into() - .unwrap(), - ); + multi_polygons = + Some((value.child(type_id).as_ref(), dim).try_into().unwrap()); } _ => { return Err(GeoArrowError::General(format!( @@ -571,37 +557,28 @@ impl TryFrom<&UnionArray> for MixedGeometryArray { // This is after checking for dense union let offsets = value.offsets().unwrap().clone(); - // TODO: make nicer errors. We don't currently allow a mixed geometry array with multiple - // dimensions of underlying geometries. - assert!(points.len() <= 1); - assert!(line_strings.len() <= 1); - assert!(polygons.len() <= 1); - assert!(multi_points.len() <= 1); - assert!(multi_line_strings.len() <= 1); - assert!(multi_polygons.len() <= 1); - Ok(Self::new( type_ids, offsets, - points.first().cloned().unwrap_or_default(), - line_strings.first().cloned().unwrap_or_default(), - polygons.first().cloned().unwrap_or_default(), - multi_points.first().cloned().unwrap_or_default(), - multi_line_strings.first().cloned().unwrap_or_default(), - multi_polygons.first().cloned().unwrap_or_default(), + points.unwrap_or_default(), + line_strings.unwrap_or_default(), + polygons.unwrap_or_default(), + multi_points.unwrap_or_default(), + multi_line_strings.unwrap_or_default(), + multi_polygons.unwrap_or_default(), Default::default(), )) } } -impl TryFrom<&dyn Array> for MixedGeometryArray { +impl TryFrom<(&dyn Array, Dimension)> for MixedGeometryArray { type Error = GeoArrowError; - fn try_from(value: &dyn Array) -> Result { + fn try_from((value, dim): (&dyn Array, Dimension)) -> Result { match value.data_type() { DataType::Union(_, _) => { let downcasted = value.as_any().downcast_ref::().unwrap(); - downcasted.try_into() + (downcasted, dim).try_into() } _ => Err(GeoArrowError::General(format!( "Unexpected type: {:?}", @@ -611,11 +588,16 @@ impl TryFrom<&dyn Array> for MixedGeometryArray { } } +// TODO:, thinking all geoarrow.geometry will go through primary dimensionless geometry array impl TryFrom<(&dyn Array, &Field)> for MixedGeometryArray { type Error = GeoArrowError; fn try_from((arr, field): (&dyn Array, &Field)) -> Result { - let mut arr: Self = arr.try_into()?; + let geom_type = NativeType::try_from(field)?; + let dim = geom_type + .dimension() + .ok_or(GeoArrowError::General("Expected dimension".to_string()))?; + let mut arr: Self = (arr, dim).try_into()?; arr.metadata = Arc::new(ArrayMetadata::try_from(field)?); Ok(arr) } @@ -871,7 +853,7 @@ mod test { // Round trip to/from arrow-rs let arrow_array = arr.into_arrow(); - let round_trip_arr: MixedGeometryArray = (&arrow_array).try_into().unwrap(); + let round_trip_arr: MixedGeometryArray = (&arrow_array, Dimension::XY).try_into().unwrap(); assert_eq!( round_trip_arr.value_as_geo(0), @@ -901,7 +883,7 @@ mod test { // Round trip to/from arrow-rs let arrow_array = arr.into_arrow(); - let round_trip_arr: MixedGeometryArray = (&arrow_array).try_into().unwrap(); + let round_trip_arr: MixedGeometryArray = (&arrow_array, Dimension::XY).try_into().unwrap(); assert_eq!(round_trip_arr.value_as_geo(0), geoms[0]); assert_eq!(round_trip_arr.value_as_geo(1), geoms[1]); @@ -918,7 +900,7 @@ mod test { // Round trip to/from arrow-rs let arrow_array = arr.into_arrow(); - let round_trip_arr: MixedGeometryArray = (&arrow_array).try_into().unwrap(); + let round_trip_arr: MixedGeometryArray = (&arrow_array, Dimension::XY).try_into().unwrap(); assert_eq!(round_trip_arr.value_as_geo(0), geoms[0]); assert_eq!(round_trip_arr.value_as_geo(1), geoms[1]); diff --git a/rust/geoarrow/src/array/unknown/array.rs b/rust/geoarrow/src/array/unknown/array.rs index fd9592d4..0aa9e075 100644 --- a/rust/geoarrow/src/array/unknown/array.rs +++ b/rust/geoarrow/src/array/unknown/array.rs @@ -9,8 +9,8 @@ use crate::array::metadata::ArrayMetadata; use crate::array::unknown::builder::UnknownGeometryBuilder; use crate::array::unknown::capacity::UnknownCapacity; use crate::array::{ - CoordType, LineStringArray, MultiLineStringArray, MultiPointArray, MultiPolygonArray, - PointArray, PolygonArray, WKBArray, + CoordType, GeometryCollectionArray, LineStringArray, MultiLineStringArray, MultiPointArray, + MultiPolygonArray, PointArray, PolygonArray, WKBArray, }; use crate::datatypes::{Dimension, NativeType}; use crate::error::{GeoArrowError, Result}; @@ -59,26 +59,29 @@ pub struct UnknownGeometryArray { pub(crate) metadata: Arc, - /// Invariant: every item in `type_ids` is `> 0 && < fields.len()` if `type_ids` are not provided. If `type_ids` exist in the NativeType, then every item in `type_ids` is `> 0 && ` + /// Invariant: every item in `type_ids` is `> 0 && < fields.len()` if `type_ids` are not + /// provided. If `type_ids` exist in the NativeType, then every item in `type_ids` is `> 0 && ` pub(crate) type_ids: ScalarBuffer, /// Invariant: `offsets.len() == type_ids.len()` pub(crate) offsets: ScalarBuffer, // In the future we'll additionally have xym, xyzm array variants. - point_xy: PointArray, - line_string_xy: LineStringArray, - polygon_xy: PolygonArray, - mpoint_xy: MultiPointArray, - mline_string_xy: MultiLineStringArray, - mpolygon_xy: MultiPolygonArray, - - point_xyz: PointArray, - line_string_xyz: LineStringArray, - polygon_xyz: PolygonArray, - mpoint_xyz: MultiPointArray, - mline_string_xyz: MultiLineStringArray, - mpolygon_xyz: MultiPolygonArray, + pub(crate) point_xy: PointArray, + pub(crate) line_string_xy: LineStringArray, + pub(crate) polygon_xy: PolygonArray, + pub(crate) mpoint_xy: MultiPointArray, + pub(crate) mline_string_xy: MultiLineStringArray, + pub(crate) mpolygon_xy: MultiPolygonArray, + pub(crate) gc_xy: GeometryCollectionArray, + + pub(crate) point_xyz: PointArray, + pub(crate) line_string_xyz: LineStringArray, + pub(crate) polygon_xyz: PolygonArray, + pub(crate) mpoint_xyz: MultiPointArray, + pub(crate) mline_string_xyz: MultiLineStringArray, + pub(crate) mpolygon_xyz: MultiPolygonArray, + pub(crate) gc_xyz: GeometryCollectionArray, /// An offset used for slicing into this array. The offset will be 0 if the array has not been /// sliced. @@ -121,12 +124,14 @@ impl UnknownGeometryArray { mpoint_xy: MultiPointArray, mline_string_xy: MultiLineStringArray, mpolygon_xy: MultiPolygonArray, + gc_xy: GeometryCollectionArray, point_xyz: PointArray, line_string_xyz: LineStringArray, polygon_xyz: PolygonArray, mpoint_xyz: MultiPointArray, mline_string_xyz: MultiLineStringArray, mpolygon_xyz: MultiPolygonArray, + gc_xyz: GeometryCollectionArray, metadata: Arc, ) -> Self { let mut coord_types = HashSet::new(); @@ -136,6 +141,7 @@ impl UnknownGeometryArray { coord_types.insert(mpoint_xy.coord_type()); coord_types.insert(mline_string_xy.coord_type()); coord_types.insert(mpolygon_xy.coord_type()); + coord_types.insert(gc_xy.coord_type()); coord_types.insert(point_xyz.coord_type()); coord_types.insert(line_string_xyz.coord_type()); @@ -143,6 +149,7 @@ impl UnknownGeometryArray { coord_types.insert(mpoint_xyz.coord_type()); coord_types.insert(mline_string_xyz.coord_type()); coord_types.insert(mpolygon_xyz.coord_type()); + coord_types.insert(gc_xyz.coord_type()); assert_eq!(coord_types.len(), 1); let coord_type = coord_types.into_iter().next().unwrap(); @@ -159,12 +166,14 @@ impl UnknownGeometryArray { mpoint_xy, mline_string_xy, mpolygon_xy, + gc_xy, point_xyz, line_string_xyz, polygon_xyz, mpoint_xyz, mline_string_xyz, mpolygon_xyz, + gc_xyz, slice_offset: 0, metadata, } @@ -180,12 +189,14 @@ impl UnknownGeometryArray { self.mpoint_xy.buffer_lengths(), self.mline_string_xy.buffer_lengths(), self.mpolygon_xy.buffer_lengths(), + self.gc_xy.buffer_lengths(), self.point_xyz.buffer_lengths(), self.line_string_xyz.buffer_lengths(), self.polygon_xyz.buffer_lengths(), self.mpoint_xyz.buffer_lengths(), self.mline_string_xyz.buffer_lengths(), self.mpolygon_xyz.buffer_lengths(), + self.gc_xyz.buffer_lengths(), false, ) } @@ -232,15 +243,112 @@ impl UnknownGeometryArray { } } + /// Return `true` if this array holds at least one geometry array of the given dimension + pub fn has_dimension(&self, dim: Dimension) -> bool { + use Dimension::*; + match dim { + XY => { + self.has_points(XY) + || self.has_line_strings(XY) + || self.has_polygons(XY) + || self.has_multi_points(XY) + || self.has_multi_line_strings(XY) + || self.has_multi_polygons(XY) + } + XYZ => { + self.has_points(XYZ) + || self.has_line_strings(XYZ) + || self.has_polygons(XYZ) + || self.has_multi_points(XYZ) + || self.has_multi_line_strings(XYZ) + || self.has_multi_polygons(XYZ) + } + } + } + + /// Return `true` if this array holds at least one geometry array of the given dimension and no + /// arrays of any other dimension. + pub fn has_only_dimension(&self, dim: Dimension) -> bool { + use Dimension::*; + match dim { + XY => self.has_dimension(XY) && !self.has_dimension(XYZ), + XYZ => self.has_dimension(XYZ) && !self.has_dimension(XY), + } + } + + // /// The number of non-empty child arrays + // fn num_non_empty_children(&self) -> usize { + // let mut count = 0; + + // if !self.point_xy.is_empty() { + // count += 1 + // }; + // if !self.line_string_xy.is_empty() { + // count += 1 + // }; + // if !self.polygon_xy.is_empty() { + // count += 1 + // }; + // if !self.mpoint_xy.is_empty() { + // count += 1 + // }; + // if !self.mline_string_xy.is_empty() { + // count += 1 + // }; + // if !self.mpolygon_xy.is_empty() { + // count += 1 + // }; + + // if !self.point_xyz.is_empty() { + // count += 1 + // }; + // if !self.line_string_xyz.is_empty() { + // count += 1 + // }; + // if !self.polygon_xyz.is_empty() { + // count += 1 + // }; + // if !self.mpoint_xyz.is_empty() { + // count += 1 + // }; + // if !self.mline_string_xyz.is_empty() { + // count += 1 + // }; + // if !self.mpolygon_xyz.is_empty() { + // count += 1 + // }; + + // count + // } + // TODO: restore to enable downcasting - // pub fn has_only_points(&self) -> bool { - // self.has_points() - // && !self.has_line_strings() - // && !self.has_polygons() - // && !self.has_multi_points() - // && !self.has_multi_line_strings() - // && !self.has_multi_polygons() + // pub fn has_only_type(&self, typ: NativeType) -> bool { + // use Dimension::*; + + // if self.num_non_empty_children() == 0 { + // // Empty array + // false + // } + + // if self.num_non_empty_children() > 1 {} + + // match typ { + // NativeType::Point(_, dim) + // } + + // self.has_points(XY) + // && !self.has_line_strings(XY) + // && !self.has_polygons(XY) + // && !self.has_multi_points(XY) + // && !self.has_multi_line_strings(XY) + // && !self.has_multi_polygons(XY) + // && !self.has_points(XYZ) + // && !self.has_line_strings(XYZ) + // && !self.has_polygons(XYZ) + // && !self.has_multi_points(XYZ) + // && !self.has_multi_line_strings(XYZ) + // && !self.has_multi_polygons(XYZ) // } // pub fn has_only_line_strings(&self) -> bool { @@ -319,6 +427,7 @@ impl UnknownGeometryArray { mpoint_xy: self.mpoint_xy.clone(), mline_string_xy: self.mline_string_xy.clone(), mpolygon_xy: self.mpolygon_xy.clone(), + gc_xy: self.gc_xy.clone(), point_xyz: self.point_xyz.clone(), line_string_xyz: self.line_string_xyz.clone(), @@ -326,6 +435,7 @@ impl UnknownGeometryArray { mpoint_xyz: self.mpoint_xyz.clone(), mline_string_xyz: self.mline_string_xyz.clone(), mpolygon_xyz: self.mpolygon_xyz.clone(), + gc_xyz: self.gc_xyz.clone(), slice_offset: self.slice_offset + offset, metadata: self.metadata.clone(), @@ -346,12 +456,14 @@ impl UnknownGeometryArray { self.mpoint_xy.into_coord_type(coord_type), self.mline_string_xy.into_coord_type(coord_type), self.mpolygon_xy.into_coord_type(coord_type), + self.gc_xy.into_coord_type(coord_type), self.point_xyz.into_coord_type(coord_type), self.line_string_xyz.into_coord_type(coord_type), self.polygon_xyz.into_coord_type(coord_type), self.mpoint_xyz.into_coord_type(coord_type), self.mline_string_xyz.into_coord_type(coord_type), self.mpolygon_xyz.into_coord_type(coord_type), + self.gc_xyz.into_coord_type(coord_type), self.metadata, ) } @@ -557,6 +669,7 @@ impl TryFrom<&UnionArray> for UnknownGeometryArray { let mut mpoint_xy: Option = None; let mut mline_string_xy: Option = None; let mut mpolygon_xy: Option = None; + let mut gc_xy: Option = None; let mut point_xyz: Option = None; let mut line_string_xyz: Option = None; @@ -564,6 +677,7 @@ impl TryFrom<&UnionArray> for UnknownGeometryArray { let mut mpoint_xyz: Option = None; let mut mline_string_xyz: Option = None; let mut mpolygon_xyz: Option = None; + let mut gc_xyz: Option = None; match value.data_type() { DataType::Union(fields, mode) => { @@ -626,6 +740,13 @@ impl TryFrom<&UnionArray> for UnknownGeometryArray { .unwrap(), ); } + 7 => { + gc_xy = Some( + (value.child(type_id).as_ref(), dimension) + .try_into() + .unwrap(), + ); + } 11 => { point_xyz = Some( (value.child(type_id).as_ref(), dimension) @@ -668,6 +789,13 @@ impl TryFrom<&UnionArray> for UnknownGeometryArray { .unwrap(), ); } + 17 => { + gc_xyz = Some( + (value.child(type_id).as_ref(), dimension) + .try_into() + .unwrap(), + ); + } _ => { return Err(GeoArrowError::General(format!( "Unexpected type_id {}", @@ -693,12 +821,14 @@ impl TryFrom<&UnionArray> for UnknownGeometryArray { mpoint_xy.unwrap_or_default(), mline_string_xy.unwrap_or_default(), mpolygon_xy.unwrap_or_default(), + gc_xy.unwrap_or_default(), point_xyz.unwrap_or_default(), line_string_xyz.unwrap_or_default(), polygon_xyz.unwrap_or_default(), mpoint_xyz.unwrap_or_default(), mline_string_xyz.unwrap_or_default(), mpolygon_xyz.unwrap_or_default(), + gc_xyz.unwrap_or_default(), Default::default(), )) } diff --git a/rust/geoarrow/src/array/unknown/builder.rs b/rust/geoarrow/src/array/unknown/builder.rs index 9ed6509e..586f61cc 100644 --- a/rust/geoarrow/src/array/unknown/builder.rs +++ b/rust/geoarrow/src/array/unknown/builder.rs @@ -4,8 +4,8 @@ use crate::array::metadata::ArrayMetadata; use crate::array::unknown::array::UnknownGeometryArray; use crate::array::unknown::capacity::UnknownCapacity; use crate::array::{ - CoordType, LineStringBuilder, MultiLineStringBuilder, MultiPointBuilder, MultiPolygonBuilder, - PointBuilder, PolygonBuilder, WKBArray, + CoordType, GeometryCollectionBuilder, LineStringBuilder, MultiLineStringBuilder, + MultiPointBuilder, MultiPolygonBuilder, PointBuilder, PolygonBuilder, WKBArray, }; use crate::datatypes::Dimension; use crate::error::{GeoArrowError, Result}; @@ -44,6 +44,7 @@ pub struct UnknownGeometryBuilder { mpoint_xy: MultiPointBuilder, mline_string_xy: MultiLineStringBuilder, mpolygon_xy: MultiPolygonBuilder, + gc_xy: GeometryCollectionBuilder, point_xyz: PointBuilder, line_string_xyz: LineStringBuilder, @@ -51,6 +52,7 @@ pub struct UnknownGeometryBuilder { mpoint_xyz: MultiPointBuilder, mline_string_xyz: MultiLineStringBuilder, mpolygon_xyz: MultiPolygonBuilder, + gc_xyz: GeometryCollectionBuilder, // Invariant: `offsets.len() == types.len()` offsets: Vec, @@ -111,82 +113,98 @@ impl<'a> UnknownGeometryBuilder { metadata: Arc, prefer_multi: bool, ) -> Self { + use Dimension::*; + // Don't store array metadata on child arrays Self { metadata, types: vec![], point_xy: PointBuilder::with_capacity_and_options( - Dimension::XY, + XY, capacity.point_xy(), coord_type, Default::default(), ), line_string_xy: LineStringBuilder::with_capacity_and_options( - Dimension::XY, + XY, capacity.line_string_xy(), coord_type, Default::default(), ), polygon_xy: PolygonBuilder::with_capacity_and_options( - Dimension::XY, + XY, capacity.polygon_xy(), coord_type, Default::default(), ), mpoint_xy: MultiPointBuilder::with_capacity_and_options( - Dimension::XY, + XY, capacity.mpoint_xy(), coord_type, Default::default(), ), mline_string_xy: MultiLineStringBuilder::with_capacity_and_options( - Dimension::XY, + XY, capacity.mline_string_xy(), coord_type, Default::default(), ), mpolygon_xy: MultiPolygonBuilder::with_capacity_and_options( - Dimension::XY, + XY, capacity.mpolygon_xy(), coord_type, Default::default(), ), + gc_xy: GeometryCollectionBuilder::with_capacity_and_options( + XY, + capacity.gc_xy(), + coord_type, + Default::default(), + prefer_multi, + ), point_xyz: PointBuilder::with_capacity_and_options( - Dimension::XYZ, + XYZ, capacity.point_xyz(), coord_type, Default::default(), ), line_string_xyz: LineStringBuilder::with_capacity_and_options( - Dimension::XYZ, + XYZ, capacity.line_string_xyz(), coord_type, Default::default(), ), polygon_xyz: PolygonBuilder::with_capacity_and_options( - Dimension::XYZ, + XYZ, capacity.polygon_xyz(), coord_type, Default::default(), ), mpoint_xyz: MultiPointBuilder::with_capacity_and_options( - Dimension::XYZ, + XYZ, capacity.mpoint_xyz(), coord_type, Default::default(), ), mline_string_xyz: MultiLineStringBuilder::with_capacity_and_options( - Dimension::XYZ, + XYZ, capacity.mline_string_xyz(), coord_type, Default::default(), ), mpolygon_xyz: MultiPolygonBuilder::with_capacity_and_options( - Dimension::XYZ, + XYZ, capacity.mpolygon_xyz(), coord_type, Default::default(), ), + gc_xyz: GeometryCollectionBuilder::with_capacity_and_options( + XYZ, + capacity.gc_xyz(), + coord_type, + Default::default(), + prefer_multi, + ), offsets: vec![], prefer_multi, deferred_nulls: 0, @@ -204,6 +222,7 @@ impl<'a> UnknownGeometryBuilder { self.mpoint_xy.reserve(capacity.mpoint_xy()); self.mline_string_xy.reserve(capacity.mline_string_xy()); self.mpolygon_xy.reserve(capacity.mpolygon_xy()); + self.gc_xy.reserve(capacity.gc_xy()); self.point_xyz.reserve(capacity.point_xyz()); self.line_string_xyz.reserve(capacity.line_string_xyz()); @@ -211,6 +230,7 @@ impl<'a> UnknownGeometryBuilder { self.mpoint_xyz.reserve(capacity.mpoint_xyz()); self.mline_string_xyz.reserve(capacity.mline_string_xyz()); self.mpolygon_xyz.reserve(capacity.mpolygon_xyz()); + self.gc_xyz.reserve(capacity.gc_xyz()); } pub fn reserve_exact(&mut self, capacity: UnknownCapacity) { @@ -226,6 +246,7 @@ impl<'a> UnknownGeometryBuilder { self.mline_string_xy .reserve_exact(capacity.mline_string_xy()); self.mpolygon_xy.reserve_exact(capacity.mpolygon_xy()); + self.gc_xy.reserve_exact(capacity.gc_xy()); self.point_xyz.reserve_exact(capacity.point_xyz()); self.line_string_xyz @@ -235,6 +256,7 @@ impl<'a> UnknownGeometryBuilder { self.mline_string_xyz .reserve_exact(capacity.mline_string_xyz()); self.mpolygon_xyz.reserve_exact(capacity.mpolygon_xyz()); + self.gc_xyz.reserve_exact(capacity.gc_xyz()); } // /// The canonical method to create a [`MixedGeometryBuilder`] out of its internal @@ -745,9 +767,7 @@ impl<'a> UnknownGeometryBuilder { if gc.num_geometries() == 1 { self.push_geometry(Some(&gc.geometry(0).unwrap()))? } else { - return Err(GeoArrowError::General( - "nested geometry collections not supported".to_string(), - )); + self.push_geometry_collection(Some(gc))? } } Rect(_) | Triangle(_) | Line(_) => todo!(), @@ -758,6 +778,60 @@ impl<'a> UnknownGeometryBuilder { Ok(()) } + /// Add a new GeometryCollection to the end of this array. + /// + /// # Errors + /// + /// This function errors iff the new last item is larger than what O supports. + #[inline] + pub fn push_geometry_collection( + &mut self, + value: Option<&impl GeometryCollectionTrait>, + ) -> Result<()> { + if let Some(gc) = value { + self.add_geometry_collection_type(gc.dim().try_into().unwrap()); + match gc.dim() { + Dimensions::Xy | Dimensions::Unknown(2) => { + // Flush deferred nulls + (0..self.deferred_nulls).for_each(|_| self.gc_xy.push_null()); + self.deferred_nulls = 0; + + self.gc_xy.push_geometry_collection(Some(gc))?; + } + Dimensions::Xyz | Dimensions::Unknown(3) => { + // Flush deferred nulls + (0..self.deferred_nulls).for_each(|_| self.gc_xyz.push_null()); + self.deferred_nulls = 0; + + self.gc_xyz.push_geometry_collection(Some(gc))?; + } + dim => { + return Err(GeoArrowError::General(format!( + "Unsupported dimension {dim:?}" + ))) + } + } + } else { + self.push_null(); + }; + + Ok(()) + } + + #[inline] + pub(crate) fn add_geometry_collection_type(&mut self, dim: Dimension) { + match dim { + Dimension::XY => { + self.offsets.push(self.gc_xy.len().try_into().unwrap()); + self.types.push(7) + } + Dimension::XYZ => { + self.offsets.push(self.gc_xyz.len().try_into().unwrap()); + self.types.push(17) + } + } + } + /// Push a null to this builder /// /// Nulls will be pushed to one of the underlying non-empty arrays, to simplify downcasting. @@ -875,12 +949,14 @@ impl From for UnknownGeometryArray { other.mpoint_xy.into(), other.mline_string_xy.into(), other.mpolygon_xy.into(), + other.gc_xy.into(), other.point_xyz.into(), other.line_string_xyz.into(), other.polygon_xyz.into(), other.mpoint_xyz.into(), other.mline_string_xyz.into(), other.mpolygon_xyz.into(), + other.gc_xyz.into(), other.metadata, ) } diff --git a/rust/geoarrow/src/array/unknown/capacity.rs b/rust/geoarrow/src/array/unknown/capacity.rs index 50c5abd1..7fab91af 100644 --- a/rust/geoarrow/src/array/unknown/capacity.rs +++ b/rust/geoarrow/src/array/unknown/capacity.rs @@ -5,6 +5,7 @@ use crate::array::multilinestring::MultiLineStringCapacity; use crate::array::multipoint::MultiPointCapacity; use crate::array::multipolygon::MultiPolygonCapacity; use crate::array::polygon::PolygonCapacity; +use crate::array::GeometryCollectionCapacity; use crate::error::Result; use geo_traits::*; @@ -24,6 +25,7 @@ pub struct UnknownCapacity { mpoint_xy: MultiPointCapacity, mline_string_xy: MultiLineStringCapacity, mpolygon_xy: MultiPolygonCapacity, + gc_xy: GeometryCollectionCapacity, point_xyz: usize, line_string_xyz: LineStringCapacity, @@ -31,6 +33,7 @@ pub struct UnknownCapacity { mpoint_xyz: MultiPointCapacity, mline_string_xyz: MultiLineStringCapacity, mpolygon_xyz: MultiPolygonCapacity, + gc_xyz: GeometryCollectionCapacity, /// Whether to prefer multi or single arrays for new geometries. prefer_multi: bool, @@ -46,6 +49,7 @@ impl UnknownCapacity { mpoint_xy: MultiPointCapacity, mline_string_xy: MultiLineStringCapacity, mpolygon_xy: MultiPolygonCapacity, + gc_xy: GeometryCollectionCapacity, point_xyz: usize, line_string_xyz: LineStringCapacity, @@ -53,6 +57,7 @@ impl UnknownCapacity { mpoint_xyz: MultiPointCapacity, mline_string_xyz: MultiLineStringCapacity, mpolygon_xyz: MultiPolygonCapacity, + gc_xyz: GeometryCollectionCapacity, prefer_multi: bool, ) -> Self { Self { @@ -63,12 +68,14 @@ impl UnknownCapacity { mpoint_xy, mline_string_xy, mpolygon_xy, + gc_xy, point_xyz, line_string_xyz, polygon_xyz, mpoint_xyz, mline_string_xyz, mpolygon_xyz, + gc_xyz, prefer_multi, } } @@ -83,12 +90,14 @@ impl UnknownCapacity { mpoint_xy: MultiPointCapacity::new_empty(), mline_string_xy: MultiLineStringCapacity::new_empty(), mpolygon_xy: MultiPolygonCapacity::new_empty(), + gc_xy: GeometryCollectionCapacity::new_empty(), point_xyz: 0, line_string_xyz: LineStringCapacity::new_empty(), polygon_xyz: PolygonCapacity::new_empty(), mpoint_xyz: MultiPointCapacity::new_empty(), mline_string_xyz: MultiLineStringCapacity::new_empty(), mpolygon_xyz: MultiPolygonCapacity::new_empty(), + gc_xyz: GeometryCollectionCapacity::new_empty(), prefer_multi, } } @@ -155,6 +164,10 @@ impl UnknownCapacity { self.mpolygon_xy } + pub fn gc_xy(&self) -> GeometryCollectionCapacity { + self.gc_xy + } + pub fn point_xyz(&self) -> usize { self.point_xyz } @@ -179,6 +192,10 @@ impl UnknownCapacity { self.mpolygon_xyz } + pub fn gc_xyz(&self) -> GeometryCollectionCapacity { + self.gc_xyz + } + // pub fn point_compatible(&self) -> bool { // self.line_string.is_empty() // && self.polygon.is_empty() @@ -362,8 +379,8 @@ impl UnknownCapacity { geo_traits::GeometryType::MultiPoint(p) => self.add_multi_point(Some(p)), geo_traits::GeometryType::MultiLineString(p) => self.add_multi_line_string(Some(p)), geo_traits::GeometryType::MultiPolygon(p) => self.add_multi_polygon(Some(p)), - geo_traits::GeometryType::GeometryCollection(_) => { - panic!("nested geometry collections not supported") + geo_traits::GeometryType::GeometryCollection(p) => { + self.add_geometry_collection(Some(p))? } _ => todo!(), }; @@ -373,6 +390,27 @@ impl UnknownCapacity { Ok(()) } + #[inline] + pub fn add_geometry_collection( + &mut self, + gc: Option<&impl GeometryCollectionTrait>, + ) -> Result<()> { + if let Some(gc) = gc { + match gc.dim() { + Dimensions::Xy | Dimensions::Unknown(2) => { + self.gc_xy.add_geometry_collection(Some(gc))?; + } + Dimensions::Xyz | Dimensions::Unknown(3) => { + self.gc_xyz.add_geometry_collection(Some(gc))?; + } + _ => todo!(), + } + } else { + self.nulls += 1; + }; + Ok(()) + } + pub fn from_geometries<'a>( geoms: impl Iterator>, prefer_multi: bool, @@ -405,6 +443,7 @@ impl UnknownCapacity { count += self.mpoint_xy.num_bytes(); count += self.mline_string_xy.num_bytes(); count += self.mpolygon_xy.num_bytes(); + count += self.gc_xy.num_bytes(); count += self.point_xyz * 3 * 8; count += self.line_string_xyz.num_bytes(); @@ -412,6 +451,7 @@ impl UnknownCapacity { count += self.mpoint_xyz.num_bytes(); count += self.mline_string_xyz.num_bytes(); count += self.mpolygon_xyz.num_bytes(); + count += self.gc_xyz.num_bytes(); count } @@ -428,6 +468,7 @@ impl AddAssign for UnknownCapacity { self.mpoint_xy = self.mpoint_xy + rhs.mpoint_xy; self.mline_string_xy = self.mline_string_xy + rhs.mline_string_xy; self.mpolygon_xy = self.mpolygon_xy + rhs.mpolygon_xy; + self.gc_xy = self.gc_xy + rhs.gc_xy; self.point_xyz = self.point_xyz + rhs.point_xyz; self.line_string_xyz = self.line_string_xyz + rhs.line_string_xyz; @@ -435,5 +476,6 @@ impl AddAssign for UnknownCapacity { self.mpoint_xyz = self.mpoint_xyz + rhs.mpoint_xyz; self.mline_string_xyz = self.mline_string_xyz + rhs.mline_string_xyz; self.mpolygon_xyz = self.mpolygon_xyz + rhs.mpolygon_xyz; + self.gc_xyz = self.gc_xyz + rhs.gc_xyz; } } diff --git a/rust/geoarrow/src/io/flatgeobuf/reader/async.rs b/rust/geoarrow/src/io/flatgeobuf/reader/async.rs index 0c55370e..92f8eb84 100644 --- a/rust/geoarrow/src/io/flatgeobuf/reader/async.rs +++ b/rust/geoarrow/src/io/flatgeobuf/reader/async.rs @@ -99,7 +99,7 @@ pub async fn read_flatgeobuf_async( ); selection.process_features(&mut builder).await?; let table = builder.finish()?; - table.downcast(true) + table.downcast() } (GeometryType::Point, true) => { impl_read!(PointBuilder, Dimension::XYZ) @@ -122,7 +122,7 @@ pub async fn read_flatgeobuf_async( ); selection.process_features(&mut builder).await?; let table = builder.finish()?; - table.downcast(true) + table.downcast() } // TODO: Parse into a GeometryCollection array and then downcast to a single-typed array if possible. geom_type => Err(GeoArrowError::NotYetImplemented(format!( diff --git a/rust/geoarrow/src/io/flatgeobuf/reader/sync.rs b/rust/geoarrow/src/io/flatgeobuf/reader/sync.rs index f41c82fd..5d5c0376 100644 --- a/rust/geoarrow/src/io/flatgeobuf/reader/sync.rs +++ b/rust/geoarrow/src/io/flatgeobuf/reader/sync.rs @@ -105,7 +105,7 @@ pub fn read_flatgeobuf( ); selection.process_features(&mut builder)?; let table = builder.finish()?; - table.downcast(true) + table.downcast() } (GeometryType::Point, true) => { impl_read!(PointBuilder, Dimension::XYZ) @@ -129,7 +129,7 @@ pub fn read_flatgeobuf( selection.process_features(&mut builder)?; let table = builder.finish()?; // TODO: 3d downcasting not implemented - // table.downcast(true) + // table.downcast() Ok(table) } // TODO: Parse into a GeometryCollection array and then downcast to a single-typed array if possible. diff --git a/rust/geoarrow/src/io/wkb/api.rs b/rust/geoarrow/src/io/wkb/api.rs index 0b733105..70d04bc0 100644 --- a/rust/geoarrow/src/io/wkb/api.rs +++ b/rust/geoarrow/src/io/wkb/api.rs @@ -124,7 +124,7 @@ impl FromWKB for Arc { arr.metadata(), true, )?; - Ok(builder.finish().downcast(true)) + Ok(builder.finish().downcast()) } } @@ -175,7 +175,7 @@ impl FromWKB for Arc { dim: Dimension, ) -> Result { let geom_arr = ChunkedGeometryCollectionArray::from_wkb(arr, coord_type, dim)?; - Ok(geom_arr.downcast(true)) + Ok(geom_arr.downcast()) } } @@ -376,7 +376,7 @@ mod test { .unwrap(); let rt_ref = roundtrip.as_ref(); let rt_mixed_arr = rt_ref.as_mixed(); - let downcasted = rt_mixed_arr.downcast(true); + let downcasted = rt_mixed_arr.downcast(); let downcasted_ref = downcasted.as_ref(); let rt_point_arr = downcasted_ref.as_point(); assert_eq!(&arr, rt_point_arr); diff --git a/rust/geoarrow/src/table.rs b/rust/geoarrow/src/table.rs index 30de22c8..9bc87bb6 100644 --- a/rust/geoarrow/src/table.rs +++ b/rust/geoarrow/src/table.rs @@ -236,7 +236,7 @@ impl Table { ChunkedNativeArrayDyn::from_geoarrow_chunks(parsed_chunks_refs.as_slice())? .into_inner() .as_ref() - .downcast(true) + .downcast() } SerializedType::LargeWKB => { let wkb_chunks = array_slices @@ -254,7 +254,7 @@ impl Table { ChunkedNativeArrayDyn::from_geoarrow_chunks(parsed_chunks_refs.as_slice())? .into_inner() .as_ref() - .downcast(true) + .downcast() } _ => panic!("WKT input not supported yet"), };