• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geo-engine / geoengine / 3929938005

pending completion
3929938005

push

github

GitHub
Merge #713

84930 of 96741 relevant lines covered (87.79%)

79640.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.85
/datatypes/src/primitives/multi_polygon.rs
1
use std::convert::TryFrom;
2

3
use arrow::array::{ArrayBuilder, BooleanArray};
4
use arrow::error::ArrowError;
5
use float_cmp::{ApproxEq, F64Margin};
6
use geo::intersects::Intersects;
7
use serde::{Deserialize, Serialize};
8
use snafu::ensure;
9

10
use crate::collections::VectorDataType;
11
use crate::error::Error;
12
use crate::primitives::{
13
    error, BoundingBox2D, GeometryRef, MultiLineString, PrimitivesError, TypedGeometry,
14
};
15
use crate::primitives::{Coordinate2D, Geometry};
16
use crate::util::arrow::{downcast_array, ArrowTyped};
17
use crate::util::Result;
18
use arrow::datatypes::DataType;
19

20
/// A trait that allows a common access to polygons of `MultiPolygon`s and its references
21
pub trait MultiPolygonAccess {
22
    type L: AsRef<[Coordinate2D]>;
23
    type R: AsRef<[Self::L]>;
24
    fn polygons(&self) -> &[Self::R];
25
}
26

27
type Ring = Vec<Coordinate2D>;
28
type Polygon = Vec<Ring>;
29

30
/// A representation of a simple feature multi polygon
31
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
446✔
32
pub struct MultiPolygon {
33
    polygons: Vec<Polygon>,
34
}
35

36
impl MultiPolygon {
37
    pub fn new(polygons: Vec<Polygon>) -> Result<Self> {
44✔
38
        ensure!(
44✔
39
            !polygons.is_empty() && polygons.iter().all(|polygon| !polygon.is_empty()),
78✔
40
            error::UnallowedEmpty
×
41
        );
42
        ensure!(
44✔
43
            polygons
44✔
44
                .iter()
44✔
45
                .all(|polygon| Self::polygon_is_valid(polygon)),
78✔
46
            error::UnclosedPolygonRing
×
47
        );
48

49
        Ok(Self::new_unchecked(polygons))
44✔
50
    }
44✔
51

52
    fn polygon_is_valid(polygon: &[Ring]) -> bool {
78✔
53
        for ring in polygon {
176✔
54
            if !Self::ring_is_valid(ring) {
98✔
55
                return false;
×
56
            }
98✔
57
        }
58
        true
78✔
59
    }
78✔
60

61
    fn ring_is_valid(ring: &[Coordinate2D]) -> bool {
100✔
62
        if ring.len() < 4 {
100✔
63
            // must have at least four coordinates
64
            return false;
×
65
        }
100✔
66
        if ring.first() != ring.last() {
100✔
67
            // first and last coordinate must match, i.e., it is closed
68
            return false;
×
69
        }
100✔
70

100✔
71
        true
100✔
72
    }
100✔
73

74
    pub(crate) fn new_unchecked(polygons: Vec<Polygon>) -> Self {
47✔
75
        Self { polygons }
47✔
76
    }
47✔
77
}
78

79
impl MultiPolygonAccess for MultiPolygon {
80
    type R = Polygon;
81
    type L = Ring;
82
    fn polygons(&self) -> &[Self::R] {
914✔
83
        &self.polygons
914✔
84
    }
914✔
85
}
86

87
impl Geometry for MultiPolygon {
88
    const DATA_TYPE: VectorDataType = VectorDataType::MultiPolygon;
89

90
    fn intersects_bbox(&self, bbox: &BoundingBox2D) -> bool {
433✔
91
        let geo_multi_polygon: geo::MultiPolygon<f64> = self.into();
433✔
92
        let geo_rect: geo::Rect<f64> = bbox.into();
433✔
93

94
        for polygon in geo_multi_polygon {
433✔
95
            if polygon.intersects(&geo_rect) {
433✔
96
                return true;
433✔
97
            }
×
98
        }
99

100
        false
×
101
    }
433✔
102
}
103

104
impl From<&MultiPolygon> for geo::MultiPolygon<f64> {
105
    fn from(geometry: &MultiPolygon) -> geo::MultiPolygon<f64> {
433✔
106
        let polygons: Vec<geo::Polygon<f64>> = geometry
433✔
107
            .polygons()
433✔
108
            .iter()
433✔
109
            .map(|polygon| {
454✔
110
                let mut line_strings: Vec<geo::LineString<f64>> = polygon
454✔
111
                    .iter()
454✔
112
                    .map(|ring| geo::LineString(ring.iter().map(Into::into).collect()))
454✔
113
                    .collect();
454✔
114

454✔
115
                let exterior = line_strings.remove(0);
454✔
116

454✔
117
                geo::Polygon::new(exterior, line_strings)
454✔
118
            })
454✔
119
            .collect();
433✔
120
        geo::MultiPolygon(polygons)
433✔
121
    }
433✔
122
}
123

124
impl TryFrom<TypedGeometry> for MultiPolygon {
125
    type Error = Error;
126

127
    fn try_from(value: TypedGeometry) -> Result<Self, Self::Error> {
128
        if let TypedGeometry::MultiPolygon(geometry) = value {
4✔
129
            Ok(geometry)
4✔
130
        } else {
131
            Err(PrimitivesError::InvalidConversion.into())
×
132
        }
133
    }
4✔
134
}
135

136
impl AsRef<[Polygon]> for MultiPolygon {
137
    fn as_ref(&self) -> &[Polygon] {
20✔
138
        &self.polygons
20✔
139
    }
20✔
140
}
141

142
impl ArrowTyped for MultiPolygon {
143
    type ArrowArray = arrow::array::ListArray;
144
    type ArrowBuilder = arrow::array::ListBuilder<
145
        arrow::array::ListBuilder<
146
            arrow::array::ListBuilder<<Coordinate2D as ArrowTyped>::ArrowBuilder>,
147
        >,
148
    >;
149

150
    fn arrow_data_type() -> DataType {
46✔
151
        MultiLineString::arrow_list_data_type()
46✔
152
    }
46✔
153

154
    fn builder_byte_size(builder: &mut Self::ArrowBuilder) -> usize {
433✔
155
        let multi_polygon_indices_size = builder.len() * std::mem::size_of::<i32>();
433✔
156

433✔
157
        let ring_builder = builder.values();
433✔
158
        let ring_indices_size = ring_builder.len() * std::mem::size_of::<i32>();
433✔
159

433✔
160
        let line_builder = ring_builder.values();
433✔
161
        let line_indices_size = line_builder.len() * std::mem::size_of::<i32>();
433✔
162

433✔
163
        let point_builder = line_builder.values();
433✔
164
        let point_indices_size = point_builder.len() * std::mem::size_of::<i32>();
433✔
165

433✔
166
        let coordinates_size = Coordinate2D::builder_byte_size(point_builder);
433✔
167

433✔
168
        multi_polygon_indices_size
433✔
169
            + ring_indices_size
433✔
170
            + line_indices_size
433✔
171
            + point_indices_size
433✔
172
            + coordinates_size
433✔
173
    }
433✔
174

175
    fn arrow_builder(_capacity: usize) -> Self::ArrowBuilder {
48✔
176
        let coordinate_builder = Coordinate2D::arrow_builder(0);
48✔
177
        let ring_builder = arrow::array::ListBuilder::new(coordinate_builder);
48✔
178
        let polygon_builder = arrow::array::ListBuilder::new(ring_builder);
48✔
179
        arrow::array::ListBuilder::new(polygon_builder)
48✔
180
    }
48✔
181

182
    fn concat(a: &Self::ArrowArray, b: &Self::ArrowArray) -> Result<Self::ArrowArray, ArrowError> {
2✔
183
        use arrow::array::{Array, FixedSizeListArray, Float64Array, ListArray};
2✔
184

2✔
185
        let mut multi_polygon_builder = Self::arrow_builder(a.len() + b.len());
2✔
186

187
        for multi_polygons in &[a, b] {
4✔
188
            for multi_polygon_index in 0..multi_polygons.len() {
4✔
189
                let polygon_builder = multi_polygon_builder.values();
4✔
190

4✔
191
                let polygons_ref = multi_polygons.value(multi_polygon_index);
4✔
192
                let polygons = downcast_array::<ListArray>(&polygons_ref);
4✔
193

194
                for polygon_index in 0..polygons.len() {
4✔
195
                    let ring_builder = polygon_builder.values();
4✔
196

4✔
197
                    let rings_ref = polygons.value(polygon_index);
4✔
198
                    let rings = downcast_array::<ListArray>(&rings_ref);
4✔
199

200
                    for ring_index in 0..rings.len() {
5✔
201
                        let coordinate_builder = ring_builder.values();
5✔
202

5✔
203
                        let coordinates_ref = rings.value(ring_index);
5✔
204
                        let coordinates = downcast_array::<FixedSizeListArray>(&coordinates_ref);
5✔
205

206
                        for coordinate_index in 0..coordinates.len() {
22✔
207
                            let floats_ref = coordinates.value(coordinate_index);
22✔
208
                            let floats: &Float64Array = downcast_array(&floats_ref);
22✔
209

22✔
210
                            coordinate_builder.values().append_slice(floats.values());
22✔
211

22✔
212
                            coordinate_builder.append(true);
22✔
213
                        }
22✔
214

215
                        ring_builder.append(true);
5✔
216
                    }
217

218
                    polygon_builder.append(true);
4✔
219
                }
220

221
                multi_polygon_builder.append(true);
4✔
222
            }
223
        }
224

225
        Ok(multi_polygon_builder.finish())
2✔
226
    }
2✔
227

228
    fn filter(
17✔
229
        multi_polygons: &Self::ArrowArray,
17✔
230
        filter_array: &BooleanArray,
17✔
231
    ) -> Result<Self::ArrowArray, ArrowError> {
17✔
232
        use arrow::array::{Array, FixedSizeListArray, Float64Array, ListArray};
17✔
233

17✔
234
        let mut multi_polygon_builder = Self::arrow_builder(0);
17✔
235

236
        for multi_polygon_index in 0..multi_polygons.len() {
24✔
237
            if !filter_array.value(multi_polygon_index) {
24✔
238
                continue;
3✔
239
            }
21✔
240

21✔
241
            let polygon_builder = multi_polygon_builder.values();
21✔
242

21✔
243
            let polygons_ref = multi_polygons.value(multi_polygon_index);
21✔
244
            let polygons = downcast_array::<ListArray>(&polygons_ref);
21✔
245

246
            for polygon_index in 0..polygons.len() {
22✔
247
                let ring_builder = polygon_builder.values();
22✔
248

22✔
249
                let rings_ref = polygons.value(polygon_index);
22✔
250
                let rings = downcast_array::<ListArray>(&rings_ref);
22✔
251

252
                for ring_index in 0..rings.len() {
23✔
253
                    let coordinate_builder = ring_builder.values();
23✔
254

23✔
255
                    let coordinates_ref = rings.value(ring_index);
23✔
256
                    let coordinates = downcast_array::<FixedSizeListArray>(&coordinates_ref);
23✔
257

258
                    for coordinate_index in 0..coordinates.len() {
109✔
259
                        let floats_ref = coordinates.value(coordinate_index);
109✔
260
                        let floats: &Float64Array = downcast_array(&floats_ref);
109✔
261

109✔
262
                        coordinate_builder.values().append_slice(floats.values());
109✔
263

109✔
264
                        coordinate_builder.append(true);
109✔
265
                    }
109✔
266

267
                    ring_builder.append(true);
23✔
268
                }
269

270
                polygon_builder.append(true);
22✔
271
            }
272

273
            multi_polygon_builder.append(true);
21✔
274
        }
275

276
        Ok(multi_polygon_builder.finish())
17✔
277
    }
17✔
278

279
    fn from_vec(multi_polygons: Vec<Self>) -> Result<Self::ArrowArray, ArrowError>
17✔
280
    where
17✔
281
        Self: Sized,
17✔
282
    {
17✔
283
        let mut builder = Self::arrow_builder(multi_polygons.len());
17✔
284
        for multi_polygon in multi_polygons {
37✔
285
            let polygon_builder = builder.values();
20✔
286

287
            for polygon in multi_polygon.as_ref() {
23✔
288
                let ring_builder = polygon_builder.values();
23✔
289

290
                for ring in polygon {
50✔
291
                    let coordinate_builder = ring_builder.values();
27✔
292

293
                    for coordinate in ring {
155✔
294
                        let float_builder = coordinate_builder.values();
128✔
295
                        float_builder.append_value(coordinate.x);
128✔
296
                        float_builder.append_value(coordinate.y);
128✔
297
                        coordinate_builder.append(true);
128✔
298
                    }
128✔
299

300
                    ring_builder.append(true);
27✔
301
                }
302

303
                polygon_builder.append(true);
23✔
304
            }
305

306
            builder.append(true);
20✔
307
        }
308

309
        Ok(builder.finish())
17✔
310
    }
17✔
311
}
312

313
type RingRef<'g> = &'g [Coordinate2D];
314
type PolygonRef<'g> = Vec<RingRef<'g>>;
315

316
#[derive(Debug, PartialEq)]
×
317
pub struct MultiPolygonRef<'g> {
318
    polygons: Vec<PolygonRef<'g>>,
319
}
320

321
impl<'r> GeometryRef for MultiPolygonRef<'r> {}
322

323
impl<'g> MultiPolygonRef<'g> {
324
    pub fn new(polygons: Vec<PolygonRef<'g>>) -> Result<Self> {
1✔
325
        ensure!(!polygons.is_empty(), error::UnallowedEmpty);
1✔
326
        ensure!(
1✔
327
            polygons
1✔
328
                .iter()
1✔
329
                .all(|polygon| Self::polygon_is_valid(polygon)),
1✔
330
            error::UnclosedPolygonRing
×
331
        );
332

333
        Ok(Self::new_unchecked(polygons))
1✔
334
    }
1✔
335

336
    fn polygon_is_valid(polygon: &[RingRef<'g>]) -> bool {
1✔
337
        for ring in polygon {
3✔
338
            if !MultiPolygon::ring_is_valid(ring) {
2✔
339
                return false;
×
340
            }
2✔
341
        }
342
        true
1✔
343
    }
1✔
344

345
    pub(crate) fn new_unchecked(polygons: Vec<PolygonRef<'g>>) -> Self {
13✔
346
        Self { polygons }
13✔
347
    }
13✔
348
}
349

350
impl<'g> MultiPolygonAccess for MultiPolygonRef<'g> {
351
    type R = PolygonRef<'g>;
352
    type L = RingRef<'g>;
353

354
    fn polygons(&self) -> &[Self::R] {
10✔
355
        &self.polygons
10✔
356
    }
10✔
357
}
358

359
impl<'g> From<MultiPolygonRef<'g>> for geojson::Geometry {
360
    fn from(geometry: MultiPolygonRef<'g>) -> geojson::Geometry {
2✔
361
        geojson::Geometry::new(match geometry.polygons.len() {
2✔
362
            1 => {
363
                let polygon = &geometry.polygons[0];
1✔
364
                geojson::Value::Polygon(
1✔
365
                    polygon
1✔
366
                        .iter()
1✔
367
                        .map(|coordinates| coordinates.iter().map(|c| vec![c.x, c.y]).collect())
4✔
368
                        .collect(),
1✔
369
                )
1✔
370
            }
371
            _ => geojson::Value::MultiPolygon(
1✔
372
                geometry
1✔
373
                    .polygons
1✔
374
                    .iter()
1✔
375
                    .map(|polygon| {
2✔
376
                        polygon
2✔
377
                            .iter()
2✔
378
                            .map(|coordinates| coordinates.iter().map(|c| vec![c.x, c.y]).collect())
12✔
379
                            .collect()
2✔
380
                    })
2✔
381
                    .collect(),
1✔
382
            ),
1✔
383
        })
384
    }
2✔
385
}
386

387
impl<'g> From<MultiPolygonRef<'g>> for MultiPolygon {
388
    fn from(multi_point_ref: MultiPolygonRef<'g>) -> Self {
3✔
389
        MultiPolygon::from(&multi_point_ref)
3✔
390
    }
3✔
391
}
392

393
impl<'g> From<&MultiPolygonRef<'g>> for MultiPolygon {
394
    fn from(multi_point_ref: &MultiPolygonRef<'g>) -> Self {
3✔
395
        MultiPolygon::new_unchecked(
3✔
396
            multi_point_ref
3✔
397
                .polygons
3✔
398
                .iter()
3✔
399
                .map(|polygon| polygon.iter().copied().map(ToOwned::to_owned).collect())
4✔
400
                .collect(),
3✔
401
        )
3✔
402
    }
3✔
403
}
404

405
impl ApproxEq for &MultiPolygon {
406
    type Margin = F64Margin;
407

408
    fn approx_eq<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool {
6✔
409
        let m = margin.into();
6✔
410
        self.polygons().len() == other.polygons().len()
6✔
411
            && self
6✔
412
                .polygons()
6✔
413
                .iter()
6✔
414
                .zip(other.polygons())
6✔
415
                .all(|(polygon_a, polygon_b)| {
6✔
416
                    polygon_a.len() == polygon_b.len()
10✔
417
                        && polygon_a.iter().zip(polygon_b).all(|(ring_a, ring_b)| {
9✔
418
                            ring_a.len() == ring_b.len()
14✔
419
                                && ring_a.iter().zip(ring_b).all(
13✔
420
                                    |(&coordinate_a, &coordinate_b)| {
52✔
421
                                        coordinate_a.approx_eq(coordinate_b, m)
52✔
422
                                    },
52✔
423
                                )
13✔
424
                        })
14✔
425
                })
10✔
426
    }
6✔
427
}
428

429
#[cfg(test)]
430
mod tests {
431
    use float_cmp::approx_eq;
432

433
    use super::*;
434

435
    #[test]
1✔
436
    fn access() {
1✔
437
        fn aggregate<T: MultiPolygonAccess>(multi_line_string: &T) -> (usize, usize, usize) {
3✔
438
            let number_of_polygons = multi_line_string.polygons().len();
3✔
439
            let number_of_rings = multi_line_string
3✔
440
                .polygons()
3✔
441
                .iter()
3✔
442
                .map(AsRef::as_ref)
3✔
443
                .map(<[_]>::len)
3✔
444
                .sum();
3✔
445
            let number_of_coordinates = multi_line_string
3✔
446
                .polygons()
3✔
447
                .iter()
3✔
448
                .map(AsRef::as_ref)
3✔
449
                .flat_map(<[_]>::iter)
3✔
450
                .map(AsRef::as_ref)
3✔
451
                .map(<[Coordinate2D]>::len)
3✔
452
                .sum();
3✔
453

3✔
454
            (number_of_polygons, number_of_rings, number_of_coordinates)
3✔
455
        }
3✔
456

1✔
457
        let coordinates = vec![vec![
1✔
458
            vec![
1✔
459
                (0.0, 0.1).into(),
1✔
460
                (1.0, 1.1).into(),
1✔
461
                (1.0, 0.1).into(),
1✔
462
                (0.0, 0.1).into(),
1✔
463
            ],
1✔
464
            vec![
1✔
465
                (3.0, 3.1).into(),
1✔
466
                (4.0, 4.1).into(),
1✔
467
                (4.0, 3.1).into(),
1✔
468
                (3.0, 3.1).into(),
1✔
469
            ],
1✔
470
        ]];
1✔
471
        let multi_polygon = MultiPolygon::new(coordinates.clone()).unwrap();
1✔
472
        let multi_polygon_ref = MultiPolygonRef::new(
1✔
473
            coordinates
1✔
474
                .iter()
1✔
475
                .map(|r| r.iter().map(AsRef::as_ref).collect())
1✔
476
                .collect(),
1✔
477
        )
1✔
478
        .unwrap();
1✔
479

1✔
480
        assert_eq!(aggregate(&multi_polygon), (1, 2, 8));
1✔
481
        assert_eq!(aggregate(&multi_polygon), aggregate(&multi_polygon_ref));
1✔
482
    }
1✔
483

484
    #[test]
1✔
485
    fn approx_equal() {
1✔
486
        let a = MultiPolygon::new(vec![
1✔
487
            vec![
1✔
488
                vec![
1✔
489
                    (0.1, 0.1).into(),
1✔
490
                    (0.8, 0.1).into(),
1✔
491
                    (0.8, 0.8).into(),
1✔
492
                    (0.1, 0.1).into(),
1✔
493
                ],
1✔
494
                vec![
1✔
495
                    (0.2, 0.2).into(),
1✔
496
                    (0.9, 0.2).into(),
1✔
497
                    (0.9, 0.9).into(),
1✔
498
                    (0.2, 0.2).into(),
1✔
499
                ],
1✔
500
            ],
1✔
501
            vec![
1✔
502
                vec![
1✔
503
                    (1.1, 1.1).into(),
1✔
504
                    (1.8, 1.1).into(),
1✔
505
                    (1.8, 1.8).into(),
1✔
506
                    (1.1, 1.1).into(),
1✔
507
                ],
1✔
508
                vec![
1✔
509
                    (1.2, 1.2).into(),
1✔
510
                    (1.9, 1.2).into(),
1✔
511
                    (1.9, 1.9).into(),
1✔
512
                    (1.2, 1.2).into(),
1✔
513
                ],
1✔
514
            ],
1✔
515
        ])
1✔
516
        .unwrap();
1✔
517

1✔
518
        let b = MultiPolygon::new(vec![
1✔
519
            vec![
1✔
520
                vec![
1✔
521
                    (0.1, 0.099_999_999).into(),
1✔
522
                    (0.8, 0.1).into(),
1✔
523
                    (0.8, 0.8).into(),
1✔
524
                    (0.1, 0.099_999_999).into(),
1✔
525
                ],
1✔
526
                vec![
1✔
527
                    (0.2, 0.2).into(),
1✔
528
                    (0.9, 0.2).into(),
1✔
529
                    (0.9, 0.9).into(),
1✔
530
                    (0.2, 0.2).into(),
1✔
531
                ],
1✔
532
            ],
1✔
533
            vec![
1✔
534
                vec![
1✔
535
                    (1.1, 1.1).into(),
1✔
536
                    (1.8, 1.1).into(),
1✔
537
                    (1.8, 1.8).into(),
1✔
538
                    (1.1, 1.1).into(),
1✔
539
                ],
1✔
540
                vec![
1✔
541
                    (1.2, 1.2).into(),
1✔
542
                    (1.9, 1.2).into(),
1✔
543
                    (1.9, 1.9).into(),
1✔
544
                    (1.2, 1.2).into(),
1✔
545
                ],
1✔
546
            ],
1✔
547
        ])
1✔
548
        .unwrap();
1✔
549

1✔
550
        assert!(approx_eq!(&MultiPolygon, &a, &b, epsilon = 0.000_001));
1✔
551
    }
1✔
552

553
    #[test]
1✔
554
    fn not_approx_equal_ring_len() {
1✔
555
        let a = MultiPolygon::new(vec![
1✔
556
            vec![
1✔
557
                vec![
1✔
558
                    (0.1, 0.1).into(),
1✔
559
                    (0.8, 0.1).into(),
1✔
560
                    (0.8, 0.8).into(),
1✔
561
                    (0.1, 0.1).into(),
1✔
562
                ],
1✔
563
                vec![
1✔
564
                    (0.2, 0.2).into(),
1✔
565
                    (0.9, 0.2).into(),
1✔
566
                    (0.9, 0.9).into(),
1✔
567
                    (0.2, 0.2).into(),
1✔
568
                ],
1✔
569
            ],
1✔
570
            vec![vec![
1✔
571
                (1.1, 1.1).into(),
1✔
572
                (1.8, 1.1).into(),
1✔
573
                (1.8, 1.8).into(),
1✔
574
                (1.1, 1.1).into(),
1✔
575
            ]],
1✔
576
        ])
1✔
577
        .unwrap();
1✔
578

1✔
579
        let b = MultiPolygon::new(vec![
1✔
580
            vec![
1✔
581
                vec![
1✔
582
                    (0.1, 0.1).into(),
1✔
583
                    (0.8, 0.1).into(),
1✔
584
                    (0.8, 0.8).into(),
1✔
585
                    (0.1, 0.1).into(),
1✔
586
                ],
1✔
587
                vec![
1✔
588
                    (0.2, 0.2).into(),
1✔
589
                    (0.9, 0.2).into(),
1✔
590
                    (0.9, 0.9).into(),
1✔
591
                    (0.2, 0.2).into(),
1✔
592
                ],
1✔
593
            ],
1✔
594
            vec![
1✔
595
                vec![
1✔
596
                    (1.1, 1.1).into(),
1✔
597
                    (1.8, 1.1).into(),
1✔
598
                    (1.8, 1.8).into(),
1✔
599
                    (1.1, 1.1).into(),
1✔
600
                ],
1✔
601
                vec![
1✔
602
                    (1.2, 1.2).into(),
1✔
603
                    (1.9, 1.2).into(),
1✔
604
                    (1.9, 1.9).into(),
1✔
605
                    (1.2, 1.2).into(),
1✔
606
                ],
1✔
607
            ],
1✔
608
        ])
1✔
609
        .unwrap();
1✔
610

1✔
611
        assert!(!approx_eq!(&MultiPolygon, &a, &b, F64Margin::default()));
1✔
612
    }
1✔
613

614
    #[test]
1✔
615
    fn not_approx_equal_inner_len() {
1✔
616
        let a = MultiPolygon::new(vec![
1✔
617
            vec![
1✔
618
                vec![
1✔
619
                    (0.1, 0.1).into(),
1✔
620
                    (0.8, 0.1).into(),
1✔
621
                    (0.8, 0.8).into(),
1✔
622
                    (0.1, 0.1).into(),
1✔
623
                ],
1✔
624
                vec![
1✔
625
                    (0.2, 0.2).into(),
1✔
626
                    (0.9, 0.2).into(),
1✔
627
                    (0.9, 0.9).into(),
1✔
628
                    (0.2, 0.2).into(),
1✔
629
                ],
1✔
630
            ],
1✔
631
            vec![
1✔
632
                vec![
1✔
633
                    (1.1, 1.1).into(),
1✔
634
                    (1.8, 1.1).into(),
1✔
635
                    (1.8, 1.8).into(),
1✔
636
                    (1.1, 1.1).into(),
1✔
637
                ],
1✔
638
                vec![
1✔
639
                    (1.2, 1.2).into(),
1✔
640
                    (1.9, 1.2).into(),
1✔
641
                    (1.9, 1.9).into(),
1✔
642
                    (1.2, 1.2).into(),
1✔
643
                ],
1✔
644
            ],
1✔
645
        ])
1✔
646
        .unwrap();
1✔
647

1✔
648
        let b = MultiPolygon::new(vec![
1✔
649
            vec![
1✔
650
                vec![
1✔
651
                    (0.1, 0.1).into(),
1✔
652
                    (0.8, 0.1).into(),
1✔
653
                    (0.8, 0.8).into(),
1✔
654
                    (0.1, 0.1).into(),
1✔
655
                ],
1✔
656
                vec![
1✔
657
                    (0.2, 0.2).into(),
1✔
658
                    (0.9, 0.2).into(),
1✔
659
                    (0.9, 0.9).into(),
1✔
660
                    (0.2, 0.2).into(),
1✔
661
                ],
1✔
662
            ],
1✔
663
            vec![
1✔
664
                vec![
1✔
665
                    (1.1, 1.1).into(),
1✔
666
                    (1.8, 1.1).into(),
1✔
667
                    (1.8, 1.8).into(),
1✔
668
                    (1.1, 1.1).into(),
1✔
669
                ],
1✔
670
                vec![
1✔
671
                    (1.2, 1.2).into(),
1✔
672
                    (1.7, 1.2).into(),
1✔
673
                    (1.9, 1.2).into(),
1✔
674
                    (1.9, 1.9).into(),
1✔
675
                    (1.2, 1.2).into(),
1✔
676
                ],
1✔
677
            ],
1✔
678
        ])
1✔
679
        .unwrap();
1✔
680

1✔
681
        assert!(!approx_eq!(&MultiPolygon, &a, &b, F64Margin::default()));
1✔
682
    }
1✔
683
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc