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(crate) fn value(&self, index: usize) -> InterleavedCoord<'_> {
118 assert!(index <= self.len());
119 self.value_unchecked(index)
120 }
121
122 pub(crate) fn value_unchecked(&self, index: usize) -> InterleavedCoord<'_> {
123 InterleavedCoord {
124 coords: &self.coords,
125 i: index,
126 dim: self.dim,
127 }
128 }
129
130 pub(crate) fn from_arrow(array: &FixedSizeListArray, dim: Dimension) -> GeoArrowResult<Self> {
131 if array.value_length() != dim.size() as i32 {
132 return Err(GeoArrowError::InvalidGeoArrow(format!(
133 "Expected the FixedSizeListArray to match the dimension. Array length is {}, dimension is: {:?} have size 2",
134 array.value_length(),
135 dim
136 )));
137 }
138
139 let coord_array_values = array
140 .values()
141 .as_any()
142 .downcast_ref::<Float64Array>()
143 .unwrap();
144
145 Ok(InterleavedCoordBuffer::new(
146 coord_array_values.values().clone(),
147 dim,
148 ))
149 }
150}
151
152impl From<InterleavedCoordBuffer> for FixedSizeListArray {
153 fn from(value: InterleavedCoordBuffer) -> Self {
154 FixedSizeListArray::new(
155 Arc::new(value.values_field()),
156 value.dim.size() as i32,
157 Arc::new(value.values_array()),
158 None,
159 )
160 }
161}
162
163#[cfg(test)]
164mod test {
165 use super::*;
166
167 #[test]
168 fn test_eq_slicing() {
169 let coords1 = vec![0., 3., 1., 4., 2., 5.];
170 let buf1 = InterleavedCoordBuffer::new(coords1.into(), Dimension::XY).slice(1, 1);
171
172 let coords2 = vec![1., 4.];
173 let buf2 = InterleavedCoordBuffer::new(coords2.into(), Dimension::XY);
174
175 assert_eq!(buf1, buf2);
176 }
177}