@@ -18,6 +18,50 @@ use crate::capacity::GeometryCapacity;
1818use crate :: scalar:: Geometry ;
1919use crate :: trait_:: { GeoArrowArray , GeoArrowArrayAccessor , IntoArrow } ;
2020
21+ /// Macro to implement child array accessor with proper slicing support
22+ ///
23+ /// This macro generates code to access a child array from a GeometryArray,
24+ /// handling both sliced and non-sliced cases.
25+ ///
26+ /// # Arguments
27+ /// * `$geom_arr` - The child geometry array
28+ ///
29+ /// # Returns
30+ /// A cloned or sliced version of the child array
31+ macro_rules! impl_child_accessor {
32+ ( $self: expr, $geom_arr: expr) => { {
33+ let geom_arr = $geom_arr;
34+ if !$self. is_sliced( ) {
35+ // Fast path: if not sliced, just clone the array
36+ geom_arr. clone( )
37+ } else {
38+ // Slow path: find the range of this geometry type in the sliced view
39+ let target_type_id = geom_arr. geometry_type_id( ) ;
40+ let first_index = $self. type_ids. iter( ) . position( |id| * id == target_type_id) ;
41+ let last_index = $self. type_ids. iter( ) . rposition( |id| * id == target_type_id) ;
42+
43+ match ( first_index, last_index) {
44+ ( Some ( first) , Some ( last) ) => {
45+ // Found both first and last occurrence
46+ let first_offset = $self. offsets[ first] as usize ;
47+ let last_offset = $self. offsets[ last] as usize ;
48+ geom_arr. slice( first_offset, last_offset - first_offset + 1 )
49+ }
50+ ( Some ( first) , None ) => {
51+ unreachable!( "Shouldn't happen: found first offset but not last: {first}" ) ;
52+ }
53+ ( None , Some ( last) ) => {
54+ unreachable!( "Shouldn't happen: found last offset but not first: {last}" ) ;
55+ }
56+ ( None , None ) => {
57+ // This geometry type is not present in the sliced view
58+ geom_arr. slice( 0 , 0 )
59+ }
60+ }
61+ }
62+ } } ;
63+ }
64+
2165/// An immutable array of geometries of unknown geometry type and dimension.
2266///
2367// # Invariants
@@ -160,6 +204,84 @@ impl GeometryArray {
160204 & self . offsets
161205 }
162206
207+ /// Determine whether this array has been sliced.
208+ ///
209+ /// This array has been sliced iff the total number of geometries in the child arrays does not
210+ /// equal the number of values in the type_ids array.
211+ ///
212+ /// Since the length of each child array is pre-computed, this operation is O(1).
213+ fn is_sliced ( & self ) -> bool {
214+ let mut physical_geom_len = 0 ;
215+ physical_geom_len += self . points . iter ( ) . fold ( 0 , |acc, arr| acc + arr. len ( ) ) ;
216+ physical_geom_len += self . line_strings . iter ( ) . fold ( 0 , |acc, arr| acc + arr. len ( ) ) ;
217+ physical_geom_len += self . polygons . iter ( ) . fold ( 0 , |acc, arr| acc + arr. len ( ) ) ;
218+ physical_geom_len += self . mpoints . iter ( ) . fold ( 0 , |acc, arr| acc + arr. len ( ) ) ;
219+ physical_geom_len += self
220+ . mline_strings
221+ . iter ( )
222+ . fold ( 0 , |acc, arr| acc + arr. len ( ) ) ;
223+ physical_geom_len += self . mpolygons . iter ( ) . fold ( 0 , |acc, arr| acc + arr. len ( ) ) ;
224+ physical_geom_len += self . gcs . iter ( ) . fold ( 0 , |acc, arr| acc + arr. len ( ) ) ;
225+
226+ physical_geom_len != self . type_ids . len ( )
227+ }
228+
229+ /// Access the PointArray child for the given dimension.
230+ ///
231+ /// Note that ordering will be maintained within the child array, but there may have been other
232+ /// geometries in between in the parent array.
233+ pub fn point_child ( & self , dim : Dimension ) -> PointArray {
234+ impl_child_accessor ! ( self , & self . points[ dim. order( ) ] )
235+ }
236+
237+ /// Access the LineStringArray child for the given dimension.
238+ ///
239+ /// Note that ordering will be maintained within the child array, but there may have been other
240+ /// geometries in between in the parent array.
241+ pub fn line_string_child ( & self , dim : Dimension ) -> LineStringArray {
242+ impl_child_accessor ! ( self , & self . line_strings[ dim. order( ) ] )
243+ }
244+
245+ /// Access the PolygonArray child for the given dimension.
246+ ///
247+ /// Note that ordering will be maintained within the child array, but there may have been other
248+ /// geometries in between in the parent array.
249+ pub fn polygon_child ( & self , dim : Dimension ) -> PolygonArray {
250+ impl_child_accessor ! ( self , & self . polygons[ dim. order( ) ] )
251+ }
252+
253+ /// Access the MultiPointArray child for the given dimension.
254+ ///
255+ /// Note that ordering will be maintained within the child array, but there may have been other
256+ /// geometries in between in the parent array.
257+ pub fn multi_point_child ( & self , dim : Dimension ) -> MultiPointArray {
258+ impl_child_accessor ! ( self , & self . mpoints[ dim. order( ) ] )
259+ }
260+
261+ /// Access the MultiLineStringArray child for the given dimension.
262+ ///
263+ /// Note that ordering will be maintained within the child array, but there may have been other
264+ /// geometries in between in the parent array.
265+ pub fn multi_line_string_child ( & self , dim : Dimension ) -> MultiLineStringArray {
266+ impl_child_accessor ! ( self , & self . mline_strings[ dim. order( ) ] )
267+ }
268+
269+ /// Access the MultiPolygonArray child for the given dimension.
270+ ///
271+ /// Note that ordering will be maintained within the child array, but there may have been other
272+ /// geometries in between in the parent array.
273+ pub fn multi_polygon_child ( & self , dim : Dimension ) -> MultiPolygonArray {
274+ impl_child_accessor ! ( self , & self . mpolygons[ dim. order( ) ] )
275+ }
276+
277+ /// Access the GeometryCollectionArray child for the given dimension.
278+ ///
279+ /// Note that ordering will be maintained within the child array, but there may have been other
280+ /// geometries in between in the parent array.
281+ pub fn geometry_collection_child ( & self , dim : Dimension ) -> GeometryCollectionArray {
282+ impl_child_accessor ! ( self , & self . gcs[ dim. order( ) ] )
283+ }
284+
163285 // TODO: handle slicing
164286 pub ( crate ) fn has_points ( & self , dim : Dimension ) -> bool {
165287 !self . points [ dim. order ( ) ] . is_empty ( )
@@ -872,6 +994,7 @@ impl_primitive_cast!(GeometryCollectionArray, 6);
872994
873995#[ cfg( test) ]
874996mod test {
997+ use :: wkt:: { Wkt , wkt} ;
875998 use geo_traits:: to_geo:: ToGeoGeometry ;
876999 use geoarrow_schema:: Crs ;
8771000 use geoarrow_test:: raw;
@@ -1042,4 +1165,92 @@ mod test {
10421165 assert_eq ! ( sliced, geo_arr2) ;
10431166 assert_eq ! ( sliced. value( 0 ) . unwrap( ) , geo_arr2. value( 0 ) . unwrap( ) ) ;
10441167 }
1168+
1169+ #[ test]
1170+ fn determine_if_sliced ( ) {
1171+ let geo_arr = crate :: test:: geometry:: array ( CoordType :: Separated , false ) ;
1172+ assert ! ( !geo_arr. is_sliced( ) ) ;
1173+
1174+ let sliced = geo_arr. slice ( 2 , 4 ) ;
1175+ assert ! ( sliced. is_sliced( ) ) ;
1176+ }
1177+
1178+ #[ test]
1179+ fn test_point_child_via_slicing ( ) {
1180+ let point_array = crate :: test:: point:: array ( Default :: default ( ) , Dimension :: XY ) ;
1181+ let geometry_array = GeometryArray :: from ( point_array. clone ( ) ) ;
1182+
1183+ let returned = geometry_array. point_child ( Dimension :: XY ) ;
1184+ assert_eq ! ( returned, point_array) ;
1185+
1186+ // Sliced at beginning
1187+ let sliced_geometry_array = geometry_array. slice ( 0 , 2 ) ;
1188+ let point_child = sliced_geometry_array. point_child ( Dimension :: XY ) ;
1189+ assert_eq ! ( point_child, point_array. slice( 0 , 2 ) ) ;
1190+
1191+ // Sliced in middle
1192+ let sliced_geometry_array = geometry_array. slice ( 1 , 2 ) ;
1193+ let point_child = sliced_geometry_array. point_child ( Dimension :: XY ) ;
1194+ assert_eq ! ( point_child, point_array. slice( 1 , 2 ) ) ;
1195+
1196+ // Sliced at end
1197+ let sliced_geometry_array = geometry_array. slice ( 2 , 2 ) ;
1198+ let point_child = sliced_geometry_array. point_child ( Dimension :: XY ) ;
1199+ assert_eq ! ( point_child, point_array. slice( 2 , 2 ) ) ;
1200+ }
1201+
1202+ #[ test]
1203+ fn test_point_child_mixed_geometries ( ) {
1204+ let geoms: Vec < Option < Wkt > > = vec ! [
1205+ // 2D points
1206+ Some ( wkt! { POINT ( 30. 10. ) } . into( ) ) ,
1207+ Some ( wkt! { POINT ( 40. 20. ) } . into( ) ) ,
1208+ // 3D points
1209+ Some ( wkt! { POINT Z ( 30. 10. 40. ) } . into( ) ) ,
1210+ Some ( wkt! { POINT Z ( 40. 20. 60. ) } . into( ) ) ,
1211+ // More 2D points
1212+ Some ( wkt! { POINT ( 30. 10. ) } . into( ) ) ,
1213+ Some ( wkt! { POINT ( 40. 20. ) } . into( ) ) ,
1214+ ] ;
1215+
1216+ let mut full_xy_point_arr =
1217+ PointBuilder :: new ( PointType :: new ( Dimension :: XY , Default :: default ( ) ) ) ;
1218+ for idx in [ 0 , 1 , 4 , 5 ] {
1219+ full_xy_point_arr
1220+ . push_geometry ( geoms[ idx] . as_ref ( ) )
1221+ . unwrap ( ) ;
1222+ }
1223+ let full_xy_point_arr = full_xy_point_arr. finish ( ) ;
1224+
1225+ let geometry_array = GeometryBuilder :: from_nullable_geometries ( & geoms, Default :: default ( ) )
1226+ . unwrap ( )
1227+ . finish ( ) ;
1228+
1229+ let returned = geometry_array. point_child ( Dimension :: XY ) ;
1230+ assert_eq ! ( returned, full_xy_point_arr) ;
1231+
1232+ // Sliced at beginning
1233+ let sliced_geometry_array = geometry_array. slice ( 0 , 2 ) ;
1234+ let point_child = sliced_geometry_array. point_child ( Dimension :: XY ) ;
1235+ assert_eq ! ( point_child, full_xy_point_arr. slice( 0 , 2 ) ) ;
1236+
1237+ // Sliced in middle
1238+ let sliced_geometry_array = geometry_array. slice ( 1 , 2 ) ;
1239+ let point_child = sliced_geometry_array. point_child ( Dimension :: XY ) ;
1240+ assert_eq ! ( point_child, full_xy_point_arr. slice( 1 , 1 ) ) ;
1241+
1242+ // Sliced in middle, removing all 2D points
1243+ let sliced_geometry_array = geometry_array. slice ( 2 , 2 ) ;
1244+ let point_child = sliced_geometry_array. point_child ( Dimension :: XY ) ;
1245+ assert_eq ! ( point_child, full_xy_point_arr. slice( 1 , 0 ) ) ;
1246+
1247+ let sliced_geometry_array = geometry_array. slice ( 3 , 2 ) ;
1248+ let point_child = sliced_geometry_array. point_child ( Dimension :: XY ) ;
1249+ assert_eq ! ( point_child, full_xy_point_arr. slice( 2 , 1 ) ) ;
1250+
1251+ // Sliced at end
1252+ let sliced_geometry_array = geometry_array. slice ( 4 , 2 ) ;
1253+ let point_child = sliced_geometry_array. point_child ( Dimension :: XY ) ;
1254+ assert_eq ! ( point_child, full_xy_point_arr. slice( 2 , 2 ) ) ;
1255+ }
10451256}
0 commit comments