use std::sync::Arc;
use arrow_array::OffsetSizeTrait;
use arrow_buffer::NullBufferBuilder;
use geo_traits::{
GeometryCollectionTrait, GeometryTrait, LineStringTrait, MultiLineStringTrait, MultiPointTrait,
MultiPolygonTrait, PointTrait, PolygonTrait,
};
use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
use geoarrow_schema::type_id::GeometryTypeId;
use geoarrow_schema::{Dimension, GeometryCollectionType};
use crate::GeoArrowArray;
use crate::array::{GenericWkbArray, GeometryCollectionArray};
use crate::builder::geo_trait_wrappers::{LineWrapper, RectWrapper, TriangleWrapper};
use crate::builder::{MixedGeometryBuilder, OffsetsBuilder};
use crate::capacity::GeometryCollectionCapacity;
use crate::trait_::{GeoArrowArrayAccessor, GeoArrowArrayBuilder};
#[derive(Debug)]
pub struct GeometryCollectionBuilder {
data_type: GeometryCollectionType,
pub(crate) geoms: MixedGeometryBuilder,
pub(crate) geom_offsets: OffsetsBuilder<i32>,
pub(crate) validity: NullBufferBuilder,
}
impl<'a> GeometryCollectionBuilder {
pub fn new(typ: GeometryCollectionType) -> Self {
Self::with_capacity(typ, Default::default())
}
pub fn with_capacity(
typ: GeometryCollectionType,
capacity: GeometryCollectionCapacity,
) -> Self {
Self {
geoms: MixedGeometryBuilder::with_capacity_and_options(
typ.dimension(),
capacity.mixed_capacity,
typ.coord_type(),
),
geom_offsets: OffsetsBuilder::with_capacity(capacity.geom_capacity),
validity: NullBufferBuilder::new(capacity.geom_capacity),
data_type: typ,
}
}
pub fn with_prefer_multi(self, prefer_multi: bool) -> Self {
Self {
geoms: self.geoms.with_prefer_multi(prefer_multi),
..self
}
}
pub fn reserve(&mut self, additional: GeometryCollectionCapacity) {
self.geoms.reserve(additional.mixed_capacity);
self.geom_offsets.reserve(additional.geom_capacity);
}
pub fn reserve_exact(&mut self, additional: GeometryCollectionCapacity) {
self.geoms.reserve_exact(additional.mixed_capacity);
self.geom_offsets.reserve_exact(additional.geom_capacity);
}
pub fn shrink_to_fit(&mut self) {
self.geoms.shrink_to_fit();
self.geom_offsets.shrink_to_fit();
}
pub fn finish(mut self) -> GeometryCollectionArray {
let validity = self.validity.finish();
GeometryCollectionArray::new(
self.geoms.finish(),
self.geom_offsets.finish(),
validity,
self.data_type.metadata().clone(),
)
}
#[inline]
fn push_point(&mut self, value: Option<&impl PointTrait<T = f64>>) -> GeoArrowResult<()> {
if let Some(geom) = value {
self.geoms.push_point(geom)?;
self.geom_offsets.try_push_usize(1)?;
self.validity.append(value.is_some());
} else {
self.push_null();
}
Ok(())
}
#[inline]
fn push_line_string(
&mut self,
value: Option<&impl LineStringTrait<T = f64>>,
) -> GeoArrowResult<()> {
if let Some(geom) = value {
self.geoms.push_line_string(geom)?;
self.geom_offsets.try_push_usize(1)?;
self.validity.append(value.is_some());
} else {
self.push_null();
}
Ok(())
}
#[inline]
fn push_polygon(&mut self, value: Option<&impl PolygonTrait<T = f64>>) -> GeoArrowResult<()> {
if let Some(geom) = value {
self.geoms.push_polygon(geom)?;
self.geom_offsets.try_push_usize(1)?;
self.validity.append(value.is_some());
} else {
self.push_null();
}
Ok(())
}
#[inline]
fn push_multi_point(
&mut self,
value: Option<&impl MultiPointTrait<T = f64>>,
) -> GeoArrowResult<()> {
if let Some(geom) = value {
self.geoms.push_multi_point(geom)?;
self.geom_offsets.try_push_usize(1)?;
self.validity.append(value.is_some());
} else {
self.push_null();
}
Ok(())
}
#[inline]
fn push_multi_line_string(
&mut self,
value: Option<&impl MultiLineStringTrait<T = f64>>,
) -> GeoArrowResult<()> {
if let Some(geom) = value {
self.geoms.push_multi_line_string(geom)?;
self.geom_offsets.try_push_usize(1)?;
self.validity.append(value.is_some());
} else {
self.push_null();
}
Ok(())
}
#[inline]
fn push_multi_polygon(
&mut self,
value: Option<&impl MultiPolygonTrait<T = f64>>,
) -> GeoArrowResult<()> {
if let Some(geom) = value {
self.geoms.push_multi_polygon(geom)?;
self.geom_offsets.try_push_usize(1)?;
self.validity.append(value.is_some());
} else {
self.push_null();
}
Ok(())
}
#[inline]
pub fn push_geometry(
&mut self,
value: Option<&impl GeometryTrait<T = f64>>,
) -> GeoArrowResult<()> {
use geo_traits::GeometryType::*;
if let Some(g) = value {
match g.as_type() {
Point(p) => self.push_point(Some(p))?,
LineString(p) => {
self.push_line_string(Some(p))?;
}
Polygon(p) => self.push_polygon(Some(p))?,
MultiPoint(p) => self.push_multi_point(Some(p))?,
MultiLineString(p) => self.push_multi_line_string(Some(p))?,
MultiPolygon(p) => self.push_multi_polygon(Some(p))?,
GeometryCollection(p) => self.push_geometry_collection(Some(p))?,
Rect(r) => self.push_polygon(Some(&RectWrapper::try_new(r)?))?,
Triangle(tri) => self.push_polygon(Some(&TriangleWrapper(tri)))?,
Line(l) => self.push_line_string(Some(&LineWrapper(l)))?,
}
} else {
self.push_null();
};
Ok(())
}
#[inline]
pub fn push_geometry_collection(
&mut self,
value: Option<&impl GeometryCollectionTrait<T = f64>>,
) -> GeoArrowResult<()> {
if let Some(gc) = value {
let num_geoms = gc.num_geometries();
for g in gc.geometries() {
self.geoms.push_geometry(&g)?;
}
self.try_push_length(num_geoms)?;
} else {
self.push_null();
}
Ok(())
}
pub fn extend_from_iter(
&mut self,
geoms: impl Iterator<Item = Option<&'a (impl GeometryCollectionTrait<T = f64> + 'a)>>,
) {
geoms
.into_iter()
.try_for_each(|maybe_gc| self.push_geometry_collection(maybe_gc))
.unwrap();
}
#[inline]
pub(crate) fn try_push_length(&mut self, geom_offsets_length: usize) -> GeoArrowResult<()> {
self.geom_offsets.try_push_usize(geom_offsets_length)?;
self.validity.append(true);
Ok(())
}
#[inline]
pub(crate) fn push_null(&mut self) {
self.geom_offsets.extend_constant(1);
self.validity.append(false);
}
pub fn from_geometry_collections(
geoms: &[impl GeometryCollectionTrait<T = f64>],
typ: GeometryCollectionType,
) -> GeoArrowResult<Self> {
let capacity =
GeometryCollectionCapacity::from_geometry_collections(geoms.iter().map(Some))?;
let mut array = Self::with_capacity(typ, capacity);
array.extend_from_iter(geoms.iter().map(Some));
Ok(array)
}
pub fn from_nullable_geometry_collections(
geoms: &[Option<impl GeometryCollectionTrait<T = f64>>],
typ: GeometryCollectionType,
) -> GeoArrowResult<Self> {
let capacity = GeometryCollectionCapacity::from_geometry_collections(
geoms.iter().map(|x| x.as_ref()),
)?;
let mut array = Self::with_capacity(typ, capacity);
array.extend_from_iter(geoms.iter().map(|x| x.as_ref()));
Ok(array)
}
pub fn from_nullable_geometries(
geoms: &[Option<impl GeometryTrait<T = f64>>],
typ: GeometryCollectionType,
) -> GeoArrowResult<Self> {
let capacity =
GeometryCollectionCapacity::from_geometries(geoms.iter().map(|x| x.as_ref()))?;
let mut array = Self::with_capacity(typ, capacity);
for geom in geoms {
array.push_geometry(geom.as_ref())?;
}
Ok(array)
}
}
impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, GeometryCollectionType)>
for GeometryCollectionBuilder
{
type Error = GeoArrowError;
fn try_from(
(value, typ): (GenericWkbArray<O>, GeometryCollectionType),
) -> GeoArrowResult<Self> {
let wkb_objects = value
.iter()
.map(|x| x.transpose())
.collect::<GeoArrowResult<Vec<_>>>()?;
Self::from_nullable_geometries(&wkb_objects, typ)
}
}
impl GeoArrowArrayBuilder for GeometryCollectionBuilder {
fn len(&self) -> usize {
self.geom_offsets.len_proxy()
}
fn push_null(&mut self) {
self.push_null();
}
fn push_geometry(
&mut self,
geometry: Option<&impl GeometryTrait<T = f64>>,
) -> GeoArrowResult<()> {
self.push_geometry(geometry)
}
fn finish(self) -> Arc<dyn GeoArrowArray> {
Arc::new(self.finish())
}
}
impl GeometryTypeId for GeometryCollectionBuilder {
const GEOMETRY_TYPE_OFFSET: i8 = 7;
fn dimension(&self) -> Dimension {
self.data_type.dimension()
}
}