• 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.48
/datatypes/src/primitives/multi_point.rs
1
use std::convert::{TryFrom, TryInto};
2

3
use arrow::array::{BooleanArray, FixedSizeListArray, Float64Array};
4
use arrow::error::ArrowError;
5
use float_cmp::{ApproxEq, F64Margin};
6
use serde::{Deserialize, Serialize};
7
use snafu::ensure;
8
use wkt::{ToWkt, Wkt};
9

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

17
use super::SpatialBounded;
18

19
/// A trait that allows a common access to points of `MultiPoint`s and its references
20
pub trait MultiPointAccess {
21
    fn points(&self) -> &[Coordinate2D];
22
}
23

24
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
354✔
25
pub struct MultiPoint {
26
    coordinates: Vec<Coordinate2D>,
27
}
28

29
impl MultiPoint {
30
    pub fn new(coordinates: Vec<Coordinate2D>) -> Result<Self> {
8,954✔
31
        ensure!(!coordinates.is_empty(), error::UnallowedEmpty);
8,954✔
32

33
        Ok(Self::new_unchecked(coordinates))
8,954✔
34
    }
8,954✔
35

36
    pub(crate) fn new_unchecked(coordinates: Vec<Coordinate2D>) -> Self {
10,628✔
37
        Self { coordinates }
10,628✔
38
    }
10,628✔
39

40
    pub fn many<M, E>(raw_multi_points: Vec<M>) -> Result<Vec<Self>>
119✔
41
    where
119✔
42
        M: TryInto<MultiPoint, Error = E>,
119✔
43
        E: Into<crate::error::Error>,
119✔
44
    {
119✔
45
        raw_multi_points
119✔
46
            .into_iter()
119✔
47
            .map(|m| m.try_into().map_err(Into::into))
1,559✔
48
            .collect()
119✔
49
    }
119✔
50
}
51

52
impl MultiPointAccess for MultiPoint {
53
    fn points(&self) -> &[Coordinate2D] {
113✔
54
        &self.coordinates
113✔
55
    }
113✔
56
}
57

58
impl Geometry for MultiPoint {
59
    const DATA_TYPE: VectorDataType = VectorDataType::MultiPoint;
60

61
    fn intersects_bbox(&self, bbox: &BoundingBox2D) -> bool {
8,828✔
62
        self.coordinates.iter().any(|c| bbox.contains_coordinate(c))
8,829✔
63
    }
8,828✔
64
}
65

66
impl TryFrom<TypedGeometry> for MultiPoint {
67
    type Error = Error;
68

69
    fn try_from(value: TypedGeometry) -> Result<Self, Self::Error> {
70
        if let TypedGeometry::MultiPoint(geometry) = value {
4✔
71
            Ok(geometry)
4✔
72
        } else {
73
            Err(PrimitivesError::InvalidConversion.into())
×
74
        }
75
    }
4✔
76
}
77

78
impl AsRef<[Coordinate2D]> for MultiPoint {
79
    fn as_ref(&self) -> &[Coordinate2D] {
4,981✔
80
        &self.coordinates
4,981✔
81
    }
4,981✔
82
}
83

84
impl<C> From<C> for MultiPoint
85
where
86
    C: Into<Coordinate2D>,
87
{
88
    fn from(c: C) -> Self {
1,510✔
89
        Self::new_unchecked(vec![c.into()])
1,510✔
90
    }
1,510✔
91
}
92

93
impl From<&Coordinate2D> for MultiPoint {
94
    fn from(point: &Coordinate2D) -> Self {
58✔
95
        Self::new_unchecked(vec![*point])
58✔
96
    }
58✔
97
}
98

99
impl TryFrom<Vec<Coordinate2D>> for MultiPoint {
100
    type Error = crate::error::Error;
101

102
    fn try_from(coordinates: Vec<Coordinate2D>) -> Result<Self, Self::Error> {
6✔
103
        MultiPoint::new(coordinates)
6✔
104
    }
6✔
105
}
106

107
impl TryFrom<Vec<(f64, f64)>> for MultiPoint {
108
    type Error = crate::error::Error;
109

110
    fn try_from(coordinates: Vec<(f64, f64)>) -> Result<Self, Self::Error> {
81✔
111
        MultiPoint::new(coordinates.into_iter().map(Into::into).collect())
81✔
112
    }
81✔
113
}
114

115
impl ArrowTyped for MultiPoint {
116
    type ArrowArray = arrow::array::ListArray;
117
    type ArrowBuilder = arrow::array::ListBuilder<<Coordinate2D as ArrowTyped>::ArrowBuilder>;
118

119
    fn arrow_data_type() -> arrow::datatypes::DataType {
631✔
120
        Coordinate2D::arrow_list_data_type()
631✔
121
    }
631✔
122

123
    fn estimate_array_memory_size(builder: &mut Self::ArrowBuilder) -> usize {
8,798✔
124
        let static_size = std::mem::size_of::<Self::ArrowArray>();
8,798✔
125

8,798✔
126
        let coords_size = Coordinate2D::estimate_array_memory_size(builder.values());
8,798✔
127

8,798✔
128
        let offset_bytes = builder.offsets_slice();
8,798✔
129
        let offset_bytes_size = std::mem::size_of_val(offset_bytes);
8,798✔
130

8,798✔
131
        static_size + coords_size + padded_buffer_size(offset_bytes_size, 64)
8,798✔
132
    }
8,798✔
133

134
    fn arrow_builder(capacity: usize) -> Self::ArrowBuilder {
2,660✔
135
        arrow::array::ListBuilder::new(Coordinate2D::arrow_builder(capacity))
2,660✔
136
    }
2,660✔
137

138
    fn concat(a: &Self::ArrowArray, b: &Self::ArrowArray) -> Result<Self::ArrowArray, ArrowError> {
12✔
139
        use arrow::array::Array;
12✔
140

12✔
141
        let mut new_multipoints = Self::arrow_builder(a.len() + b.len());
12✔
142

143
        for old_multipoints in &[a, b] {
24✔
144
            for multipoint_index in 0..old_multipoints.len() {
39✔
145
                let multipoint_ref = old_multipoints.value(multipoint_index);
39✔
146
                let multipoint: &FixedSizeListArray = downcast_array(&multipoint_ref);
39✔
147

39✔
148
                let new_points = new_multipoints.values();
39✔
149

150
                for point_index in 0..multipoint.len() {
43✔
151
                    let floats_ref = multipoint.value(point_index);
43✔
152
                    let floats: &Float64Array = downcast_array(&floats_ref);
43✔
153

43✔
154
                    let new_floats = new_points.values();
43✔
155
                    new_floats.append_slice(floats.values());
43✔
156

43✔
157
                    new_points.append(true);
43✔
158
                }
43✔
159

160
                new_multipoints.append(true);
39✔
161
            }
162
        }
163

164
        Ok(new_multipoints.finish_cloned())
12✔
165
    }
12✔
166

167
    fn filter(
79✔
168
        features: &Self::ArrowArray,
79✔
169
        filter_array: &BooleanArray,
79✔
170
    ) -> Result<Self::ArrowArray, ArrowError> {
79✔
171
        use arrow::array::Array;
79✔
172

79✔
173
        let mut new_features = Self::arrow_builder(0);
79✔
174

175
        for feature_index in 0..features.len() {
293✔
176
            if filter_array.value(feature_index) {
293✔
177
                let coordinate_builder = new_features.values();
231✔
178

231✔
179
                let old_coordinates = features.value(feature_index);
231✔
180

181
                for coordinate_index in 0..features.value_length(feature_index) {
239✔
182
                    let old_floats_array = downcast_array::<FixedSizeListArray>(&old_coordinates)
239✔
183
                        .value(coordinate_index as usize);
239✔
184

239✔
185
                    let old_floats: &Float64Array = downcast_array(&old_floats_array);
239✔
186

239✔
187
                    let float_builder = coordinate_builder.values();
239✔
188
                    float_builder.append_slice(old_floats.values());
239✔
189

239✔
190
                    coordinate_builder.append(true);
239✔
191
                }
239✔
192

193
                new_features.append(true);
231✔
194
            }
62✔
195
        }
196

197
        Ok(new_features.finish_cloned())
79✔
198
    }
79✔
199

200
    fn from_vec(multi_points: Vec<Self>) -> Result<Self::ArrowArray, ArrowError>
279✔
201
    where
279✔
202
        Self: Sized,
279✔
203
    {
279✔
204
        let mut builder = Self::arrow_builder(multi_points.len());
279✔
205
        for multi_point in multi_points {
2,002✔
206
            let coordinate_builder = builder.values();
1,723✔
207
            for coordinate in multi_point.as_ref() {
1,740✔
208
                let float_builder = coordinate_builder.values();
1,740✔
209
                float_builder.append_value(coordinate.x);
1,740✔
210
                float_builder.append_value(coordinate.y);
1,740✔
211
                coordinate_builder.append(true);
1,740✔
212
            }
1,740✔
213
            builder.append(true);
1,723✔
214
        }
215

216
        Ok(builder.finish_cloned())
279✔
217
    }
279✔
218
}
219

220
#[derive(Debug, PartialEq)]
3✔
221
pub struct MultiPointRef<'g> {
222
    point_coordinates: &'g [Coordinate2D],
223
}
224

225
impl<'g> MultiPointRef<'g> {
226
    pub fn new(coordinates: &'g [Coordinate2D]) -> Result<Self> {
1✔
227
        ensure!(!coordinates.is_empty(), error::UnallowedEmpty);
1✔
228

229
        Ok(Self::new_unchecked(coordinates))
1✔
230
    }
1✔
231

232
    pub(crate) fn new_unchecked(coordinates: &'g [Coordinate2D]) -> Self {
863✔
233
        Self {
863✔
234
            point_coordinates: coordinates,
863✔
235
        }
863✔
236
    }
863✔
237

238
    pub fn bbox(&self) -> Option<BoundingBox2D> {
×
239
        BoundingBox2D::from_coord_ref_iter(self.point_coordinates)
×
240
    }
×
241
}
242

243
impl<'r> GeometryRef for MultiPointRef<'r> {
244
    type GeometryType = MultiPoint;
245

246
    fn as_geometry(&self) -> Self::GeometryType {
×
247
        MultiPoint::from(self)
×
248
    }
×
249

250
    fn bbox(&self) -> Option<BoundingBox2D> {
×
251
        self.bbox()
×
252
    }
×
253
}
254

255
impl<'g> MultiPointAccess for MultiPointRef<'g> {
256
    fn points(&self) -> &[Coordinate2D] {
897✔
257
        self.point_coordinates
897✔
258
    }
897✔
259
}
260

261
impl<'r> ToWkt<f64> for MultiPointRef<'r> {
262
    fn to_wkt(&self) -> Wkt<f64> {
10✔
263
        let points = self.points();
10✔
264
        let mut multi_point = wkt::types::MultiPoint(Vec::with_capacity(points.len()));
10✔
265

266
        for point in points {
22✔
267
            multi_point.0.push(wkt::types::Point(Some(point.into())));
12✔
268
        }
12✔
269

270
        Wkt {
10✔
271
            item: wkt::Geometry::MultiPoint(multi_point),
10✔
272
        }
10✔
273
    }
10✔
274
}
275

276
impl<'g> From<MultiPointRef<'g>> for geojson::Geometry {
277
    fn from(geometry: MultiPointRef<'g>) -> geojson::Geometry {
26✔
278
        geojson::Geometry::new(match geometry.point_coordinates.len() {
26✔
279
            1 => {
280
                let floats: [f64; 2] = geometry.point_coordinates[0].into();
23✔
281
                geojson::Value::Point(floats.to_vec())
23✔
282
            }
283
            _ => geojson::Value::MultiPoint(
3✔
284
                geometry
3✔
285
                    .point_coordinates
3✔
286
                    .iter()
3✔
287
                    .map(|&c| {
6✔
288
                        let floats: [f64; 2] = c.into();
6✔
289
                        floats.to_vec()
6✔
290
                    })
6✔
291
                    .collect(),
3✔
292
            ),
3✔
293
        })
294
    }
26✔
295
}
296

297
impl<'g> From<MultiPointRef<'g>> for MultiPoint {
298
    fn from(multi_point_ref: MultiPointRef<'g>) -> Self {
15✔
299
        MultiPoint::from(&multi_point_ref)
15✔
300
    }
15✔
301
}
302

303
impl<'g> From<&MultiPointRef<'g>> for MultiPoint {
304
    fn from(multi_point_ref: &MultiPointRef<'g>) -> Self {
15✔
305
        MultiPoint::new_unchecked(multi_point_ref.point_coordinates.to_owned())
15✔
306
    }
15✔
307
}
308

309
impl<'g> From<&'g MultiPoint> for MultiPointRef<'g> {
310
    fn from(multi_point: &'g MultiPoint) -> Self {
1✔
311
        MultiPointRef::new_unchecked(&multi_point.coordinates)
1✔
312
    }
1✔
313
}
314

315
impl<A> SpatialBounded for A
316
where
317
    A: MultiPointAccess,
318
{
319
    fn spatial_bounds(&self) -> BoundingBox2D {
106✔
320
        BoundingBox2D::from_coord_ref_iter(self.points())
106✔
321
            .expect("there must be at least one cordinate in a multipoint")
106✔
322
    }
106✔
323
}
324

325
impl ApproxEq for &MultiPoint {
326
    type Margin = F64Margin;
327

328
    fn approx_eq<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool {
7✔
329
        let m = margin.into();
7✔
330
        self.coordinates.len() == other.coordinates.len()
7✔
331
            && self
6✔
332
                .coordinates
6✔
333
                .iter()
6✔
334
                .zip(other.coordinates.iter())
6✔
335
                .all(|(&a, &b)| a.approx_eq(b, m))
9✔
336
    }
7✔
337
}
338

339
#[cfg(test)]
340
mod tests {
341
    use arrow::array::{Array, ArrayBuilder};
342
    use float_cmp::approx_eq;
343

344
    use super::*;
345

346
    #[test]
1✔
347
    fn access() {
1✔
348
        fn aggregate<T: MultiPointAccess>(multi_point: &T) -> Coordinate2D {
3✔
349
            let (x, y) = multi_point
3✔
350
                .points()
3✔
351
                .iter()
3✔
352
                .fold((0., 0.), |(x, y), c| (x + c.x, y + c.y));
6✔
353
            (x, y).into()
3✔
354
        }
3✔
355

1✔
356
        let coordinates = vec![(0.0, 0.1).into(), (1.0, 1.1).into()];
1✔
357
        let multi_point = MultiPoint::new(coordinates.clone()).unwrap();
1✔
358
        let multi_point_ref = MultiPointRef::new(&coordinates).unwrap();
1✔
359

1✔
360
        let Coordinate2D { x, y } = aggregate(&multi_point);
1✔
361
        float_cmp::approx_eq!(f64, x, 1.0);
1✔
362
        float_cmp::approx_eq!(f64, y, 1.2);
1✔
363
        assert_eq!(aggregate(&multi_point), aggregate(&multi_point_ref));
1✔
364
    }
1✔
365

366
    #[test]
1✔
367
    fn intersects_bbox() -> Result<()> {
1✔
368
        let bbox = BoundingBox2D::new((0.0, 0.0).into(), (1.0, 1.0).into())?;
1✔
369

370
        assert!(MultiPoint::new(vec![(0.5, 0.5).into()])?.intersects_bbox(&bbox));
1✔
371
        assert!(MultiPoint::new(vec![(1.0, 1.0).into()])?.intersects_bbox(&bbox));
1✔
372
        assert!(MultiPoint::new(vec![(0.5, 0.5).into(), (1.5, 1.5).into()])?.intersects_bbox(&bbox));
1✔
373
        assert!(!MultiPoint::new(vec![(1.1, 1.1).into()])?.intersects_bbox(&bbox));
1✔
374
        assert!(
1✔
375
            !MultiPoint::new(vec![(-0.1, -0.1).into(), (1.1, 1.1).into()])?.intersects_bbox(&bbox)
1✔
376
        );
377

378
        Ok(())
1✔
379
    }
1✔
380

381
    #[test]
1✔
382
    fn spatial_bounds() {
1✔
383
        let expected = BoundingBox2D::new_unchecked((0., 0.).into(), (1., 1.).into());
1✔
384
        let coordinates: Vec<Coordinate2D> = Vec::from([
1✔
385
            (1., 0.4).into(),
1✔
386
            (0.8, 0.0).into(),
1✔
387
            (0.3, 0.1).into(),
1✔
388
            (0.0, 1.0).into(),
1✔
389
        ]);
1✔
390
        let mp = MultiPoint { coordinates };
1✔
391
        assert_eq!(mp.spatial_bounds(), expected);
1✔
392
    }
1✔
393

394
    #[test]
1✔
395
    fn approx_equal() {
1✔
396
        let a = MultiPoint::new(vec![
1✔
397
            (0.5, 0.5).into(),
1✔
398
            (0.5, 0.5).into(),
1✔
399
            (0.5, 0.5).into(),
1✔
400
        ])
1✔
401
        .unwrap();
1✔
402

1✔
403
        let b = MultiPoint::new(vec![
1✔
404
            (0.5, 0.499_999_999).into(),
1✔
405
            (0.5, 0.5).into(),
1✔
406
            (0.5, 0.5).into(),
1✔
407
        ])
1✔
408
        .unwrap();
1✔
409

1✔
410
        assert!(approx_eq!(&MultiPoint, &a, &b, epsilon = 0.000_001));
1✔
411
    }
1✔
412

413
    #[test]
1✔
414
    fn not_approx_equal_len() {
1✔
415
        let a = MultiPoint::new(vec![
1✔
416
            (0.5, 0.5).into(),
1✔
417
            (0.5, 0.5).into(),
1✔
418
            (0.5, 0.5).into(),
1✔
419
        ])
1✔
420
        .unwrap();
1✔
421

1✔
422
        let b = MultiPoint::new(vec![
1✔
423
            (0.5, 0.5).into(),
1✔
424
            (0.5, 0.5).into(),
1✔
425
            (0.5, 0.5).into(),
1✔
426
            (123_456_789.5, 123_456_789.5).into(),
1✔
427
        ])
1✔
428
        .unwrap();
1✔
429

1✔
430
        assert!(!approx_eq!(&MultiPoint, &a, &b, F64Margin::default()));
1✔
431
    }
1✔
432

433
    #[test]
1✔
434
    fn test_to_wkt() {
1✔
435
        let a = MultiPoint::new(vec![
1✔
436
            (0.5, 0.6).into(),
1✔
437
            (0.7, 0.8).into(),
1✔
438
            (0.9, 0.99).into(),
1✔
439
        ])
1✔
440
        .unwrap();
1✔
441

1✔
442
        let a_ref = MultiPointRef::from(&a);
1✔
443

1✔
444
        assert_eq!(
1✔
445
            a_ref.wkt_string(),
1✔
446
            "MULTIPOINT((0.5 0.6),(0.7 0.8),(0.9 0.99))"
1✔
447
        );
1✔
448
    }
1✔
449

450
    #[test]
1✔
451
    fn arrow_builder_size_points() {
1✔
452
        for num_multipoints in 0..514 {
515✔
453
            for capacity in [0, num_multipoints] {
1,028✔
454
                let mut multi_points_builder = MultiPoint::arrow_builder(capacity);
1,028✔
455

1,028✔
456
                for _ in 0..num_multipoints {
263,682✔
457
                    multi_points_builder
263,682✔
458
                        .values()
263,682✔
459
                        .values()
263,682✔
460
                        .append_values(&[1., 2.], &[true, true]);
263,682✔
461

263,682✔
462
                    multi_points_builder.values().append(true);
263,682✔
463

263,682✔
464
                    multi_points_builder.append(true);
263,682✔
465
                }
263,682✔
466

467
                assert_eq!(multi_points_builder.len(), num_multipoints);
1,028✔
468

469
                let builder_byte_size =
1,028✔
470
                    MultiPoint::estimate_array_memory_size(&mut multi_points_builder);
1,028✔
471

1,028✔
472
                let array = multi_points_builder.finish_cloned();
1,028✔
473

1,028✔
474
                assert_eq!(
1,028✔
475
                    builder_byte_size,
1,028✔
476
                    array.get_array_memory_size(),
1,028✔
477
                    "{num_multipoints}"
×
478
                );
479
            }
480
        }
481
    }
1✔
482

483
    #[test]
1✔
484
    fn arrow_builder_size_multi_points() {
1✔
485
        // TODO: test fewer multipoints?
486
        for num_multipoints in 0..514 {
515✔
487
            for capacity in [0, num_multipoints] {
1,028✔
488
                let mut multi_points_builder = MultiPoint::arrow_builder(capacity);
1,028✔
489

490
                // we have 1 point for the first multipoint, 2 for the second, etc.
491
                for num_points in 0..num_multipoints {
263,682✔
492
                    for _ in 0..num_points {
45,001,728✔
493
                        multi_points_builder
45,001,728✔
494
                            .values()
45,001,728✔
495
                            .values()
45,001,728✔
496
                            .append_values(&[1., 2.], &[true, true]);
45,001,728✔
497

45,001,728✔
498
                        multi_points_builder.values().append(true);
45,001,728✔
499
                    }
45,001,728✔
500

501
                    multi_points_builder.append(true);
263,682✔
502
                }
503

504
                assert_eq!(multi_points_builder.len(), num_multipoints);
1,028✔
505

506
                let builder_byte_size =
1,028✔
507
                    MultiPoint::estimate_array_memory_size(&mut multi_points_builder);
1,028✔
508

1,028✔
509
                let array = multi_points_builder.finish_cloned();
1,028✔
510

1,028✔
511
                assert_eq!(
1,028✔
512
                    builder_byte_size,
1,028✔
513
                    array.get_array_memory_size(),
1,028✔
514
                    "{num_multipoints}"
×
515
                );
516
            }
517
        }
518
    }
1✔
519
}
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