postgis_diesel/
polygon.rs

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