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

geo-engine / geoengine / 12863921949

20 Jan 2025 08:29AM UTC coverage: 90.007% (-0.6%) from 90.64%
12863921949

Pull #1008

github

web-flow
Merge 3737c26f3 into de81b44f7
Pull Request #1008: user ctx in ge_test

3317 of 3419 new or added lines in 61 files covered. (97.02%)

813 existing lines in 20 files now uncovered.

127380 of 141522 relevant lines covered (90.01%)

56878.97 hits per line

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

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

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

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

30
type Ring = Vec<Coordinate2D>;
31
type Polygon = Vec<Ring>;
32

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

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

52
        Ok(Self::new_unchecked(polygons))
4,104✔
53
    }
4,104✔
54

55
    fn polygon_is_valid(polygon: &[Ring]) -> bool {
6,898✔
56
        for ring in polygon {
19,238✔
57
            if !Self::ring_is_valid(ring) {
12,340✔
58
                return false;
×
59
            }
12,340✔
60
        }
61
        true
6,898✔
62
    }
6,898✔
63

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

12,342✔
74
        true
12,342✔
75
    }
12,342✔
76

77
    pub(crate) fn new_unchecked(polygons: Vec<Polygon>) -> Self {
4,113✔
78
        Self { polygons }
4,113✔
79
    }
4,113✔
80
}
81

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

90
impl Geometry for MultiPolygon {
91
    const DATA_TYPE: VectorDataType = VectorDataType::MultiPolygon;
92

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

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

103
        false
×
104
    }
434✔
105
}
106

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

478✔
118
                let exterior = line_strings.remove(0);
478✔
119

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

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

2✔
131
        let mut polygons = Vec::with_capacity(geo_polygons.len());
2✔
132

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

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

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

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

147
            polygons.push(rings);
24✔
148
        }
149

150
        MultiPolygon::new_unchecked(polygons)
2✔
151
    }
2✔
152
}
153

154
impl TryFrom<TypedGeometry> for MultiPolygon {
155
    type Error = Error;
156

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

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

172
mod db_types {
173
    use super::*;
174

175
    #[derive(Debug)]
176
    pub struct PolygonRef<'p> {
177
        pub rings: &'p [Vec<Coordinate2D>],
178
    }
179

180
    #[derive(Debug)]
181
    pub struct PolygonOwned {
182
        pub rings: Vec<Vec<Coordinate2D>>,
183
    }
184

185
    impl ToSql for PolygonRef<'_> {
UNCOV
186
        fn to_sql(
×
UNCOV
187
            &self,
×
UNCOV
188
            ty: &postgres_types::Type,
×
UNCOV
189
            w: &mut bytes::BytesMut,
×
UNCOV
190
        ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
UNCOV
191
            let postgres_types::Kind::Domain(domain_type) = ty.kind() else {
×
192
                panic!("expected domain type");
×
193
            };
194

UNCOV
195
            let postgres_types::Kind::Array(member_type) = domain_type.kind() else {
×
196
                panic!("expected array type");
×
197
            };
198

UNCOV
199
            let dimension = postgres_protocol::types::ArrayDimension {
×
UNCOV
200
                len: self.rings.len() as i32,
×
UNCOV
201
                lower_bound: 1, // arrays are one-indexed
×
UNCOV
202
            };
×
UNCOV
203

×
UNCOV
204
            postgres_protocol::types::array_to_sql(
×
UNCOV
205
                Some(dimension),
×
UNCOV
206
                member_type.oid(),
×
UNCOV
207
                self.rings.iter(),
×
UNCOV
208
                |coordinates, w| {
×
UNCOV
209
                    postgres_protocol::types::path_to_sql(
×
UNCOV
210
                        true,
×
UNCOV
211
                        coordinates.iter().map(|p| (p.x, p.y)),
×
UNCOV
212
                        w,
×
UNCOV
213
                    )?;
×
214

UNCOV
215
                    Ok(postgres_protocol::IsNull::No)
×
UNCOV
216
                },
×
UNCOV
217
                w,
×
UNCOV
218
            )?;
×
219

UNCOV
220
            Ok(postgres_types::IsNull::No)
×
UNCOV
221
        }
×
222

UNCOV
223
        fn accepts(ty: &postgres_types::Type) -> bool {
×
UNCOV
224
            if ty.name() != "Polygon" {
×
225
                return false;
×
UNCOV
226
            }
×
227

UNCOV
228
            let postgres_types::Kind::Domain(inner_type) = ty.kind() else {
×
229
                return false;
×
230
            };
231

UNCOV
232
            let postgres_types::Kind::Array(inner_type) = inner_type.kind() else {
×
233
                return false;
×
234
            };
235

UNCOV
236
            matches!(inner_type, &postgres_types::Type::PATH)
×
UNCOV
237
        }
×
238

239
        postgres_types::to_sql_checked!();
240
    }
241

242
    impl<'a> FromSql<'a> for PolygonOwned {
UNCOV
243
        fn from_sql(
×
UNCOV
244
            _ty: &postgres_types::Type,
×
UNCOV
245
            raw: &'a [u8],
×
UNCOV
246
        ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
UNCOV
247
            let array = postgres_protocol::types::array_from_sql(raw)?;
×
UNCOV
248
            if array.dimensions().count()? > 1 {
×
249
                return Err("array contains too many dimensions".into());
×
UNCOV
250
            }
×
251

UNCOV
252
            let rings = array
×
UNCOV
253
                .values()
×
UNCOV
254
                .map(|raw| {
×
UNCOV
255
                    let Some(raw) = raw else {
×
256
                        return Err("array contains NULL values".into());
×
257
                    };
UNCOV
258
                    let path = postgres_protocol::types::path_from_sql(raw)?;
×
259

UNCOV
260
                    let coordinates = path
×
UNCOV
261
                        .points()
×
UNCOV
262
                        .map(|point| {
×
UNCOV
263
                            Ok(Coordinate2D {
×
UNCOV
264
                                x: point.x(),
×
UNCOV
265
                                y: point.y(),
×
UNCOV
266
                            })
×
UNCOV
267
                        })
×
UNCOV
268
                        .collect()?;
×
UNCOV
269
                    Ok(coordinates)
×
UNCOV
270
                })
×
UNCOV
271
                .collect()?;
×
272

UNCOV
273
            Ok(Self { rings })
×
UNCOV
274
        }
×
275

276
        fn accepts(ty: &postgres_types::Type) -> bool {
×
277
            if ty.name() != "Polygon" {
×
278
                return false;
×
279
            }
×
280

281
            let postgres_types::Kind::Domain(inner_type) = ty.kind() else {
×
282
                return false;
×
283
            };
284

285
            let postgres_types::Kind::Array(inner_type) = inner_type.kind() else {
×
286
                return false;
×
287
            };
288

289
            matches!(inner_type, &postgres_types::Type::PATH)
×
290
        }
×
291
    }
292

293
    impl ToSql for MultiPolygon {
UNCOV
294
        fn to_sql(
×
UNCOV
295
            &self,
×
UNCOV
296
            ty: &postgres_types::Type,
×
UNCOV
297
            w: &mut bytes::BytesMut,
×
UNCOV
298
        ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
UNCOV
299
            let postgres_types::Kind::Array(member_type) = ty.kind() else {
×
300
                panic!("expected array type");
×
301
            };
302

UNCOV
303
            let dimension = postgres_protocol::types::ArrayDimension {
×
UNCOV
304
                len: self.polygons.len() as i32,
×
UNCOV
305
                lower_bound: 1, // arrays are one-indexed
×
UNCOV
306
            };
×
UNCOV
307

×
UNCOV
308
            postgres_protocol::types::array_to_sql(
×
UNCOV
309
                Some(dimension),
×
UNCOV
310
                member_type.oid(),
×
UNCOV
311
                self.polygons.iter(),
×
UNCOV
312
                |rings, w| match <PolygonRef as ToSql>::to_sql(
×
UNCOV
313
                    &PolygonRef { rings },
×
UNCOV
314
                    member_type,
×
UNCOV
315
                    w,
×
UNCOV
316
                )? {
×
UNCOV
317
                    postgres_types::IsNull::No => Ok(postgres_protocol::IsNull::No),
×
318
                    postgres_types::IsNull::Yes => Ok(postgres_protocol::IsNull::Yes),
×
UNCOV
319
                },
×
UNCOV
320
                w,
×
UNCOV
321
            )?;
×
322

UNCOV
323
            Ok(postgres_types::IsNull::No)
×
UNCOV
324
        }
×
325

UNCOV
326
        fn accepts(ty: &postgres_types::Type) -> bool {
×
UNCOV
327
            let postgres_types::Kind::Array(inner_type) = ty.kind() else {
×
328
                return false;
×
329
            };
330

UNCOV
331
            <PolygonRef as ToSql>::accepts(inner_type)
×
UNCOV
332
        }
×
333

334
        postgres_types::to_sql_checked!();
335
    }
336

337
    impl<'a> FromSql<'a> for MultiPolygon {
UNCOV
338
        fn from_sql(
×
UNCOV
339
            ty: &postgres_types::Type,
×
UNCOV
340
            raw: &'a [u8],
×
UNCOV
341
        ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
UNCOV
342
            let postgres_types::Kind::Array(inner_type) = ty.kind() else {
×
343
                return Err("inner type is not of type array".into());
×
344
            };
345

UNCOV
346
            let array = postgres_protocol::types::array_from_sql(raw)?;
×
UNCOV
347
            if array.dimensions().count()? > 1 {
×
348
                return Err("array contains too many dimensions".into());
×
UNCOV
349
            }
×
350

UNCOV
351
            let polygons = array
×
UNCOV
352
                .values()
×
UNCOV
353
                .map(|raw| {
×
UNCOV
354
                    let Some(raw) = raw else {
×
355
                        return Err("array contains NULL values".into());
×
356
                    };
UNCOV
357
                    let polygon = <PolygonOwned as FromSql>::from_sql(inner_type, raw)?;
×
UNCOV
358
                    Ok(polygon.rings)
×
UNCOV
359
                })
×
UNCOV
360
                .collect()?;
×
361

UNCOV
362
            Ok(Self { polygons })
×
UNCOV
363
        }
×
364

365
        fn accepts(ty: &postgres_types::Type) -> bool {
145✔
366
            let postgres_types::Kind::Array(inner_type) = ty.kind() else {
145✔
367
                return false;
×
368
            };
369

370
            inner_type.name() == "Polygon"
145✔
371
        }
145✔
372
    }
373
}
374

375
impl ArrowTyped for MultiPolygon {
376
    type ArrowArray = arrow::array::ListArray;
377
    type ArrowBuilder = arrow::array::ListBuilder<
378
        arrow::array::ListBuilder<
379
            arrow::array::ListBuilder<<Coordinate2D as ArrowTyped>::ArrowBuilder>,
380
        >,
381
    >;
382

383
    fn arrow_data_type() -> DataType {
65✔
384
        MultiLineString::arrow_list_data_type()
65✔
385
    }
65✔
386

387
    fn estimate_array_memory_size(builder: &mut Self::ArrowBuilder) -> usize {
562✔
388
        let static_size = std::mem::size_of::<Self::ArrowArray>()
562✔
389
            + std::mem::size_of::<<MultiLineString as ArrowTyped>::ArrowArray>()
562✔
390
            + std::mem::size_of::<<MultiPoint as ArrowTyped>::ArrowArray>();
562✔
391

562✔
392
        let feature_offset_bytes_size = std::mem::size_of_val(builder.offsets_slice());
562✔
393

562✔
394
        let polygon_builder = builder.values();
562✔
395

562✔
396
        let polygon_offset_bytes_size = std::mem::size_of_val(polygon_builder.offsets_slice());
562✔
397

562✔
398
        let ring_builder = polygon_builder.values();
562✔
399

562✔
400
        let ring_offset_bytes_size = std::mem::size_of_val(ring_builder.offsets_slice());
562✔
401

562✔
402
        let coordinates_builder = ring_builder.values();
562✔
403

562✔
404
        let coords_size = Coordinate2D::estimate_array_memory_size(coordinates_builder);
562✔
405

562✔
406
        static_size
562✔
407
            + coords_size
562✔
408
            + padded_buffer_size(ring_offset_bytes_size, 64)
562✔
409
            + padded_buffer_size(polygon_offset_bytes_size, 64)
562✔
410
            + padded_buffer_size(feature_offset_bytes_size, 64)
562✔
411
    }
562✔
412

413
    fn arrow_builder(_capacity: usize) -> Self::ArrowBuilder {
199✔
414
        let coordinate_builder = Coordinate2D::arrow_builder(0);
199✔
415
        let ring_builder = arrow::array::ListBuilder::new(coordinate_builder);
199✔
416
        let polygon_builder = arrow::array::ListBuilder::new(ring_builder);
199✔
417
        arrow::array::ListBuilder::new(polygon_builder)
199✔
418
    }
199✔
419

420
    fn concat(a: &Self::ArrowArray, b: &Self::ArrowArray) -> Result<Self::ArrowArray, ArrowError> {
3✔
421
        use arrow::array::{Array, FixedSizeListArray, Float64Array, ListArray};
422

423
        let mut multi_polygon_builder = Self::arrow_builder(a.len() + b.len());
3✔
424

425
        for multi_polygons in &[a, b] {
6✔
426
            for multi_polygon_index in 0..multi_polygons.len() {
6✔
427
                let polygon_builder = multi_polygon_builder.values();
6✔
428

6✔
429
                let polygons_ref = multi_polygons.value(multi_polygon_index);
6✔
430
                let polygons = downcast_array::<ListArray>(&polygons_ref);
6✔
431

432
                for polygon_index in 0..polygons.len() {
6✔
433
                    let ring_builder = polygon_builder.values();
6✔
434

6✔
435
                    let rings_ref = polygons.value(polygon_index);
6✔
436
                    let rings = downcast_array::<ListArray>(&rings_ref);
6✔
437

438
                    for ring_index in 0..rings.len() {
7✔
439
                        let coordinate_builder = ring_builder.values();
7✔
440

7✔
441
                        let coordinates_ref = rings.value(ring_index);
7✔
442
                        let coordinates = downcast_array::<FixedSizeListArray>(&coordinates_ref);
7✔
443

444
                        for coordinate_index in 0..coordinates.len() {
30✔
445
                            let floats_ref = coordinates.value(coordinate_index);
30✔
446
                            let floats: &Float64Array = downcast_array(&floats_ref);
30✔
447

30✔
448
                            coordinate_builder.values().append_slice(floats.values());
30✔
449

30✔
450
                            coordinate_builder.append(true);
30✔
451
                        }
30✔
452

453
                        ring_builder.append(true);
7✔
454
                    }
455

456
                    polygon_builder.append(true);
6✔
457
                }
458

459
                multi_polygon_builder.append(true);
6✔
460
            }
461
        }
462

463
        Ok(multi_polygon_builder.finish_cloned())
3✔
464
    }
3✔
465

466
    fn filter(
24✔
467
        multi_polygons: &Self::ArrowArray,
24✔
468
        filter_array: &BooleanArray,
24✔
469
    ) -> Result<Self::ArrowArray, ArrowError> {
24✔
470
        use arrow::array::{Array, FixedSizeListArray, Float64Array, ListArray};
471

472
        let mut multi_polygon_builder = Self::arrow_builder(0);
24✔
473

474
        for multi_polygon_index in 0..multi_polygons.len() {
34✔
475
            if !filter_array.value(multi_polygon_index) {
34✔
476
                continue;
3✔
477
            }
31✔
478

31✔
479
            let polygon_builder = multi_polygon_builder.values();
31✔
480

31✔
481
            let polygons_ref = multi_polygons.value(multi_polygon_index);
31✔
482
            let polygons = downcast_array::<ListArray>(&polygons_ref);
31✔
483

484
            for polygon_index in 0..polygons.len() {
32✔
485
                let ring_builder = polygon_builder.values();
32✔
486

32✔
487
                let rings_ref = polygons.value(polygon_index);
32✔
488
                let rings = downcast_array::<ListArray>(&rings_ref);
32✔
489

490
                for ring_index in 0..rings.len() {
33✔
491
                    let coordinate_builder = ring_builder.values();
33✔
492

33✔
493
                    let coordinates_ref = rings.value(ring_index);
33✔
494
                    let coordinates = downcast_array::<FixedSizeListArray>(&coordinates_ref);
33✔
495

496
                    for coordinate_index in 0..coordinates.len() {
151✔
497
                        let floats_ref = coordinates.value(coordinate_index);
151✔
498
                        let floats: &Float64Array = downcast_array(&floats_ref);
151✔
499

151✔
500
                        coordinate_builder.values().append_slice(floats.values());
151✔
501

151✔
502
                        coordinate_builder.append(true);
151✔
503
                    }
151✔
504

505
                    ring_builder.append(true);
33✔
506
                }
507

508
                polygon_builder.append(true);
32✔
509
            }
510

511
            multi_polygon_builder.append(true);
31✔
512
        }
513

514
        Ok(multi_polygon_builder.finish_cloned())
24✔
515
    }
24✔
516

517
    fn from_vec(multi_polygons: Vec<Self>) -> Result<Self::ArrowArray, ArrowError>
32✔
518
    where
32✔
519
        Self: Sized,
32✔
520
    {
32✔
521
        let mut builder = Self::arrow_builder(multi_polygons.len());
32✔
522
        for multi_polygon in multi_polygons {
74✔
523
            let polygon_builder = builder.values();
42✔
524

525
            for polygon in multi_polygon.as_ref() {
71✔
526
                let ring_builder = polygon_builder.values();
71✔
527

528
                for ring in polygon {
146✔
529
                    let coordinate_builder = ring_builder.values();
75✔
530

531
                    for coordinate in ring {
407✔
532
                        let float_builder = coordinate_builder.values();
332✔
533
                        float_builder.append_value(coordinate.x);
332✔
534
                        float_builder.append_value(coordinate.y);
332✔
535
                        coordinate_builder.append(true);
332✔
536
                    }
332✔
537

538
                    ring_builder.append(true);
75✔
539
                }
540

541
                polygon_builder.append(true);
71✔
542
            }
543

544
            builder.append(true);
42✔
545
        }
546

547
        Ok(builder.finish_cloned())
32✔
548
    }
32✔
549
}
550

551
type RingRef<'g> = &'g [Coordinate2D];
552
type PolygonRef<'g> = Vec<RingRef<'g>>;
553

554
#[derive(Debug, PartialEq)]
555
pub struct MultiPolygonRef<'g> {
556
    polygons: Vec<PolygonRef<'g>>,
557
}
558

559
impl GeometryRef for MultiPolygonRef<'_> {
560
    type GeometryType = MultiPolygon;
561

562
    fn as_geometry(&self) -> Self::GeometryType {
×
563
        MultiPolygon::from(self)
×
564
    }
×
565

566
    fn bbox(&self) -> Option<BoundingBox2D> {
×
567
        self.bbox()
×
568
    }
×
569
}
570

571
impl<'g> MultiPolygonRef<'g> {
572
    pub fn new(polygons: Vec<PolygonRef<'g>>) -> Result<Self> {
1✔
573
        ensure!(!polygons.is_empty(), error::UnallowedEmpty);
1✔
574
        ensure!(
1✔
575
            polygons
1✔
576
                .iter()
1✔
577
                .all(|polygon| Self::polygon_is_valid(polygon)),
1✔
578
            error::UnclosedPolygonRing
×
579
        );
580

581
        Ok(Self::new_unchecked(polygons))
1✔
582
    }
1✔
583

584
    fn polygon_is_valid(polygon: &[RingRef<'g>]) -> bool {
1✔
585
        for ring in polygon {
3✔
586
            if !MultiPolygon::ring_is_valid(ring) {
2✔
587
                return false;
×
588
            }
2✔
589
        }
590
        true
1✔
591
    }
1✔
592

593
    pub(crate) fn new_unchecked(polygons: Vec<PolygonRef<'g>>) -> Self {
58✔
594
        Self { polygons }
58✔
595
    }
58✔
596

597
    pub fn bbox(&self) -> Option<BoundingBox2D> {
×
598
        self.polygons().iter().fold(None, |bbox, rings| {
×
599
            let lbox = BoundingBox2D::from_coord_ref_iter(rings[0].iter()); // we only need to look at the outer ring coords
×
600
            match (bbox, lbox) {
×
601
                (None, Some(lbox)) => Some(lbox),
×
602
                (Some(bbox), Some(lbox)) => Some(bbox.union(&lbox)),
×
603
                (bbox, None) => bbox,
×
604
            }
605
        })
×
606
    }
×
607
}
608

609
impl<'g> MultiPolygonAccess for MultiPolygonRef<'g> {
610
    type R = PolygonRef<'g>;
611
    type L = RingRef<'g>;
612

613
    fn polygons(&self) -> &[Self::R] {
49✔
614
        &self.polygons
49✔
615
    }
49✔
616
}
617

618
impl ToWkt<f64> for MultiPolygonRef<'_> {
619
    fn to_wkt(&self) -> Wkt<f64> {
5✔
620
        let multi_polygon = self.polygons();
5✔
621
        let mut wkt_multi_polygon =
5✔
622
            wkt::types::MultiPolygon(Vec::with_capacity(multi_polygon.len()));
5✔
623

624
        for polygon in multi_polygon {
13✔
625
            let mut wkt_polygon = wkt::types::Polygon(Vec::with_capacity(polygon.len()));
8✔
626

627
            for ring in polygon {
18✔
628
                let mut wkt_line_string = wkt::types::LineString(Vec::with_capacity(ring.len()));
10✔
629

630
                for coord in *ring {
50✔
631
                    wkt_line_string.0.push(coord.into());
40✔
632
                }
40✔
633

634
                wkt_polygon.0.push(wkt_line_string);
10✔
635
            }
636

637
            wkt_multi_polygon.0.push(wkt_polygon);
8✔
638
        }
639

640
        Wkt::MultiPolygon(wkt_multi_polygon)
5✔
641
    }
5✔
642
}
643

644
impl<'g> From<MultiPolygonRef<'g>> for geojson::Geometry {
645
    fn from(geometry: MultiPolygonRef<'g>) -> geojson::Geometry {
2✔
646
        geojson::Geometry::new(match geometry.polygons.len() {
2✔
647
            1 => {
648
                let polygon = &geometry.polygons[0];
1✔
649
                geojson::Value::Polygon(
1✔
650
                    polygon
1✔
651
                        .iter()
1✔
652
                        .map(|coordinates| coordinates.iter().map(|c| vec![c.x, c.y]).collect())
4✔
653
                        .collect(),
1✔
654
                )
1✔
655
            }
656
            _ => geojson::Value::MultiPolygon(
1✔
657
                geometry
1✔
658
                    .polygons
1✔
659
                    .iter()
1✔
660
                    .map(|polygon| {
2✔
661
                        polygon
2✔
662
                            .iter()
2✔
663
                            .map(|coordinates| coordinates.iter().map(|c| vec![c.x, c.y]).collect())
12✔
664
                            .collect()
2✔
665
                    })
2✔
666
                    .collect(),
1✔
667
            ),
1✔
668
        })
669
    }
2✔
670
}
671

672
impl<'g> From<MultiPolygonRef<'g>> for MultiPolygon {
673
    fn from(multi_point_ref: MultiPolygonRef<'g>) -> Self {
7✔
674
        MultiPolygon::from(&multi_point_ref)
7✔
675
    }
7✔
676
}
677

678
impl<'g> From<&MultiPolygonRef<'g>> for MultiPolygon {
679
    fn from(multi_point_ref: &MultiPolygonRef<'g>) -> Self {
7✔
680
        MultiPolygon::new_unchecked(
7✔
681
            multi_point_ref
7✔
682
                .polygons
7✔
683
                .iter()
7✔
684
                .map(|polygon| polygon.iter().copied().map(ToOwned::to_owned).collect())
10✔
685
                .collect(),
7✔
686
        )
7✔
687
    }
7✔
688
}
689

690
impl<'g> From<&'g MultiPolygon> for MultiPolygonRef<'g> {
691
    fn from(multi_point_ref: &'g MultiPolygon) -> Self {
1✔
692
        let mut polygons = Vec::with_capacity(multi_point_ref.polygons().len());
1✔
693

694
        for polygon in multi_point_ref.polygons() {
2✔
695
            let mut rings = Vec::with_capacity(polygon.len());
2✔
696

697
            for ring in polygon {
6✔
698
                rings.push(ring.as_ref());
4✔
699
            }
4✔
700

701
            polygons.push(rings);
2✔
702
        }
703

704
        MultiPolygonRef::new_unchecked(polygons)
1✔
705
    }
1✔
706
}
707

708
impl<'g> From<&MultiPolygonRef<'g>> for geo::MultiPolygon<f64> {
709
    fn from(geometry: &MultiPolygonRef<'g>) -> Self {
5✔
710
        let polygons: Vec<geo::Polygon<f64>> = geometry
5✔
711
            .polygons()
5✔
712
            .iter()
5✔
713
            .map(|polygon| {
26✔
714
                let mut line_strings: Vec<geo::LineString<f64>> = polygon
26✔
715
                    .iter()
26✔
716
                    .map(|ring| geo::LineString(ring.iter().map(Into::into).collect()))
26✔
717
                    .collect();
26✔
718

26✔
719
                let exterior = line_strings.remove(0);
26✔
720

26✔
721
                geo::Polygon::new(exterior, line_strings)
26✔
722
            })
26✔
723
            .collect();
5✔
724
        geo::MultiPolygon(polygons)
5✔
725
    }
5✔
726
}
727

728
impl ApproxEq for &MultiPolygon {
729
    type Margin = F64Margin;
730

731
    fn approx_eq<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool {
6✔
732
        let m = margin.into();
6✔
733
        self.polygons().len() == other.polygons().len()
6✔
734
            && self
6✔
735
                .polygons()
6✔
736
                .iter()
6✔
737
                .zip(other.polygons())
6✔
738
                .all(|(polygon_a, polygon_b)| {
10✔
739
                    polygon_a.len() == polygon_b.len()
10✔
740
                        && polygon_a.iter().zip(polygon_b).all(|(ring_a, ring_b)| {
14✔
741
                            ring_a.len() == ring_b.len()
14✔
742
                                && ring_a.iter().zip(ring_b).all(
13✔
743
                                    |(&coordinate_a, &coordinate_b)| {
52✔
744
                                        coordinate_a.approx_eq(coordinate_b, m)
52✔
745
                                    },
52✔
746
                                )
13✔
747
                        })
14✔
748
                })
10✔
749
    }
6✔
750
}
751

752
#[cfg(test)]
753
mod tests {
754
    use super::*;
755
    use arrow::array::{Array, ArrayBuilder};
756
    use float_cmp::approx_eq;
757

758
    #[test]
759
    fn access() {
1✔
760
        fn aggregate<T: MultiPolygonAccess>(multi_line_string: &T) -> (usize, usize, usize) {
3✔
761
            let number_of_polygons = multi_line_string.polygons().len();
3✔
762
            let number_of_rings = multi_line_string
3✔
763
                .polygons()
3✔
764
                .iter()
3✔
765
                .map(AsRef::as_ref)
3✔
766
                .map(<[_]>::len)
3✔
767
                .sum();
3✔
768
            let number_of_coordinates = multi_line_string
3✔
769
                .polygons()
3✔
770
                .iter()
3✔
771
                .map(AsRef::as_ref)
3✔
772
                .flat_map(<[_]>::iter)
3✔
773
                .map(AsRef::as_ref)
3✔
774
                .map(<[Coordinate2D]>::len)
3✔
775
                .sum();
3✔
776

3✔
777
            (number_of_polygons, number_of_rings, number_of_coordinates)
3✔
778
        }
3✔
779

780
        let coordinates = vec![vec![
1✔
781
            vec![
1✔
782
                (0.0, 0.1).into(),
1✔
783
                (1.0, 1.1).into(),
1✔
784
                (1.0, 0.1).into(),
1✔
785
                (0.0, 0.1).into(),
1✔
786
            ],
1✔
787
            vec![
1✔
788
                (3.0, 3.1).into(),
1✔
789
                (4.0, 4.1).into(),
1✔
790
                (4.0, 3.1).into(),
1✔
791
                (3.0, 3.1).into(),
1✔
792
            ],
1✔
793
        ]];
1✔
794
        let multi_polygon = MultiPolygon::new(coordinates.clone()).unwrap();
1✔
795
        let multi_polygon_ref = MultiPolygonRef::new(
1✔
796
            coordinates
1✔
797
                .iter()
1✔
798
                .map(|r| r.iter().map(AsRef::as_ref).collect())
1✔
799
                .collect(),
1✔
800
        )
1✔
801
        .unwrap();
1✔
802

1✔
803
        assert_eq!(aggregate(&multi_polygon), (1, 2, 8));
1✔
804
        assert_eq!(aggregate(&multi_polygon), aggregate(&multi_polygon_ref));
1✔
805
    }
1✔
806

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

1✔
841
        let b = MultiPolygon::new(vec![
1✔
842
            vec![
1✔
843
                vec![
1✔
844
                    (0.1, 0.099_999_999).into(),
1✔
845
                    (0.8, 0.1).into(),
1✔
846
                    (0.8, 0.8).into(),
1✔
847
                    (0.1, 0.099_999_999).into(),
1✔
848
                ],
1✔
849
                vec![
1✔
850
                    (0.2, 0.2).into(),
1✔
851
                    (0.9, 0.2).into(),
1✔
852
                    (0.9, 0.9).into(),
1✔
853
                    (0.2, 0.2).into(),
1✔
854
                ],
1✔
855
            ],
1✔
856
            vec![
1✔
857
                vec![
1✔
858
                    (1.1, 1.1).into(),
1✔
859
                    (1.8, 1.1).into(),
1✔
860
                    (1.8, 1.8).into(),
1✔
861
                    (1.1, 1.1).into(),
1✔
862
                ],
1✔
863
                vec![
1✔
864
                    (1.2, 1.2).into(),
1✔
865
                    (1.9, 1.2).into(),
1✔
866
                    (1.9, 1.9).into(),
1✔
867
                    (1.2, 1.2).into(),
1✔
868
                ],
1✔
869
            ],
1✔
870
        ])
1✔
871
        .unwrap();
1✔
872

1✔
873
        assert!(approx_eq!(&MultiPolygon, &a, &b, epsilon = 0.000_001));
1✔
874
    }
1✔
875

876
    #[test]
877
    fn not_approx_equal_ring_len() {
1✔
878
        let a = MultiPolygon::new(vec![
1✔
879
            vec![
1✔
880
                vec![
1✔
881
                    (0.1, 0.1).into(),
1✔
882
                    (0.8, 0.1).into(),
1✔
883
                    (0.8, 0.8).into(),
1✔
884
                    (0.1, 0.1).into(),
1✔
885
                ],
1✔
886
                vec![
1✔
887
                    (0.2, 0.2).into(),
1✔
888
                    (0.9, 0.2).into(),
1✔
889
                    (0.9, 0.9).into(),
1✔
890
                    (0.2, 0.2).into(),
1✔
891
                ],
1✔
892
            ],
1✔
893
            vec![vec![
1✔
894
                (1.1, 1.1).into(),
1✔
895
                (1.8, 1.1).into(),
1✔
896
                (1.8, 1.8).into(),
1✔
897
                (1.1, 1.1).into(),
1✔
898
            ]],
1✔
899
        ])
1✔
900
        .unwrap();
1✔
901

1✔
902
        let b = MultiPolygon::new(vec![
1✔
903
            vec![
1✔
904
                vec![
1✔
905
                    (0.1, 0.1).into(),
1✔
906
                    (0.8, 0.1).into(),
1✔
907
                    (0.8, 0.8).into(),
1✔
908
                    (0.1, 0.1).into(),
1✔
909
                ],
1✔
910
                vec![
1✔
911
                    (0.2, 0.2).into(),
1✔
912
                    (0.9, 0.2).into(),
1✔
913
                    (0.9, 0.9).into(),
1✔
914
                    (0.2, 0.2).into(),
1✔
915
                ],
1✔
916
            ],
1✔
917
            vec![
1✔
918
                vec![
1✔
919
                    (1.1, 1.1).into(),
1✔
920
                    (1.8, 1.1).into(),
1✔
921
                    (1.8, 1.8).into(),
1✔
922
                    (1.1, 1.1).into(),
1✔
923
                ],
1✔
924
                vec![
1✔
925
                    (1.2, 1.2).into(),
1✔
926
                    (1.9, 1.2).into(),
1✔
927
                    (1.9, 1.9).into(),
1✔
928
                    (1.2, 1.2).into(),
1✔
929
                ],
1✔
930
            ],
1✔
931
        ])
1✔
932
        .unwrap();
1✔
933

1✔
934
        assert!(!approx_eq!(&MultiPolygon, &a, &b, F64Margin::default()));
1✔
935
    }
1✔
936

937
    #[test]
938
    fn not_approx_equal_inner_len() {
1✔
939
        let a = MultiPolygon::new(vec![
1✔
940
            vec![
1✔
941
                vec![
1✔
942
                    (0.1, 0.1).into(),
1✔
943
                    (0.8, 0.1).into(),
1✔
944
                    (0.8, 0.8).into(),
1✔
945
                    (0.1, 0.1).into(),
1✔
946
                ],
1✔
947
                vec![
1✔
948
                    (0.2, 0.2).into(),
1✔
949
                    (0.9, 0.2).into(),
1✔
950
                    (0.9, 0.9).into(),
1✔
951
                    (0.2, 0.2).into(),
1✔
952
                ],
1✔
953
            ],
1✔
954
            vec![
1✔
955
                vec![
1✔
956
                    (1.1, 1.1).into(),
1✔
957
                    (1.8, 1.1).into(),
1✔
958
                    (1.8, 1.8).into(),
1✔
959
                    (1.1, 1.1).into(),
1✔
960
                ],
1✔
961
                vec![
1✔
962
                    (1.2, 1.2).into(),
1✔
963
                    (1.9, 1.2).into(),
1✔
964
                    (1.9, 1.9).into(),
1✔
965
                    (1.2, 1.2).into(),
1✔
966
                ],
1✔
967
            ],
1✔
968
        ])
1✔
969
        .unwrap();
1✔
970

1✔
971
        let b = MultiPolygon::new(vec![
1✔
972
            vec![
1✔
973
                vec![
1✔
974
                    (0.1, 0.1).into(),
1✔
975
                    (0.8, 0.1).into(),
1✔
976
                    (0.8, 0.8).into(),
1✔
977
                    (0.1, 0.1).into(),
1✔
978
                ],
1✔
979
                vec![
1✔
980
                    (0.2, 0.2).into(),
1✔
981
                    (0.9, 0.2).into(),
1✔
982
                    (0.9, 0.9).into(),
1✔
983
                    (0.2, 0.2).into(),
1✔
984
                ],
1✔
985
            ],
1✔
986
            vec![
1✔
987
                vec![
1✔
988
                    (1.1, 1.1).into(),
1✔
989
                    (1.8, 1.1).into(),
1✔
990
                    (1.8, 1.8).into(),
1✔
991
                    (1.1, 1.1).into(),
1✔
992
                ],
1✔
993
                vec![
1✔
994
                    (1.2, 1.2).into(),
1✔
995
                    (1.7, 1.2).into(),
1✔
996
                    (1.9, 1.2).into(),
1✔
997
                    (1.9, 1.9).into(),
1✔
998
                    (1.2, 1.2).into(),
1✔
999
                ],
1✔
1000
            ],
1✔
1001
        ])
1✔
1002
        .unwrap();
1✔
1003

1✔
1004
        assert!(!approx_eq!(&MultiPolygon, &a, &b, F64Margin::default()));
1✔
1005
    }
1✔
1006

1007
    #[test]
1008
    fn test_to_wkt() {
1✔
1009
        let a = MultiPolygon::new(vec![
1✔
1010
            vec![
1✔
1011
                vec![
1✔
1012
                    (0.1, 0.1).into(),
1✔
1013
                    (0.8, 0.1).into(),
1✔
1014
                    (0.8, 0.8).into(),
1✔
1015
                    (0.1, 0.1).into(),
1✔
1016
                ],
1✔
1017
                vec![
1✔
1018
                    (0.2, 0.2).into(),
1✔
1019
                    (0.9, 0.2).into(),
1✔
1020
                    (0.9, 0.9).into(),
1✔
1021
                    (0.2, 0.2).into(),
1✔
1022
                ],
1✔
1023
            ],
1✔
1024
            vec![
1✔
1025
                vec![
1✔
1026
                    (1.1, 1.1).into(),
1✔
1027
                    (1.8, 1.1).into(),
1✔
1028
                    (1.8, 1.8).into(),
1✔
1029
                    (1.1, 1.1).into(),
1✔
1030
                ],
1✔
1031
                vec![
1✔
1032
                    (1.2, 1.2).into(),
1✔
1033
                    (1.9, 1.2).into(),
1✔
1034
                    (1.9, 1.9).into(),
1✔
1035
                    (1.2, 1.2).into(),
1✔
1036
                ],
1✔
1037
            ],
1✔
1038
        ])
1✔
1039
        .unwrap();
1✔
1040

1✔
1041
        let a_ref = MultiPolygonRef::from(&a);
1✔
1042

1✔
1043
        assert_eq!(
1✔
1044
            a_ref.wkt_string(),
1✔
1045
            "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✔
1046
        );
1✔
1047
    }
1✔
1048

1049
    #[test]
1050
    fn test_to_geo_and_back() {
1✔
1051
        let polygon = MultiPolygon::new(vec![
1✔
1052
            vec![
1✔
1053
                vec![
1✔
1054
                    (0.1, 0.1).into(),
1✔
1055
                    (0.8, 0.1).into(),
1✔
1056
                    (0.8, 0.8).into(),
1✔
1057
                    (0.1, 0.1).into(),
1✔
1058
                ],
1✔
1059
                vec![
1✔
1060
                    (0.2, 0.2).into(),
1✔
1061
                    (0.9, 0.2).into(),
1✔
1062
                    (0.9, 0.9).into(),
1✔
1063
                    (0.2, 0.2).into(),
1✔
1064
                ],
1✔
1065
            ],
1✔
1066
            vec![
1✔
1067
                vec![
1✔
1068
                    (1.1, 1.1).into(),
1✔
1069
                    (1.8, 1.1).into(),
1✔
1070
                    (1.8, 1.8).into(),
1✔
1071
                    (1.1, 1.1).into(),
1✔
1072
                ],
1✔
1073
                vec![
1✔
1074
                    (1.2, 1.2).into(),
1✔
1075
                    (1.9, 1.2).into(),
1✔
1076
                    (1.9, 1.9).into(),
1✔
1077
                    (1.2, 1.2).into(),
1✔
1078
                ],
1✔
1079
            ],
1✔
1080
        ])
1✔
1081
        .unwrap();
1✔
1082

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

1✔
1085
        let polygon_back = MultiPolygon::from(geo_polygon);
1✔
1086

1✔
1087
        assert_eq!(polygon, polygon_back);
1✔
1088
    }
1✔
1089

1090
    #[test]
1091
    #[allow(clippy::too_many_lines)]
1092
    fn arrow_builder_size() {
1✔
1093
        fn push_geometry(
4,032✔
1094
            geometries_builder: &mut <MultiPolygon as ArrowTyped>::ArrowBuilder,
4,032✔
1095
            geometry: &MultiPolygon,
4,032✔
1096
        ) {
4,032✔
1097
            let polygon_builder = geometries_builder.values();
4,032✔
1098

1099
            for polygon in geometry.polygons() {
6,762✔
1100
                let ring_builder = polygon_builder.values();
6,762✔
1101

1102
                for ring in polygon {
18,942✔
1103
                    let coordinate_builder = ring_builder.values();
12,180✔
1104

1105
                    for coordinate in ring {
60,900✔
1106
                        coordinate_builder
48,720✔
1107
                            .values()
48,720✔
1108
                            .append_slice(coordinate.as_ref());
48,720✔
1109

48,720✔
1110
                        coordinate_builder.append(true);
48,720✔
1111
                    }
48,720✔
1112

1113
                    ring_builder.append(true);
12,180✔
1114
                }
1115

1116
                polygon_builder.append(true);
6,762✔
1117
            }
1118

1119
            geometries_builder.append(true);
4,032✔
1120
        }
4,032✔
1121

1122
        for num_multi_polygons in 0..64 {
65✔
1123
            for capacity in [0, num_multi_polygons] {
128✔
1124
                let mut builder = MultiPolygon::arrow_builder(capacity);
128✔
1125

1126
                for i in 0..num_multi_polygons {
4,032✔
1127
                    match i % 3 {
4,032✔
1128
                        0 => {
1,386✔
1129
                            push_geometry(
1,386✔
1130
                                &mut builder,
1,386✔
1131
                                &MultiPolygon::new(vec![
1,386✔
1132
                                    vec![
1,386✔
1133
                                        vec![
1,386✔
1134
                                            (0.1, 0.1).into(),
1,386✔
1135
                                            (0.8, 0.1).into(),
1,386✔
1136
                                            (0.8, 0.8).into(),
1,386✔
1137
                                            (0.1, 0.1).into(),
1,386✔
1138
                                        ],
1,386✔
1139
                                        vec![
1,386✔
1140
                                            (0.2, 0.2).into(),
1,386✔
1141
                                            (0.9, 0.2).into(),
1,386✔
1142
                                            (0.9, 0.9).into(),
1,386✔
1143
                                            (0.2, 0.2).into(),
1,386✔
1144
                                        ],
1,386✔
1145
                                    ],
1,386✔
1146
                                    vec![
1,386✔
1147
                                        vec![
1,386✔
1148
                                            (1.1, 1.1).into(),
1,386✔
1149
                                            (1.8, 1.1).into(),
1,386✔
1150
                                            (1.8, 1.8).into(),
1,386✔
1151
                                            (1.1, 1.1).into(),
1,386✔
1152
                                        ],
1,386✔
1153
                                        vec![
1,386✔
1154
                                            (1.2, 1.2).into(),
1,386✔
1155
                                            (1.9, 1.2).into(),
1,386✔
1156
                                            (1.9, 1.9).into(),
1,386✔
1157
                                            (1.2, 1.2).into(),
1,386✔
1158
                                        ],
1,386✔
1159
                                    ],
1,386✔
1160
                                ])
1,386✔
1161
                                .unwrap(),
1,386✔
1162
                            );
1,386✔
1163
                        }
1,386✔
1164
                        1 => {
1,344✔
1165
                            push_geometry(
1,344✔
1166
                                &mut builder,
1,344✔
1167
                                &MultiPolygon::new(vec![
1,344✔
1168
                                    vec![
1,344✔
1169
                                        vec![
1,344✔
1170
                                            (0.1, 0.1).into(),
1,344✔
1171
                                            (0.8, 0.1).into(),
1,344✔
1172
                                            (0.8, 0.8).into(),
1,344✔
1173
                                            (0.1, 0.1).into(),
1,344✔
1174
                                        ],
1,344✔
1175
                                        vec![
1,344✔
1176
                                            (0.2, 0.2).into(),
1,344✔
1177
                                            (0.9, 0.2).into(),
1,344✔
1178
                                            (0.9, 0.9).into(),
1,344✔
1179
                                            (0.2, 0.2).into(),
1,344✔
1180
                                        ],
1,344✔
1181
                                    ],
1,344✔
1182
                                    vec![vec![
1,344✔
1183
                                        (1.1, 1.1).into(),
1,344✔
1184
                                        (1.8, 1.1).into(),
1,344✔
1185
                                        (1.8, 1.8).into(),
1,344✔
1186
                                        (1.1, 1.1).into(),
1,344✔
1187
                                    ]],
1,344✔
1188
                                ])
1,344✔
1189
                                .unwrap(),
1,344✔
1190
                            );
1,344✔
1191
                        }
1,344✔
1192
                        2 => {
1,302✔
1193
                            push_geometry(
1,302✔
1194
                                &mut builder,
1,302✔
1195
                                &MultiPolygon::new(vec![vec![
1,302✔
1196
                                    vec![
1,302✔
1197
                                        (0.0, 0.1).into(),
1,302✔
1198
                                        (1.0, 1.1).into(),
1,302✔
1199
                                        (1.0, 0.1).into(),
1,302✔
1200
                                        (0.0, 0.1).into(),
1,302✔
1201
                                    ],
1,302✔
1202
                                    vec![
1,302✔
1203
                                        (3.0, 3.1).into(),
1,302✔
1204
                                        (4.0, 4.1).into(),
1,302✔
1205
                                        (4.0, 3.1).into(),
1,302✔
1206
                                        (3.0, 3.1).into(),
1,302✔
1207
                                    ],
1,302✔
1208
                                ]])
1,302✔
1209
                                .unwrap(),
1,302✔
1210
                            );
1,302✔
1211
                        }
1,302✔
1212
                        _ => unreachable!(),
×
1213
                    }
1214
                }
1215

1216
                assert_eq!(builder.len(), num_multi_polygons);
128✔
1217

1218
                let builder_byte_size = MultiPolygon::estimate_array_memory_size(&mut builder);
128✔
1219

128✔
1220
                let array = builder.finish_cloned();
128✔
1221

128✔
1222
                assert_eq!(
128✔
1223
                    builder_byte_size,
128✔
1224
                    array.get_array_memory_size(),
128✔
1225
                    "{num_multi_polygons}"
×
1226
                );
1227
            }
1228
        }
1229
    }
1✔
1230
}
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