shapefile/
geo_traits_impl.rs

1use std::hint::unreachable_unchecked;
2
3use crate::{
4    Multipoint, MultipointM, MultipointZ, Point, PointM, PointZ, PolygonRing, Polyline, PolylineM,
5    PolylineZ, NO_DATA,
6};
7use geo_traits::{
8    CoordTrait, LineStringTrait, MultiLineStringTrait, MultiPointTrait, MultiPolygonTrait,
9    PointTrait, PolygonTrait,
10};
11
12// Shapefile points can't be null, so we implement both traits on them
13impl CoordTrait for Point {
14    type T = f64;
15
16    fn dim(&self) -> geo_traits::Dimensions {
17        geo_traits::Dimensions::Xy
18    }
19
20    fn nth_or_panic(&self, n: usize) -> Self::T {
21        match n {
22            0 => self.x(),
23            1 => self.y(),
24            _ => panic!("invalid dimension index"),
25        }
26    }
27
28    unsafe fn nth_unchecked(&self, n: usize) -> Self::T {
29        match n {
30            0 => self.x(),
31            1 => self.y(),
32            _ => unreachable_unchecked(),
33        }
34    }
35
36    fn x(&self) -> Self::T {
37        self.x
38    }
39
40    fn y(&self) -> Self::T {
41        self.y
42    }
43}
44
45impl CoordTrait for &Point {
46    type T = f64;
47
48    fn dim(&self) -> geo_traits::Dimensions {
49        geo_traits::Dimensions::Xy
50    }
51
52    fn nth_or_panic(&self, n: usize) -> Self::T {
53        match n {
54            0 => self.x(),
55            1 => self.y(),
56            _ => panic!("invalid dimension index"),
57        }
58    }
59
60    unsafe fn nth_unchecked(&self, n: usize) -> Self::T {
61        match n {
62            0 => self.x(),
63            1 => self.y(),
64            _ => unreachable_unchecked(),
65        }
66    }
67
68    fn x(&self) -> Self::T {
69        self.x
70    }
71
72    fn y(&self) -> Self::T {
73        self.y
74    }
75}
76
77impl PointTrait for Point {
78    type T = f64;
79    type CoordType<'a>
80        = &'a Point
81    where
82        Self: 'a;
83
84    fn dim(&self) -> geo_traits::Dimensions {
85        geo_traits::Dimensions::Xy
86    }
87
88    fn coord(&self) -> Option<Self::CoordType<'_>> {
89        Some(self)
90    }
91}
92
93impl PointTrait for &Point {
94    type T = f64;
95    type CoordType<'a>
96        = &'a Point
97    where
98        Self: 'a;
99
100    fn dim(&self) -> geo_traits::Dimensions {
101        geo_traits::Dimensions::Xy
102    }
103
104    fn coord(&self) -> Option<Self::CoordType<'_>> {
105        Some(self)
106    }
107}
108
109// Shapefile points can't be null, so we implement both traits on them
110impl CoordTrait for PointM {
111    type T = f64;
112
113    fn dim(&self) -> geo_traits::Dimensions {
114        if self.m <= NO_DATA {
115            geo_traits::Dimensions::Xy
116        } else {
117            geo_traits::Dimensions::Xym
118        }
119    }
120
121    fn nth_or_panic(&self, n: usize) -> Self::T {
122        match n {
123            0 => self.x(),
124            1 => self.y(),
125            2 => self.m,
126            _ => panic!("invalid dimension index"),
127        }
128    }
129
130    fn x(&self) -> Self::T {
131        self.x
132    }
133
134    fn y(&self) -> Self::T {
135        self.y
136    }
137}
138
139impl CoordTrait for &PointM {
140    type T = f64;
141
142    fn dim(&self) -> geo_traits::Dimensions {
143        if self.m <= NO_DATA {
144            geo_traits::Dimensions::Xy
145        } else {
146            geo_traits::Dimensions::Xym
147        }
148    }
149
150    fn nth_or_panic(&self, n: usize) -> Self::T {
151        match n {
152            0 => self.x(),
153            1 => self.y(),
154            2 => self.m,
155            _ => panic!("invalid dimension index"),
156        }
157    }
158
159    fn x(&self) -> Self::T {
160        self.x
161    }
162
163    fn y(&self) -> Self::T {
164        self.y
165    }
166}
167
168impl PointTrait for PointM {
169    type T = f64;
170    type CoordType<'a>
171        = &'a PointM
172    where
173        Self: 'a;
174
175    fn dim(&self) -> geo_traits::Dimensions {
176        if self.m <= NO_DATA {
177            geo_traits::Dimensions::Xy
178        } else {
179            geo_traits::Dimensions::Xym
180        }
181    }
182
183    fn coord(&self) -> Option<Self::CoordType<'_>> {
184        Some(self)
185    }
186}
187
188impl PointTrait for &PointM {
189    type T = f64;
190    type CoordType<'a>
191        = &'a PointM
192    where
193        Self: 'a;
194
195    fn dim(&self) -> geo_traits::Dimensions {
196        if self.m <= NO_DATA {
197            geo_traits::Dimensions::Xy
198        } else {
199            geo_traits::Dimensions::Xym
200        }
201    }
202
203    fn coord(&self) -> Option<Self::CoordType<'_>> {
204        Some(self)
205    }
206}
207
208// Shapefile points can't be null, so we implement both traits on them
209impl CoordTrait for PointZ {
210    type T = f64;
211
212    fn dim(&self) -> geo_traits::Dimensions {
213        if self.m <= NO_DATA {
214            geo_traits::Dimensions::Xyz
215        } else {
216            geo_traits::Dimensions::Xyzm
217        }
218    }
219
220    fn nth_or_panic(&self, n: usize) -> Self::T {
221        match n {
222            0 => self.x(),
223            1 => self.y(),
224            2 => self.z,
225            3 => {
226                if self.m > NO_DATA {
227                    self.m
228                } else {
229                    panic!("asked for 4th item from coordinate but this coordinate does not have 4 dimensions.")
230                }
231            }
232            _ => panic!("invalid dimension index"),
233        }
234    }
235
236    fn x(&self) -> Self::T {
237        self.x
238    }
239
240    fn y(&self) -> Self::T {
241        self.y
242    }
243}
244
245impl CoordTrait for &PointZ {
246    type T = f64;
247
248    fn dim(&self) -> geo_traits::Dimensions {
249        if self.m <= NO_DATA {
250            geo_traits::Dimensions::Xyz
251        } else {
252            geo_traits::Dimensions::Xyzm
253        }
254    }
255
256    fn nth_or_panic(&self, n: usize) -> Self::T {
257        match n {
258            0 => self.x(),
259            1 => self.y(),
260            2 => self.z,
261            3 => {
262                if self.m > NO_DATA {
263                    self.m
264                } else {
265                    panic!("asked for 4th item from coordinate but this coordinate does not have 4 dimensions.")
266                }
267            }
268            _ => panic!("invalid dimension index"),
269        }
270    }
271
272    fn x(&self) -> Self::T {
273        self.x
274    }
275
276    fn y(&self) -> Self::T {
277        self.y
278    }
279}
280
281impl PointTrait for PointZ {
282    type T = f64;
283    type CoordType<'a>
284        = &'a PointZ
285    where
286        Self: 'a;
287
288    fn dim(&self) -> geo_traits::Dimensions {
289        if self.m <= NO_DATA {
290            geo_traits::Dimensions::Xyz
291        } else {
292            geo_traits::Dimensions::Xyzm
293        }
294    }
295
296    fn coord(&self) -> Option<Self::CoordType<'_>> {
297        Some(self)
298    }
299}
300
301impl PointTrait for &PointZ {
302    type T = f64;
303    type CoordType<'a>
304        = &'a PointZ
305    where
306        Self: 'a;
307
308    fn dim(&self) -> geo_traits::Dimensions {
309        if self.m <= NO_DATA {
310            geo_traits::Dimensions::Xyz
311        } else {
312            geo_traits::Dimensions::Xyzm
313        }
314    }
315
316    fn coord(&self) -> Option<Self::CoordType<'_>> {
317        Some(self)
318    }
319}
320
321pub struct LineString<'a>(&'a [Point]);
322
323impl LineStringTrait for LineString<'_> {
324    type T = f64;
325    type CoordType<'b>
326        = &'b Point
327    where
328        Self: 'b;
329
330    fn dim(&self) -> geo_traits::Dimensions {
331        geo_traits::Dimensions::Xy
332    }
333
334    fn num_coords(&self) -> usize {
335        self.0.len()
336    }
337
338    unsafe fn coord_unchecked(&self, i: usize) -> Self::CoordType<'_> {
339        self.0.get_unchecked(i)
340    }
341}
342
343pub struct LineStringM<'a>(&'a [PointM]);
344
345impl LineStringTrait for LineStringM<'_> {
346    type T = f64;
347    type CoordType<'b>
348        = &'b PointM
349    where
350        Self: 'b;
351
352    fn dim(&self) -> geo_traits::Dimensions {
353        geo_traits::Dimensions::Xym
354    }
355
356    fn num_coords(&self) -> usize {
357        self.0.len()
358    }
359
360    unsafe fn coord_unchecked(&self, i: usize) -> Self::CoordType<'_> {
361        self.0.get_unchecked(i)
362    }
363}
364
365pub struct LineStringZ<'a>(&'a [PointZ]);
366
367impl LineStringTrait for LineStringZ<'_> {
368    type T = f64;
369    type CoordType<'b>
370        = &'b PointZ
371    where
372        Self: 'b;
373
374    fn dim(&self) -> geo_traits::Dimensions {
375        // Check the first underlying coordinate to check if it's XYZ or XYZM
376        self.0
377            .first()
378            .map(CoordTrait::dim)
379            .unwrap_or(geo_traits::Dimensions::Xyz)
380    }
381
382    fn num_coords(&self) -> usize {
383        self.0.len()
384    }
385
386    unsafe fn coord_unchecked(&self, i: usize) -> Self::CoordType<'_> {
387        self.0.get_unchecked(i)
388    }
389}
390
391pub struct Polygon {
392    outer: Vec<Point>,
393    inner: Vec<Vec<Point>>,
394}
395
396impl<'a> PolygonTrait for &'a Polygon {
397    type T = f64;
398    type RingType<'b>
399        = LineString<'a>
400    where
401        Self: 'b;
402
403    fn dim(&self) -> geo_traits::Dimensions {
404        geo_traits::Dimensions::Xy
405    }
406
407    fn num_interiors(&self) -> usize {
408        self.inner.len()
409    }
410
411    fn exterior(&self) -> Option<Self::RingType<'_>> {
412        Some(LineString(&self.outer))
413    }
414
415    unsafe fn interior_unchecked(&self, i: usize) -> Self::RingType<'_> {
416        LineString(&self.inner[i])
417    }
418}
419
420pub struct PolygonM {
421    outer: Vec<PointM>,
422    inner: Vec<Vec<PointM>>,
423}
424
425impl<'a> PolygonTrait for &'a PolygonM {
426    type T = f64;
427    type RingType<'b>
428        = LineStringM<'a>
429    where
430        Self: 'b;
431
432    fn dim(&self) -> geo_traits::Dimensions {
433        geo_traits::Dimensions::Xym
434    }
435
436    fn num_interiors(&self) -> usize {
437        self.inner.len()
438    }
439
440    fn exterior(&self) -> Option<Self::RingType<'_>> {
441        Some(LineStringM(&self.outer))
442    }
443
444    unsafe fn interior_unchecked(&self, i: usize) -> Self::RingType<'_> {
445        LineStringM(&self.inner[i])
446    }
447}
448
449pub struct PolygonZ {
450    outer: Vec<PointZ>,
451    inner: Vec<Vec<PointZ>>,
452}
453
454impl<'a> PolygonTrait for &'a PolygonZ {
455    type T = f64;
456    type RingType<'b>
457        = LineStringZ<'a>
458    where
459        Self: 'b;
460
461    fn dim(&self) -> geo_traits::Dimensions {
462        // Check the first coord of the outer ring to check if it's XYZ or XYZM
463        self.outer
464            .first()
465            .map(CoordTrait::dim)
466            .unwrap_or(geo_traits::Dimensions::Xyz)
467    }
468
469    fn num_interiors(&self) -> usize {
470        self.inner.len()
471    }
472
473    fn exterior(&self) -> Option<Self::RingType<'_>> {
474        Some(LineStringZ(&self.outer))
475    }
476
477    unsafe fn interior_unchecked(&self, i: usize) -> Self::RingType<'_> {
478        LineStringZ(&self.inner[i])
479    }
480}
481
482impl MultiPointTrait for Multipoint {
483    type T = f64;
484    type PointType<'b>
485        = &'b Point
486    where
487        Self: 'b;
488
489    fn dim(&self) -> geo_traits::Dimensions {
490        geo_traits::Dimensions::Xy
491    }
492
493    fn num_points(&self) -> usize {
494        self.points().len()
495    }
496
497    unsafe fn point_unchecked(&self, i: usize) -> Self::PointType<'_> {
498        self.point(i).unwrap()
499    }
500}
501
502impl MultiPointTrait for MultipointM {
503    type T = f64;
504    type PointType<'b>
505        = &'b PointM
506    where
507        Self: 'b;
508
509    fn dim(&self) -> geo_traits::Dimensions {
510        geo_traits::Dimensions::Xym
511    }
512
513    fn num_points(&self) -> usize {
514        self.points().len()
515    }
516
517    unsafe fn point_unchecked(&self, i: usize) -> Self::PointType<'_> {
518        self.point(i).unwrap()
519    }
520}
521
522impl MultiPointTrait for MultipointZ {
523    type T = f64;
524    type PointType<'b>
525        = &'b PointZ
526    where
527        Self: 'b;
528
529    fn dim(&self) -> geo_traits::Dimensions {
530        // Check the first point to check if it's XYZ or XYZM
531        self.points
532            .first()
533            .map(CoordTrait::dim)
534            .unwrap_or(geo_traits::Dimensions::Xyz)
535    }
536
537    fn num_points(&self) -> usize {
538        self.points().len()
539    }
540
541    unsafe fn point_unchecked(&self, i: usize) -> Self::PointType<'_> {
542        self.point(i).unwrap()
543    }
544}
545
546impl MultiLineStringTrait for Polyline {
547    type T = f64;
548    type LineStringType<'a>
549        = LineString<'a>
550    where
551        Self: 'a;
552
553    fn dim(&self) -> geo_traits::Dimensions {
554        geo_traits::Dimensions::Xy
555    }
556
557    fn num_line_strings(&self) -> usize {
558        self.parts().len()
559    }
560
561    unsafe fn line_string_unchecked(&self, i: usize) -> Self::LineStringType<'_> {
562        LineString(self.part(i).unwrap())
563    }
564}
565
566impl MultiLineStringTrait for PolylineM {
567    type T = f64;
568    type LineStringType<'a>
569        = LineStringM<'a>
570    where
571        Self: 'a;
572
573    fn dim(&self) -> geo_traits::Dimensions {
574        geo_traits::Dimensions::Xym
575    }
576
577    fn num_line_strings(&self) -> usize {
578        self.parts().len()
579    }
580
581    unsafe fn line_string_unchecked(&self, i: usize) -> Self::LineStringType<'_> {
582        LineStringM(self.part(i).unwrap())
583    }
584}
585
586impl MultiLineStringTrait for PolylineZ {
587    type T = f64;
588    type LineStringType<'a>
589        = LineStringZ<'a>
590    where
591        Self: 'a;
592
593    fn dim(&self) -> geo_traits::Dimensions {
594        // Check the first point to check if it's XYZ or XYZM
595        self.parts
596            .first()
597            .and_then(|line_string| line_string.first().map(CoordTrait::dim))
598            .unwrap_or(geo_traits::Dimensions::Xyz)
599    }
600
601    fn num_line_strings(&self) -> usize {
602        self.parts().len()
603    }
604
605    unsafe fn line_string_unchecked(&self, i: usize) -> Self::LineStringType<'_> {
606        LineStringZ(self.part(i).unwrap())
607    }
608}
609
610pub struct MultiPolygon(Vec<Polygon>);
611
612impl From<crate::Polygon> for MultiPolygon {
613    fn from(geom: crate::Polygon) -> Self {
614        let mut last_poly = None;
615        let mut polygons = Vec::new();
616        for ring in geom.into_inner() {
617            match ring {
618                PolygonRing::Outer(points) => {
619                    if let Some(poly) = last_poly.take() {
620                        polygons.push(poly);
621                    }
622                    last_poly = Some(Polygon {
623                        outer: points,
624                        inner: vec![],
625                    });
626                }
627                PolygonRing::Inner(points) => {
628                    if let Some(poly) = last_poly.as_mut() {
629                        poly.inner.push(points);
630                    } else {
631                        panic!("inner ring without a previous outer ring");
632                        // This is the strange (?) case: inner ring without a previous outer ring
633                        // polygons.push(geo_types::Polygon::<f64>::new(
634                        //     LineString::<f64>::from(Vec::<Coordinate<f64>>::new()),
635                        //     vec![LineString::from(interior)],
636                        // ));
637                    }
638                }
639            }
640        }
641
642        if let Some(poly) = last_poly.take() {
643            polygons.push(poly);
644        }
645
646        Self(polygons)
647    }
648}
649
650impl MultiPolygonTrait for MultiPolygon {
651    type T = f64;
652    type PolygonType<'a> = &'a Polygon;
653
654    fn dim(&self) -> geo_traits::Dimensions {
655        geo_traits::Dimensions::Xy
656    }
657
658    fn num_polygons(&self) -> usize {
659        self.0.len()
660    }
661
662    unsafe fn polygon_unchecked(&self, i: usize) -> Self::PolygonType<'_> {
663        &self.0[i]
664    }
665}
666
667pub struct MultiPolygonM(Vec<Polygon>);
668
669impl From<crate::Polygon> for MultiPolygonM {
670    fn from(geom: crate::Polygon) -> Self {
671        let mut last_poly = None;
672        let mut polygons = Vec::new();
673        for ring in geom.into_inner() {
674            match ring {
675                PolygonRing::Outer(points) => {
676                    if let Some(poly) = last_poly.take() {
677                        polygons.push(poly);
678                    }
679                    last_poly = Some(Polygon {
680                        outer: points,
681                        inner: vec![],
682                    });
683                }
684                PolygonRing::Inner(points) => {
685                    if let Some(poly) = last_poly.as_mut() {
686                        poly.inner.push(points);
687                    } else {
688                        panic!("inner ring without a previous outer ring");
689                        // This is the strange (?) case: inner ring without a previous outer ring
690                        // polygons.push(geo_types::Polygon::<f64>::new(
691                        //     LineString::<f64>::from(Vec::<Coordinate<f64>>::new()),
692                        //     vec![LineString::from(interior)],
693                        // ));
694                    }
695                }
696            }
697        }
698
699        if let Some(poly) = last_poly.take() {
700            polygons.push(poly);
701        }
702
703        Self(polygons)
704    }
705}
706
707impl MultiPolygonTrait for MultiPolygonM {
708    type T = f64;
709    type PolygonType<'a> = &'a Polygon;
710
711    fn dim(&self) -> geo_traits::Dimensions {
712        geo_traits::Dimensions::Xym
713    }
714
715    fn num_polygons(&self) -> usize {
716        self.0.len()
717    }
718
719    unsafe fn polygon_unchecked(&self, i: usize) -> Self::PolygonType<'_> {
720        &self.0[i]
721    }
722}
723
724pub struct MultiPolygonZ(Vec<PolygonZ>);
725
726impl From<crate::PolygonZ> for MultiPolygonZ {
727    fn from(geom: crate::PolygonZ) -> Self {
728        let mut last_poly = None;
729        let mut polygons = Vec::new();
730        for ring in geom.into_inner() {
731            match ring {
732                PolygonRing::Outer(points) => {
733                    if let Some(poly) = last_poly.take() {
734                        polygons.push(poly);
735                    }
736                    last_poly = Some(PolygonZ {
737                        outer: points,
738                        inner: vec![],
739                    });
740                }
741                PolygonRing::Inner(points) => {
742                    if let Some(poly) = last_poly.as_mut() {
743                        poly.inner.push(points);
744                    } else {
745                        panic!("inner ring without a previous outer ring");
746                        // This is the strange (?) case: inner ring without a previous outer ring
747                        // polygons.push(geo_types::Polygon::<f64>::new(
748                        //     LineString::<f64>::from(Vec::<Coordinate<f64>>::new()),
749                        //     vec![LineString::from(interior)],
750                        // ));
751                    }
752                }
753            }
754        }
755
756        if let Some(poly) = last_poly.take() {
757            polygons.push(poly);
758        }
759
760        Self(polygons)
761    }
762}
763
764impl MultiPolygonTrait for MultiPolygonZ {
765    type T = f64;
766    type PolygonType<'a> = &'a PolygonZ;
767
768    fn dim(&self) -> geo_traits::Dimensions {
769        // Check the first polygon to check if it's XYZ or XYZM
770        self.0
771            .first()
772            .map(|polygon| polygon.dim())
773            .unwrap_or(geo_traits::Dimensions::Xyz)
774    }
775
776    fn num_polygons(&self) -> usize {
777        self.0.len()
778    }
779
780    unsafe fn polygon_unchecked(&self, i: usize) -> Self::PolygonType<'_> {
781        &self.0[i]
782    }
783}