spatial_join/
conv.rs

1use std::convert::TryFrom;
2
3use geo::{Geometry, Line, LineString, Point, Polygon, Rect, Triangle};
4#[cfg(feature = "parallel")]
5use rayon::prelude::*;
6
7use super::validation::IsSafe;
8use crate::{Error, Indexes, SplitGeo, SplitGeoIndexes, SplitGeoSeq};
9
10#[cfg(feature = "parallel")]
11use crate::structs::Par;
12
13impl TryFrom<&[Geometry<f64>]> for SplitGeoSeq {
14    type Error = Error;
15
16    fn try_from(seq: &[Geometry<f64>]) -> Result<Self, Self::Error> {
17        let mut result = SplitGeoSeq::default();
18        for (i, geo) in seq.iter().enumerate() {
19            match geo {
20                Geometry::Line(ln) => {
21                    ln.is_safe(i)?;
22                    result.geos.lines.push(*ln);
23                    result.indexes.lines.push(i);
24                }
25                Geometry::Point(pt) => {
26                    pt.is_safe(i)?;
27                    result.geos.points.push(*pt);
28                    result.indexes.points.push(i)
29                }
30                Geometry::Polygon(poly) => {
31                    poly.is_safe(i)?;
32                    result.geos.polys.push(poly.clone());
33                    result.indexes.polys.push(i)
34                }
35                Geometry::LineString(ls) => {
36                    ls.is_safe(i)?;
37                    result.geos.line_strings.push(ls.clone());
38                    result.indexes.line_strings.push(i)
39                }
40                Geometry::Rect(r) => {
41                    r.is_safe(i)?;
42                    result.geos.rects.push(*r);
43                    result.indexes.rects.push(i)
44                }
45                Geometry::Triangle(tri) => {
46                    tri.is_safe(i)?;
47                    result.geos.tris.push(*tri);
48                    result.indexes.tris.push(i)
49                }
50
51                _ => unimplemented!("ugh"),
52            }
53        }
54
55        result.indexes.points.canonicalize();
56        result.indexes.lines.canonicalize();
57        result.indexes.polys.canonicalize();
58        result.indexes.line_strings.canonicalize();
59        result.indexes.rects.canonicalize();
60        result.indexes.tris.canonicalize();
61
62        Ok(result)
63    }
64    // FIXME: add an optimization that looks for cases where all but
65    // one variants are empty and makes them implicit.
66}
67
68#[cfg(feature = "parallel")]
69impl TryFrom<SplitGeoSeq> for Par<SplitGeoSeq> {
70    type Error = Error;
71    fn try_from(sgs: SplitGeoSeq) -> Result<Self, Self::Error> {
72        Ok(Par(sgs))
73    }
74}
75
76#[cfg(feature = "parallel")]
77impl TryFrom<&[Geometry<f64>]> for Par<SplitGeoSeq> {
78    type Error = Error;
79
80    fn try_from(seq: &[Geometry<f64>]) -> Result<Self, Self::Error> {
81        let step = (seq.len() / num_cpus::get()).max(1);
82        let end = seq.len();
83        (0..seq.len())
84            .into_par_iter()
85            .step_by(step)
86            .map(|start| std::ops::Range {
87                start,
88                end: (start + step).min(end),
89            })
90            .map(|range| {
91                let offset = range.start;
92                SplitGeoSeq::try_from(&seq[range]).map(|mut sgs| {
93                    sgs.indexes.points.add_offset(offset);
94                    sgs.indexes.lines.add_offset(offset);
95                    sgs.indexes.polys.add_offset(offset);
96                    sgs.indexes.line_strings.add_offset(offset);
97                    sgs.indexes.rects.add_offset(offset);
98                    sgs.indexes.tris.add_offset(offset);
99
100                    sgs
101                })
102            })
103            .try_reduce(SplitGeoSeq::default, |a, b| Ok(SplitGeoSeq::merge(a, b)))
104            .map(Par)
105    }
106}
107
108// FIXME: make versions of these two that consume the arg (i.e., Vec instead of &Vec)
109impl TryFrom<&Vec<Geometry<f64>>> for SplitGeoSeq {
110    type Error = Error;
111
112    fn try_from(seq: &Vec<Geometry<f64>>) -> Result<Self, Self::Error> {
113        SplitGeoSeq::try_from(&seq[..])
114    }
115}
116
117impl TryFrom<Vec<Geometry<f64>>> for SplitGeoSeq {
118    type Error = Error;
119
120    fn try_from(seq: Vec<Geometry<f64>>) -> Result<Self, Self::Error> {
121        SplitGeoSeq::try_from(&seq)
122    }
123}
124
125impl TryFrom<&geo::GeometryCollection<f64>> for SplitGeoSeq {
126    type Error = Error;
127
128    fn try_from(seq: &geo::GeometryCollection<f64>) -> Result<Self, Self::Error> {
129        SplitGeoSeq::try_from(&seq.0[..])
130    }
131}
132
133#[cfg(feature = "parallel")]
134impl TryFrom<&Vec<Geometry<f64>>> for Par<SplitGeoSeq> {
135    type Error = Error;
136
137    fn try_from(seq: &Vec<Geometry<f64>>) -> Result<Self, Self::Error> {
138        Par::<SplitGeoSeq>::try_from(&seq[..])
139    }
140}
141
142#[cfg(feature = "parallel")]
143impl TryFrom<Vec<Geometry<f64>>> for Par<SplitGeoSeq> {
144    type Error = Error;
145
146    fn try_from(seq: Vec<Geometry<f64>>) -> Result<Self, Self::Error> {
147        Par::<SplitGeoSeq>::try_from(&seq)
148    }
149}
150
151#[cfg(feature = "parallel")]
152impl TryFrom<&geo::GeometryCollection<f64>> for Par<SplitGeoSeq> {
153    type Error = Error;
154
155    fn try_from(seq: &geo::GeometryCollection<f64>) -> Result<Self, Self::Error> {
156        Par::<SplitGeoSeq>::try_from(&seq.0[..])
157    }
158}
159
160macro_rules! static_cond {
161    (true, $consequent:expr, $alternative:expr) => {
162        $consequent
163    };
164    (false, $consequent:expr, $alternative:expr) => {
165        $alternative
166    };
167}
168
169// make conversions from &Vec, Vec, and slice
170macro_rules! from_impls {
171    ($ItemType:ty, $Var:ident, $IsCopyable:ident) => {
172        impl TryFrom<&[$ItemType]> for SplitGeoSeq {
173            type Error = Error;
174
175            fn try_from(seq: &[$ItemType]) -> Result<Self, Self::Error> {
176                seq.iter()
177                    .enumerate()
178                    .try_for_each(|(i, x)| (*x).is_safe(i))
179                    .map(|_| SplitGeoSeq {
180                        geos: SplitGeo {
181                            $Var: static_cond!(
182                                $IsCopyable,
183                                seq.to_vec(),
184                                seq.iter().map(|poly| poly.clone()).collect()
185                            ),
186                            ..Default::default()
187                        },
188                        indexes: SplitGeoIndexes {
189                            $Var: Indexes::Range(0..seq.len()),
190                            ..Default::default()
191                        },
192                    })
193            }
194        }
195
196        #[cfg(feature = "parallel")]
197        impl TryFrom<&[$ItemType]> for Par<SplitGeoSeq> {
198            type Error = Error;
199
200            fn try_from(seq: &[$ItemType]) -> Result<Self, Self::Error> {
201                seq.par_iter()
202                    .enumerate()
203                    .try_for_each(|(i, x)| (*x).is_safe(i))
204                    .map(|_| {
205                        Par(SplitGeoSeq {
206                            geos: SplitGeo {
207                                $Var: static_cond!(
208                                    $IsCopyable,
209                                    seq.to_vec(),
210                                    seq.iter().map(|poly| poly.clone()).collect()
211                                ),
212                                ..Default::default()
213                            },
214                            indexes: SplitGeoIndexes {
215                                $Var: Indexes::Range(0..seq.len()),
216                                ..Default::default()
217                            },
218                        })
219                    })
220            }
221        }
222
223        impl TryFrom<Vec<$ItemType>> for SplitGeoSeq {
224            type Error = Error;
225
226            fn try_from(seq: Vec<$ItemType>) -> Result<Self, Self::Error> {
227                seq.iter()
228                    .enumerate()
229                    .try_for_each(|(i, x)| (*x).is_safe(i))
230                    .map(|_| SplitGeoSeq {
231                        indexes: SplitGeoIndexes {
232                            $Var: Indexes::Range(0..seq.len()),
233                            ..Default::default()
234                        },
235                        geos: SplitGeo {
236                            $Var: seq,
237                            ..Default::default()
238                        },
239                    })
240            }
241        }
242
243        #[cfg(feature = "parallel")]
244        impl TryFrom<Vec<$ItemType>> for Par<SplitGeoSeq> {
245            type Error = Error;
246
247            fn try_from(seq: Vec<$ItemType>) -> Result<Self, Self::Error> {
248                seq.par_iter()
249                    .enumerate()
250                    .try_for_each(|(i, x)| (*x).is_safe(i))
251                    .map(|_| {
252                        Par(SplitGeoSeq {
253                            indexes: SplitGeoIndexes {
254                                $Var: Indexes::Range(0..seq.len()),
255                                ..Default::default()
256                            },
257                            geos: SplitGeo {
258                                $Var: seq,
259                                ..Default::default()
260                            },
261                        })
262                    })
263            }
264        }
265
266        impl TryFrom<&Vec<$ItemType>> for SplitGeoSeq {
267            type Error = Error;
268
269            fn try_from(seq: &Vec<$ItemType>) -> Result<Self, Self::Error> {
270                SplitGeoSeq::try_from(&seq[..])
271            }
272        }
273
274        #[cfg(feature = "parallel")]
275        impl TryFrom<&Vec<$ItemType>> for Par<SplitGeoSeq> {
276            type Error = Error;
277
278            fn try_from(seq: &Vec<$ItemType>) -> Result<Self, Self::Error> {
279                Par::<SplitGeoSeq>::try_from(&seq[..])
280            }
281        }
282    };
283}
284
285from_impls!(Point<f64>, points, true);
286from_impls!(Line<f64>, lines, true);
287from_impls!(Polygon<f64>, polys, false);
288from_impls!(LineString<f64>, line_strings, false);
289from_impls!(Rect<f64>, rects, true);
290from_impls!(Triangle<f64>, tris, true);
291
292#[cfg(test)]
293mod tests {
294    use super::*;
295    use pretty_assertions::assert_eq;
296
297    #[test]
298    fn from_geos() {
299        let pt: Point<f64> = (1.1, 2.2).into();
300        let ln1: Line<f64> = Line::new((0., 0.), (1., 1.));
301        let ln2: Line<f64> = Line::new((2., 2.), (1., 1.));
302        let poly = Polygon::new(
303            geo::LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]),
304            vec![],
305        );
306        let expected = Ok(SplitGeoSeq {
307            geos: SplitGeo {
308                points: vec![pt],
309                lines: vec![ln1, ln2],
310                polys: vec![poly.clone()],
311                line_strings: vec![],
312                rects: vec![],
313                tris: vec![],
314            },
315            indexes: SplitGeoIndexes {
316                points: Indexes::Range(0..1),
317                lines: Indexes::Explicit(vec![1, 3]),
318                polys: Indexes::Range(2..3),
319                line_strings: Indexes::default(),
320                rects: Indexes::default(),
321                tris: Indexes::default(),
322            },
323        });
324        let geos = vec![
325            Geometry::Point(pt),
326            Geometry::Line(ln1),
327            Geometry::Polygon(poly.clone()),
328            Geometry::Line(ln2),
329        ];
330        let slice_result = SplitGeoSeq::try_from(&geos[..]);
331        let vec_result = SplitGeoSeq::try_from(&geos);
332        let geo_collection_result = SplitGeoSeq::try_from(&geo::GeometryCollection(geos));
333        assert_eq!(expected, slice_result);
334        assert_eq!(expected, vec_result);
335        assert_eq!(expected, geo_collection_result);
336    }
337
338    #[test]
339    fn from_points() {
340        let pt: Point<f64> = (1.1, 2.2).into();
341        let pts = vec![pt, pt, pt];
342        let expected = Ok(SplitGeoSeq {
343            geos: SplitGeo {
344                points: pts.clone(),
345                ..Default::default()
346            },
347            indexes: SplitGeoIndexes {
348                points: Indexes::Range(0..3),
349                ..Default::default()
350            },
351        });
352        let slice_result = SplitGeoSeq::try_from(&pts[..]);
353        let vec_result = SplitGeoSeq::try_from(&pts);
354        let vec_into_result = SplitGeoSeq::try_from(pts);
355        assert_eq!(expected, slice_result);
356        assert_eq!(expected, vec_result);
357        assert_eq!(expected, vec_into_result);
358    }
359}