postgis_diesel/
polygon.rs1use 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 out.write_u32::<LittleEndian>(polygon.rings.len() as u32)?;
119 for ring in polygon.rings.iter() {
120 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}