1use std::convert::TryInto;
2
3use smallvec::SmallVec;
4
5#[cfg(feature = "parallel")]
6use rayon::prelude::*;
7
8use crate::rtrees::Envelope;
9use crate::{
10 Config, Error, Interaction, ProxMapGeoRow, ProxMapRow, SJoinGeoRow, SJoinRow, SpatialIndex,
11 SplitGeoSeq,
12};
13#[cfg(feature = "parallel")]
14use crate::{Par, ParSpatialIndex};
15
16use crate::relates::Relates;
17
18macro_rules! chain {
19 ($thing:expr) => ($thing);
20 ($head:expr, $($tail:expr),+) => ($head.chain(chain!($($tail),+)));
21
22}
23
24macro_rules! join_outer {
25 ($big:expr;
26 $geo_big:ident, $ext_index_big:ident, $env:ident;
27 $expr_copyable:expr, $expr_noncopyable:expr) => {
28 chain!(
29 $big.geos
30 .points
31 .into_iter()
32 .zip($big.indexes.points.into_iter())
33 .flat_map(move |($geo_big, $ext_index_big)| {
34 let $env = $geo_big.to_env();
35 $expr_copyable
36 }),
37 $big.geos
38 .lines
39 .into_iter()
40 .zip($big.indexes.lines.into_iter())
41 .flat_map(move |($geo_big, $ext_index_big)| {
42 let $env = $geo_big.to_env();
43 $expr_copyable
44 }),
45 $big.geos
46 .rects
47 .into_iter()
48 .zip($big.indexes.rects.into_iter())
49 .flat_map(move |($geo_big, $ext_index_big)| {
50 let $env = $geo_big.to_env();
51 $expr_copyable
52 }),
53 $big.geos
54 .tris
55 .into_iter()
56 .zip($big.indexes.tris.into_iter())
57 .flat_map(move |($geo_big, $ext_index_big)| {
58 let $env = $geo_big.to_env();
59 $expr_copyable
60 }),
61 $big.geos
62 .polys
63 .into_iter()
64 .zip($big.indexes.polys.into_iter())
65 .flat_map(move |($geo_big, $ext_index_big)| {
66 let $env = $geo_big.to_env();
67 let $geo_big = &$geo_big;
68 $expr_noncopyable.into_iter()
69 }),
70 $big.geos
71 .line_strings
72 .into_iter()
73 .zip($big.indexes.line_strings.into_iter())
74 .flat_map(move |($geo_big, $ext_index_big)| {
75 let $env = $geo_big.to_env();
76 let $geo_big = &$geo_big;
77 $expr_noncopyable.into_iter()
78 })
79 )
80 };
81}
82
83#[cfg(feature = "parallel")]
84macro_rules! par_join_outer {
85 ($big:expr;
86 $geo_big:ident, $ext_index_big:ident, $env:ident;
87 $expr_copyable:expr, $expr_noncopyable:expr) => {{
88 chain!(
89 $big.geos
90 .points
91 .into_par_iter()
92 .zip($big.indexes.points.into_par_iter())
93 .flat_map(move |($geo_big, $ext_index_big)| {
94 let $env = $geo_big.to_env();
95 ($expr_copyable).par_bridge()
96 }),
97 $big.geos
98 .lines
99 .into_par_iter()
100 .zip($big.indexes.lines.into_par_iter())
101 .flat_map(move |($geo_big, $ext_index_big)| {
102 let $env = $geo_big.to_env();
103 ($expr_copyable).par_bridge()
104 }),
105 $big.geos
106 .rects
107 .into_par_iter()
108 .zip($big.indexes.rects.into_par_iter())
109 .flat_map(move |($geo_big, $ext_index_big)| {
110 let $env = $geo_big.to_env();
111 ($expr_copyable).par_bridge()
112 }),
113 $big.geos
114 .tris
115 .into_par_iter()
116 .zip($big.indexes.tris.into_par_iter())
117 .flat_map(move |($geo_big, $ext_index_big)| {
118 let $env = $geo_big.to_env();
119 ($expr_copyable).par_bridge()
120 }),
121 $big.geos
122 .polys
123 .into_par_iter()
124 .zip($big.indexes.polys.into_par_iter())
125 .flat_map(move |($geo_big, $ext_index_big)| {
126 let $env = $geo_big.to_env();
127 let $geo_big = &$geo_big;
128 $expr_noncopyable.into_iter().par_bridge()
129 }),
130 $big.geos
131 .line_strings
132 .into_par_iter()
133 .zip($big.indexes.line_strings.into_par_iter())
134 .flat_map(move |($geo_big, $ext_index_big)| {
135 let $env = $geo_big.to_env();
136 let $geo_big = &$geo_big;
137 $expr_noncopyable.into_iter().par_bridge()
138 })
139 )
140 }};
141}
142
143macro_rules! join_inner_copyable {
144 ($pm:expr;
145 $geo_big:ident, $ext_index_big:ident, $env:ident,
146 $geo_small:ident, $ext_index_small:ident;
147 $expr:expr) => {{
148 chain!(
149 $pm.point_tree
150 .locate_in_envelope_intersecting(&$env)
151 .map(|fake| fake.id)
152 .filter_map({
153 let $geo_big = $geo_big.clone();
154 move |index_small| {
155 let $geo_small = &$pm.small.geos.points[index_small];
156 let $ext_index_small = $pm.small.indexes.points.get(index_small);
157 let $geo_big = &$geo_big;
158 $expr
159 }
160 }),
161 $pm.line_tree
162 .locate_in_envelope_intersecting(&$env)
163 .map(|fake| fake.id)
164 .filter_map({
165 let $geo_big = $geo_big.clone();
166 move |index_small| {
167 let $geo_small = &$pm.small.geos.lines[index_small];
168 let $ext_index_small = $pm.small.indexes.lines.get(index_small);
169 let $geo_big = &$geo_big;
170 $expr
171 }
172 }),
173 $pm.poly_tree
174 .locate_in_envelope_intersecting(&$env)
175 .map(|fake| fake.id)
176 .filter_map({
177 move |index_small| {
178 let $geo_small = &$pm.small.geos.polys[index_small];
179 let $ext_index_small = $pm.small.indexes.polys.get(index_small);
180 let $geo_big = &$geo_big;
181 $expr
182 }
183 }),
184 $pm.ls_tree
185 .locate_in_envelope_intersecting(&$env)
186 .map(|fake| fake.id)
187 .filter_map({
188 move |index_small| {
189 let $geo_small = &$pm.small.geos.line_strings[index_small];
190 let $ext_index_small = $pm.small.indexes.line_strings.get(index_small);
191 let $geo_big = &$geo_big;
192 $expr
193 }
194 }),
195 $pm.rect_tree
196 .locate_in_envelope_intersecting(&$env)
197 .map(|fake| fake.id)
198 .filter_map({
199 let $geo_big = $geo_big.clone();
200 move |index_small| {
201 let $geo_small = &$pm.small.geos.rects[index_small];
202 let $ext_index_small = $pm.small.indexes.rects.get(index_small);
203 let $geo_big = &$geo_big;
204 $expr
205 }
206 }),
207 $pm.tri_tree
208 .locate_in_envelope_intersecting(&$env)
209 .map(|fake| fake.id)
210 .filter_map({
211 let $geo_big = $geo_big.clone();
212 move |index_small| {
213 let $geo_small = &$pm.small.geos.tris[index_small];
214 let $ext_index_small = $pm.small.indexes.tris.get(index_small);
215 let $geo_big = &$geo_big;
216 $expr
217 }
218 })
219 )
220 }};
221}
222
223macro_rules! join_inner_noncopyable {
224 ($pm:expr; $expr_type:ty;
225 $geo_big:ident, $ext_index_big:ident, $env:ident,
226 $geo_small:ident, $ext_index_small:ident;
227 $expr:expr) => {{
228 let mut result = SmallVec::<[$expr_type; 10]>::new();
229 result.extend(
230 $pm.point_tree
231 .locate_in_envelope_intersecting(&$env)
232 .map(|fake| fake.id)
233 .filter_map({
234 move |index_small| {
235 let $geo_small = &$pm.small.geos.points[index_small];
236 let $ext_index_small = $pm.small.indexes.points.get(index_small);
237 $expr
238 }
239 }),
240 );
241 result.extend(
242 $pm.line_tree
243 .locate_in_envelope_intersecting(&$env)
244 .map(|fake| fake.id)
245 .filter_map({
246 move |index_small| {
247 let $geo_small = &$pm.small.geos.lines[index_small];
248 let $ext_index_small = $pm.small.indexes.lines.get(index_small);
249 $expr
250 }
251 }),
252 );
253 result.extend(
254 $pm.poly_tree
255 .locate_in_envelope_intersecting(&$env)
256 .map(|fake| fake.id)
257 .filter_map({
258 move |index_small| {
259 let $geo_small = &$pm.small.geos.polys[index_small];
260 let $ext_index_small = $pm.small.indexes.polys.get(index_small);
261 $expr
262 }
263 }),
264 );
265 result.extend(
266 $pm.ls_tree
267 .locate_in_envelope_intersecting(&$env)
268 .map(|fake| fake.id)
269 .filter_map({
270 move |index_small| {
271 let $geo_small = &$pm.small.geos.line_strings[index_small];
272 let $ext_index_small = $pm.small.indexes.line_strings.get(index_small);
273 $expr
274 }
275 }),
276 );
277 result.extend(
278 $pm.rect_tree
279 .locate_in_envelope_intersecting(&$env)
280 .map(|fake| fake.id)
281 .filter_map({
282 move |index_small| {
283 let $geo_small = &$pm.small.geos.rects[index_small];
284 let $ext_index_small = $pm.small.indexes.rects.get(index_small);
285 $expr
286 }
287 }),
288 );
289 result.extend(
290 $pm.tri_tree
291 .locate_in_envelope_intersecting(&$env)
292 .map(|fake| fake.id)
293 .filter_map({
294 move |index_small| {
295 let $geo_small = &$pm.small.geos.tris[index_small];
296 let $ext_index_small = $pm.small.indexes.tris.get(index_small);
297 $expr
298 }
299 }),
300 );
301 result
302 }};
303}
304
305macro_rules! join {
306 ($pm:expr,
307 $expr_type:ty,
308 $big:expr;
309
310 $geo_big:ident, $ext_index_big:ident, $env:ident,
311 $geo_small:ident, $ext_index_small:ident;
312
313 $expr:expr) => {
314 join_outer!(
315 $big;
316 $geo_big,
317 $ext_index_big,
318 $env;
319 join_inner_copyable!(
320 $pm;
321 $geo_big,
322 $ext_index_big,
323 $env,
324 $geo_small,
325 $ext_index_small;
326 $expr
327 ),
328 join_inner_noncopyable!(
329 $pm; $expr_type;
330 $geo_big,
331 $ext_index_big,
332 $env,
333 $geo_small,
334 $ext_index_small;
335 $expr
336 )
337
338 )
339 }
340}
341
342#[cfg(feature = "parallel")]
343macro_rules! par_join {
344 ($pm:expr,
345 $expr_type:ty,
346 $big:expr;
347
348 $geo_big:ident, $ext_index_big:ident, $env:ident,
349 $geo_small:ident, $ext_index_small:ident;
350
351 $expr:expr) => {
352 par_join_outer!(
353 $big;
354 $geo_big,
355 $ext_index_big,
356 $env;
357 join_inner_copyable!(
358 $pm;
359 $geo_big,
360 $ext_index_big,
361 $env,
362 $geo_small,
363 $ext_index_small;
364 $expr
365 ),
366 join_inner_noncopyable!(
367 $pm; $expr_type;
368 $geo_big,
369 $ext_index_big,
370 $env,
371 $geo_small,
372 $ext_index_small;
373 $expr
374 )
375
376 )
377 }
378}
379
380pub(crate) fn sgs_try_into<T, U>(thing: T) -> Result<SplitGeoSeq, Error>
381where
382 T: TryInto<SplitGeoSeq, Error = U>,
383 U: std::any::Any,
384{
385 let thing: Result<SplitGeoSeq, _> = thing.try_into();
386 match thing {
388 Ok(thing) => {
389 Ok(thing)
391 }
392 Err(e) => {
393 let any_e = &e as &dyn std::any::Any;
394 Err(any_e.downcast_ref::<Error>().expect("impossible").clone())
395 }
396 }
397}
398
399impl SpatialIndex {
400 pub fn new<T, U>(small: T, config: Config) -> Result<Self, Error>
401 where
402 T: TryInto<SplitGeoSeq, Error = U>,
403 U: std::any::Any,
404 {
405 let max_distance = config.max_distance;
406 let small = sgs_try_into(small)?;
407
408 let [point_tree, line_tree, poly_tree, ls_tree, rect_tree, tri_tree] =
409 small.to_rtrees(max_distance);
410 Ok(SpatialIndex {
411 small,
412 point_tree,
413 line_tree,
414 poly_tree,
415 ls_tree,
416 rect_tree,
417 tri_tree,
418 config,
419 })
420 }
421
422 pub fn proximity_map<'a, T: 'a, U>(
423 &'a self,
424 big: T,
425 ) -> Result<impl Iterator<Item = ProxMapRow> + 'a, Error>
426 where
427 T: TryInto<SplitGeoSeq, Error = U>,
428 U: std::any::Any + std::fmt::Debug,
429 {
430 let big = sgs_try_into(big)?;
431 Ok(join!(self, ProxMapRow, big;
432 geo_big, ext_index_big, env,
433 geo_small, ext_index_small;
434 {
435 let distance = geo_big.EuclideanDistance(geo_small);
436 assert!(distance.is_finite());
437
438 if distance <= self.config.max_distance {
439 Some(ProxMapRow {big_index: ext_index_big,
440 small_index: ext_index_small,
441 distance})
442 } else {
443 None
444 }
445 }
446 ))
447 }
448
449 pub fn proximity_map_with_geos<'a, T: 'a, U>(
450 &'a self,
451 big: T,
452 ) -> Result<impl Iterator<Item = ProxMapGeoRow> + 'a, Error>
453 where
454 T: TryInto<SplitGeoSeq, Error = U>,
455 U: std::any::Any + std::fmt::Debug,
456 {
457 let big = sgs_try_into(big)?;
458 Ok(join!(self, ProxMapGeoRow, big;
459 geo_big, ext_index_big, env,
460 geo_small, ext_index_small;
461 {
462 let distance = geo_big.EuclideanDistance(geo_small);
463 assert!(distance.is_finite());
464
465 if distance <= self.config.max_distance {
466 Some(ProxMapGeoRow {big_index: ext_index_big,
467 small_index: ext_index_small,
468 big: geo_big.clone().into(),
469 small: geo_small.clone().into(), distance})
470 } else {
471 None
472 }
473 }
474 ))
475 }
476
477 pub fn spatial_join<'a, T, U>(
478 &'a self,
479 big: T,
480 interaction: Interaction,
481 ) -> Result<impl Iterator<Item = SJoinRow> + 'a, Error>
482 where
483 T: TryInto<SplitGeoSeq, Error = U>,
484 U: std::any::Any + std::fmt::Debug,
485 {
486 let big = sgs_try_into(big)?;
487 let (big_intersects, big_contains, big_within) = match interaction {
498 Interaction::Intersects => (big, SplitGeoSeq::default(), SplitGeoSeq::default()),
499 Interaction::Contains => (SplitGeoSeq::default(), big, SplitGeoSeq::default()),
500 Interaction::Within => (SplitGeoSeq::default(), SplitGeoSeq::default(), big),
501 };
502 Ok(chain!(
503 join!(self, SJoinRow, big_intersects;
506 geo_big, ext_index_big, env,
507 geo_small, ext_index_small;
508
509 if geo_small.Intersects(geo_big) {
510 Some(SJoinRow {big_index: ext_index_big, small_index: ext_index_small })
511 } else {
512 None
513 }
514 ),
515 join!(self, SJoinRow, big_contains;
516 geo_big, ext_index_big, env,
517 geo_small, ext_index_small;
518 if geo_small.Contains(geo_big) {
519 Some(SJoinRow {big_index: ext_index_big, small_index: ext_index_small})
520 } else {
521 None
522 }
523 ),
524 join!(self, SJoinRow, big_within;
525 geo_big, ext_index_big, env,
526 geo_small, ext_index_small;
527
528 if (geo_big).Contains(geo_small) {
529 Some(SJoinRow {big_index: ext_index_big, small_index: ext_index_small})
530 } else {
531 None
532 }
533 )
534 ))
535 }
536
537 pub fn spatial_join_with_geos<'a, T, U>(
538 &'a self,
539 big: T,
540 interaction: Interaction,
541 ) -> Result<impl Iterator<Item = SJoinGeoRow> + 'a, Error>
542 where
543 T: TryInto<SplitGeoSeq, Error = U>,
544 U: std::any::Any + std::fmt::Debug,
545 {
546 let big = sgs_try_into(big)?;
547
548 let (big_intersects, big_contains, big_within) = match interaction {
559 Interaction::Intersects => (big, SplitGeoSeq::default(), SplitGeoSeq::default()),
560 Interaction::Contains => (SplitGeoSeq::default(), big, SplitGeoSeq::default()),
561 Interaction::Within => (SplitGeoSeq::default(), SplitGeoSeq::default(), big),
562 };
563 Ok(chain!(
564 join!(self, SJoinGeoRow, big_intersects;
567 geo_big, ext_index_big, env,
568 geo_small, ext_index_small;
569
570 if geo_small.Intersects(geo_big) {
571 Some(SJoinGeoRow{big_index: ext_index_big, small_index: ext_index_small,
572 big: geo_big.clone().into(), small: geo_small.clone().into()})
573 } else {
574 None
575 }
576 ),
577 join!(self, SJoinGeoRow, big_contains;
578 geo_big, ext_index_big, env,
579 geo_small, ext_index_small;
580 if geo_small.Contains(geo_big) {
581 Some(SJoinGeoRow{big_index: ext_index_big, small_index: ext_index_small,
582 big: geo_big.clone().into(), small: geo_small.clone().into()})
583 } else {
584 None
585 }
586 ),
587 join!(self, SJoinGeoRow, big_within;
588 geo_big, ext_index_big, env,
589 geo_small, ext_index_small;
590
591 if geo_big.Contains(geo_small) {
592 Some(SJoinGeoRow{big_index: ext_index_big, small_index: ext_index_small,
593 big: geo_big.clone().into(), small: geo_small.clone().into()})
594 } else {
595 None
596 }
597 )
598 ))
599 }
600}
601
602#[cfg(feature = "parallel")]
603pub(crate) fn par_sgs_try_into<T, U>(thing: T) -> Result<SplitGeoSeq, Error>
604where
605 T: TryInto<Par<SplitGeoSeq>, Error = U>,
606 U: std::any::Any,
607{
608 let thing: Result<Par<SplitGeoSeq>, _> = thing.try_into();
609 match thing {
611 Ok(thing) => {
612 Ok(thing.0)
614 }
615 Err(e) => {
616 let any_e = &e as &dyn std::any::Any;
617 Err(any_e.downcast_ref::<Error>().expect("impossible").clone())
618 }
619 }
620}
621
622#[cfg(feature = "parallel")]
626impl ParSpatialIndex {
627 pub fn new<T, U>(small: T, config: Config) -> Result<Self, Error>
628 where
629 T: TryInto<Par<SplitGeoSeq>, Error = U>,
630 U: std::any::Any,
631 {
632 let max_distance = config.max_distance;
633 let small = par_sgs_try_into(small)?;
634
635 let [point_tree, line_tree, poly_tree, ls_tree, rect_tree, tri_tree] =
636 small.to_rtrees(max_distance);
637 Ok(ParSpatialIndex(SpatialIndex {
638 small,
639 point_tree,
640 line_tree,
641 poly_tree,
642 ls_tree,
643 rect_tree,
644 tri_tree,
645 config,
646 }))
647 }
648
649 pub fn proximity_map<'a, T: 'a, U>(
650 &'a self,
651 big: T,
652 ) -> Result<impl ParallelIterator<Item = ProxMapRow> + 'a, Error>
653 where
654 T: TryInto<Par<SplitGeoSeq>, Error = U>,
655 U: std::any::Any + std::fmt::Debug,
656 {
657 let big = par_sgs_try_into(big)?;
658
659 Ok(par_join!(self.0, ProxMapRow, big;
660 geo_big, ext_index_big, env,
661 geo_small, ext_index_small;
662 {
663 let distance = geo_big.EuclideanDistance(geo_small);
664 assert!(distance.is_finite());
665
666 if distance <= self.0.config.max_distance {
667 Some(ProxMapRow {big_index: ext_index_big,
668 small_index: ext_index_small,
669 distance})
670 } else {
671 None
672 }
673 }
674 ))
675 }
676
677 pub fn proximity_map_with_geos<'a, T: 'a, U>(
678 &'a self,
679 big: T,
680 ) -> Result<impl ParallelIterator<Item = ProxMapGeoRow> + 'a, Error>
681 where
682 T: TryInto<Par<SplitGeoSeq>, Error = U>,
683 U: std::any::Any + std::fmt::Debug,
684 {
685 let big = par_sgs_try_into(big)?;
686 Ok(par_join!(self.0, ProxMapGeoRow, big;
687 geo_big, ext_index_big, env,
688 geo_small, ext_index_small;
689 {
690 let distance = geo_big.EuclideanDistance(geo_small);
691 assert!(distance.is_finite());
692
693 if distance <= self.0.config.max_distance {
694 Some(ProxMapGeoRow {big_index: ext_index_big,
695 small_index: ext_index_small,
696 big: geo_big.clone().into(),
697 small: geo_small.clone().into(), distance})
698 } else {
699 None
700 }
701 }
702 ))
703 }
704
705 pub fn spatial_join<'a, T, U>(
706 &'a self,
707 big: T,
708 interaction: Interaction,
709 ) -> Result<impl ParallelIterator<Item = SJoinRow> + 'a, Error>
710 where
711 T: TryInto<Par<SplitGeoSeq>, Error = U>,
712 U: std::any::Any + std::fmt::Debug,
713 {
714 let big = par_sgs_try_into(big)?;
715 let (big_intersects, big_contains, big_within) = match interaction {
726 Interaction::Intersects => (big, SplitGeoSeq::default(), SplitGeoSeq::default()),
727 Interaction::Contains => (SplitGeoSeq::default(), big, SplitGeoSeq::default()),
728 Interaction::Within => (SplitGeoSeq::default(), SplitGeoSeq::default(), big),
729 };
730 Ok(chain!(
731 par_join!(self.0, SJoinRow, big_intersects;
734 geo_big, ext_index_big, env,
735 geo_small, ext_index_small;
736
737 if geo_small.Intersects(geo_big) {
738 Some(SJoinRow {big_index: ext_index_big, small_index: ext_index_small })
739 } else {
740 None
741 }
742 ),
743 par_join!(self.0, SJoinRow, big_contains;
744 geo_big, ext_index_big, env,
745 geo_small, ext_index_small;
746 if geo_small.Contains(geo_big) {
747 Some(SJoinRow {big_index: ext_index_big, small_index: ext_index_small})
748 } else {
749 None
750 }
751 ),
752 par_join!(self.0, SJoinRow, big_within;
753 geo_big, ext_index_big, env,
754 geo_small, ext_index_small;
755
756 if (geo_big).Contains(geo_small) {
757 Some(SJoinRow {big_index: ext_index_big, small_index: ext_index_small})
758 } else {
759 None
760 }
761 )
762 ))
763 }
764
765 pub fn spatial_join_with_geos<'a, T, U>(
766 &'a self,
767 big: T,
768 interaction: Interaction,
769 ) -> Result<impl ParallelIterator<Item = SJoinGeoRow> + 'a, Error>
770 where
771 T: TryInto<Par<SplitGeoSeq>, Error = U>,
772 U: std::any::Any + std::fmt::Debug,
773 {
774 let big = par_sgs_try_into(big)?;
775
776 let (big_intersects, big_contains, big_within) = match interaction {
787 Interaction::Intersects => (big, SplitGeoSeq::default(), SplitGeoSeq::default()),
788 Interaction::Contains => (SplitGeoSeq::default(), big, SplitGeoSeq::default()),
789 Interaction::Within => (SplitGeoSeq::default(), SplitGeoSeq::default(), big),
790 };
791 Ok(chain!(
792 par_join!(self.0, SJoinGeoRow, big_intersects;
795 geo_big, ext_index_big, env,
796 geo_small, ext_index_small;
797
798 if geo_small.Intersects(geo_big) {
799 Some(SJoinGeoRow{big_index: ext_index_big, small_index: ext_index_small,
800 big: geo_big.clone().into(), small: geo_small.clone().into()})
801 } else {
802 None
803 }
804 ),
805 par_join!(self.0, SJoinGeoRow, big_contains;
806 geo_big, ext_index_big, env,
807 geo_small, ext_index_small;
808 if geo_small.Contains(geo_big) {
809 Some(SJoinGeoRow{big_index: ext_index_big, small_index: ext_index_small,
810 big: geo_big.clone().into(), small: geo_small.clone().into()})
811 } else {
812 None
813 }
814 ),
815 par_join!(self.0, SJoinGeoRow, big_within;
816 geo_big, ext_index_big, env,
817 geo_small, ext_index_small;
818
819 if geo_big.Contains(geo_small) {
820 Some(SJoinGeoRow{big_index: ext_index_big, small_index: ext_index_small,
821 big: geo_big.clone().into(), small: geo_small.clone().into()})
822 } else {
823 None
824 }
825 )
826 ))
827 }
828}
829
830#[cfg(test)]
831mod tests {
832 #[test]
833 fn chain_macro() {
834 let x: Vec<i32> = chain!(3..7, 9..12, 2..4).collect();
835 assert_eq!(x, vec![3, 4, 5, 6, 9, 10, 11, 2, 3]);
836
837 let x: Vec<i32> = chain!(3..7).collect();
838 assert_eq!(x, vec![3, 4, 5, 6])
839 }
840}