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