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

geo-engine / geoengine / 18554766227

16 Oct 2025 08:12AM UTC coverage: 88.843% (+0.3%) from 88.543%
18554766227

push

github

web-flow
build: update dependencies (#1081)

* update sqlfluff

* clippy autofix

* manual clippy fixes

* removal of unused code

* update deps

* upgrade packages

* enable cargo lints

* make sqlfluff happy

* fix chrono parsin error

* clippy

* byte_size

* fix image cmp with tiffs

* remove debug

177 of 205 new or added lines in 38 files covered. (86.34%)

41 existing lines in 20 files now uncovered.

106415 of 119779 relevant lines covered (88.84%)

84190.21 hits per line

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

87.18
/operators/src/processing/line_simplification.rs
1
use crate::{
2
    engine::{
3
        CanonicOperatorName, ExecutionContext, InitializedSources, InitializedVectorOperator,
4
        Operator, OperatorName, QueryContext, QueryProcessor, SingleVectorSource,
5
        TypedVectorQueryProcessor, VectorOperator, VectorQueryProcessor, VectorResultDescriptor,
6
        WorkflowOperatorPath,
7
    },
8
    util::Result,
9
};
10
use async_trait::async_trait;
11
use futures::{StreamExt, TryStreamExt, stream::BoxStream};
12
use geoengine_datatypes::{
13
    collections::{
14
        FeatureCollection, GeoFeatureCollectionModifications, IntoGeometryIterator, VectorDataType,
15
    },
16
    error::{BoxedResultExt, ErrorSource},
17
    primitives::{
18
        BoundingBox2D, ColumnSelection, Geometry, MultiLineString, MultiLineStringRef,
19
        MultiPolygon, MultiPolygonRef, SpatialResolution, VectorQueryRectangle,
20
    },
21
    util::arrow::ArrowTyped,
22
};
23
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
24
use serde::{Deserialize, Serialize};
25
use snafu::Snafu;
26

27
/// A `LineSimplification` operator simplifies the geometry of a `Vector` by removing vertices.
28
///
29
/// The simplification is performed on the geometry of the `Vector` and not on the data.
30
/// The `LineSimplification` operator is only available for (multi-)lines or (multi-)polygons.
31
///
32
pub type LineSimplification = Operator<LineSimplificationParams, SingleVectorSource>;
33

34
impl OperatorName for LineSimplification {
35
    const TYPE_NAME: &'static str = "LineSimplification";
36
}
37

38
#[typetag::serde]
×
39
#[async_trait]
40
impl VectorOperator for LineSimplification {
41
    async fn _initialize(
42
        self: Box<Self>,
43
        path: WorkflowOperatorPath,
44
        context: &dyn ExecutionContext,
45
    ) -> Result<Box<dyn InitializedVectorOperator>> {
5✔
46
        if self
47
            .params
48
            .epsilon
49
            .map_or(false, |e| !e.is_finite() || e <= 0.0)
3✔
50
        {
51
            return Err(LineSimplificationError::InvalidEpsilon.into());
52
        }
53

54
        let name = CanonicOperatorName::from(&self);
55

56
        let sources = self
57
            .sources
58
            .initialize_sources(path.clone(), context)
59
            .await?;
60
        let source = sources.vector;
61

62
        if source.result_descriptor().data_type != VectorDataType::MultiLineString
63
            && source.result_descriptor().data_type != VectorDataType::MultiPolygon
64
        {
65
            return Err(LineSimplificationError::InvalidGeometryType.into());
66
        }
67

68
        let initialized_operator = InitializedLineSimplification {
69
            name,
70
            path,
71
            result_descriptor: source.result_descriptor().clone(),
72
            source,
73
            algorithm: self.params.algorithm,
74
            epsilon: self.params.epsilon,
75
        };
76

77
        Ok(initialized_operator.boxed())
78
    }
5✔
79

80
    span_fn!(LineSimplification);
81
}
82

83
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
84
#[serde(rename_all = "camelCase")]
85
pub struct LineSimplificationParams {
86
    pub algorithm: LineSimplificationAlgorithm,
87
    /// The epsilon parameter is used to determine the maximum distance between the original and the simplified geometry.
88
    /// If `None` is provided, the epsilon is derived by the query's [`SpatialResolution`].
89
    pub epsilon: Option<f64>,
90
}
91

92
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
93
#[serde(rename_all = "camelCase")]
94
pub enum LineSimplificationAlgorithm {
95
    DouglasPeucker,
96
    Visvalingam,
97
}
98

99
pub struct InitializedLineSimplification {
100
    name: CanonicOperatorName,
101
    path: WorkflowOperatorPath,
102
    result_descriptor: VectorResultDescriptor,
103
    source: Box<dyn InitializedVectorOperator>,
104
    algorithm: LineSimplificationAlgorithm,
105
    epsilon: Option<f64>,
106
}
107

108
impl InitializedVectorOperator for InitializedLineSimplification {
109
    fn result_descriptor(&self) -> &VectorResultDescriptor {
1✔
110
        &self.result_descriptor
1✔
111
    }
1✔
112

113
    fn query_processor(&self) -> Result<TypedVectorQueryProcessor> {
2✔
114
        match (self.source.query_processor()?, self.algorithm) {
2✔
115
            (
116
                TypedVectorQueryProcessor::Data(..) | TypedVectorQueryProcessor::MultiPoint(..),
117
                _,
118
            ) => Err(LineSimplificationError::InvalidGeometryType.into()),
×
119
            (
120
                TypedVectorQueryProcessor::MultiLineString(source),
1✔
121
                LineSimplificationAlgorithm::DouglasPeucker,
122
            ) => Ok(TypedVectorQueryProcessor::MultiLineString(
1✔
123
                LineSimplificationProcessor {
1✔
124
                    source,
1✔
125
                    _algorithm: DouglasPeucker,
1✔
126
                    epsilon: self.epsilon,
1✔
127
                }
1✔
128
                .boxed(),
1✔
129
            )),
1✔
130
            (
131
                TypedVectorQueryProcessor::MultiLineString(source),
×
132
                LineSimplificationAlgorithm::Visvalingam,
133
            ) => Ok(TypedVectorQueryProcessor::MultiLineString(
×
134
                LineSimplificationProcessor {
×
135
                    source,
×
136
                    _algorithm: Visvalingam,
×
137
                    epsilon: self.epsilon,
×
138
                }
×
139
                .boxed(),
×
140
            )),
×
141
            (
142
                TypedVectorQueryProcessor::MultiPolygon(source),
×
143
                LineSimplificationAlgorithm::DouglasPeucker,
144
            ) => Ok(TypedVectorQueryProcessor::MultiPolygon(
×
145
                LineSimplificationProcessor {
×
146
                    source,
×
147
                    _algorithm: DouglasPeucker,
×
148
                    epsilon: self.epsilon,
×
149
                }
×
150
                .boxed(),
×
151
            )),
×
152
            (
153
                TypedVectorQueryProcessor::MultiPolygon(source),
1✔
154
                LineSimplificationAlgorithm::Visvalingam,
155
            ) => Ok(TypedVectorQueryProcessor::MultiPolygon(
1✔
156
                LineSimplificationProcessor {
1✔
157
                    source,
1✔
158
                    _algorithm: Visvalingam,
1✔
159
                    epsilon: self.epsilon,
1✔
160
                }
1✔
161
                .boxed(),
1✔
162
            )),
1✔
163
        }
164
    }
2✔
165

166
    fn canonic_name(&self) -> CanonicOperatorName {
×
167
        self.name.clone()
×
168
    }
×
169

170
    fn name(&self) -> &'static str {
×
171
        LineSimplification::TYPE_NAME
×
172
    }
×
173

174
    fn path(&self) -> WorkflowOperatorPath {
×
175
        self.path.clone()
×
176
    }
×
177
}
178

179
struct LineSimplificationProcessor<P, G, A>
180
where
181
    P: VectorQueryProcessor<VectorType = FeatureCollection<G>>,
182
    G: Geometry,
183
    for<'c> FeatureCollection<G>: IntoGeometryIterator<'c>,
184
    for<'c> A: LineSimplificationAlgorithmImpl<
185
            <FeatureCollection<G> as IntoGeometryIterator<'c>>::GeometryType,
186
            G,
187
        >,
188
{
189
    source: P,
190
    _algorithm: A,
191
    epsilon: Option<f64>,
192
}
193

194
pub trait LineSimplificationAlgorithmImpl<In, Out: Geometry>: Send + Sync {
195
    fn simplify(geometry_ref: In, epsilon: f64) -> Out;
196

197
    fn derive_epsilon(spatial_resolution: SpatialResolution) -> f64 {
×
198
        f64::sqrt(spatial_resolution.x.powi(2) + spatial_resolution.y.powi(2)) / f64::sqrt(2.)
×
199
    }
×
200
}
201

202
struct DouglasPeucker;
203
struct Visvalingam;
204

205
impl<'c> LineSimplificationAlgorithmImpl<MultiLineStringRef<'c>, MultiLineString>
206
    for DouglasPeucker
207
{
208
    fn simplify(geometry: MultiLineStringRef<'c>, epsilon: f64) -> MultiLineString {
2✔
209
        use geo::Simplify;
210

211
        let geo_geometry = geo::MultiLineString::<f64>::from(&geometry);
2✔
212
        let geo_geometry = geo_geometry.simplify(epsilon);
2✔
213
        geo_geometry.into()
2✔
214
    }
2✔
215
}
216

217
impl<'c> LineSimplificationAlgorithmImpl<MultiPolygonRef<'c>, MultiPolygon> for DouglasPeucker {
218
    fn simplify(geometry: MultiPolygonRef<'c>, epsilon: f64) -> MultiPolygon {
×
219
        use geo::Simplify;
220

221
        let geo_geometry = geo::MultiPolygon::<f64>::from(&geometry);
×
NEW
222
        let geo_geometry = geo_geometry.simplify(epsilon);
×
223
        geo_geometry.into()
×
224
    }
×
225
}
226

227
impl<'c> LineSimplificationAlgorithmImpl<MultiLineStringRef<'c>, MultiLineString> for Visvalingam {
228
    fn simplify(geometry: MultiLineStringRef<'c>, epsilon: f64) -> MultiLineString {
×
229
        use geo::SimplifyVwPreserve;
230

231
        let geo_geometry = geo::MultiLineString::<f64>::from(&geometry);
×
NEW
232
        let geo_geometry = geo_geometry.simplify_vw_preserve(epsilon);
×
233
        geo_geometry.into()
×
234
    }
×
235

236
    fn derive_epsilon(spatial_resolution: SpatialResolution) -> f64 {
×
237
        // for visvalingam, the epsilon is squared since it reflects some triangle area
238
        // this is a heuristic, though
239
        spatial_resolution.x * spatial_resolution.y
×
240
    }
×
241
}
242

243
impl<'c> LineSimplificationAlgorithmImpl<MultiPolygonRef<'c>, MultiPolygon> for Visvalingam {
244
    fn simplify(geometry: MultiPolygonRef<'c>, epsilon: f64) -> MultiPolygon {
1✔
245
        use geo::SimplifyVwPreserve;
246

247
        let geo_geometry = geo::MultiPolygon::<f64>::from(&geometry);
1✔
248
        let geo_geometry = geo_geometry.simplify_vw_preserve(epsilon);
1✔
249
        geo_geometry.into()
1✔
250
    }
1✔
251

252
    fn derive_epsilon(spatial_resolution: SpatialResolution) -> f64 {
1✔
253
        // for visvalingam, the epsilon is squared since it reflects some triangle area
254
        // this is a heuristic, though
255
        spatial_resolution.x * spatial_resolution.y
1✔
256
    }
1✔
257
}
258

259
impl<P, G, A> LineSimplificationProcessor<P, G, A>
260
where
261
    P: VectorQueryProcessor<VectorType = FeatureCollection<G>>,
262
    G: Geometry,
263
    for<'c> FeatureCollection<G>: IntoGeometryIterator<'c> + GeoFeatureCollectionModifications<G>,
264
    for<'c> A: LineSimplificationAlgorithmImpl<
265
            <FeatureCollection<G> as IntoGeometryIterator<'c>>::GeometryType,
266
            G,
267
        >,
268
{
269
    fn simplify(collection: &FeatureCollection<G>, epsilon: f64) -> Result<FeatureCollection<G>> {
2✔
270
        // TODO: chunk within parallelization to reduce overhead if necessary
271

272
        let simplified_geometries = collection
2✔
273
            .geometries()
2✔
274
            .into_par_iter()
2✔
275
            .map(|geometry| A::simplify(geometry, epsilon))
3✔
276
            .collect();
2✔
277

278
        Ok(collection
2✔
279
            .replace_geometries(simplified_geometries)
2✔
280
            .boxed_context(error::ErrorDuringSimplification)?)
2✔
281
    }
2✔
282
}
283

284
#[async_trait]
285
impl<P, G, A> QueryProcessor for LineSimplificationProcessor<P, G, A>
286
where
287
    P: QueryProcessor<
288
            Output = FeatureCollection<G>,
289
            SpatialBounds = BoundingBox2D,
290
            Selection = ColumnSelection,
291
            ResultDescription = VectorResultDescriptor,
292
        >,
293
    G: Geometry + ArrowTyped + 'static,
294
    for<'c> FeatureCollection<G>: IntoGeometryIterator<'c> + GeoFeatureCollectionModifications<G>,
295
    for<'c> A: LineSimplificationAlgorithmImpl<
296
            <FeatureCollection<G> as IntoGeometryIterator<'c>>::GeometryType,
297
            G,
298
        >,
299
{
300
    type Output = FeatureCollection<G>;
301
    type SpatialBounds = BoundingBox2D;
302
    type Selection = ColumnSelection;
303
    type ResultDescription = VectorResultDescriptor;
304

305
    async fn _query<'a>(
306
        &'a self,
307
        query: VectorQueryRectangle,
308
        ctx: &'a dyn QueryContext,
309
    ) -> Result<BoxStream<'a, Result<Self::Output>>> {
2✔
310
        let chunks = self.source.query(query.clone(), ctx).await?;
311

312
        let epsilon = self
313
            .epsilon
314
            .unwrap_or_else(|| A::derive_epsilon(query.spatial_resolution));
1✔
315

316
        let simplified_chunks = chunks.and_then(move |chunk| async move {
2✔
317
            crate::util::spawn_blocking_with_thread_pool(ctx.thread_pool().clone(), move || {
2✔
318
                Self::simplify(&chunk, epsilon)
2✔
319
            })
2✔
320
            .await
2✔
321
            .boxed_context(error::UnexpectedInternal)?
2✔
322
        });
4✔
323

324
        Ok(simplified_chunks.boxed())
325
    }
2✔
326

327
    fn result_descriptor(&self) -> &VectorResultDescriptor {
4✔
328
        self.source.result_descriptor()
4✔
329
    }
4✔
330
}
331

332
#[derive(Debug, Snafu)]
333
#[snafu(visibility(pub(crate)), context(suffix(false)), module(error))]
334
pub enum LineSimplificationError {
335
    #[snafu(display("`epsilon` parameter must be greater than 0"))]
336
    InvalidEpsilon,
337
    #[snafu(display("Geometry must be of type `MultiLineString` or `MultiPolygon`"))]
338
    InvalidGeometryType,
339
    #[snafu(display("Error during simplification of geometry: {}", source))]
340
    ErrorDuringSimplification { source: Box<dyn ErrorSource> },
341
    #[snafu(display("Unexpected internal error: {}", source))]
342
    UnexpectedInternal { source: Box<dyn ErrorSource> },
343
}
344

345
#[cfg(test)]
346
mod tests {
347
    use super::*;
348
    use crate::{
349
        engine::{MockExecutionContext, MockQueryContext, StaticMetaData},
350
        mock::MockFeatureCollectionSource,
351
        source::{
352
            OgrSource, OgrSourceColumnSpec, OgrSourceDataset, OgrSourceDatasetTimeType,
353
            OgrSourceErrorSpec, OgrSourceParameters,
354
        },
355
    };
356
    use geoengine_datatypes::{
357
        collections::{
358
            ChunksEqualIgnoringCacheHint, FeatureCollectionInfos, GeometryCollection,
359
            MultiLineStringCollection, MultiPointCollection, MultiPolygonCollection,
360
        },
361
        dataset::{DataId, DatasetId, NamedData},
362
        primitives::{
363
            FeatureData, MultiLineString, MultiPoint, TimeInterval, {CacheHint, CacheTtlSeconds},
364
        },
365
        spatial_reference::SpatialReference,
366
        test_data,
367
        util::{Identifier, test::TestDefault},
368
    };
369

370
    #[tokio::test]
371
    async fn test_ser_de() {
1✔
372
        let operator = LineSimplification {
1✔
373
            params: LineSimplificationParams {
1✔
374
                epsilon: Some(1.0),
1✔
375
                algorithm: LineSimplificationAlgorithm::DouglasPeucker,
1✔
376
            },
1✔
377
            sources: MockFeatureCollectionSource::<MultiPolygon>::multiple(vec![])
1✔
378
                .boxed()
1✔
379
                .into(),
1✔
380
        }
1✔
381
        .boxed();
1✔
382

383
        let serialized = serde_json::to_value(&operator).unwrap();
1✔
384

385
        assert_eq!(
1✔
386
            serialized,
387
            serde_json::json!({
1✔
388
                "type": "LineSimplification",
1✔
389
                "params": {
1✔
390
                    "epsilon": 1.0,
1✔
391
                    "algorithm": "douglasPeucker",
1✔
392
                },
393
                "sources": {
1✔
394
                    "vector": {
1✔
395
                        "type": "MockFeatureCollectionSourceMultiPolygon",
1✔
396
                        "params": {
1✔
397
                            "collections": [],
1✔
398
                            "spatialReference": "EPSG:4326",
1✔
399
                            "measurements": null,
1✔
400
                        }
401
                    }
402
                },
403
            })
404
        );
405

406
        let _operator: Box<dyn VectorOperator> = serde_json::from_value(serialized).unwrap();
1✔
407
    }
1✔
408

409
    #[tokio::test]
410
    async fn test_errors() {
1✔
411
        // zero epsilon
412
        assert!(
1✔
413
            LineSimplification {
1✔
414
                params: LineSimplificationParams {
1✔
415
                    epsilon: Some(0.0),
1✔
416
                    algorithm: LineSimplificationAlgorithm::DouglasPeucker,
1✔
417
                },
1✔
418
                sources: MockFeatureCollectionSource::<MultiPolygon>::single(
1✔
419
                    MultiPolygonCollection::empty()
1✔
420
                )
1✔
421
                .boxed()
1✔
422
                .into(),
1✔
423
            }
1✔
424
            .boxed()
1✔
425
            .initialize(
1✔
426
                WorkflowOperatorPath::initialize_root(),
1✔
427
                &MockExecutionContext::test_default()
1✔
428
            )
429
            .await
1✔
430
            .is_err()
1✔
431
        );
432

433
        // invalid epsilon
434
        assert!(
1✔
435
            LineSimplification {
1✔
436
                params: LineSimplificationParams {
1✔
437
                    epsilon: Some(f64::NAN),
1✔
438
                    algorithm: LineSimplificationAlgorithm::Visvalingam,
1✔
439
                },
1✔
440
                sources: MockFeatureCollectionSource::<MultiPolygon>::single(
1✔
441
                    MultiPolygonCollection::empty()
1✔
442
                )
1✔
443
                .boxed()
1✔
444
                .into(),
1✔
445
            }
1✔
446
            .boxed()
1✔
447
            .initialize(
1✔
448
                WorkflowOperatorPath::initialize_root(),
1✔
449
                &MockExecutionContext::test_default()
1✔
450
            )
451
            .await
1✔
452
            .is_err()
1✔
453
        );
454

455
        // not lines or polygons
456
        assert!(
1✔
457
            LineSimplification {
1✔
458
                params: LineSimplificationParams {
1✔
459
                    epsilon: None,
1✔
460
                    algorithm: LineSimplificationAlgorithm::DouglasPeucker,
1✔
461
                },
1✔
462
                sources: MockFeatureCollectionSource::<MultiPoint>::single(
1✔
463
                    MultiPointCollection::empty()
1✔
464
                )
1✔
465
                .boxed()
1✔
466
                .into(),
1✔
467
            }
1✔
468
            .boxed()
1✔
469
            .initialize(
1✔
470
                WorkflowOperatorPath::initialize_root(),
1✔
471
                &MockExecutionContext::test_default()
1✔
472
            )
1✔
473
            .await
1✔
474
            .is_err()
1✔
475
        );
1✔
476
    }
1✔
477

478
    #[tokio::test]
479
    async fn test_line_simplification() {
1✔
480
        let collection = MultiLineStringCollection::from_data(
1✔
481
            vec![
1✔
482
                MultiLineString::new(vec![vec![
1✔
483
                    (0.0, 0.0).into(),
1✔
484
                    (5.0, 4.0).into(),
1✔
485
                    (11.0, 5.5).into(),
1✔
486
                    (17.3, 3.2).into(),
1✔
487
                    (27.8, 0.1).into(),
1✔
488
                ]])
489
                .unwrap(),
1✔
490
                MultiLineString::new(vec![vec![(0.0, 0.0).into(), (5.0, 4.0).into()]]).unwrap(),
1✔
491
            ],
492
            vec![TimeInterval::new(0, 1).unwrap(); 2],
1✔
493
            [("foo".to_string(), FeatureData::Float(vec![0., 1.]))]
1✔
494
                .iter()
1✔
495
                .cloned()
1✔
496
                .collect(),
1✔
497
            CacheHint::default(),
1✔
498
        )
499
        .unwrap();
1✔
500

501
        let source = MockFeatureCollectionSource::single(collection.clone()).boxed();
1✔
502

503
        let simplification = LineSimplification {
1✔
504
            params: LineSimplificationParams {
1✔
505
                epsilon: Some(1.0),
1✔
506
                algorithm: LineSimplificationAlgorithm::DouglasPeucker,
1✔
507
            },
1✔
508
            sources: source.into(),
1✔
509
        }
1✔
510
        .boxed();
1✔
511

512
        let initialized = simplification
1✔
513
            .initialize(
1✔
514
                WorkflowOperatorPath::initialize_root(),
1✔
515
                &MockExecutionContext::test_default(),
1✔
516
            )
1✔
517
            .await
1✔
518
            .unwrap();
1✔
519

520
        let processor = initialized
1✔
521
            .query_processor()
1✔
522
            .unwrap()
1✔
523
            .multi_line_string()
1✔
524
            .unwrap();
1✔
525

526
        let query_rectangle = VectorQueryRectangle {
1✔
527
            spatial_bounds: BoundingBox2D::new((0., 0.).into(), (4., 4.).into()).unwrap(),
1✔
528
            time_interval: TimeInterval::default(),
1✔
529
            spatial_resolution: SpatialResolution::one(),
1✔
530
            attributes: ColumnSelection::all(),
1✔
531
        };
1✔
532

533
        let query_ctx = MockQueryContext::test_default();
1✔
534

535
        let stream = processor.query(query_rectangle, &query_ctx).await.unwrap();
1✔
536

537
        let collections: Vec<MultiLineStringCollection> =
1✔
538
            stream.map(Result::unwrap).collect().await;
1✔
539

540
        assert_eq!(collections.len(), 1);
1✔
541

542
        let expected = MultiLineStringCollection::from_data(
1✔
543
            vec![
1✔
544
                MultiLineString::new(vec![vec![
1✔
545
                    (0.0, 0.0).into(),
1✔
546
                    (5.0, 4.0).into(),
1✔
547
                    (11.0, 5.5).into(),
1✔
548
                    (27.8, 0.1).into(),
1✔
549
                ]])
550
                .unwrap(),
1✔
551
                MultiLineString::new(vec![vec![(0.0, 0.0).into(), (5.0, 4.0).into()]]).unwrap(),
1✔
552
            ],
553
            vec![TimeInterval::new(0, 1).unwrap(); 2],
1✔
554
            [("foo".to_string(), FeatureData::Float(vec![0., 1.]))]
1✔
555
                .iter()
1✔
556
                .cloned()
1✔
557
                .collect(),
1✔
558
            CacheHint::default(),
1✔
559
        )
560
        .unwrap();
1✔
561

562
        assert!(collections[0].chunks_equal_ignoring_cache_hint(&expected));
1✔
563
    }
1✔
564

565
    #[tokio::test]
566
    async fn test_polygon_simplification() {
1✔
567
        let id: DataId = DatasetId::new().into();
1✔
568
        let name = NamedData::with_system_name("polygons");
1✔
569
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
570
        exe_ctx.add_meta_data::<OgrSourceDataset, VectorResultDescriptor, VectorQueryRectangle>(
1✔
571
            id.clone(),
1✔
572
            name.clone(),
1✔
573
            Box::new(StaticMetaData {
1✔
574
                loading_info: OgrSourceDataset {
1✔
575
                    file_name: test_data!("vector/data/germany_polygon.gpkg").into(),
1✔
576
                    layer_name: "test_germany".to_owned(),
1✔
577
                    data_type: Some(VectorDataType::MultiPolygon),
1✔
578
                    time: OgrSourceDatasetTimeType::None,
1✔
579
                    default_geometry: None,
1✔
580
                    columns: Some(OgrSourceColumnSpec {
1✔
581
                        format_specifics: None,
1✔
582
                        x: String::new(),
1✔
583
                        y: None,
1✔
584
                        int: vec![],
1✔
585
                        float: vec![],
1✔
586
                        text: vec![],
1✔
587
                        bool: vec![],
1✔
588
                        datetime: vec![],
1✔
589
                        rename: None,
1✔
590
                    }),
1✔
591
                    force_ogr_time_filter: false,
1✔
592
                    force_ogr_spatial_filter: false,
1✔
593
                    on_error: OgrSourceErrorSpec::Abort,
1✔
594
                    sql_query: None,
1✔
595
                    attribute_query: None,
1✔
596
                    cache_ttl: CacheTtlSeconds::default(),
1✔
597
                },
1✔
598
                result_descriptor: VectorResultDescriptor {
1✔
599
                    data_type: VectorDataType::MultiPolygon,
1✔
600
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
601
                    columns: Default::default(),
1✔
602
                    time: None,
1✔
603
                    bbox: None,
1✔
604
                },
1✔
605
                phantom: Default::default(),
1✔
606
            }),
1✔
607
        );
608

609
        let simplification = LineSimplification {
1✔
610
            params: LineSimplificationParams {
1✔
611
                epsilon: None,
1✔
612
                algorithm: LineSimplificationAlgorithm::Visvalingam,
1✔
613
            },
1✔
614
            sources: OgrSource {
1✔
615
                params: OgrSourceParameters {
1✔
616
                    data: name,
1✔
617
                    attribute_projection: None,
1✔
618
                    attribute_filters: None,
1✔
619
                },
1✔
620
            }
1✔
621
            .boxed()
1✔
622
            .into(),
1✔
623
        }
1✔
624
        .boxed()
1✔
625
        .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
626
        .await
1✔
627
        .unwrap();
1✔
628

629
        assert_eq!(
1✔
630
            simplification.result_descriptor().data_type,
1✔
631
            VectorDataType::MultiPolygon
632
        );
633

634
        let query_processor = simplification
1✔
635
            .query_processor()
1✔
636
            .unwrap()
1✔
637
            .multi_polygon()
1✔
638
            .unwrap();
1✔
639

640
        let query_bbox = BoundingBox2D::new((-180.0, -90.0).into(), (180.00, 90.0).into()).unwrap();
1✔
641

642
        let query_context = MockQueryContext::test_default();
1✔
643
        let query = query_processor
1✔
644
            .query(
1✔
645
                VectorQueryRectangle {
1✔
646
                    spatial_bounds: query_bbox,
1✔
647
                    time_interval: Default::default(),
1✔
648
                    spatial_resolution: SpatialResolution::new(1., 1.).unwrap(),
1✔
649
                    attributes: ColumnSelection::all(),
1✔
650
                },
1✔
651
                &query_context,
1✔
652
            )
1✔
653
            .await
1✔
654
            .unwrap();
1✔
655

656
        let result: Vec<MultiPolygonCollection> = query.try_collect().await.unwrap();
1✔
657

658
        assert_eq!(result.len(), 1);
1✔
659
        let result = result.into_iter().next().unwrap();
1✔
660

661
        assert_eq!(result.len(), 1);
1✔
662
        assert_eq!(result.feature_offsets().len(), 2);
1✔
663
        assert_eq!(result.polygon_offsets().len(), 23);
1✔
664
        assert_eq!(result.ring_offsets().len(), 23);
1✔
665
        assert_eq!(result.coordinates().len(), 96 /* was 3027 */);
1✔
666
    }
1✔
667
}
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

© 2025 Coveralls, Inc