geoarrow_array/capacity/
multipolygon.rs

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