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 }
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
108impl 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
169macro_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}