geoarrow_array/builder/coord/
separated.rs1use geo_traits::{CoordTrait, PointTrait};
2use geoarrow_schema::Dimension;
3use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
4
5use crate::array::SeparatedCoordBuffer;
6
7#[derive(Debug, Clone)]
13pub struct SeparatedCoordBufferBuilder {
14 buffers: [Vec<f64>; 4],
15 dim: Dimension,
16}
17
18impl SeparatedCoordBufferBuilder {
19 pub fn new(dim: Dimension) -> Self {
21 Self::with_capacity(0, dim)
22 }
23
24 pub fn with_capacity(capacity: usize, dim: Dimension) -> Self {
26 let buffers = core::array::from_fn(|i| {
28 if i < dim.size() {
29 Vec::with_capacity(capacity)
30 } else {
31 Vec::new()
32 }
33 });
34
35 Self { buffers, dim }
36 }
37
38 pub fn initialize(len: usize, dim: Dimension, value: f64) -> Self {
40 let buffers = core::array::from_fn(|i| {
42 if i < dim.size() {
43 vec![value; len]
44 } else {
45 Vec::new()
46 }
47 });
48
49 Self { buffers, dim }
50 }
51
52 pub fn reserve(&mut self, additional: usize) {
58 self.buffers
59 .iter_mut()
60 .for_each(|buffer| buffer.reserve(additional))
61 }
62
63 pub fn reserve_exact(&mut self, additional: usize) {
75 self.buffers
76 .iter_mut()
77 .for_each(|buffer| buffer.reserve_exact(additional))
78 }
79
80 pub fn shrink_to_fit(&mut self) {
82 self.buffers
83 .iter_mut()
84 .for_each(|buffer| buffer.shrink_to_fit());
85 }
86
87 pub fn capacity(&self) -> usize {
89 self.buffers[0].capacity()
90 }
91
92 pub fn len(&self) -> usize {
94 self.buffers[0].len()
95 }
96
97 pub fn is_empty(&self) -> bool {
99 self.len() == 0
100 }
101
102 pub fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) {
108 self.try_push_coord(coord).unwrap()
109 }
110
111 pub fn try_push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
117 match self.dim {
119 Dimension::XY => match coord.dim() {
120 geo_traits::Dimensions::Xy | geo_traits::Dimensions::Unknown(2) => {}
121 d => {
122 return Err(GeoArrowError::IncorrectGeometryType(format!(
123 "coord dimension must be XY for this buffer; got {d:?}."
124 )));
125 }
126 },
127 Dimension::XYZ => match coord.dim() {
128 geo_traits::Dimensions::Xyz | geo_traits::Dimensions::Unknown(3) => {}
129 d => {
130 return Err(GeoArrowError::IncorrectGeometryType(format!(
131 "coord dimension must be XYZ for this buffer; got {d:?}."
132 )));
133 }
134 },
135 Dimension::XYM => match coord.dim() {
136 geo_traits::Dimensions::Xym | geo_traits::Dimensions::Unknown(3) => {}
137 d => {
138 return Err(GeoArrowError::IncorrectGeometryType(format!(
139 "coord dimension must be XYM for this buffer; got {d:?}."
140 )));
141 }
142 },
143 Dimension::XYZM => match coord.dim() {
144 geo_traits::Dimensions::Xyzm | geo_traits::Dimensions::Unknown(4) => {}
145 d => {
146 return Err(GeoArrowError::IncorrectGeometryType(format!(
147 "coord dimension must be XYZM for this buffer; got {d:?}."
148 )));
149 }
150 },
151 }
152
153 self.buffers[0].push(coord.x());
154 self.buffers[1].push(coord.y());
155 if let Some(z) = coord.nth(2) {
156 self.buffers[2].push(z);
157 };
158 if let Some(m) = coord.nth(3) {
159 self.buffers[3].push(m);
160 };
161 Ok(())
162 }
163
164 pub(crate) fn push_constant(&mut self, value: f64) {
169 for i in 0..self.dim.size() {
170 self.buffers[i].push(value);
171 }
172 }
173
174 pub(crate) fn push_point(&mut self, point: &impl PointTrait<T = f64>) {
180 self.try_push_point(point).unwrap()
181 }
182
183 pub(crate) fn try_push_point(
189 &mut self,
190 point: &impl PointTrait<T = f64>,
191 ) -> GeoArrowResult<()> {
192 if let Some(coord) = point.coord() {
193 self.try_push_coord(&coord)?;
194 } else {
195 self.push_constant(f64::NAN);
196 };
197 Ok(())
198 }
199
200 pub fn from_coords<'a>(
202 coords: impl ExactSizeIterator<Item = &'a (impl CoordTrait<T = f64> + 'a)>,
203 dim: Dimension,
204 ) -> GeoArrowResult<Self> {
205 let mut buffer = SeparatedCoordBufferBuilder::with_capacity(coords.len(), dim);
206 for coord in coords {
207 buffer.try_push_coord(coord)?;
208 }
209 Ok(buffer)
210 }
211
212 pub fn finish(self) -> SeparatedCoordBuffer {
214 let mut buffers = core::array::from_fn(|_| vec![].into());
216 for (i, buffer) in self.buffers.into_iter().enumerate() {
217 buffers[i] = buffer.into();
218 }
219 SeparatedCoordBuffer::from_array(buffers, self.dim).unwrap()
220 }
221}
222
223#[cfg(test)]
224mod test {
225 use wkt::types::Coord;
226
227 use super::*;
228
229 #[test]
230 fn errors_when_pushing_incompatible_coord() {
231 let mut builder = SeparatedCoordBufferBuilder::new(Dimension::XY);
232 builder
233 .try_push_coord(&Coord {
234 x: 0.0,
235 y: 0.0,
236 z: Some(0.0),
237 m: None,
238 })
239 .expect_err("Should err pushing XYZ to XY buffer");
240
241 let mut builder = SeparatedCoordBufferBuilder::new(Dimension::XYZ);
242 builder
243 .try_push_coord(&Coord {
244 x: 0.0,
245 y: 0.0,
246 z: None,
247 m: None,
248 })
249 .expect_err("Should err pushing XY to XYZ buffer");
250 builder
251 .try_push_coord(&Coord {
252 x: 0.0,
253 y: 0.0,
254 z: Some(0.0),
255 m: None,
256 })
257 .unwrap();
258 }
259}