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

geo-engine / geoengine / 5666839735

26 Jul 2023 09:06AM UTC coverage: 88.916% (-0.3%) from 89.193%
5666839735

Pull #833

github

web-flow
Merge 865f64877 into 3d8a7e0ad
Pull Request #833: Shared-cache

1353 of 1353 new or added lines in 15 files covered. (100.0%)

105888 of 119088 relevant lines covered (88.92%)

60793.87 hits per line

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

96.49
/datatypes/src/primitives/multi_polygon.rs
1
use arrow::array::BooleanArray;
2
use arrow::error::ArrowError;
3
use float_cmp::{ApproxEq, F64Margin};
4
use geo::intersects::Intersects;
5
use serde::{Deserialize, Serialize};
6
use snafu::ensure;
7
use std::convert::TryFrom;
8
use wkt::{ToWkt, Wkt};
9

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

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

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

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

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

50
        Ok(Self::new_unchecked(polygons))
4,093✔
51
    }
4,093✔
52

53
    fn polygon_is_valid(polygon: &[Ring]) -> bool {
6,887✔
54
        for ring in polygon {
19,216✔
55
            if !Self::ring_is_valid(ring) {
12,329✔
56
                return false;
×
57
            }
12,329✔
58
        }
59
        true
6,887✔
60
    }
6,887✔
61

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

12,331✔
72
        true
12,331✔
73
    }
12,331✔
74

75
    pub(crate) fn new_unchecked(polygons: Vec<Polygon>) -> Self {
4,102✔
76
        Self { polygons }
4,102✔
77
    }
4,102✔
78
}
79

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

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

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

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

101
        false
×
102
    }
434✔
103
}
104

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

478✔
116
                let exterior = line_strings.remove(0);
478✔
117

478✔
118
                geo::Polygon::new(exterior, line_strings)
478✔
119
            })
478✔
120
            .collect();
435✔
121
        geo::MultiPolygon(polygons)
435✔
122
    }
435✔
123
}
124

125
impl From<geo::MultiPolygon<f64>> for MultiPolygon {
126
    fn from(geometry: geo::MultiPolygon<f64>) -> MultiPolygon {
2✔
127
        let geo::MultiPolygon(geo_polygons) = geometry;
2✔
128

2✔
129
        let mut polygons = Vec::with_capacity(geo_polygons.len());
2✔
130

131
        for geo_polygon in geo_polygons {
26✔
132
            let (exterior, interiors) = geo_polygon.into_inner();
24✔
133

24✔
134
            let mut rings = Vec::with_capacity(1 + interiors.len());
24✔
135

24✔
136
            let geo::LineString(exterior_coords) = exterior;
24✔
137
            let ring: Ring = exterior_coords.into_iter().map(Into::into).collect();
24✔
138
            rings.push(ring);
24✔
139

140
            for geo::LineString(interior_coords) in interiors {
26✔
141
                let ring: Ring = interior_coords.into_iter().map(Into::into).collect();
2✔
142
                rings.push(ring);
2✔
143
            }
2✔
144

145
            polygons.push(rings);
24✔
146
        }
147

148
        MultiPolygon::new_unchecked(polygons)
2✔
149
    }
2✔
150
}
151

152
impl TryFrom<TypedGeometry> for MultiPolygon {
153
    type Error = Error;
154

155
    fn try_from(value: TypedGeometry) -> Result<Self, Self::Error> {
156
        if let TypedGeometry::MultiPolygon(geometry) = value {
2✔
157
            Ok(geometry)
2✔
158
        } else {
159
            Err(PrimitivesError::InvalidConversion.into())
×
160
        }
161
    }
2✔
162
}
163

164
impl AsRef<[Polygon]> for MultiPolygon {
165
    fn as_ref(&self) -> &[Polygon] {
31✔
166
        &self.polygons
31✔
167
    }
31✔
168
}
169

170
impl ArrowTyped for MultiPolygon {
171
    type ArrowArray = arrow::array::ListArray;
172
    type ArrowBuilder = arrow::array::ListBuilder<
173
        arrow::array::ListBuilder<
174
            arrow::array::ListBuilder<<Coordinate2D as ArrowTyped>::ArrowBuilder>,
175
        >,
176
    >;
177

178
    fn arrow_data_type() -> DataType {
52✔
179
        MultiLineString::arrow_list_data_type()
52✔
180
    }
52✔
181

182
    fn estimate_array_memory_size(builder: &mut Self::ArrowBuilder) -> usize {
562✔
183
        let static_size = std::mem::size_of::<Self::ArrowArray>()
562✔
184
            + std::mem::size_of::<<MultiLineString as ArrowTyped>::ArrowArray>()
562✔
185
            + std::mem::size_of::<<MultiPoint as ArrowTyped>::ArrowArray>();
562✔
186

562✔
187
        let feature_offset_bytes_size = std::mem::size_of_val(builder.offsets_slice());
562✔
188

562✔
189
        let polygon_builder = builder.values();
562✔
190

562✔
191
        let polygon_offset_bytes_size = std::mem::size_of_val(polygon_builder.offsets_slice());
562✔
192

562✔
193
        let ring_builder = polygon_builder.values();
562✔
194

562✔
195
        let ring_offset_bytes_size = std::mem::size_of_val(ring_builder.offsets_slice());
562✔
196

562✔
197
        let coordinates_builder = ring_builder.values();
562✔
198

562✔
199
        let coords_size = Coordinate2D::estimate_array_memory_size(coordinates_builder);
562✔
200

562✔
201
        static_size
562✔
202
            + coords_size
562✔
203
            + padded_buffer_size(ring_offset_bytes_size, 64)
562✔
204
            + padded_buffer_size(polygon_offset_bytes_size, 64)
562✔
205
            + padded_buffer_size(feature_offset_bytes_size, 64)
562✔
206
    }
562✔
207

208
    fn arrow_builder(_capacity: usize) -> Self::ArrowBuilder {
184✔
209
        let coordinate_builder = Coordinate2D::arrow_builder(0);
184✔
210
        let ring_builder = arrow::array::ListBuilder::new(coordinate_builder);
184✔
211
        let polygon_builder = arrow::array::ListBuilder::new(ring_builder);
184✔
212
        arrow::array::ListBuilder::new(polygon_builder)
184✔
213
    }
184✔
214

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

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

220
        for multi_polygons in &[a, b] {
4✔
221
            for multi_polygon_index in 0..multi_polygons.len() {
4✔
222
                let polygon_builder = multi_polygon_builder.values();
4✔
223

4✔
224
                let polygons_ref = multi_polygons.value(multi_polygon_index);
4✔
225
                let polygons = downcast_array::<ListArray>(&polygons_ref);
4✔
226

227
                for polygon_index in 0..polygons.len() {
4✔
228
                    let ring_builder = polygon_builder.values();
4✔
229

4✔
230
                    let rings_ref = polygons.value(polygon_index);
4✔
231
                    let rings = downcast_array::<ListArray>(&rings_ref);
4✔
232

233
                    for ring_index in 0..rings.len() {
5✔
234
                        let coordinate_builder = ring_builder.values();
5✔
235

5✔
236
                        let coordinates_ref = rings.value(ring_index);
5✔
237
                        let coordinates = downcast_array::<FixedSizeListArray>(&coordinates_ref);
5✔
238

239
                        for coordinate_index in 0..coordinates.len() {
22✔
240
                            let floats_ref = coordinates.value(coordinate_index);
22✔
241
                            let floats: &Float64Array = downcast_array(&floats_ref);
22✔
242

22✔
243
                            coordinate_builder.values().append_slice(floats.values());
22✔
244

22✔
245
                            coordinate_builder.append(true);
22✔
246
                        }
22✔
247

248
                        ring_builder.append(true);
5✔
249
                    }
250

251
                    polygon_builder.append(true);
4✔
252
                }
253

254
                multi_polygon_builder.append(true);
4✔
255
            }
256
        }
257

258
        Ok(multi_polygon_builder.finish_cloned())
2✔
259
    }
2✔
260

261
    fn filter(
17✔
262
        multi_polygons: &Self::ArrowArray,
17✔
263
        filter_array: &BooleanArray,
17✔
264
    ) -> Result<Self::ArrowArray, ArrowError> {
17✔
265
        use arrow::array::{Array, FixedSizeListArray, Float64Array, ListArray};
17✔
266

17✔
267
        let mut multi_polygon_builder = Self::arrow_builder(0);
17✔
268

269
        for multi_polygon_index in 0..multi_polygons.len() {
24✔
270
            if !filter_array.value(multi_polygon_index) {
24✔
271
                continue;
3✔
272
            }
21✔
273

21✔
274
            let polygon_builder = multi_polygon_builder.values();
21✔
275

21✔
276
            let polygons_ref = multi_polygons.value(multi_polygon_index);
21✔
277
            let polygons = downcast_array::<ListArray>(&polygons_ref);
21✔
278

279
            for polygon_index in 0..polygons.len() {
22✔
280
                let ring_builder = polygon_builder.values();
22✔
281

22✔
282
                let rings_ref = polygons.value(polygon_index);
22✔
283
                let rings = downcast_array::<ListArray>(&rings_ref);
22✔
284

285
                for ring_index in 0..rings.len() {
23✔
286
                    let coordinate_builder = ring_builder.values();
23✔
287

23✔
288
                    let coordinates_ref = rings.value(ring_index);
23✔
289
                    let coordinates = downcast_array::<FixedSizeListArray>(&coordinates_ref);
23✔
290

291
                    for coordinate_index in 0..coordinates.len() {
109✔
292
                        let floats_ref = coordinates.value(coordinate_index);
109✔
293
                        let floats: &Float64Array = downcast_array(&floats_ref);
109✔
294

109✔
295
                        coordinate_builder.values().append_slice(floats.values());
109✔
296

109✔
297
                        coordinate_builder.append(true);
109✔
298
                    }
109✔
299

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

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

306
            multi_polygon_builder.append(true);
21✔
307
        }
308

309
        Ok(multi_polygon_builder.finish_cloned())
17✔
310
    }
17✔
311

312
    fn from_vec(multi_polygons: Vec<Self>) -> Result<Self::ArrowArray, ArrowError>
25✔
313
    where
25✔
314
        Self: Sized,
25✔
315
    {
25✔
316
        let mut builder = Self::arrow_builder(multi_polygons.len());
25✔
317
        for multi_polygon in multi_polygons {
56✔
318
            let polygon_builder = builder.values();
31✔
319

320
            for polygon in multi_polygon.as_ref() {
60✔
321
                let ring_builder = polygon_builder.values();
60✔
322

323
                for ring in polygon {
124✔
324
                    let coordinate_builder = ring_builder.values();
64✔
325

326
                    for coordinate in ring {
353✔
327
                        let float_builder = coordinate_builder.values();
289✔
328
                        float_builder.append_value(coordinate.x);
289✔
329
                        float_builder.append_value(coordinate.y);
289✔
330
                        coordinate_builder.append(true);
289✔
331
                    }
289✔
332

333
                    ring_builder.append(true);
64✔
334
                }
335

336
                polygon_builder.append(true);
60✔
337
            }
338

339
            builder.append(true);
31✔
340
        }
341

342
        Ok(builder.finish_cloned())
25✔
343
    }
25✔
344
}
345

346
type RingRef<'g> = &'g [Coordinate2D];
347
type PolygonRef<'g> = Vec<RingRef<'g>>;
348

349
#[derive(Debug, PartialEq)]
×
350
pub struct MultiPolygonRef<'g> {
351
    polygons: Vec<PolygonRef<'g>>,
352
}
353

354
impl<'r> GeometryRef for MultiPolygonRef<'r> {
355
    type GeometryType = MultiPolygon;
356

357
    fn as_geometry(&self) -> Self::GeometryType {
×
358
        MultiPolygon::from(self)
×
359
    }
×
360

361
    fn bbox(&self) -> Option<BoundingBox2D> {
×
362
        self.bbox()
×
363
    }
×
364
}
365

366
impl<'g> MultiPolygonRef<'g> {
367
    pub fn new(polygons: Vec<PolygonRef<'g>>) -> Result<Self> {
1✔
368
        ensure!(!polygons.is_empty(), error::UnallowedEmpty);
1✔
369
        ensure!(
1✔
370
            polygons
1✔
371
                .iter()
1✔
372
                .all(|polygon| Self::polygon_is_valid(polygon)),
1✔
373
            error::UnclosedPolygonRing
×
374
        );
375

376
        Ok(Self::new_unchecked(polygons))
1✔
377
    }
1✔
378

379
    fn polygon_is_valid(polygon: &[RingRef<'g>]) -> bool {
1✔
380
        for ring in polygon {
3✔
381
            if !MultiPolygon::ring_is_valid(ring) {
2✔
382
                return false;
×
383
            }
2✔
384
        }
385
        true
1✔
386
    }
1✔
387

388
    pub(crate) fn new_unchecked(polygons: Vec<PolygonRef<'g>>) -> Self {
51✔
389
        Self { polygons }
51✔
390
    }
51✔
391

392
    pub fn bbox(&self) -> Option<BoundingBox2D> {
×
393
        self.polygons().iter().fold(None, |bbox, rings| {
×
394
            let lbox = BoundingBox2D::from_coord_ref_iter(rings[0].iter()); // we only need to look at the outer ring coords
×
395
            match (bbox, lbox) {
×
396
                (None, Some(lbox)) => Some(lbox),
×
397
                (Some(bbox), Some(lbox)) => Some(bbox.union(&lbox)),
×
398
                (bbox, None) => bbox,
×
399
            }
400
        })
×
401
    }
×
402
}
403

404
impl<'g> MultiPolygonAccess for MultiPolygonRef<'g> {
405
    type R = PolygonRef<'g>;
406
    type L = RingRef<'g>;
407

408
    fn polygons(&self) -> &[Self::R] {
42✔
409
        &self.polygons
42✔
410
    }
42✔
411
}
412

413
impl<'r> ToWkt<f64> for MultiPolygonRef<'r> {
414
    fn to_wkt(&self) -> Wkt<f64> {
5✔
415
        let multi_polygon = self.polygons();
5✔
416
        let mut wkt_multi_polygon =
5✔
417
            wkt::types::MultiPolygon(Vec::with_capacity(multi_polygon.len()));
5✔
418

419
        for polygon in multi_polygon {
13✔
420
            let mut wkt_polygon = wkt::types::Polygon(Vec::with_capacity(polygon.len()));
8✔
421

422
            for ring in polygon {
18✔
423
                let mut wkt_line_string = wkt::types::LineString(Vec::with_capacity(ring.len()));
10✔
424

425
                for coord in *ring {
50✔
426
                    wkt_line_string.0.push(coord.into());
40✔
427
                }
40✔
428

429
                wkt_polygon.0.push(wkt_line_string);
10✔
430
            }
431

432
            wkt_multi_polygon.0.push(wkt_polygon);
8✔
433
        }
434

435
        Wkt {
5✔
436
            item: wkt::Geometry::MultiPolygon(wkt_multi_polygon),
5✔
437
        }
5✔
438
    }
5✔
439
}
440

441
impl<'g> From<MultiPolygonRef<'g>> for geojson::Geometry {
442
    fn from(geometry: MultiPolygonRef<'g>) -> geojson::Geometry {
2✔
443
        geojson::Geometry::new(match geometry.polygons.len() {
2✔
444
            1 => {
445
                let polygon = &geometry.polygons[0];
1✔
446
                geojson::Value::Polygon(
1✔
447
                    polygon
1✔
448
                        .iter()
1✔
449
                        .map(|coordinates| coordinates.iter().map(|c| vec![c.x, c.y]).collect())
4✔
450
                        .collect(),
1✔
451
                )
1✔
452
            }
453
            _ => geojson::Value::MultiPolygon(
1✔
454
                geometry
1✔
455
                    .polygons
1✔
456
                    .iter()
1✔
457
                    .map(|polygon| {
2✔
458
                        polygon
2✔
459
                            .iter()
2✔
460
                            .map(|coordinates| coordinates.iter().map(|c| vec![c.x, c.y]).collect())
12✔
461
                            .collect()
2✔
462
                    })
2✔
463
                    .collect(),
1✔
464
            ),
1✔
465
        })
466
    }
2✔
467
}
468

469
impl<'g> From<MultiPolygonRef<'g>> for MultiPolygon {
470
    fn from(multi_point_ref: MultiPolygonRef<'g>) -> Self {
7✔
471
        MultiPolygon::from(&multi_point_ref)
7✔
472
    }
7✔
473
}
474

475
impl<'g> From<&MultiPolygonRef<'g>> for MultiPolygon {
476
    fn from(multi_point_ref: &MultiPolygonRef<'g>) -> Self {
7✔
477
        MultiPolygon::new_unchecked(
7✔
478
            multi_point_ref
7✔
479
                .polygons
7✔
480
                .iter()
7✔
481
                .map(|polygon| polygon.iter().copied().map(ToOwned::to_owned).collect())
10✔
482
                .collect(),
7✔
483
        )
7✔
484
    }
7✔
485
}
486

487
impl<'g> From<&'g MultiPolygon> for MultiPolygonRef<'g> {
488
    fn from(multi_point_ref: &'g MultiPolygon) -> Self {
1✔
489
        let mut polygons = Vec::with_capacity(multi_point_ref.polygons().len());
1✔
490

491
        for polygon in multi_point_ref.polygons() {
2✔
492
            let mut rings = Vec::with_capacity(polygon.len());
2✔
493

494
            for ring in polygon {
6✔
495
                rings.push(ring.as_ref());
4✔
496
            }
4✔
497

498
            polygons.push(rings);
2✔
499
        }
500

501
        MultiPolygonRef::new_unchecked(polygons)
1✔
502
    }
1✔
503
}
504

505
impl<'g> From<&MultiPolygonRef<'g>> for geo::MultiPolygon<f64> {
506
    fn from(geometry: &MultiPolygonRef<'g>) -> Self {
1✔
507
        let polygons: Vec<geo::Polygon<f64>> = geometry
1✔
508
            .polygons()
1✔
509
            .iter()
1✔
510
            .map(|polygon| {
22✔
511
                let mut line_strings: Vec<geo::LineString<f64>> = polygon
22✔
512
                    .iter()
22✔
513
                    .map(|ring| geo::LineString(ring.iter().map(Into::into).collect()))
22✔
514
                    .collect();
22✔
515

22✔
516
                let exterior = line_strings.remove(0);
22✔
517

22✔
518
                geo::Polygon::new(exterior, line_strings)
22✔
519
            })
22✔
520
            .collect();
1✔
521
        geo::MultiPolygon(polygons)
1✔
522
    }
1✔
523
}
524

525
impl ApproxEq for &MultiPolygon {
526
    type Margin = F64Margin;
527

528
    fn approx_eq<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool {
6✔
529
        let m = margin.into();
6✔
530
        self.polygons().len() == other.polygons().len()
6✔
531
            && self
6✔
532
                .polygons()
6✔
533
                .iter()
6✔
534
                .zip(other.polygons())
6✔
535
                .all(|(polygon_a, polygon_b)| {
6✔
536
                    polygon_a.len() == polygon_b.len()
10✔
537
                        && polygon_a.iter().zip(polygon_b).all(|(ring_a, ring_b)| {
9✔
538
                            ring_a.len() == ring_b.len()
14✔
539
                                && ring_a.iter().zip(ring_b).all(
13✔
540
                                    |(&coordinate_a, &coordinate_b)| {
52✔
541
                                        coordinate_a.approx_eq(coordinate_b, m)
52✔
542
                                    },
52✔
543
                                )
13✔
544
                        })
14✔
545
                })
10✔
546
    }
6✔
547
}
548

549
#[cfg(test)]
550
mod tests {
551
    use super::*;
552
    use arrow::array::{Array, ArrayBuilder};
553
    use float_cmp::approx_eq;
554

555
    #[test]
1✔
556
    fn access() {
1✔
557
        fn aggregate<T: MultiPolygonAccess>(multi_line_string: &T) -> (usize, usize, usize) {
3✔
558
            let number_of_polygons = multi_line_string.polygons().len();
3✔
559
            let number_of_rings = multi_line_string
3✔
560
                .polygons()
3✔
561
                .iter()
3✔
562
                .map(AsRef::as_ref)
3✔
563
                .map(<[_]>::len)
3✔
564
                .sum();
3✔
565
            let number_of_coordinates = multi_line_string
3✔
566
                .polygons()
3✔
567
                .iter()
3✔
568
                .map(AsRef::as_ref)
3✔
569
                .flat_map(<[_]>::iter)
3✔
570
                .map(AsRef::as_ref)
3✔
571
                .map(<[Coordinate2D]>::len)
3✔
572
                .sum();
3✔
573

3✔
574
            (number_of_polygons, number_of_rings, number_of_coordinates)
3✔
575
        }
3✔
576

1✔
577
        let coordinates = vec![vec![
1✔
578
            vec![
1✔
579
                (0.0, 0.1).into(),
1✔
580
                (1.0, 1.1).into(),
1✔
581
                (1.0, 0.1).into(),
1✔
582
                (0.0, 0.1).into(),
1✔
583
            ],
1✔
584
            vec![
1✔
585
                (3.0, 3.1).into(),
1✔
586
                (4.0, 4.1).into(),
1✔
587
                (4.0, 3.1).into(),
1✔
588
                (3.0, 3.1).into(),
1✔
589
            ],
1✔
590
        ]];
1✔
591
        let multi_polygon = MultiPolygon::new(coordinates.clone()).unwrap();
1✔
592
        let multi_polygon_ref = MultiPolygonRef::new(
1✔
593
            coordinates
1✔
594
                .iter()
1✔
595
                .map(|r| r.iter().map(AsRef::as_ref).collect())
1✔
596
                .collect(),
1✔
597
        )
1✔
598
        .unwrap();
1✔
599

1✔
600
        assert_eq!(aggregate(&multi_polygon), (1, 2, 8));
1✔
601
        assert_eq!(aggregate(&multi_polygon), aggregate(&multi_polygon_ref));
1✔
602
    }
1✔
603

604
    #[test]
1✔
605
    fn approx_equal() {
1✔
606
        let a = MultiPolygon::new(vec![
1✔
607
            vec![
1✔
608
                vec![
1✔
609
                    (0.1, 0.1).into(),
1✔
610
                    (0.8, 0.1).into(),
1✔
611
                    (0.8, 0.8).into(),
1✔
612
                    (0.1, 0.1).into(),
1✔
613
                ],
1✔
614
                vec![
1✔
615
                    (0.2, 0.2).into(),
1✔
616
                    (0.9, 0.2).into(),
1✔
617
                    (0.9, 0.9).into(),
1✔
618
                    (0.2, 0.2).into(),
1✔
619
                ],
1✔
620
            ],
1✔
621
            vec![
1✔
622
                vec![
1✔
623
                    (1.1, 1.1).into(),
1✔
624
                    (1.8, 1.1).into(),
1✔
625
                    (1.8, 1.8).into(),
1✔
626
                    (1.1, 1.1).into(),
1✔
627
                ],
1✔
628
                vec![
1✔
629
                    (1.2, 1.2).into(),
1✔
630
                    (1.9, 1.2).into(),
1✔
631
                    (1.9, 1.9).into(),
1✔
632
                    (1.2, 1.2).into(),
1✔
633
                ],
1✔
634
            ],
1✔
635
        ])
1✔
636
        .unwrap();
1✔
637

1✔
638
        let b = MultiPolygon::new(vec![
1✔
639
            vec![
1✔
640
                vec![
1✔
641
                    (0.1, 0.099_999_999).into(),
1✔
642
                    (0.8, 0.1).into(),
1✔
643
                    (0.8, 0.8).into(),
1✔
644
                    (0.1, 0.099_999_999).into(),
1✔
645
                ],
1✔
646
                vec![
1✔
647
                    (0.2, 0.2).into(),
1✔
648
                    (0.9, 0.2).into(),
1✔
649
                    (0.9, 0.9).into(),
1✔
650
                    (0.2, 0.2).into(),
1✔
651
                ],
1✔
652
            ],
1✔
653
            vec![
1✔
654
                vec![
1✔
655
                    (1.1, 1.1).into(),
1✔
656
                    (1.8, 1.1).into(),
1✔
657
                    (1.8, 1.8).into(),
1✔
658
                    (1.1, 1.1).into(),
1✔
659
                ],
1✔
660
                vec![
1✔
661
                    (1.2, 1.2).into(),
1✔
662
                    (1.9, 1.2).into(),
1✔
663
                    (1.9, 1.9).into(),
1✔
664
                    (1.2, 1.2).into(),
1✔
665
                ],
1✔
666
            ],
1✔
667
        ])
1✔
668
        .unwrap();
1✔
669

1✔
670
        assert!(approx_eq!(&MultiPolygon, &a, &b, epsilon = 0.000_001));
1✔
671
    }
1✔
672

673
    #[test]
1✔
674
    fn not_approx_equal_ring_len() {
1✔
675
        let a = MultiPolygon::new(vec![
1✔
676
            vec![
1✔
677
                vec![
1✔
678
                    (0.1, 0.1).into(),
1✔
679
                    (0.8, 0.1).into(),
1✔
680
                    (0.8, 0.8).into(),
1✔
681
                    (0.1, 0.1).into(),
1✔
682
                ],
1✔
683
                vec![
1✔
684
                    (0.2, 0.2).into(),
1✔
685
                    (0.9, 0.2).into(),
1✔
686
                    (0.9, 0.9).into(),
1✔
687
                    (0.2, 0.2).into(),
1✔
688
                ],
1✔
689
            ],
1✔
690
            vec![vec![
1✔
691
                (1.1, 1.1).into(),
1✔
692
                (1.8, 1.1).into(),
1✔
693
                (1.8, 1.8).into(),
1✔
694
                (1.1, 1.1).into(),
1✔
695
            ]],
1✔
696
        ])
1✔
697
        .unwrap();
1✔
698

1✔
699
        let b = MultiPolygon::new(vec![
1✔
700
            vec![
1✔
701
                vec![
1✔
702
                    (0.1, 0.1).into(),
1✔
703
                    (0.8, 0.1).into(),
1✔
704
                    (0.8, 0.8).into(),
1✔
705
                    (0.1, 0.1).into(),
1✔
706
                ],
1✔
707
                vec![
1✔
708
                    (0.2, 0.2).into(),
1✔
709
                    (0.9, 0.2).into(),
1✔
710
                    (0.9, 0.9).into(),
1✔
711
                    (0.2, 0.2).into(),
1✔
712
                ],
1✔
713
            ],
1✔
714
            vec![
1✔
715
                vec![
1✔
716
                    (1.1, 1.1).into(),
1✔
717
                    (1.8, 1.1).into(),
1✔
718
                    (1.8, 1.8).into(),
1✔
719
                    (1.1, 1.1).into(),
1✔
720
                ],
1✔
721
                vec![
1✔
722
                    (1.2, 1.2).into(),
1✔
723
                    (1.9, 1.2).into(),
1✔
724
                    (1.9, 1.9).into(),
1✔
725
                    (1.2, 1.2).into(),
1✔
726
                ],
1✔
727
            ],
1✔
728
        ])
1✔
729
        .unwrap();
1✔
730

1✔
731
        assert!(!approx_eq!(&MultiPolygon, &a, &b, F64Margin::default()));
1✔
732
    }
1✔
733

734
    #[test]
1✔
735
    fn not_approx_equal_inner_len() {
1✔
736
        let a = MultiPolygon::new(vec![
1✔
737
            vec![
1✔
738
                vec![
1✔
739
                    (0.1, 0.1).into(),
1✔
740
                    (0.8, 0.1).into(),
1✔
741
                    (0.8, 0.8).into(),
1✔
742
                    (0.1, 0.1).into(),
1✔
743
                ],
1✔
744
                vec![
1✔
745
                    (0.2, 0.2).into(),
1✔
746
                    (0.9, 0.2).into(),
1✔
747
                    (0.9, 0.9).into(),
1✔
748
                    (0.2, 0.2).into(),
1✔
749
                ],
1✔
750
            ],
1✔
751
            vec![
1✔
752
                vec![
1✔
753
                    (1.1, 1.1).into(),
1✔
754
                    (1.8, 1.1).into(),
1✔
755
                    (1.8, 1.8).into(),
1✔
756
                    (1.1, 1.1).into(),
1✔
757
                ],
1✔
758
                vec![
1✔
759
                    (1.2, 1.2).into(),
1✔
760
                    (1.9, 1.2).into(),
1✔
761
                    (1.9, 1.9).into(),
1✔
762
                    (1.2, 1.2).into(),
1✔
763
                ],
1✔
764
            ],
1✔
765
        ])
1✔
766
        .unwrap();
1✔
767

1✔
768
        let b = MultiPolygon::new(vec![
1✔
769
            vec![
1✔
770
                vec![
1✔
771
                    (0.1, 0.1).into(),
1✔
772
                    (0.8, 0.1).into(),
1✔
773
                    (0.8, 0.8).into(),
1✔
774
                    (0.1, 0.1).into(),
1✔
775
                ],
1✔
776
                vec![
1✔
777
                    (0.2, 0.2).into(),
1✔
778
                    (0.9, 0.2).into(),
1✔
779
                    (0.9, 0.9).into(),
1✔
780
                    (0.2, 0.2).into(),
1✔
781
                ],
1✔
782
            ],
1✔
783
            vec![
1✔
784
                vec![
1✔
785
                    (1.1, 1.1).into(),
1✔
786
                    (1.8, 1.1).into(),
1✔
787
                    (1.8, 1.8).into(),
1✔
788
                    (1.1, 1.1).into(),
1✔
789
                ],
1✔
790
                vec![
1✔
791
                    (1.2, 1.2).into(),
1✔
792
                    (1.7, 1.2).into(),
1✔
793
                    (1.9, 1.2).into(),
1✔
794
                    (1.9, 1.9).into(),
1✔
795
                    (1.2, 1.2).into(),
1✔
796
                ],
1✔
797
            ],
1✔
798
        ])
1✔
799
        .unwrap();
1✔
800

1✔
801
        assert!(!approx_eq!(&MultiPolygon, &a, &b, F64Margin::default()));
1✔
802
    }
1✔
803

804
    #[test]
1✔
805
    fn test_to_wkt() {
1✔
806
        let a = MultiPolygon::new(vec![
1✔
807
            vec![
1✔
808
                vec![
1✔
809
                    (0.1, 0.1).into(),
1✔
810
                    (0.8, 0.1).into(),
1✔
811
                    (0.8, 0.8).into(),
1✔
812
                    (0.1, 0.1).into(),
1✔
813
                ],
1✔
814
                vec![
1✔
815
                    (0.2, 0.2).into(),
1✔
816
                    (0.9, 0.2).into(),
1✔
817
                    (0.9, 0.9).into(),
1✔
818
                    (0.2, 0.2).into(),
1✔
819
                ],
1✔
820
            ],
1✔
821
            vec![
1✔
822
                vec![
1✔
823
                    (1.1, 1.1).into(),
1✔
824
                    (1.8, 1.1).into(),
1✔
825
                    (1.8, 1.8).into(),
1✔
826
                    (1.1, 1.1).into(),
1✔
827
                ],
1✔
828
                vec![
1✔
829
                    (1.2, 1.2).into(),
1✔
830
                    (1.9, 1.2).into(),
1✔
831
                    (1.9, 1.9).into(),
1✔
832
                    (1.2, 1.2).into(),
1✔
833
                ],
1✔
834
            ],
1✔
835
        ])
1✔
836
        .unwrap();
1✔
837

1✔
838
        let a_ref = MultiPolygonRef::from(&a);
1✔
839

1✔
840
        assert_eq!(
1✔
841
            a_ref.wkt_string(),
1✔
842
            "MULTIPOLYGON(((0.1 0.1,0.8 0.1,0.8 0.8,0.1 0.1),(0.2 0.2,0.9 0.2,0.9 0.9,0.2 0.2)),((1.1 1.1,1.8 1.1,1.8 1.8,1.1 1.1),(1.2 1.2,1.9 1.2,1.9 1.9,1.2 1.2)))"
1✔
843
        );
1✔
844
    }
1✔
845

846
    #[test]
1✔
847
    fn test_to_geo_and_back() {
1✔
848
        let polygon = MultiPolygon::new(vec![
1✔
849
            vec![
1✔
850
                vec![
1✔
851
                    (0.1, 0.1).into(),
1✔
852
                    (0.8, 0.1).into(),
1✔
853
                    (0.8, 0.8).into(),
1✔
854
                    (0.1, 0.1).into(),
1✔
855
                ],
1✔
856
                vec![
1✔
857
                    (0.2, 0.2).into(),
1✔
858
                    (0.9, 0.2).into(),
1✔
859
                    (0.9, 0.9).into(),
1✔
860
                    (0.2, 0.2).into(),
1✔
861
                ],
1✔
862
            ],
1✔
863
            vec![
1✔
864
                vec![
1✔
865
                    (1.1, 1.1).into(),
1✔
866
                    (1.8, 1.1).into(),
1✔
867
                    (1.8, 1.8).into(),
1✔
868
                    (1.1, 1.1).into(),
1✔
869
                ],
1✔
870
                vec![
1✔
871
                    (1.2, 1.2).into(),
1✔
872
                    (1.9, 1.2).into(),
1✔
873
                    (1.9, 1.9).into(),
1✔
874
                    (1.2, 1.2).into(),
1✔
875
                ],
1✔
876
            ],
1✔
877
        ])
1✔
878
        .unwrap();
1✔
879

1✔
880
        let geo_polygon = geo::MultiPolygon::<f64>::from(&polygon);
1✔
881

1✔
882
        let polygon_back = MultiPolygon::from(geo_polygon);
1✔
883

1✔
884
        assert_eq!(polygon, polygon_back);
1✔
885
    }
1✔
886

887
    #[test]
1✔
888
    #[allow(clippy::too_many_lines)]
889
    fn arrow_builder_size() {
1✔
890
        fn push_geometry(
4,032✔
891
            geometries_builder: &mut <MultiPolygon as ArrowTyped>::ArrowBuilder,
4,032✔
892
            geometry: &MultiPolygon,
4,032✔
893
        ) {
4,032✔
894
            let polygon_builder = geometries_builder.values();
4,032✔
895

896
            for polygon in geometry.polygons() {
6,762✔
897
                let ring_builder = polygon_builder.values();
6,762✔
898

899
                for ring in polygon {
18,942✔
900
                    let coordinate_builder = ring_builder.values();
12,180✔
901

902
                    for coordinate in ring {
60,900✔
903
                        coordinate_builder
48,720✔
904
                            .values()
48,720✔
905
                            .append_slice(coordinate.as_ref());
48,720✔
906

48,720✔
907
                        coordinate_builder.append(true);
48,720✔
908
                    }
48,720✔
909

910
                    ring_builder.append(true);
12,180✔
911
                }
912

913
                polygon_builder.append(true);
6,762✔
914
            }
915

916
            geometries_builder.append(true);
4,032✔
917
        }
4,032✔
918

919
        for num_multi_polygons in 0..64 {
65✔
920
            for capacity in [0, num_multi_polygons] {
128✔
921
                let mut builder = MultiPolygon::arrow_builder(capacity);
128✔
922

923
                for i in 0..num_multi_polygons {
4,032✔
924
                    match i % 3 {
4,032✔
925
                        0 => {
1,386✔
926
                            push_geometry(
1,386✔
927
                                &mut builder,
1,386✔
928
                                &MultiPolygon::new(vec![
1,386✔
929
                                    vec![
1,386✔
930
                                        vec![
1,386✔
931
                                            (0.1, 0.1).into(),
1,386✔
932
                                            (0.8, 0.1).into(),
1,386✔
933
                                            (0.8, 0.8).into(),
1,386✔
934
                                            (0.1, 0.1).into(),
1,386✔
935
                                        ],
1,386✔
936
                                        vec![
1,386✔
937
                                            (0.2, 0.2).into(),
1,386✔
938
                                            (0.9, 0.2).into(),
1,386✔
939
                                            (0.9, 0.9).into(),
1,386✔
940
                                            (0.2, 0.2).into(),
1,386✔
941
                                        ],
1,386✔
942
                                    ],
1,386✔
943
                                    vec![
1,386✔
944
                                        vec![
1,386✔
945
                                            (1.1, 1.1).into(),
1,386✔
946
                                            (1.8, 1.1).into(),
1,386✔
947
                                            (1.8, 1.8).into(),
1,386✔
948
                                            (1.1, 1.1).into(),
1,386✔
949
                                        ],
1,386✔
950
                                        vec![
1,386✔
951
                                            (1.2, 1.2).into(),
1,386✔
952
                                            (1.9, 1.2).into(),
1,386✔
953
                                            (1.9, 1.9).into(),
1,386✔
954
                                            (1.2, 1.2).into(),
1,386✔
955
                                        ],
1,386✔
956
                                    ],
1,386✔
957
                                ])
1,386✔
958
                                .unwrap(),
1,386✔
959
                            );
1,386✔
960
                        }
1,386✔
961
                        1 => {
1,344✔
962
                            push_geometry(
1,344✔
963
                                &mut builder,
1,344✔
964
                                &MultiPolygon::new(vec![
1,344✔
965
                                    vec![
1,344✔
966
                                        vec![
1,344✔
967
                                            (0.1, 0.1).into(),
1,344✔
968
                                            (0.8, 0.1).into(),
1,344✔
969
                                            (0.8, 0.8).into(),
1,344✔
970
                                            (0.1, 0.1).into(),
1,344✔
971
                                        ],
1,344✔
972
                                        vec![
1,344✔
973
                                            (0.2, 0.2).into(),
1,344✔
974
                                            (0.9, 0.2).into(),
1,344✔
975
                                            (0.9, 0.9).into(),
1,344✔
976
                                            (0.2, 0.2).into(),
1,344✔
977
                                        ],
1,344✔
978
                                    ],
1,344✔
979
                                    vec![vec![
1,344✔
980
                                        (1.1, 1.1).into(),
1,344✔
981
                                        (1.8, 1.1).into(),
1,344✔
982
                                        (1.8, 1.8).into(),
1,344✔
983
                                        (1.1, 1.1).into(),
1,344✔
984
                                    ]],
1,344✔
985
                                ])
1,344✔
986
                                .unwrap(),
1,344✔
987
                            );
1,344✔
988
                        }
1,344✔
989
                        2 => {
1,302✔
990
                            push_geometry(
1,302✔
991
                                &mut builder,
1,302✔
992
                                &MultiPolygon::new(vec![vec![
1,302✔
993
                                    vec![
1,302✔
994
                                        (0.0, 0.1).into(),
1,302✔
995
                                        (1.0, 1.1).into(),
1,302✔
996
                                        (1.0, 0.1).into(),
1,302✔
997
                                        (0.0, 0.1).into(),
1,302✔
998
                                    ],
1,302✔
999
                                    vec![
1,302✔
1000
                                        (3.0, 3.1).into(),
1,302✔
1001
                                        (4.0, 4.1).into(),
1,302✔
1002
                                        (4.0, 3.1).into(),
1,302✔
1003
                                        (3.0, 3.1).into(),
1,302✔
1004
                                    ],
1,302✔
1005
                                ]])
1,302✔
1006
                                .unwrap(),
1,302✔
1007
                            );
1,302✔
1008
                        }
1,302✔
1009
                        _ => unreachable!(),
×
1010
                    }
1011
                }
1012

1013
                assert_eq!(builder.len(), num_multi_polygons);
128✔
1014

1015
                let builder_byte_size = MultiPolygon::estimate_array_memory_size(&mut builder);
128✔
1016

128✔
1017
                let array = builder.finish_cloned();
128✔
1018

128✔
1019
                assert_eq!(
128✔
1020
                    builder_byte_size,
128✔
1021
                    array.get_array_memory_size(),
128✔
1022
                    "{num_multi_polygons}"
×
1023
                );
1024
            }
1025
        }
1026
    }
1✔
1027
}
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