geoarrow_array/array/coord/
interleaved.rs1use std::sync::Arc;
2
3use arrow_array::{Array, FixedSizeListArray, Float64Array};
4use arrow_buffer::ScalarBuffer;
5use arrow_schema::{DataType, Field};
6use geo_traits::CoordTrait;
7use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
8use geoarrow_schema::{CoordType, Dimension, PointType};
9
10use crate::builder::InterleavedCoordBufferBuilder;
11use crate::scalar::InterleavedCoord;
12
13#[derive(Debug, Clone, PartialEq)]
18pub struct InterleavedCoordBuffer {
19 pub(crate) coords: ScalarBuffer<f64>,
20 pub(crate) dim: Dimension,
21}
22
23fn check(coords: &ScalarBuffer<f64>, dim: Dimension) -> GeoArrowResult<()> {
24 if coords.len() % dim.size() != 0 {
25 return Err(GeoArrowError::InvalidGeoArrow(
26 "Length of interleaved coordinate buffer must be a multiple of the dimension size"
27 .to_string(),
28 ));
29 }
30
31 Ok(())
32}
33
34impl InterleavedCoordBuffer {
35 pub const COORD_TYPE: CoordType = CoordType::Interleaved;
37
38 pub fn new(coords: ScalarBuffer<f64>, dim: Dimension) -> Self {
44 Self::try_new(coords, dim).unwrap()
45 }
46
47 pub fn try_new(coords: ScalarBuffer<f64>, dim: Dimension) -> GeoArrowResult<Self> {
53 check(&coords, dim)?;
54 Ok(Self { coords, dim })
55 }
56
57 pub fn from_coords<'a>(
59 coords: impl ExactSizeIterator<Item = &'a (impl CoordTrait<T = f64> + 'a)>,
60 dim: Dimension,
61 ) -> GeoArrowResult<Self> {
62 Ok(InterleavedCoordBufferBuilder::from_coords(coords, dim)?.finish())
63 }
64
65 pub fn coords(&self) -> &ScalarBuffer<f64> {
67 &self.coords
68 }
69
70 pub(crate) fn values_array(&self) -> Float64Array {
71 Float64Array::new(self.coords.clone(), None)
72 }
73
74 pub fn dim(&self) -> Dimension {
76 self.dim
77 }
78
79 pub(crate) fn values_field(&self) -> Field {
80 match self.dim {
81 Dimension::XY => Field::new("xy", DataType::Float64, false),
82 Dimension::XYZ => Field::new("xyz", DataType::Float64, false),
83 Dimension::XYM => Field::new("xym", DataType::Float64, false),
84 Dimension::XYZM => Field::new("xyzm", DataType::Float64, false),
85 }
86 }
87
88 pub(crate) fn slice(&self, offset: usize, length: usize) -> Self {
89 assert!(
90 offset + length <= self.len(),
91 "offset + length may not exceed length of array"
92 );
93 Self {
94 coords: self
95 .coords
96 .slice(offset * self.dim.size(), length * self.dim.size()),
97 dim: self.dim,
98 }
99 }
100
101 pub(crate) fn storage_type(&self) -> DataType {
102 PointType::new(self.dim, Default::default())
103 .with_coord_type(Self::COORD_TYPE)
104 .data_type()
105 }
106
107 pub fn len(&self) -> usize {
109 self.coords.len() / self.dim.size()
110 }
111
112 pub fn is_empty(&self) -> bool {
114 self.len() == 0
115 }
116
117 pub fn value(&self, index: usize) -> InterleavedCoord<'_> {
140 assert!(index <= self.len());
141 unsafe { self.value_unchecked(index) }
142 }
143
144 pub unsafe fn value_unchecked(&self, index: usize) -> InterleavedCoord<'_> {
167 InterleavedCoord {
168 coords: &self.coords,
169 i: index,
170 dim: self.dim,
171 }
172 }
173
174 pub(crate) fn from_arrow(array: &FixedSizeListArray, dim: Dimension) -> GeoArrowResult<Self> {
175 if array.value_length() != dim.size() as i32 {
176 return Err(GeoArrowError::InvalidGeoArrow(format!(
177 "Expected the FixedSizeListArray to match the dimension. Array length is {}, dimension is: {:?} have size 2",
178 array.value_length(),
179 dim
180 )));
181 }
182
183 let coord_array_values = array
184 .values()
185 .as_any()
186 .downcast_ref::<Float64Array>()
187 .unwrap();
188
189 Ok(InterleavedCoordBuffer::new(
190 coord_array_values.values().clone(),
191 dim,
192 ))
193 }
194}
195
196impl From<InterleavedCoordBuffer> for FixedSizeListArray {
197 fn from(value: InterleavedCoordBuffer) -> Self {
198 FixedSizeListArray::new(
199 Arc::new(value.values_field()),
200 value.dim.size() as i32,
201 Arc::new(value.values_array()),
202 None,
203 )
204 }
205}
206
207#[cfg(test)]
208mod test {
209 use super::*;
210
211 #[test]
212 fn test_eq_slicing() {
213 let coords1 = vec![0., 3., 1., 4., 2., 5.];
214 let buf1 = InterleavedCoordBuffer::new(coords1.into(), Dimension::XY).slice(1, 1);
215
216 let coords2 = vec![1., 4.];
217 let buf2 = InterleavedCoordBuffer::new(coords2.into(), Dimension::XY);
218
219 assert_eq!(buf1, buf2);
220 }
221}