geoarrow_array/capacity/
multipolygon.rs

1use std::ops::{Add, AddAssign};
2
3use geo_traits::{GeometryTrait, GeometryType, LineStringTrait, MultiPolygonTrait, PolygonTrait};
4use geoarrow_schema::Dimension;
5use geoarrow_schema::error::{GeoArrowError, GeoArrowResult};
6
7use crate::capacity::PolygonCapacity;
8use crate::util::GeometryTypeName;
9
10/// A counter for the buffer sizes of a [`MultiPolygonArray`][crate::array::MultiPolygonArray].
11///
12/// This can be used to reduce allocations by allocating once for exactly the array size you need.
13#[derive(Debug, Clone, Copy)]
14pub struct MultiPolygonCapacity {
15    pub(crate) coord_capacity: usize,
16    pub(crate) ring_capacity: usize,
17    pub(crate) polygon_capacity: usize,
18    pub(crate) geom_capacity: usize,
19}
20
21impl MultiPolygonCapacity {
22    /// Create a new capacity with known sizes.
23    pub fn new(
24        coord_capacity: usize,
25        ring_capacity: usize,
26        polygon_capacity: usize,
27        geom_capacity: usize,
28    ) -> Self {
29        Self {
30            coord_capacity,
31            ring_capacity,
32            polygon_capacity,
33            geom_capacity,
34        }
35    }
36
37    /// Create a new empty capacity.
38    pub fn new_empty() -> Self {
39        Self::new(0, 0, 0, 0)
40    }
41
42    /// Return `true` if the capacity is empty.
43    pub fn is_empty(&self) -> bool {
44        self.coord_capacity == 0
45            && self.ring_capacity == 0
46            && self.polygon_capacity == 0
47            && self.geom_capacity == 0
48    }
49
50    /// The coordinate buffer capacity
51    pub fn coord_capacity(&self) -> usize {
52        self.coord_capacity
53    }
54
55    /// The ring offset buffer capacity
56    pub fn ring_capacity(&self) -> usize {
57        self.ring_capacity
58    }
59
60    /// The polygon offset buffer capacity
61    pub fn polygon_capacity(&self) -> usize {
62        self.polygon_capacity
63    }
64
65    /// The geometry offset buffer capacity
66    pub fn geom_capacity(&self) -> usize {
67        self.geom_capacity
68    }
69
70    /// Add the capacity of the given Polygon
71    #[inline]
72    pub fn add_polygon<'a>(&mut self, polygon: Option<&'a (impl PolygonTrait + 'a)>) {
73        self.geom_capacity += 1;
74        if let Some(polygon) = polygon {
75            // A single polygon
76            self.polygon_capacity += 1;
77
78            // Total number of rings in this polygon
79            let num_interiors = polygon.num_interiors();
80            self.ring_capacity += num_interiors + 1;
81
82            // Number of coords for each ring
83            if let Some(exterior) = polygon.exterior() {
84                self.coord_capacity += exterior.num_coords();
85            }
86
87            for int_ring in polygon.interiors() {
88                self.coord_capacity += int_ring.num_coords();
89            }
90        }
91    }
92
93    /// Add the capacity of the given MultiPolygon
94    #[inline]
95    pub fn add_multi_polygon<'a>(
96        &mut self,
97        multi_polygon: Option<&'a (impl MultiPolygonTrait + 'a)>,
98    ) {
99        self.geom_capacity += 1;
100
101        if let Some(multi_polygon) = multi_polygon {
102            // Total number of polygons in this MultiPolygon
103            let num_polygons = multi_polygon.num_polygons();
104            self.polygon_capacity += num_polygons;
105
106            for polygon in multi_polygon.polygons() {
107                // Total number of rings in this MultiPolygon
108                self.ring_capacity += polygon.num_interiors() + 1;
109
110                // Number of coords for each ring
111                if let Some(exterior) = polygon.exterior() {
112                    self.coord_capacity += exterior.num_coords();
113                }
114
115                for int_ring in polygon.interiors() {
116                    self.coord_capacity += int_ring.num_coords();
117                }
118            }
119        }
120    }
121
122    /// Add the capacity of the given Geometry
123    ///
124    /// The type of the geometry must be either Polygon or MultiPolygon
125    #[inline]
126    pub fn add_geometry(&mut self, value: Option<&impl GeometryTrait>) -> GeoArrowResult<()> {
127        if let Some(geom) = value {
128            match geom.as_type() {
129                GeometryType::Polygon(g) => self.add_polygon(Some(g)),
130                GeometryType::MultiPolygon(g) => self.add_multi_polygon(Some(g)),
131                gt => {
132                    return Err(GeoArrowError::IncorrectGeometryType(format!(
133                        "Expected MultiPolygon, got {}",
134                        gt.name()
135                    )));
136                }
137            }
138        } else {
139            self.geom_capacity += 1;
140        };
141        Ok(())
142    }
143
144    /// Construct a new counter pre-filled with the given MultiPolygons
145    pub fn from_multi_polygons<'a>(
146        geoms: impl Iterator<Item = Option<&'a (impl MultiPolygonTrait + 'a)>>,
147    ) -> Self {
148        let mut counter = Self::new_empty();
149        for maybe_multi_polygon in geoms.into_iter() {
150            counter.add_multi_polygon(maybe_multi_polygon);
151        }
152        counter
153    }
154
155    /// Construct a new counter pre-filled with the given geometries
156    pub fn from_geometries<'a>(
157        geoms: impl Iterator<Item = Option<&'a (impl GeometryTrait + 'a)>>,
158    ) -> GeoArrowResult<Self> {
159        let mut counter = Self::new_empty();
160        for g in geoms.into_iter() {
161            counter.add_geometry(g)?;
162        }
163        Ok(counter)
164    }
165
166    /// The number of bytes an array with this capacity would occupy.
167    pub fn num_bytes(&self, dim: Dimension) -> usize {
168        let offsets_byte_width = 4;
169        let num_offsets = self.geom_capacity + self.polygon_capacity + self.ring_capacity;
170        (offsets_byte_width * num_offsets) + (self.coord_capacity * dim.size() * 8)
171    }
172}
173
174impl Default for MultiPolygonCapacity {
175    fn default() -> Self {
176        Self::new_empty()
177    }
178}
179
180impl Add for MultiPolygonCapacity {
181    type Output = Self;
182
183    fn add(self, rhs: Self) -> Self::Output {
184        let coord_capacity = self.coord_capacity + rhs.coord_capacity;
185        let ring_capacity = self.ring_capacity + rhs.ring_capacity;
186        let polygon_capacity = self.polygon_capacity + rhs.polygon_capacity;
187        let geom_capacity = self.geom_capacity + rhs.geom_capacity;
188        Self::new(
189            coord_capacity,
190            ring_capacity,
191            polygon_capacity,
192            geom_capacity,
193        )
194    }
195}
196
197impl AddAssign for MultiPolygonCapacity {
198    fn add_assign(&mut self, rhs: Self) {
199        self.coord_capacity += rhs.coord_capacity;
200        self.ring_capacity += rhs.ring_capacity;
201        self.polygon_capacity += rhs.polygon_capacity;
202        self.geom_capacity += rhs.geom_capacity;
203    }
204}
205
206impl AddAssign<PolygonCapacity> for MultiPolygonCapacity {
207    fn add_assign(&mut self, rhs: PolygonCapacity) {
208        self.coord_capacity += rhs.coord_capacity();
209        self.ring_capacity += rhs.ring_capacity();
210        self.polygon_capacity += rhs.geom_capacity();
211        self.geom_capacity += rhs.geom_capacity();
212    }
213}