use std::sync::Arc;
use arrow_array::OffsetSizeTrait;
use arrow_buffer::NullBufferBuilder;
use geo_traits::{
CoordTrait, GeometryTrait, GeometryType, LineStringTrait, MultiPolygonTrait, PolygonTrait,
};
use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
use geoarrow_schema::type_id::GeometryTypeId;
use geoarrow_schema::{Dimension, MultiPolygonType};
use crate::GeoArrowArray;
use crate::array::{GenericWkbArray, MultiPolygonArray};
use crate::builder::geo_trait_wrappers::RectWrapper;
use crate::builder::{CoordBufferBuilder, OffsetsBuilder};
use crate::capacity::MultiPolygonCapacity;
use crate::trait_::{GeoArrowArrayAccessor, GeoArrowArrayBuilder};
use crate::util::GeometryTypeName;
#[derive(Debug)]
pub struct MultiPolygonBuilder {
data_type: MultiPolygonType,
pub(crate) coords: CoordBufferBuilder,
pub(crate) geom_offsets: OffsetsBuilder<i32>,
pub(crate) polygon_offsets: OffsetsBuilder<i32>,
pub(crate) ring_offsets: OffsetsBuilder<i32>,
pub(crate) validity: NullBufferBuilder,
}
impl MultiPolygonBuilder {
pub fn new(typ: MultiPolygonType) -> Self {
Self::with_capacity(typ, Default::default())
}
pub fn with_capacity(typ: MultiPolygonType, capacity: MultiPolygonCapacity) -> Self {
let coords = CoordBufferBuilder::with_capacity(
capacity.coord_capacity,
typ.coord_type(),
typ.dimension(),
);
Self {
coords,
geom_offsets: OffsetsBuilder::with_capacity(capacity.geom_capacity),
polygon_offsets: OffsetsBuilder::with_capacity(capacity.polygon_capacity),
ring_offsets: OffsetsBuilder::with_capacity(capacity.ring_capacity),
validity: NullBufferBuilder::new(capacity.geom_capacity),
data_type: typ,
}
}
pub fn reserve(&mut self, additional: MultiPolygonCapacity) {
self.coords.reserve(additional.coord_capacity);
self.ring_offsets.reserve(additional.ring_capacity);
self.polygon_offsets.reserve(additional.polygon_capacity);
self.geom_offsets.reserve(additional.geom_capacity);
}
pub fn reserve_exact(&mut self, additional: MultiPolygonCapacity) {
self.coords.reserve_exact(additional.coord_capacity);
self.ring_offsets.reserve_exact(additional.ring_capacity);
self.polygon_offsets
.reserve_exact(additional.polygon_capacity);
self.geom_offsets.reserve_exact(additional.geom_capacity);
}
pub fn shrink_to_fit(&mut self) {
self.coords.shrink_to_fit();
self.ring_offsets.shrink_to_fit();
self.polygon_offsets.shrink_to_fit();
self.geom_offsets.shrink_to_fit();
}
pub fn finish(mut self) -> MultiPolygonArray {
let validity = self.validity.finish();
MultiPolygonArray::new(
self.coords.finish(),
self.geom_offsets.finish(),
self.polygon_offsets.finish(),
self.ring_offsets.finish(),
validity,
self.data_type.metadata().clone(),
)
}
#[inline]
pub fn push_polygon(
&mut self,
value: Option<&impl PolygonTrait<T = f64>>,
) -> GeoArrowResult<()> {
if let Some(polygon) = value {
let exterior_ring = polygon.exterior();
if exterior_ring.is_none() {
self.push_empty()?;
return Ok(());
}
if let Some(ext_ring) = polygon.exterior() {
let num_polygons = 1;
self.geom_offsets.try_push_usize(num_polygons)?;
for coord in ext_ring.coords() {
self.coords.push_coord(&coord);
}
self.polygon_offsets
.try_push_usize(polygon.num_interiors() + 1)?;
self.ring_offsets.try_push_usize(ext_ring.num_coords())?;
for int_ring in polygon.interiors() {
self.ring_offsets.try_push_usize(int_ring.num_coords())?;
for coord in int_ring.coords() {
self.coords.push_coord(&coord);
}
}
} else {
let num_polygons = 0;
self.geom_offsets.try_push_usize(num_polygons)?;
}
} else {
self.push_null();
};
Ok(())
}
#[inline]
pub fn push_multi_polygon(
&mut self,
value: Option<&impl MultiPolygonTrait<T = f64>>,
) -> GeoArrowResult<()> {
if let Some(multi_polygon) = value {
let num_polygons = multi_polygon.num_polygons();
self.try_push_geom_offset(num_polygons)?;
for polygon in multi_polygon.polygons() {
let ext_ring = polygon.exterior().unwrap();
for coord in ext_ring.coords() {
self.coords.push_coord(&coord);
}
self.polygon_offsets
.try_push_usize(polygon.num_interiors() + 1)?;
self.ring_offsets.try_push_usize(ext_ring.num_coords())?;
for int_ring in polygon.interiors() {
self.ring_offsets.try_push_usize(int_ring.num_coords())?;
for coord in int_ring.coords() {
self.coords.push_coord(&coord);
}
}
}
} else {
self.push_null();
};
Ok(())
}
#[inline]
pub fn push_geometry(
&mut self,
value: Option<&impl GeometryTrait<T = f64>>,
) -> GeoArrowResult<()> {
if let Some(value) = value {
match value.as_type() {
GeometryType::Polygon(g) => self.push_polygon(Some(g))?,
GeometryType::MultiPolygon(g) => self.push_multi_polygon(Some(g))?,
GeometryType::Rect(g) => self.push_polygon(Some(&RectWrapper::try_new(g)?))?,
gt => {
return Err(GeoArrowError::IncorrectGeometryType(format!(
"Expected MultiPolygon compatible geometry, got {}",
gt.name()
)));
}
}
} else {
self.push_null();
};
Ok(())
}
pub fn extend_from_iter<'a>(
&mut self,
geoms: impl Iterator<Item = Option<&'a (impl MultiPolygonTrait<T = f64> + 'a)>>,
) {
geoms
.into_iter()
.try_for_each(|maybe_multi_polygon| self.push_multi_polygon(maybe_multi_polygon))
.unwrap();
}
pub fn extend_from_geometry_iter<'a>(
&mut self,
geoms: impl Iterator<Item = Option<&'a (impl GeometryTrait<T = f64> + 'a)>>,
) -> GeoArrowResult<()> {
geoms.into_iter().try_for_each(|g| self.push_geometry(g))?;
Ok(())
}
#[inline]
pub(crate) fn try_push_geom_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
self.geom_offsets.try_push_usize(offsets_length)?;
self.validity.append(true);
Ok(())
}
#[inline]
#[allow(dead_code)]
pub(crate) fn try_push_polygon_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
self.polygon_offsets.try_push_usize(offsets_length)?;
Ok(())
}
#[inline]
#[allow(dead_code)]
pub(crate) fn try_push_ring_offset(&mut self, offsets_length: usize) -> GeoArrowResult<()> {
self.ring_offsets.try_push_usize(offsets_length)?;
Ok(())
}
#[inline]
pub unsafe fn push_coord(&mut self, coord: &impl CoordTrait<T = f64>) -> GeoArrowResult<()> {
self.coords.push_coord(coord);
Ok(())
}
#[inline]
pub(crate) fn push_empty(&mut self) -> GeoArrowResult<()> {
self.geom_offsets.try_push_usize(0)?;
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_multi_polygons(
geoms: &[impl MultiPolygonTrait<T = f64>],
typ: MultiPolygonType,
) -> Self {
let capacity = MultiPolygonCapacity::from_multi_polygons(geoms.iter().map(Some));
let mut array = Self::with_capacity(typ, capacity);
array.extend_from_iter(geoms.iter().map(Some));
array
}
pub fn from_nullable_multi_polygons(
geoms: &[Option<impl MultiPolygonTrait<T = f64>>],
typ: MultiPolygonType,
) -> Self {
let capacity = MultiPolygonCapacity::from_multi_polygons(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()));
array
}
pub fn from_nullable_geometries(
geoms: &[Option<impl GeometryTrait<T = f64>>],
typ: MultiPolygonType,
) -> GeoArrowResult<Self> {
let capacity = MultiPolygonCapacity::from_geometries(geoms.iter().map(|x| x.as_ref()))?;
let mut array = Self::with_capacity(typ, capacity);
array.extend_from_geometry_iter(geoms.iter().map(|x| x.as_ref()))?;
Ok(array)
}
}
impl<O: OffsetSizeTrait> TryFrom<(GenericWkbArray<O>, MultiPolygonType)> for MultiPolygonBuilder {
type Error = GeoArrowError;
fn try_from((value, typ): (GenericWkbArray<O>, MultiPolygonType)) -> GeoArrowResult<Self> {
let wkb_objects = value
.iter()
.map(|x| x.transpose())
.collect::<GeoArrowResult<Vec<_>>>()?;
Self::from_nullable_geometries(&wkb_objects, typ)
}
}
impl GeoArrowArrayBuilder for MultiPolygonBuilder {
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 MultiPolygonBuilder {
const GEOMETRY_TYPE_OFFSET: i8 = 6;
fn dimension(&self) -> Dimension {
self.data_type.dimension()
}
}