postgis_diesel/
geometrycollection.rs

1use crate::{
2    ewkb::{EwkbSerializable, GeometryType},
3    points::Dimension,
4    types::*,
5};
6
7use crate::write_to_read_from_sql::{ReadFromSql, WriteToSql};
8
9use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
10
11impl<P> GeometryCollection<P>
12where
13    P: PointT,
14{
15    pub fn new(srid: Option<u32>) -> Self {
16        Self {
17            geometries: Vec::new(),
18            srid,
19        }
20    }
21
22    pub fn add_geometry(&mut self, geometry: GeometryContainer<P>) -> &mut Self {
23        self.geometries.push(geometry);
24        self
25    }
26
27    pub fn add_geometries(
28        &mut self,
29        geometries: impl IntoIterator<Item = GeometryContainer<P>>,
30    ) -> &mut Self {
31        for gc in geometries {
32            self.geometries.push(gc);
33        }
34        self
35    }
36
37    pub fn dimension(&self) -> u32 {
38        let mut dimension = Dimension::NONE;
39        if let Some(geometry) = self.geometries.first() {
40            dimension |= geometry.dimension();
41        }
42        dimension
43    }
44}
45
46impl<P> EwkbSerializable for GeometryCollection<P>
47where
48    P: PointT,
49{
50    fn expected_geometry_variant(_: u32) -> GeometryType {
51        GeometryType::GeometryCollection
52    }
53
54    fn geometry_type(&self) -> u32 {
55        let mut g_type = GeometryType::GeometryCollection as u32;
56        if let Some(polygon) = self.geometries.first() {
57            g_type |= polygon.dimension();
58        }
59        g_type
60    }
61
62    fn srid(&self) -> Option<u32> {
63        self.srid
64    }
65}
66
67impl<P> ReadFromSql for GeometryCollection<P>
68where
69    P: PointT,
70{
71    fn read_body<Endianness, Reader>(
72        header: crate::ewkb::EwkbHeader,
73        reader: &mut Reader,
74    ) -> Result<Self, std::io::Error>
75    where
76        Reader: std::io::Read,
77        Endianness: byteorder::ByteOrder,
78    {
79        let geometries_n = reader.read_u32::<Endianness>()?;
80        let mut g_collection = GeometryCollection::new(header.srid);
81        for _i in 0..geometries_n {
82            // skip 1 byte for byte order and 4 bytes for point type
83            reader.read_u8()?;
84            let geom_type = GeometryType::from(reader.read_u32::<Endianness>()?);
85            let g_container = match geom_type {
86                GeometryType::Point => {
87                    GeometryContainer::Point(P::read_body::<Endianness, Reader>(header, reader)?)
88                }
89                GeometryType::LineString => {
90                    GeometryContainer::LineString(LineString::read_body::<Endianness, Reader>(
91                        header, reader,
92                    )?)
93                }
94                GeometryType::Polygon => {
95                    GeometryContainer::Polygon(Polygon::read_body::<Endianness, Reader>(
96                        header, reader,
97                    )?)
98                }
99                GeometryType::MultiPoint => {
100                    GeometryContainer::MultiPoint(MultiPoint::read_body::<Endianness, Reader>(
101                        header, reader,
102                    )?)
103                }
104                GeometryType::MultiLineString => {
105                    GeometryContainer::MultiLineString(MultiLineString::read_body::<
106                        Endianness,
107                        Reader,
108                    >(header, reader)?)
109                }
110                GeometryType::MultiPolygon => GeometryContainer::MultiPolygon(
111                    MultiPolygon::read_body::<Endianness, Reader>(header, reader)?,
112                ),
113                GeometryType::GeometryCollection => {
114                    GeometryContainer::GeometryCollection(GeometryCollection::read_body::<
115                        Endianness,
116                        Reader,
117                    >(header, reader)?)
118                }
119            };
120            g_collection.geometries.push(g_container);
121        }
122        Ok(g_collection)
123    }
124}
125
126impl<P> WriteToSql for GeometryCollection<P>
127where
128    P: PointT,
129{
130    fn write_body<Writer>(&self, out: &mut Writer) -> Result<(), std::io::Error>
131    where
132        Writer: std::io::Write,
133    {
134        out.write_u32::<LittleEndian>(self.geometries.len() as u32)?;
135        for g_container in self.geometries.iter() {
136            match g_container {
137                GeometryContainer::Point(g) => g.write_to_sql(false, out)?,
138                GeometryContainer::LineString(g) => g.write_to_sql(false, out)?,
139                GeometryContainer::Polygon(g) => g.write_to_sql(false, out)?,
140                GeometryContainer::MultiPoint(g) => g.write_to_sql(false, out)?,
141                GeometryContainer::MultiLineString(g) => g.write_to_sql(false, out)?,
142                GeometryContainer::MultiPolygon(g) => g.write_to_sql(false, out)?,
143                GeometryContainer::GeometryCollection(g) => g.write_to_sql(false, out)?,
144            };
145        }
146        Ok(())
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    // Note this useful idiom: importing names from outer (for mod tests) scope.
153    use super::*;
154
155    #[test]
156    fn test_dimensions_point() {
157        assert_eq!(
158            Dimension::NONE,
159            GeometryContainer::Point(Point::new(0.0, 0.0, None)).dimension()
160        );
161        assert_eq!(
162            Dimension::Z as u32,
163            GeometryContainer::Point(PointZ::new(0.0, 0.0, 0.0, None)).dimension()
164        );
165        assert_eq!(
166            Dimension::M as u32,
167            GeometryContainer::Point(PointM::new(0.0, 0.0, 0.0, None)).dimension()
168        );
169        assert_eq!(
170            Dimension::ZM as u32,
171            GeometryContainer::Point(PointZM::new(0.0, 0.0, 0.0, 0.0, None)).dimension()
172        );
173    }
174
175    #[test]
176    fn test_dimensions_line_string() {
177        assert_eq!(
178            Dimension::NONE,
179            GeometryContainer::LineString(
180                LineString::new(None)
181                    .add_point(Point::new(0.0, 0.0, None))
182                    .unwrap()
183                    .to_owned()
184            )
185            .dimension()
186        );
187        assert_eq!(
188            Dimension::Z as u32,
189            GeometryContainer::LineString(
190                LineString::new(None)
191                    .add_point(PointZ::new(0.0, 0.0, 0.0, None))
192                    .unwrap()
193                    .to_owned()
194            )
195            .dimension()
196        );
197        assert_eq!(
198            Dimension::M as u32,
199            GeometryContainer::LineString(
200                LineString::new(None)
201                    .add_point(PointM::new(0.0, 0.0, 0.0, None))
202                    .unwrap()
203                    .to_owned()
204            )
205            .dimension()
206        );
207        assert_eq!(
208            Dimension::ZM as u32,
209            GeometryContainer::LineString(
210                LineString::new(None)
211                    .add_point(PointZM::new(0.0, 0.0, 0.0, 0.0, None))
212                    .unwrap()
213                    .to_owned()
214            )
215            .dimension()
216        );
217    }
218
219    #[test]
220    fn test_dimensions_polygon() {
221        assert_eq!(
222            Dimension::NONE,
223            GeometryContainer::Polygon(
224                Polygon::new(None)
225                    .add_point(Point::new(0.0, 0.0, None))
226                    .unwrap()
227                    .to_owned()
228            )
229            .dimension()
230        );
231        assert_eq!(
232            Dimension::Z as u32,
233            GeometryContainer::Polygon(
234                Polygon::new(None)
235                    .add_point(PointZ::new(0.0, 0.0, 0.0, None))
236                    .unwrap()
237                    .to_owned()
238            )
239            .dimension()
240        );
241        assert_eq!(
242            Dimension::M as u32,
243            GeometryContainer::Polygon(
244                Polygon::new(None)
245                    .add_point(PointM::new(0.0, 0.0, 0.0, None))
246                    .unwrap()
247                    .to_owned()
248            )
249            .dimension()
250        );
251        assert_eq!(
252            Dimension::ZM as u32,
253            GeometryContainer::Polygon(
254                Polygon::new(None)
255                    .add_point(PointZM::new(0.0, 0.0, 0.0, 0.0, None))
256                    .unwrap()
257                    .to_owned()
258            )
259            .dimension()
260        );
261    }
262
263    #[test]
264    fn test_dimensions_multi_point() {
265        assert_eq!(
266            Dimension::NONE,
267            GeometryContainer::MultiPoint(
268                MultiPoint::new(None)
269                    .add_point(Point::new(0.0, 0.0, None))
270                    .to_owned()
271            )
272            .dimension()
273        );
274        assert_eq!(
275            Dimension::Z as u32,
276            GeometryContainer::MultiPoint(
277                MultiPoint::new(None)
278                    .add_point(PointZ::new(0.0, 0.0, 0.0, None))
279                    .to_owned()
280            )
281            .dimension()
282        );
283        assert_eq!(
284            Dimension::M as u32,
285            GeometryContainer::MultiPoint(
286                MultiPoint::new(None)
287                    .add_point(PointM::new(0.0, 0.0, 0.0, None))
288                    .to_owned()
289            )
290            .dimension()
291        );
292        assert_eq!(
293            Dimension::ZM as u32,
294            GeometryContainer::MultiPoint(
295                MultiPoint::new(None)
296                    .add_point(PointZM::new(0.0, 0.0, 0.0, 0.0, None))
297                    .to_owned()
298            )
299            .dimension()
300        );
301    }
302
303    #[test]
304    fn test_dimensions_multi_line_string() {
305        assert_eq!(
306            Dimension::NONE,
307            GeometryContainer::MultiLineString(
308                MultiLineString::new(None)
309                    .add_point(Point::new(0.0, 0.0, None))
310                    .unwrap()
311                    .to_owned()
312            )
313            .dimension()
314        );
315        assert_eq!(
316            Dimension::Z as u32,
317            GeometryContainer::MultiLineString(
318                MultiLineString::new(None)
319                    .add_point(PointZ::new(0.0, 0.0, 0.0, None))
320                    .unwrap()
321                    .to_owned()
322            )
323            .dimension()
324        );
325        assert_eq!(
326            Dimension::M as u32,
327            GeometryContainer::MultiLineString(
328                MultiLineString::new(None)
329                    .add_point(PointM::new(0.0, 0.0, 0.0, None))
330                    .unwrap()
331                    .to_owned()
332            )
333            .dimension()
334        );
335        assert_eq!(
336            Dimension::ZM as u32,
337            GeometryContainer::MultiLineString(
338                MultiLineString::new(None)
339                    .add_point(PointZM::new(0.0, 0.0, 0.0, 0.0, None))
340                    .unwrap()
341                    .to_owned()
342            )
343            .dimension()
344        );
345    }
346
347    #[test]
348    fn test_dimensions_multi_polygon() {
349        assert_eq!(
350            Dimension::NONE,
351            GeometryContainer::MultiPolygon(
352                MultiPolygon::new(None)
353                    .add_point(Point::new(0.0, 0.0, None))
354                    .unwrap()
355                    .to_owned()
356            )
357            .dimension()
358        );
359        assert_eq!(
360            Dimension::Z as u32,
361            GeometryContainer::MultiPolygon(
362                MultiPolygon::new(None)
363                    .add_point(PointZ::new(0.0, 0.0, 0.0, None))
364                    .unwrap()
365                    .to_owned()
366            )
367            .dimension()
368        );
369        assert_eq!(
370            Dimension::M as u32,
371            GeometryContainer::MultiPolygon(
372                MultiPolygon::new(None)
373                    .add_point(PointM::new(0.0, 0.0, 0.0, None))
374                    .unwrap()
375                    .to_owned()
376            )
377            .dimension()
378        );
379        assert_eq!(
380            Dimension::ZM as u32,
381            GeometryContainer::MultiPolygon(
382                MultiPolygon::new(None)
383                    .add_point(PointZM::new(0.0, 0.0, 0.0, 0.0, None))
384                    .unwrap()
385                    .to_owned()
386            )
387            .dimension()
388        );
389    }
390
391    #[test]
392    fn test_dimensions_geometry_collection() {
393        assert_eq!(
394            Dimension::NONE,
395            GeometryContainer::GeometryCollection(
396                GeometryCollection::new(None)
397                    .add_geometry(GeometryContainer::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::GeometryCollection(
405                GeometryCollection::new(None)
406                    .add_geometry(GeometryContainer::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::GeometryCollection(
414                GeometryCollection::new(None)
415                    .add_geometry(GeometryContainer::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::GeometryCollection(
423                GeometryCollection::new(None)
424                    .add_geometry(GeometryContainer::Point(PointZM::new(
425                        0.0, 0.0, 0.0, 0.0, None
426                    )))
427                    .to_owned()
428            )
429            .dimension()
430        );
431    }
432}