postgis_diesel/
multipolygon.rs

1use std::fmt::Debug;
2use std::io::Cursor;
3
4#[cfg(feature = "diesel")]
5use crate::{
6    ewkb::{read_ewkb_header, write_ewkb_header},
7    polygon::{read_polygon_body, write_polygon},
8};
9use crate::{
10    ewkb::{EwkbSerializable, GeometryType, BIG_ENDIAN},
11    points::Dimension,
12    types::*,
13};
14use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
15
16use crate::sql_types::*;
17
18impl<T> MultiPolygon<T>
19where
20    T: PointT + Clone,
21{
22    pub fn new(srid: Option<u32>) -> Self {
23        Self::with_capacity(srid, 0)
24    }
25
26    pub fn with_capacity(srid: Option<u32>, cap: usize) -> Self {
27        MultiPolygon {
28            polygons: Vec::with_capacity(cap),
29            srid,
30        }
31    }
32
33    pub fn add_empty_polygon(&mut self) -> &mut Self {
34        self.add_empty_polygon_with_capacity(0)
35    }
36
37    pub fn add_empty_polygon_with_capacity(&mut self, cap: usize) -> &mut Self {
38        self.polygons.push(Polygon {
39            rings: Vec::with_capacity(cap),
40            srid: self.srid,
41        });
42        self
43    }
44
45    pub fn add_point(&mut self, point: T) -> &mut Self {
46        if self.polygons.is_empty() {
47            self.add_empty_polygon();
48        }
49        self.polygons.last_mut().unwrap().add_point(point);
50        self
51    }
52
53    pub fn add_points(&mut self, points: impl IntoIterator<Item = T>) -> &mut Self {
54        if self.polygons.is_empty() {
55            self.add_empty_polygon();
56        }
57        let last = self.polygons.last_mut().unwrap();
58        for point in points {
59            last.add_point(point);
60        }
61        self
62    }
63
64    pub fn dimension(&self) -> u32 {
65        let mut dimension = Dimension::None as u32;
66        if let Some(polygon) = self.polygons.first() {
67            dimension |= polygon.dimension();
68        }
69        dimension
70    }
71}
72
73impl<T> EwkbSerializable for MultiPolygon<T>
74where
75    T: PointT + Clone,
76{
77    fn geometry_type(&self) -> u32 {
78        let mut g_type = GeometryType::MultiPolygon as u32;
79        if let Some(polygon) = self.polygons.first() {
80            g_type |= polygon.dimension();
81        }
82        g_type
83    }
84}
85
86#[cfg(feature = "diesel")]
87impl<T> diesel::serialize::ToSql<Geometry, diesel::pg::Pg> for MultiPolygon<T>
88where
89    T: PointT + Debug + PartialEq + Clone + EwkbSerializable,
90{
91    fn to_sql(
92        &self,
93        out: &mut diesel::serialize::Output<diesel::pg::Pg>,
94    ) -> diesel::serialize::Result {
95        write_multi_polygon(self, self.srid, out)
96    }
97}
98
99#[cfg(feature = "diesel")]
100impl<T> diesel::serialize::ToSql<Geography, diesel::pg::Pg> for MultiPolygon<T>
101where
102    T: PointT + Debug + PartialEq + Clone + EwkbSerializable,
103{
104    fn to_sql(
105        &self,
106        out: &mut diesel::serialize::Output<diesel::pg::Pg>,
107    ) -> diesel::serialize::Result {
108        write_multi_polygon(self, self.srid, out)
109    }
110}
111
112#[cfg(feature = "diesel")]
113impl<T> diesel::deserialize::FromSql<Geometry, diesel::pg::Pg> for MultiPolygon<T>
114where
115    T: PointT + Debug + Clone,
116{
117    fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
118        let mut r = Cursor::new(bytes.as_bytes());
119        let end = r.read_u8()?;
120        if end == BIG_ENDIAN {
121            read_multi_polygon::<BigEndian, T>(&mut r)
122        } else {
123            read_multi_polygon::<LittleEndian, T>(&mut r)
124        }
125    }
126}
127
128#[cfg(feature = "diesel")]
129impl<T> diesel::deserialize::FromSql<Geography, diesel::pg::Pg> for MultiPolygon<T>
130where
131    T: PointT + Debug + Clone,
132{
133    fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
134        diesel::deserialize::FromSql::<Geometry, diesel::pg::Pg>::from_sql(bytes)
135    }
136}
137
138#[cfg(feature = "diesel")]
139pub fn write_multi_polygon<T>(
140    multipolygon: &MultiPolygon<T>,
141    srid: Option<u32>,
142    out: &mut diesel::serialize::Output<diesel::pg::Pg>,
143) -> diesel::serialize::Result
144where
145    T: PointT + EwkbSerializable + Clone,
146{
147    write_ewkb_header(multipolygon, srid, out)?;
148    // number of polygons
149    out.write_u32::<LittleEndian>(multipolygon.polygons.len() as u32)?;
150    for polygon in multipolygon.polygons.iter() {
151        write_polygon(polygon, None, out)?;
152    }
153    Ok(diesel::serialize::IsNull::No)
154}
155
156#[cfg(feature = "diesel")]
157fn read_multi_polygon<T, P>(
158    cursor: &mut Cursor<&[u8]>,
159) -> diesel::deserialize::Result<MultiPolygon<P>>
160where
161    T: byteorder::ByteOrder,
162    P: PointT + Clone,
163{
164    let g_header = read_ewkb_header::<T>(cursor)?.expect(GeometryType::MultiPolygon)?;
165    read_multi_polygon_body::<T, P>(g_header.g_type, g_header.srid, cursor)
166}
167
168#[cfg(feature = "diesel")]
169pub fn read_multi_polygon_body<T, P>(
170    g_type: u32,
171    srid: Option<u32>,
172    cursor: &mut Cursor<&[u8]>,
173) -> diesel::deserialize::Result<MultiPolygon<P>>
174where
175    T: byteorder::ByteOrder,
176    P: PointT + Clone,
177{
178    let polygons_n = cursor.read_u32::<T>()?;
179    let mut polygon = MultiPolygon::with_capacity(srid, polygons_n as usize);
180
181    for _i in 0..polygons_n {
182        // skip 1 byte for byte order and 4 bytes for point type
183        cursor.read_u8()?;
184        cursor.read_u32::<T>()?;
185        polygon
186            .polygons
187            .push(read_polygon_body::<T, P>(g_type, srid, cursor)?);
188    }
189    Ok(polygon)
190}