geoarrow_array/builder/
linestring.rs1use std::sync::Arc;
2
3use arrow_array::OffsetSizeTrait;
4use arrow_buffer::NullBufferBuilder;
5use geo_traits::{CoordTrait, GeometryTrait, GeometryType, LineStringTrait, MultiLineStringTrait};
6use geoarrow_schema::LineStringType;
7use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
8
9use crate::GeoArrowArray;
10use crate::array::{GenericWkbArray, LineStringArray};
11use crate::builder::geo_trait_wrappers::LineWrapper;
12use crate::builder::{CoordBufferBuilder, OffsetsBuilder};
13use crate::capacity::LineStringCapacity;
14use crate::trait_::{GeoArrowArrayAccessor, GeoArrowArrayBuilder};
15use crate::util::GeometryTypeName;
16
17#[derive(Debug)]
21pub struct LineStringBuilder {
22 data_type: LineStringType,
23
24 pub(crate) coords: CoordBufferBuilder,
25
26 pub(crate) geom_offsets: OffsetsBuilder<i32>,
28
29 pub(crate) validity: NullBufferBuilder,
31}
32
33impl LineStringBuilder {
34 pub fn new(typ: LineStringType) -> Self {
36 Self::with_capacity(typ, Default::default())
37 }
38
39 pub fn with_capacity(typ: LineStringType, capacity: LineStringCapacity) -> Self {
41 let coords = CoordBufferBuilder::with_capacity(
42 capacity.coord_capacity,
43 typ.coord_type(),
44 typ.dimension(),
45 );
46 Self {
47 coords,
48 geom_offsets: OffsetsBuilder::with_capacity(capacity.geom_capacity()),
49 validity: NullBufferBuilder::new(capacity.geom_capacity()),
50 data_type: typ,
51 }
52 }
53
54 pub fn reserve(&mut self, additional: LineStringCapacity) {
60 self.coords.reserve(additional.coord_capacity());
61 self.geom_offsets.reserve(additional.geom_capacity());
62 }
63
64 pub fn reserve_exact(&mut self, additional: LineStringCapacity) {
76 self.coords.reserve_exact(additional.coord_capacity());
77 self.geom_offsets.reserve_exact(additional.geom_capacity());
78 }
79
80 #[inline]
83 pub(crate) fn try_push_length(&mut self, geom_offsets_length: usize) -> GeoArrowResult<()> {
84 self.geom_offsets.try_push_usize(geom_offsets_length)?;
85 self.validity.append(true);
86 Ok(())
87 }
88
89 #[inline]
91 pub fn push_empty(&mut self) {
92 self.geom_offsets.extend_constant(1);
93 self.validity.append(true);
94 }
95
96 #[inline]
98 pub(crate) fn push_null(&mut self) {
99 self.geom_offsets.extend_constant(1);
100 self.validity.append(false);
101 }
102
103 pub fn finish(mut self) -> LineStringArray {
105 let validity = self.validity.finish();
106 LineStringArray::new(
107 self.coords.finish(),
108 self.geom_offsets.finish(),
109 validity,
110 self.data_type.metadata().clone(),
111 )
112 }
113
114 pub fn from_line_strings(geoms: &[impl LineStringTrait<T = f64>], typ: LineStringType) -> Self {
116 let capacity = LineStringCapacity::from_line_strings(geoms.iter().map(Some));
117 let mut array = Self::with_capacity(typ, capacity);
118 array.extend_from_iter(geoms.iter().map(Some));
119 array
120 }
121
122 pub fn from_nullable_line_strings(
124 geoms: &[Option<impl LineStringTrait<T = f64>>],
125 typ: LineStringType,
126 ) -> Self {
127 let capacity = LineStringCapacity::from_line_strings(geoms.iter().map(|x| x.as_ref()));
128 let mut array = Self::with_capacity(typ, capacity);
129 array.extend_from_iter(geoms.iter().map(|x| x.as_ref()));
130 array
131 }
132
133 #[inline]
139 pub fn push_line_string(
140 &mut self,
141 value: Option<&impl LineStringTrait<T = f64>>,
142 ) -> GeoArrowResult<()> {
143 if let Some(line_string) = value {
144 let num_coords = line_string.num_coords();
145 for coord in line_string.coords() {
146 self.coords.try_push_coord(&coord)?;
147 }
148 self.try_push_length(num_coords)?;
149 } else {
150 self.push_null();
151 }
152 Ok(())
153 }
154
155 pub fn extend_from_iter<'a>(
157 &mut self,
158 geoms: impl Iterator<Item = Option<&'a (impl LineStringTrait<T = f64> + 'a)>>,
159 ) {
160 geoms
161 .into_iter()
162 .try_for_each(|maybe_multi_point| self.push_line_string(maybe_multi_point))
163 .unwrap();
164 }
165
166 pub fn extend_from_geometry_iter<'a>(
168 &mut self,
169 geoms: impl Iterator<Item = Option<&'a (impl GeometryTrait<T = f64> + 'a)>>,
170 ) -> GeoArrowResult<()> {
171 geoms.into_iter().try_for_each(|g| self.push_geometry(g))?;
172 Ok(())
173 }
174
175 #[inline]
182 pub(crate) fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
183 self.coords.try_push_coord(coord)
184 }
185
186 #[inline]
190 pub fn push_geometry(
191 &mut self,
192 value: Option<&impl GeometryTrait<T = f64>>,
193 ) -> GeoArrowResult<()> {
194 if let Some(value) = value {
195 match value.as_type() {
196 GeometryType::LineString(g) => self.push_line_string(Some(g))?,
197 GeometryType::MultiLineString(ml) => {
198 let num_line_strings = ml.num_line_strings();
199 if num_line_strings == 0 {
200 self.push_empty();
201 } else if num_line_strings == 1 {
202 self.push_line_string(Some(&ml.line_string(0).unwrap()))?
203 } else {
204 return Err(GeoArrowError::IncorrectGeometryType(format!(
205 "Expected MultiLineString with only one LineString in LineStringBuilder, got {} line strings",
206 num_line_strings
207 )));
208 }
209 }
210 GeometryType::Line(l) => self.push_line_string(Some(&LineWrapper(l)))?,
211 gt => {
212 return Err(GeoArrowError::IncorrectGeometryType(format!(
213 "Expected LineString, got {}",
214 gt.name()
215 )));
216 }
217 }
218 } else {
219 self.push_null();
220 };
221 Ok(())
222 }
223
224 pub fn from_nullable_geometries(
226 geoms: &[Option<impl GeometryTrait<T = f64>>],
227 typ: LineStringType,
228 ) -> GeoArrowResult<Self> {
229 let capacity = LineStringCapacity::from_geometries(geoms.iter().map(|x| x.as_ref()))?;
230 let mut array = Self::with_capacity(typ, capacity);
231 array.extend_from_geometry_iter(geoms.iter().map(|x| x.as_ref()))?;
232 Ok(array)
233 }
234}
235
236impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, LineStringType)> for LineStringBuilder {
237 type Error = GeoArrowError;
238
239 fn try_from((value, typ): (GenericWkbArray<O>, LineStringType)) -> GeoArrowResult<Self> {
240 let wkb_objects = value
241 .iter()
242 .map(|x| x.transpose())
243 .collect::<GeoArrowResult<Vec<_>>>()?;
244 Self::from_nullable_geometries(&wkb_objects, typ)
245 }
246}
247
248impl GeoArrowArrayBuilder for LineStringBuilder {
249 fn len(&self) -> usize {
250 self.geom_offsets.len_proxy()
251 }
252
253 fn push_null(&mut self) {
254 self.push_null();
255 }
256
257 fn push_geometry(
258 &mut self,
259 geometry: Option<&impl GeometryTrait<T = f64>>,
260 ) -> GeoArrowResult<()> {
261 self.push_geometry(geometry)
262 }
263
264 fn finish(self) -> Arc<dyn GeoArrowArray> {
265 Arc::new(self.finish())
266 }
267}