postgis_diesel/
geometrycollection.rs

1use std::fmt::Debug;
2use std::io::Cursor;
3
4use crate::{
5    ewkb::{EwkbSerializable, GeometryType, BIG_ENDIAN},
6    points::Dimension,
7    polygon::*,
8    types::*,
9};
10
11#[cfg(feature = "diesel")]
12use crate::{
13    ewkb::{read_ewkb_header, write_ewkb_header},
14    linestring::{read_linestring_body, write_linestring},
15    multiline::{read_multiline_body, write_multiline},
16    multipoint::{read_multi_point_body, write_multi_point},
17    multipolygon::{read_multi_polygon_body, write_multi_polygon},
18    points::{read_point_coordinates, write_point},
19};
20
21use byteorder::{BigEndian, LittleEndian, ReadBytesExt, WriteBytesExt};
22
23use crate::sql_types::*;
24
25impl<T> GeometryCollection<T>
26where
27    T: PointT + Clone,
28{
29    pub fn new(srid: Option<u32>) -> Self {
30        Self {
31            geometries: Vec::new(),
32            srid,
33        }
34    }
35
36    pub fn add_geometry(&mut self, geometry: GeometryContainer<T>) -> &mut Self {
37        self.geometries.push(geometry);
38        self
39    }
40
41    pub fn add_geometries(
42        &mut self,
43        geometries: impl IntoIterator<Item = GeometryContainer<T>>,
44    ) -> &mut Self {
45        for gc in geometries {
46            self.geometries.push(gc);
47        }
48        self
49    }
50
51    pub fn dimension(&self) -> u32 {
52        let mut dimension = Dimension::None as u32;
53        if let Some(geometry) = self.geometries.first() {
54            dimension |= geometry.dimension();
55        }
56        dimension
57    }
58}
59
60impl<T> EwkbSerializable for GeometryCollection<T>
61where
62    T: PointT + Clone,
63{
64    fn geometry_type(&self) -> u32 {
65        let mut g_type = GeometryType::GeometryCollection as u32;
66        if let Some(polygon) = self.geometries.first() {
67            g_type |= polygon.dimension();
68        }
69        g_type
70    }
71}
72
73#[cfg(feature = "diesel")]
74impl<T> diesel::serialize::ToSql<Geometry, diesel::pg::Pg> for GeometryCollection<T>
75where
76    T: PointT + Debug + PartialEq + Clone + EwkbSerializable,
77{
78    fn to_sql(
79        &self,
80        out: &mut diesel::serialize::Output<diesel::pg::Pg>,
81    ) -> diesel::serialize::Result {
82        write_geometry_collection(self, self.srid, out)
83    }
84}
85
86#[cfg(feature = "diesel")]
87impl<T> diesel::serialize::ToSql<Geography, diesel::pg::Pg> for GeometryCollection<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_geometry_collection(self, self.srid, out)
96    }
97}
98
99#[cfg(feature = "diesel")]
100impl<T> diesel::deserialize::FromSql<Geometry, diesel::pg::Pg> for GeometryCollection<T>
101where
102    T: PointT + Debug + Clone,
103{
104    fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
105        let mut r = Cursor::new(bytes.as_bytes());
106        let end = r.read_u8()?;
107        if end == BIG_ENDIAN {
108            read_geometry_collection::<BigEndian, T>(&mut r)
109        } else {
110            read_geometry_collection::<LittleEndian, T>(&mut r)
111        }
112    }
113}
114
115#[cfg(feature = "diesel")]
116impl<T> diesel::deserialize::FromSql<Geography, diesel::pg::Pg> for GeometryCollection<T>
117where
118    T: PointT + Debug + Clone,
119{
120    fn from_sql(bytes: diesel::pg::PgValue) -> diesel::deserialize::Result<Self> {
121        diesel::deserialize::FromSql::<Geometry, diesel::pg::Pg>::from_sql(bytes)
122    }
123}
124
125#[cfg(feature = "diesel")]
126pub fn write_geometry_collection<T>(
127    geometrycollection: &GeometryCollection<T>,
128    srid: Option<u32>,
129    out: &mut diesel::serialize::Output<diesel::pg::Pg>,
130) -> diesel::serialize::Result
131where
132    T: PointT + EwkbSerializable + Clone,
133{
134    write_ewkb_header(geometrycollection, srid, out)?;
135    out.write_u32::<LittleEndian>(geometrycollection.geometries.len() as u32)?;
136    for g_container in geometrycollection.geometries.iter() {
137        match g_container {
138            GeometryContainer::Point(g) => write_point(g, None, out)?,
139            GeometryContainer::LineString(g) => write_linestring(g, None, out)?,
140            GeometryContainer::Polygon(g) => write_polygon(g, None, out)?,
141            GeometryContainer::MultiPoint(g) => write_multi_point(g, None, out)?,
142            GeometryContainer::MultiLineString(g) => write_multiline(g, None, out)?,
143            GeometryContainer::MultiPolygon(g) => write_multi_polygon(g, None, out)?,
144            GeometryContainer::GeometryCollection(g) => write_geometry_collection(g, None, out)?,
145        };
146    }
147    Ok(diesel::serialize::IsNull::No)
148}
149
150#[cfg(feature = "diesel")]
151fn read_geometry_collection<T, P>(
152    cursor: &mut Cursor<&[u8]>,
153) -> diesel::deserialize::Result<GeometryCollection<P>>
154where
155    T: byteorder::ByteOrder,
156    P: PointT + Clone,
157{
158    let g_header = read_ewkb_header::<T>(cursor)?.expect(GeometryType::GeometryCollection)?;
159    read_geometry_collection_body::<T, P>(g_header.g_type, g_header.srid, cursor)
160}
161
162#[cfg(feature = "diesel")]
163pub fn read_geometry_collection_body<T, P>(
164    g_type: u32,
165    srid: Option<u32>,
166    cursor: &mut Cursor<&[u8]>,
167) -> diesel::deserialize::Result<GeometryCollection<P>>
168where
169    T: byteorder::ByteOrder,
170    P: PointT + Clone,
171{
172    let geometries_n = cursor.read_u32::<T>()?;
173    let mut g_collection = GeometryCollection::new(srid);
174    for _i in 0..geometries_n {
175        // skip 1 byte for byte order and 4 bytes for point type
176        cursor.read_u8()?;
177        let geom_type = GeometryType::from(cursor.read_u32::<T>()?);
178        let g_container = match geom_type {
179            GeometryType::Point => {
180                GeometryContainer::Point(read_point_coordinates::<T, P>(cursor, g_type, srid)?)
181            }
182            GeometryType::LineString => {
183                GeometryContainer::LineString(read_linestring_body::<T, P>(g_type, srid, cursor)?)
184            }
185            GeometryType::Polygon => {
186                GeometryContainer::Polygon(read_polygon_body::<T, P>(g_type, srid, cursor)?)
187            }
188            GeometryType::MultiPoint => {
189                GeometryContainer::MultiPoint(read_multi_point_body::<T, P>(g_type, srid, cursor)?)
190            }
191            GeometryType::MultiLineString => GeometryContainer::MultiLineString(
192                read_multiline_body::<T, P>(g_type, srid, cursor)?,
193            ),
194            GeometryType::MultiPolygon => GeometryContainer::MultiPolygon(
195                read_multi_polygon_body::<T, P>(g_type, srid, cursor)?,
196            ),
197            GeometryType::GeometryCollection => GeometryContainer::GeometryCollection(
198                read_geometry_collection_body::<T, P>(g_type, srid, cursor)?,
199            ),
200        };
201        g_collection.geometries.push(g_container);
202    }
203    Ok(g_collection)
204}
205
206#[cfg(test)]
207mod tests {
208    // Note this useful idiom: importing names from outer (for mod tests) scope.
209    use super::*;
210
211    #[test]
212    fn test_dimensions_point() {
213        assert_eq!(
214            Dimension::None as u32,
215            GeometryContainer::Point(Point::new(0.0, 0.0, None)).dimension()
216        );
217        assert_eq!(
218            Dimension::Z as u32,
219            GeometryContainer::Point(PointZ::new(0.0, 0.0, 0.0, None)).dimension()
220        );
221        assert_eq!(
222            Dimension::M as u32,
223            GeometryContainer::Point(PointM::new(0.0, 0.0, 0.0, None)).dimension()
224        );
225        assert_eq!(
226            Dimension::ZM as u32,
227            GeometryContainer::Point(PointZM::new(0.0, 0.0, 0.0, 0.0, None)).dimension()
228        );
229    }
230
231    #[test]
232    fn test_dimensions_line_string() {
233        assert_eq!(
234            Dimension::None as u32,
235            GeometryContainer::LineString(
236                LineString::new(None)
237                    .add_point(Point::new(0.0, 0.0, None))
238                    .to_owned()
239            )
240            .dimension()
241        );
242        assert_eq!(
243            Dimension::Z as u32,
244            GeometryContainer::LineString(
245                LineString::new(None)
246                    .add_point(PointZ::new(0.0, 0.0, 0.0, None))
247                    .to_owned()
248            )
249            .dimension()
250        );
251        assert_eq!(
252            Dimension::M as u32,
253            GeometryContainer::LineString(
254                LineString::new(None)
255                    .add_point(PointM::new(0.0, 0.0, 0.0, None))
256                    .to_owned()
257            )
258            .dimension()
259        );
260        assert_eq!(
261            Dimension::ZM as u32,
262            GeometryContainer::LineString(
263                LineString::new(None)
264                    .add_point(PointZM::new(0.0, 0.0, 0.0, 0.0, None))
265                    .to_owned()
266            )
267            .dimension()
268        );
269    }
270
271    #[test]
272    fn test_dimensions_polygon() {
273        assert_eq!(
274            Dimension::None as u32,
275            GeometryContainer::Polygon(
276                Polygon::new(None)
277                    .add_point(Point::new(0.0, 0.0, None))
278                    .to_owned()
279            )
280            .dimension()
281        );
282        assert_eq!(
283            Dimension::Z as u32,
284            GeometryContainer::Polygon(
285                Polygon::new(None)
286                    .add_point(PointZ::new(0.0, 0.0, 0.0, None))
287                    .to_owned()
288            )
289            .dimension()
290        );
291        assert_eq!(
292            Dimension::M as u32,
293            GeometryContainer::Polygon(
294                Polygon::new(None)
295                    .add_point(PointM::new(0.0, 0.0, 0.0, None))
296                    .to_owned()
297            )
298            .dimension()
299        );
300        assert_eq!(
301            Dimension::ZM as u32,
302            GeometryContainer::Polygon(
303                Polygon::new(None)
304                    .add_point(PointZM::new(0.0, 0.0, 0.0, 0.0, None))
305                    .to_owned()
306            )
307            .dimension()
308        );
309    }
310
311    #[test]
312    fn test_dimensions_multi_point() {
313        assert_eq!(
314            Dimension::None as u32,
315            GeometryContainer::MultiPoint(
316                MultiPoint::new(None)
317                    .add_point(Point::new(0.0, 0.0, None))
318                    .to_owned()
319            )
320            .dimension()
321        );
322        assert_eq!(
323            Dimension::Z as u32,
324            GeometryContainer::MultiPoint(
325                MultiPoint::new(None)
326                    .add_point(PointZ::new(0.0, 0.0, 0.0, None))
327                    .to_owned()
328            )
329            .dimension()
330        );
331        assert_eq!(
332            Dimension::M as u32,
333            GeometryContainer::MultiPoint(
334                MultiPoint::new(None)
335                    .add_point(PointM::new(0.0, 0.0, 0.0, None))
336                    .to_owned()
337            )
338            .dimension()
339        );
340        assert_eq!(
341            Dimension::ZM as u32,
342            GeometryContainer::MultiPoint(
343                MultiPoint::new(None)
344                    .add_point(PointZM::new(0.0, 0.0, 0.0, 0.0, None))
345                    .to_owned()
346            )
347            .dimension()
348        );
349    }
350
351    #[test]
352    fn test_dimensions_multi_line_string() {
353        assert_eq!(
354            Dimension::None as u32,
355            GeometryContainer::MultiLineString(
356                MultiLineString::new(None)
357                    .add_point(Point::new(0.0, 0.0, None))
358                    .to_owned()
359            )
360            .dimension()
361        );
362        assert_eq!(
363            Dimension::Z as u32,
364            GeometryContainer::MultiLineString(
365                MultiLineString::new(None)
366                    .add_point(PointZ::new(0.0, 0.0, 0.0, None))
367                    .to_owned()
368            )
369            .dimension()
370        );
371        assert_eq!(
372            Dimension::M as u32,
373            GeometryContainer::MultiLineString(
374                MultiLineString::new(None)
375                    .add_point(PointM::new(0.0, 0.0, 0.0, None))
376                    .to_owned()
377            )
378            .dimension()
379        );
380        assert_eq!(
381            Dimension::ZM as u32,
382            GeometryContainer::MultiLineString(
383                MultiLineString::new(None)
384                    .add_point(PointZM::new(0.0, 0.0, 0.0, 0.0, None))
385                    .to_owned()
386            )
387            .dimension()
388        );
389    }
390
391    #[test]
392    fn test_dimensions_multi_polygon() {
393        assert_eq!(
394            Dimension::None as u32,
395            GeometryContainer::MultiPolygon(
396                MultiPolygon::new(None)
397                    .add_point(Point::new(0.0, 0.0, None))
398                    .to_owned()
399            )
400            .dimension()
401        );
402        assert_eq!(
403            Dimension::Z as u32,
404            GeometryContainer::MultiPolygon(
405                MultiPolygon::new(None)
406                    .add_point(PointZ::new(0.0, 0.0, 0.0, None))
407                    .to_owned()
408            )
409            .dimension()
410        );
411        assert_eq!(
412            Dimension::M as u32,
413            GeometryContainer::MultiPolygon(
414                MultiPolygon::new(None)
415                    .add_point(PointM::new(0.0, 0.0, 0.0, None))
416                    .to_owned()
417            )
418            .dimension()
419        );
420        assert_eq!(
421            Dimension::ZM as u32,
422            GeometryContainer::MultiPolygon(
423                MultiPolygon::new(None)
424                    .add_point(PointZM::new(0.0, 0.0, 0.0, 0.0, None))
425                    .to_owned()
426            )
427            .dimension()
428        );
429    }
430
431    #[test]
432    fn test_dimensions_geometry_collection() {
433        assert_eq!(
434            Dimension::None as u32,
435            GeometryContainer::GeometryCollection(
436                GeometryCollection::new(None)
437                    .add_geometry(GeometryContainer::Point(Point::new(0.0, 0.0, None)))
438                    .to_owned()
439            )
440            .dimension()
441        );
442        assert_eq!(
443            Dimension::Z as u32,
444            GeometryContainer::GeometryCollection(
445                GeometryCollection::new(None)
446                    .add_geometry(GeometryContainer::Point(PointZ::new(0.0, 0.0, 0.0, None)))
447                    .to_owned()
448            )
449            .dimension()
450        );
451        assert_eq!(
452            Dimension::M as u32,
453            GeometryContainer::GeometryCollection(
454                GeometryCollection::new(None)
455                    .add_geometry(GeometryContainer::Point(PointM::new(0.0, 0.0, 0.0, None)))
456                    .to_owned()
457            )
458            .dimension()
459        );
460        assert_eq!(
461            Dimension::ZM as u32,
462            GeometryContainer::GeometryCollection(
463                GeometryCollection::new(None)
464                    .add_geometry(GeometryContainer::Point(PointZM::new(
465                        0.0, 0.0, 0.0, 0.0, None
466                    )))
467                    .to_owned()
468            )
469            .dimension()
470        );
471    }
472}