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 pub(crate) fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
190 self.coords.try_push_coord(coord)
191 }
192
193 #[inline]
197 pub fn push_geometry(
198 &mut self,
199 value: Option<&impl GeometryTrait<T = f64>>,
200 ) -> GeoArrowResult<()> {
201 if let Some(value) = value {
202 match value.as_type() {
203 GeometryType::LineString(g) => self.push_line_string(Some(g))?,
204 GeometryType::MultiLineString(ml) => {
205 let num_line_strings = ml.num_line_strings();
206 if num_line_strings == 0 {
207 self.push_empty();
208 } else if num_line_strings == 1 {
209 self.push_line_string(Some(&ml.line_string(0).unwrap()))?
210 } else {
211 return Err(GeoArrowError::IncorrectGeometryType(format!(
212 "Expected MultiLineString with only one LineString in LineStringBuilder, got {num_line_strings} line strings",
213 )));
214 }
215 }
216 GeometryType::Line(l) => self.push_line_string(Some(&LineWrapper(l)))?,
217 gt => {
218 return Err(GeoArrowError::IncorrectGeometryType(format!(
219 "Expected LineString, got {}",
220 gt.name()
221 )));
222 }
223 }
224 } else {
225 self.push_null();
226 };
227 Ok(())
228 }
229
230 pub fn from_nullable_geometries(
232 geoms: &[Option<impl GeometryTrait<T = f64>>],
233 typ: LineStringType,
234 ) -> GeoArrowResult<Self> {
235 let capacity = LineStringCapacity::from_geometries(geoms.iter().map(|x| x.as_ref()))?;
236 let mut array = Self::with_capacity(typ, capacity);
237 array.extend_from_geometry_iter(geoms.iter().map(|x| x.as_ref()))?;
238 Ok(array)
239 }
240}
241
242impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, LineStringType)> for LineStringBuilder {
243 type Error = GeoArrowError;
244
245 fn try_from((value, typ): (GenericWkbArray<O>, LineStringType)) -> GeoArrowResult<Self> {
246 let wkb_objects = value
247 .iter()
248 .map(|x| x.transpose())
249 .collect::<GeoArrowResult<Vec<_>>>()?;
250 Self::from_nullable_geometries(&wkb_objects, typ)
251 }
252}
253
254impl GeoArrowArrayBuilder for LineStringBuilder {
255 fn len(&self) -> usize {
256 self.geom_offsets.len_proxy()
257 }
258
259 fn push_null(&mut self) {
260 self.push_null();
261 }
262
263 fn push_geometry(
264 &mut self,
265 geometry: Option<&impl GeometryTrait<T = f64>>,
266 ) -> GeoArrowResult<()> {
267 self.push_geometry(geometry)
268 }
269
270 fn finish(self) -> Arc<dyn GeoArrowArray> {
271 Arc::new(self.finish())
272 }
273}