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 capacity(&self) -> usize {
82 self.buffers[0].capacity()
83 }
84
85 pub fn len(&self) -> usize {
87 self.buffers[0].len()
88 }
89
90 pub fn is_empty(&self) -> bool {
92 self.len() == 0
93 }
94
95 pub fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) {
101 self.try_push_coord(coord).unwrap()
102 }
103
104 pub fn try_push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
110 match self.dim {
112 Dimension::XY => match coord.dim() {
113 geo_traits::Dimensions::Xy | geo_traits::Dimensions::Unknown(2) => {}
114 d => {
115 return Err(GeoArrowError::IncorrectGeometryType(format!(
116 "coord dimension must be XY for this buffer; got {d:?}."
117 )));
118 }
119 },
120 Dimension::XYZ => match coord.dim() {
121 geo_traits::Dimensions::Xyz | geo_traits::Dimensions::Unknown(3) => {}
122 d => {
123 return Err(GeoArrowError::IncorrectGeometryType(format!(
124 "coord dimension must be XYZ for this buffer; got {d:?}."
125 )));
126 }
127 },
128 Dimension::XYM => match coord.dim() {
129 geo_traits::Dimensions::Xym | geo_traits::Dimensions::Unknown(3) => {}
130 d => {
131 return Err(GeoArrowError::IncorrectGeometryType(format!(
132 "coord dimension must be XYM for this buffer; got {d:?}."
133 )));
134 }
135 },
136 Dimension::XYZM => match coord.dim() {
137 geo_traits::Dimensions::Xyzm | geo_traits::Dimensions::Unknown(4) => {}
138 d => {
139 return Err(GeoArrowError::IncorrectGeometryType(format!(
140 "coord dimension must be XYZM for this buffer; got {d:?}."
141 )));
142 }
143 },
144 }
145
146 self.buffers[0].push(coord.x());
147 self.buffers[1].push(coord.y());
148 if let Some(z) = coord.nth(2) {
149 self.buffers[2].push(z);
150 };
151 if let Some(m) = coord.nth(3) {
152 self.buffers[3].push(m);
153 };
154 Ok(())
155 }
156
157 pub(crate) fn push_constant(&mut self, value: f64) {
162 for i in 0..self.dim.size() {
163 self.buffers[i].push(value);
164 }
165 }
166
167 pub(crate) fn push_point(&mut self, point: &impl PointTrait<T = f64>) {
173 self.try_push_point(point).unwrap()
174 }
175
176 pub(crate) fn try_push_point(
182 &mut self,
183 point: &impl PointTrait<T = f64>,
184 ) -> GeoArrowResult<()> {
185 if let Some(coord) = point.coord() {
186 self.try_push_coord(&coord)?;
187 } else {
188 self.push_constant(f64::NAN);
189 };
190 Ok(())
191 }
192
193 pub fn from_coords<'a>(
195 coords: impl ExactSizeIterator<Item = &'a (impl CoordTrait<T = f64> + 'a)>,
196 dim: Dimension,
197 ) -> GeoArrowResult<Self> {
198 let mut buffer = SeparatedCoordBufferBuilder::with_capacity(coords.len(), dim);
199 for coord in coords {
200 buffer.try_push_coord(coord)?;
201 }
202 Ok(buffer)
203 }
204
205 pub fn finish(self) -> SeparatedCoordBuffer {
207 let mut buffers = core::array::from_fn(|_| vec![].into());
209 for (i, buffer) in self.buffers.into_iter().enumerate() {
210 buffers[i] = buffer.into();
211 }
212 SeparatedCoordBuffer::from_array(buffers, self.dim).unwrap()
213 }
214}
215
216#[cfg(test)]
217mod test {
218 use wkt::types::Coord;
219
220 use super::*;
221
222 #[test]
223 fn errors_when_pushing_incompatible_coord() {
224 let mut builder = SeparatedCoordBufferBuilder::new(Dimension::XY);
225 builder
226 .try_push_coord(&Coord {
227 x: 0.0,
228 y: 0.0,
229 z: Some(0.0),
230 m: None,
231 })
232 .expect_err("Should err pushing XYZ to XY buffer");
233
234 let mut builder = SeparatedCoordBufferBuilder::new(Dimension::XYZ);
235 builder
236 .try_push_coord(&Coord {
237 x: 0.0,
238 y: 0.0,
239 z: None,
240 m: None,
241 })
242 .expect_err("Should err pushing XY to XYZ buffer");
243 builder
244 .try_push_coord(&Coord {
245 x: 0.0,
246 y: 0.0,
247 z: Some(0.0),
248 m: None,
249 })
250 .unwrap();
251 }
252}