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 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 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}