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 pub fn shrink_to_fit(&mut self) {
82 self.coords.shrink_to_fit();
83 self.geom_offsets.shrink_to_fit();
84 }
86
87 #[inline]
90 pub(crate) fn try_push_length(&mut self, geom_offsets_length: usize) -> GeoArrowResult<()> {
91 self.geom_offsets.try_push_usize(geom_offsets_length)?;
92 self.validity.append(true);
93 Ok(())
94 }
95
96 #[inline]
98 pub fn push_empty(&mut self) {
99 self.geom_offsets.extend_constant(1);
100 self.validity.append(true);
101 }
102
103 #[inline]
105 pub(crate) fn push_null(&mut self) {
106 self.geom_offsets.extend_constant(1);
107 self.validity.append(false);
108 }
109
110 pub fn finish(mut self) -> LineStringArray {
112 let validity = self.validity.finish();
113 LineStringArray::new(
114 self.coords.finish(),
115 self.geom_offsets.finish(),
116 validity,
117 self.data_type.metadata().clone(),
118 )
119 }
120
121 pub fn from_line_strings(geoms: &[impl LineStringTrait<T = f64>], typ: LineStringType) -> Self {
123 let capacity = LineStringCapacity::from_line_strings(geoms.iter().map(Some));
124 let mut array = Self::with_capacity(typ, capacity);
125 array.extend_from_iter(geoms.iter().map(Some));
126 array
127 }
128
129 pub fn from_nullable_line_strings(
131 geoms: &[Option<impl LineStringTrait<T = f64>>],
132 typ: LineStringType,
133 ) -> Self {
134 let capacity = LineStringCapacity::from_line_strings(geoms.iter().map(|x| x.as_ref()));
135 let mut array = Self::with_capacity(typ, capacity);
136 array.extend_from_iter(geoms.iter().map(|x| x.as_ref()));
137 array
138 }
139
140 #[inline]
146 pub fn push_line_string(
147 &mut self,
148 value: Option<&impl LineStringTrait<T = f64>>,
149 ) -> GeoArrowResult<()> {
150 if let Some(line_string) = value {
151 let num_coords = line_string.num_coords();
152 for coord in line_string.coords() {
153 self.coords.try_push_coord(&coord)?;
154 }
155 self.try_push_length(num_coords)?;
156 } else {
157 self.push_null();
158 }
159 Ok(())
160 }
161
162 pub fn extend_from_iter<'a>(
164 &mut self,
165 geoms: impl Iterator<Item = Option<&'a (impl LineStringTrait<T = f64> + 'a)>>,
166 ) {
167 geoms
168 .into_iter()
169 .try_for_each(|maybe_multi_point| self.push_line_string(maybe_multi_point))
170 .unwrap();
171 }
172
173 pub fn extend_from_geometry_iter<'a>(
175 &mut self,
176 geoms: impl Iterator<Item = Option<&'a (impl GeometryTrait<T = f64> + 'a)>>,
177 ) -> GeoArrowResult<()> {
178 geoms.into_iter().try_for_each(|g| self.push_geometry(g))?;
179 Ok(())
180 }
181
182 #[inline]
189 #[allow(dead_code)]
190 pub(crate) fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
191 self.coords.try_push_coord(coord)
192 }
193
194 #[inline]
198 pub fn push_geometry(
199 &mut self,
200 value: Option<&impl GeometryTrait<T = f64>>,
201 ) -> GeoArrowResult<()> {
202 if let Some(value) = value {
203 match value.as_type() {
204 GeometryType::LineString(g) => self.push_line_string(Some(g))?,
205 GeometryType::MultiLineString(ml) => {
206 let num_line_strings = ml.num_line_strings();
207 if num_line_strings == 0 {
208 self.push_empty();
209 } else if num_line_strings == 1 {
210 self.push_line_string(Some(&ml.line_string(0).unwrap()))?
211 } else {
212 return Err(GeoArrowError::IncorrectGeometryType(format!(
213 "Expected MultiLineString with only one LineString in LineStringBuilder, got {num_line_strings} line strings",
214 )));
215 }
216 }
217 GeometryType::Line(l) => self.push_line_string(Some(&LineWrapper(l)))?,
218 gt => {
219 return Err(GeoArrowError::IncorrectGeometryType(format!(
220 "Expected LineString, got {}",
221 gt.name()
222 )));
223 }
224 }
225 } else {
226 self.push_null();
227 };
228 Ok(())
229 }
230
231 pub fn from_nullable_geometries(
233 geoms: &[Option<impl GeometryTrait<T = f64>>],
234 typ: LineStringType,
235 ) -> GeoArrowResult<Self> {
236 let capacity = LineStringCapacity::from_geometries(geoms.iter().map(|x| x.as_ref()))?;
237 let mut array = Self::with_capacity(typ, capacity);
238 array.extend_from_geometry_iter(geoms.iter().map(|x| x.as_ref()))?;
239 Ok(array)
240 }
241}
242
243impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, LineStringType)> for LineStringBuilder {
244 type Error = GeoArrowError;
245
246 fn try_from((value, typ): (GenericWkbArray<O>, LineStringType)) -> GeoArrowResult<Self> {
247 let wkb_objects = value
248 .iter()
249 .map(|x| x.transpose())
250 .collect::<GeoArrowResult<Vec<_>>>()?;
251 Self::from_nullable_geometries(&wkb_objects, typ)
252 }
253}
254
255impl GeoArrowArrayBuilder for LineStringBuilder {
256 fn len(&self) -> usize {
257 self.geom_offsets.len_proxy()
258 }
259
260 fn push_null(&mut self) {
261 self.push_null();
262 }
263
264 fn push_geometry(
265 &mut self,
266 geometry: Option<&impl GeometryTrait<T = f64>>,
267 ) -> GeoArrowResult<()> {
268 self.push_geometry(geometry)
269 }
270
271 fn finish(self) -> Arc<dyn GeoArrowArray> {
272 Arc::new(self.finish())
273 }
274}