geoarrow_array/scalar/
polygon.rs1use arrow_buffer::OffsetBuffer;
2use geo_traits::PolygonTrait;
3use geoarrow_schema::Dimension;
4
5use crate::array::CoordBuffer;
6use crate::eq::polygon_eq;
7use crate::scalar::LineString;
8use crate::util::OffsetBufferUtils;
9
10#[derive(Debug, Clone)]
14pub struct Polygon<'a> {
15 pub(crate) coords: &'a CoordBuffer,
16
17 pub(crate) geom_offsets: &'a OffsetBuffer<i32>,
19
20 pub(crate) ring_offsets: &'a OffsetBuffer<i32>,
22
23 pub(crate) geom_index: usize,
24
25 start_offset: usize,
26}
27
28impl<'a> Polygon<'a> {
29 pub(crate) fn new(
30 coords: &'a CoordBuffer,
31 geom_offsets: &'a OffsetBuffer<i32>,
32 ring_offsets: &'a OffsetBuffer<i32>,
33 geom_index: usize,
34 ) -> Self {
35 let (start_offset, _) = geom_offsets.start_end(geom_index);
36 Self {
37 coords,
38 geom_offsets,
39 ring_offsets,
40 geom_index,
41 start_offset,
42 }
43 }
44
45 pub(crate) fn native_dim(&self) -> Dimension {
46 self.coords.dim()
47 }
48}
49
50impl<'a> PolygonTrait for Polygon<'a> {
51 type RingType<'b>
52 = LineString<'a>
53 where
54 Self: 'b;
55
56 fn exterior(&self) -> Option<Self::RingType<'_>> {
57 let (start, end) = self.geom_offsets.start_end(self.geom_index);
58 if start == end {
59 None
60 } else {
61 Some(LineString::new(self.coords, self.ring_offsets, start))
62 }
63 }
64
65 fn num_interiors(&self) -> usize {
66 let (start, end) = self.geom_offsets.start_end(self.geom_index);
67 (end - start).saturating_sub(1)
69 }
70
71 unsafe fn interior_unchecked(&self, i: usize) -> Self::RingType<'_> {
72 LineString::new(self.coords, self.ring_offsets, self.start_offset + 1 + i)
73 }
74}
75
76impl<'a> PolygonTrait for &'a Polygon<'a> {
77 type RingType<'b>
78 = LineString<'a>
79 where
80 Self: 'b;
81
82 fn exterior(&self) -> Option<Self::RingType<'_>> {
83 let (start, end) = self.geom_offsets.start_end(self.geom_index);
84 if start == end {
85 None
86 } else {
87 Some(LineString::new(self.coords, self.ring_offsets, start))
88 }
89 }
90
91 fn num_interiors(&self) -> usize {
92 let (start, end) = self.geom_offsets.start_end(self.geom_index);
93 (end - start).saturating_sub(1)
95 }
96
97 unsafe fn interior_unchecked(&self, i: usize) -> Self::RingType<'_> {
98 LineString::new(self.coords, self.ring_offsets, self.start_offset + 1 + i)
99 }
100}
101
102impl<G: PolygonTrait<T = f64>> PartialEq<G> for Polygon<'_> {
103 fn eq(&self, other: &G) -> bool {
104 polygon_eq(self, other)
105 }
106}
107
108#[cfg(test)]
109mod test {
110 use geo::HasDimensions;
111 use geo_traits::to_geo::ToGeoPolygon;
112 use geoarrow_schema::{Dimension, PolygonType};
113 use wkt::wkt;
114
115 use crate::GeoArrowArrayAccessor;
116 use crate::builder::PolygonBuilder;
117
118 #[test]
120 fn test_access_empty_polygon() {
121 let empty_polygon: wkt::types::Polygon<f64> = wkt! { POLYGON EMPTY };
122 let typ = PolygonType::new(Dimension::XY, Default::default());
123 let polygon_array = PolygonBuilder::from_polygons(&[empty_polygon], typ).finish();
124
125 let geo_polygon = polygon_array.value(0).unwrap().to_polygon();
126 assert!(geo_polygon.is_empty());
127 }
128}