geoarrow_array/builder/coord/
interleaved.rs1use core::f64;
2
3use geo_traits::{CoordTrait, PointTrait};
4use geoarrow_schema::Dimension;
5use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
6
7use crate::array::InterleavedCoordBuffer;
8
9#[derive(Debug, Clone)]
15pub struct InterleavedCoordBufferBuilder {
16 pub(crate) coords: Vec<f64>,
17 dim: Dimension,
18}
19
20impl InterleavedCoordBufferBuilder {
21 pub fn new(dim: Dimension) -> Self {
23 Self::with_capacity(0, dim)
24 }
25
26 pub fn with_capacity(capacity: usize, dim: Dimension) -> Self {
28 Self {
29 coords: Vec::with_capacity(capacity * dim.size()),
30 dim,
31 }
32 }
33
34 pub fn initialize(len: usize, dim: Dimension, value: f64) -> Self {
36 Self {
37 coords: vec![value; len * dim.size()],
38 dim,
39 }
40 }
41
42 pub fn reserve(&mut self, additional: usize) {
48 self.coords.reserve(additional * self.dim.size());
49 }
50
51 pub fn reserve_exact(&mut self, additional: usize) {
63 self.coords.reserve_exact(additional * self.dim.size());
64 }
65
66 pub fn shrink_to_fit(&mut self) {
68 self.coords.shrink_to_fit();
69 }
70
71 pub fn capacity(&self) -> usize {
73 self.coords.capacity() / self.dim.size()
74 }
75
76 pub fn len(&self) -> usize {
78 self.coords.len() / self.dim.size()
79 }
80
81 pub fn is_empty(&self) -> bool {
83 self.len() == 0
84 }
85
86 pub fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) {
92 self.try_push_coord(coord).unwrap()
93 }
94
95 pub fn try_push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
101 match self.dim {
103 Dimension::XY => match coord.dim() {
104 geo_traits::Dimensions::Xy | geo_traits::Dimensions::Unknown(2) => {}
105 d => {
106 return Err(GeoArrowError::IncorrectGeometryType(format!(
107 "coord dimension must be XY for this buffer; got {d:?}."
108 )));
109 }
110 },
111 Dimension::XYZ => match coord.dim() {
112 geo_traits::Dimensions::Xyz | geo_traits::Dimensions::Unknown(3) => {}
113 d => {
114 return Err(GeoArrowError::IncorrectGeometryType(format!(
115 "coord dimension must be XYZ for this buffer; got {d:?}."
116 )));
117 }
118 },
119 Dimension::XYM => match coord.dim() {
120 geo_traits::Dimensions::Xym | geo_traits::Dimensions::Unknown(3) => {}
121 d => {
122 return Err(GeoArrowError::IncorrectGeometryType(format!(
123 "coord dimension must be XYM for this buffer; got {d:?}."
124 )));
125 }
126 },
127 Dimension::XYZM => match coord.dim() {
128 geo_traits::Dimensions::Xyzm | geo_traits::Dimensions::Unknown(4) => {}
129 d => {
130 return Err(GeoArrowError::IncorrectGeometryType(format!(
131 "coord dimension must be XYZM for this buffer; got {d:?}."
132 )));
133 }
134 },
135 }
136
137 self.coords.push(coord.x());
138 self.coords.push(coord.y());
139 if let Some(z) = coord.nth(2) {
140 self.coords.push(z);
141 };
142 if let Some(m) = coord.nth(3) {
143 self.coords.push(m);
144 };
145 Ok(())
146 }
147
148 pub(crate) fn push_constant(&mut self, value: f64) {
153 for _ in 0..self.dim.size() {
154 self.coords.push(value);
155 }
156 }
157
158 pub(crate) fn push_point(&mut self, point: &impl PointTrait<T = f64>) {
164 self.try_push_point(point).unwrap()
165 }
166
167 pub(crate) fn try_push_point(
173 &mut self,
174 point: &impl PointTrait<T = f64>,
175 ) -> GeoArrowResult<()> {
176 if let Some(coord) = point.coord() {
177 self.try_push_coord(&coord)?;
178 } else {
179 self.push_constant(f64::NAN);
180 };
181 Ok(())
182 }
183
184 pub fn from_coords<'a>(
186 coords: impl ExactSizeIterator<Item = &'a (impl CoordTrait<T = f64> + 'a)>,
187 dim: Dimension,
188 ) -> GeoArrowResult<Self> {
189 let mut buffer = InterleavedCoordBufferBuilder::with_capacity(coords.len(), dim);
190 for coord in coords {
191 buffer.push_coord(coord);
192 }
193 Ok(buffer)
194 }
195
196 pub fn finish(self) -> InterleavedCoordBuffer {
198 InterleavedCoordBuffer::new(self.coords.into(), self.dim)
199 }
200}
201
202#[cfg(test)]
203mod test {
204 use wkt::types::Coord;
205
206 use super::*;
207
208 #[test]
209 fn errors_when_pushing_incompatible_coord() {
210 let mut builder = InterleavedCoordBufferBuilder::new(Dimension::XY);
211 builder
212 .try_push_coord(&Coord {
213 x: 0.0,
214 y: 0.0,
215 z: Some(0.0),
216 m: None,
217 })
218 .expect_err("Should err pushing XYZ to XY buffer");
219
220 let mut builder = InterleavedCoordBufferBuilder::new(Dimension::XYZ);
221 builder
222 .try_push_coord(&Coord {
223 x: 0.0,
224 y: 0.0,
225 z: None,
226 m: None,
227 })
228 .expect_err("Should err pushing XY to XYZ buffer");
229 builder
230 .try_push_coord(&Coord {
231 x: 0.0,
232 y: 0.0,
233 z: Some(0.0),
234 m: None,
235 })
236 .unwrap();
237 }
238}