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

geo-engine / geoengine / 5006008836

pending completion
5006008836

push

github

GitHub
Merge #785 #787

936 of 936 new or added lines in 50 files covered. (100.0%)

96010 of 107707 relevant lines covered (89.14%)

72676.46 hits per line

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

97.03
/operators/src/processing/temporal_raster_aggregation/temporal_aggregation_operator.rs
1
use super::aggregators::{
2
    CountPixelAggregator, CountPixelAggregatorIngoringNoData, FirstPixelAggregatorIngoringNoData,
3
    LastPixelAggregatorIngoringNoData, MaxPixelAggregator, MaxPixelAggregatorIngoringNoData,
4
    MeanPixelAggregator, MeanPixelAggregatorIngoringNoData, MinPixelAggregator,
5
    MinPixelAggregatorIngoringNoData, SumPixelAggregator, SumPixelAggregatorIngoringNoData,
6
    TemporalRasterPixelAggregator,
7
};
8
use super::first_last_subquery::{
9
    first_tile_fold_future, last_tile_fold_future, TemporalRasterAggregationSubQueryNoDataOnly,
10
};
11
use crate::engine::{
12
    CanonicOperatorName, ExecutionContext, InitializedSources, Operator, QueryProcessor,
13
    RasterOperator, SingleRasterSource, WorkflowOperatorPath,
14
};
15
use crate::{
16
    adapters::SubQueryTileAggregator,
17
    engine::{
18
        InitializedRasterOperator, OperatorName, RasterQueryProcessor, RasterResultDescriptor,
19
        TypedRasterQueryProcessor,
20
    },
21
    error,
22
    util::Result,
23
};
24
use async_trait::async_trait;
25
use geoengine_datatypes::primitives::{RasterQueryRectangle, SpatialPartition2D, TimeInstance};
26
use geoengine_datatypes::raster::{Pixel, RasterDataType, RasterTile2D};
27
use geoengine_datatypes::{primitives::TimeStep, raster::TilingSpecification};
28
use log::debug;
29
use serde::{Deserialize, Serialize};
30
use snafu::ensure;
31
use std::marker::PhantomData;
32

33
use typetag;
34

35
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
19✔
36
#[serde(rename_all = "camelCase")]
37
pub struct TemporalRasterAggregationParameters {
38
    pub aggregation: Aggregation,
39
    pub window: TimeStep,
40
    /// Define an anchor point for `window`
41
    /// If `None`, the anchor point is `1970-01-01T00:00:00Z` by default
42
    pub window_reference: Option<TimeInstance>,
43
    /// If specified, this will be the output type.
44
    /// If not, the output type will be the same as the input type.
45
    pub output_type: Option<RasterDataType>,
46
}
47

48
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
19✔
49
#[serde(rename_all = "camelCase")]
50
#[serde(tag = "type")]
51
pub enum Aggregation {
52
    #[serde(rename_all = "camelCase")]
53
    Min { ignore_no_data: bool },
54
    #[serde(rename_all = "camelCase")]
55
    Max { ignore_no_data: bool },
56
    #[serde(rename_all = "camelCase")]
57
    First { ignore_no_data: bool },
58
    #[serde(rename_all = "camelCase")]
59
    Last { ignore_no_data: bool },
60
    #[serde(rename_all = "camelCase")]
61
    Mean { ignore_no_data: bool },
62
    #[serde(rename_all = "camelCase")]
63
    Sum { ignore_no_data: bool },
64
    #[serde(rename_all = "camelCase")]
65
    Count { ignore_no_data: bool },
66
}
67

68
pub type TemporalRasterAggregation =
69
    Operator<TemporalRasterAggregationParameters, SingleRasterSource>;
70

71
impl OperatorName for TemporalRasterAggregation {
72
    const TYPE_NAME: &'static str = "TemporalRasterAggregation";
73
}
74

75
#[typetag::serde]
×
76
#[async_trait]
77
impl RasterOperator for TemporalRasterAggregation {
78
    async fn _initialize(
19✔
79
        self: Box<Self>,
19✔
80
        path: WorkflowOperatorPath,
19✔
81
        context: &dyn ExecutionContext,
19✔
82
    ) -> Result<Box<dyn InitializedRasterOperator>> {
19✔
83
        ensure!(self.params.window.step > 0, error::WindowSizeMustNotBeZero);
19✔
84

85
        let name = CanonicOperatorName::from(&self);
19✔
86

87
        let initialized_source = self.sources.initialize_sources(path, context).await?;
19✔
88
        let source = initialized_source.raster;
19✔
89

19✔
90
        debug!(
19✔
91
            "Initializing TemporalRasterAggregation with {:?}.",
×
92
            &self.params
×
93
        );
94

95
        let initialized_operator = InitializedTemporalRasterAggregation {
19✔
96
            name,
19✔
97
            aggregation_type: self.params.aggregation,
19✔
98
            window: self.params.window,
19✔
99
            window_reference: self
19✔
100
                .params
19✔
101
                .window_reference
19✔
102
                .unwrap_or(TimeInstance::EPOCH_START),
19✔
103
            result_descriptor: source.result_descriptor().clone(),
19✔
104
            source,
19✔
105
            tiling_specification: context.tiling_specification(),
19✔
106
            output_type: self.params.output_type,
19✔
107
        };
19✔
108

19✔
109
        Ok(initialized_operator.boxed())
19✔
110
    }
38✔
111

112
    span_fn!(TemporalRasterAggregation);
×
113
}
114

115
pub struct InitializedTemporalRasterAggregation {
116
    name: CanonicOperatorName,
117
    aggregation_type: Aggregation,
118
    window: TimeStep,
119
    window_reference: TimeInstance,
120
    source: Box<dyn InitializedRasterOperator>,
121
    result_descriptor: RasterResultDescriptor,
122
    tiling_specification: TilingSpecification,
123
    output_type: Option<RasterDataType>,
124
}
125

126
impl InitializedRasterOperator for InitializedTemporalRasterAggregation {
127
    fn result_descriptor(&self) -> &RasterResultDescriptor {
×
128
        &self.result_descriptor
×
129
    }
×
130

131
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor> {
19✔
132
        let source_processor = self.source.query_processor()?;
19✔
133

134
        let source_processor: TypedRasterQueryProcessor = match self.output_type {
19✔
135
            Some(RasterDataType::U8) => source_processor.into_u8().into(),
×
136
            Some(RasterDataType::U16) => source_processor.into_u16().into(),
1✔
137
            Some(RasterDataType::U32) => source_processor.into_u32().into(),
×
138
            Some(RasterDataType::U64) => source_processor.into_u64().into(),
×
139
            Some(RasterDataType::I8) => source_processor.into_i8().into(),
×
140
            Some(RasterDataType::I16) => source_processor.into_i16().into(),
×
141
            Some(RasterDataType::I32) => source_processor.into_i32().into(),
×
142
            Some(RasterDataType::I64) => source_processor.into_i64().into(),
×
143
            Some(RasterDataType::F32) => source_processor.into_f32().into(),
×
144
            Some(RasterDataType::F64) => source_processor.into_f64().into(),
×
145
            // use the same output type as the input type
146
            None => source_processor,
18✔
147
        };
148

149
        let res = call_on_generic_raster_processor!(
19✔
150
            source_processor, p =>
19✔
151
            TemporalRasterAggregationProcessor::new(
152
                self.aggregation_type,
19✔
153
                self.window,
19✔
154
                self.window_reference,
19✔
155
                p,
19✔
156
                self.tiling_specification,
19✔
157
            ).boxed()
19✔
158
            .into()
19✔
159
        );
160

161
        Ok(res)
19✔
162
    }
19✔
163

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

169
pub struct TemporalRasterAggregationProcessor<Q, P>
170
where
171
    Q: RasterQueryProcessor<RasterType = P>,
172
    P: Pixel,
173
{
174
    aggregation_type: Aggregation,
175
    window: TimeStep,
176
    window_reference: TimeInstance,
177
    source: Q,
178
    tiling_specification: TilingSpecification,
179
}
180

181
impl<Q, P> TemporalRasterAggregationProcessor<Q, P>
182
where
183
    Q: RasterQueryProcessor<RasterType = P>,
184
    P: Pixel,
185
{
186
    fn new(
19✔
187
        aggregation_type: Aggregation,
19✔
188
        window: TimeStep,
19✔
189
        window_reference: TimeInstance,
19✔
190
        source: Q,
19✔
191
        tiling_specification: TilingSpecification,
19✔
192
    ) -> Self {
19✔
193
        Self {
19✔
194
            aggregation_type,
19✔
195
            window,
19✔
196
            window_reference,
19✔
197
            source,
19✔
198
            tiling_specification,
19✔
199
        }
19✔
200
    }
19✔
201

202
    fn create_subquery<F: TemporalRasterPixelAggregator<P> + 'static, FoldFn>(
16✔
203
        &self,
16✔
204
        fold_fn: FoldFn,
16✔
205
    ) -> super::subquery::TemporalRasterAggregationSubQuery<FoldFn, P, F> {
16✔
206
        super::subquery::TemporalRasterAggregationSubQuery {
16✔
207
            fold_fn,
16✔
208
            step: self.window,
16✔
209
            step_reference: self.window_reference,
16✔
210
            _phantom_pixel_type: PhantomData,
16✔
211
        }
16✔
212
    }
16✔
213

214
    fn create_subquery_first<F>(
1✔
215
        &self,
1✔
216
        fold_fn: F,
1✔
217
    ) -> TemporalRasterAggregationSubQueryNoDataOnly<F, P> {
1✔
218
        TemporalRasterAggregationSubQueryNoDataOnly {
1✔
219
            fold_fn,
1✔
220
            step: self.window,
1✔
221
            step_reference: self.window_reference,
1✔
222
            _phantom_pixel_type: PhantomData,
1✔
223
        }
1✔
224
    }
1✔
225

226
    fn create_subquery_last<F>(
2✔
227
        &self,
2✔
228
        fold_fn: F,
2✔
229
    ) -> TemporalRasterAggregationSubQueryNoDataOnly<F, P> {
2✔
230
        TemporalRasterAggregationSubQueryNoDataOnly {
2✔
231
            fold_fn,
2✔
232
            step: self.window,
2✔
233
            step_reference: self.window_reference,
2✔
234
            _phantom_pixel_type: PhantomData,
2✔
235
        }
2✔
236
    }
2✔
237
}
238

239
#[async_trait]
240
impl<Q, P> QueryProcessor for TemporalRasterAggregationProcessor<Q, P>
241
where
242
    Q: QueryProcessor<Output = RasterTile2D<P>, SpatialBounds = SpatialPartition2D>,
243
    P: Pixel,
244
{
245
    type Output = RasterTile2D<P>;
246
    type SpatialBounds = SpatialPartition2D;
247

248
    #[allow(clippy::too_many_lines)]
249
    async fn _query<'a>(
19✔
250
        &'a self,
19✔
251
        query: RasterQueryRectangle,
19✔
252
        ctx: &'a dyn crate::engine::QueryContext,
19✔
253
    ) -> Result<futures::stream::BoxStream<'a, Result<Self::Output>>> {
19✔
254
        match self.aggregation_type {
19✔
255
            Aggregation::Min {
256
                ignore_no_data: true,
257
            } => Ok(self
×
258
                .create_subquery(
×
259
                    super::subquery::subquery_all_tiles_fold_fn::<
×
260
                        P,
×
261
                        MinPixelAggregatorIngoringNoData,
×
262
                    >,
×
263
                )
×
264
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
×
265
                .expect("no tiles must be skipped in Aggregation::Min")),
×
266
            Aggregation::Min {
267
                ignore_no_data: false,
268
            } => Ok(self
1✔
269
                .create_subquery(
1✔
270
                    super::subquery::subquery_all_tiles_fold_fn::<P, MinPixelAggregator>,
1✔
271
                )
1✔
272
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
273
                .expect("no tiles must be skipped in Aggregation::Min")),
1✔
274
            Aggregation::Max {
275
                ignore_no_data: true,
276
            } => Ok(self
1✔
277
                .create_subquery(
1✔
278
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
279
                        P,
1✔
280
                        MaxPixelAggregatorIngoringNoData,
1✔
281
                    >,
1✔
282
                )
1✔
283
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
284
                .expect("no tiles must be skipped in Aggregation::Max")),
1✔
285
            Aggregation::Max {
286
                ignore_no_data: false,
287
            } => Ok(self
3✔
288
                .create_subquery(
3✔
289
                    super::subquery::subquery_all_tiles_fold_fn::<P, MaxPixelAggregator>,
3✔
290
                )
3✔
291
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
3✔
292
                .expect("no tiles must be skipped in Aggregation::Max")),
3✔
293

294
            Aggregation::First {
295
                ignore_no_data: true,
296
            } => Ok(self
1✔
297
                .create_subquery(
1✔
298
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
299
                        P,
1✔
300
                        FirstPixelAggregatorIngoringNoData,
1✔
301
                    >,
1✔
302
                )
1✔
303
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
304
                .expect("no tiles must be skipped in Aggregation::First")),
1✔
305
            Aggregation::First {
306
                ignore_no_data: false,
307
            } => Ok(self
1✔
308
                .create_subquery_first(first_tile_fold_future::<P>)
1✔
309
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
310
                .expect("no tiles must be skipped in Aggregation::First")),
1✔
311
            Aggregation::Last {
312
                ignore_no_data: true,
313
            } => Ok(self
1✔
314
                .create_subquery(
1✔
315
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
316
                        P,
1✔
317
                        LastPixelAggregatorIngoringNoData,
1✔
318
                    >,
1✔
319
                )
1✔
320
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
321
                .expect("no tiles must be skipped in Aggregation::Last")),
1✔
322

323
            Aggregation::Last {
324
                ignore_no_data: false,
325
            } => Ok(self
2✔
326
                .create_subquery_last(last_tile_fold_future::<P>)
2✔
327
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
2✔
328
                .expect("no tiles must be skipped in Aggregation::Last")),
2✔
329

330
            Aggregation::Mean {
331
                ignore_no_data: true,
332
            } => Ok(self
1✔
333
                .create_subquery(
1✔
334
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
335
                        P,
1✔
336
                        MeanPixelAggregatorIngoringNoData,
1✔
337
                    >,
1✔
338
                )
1✔
339
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
340
                .expect("no tiles must be skipped in Aggregation::Mean")),
1✔
341

342
            Aggregation::Mean {
343
                ignore_no_data: false,
344
            } => Ok(self
1✔
345
                .create_subquery(
1✔
346
                    super::subquery::subquery_all_tiles_fold_fn::<P, MeanPixelAggregator>,
1✔
347
                )
1✔
348
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
349
                .expect("no tiles must be skipped in Aggregation::Mean")),
1✔
350

351
            Aggregation::Sum {
352
                ignore_no_data: true,
353
            } => Ok(self
1✔
354
                .create_subquery(
1✔
355
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
356
                        P,
1✔
357
                        SumPixelAggregatorIngoringNoData,
1✔
358
                    >,
1✔
359
                )
1✔
360
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
361
                .expect("no tiles must be skipped in Aggregation::Sum")),
1✔
362

363
            Aggregation::Sum {
364
                ignore_no_data: false,
365
            } => Ok(self
3✔
366
                .create_subquery(
3✔
367
                    super::subquery::subquery_all_tiles_fold_fn::<P, SumPixelAggregator>,
3✔
368
                )
3✔
369
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
3✔
370
                .expect("no tiles must be skipped in Aggregation::Sum")),
3✔
371

372
            Aggregation::Count {
373
                ignore_no_data: true,
374
            } => Ok(self
1✔
375
                .create_subquery(
1✔
376
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
377
                        P,
1✔
378
                        CountPixelAggregatorIngoringNoData,
1✔
379
                    >,
1✔
380
                )
1✔
381
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
382
                .expect("no tiles must be skipped in Aggregation::Sum")),
1✔
383

384
            Aggregation::Count {
385
                ignore_no_data: false,
386
            } => Ok(self
2✔
387
                .create_subquery(
2✔
388
                    super::subquery::subquery_all_tiles_fold_fn::<P, CountPixelAggregator>,
2✔
389
                )
2✔
390
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
2✔
391
                .expect("no tiles must be skipped in Aggregation::Sum")),
2✔
392
        }
393
    }
38✔
394
}
395

396
#[cfg(test)]
397
mod tests {
398
    use futures::stream::StreamExt;
399
    use geoengine_datatypes::{
400
        primitives::{Measurement, SpatialResolution, TimeInterval},
401
        raster::{
402
            EmptyGrid, EmptyGrid2D, Grid2D, GridOrEmpty, MaskedGrid2D, RasterDataType,
403
            TileInformation,
404
        },
405
        spatial_reference::SpatialReference,
406
        util::test::TestDefault,
407
    };
408

409
    use crate::{
410
        engine::{MockExecutionContext, MockQueryContext},
411
        mock::{MockRasterSource, MockRasterSourceParams},
412
        processing::{Expression, ExpressionParams, ExpressionSources},
413
    };
414

415
    use super::*;
416

417
    #[tokio::test]
1✔
418
    #[allow(clippy::too_many_lines)]
419
    async fn test_min() {
1✔
420
        let raster_tiles = make_raster();
1✔
421

1✔
422
        let mrs = MockRasterSource {
1✔
423
            params: MockRasterSourceParams {
1✔
424
                data: raster_tiles,
1✔
425
                result_descriptor: RasterResultDescriptor {
1✔
426
                    data_type: RasterDataType::U8,
1✔
427
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
428
                    measurement: Measurement::Unitless,
1✔
429
                    time: None,
1✔
430
                    bbox: None,
1✔
431
                    resolution: None,
1✔
432
                },
1✔
433
            },
1✔
434
        }
1✔
435
        .boxed();
1✔
436

1✔
437
        let agg = TemporalRasterAggregation {
1✔
438
            params: TemporalRasterAggregationParameters {
1✔
439
                aggregation: Aggregation::Min {
1✔
440
                    ignore_no_data: false,
1✔
441
                },
1✔
442
                window: TimeStep {
1✔
443
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
444
                    step: 20,
1✔
445
                },
1✔
446
                window_reference: None,
1✔
447
                output_type: None,
1✔
448
            },
1✔
449
            sources: SingleRasterSource { raster: mrs },
1✔
450
        }
1✔
451
        .boxed();
1✔
452

1✔
453
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
454
            (0., 0.).into(),
1✔
455
            [3, 2].into(),
1✔
456
        ));
1✔
457
        let query_rect = RasterQueryRectangle {
1✔
458
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
459
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
460
            spatial_resolution: SpatialResolution::one(),
1✔
461
        };
1✔
462
        let query_ctx = MockQueryContext::test_default();
1✔
463

464
        let qp = agg
1✔
465
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
466
            .await
×
467
            .unwrap()
1✔
468
            .query_processor()
1✔
469
            .unwrap()
1✔
470
            .get_u8()
1✔
471
            .unwrap();
1✔
472

473
        let result = qp
1✔
474
            .query(query_rect, &query_ctx)
1✔
475
            .await
×
476
            .unwrap()
1✔
477
            .collect::<Vec<_>>()
1✔
478
            .await;
8✔
479

480
        assert_eq!(result.len(), 4);
1✔
481

482
        assert_eq!(
1✔
483
            result[0].as_ref().unwrap(),
1✔
484
            &RasterTile2D::new_with_tile_info(
1✔
485
                TimeInterval::new_unchecked(0, 20),
1✔
486
                TileInformation {
1✔
487
                    global_tile_position: [-1, 0].into(),
1✔
488
                    tile_size_in_pixels: [3, 2].into(),
1✔
489
                    global_geo_transform: TestDefault::test_default(),
1✔
490
                },
1✔
491
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
492
            )
1✔
493
        );
1✔
494

495
        assert_eq!(
1✔
496
            result[1].as_ref().unwrap(),
1✔
497
            &RasterTile2D::new_with_tile_info(
1✔
498
                TimeInterval::new_unchecked(0, 20),
1✔
499
                TileInformation {
1✔
500
                    global_tile_position: [-1, 1].into(),
1✔
501
                    tile_size_in_pixels: [3, 2].into(),
1✔
502
                    global_geo_transform: TestDefault::test_default(),
1✔
503
                },
1✔
504
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
1✔
505
            )
1✔
506
        );
1✔
507

508
        assert_eq!(
1✔
509
            result[2].as_ref().unwrap(),
1✔
510
            &RasterTile2D::new_with_tile_info(
1✔
511
                TimeInterval::new_unchecked(20, 40),
1✔
512
                TileInformation {
1✔
513
                    global_tile_position: [-1, 0].into(),
1✔
514
                    tile_size_in_pixels: [3, 2].into(),
1✔
515
                    global_geo_transform: TestDefault::test_default(),
1✔
516
                },
1✔
517
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
518
            )
1✔
519
        );
1✔
520

521
        assert_eq!(
1✔
522
            result[3].as_ref().unwrap(),
1✔
523
            &RasterTile2D::new_with_tile_info(
1✔
524
                TimeInterval::new_unchecked(20, 40),
1✔
525
                TileInformation {
1✔
526
                    global_tile_position: [-1, 1].into(),
1✔
527
                    tile_size_in_pixels: [3, 2].into(),
1✔
528
                    global_geo_transform: TestDefault::test_default(),
1✔
529
                },
1✔
530
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
1✔
531
            )
1✔
532
        );
1✔
533
    }
534

535
    #[tokio::test]
1✔
536
    #[allow(clippy::too_many_lines)]
537
    async fn test_max() {
1✔
538
        let raster_tiles = make_raster();
1✔
539

1✔
540
        let mrs = MockRasterSource {
1✔
541
            params: MockRasterSourceParams {
1✔
542
                data: raster_tiles,
1✔
543
                result_descriptor: RasterResultDescriptor {
1✔
544
                    data_type: RasterDataType::U8,
1✔
545
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
546
                    measurement: Measurement::Unitless,
1✔
547
                    time: None,
1✔
548
                    bbox: None,
1✔
549
                    resolution: None,
1✔
550
                },
1✔
551
            },
1✔
552
        }
1✔
553
        .boxed();
1✔
554

1✔
555
        let agg = TemporalRasterAggregation {
1✔
556
            params: TemporalRasterAggregationParameters {
1✔
557
                aggregation: Aggregation::Max {
1✔
558
                    ignore_no_data: false,
1✔
559
                },
1✔
560
                window: TimeStep {
1✔
561
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
562
                    step: 20,
1✔
563
                },
1✔
564
                window_reference: None,
1✔
565
                output_type: None,
1✔
566
            },
1✔
567
            sources: SingleRasterSource { raster: mrs },
1✔
568
        }
1✔
569
        .boxed();
1✔
570

1✔
571
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
572
            (0., 0.).into(),
1✔
573
            [3, 2].into(),
1✔
574
        ));
1✔
575
        let query_rect = RasterQueryRectangle {
1✔
576
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
577
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
578
            spatial_resolution: SpatialResolution::one(),
1✔
579
        };
1✔
580
        let query_ctx = MockQueryContext::test_default();
1✔
581

582
        let qp = agg
1✔
583
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
584
            .await
×
585
            .unwrap()
1✔
586
            .query_processor()
1✔
587
            .unwrap()
1✔
588
            .get_u8()
1✔
589
            .unwrap();
1✔
590

591
        let result = qp
1✔
592
            .query(query_rect, &query_ctx)
1✔
593
            .await
×
594
            .unwrap()
1✔
595
            .collect::<Vec<_>>()
1✔
596
            .await;
8✔
597

598
        assert_eq!(result.len(), 4);
1✔
599

600
        assert_eq!(
1✔
601
            result[0].as_ref().unwrap(),
1✔
602
            &RasterTile2D::new_with_tile_info(
1✔
603
                TimeInterval::new_unchecked(0, 20),
1✔
604
                TileInformation {
1✔
605
                    global_tile_position: [-1, 0].into(),
1✔
606
                    tile_size_in_pixels: [3, 2].into(),
1✔
607
                    global_geo_transform: TestDefault::test_default(),
1✔
608
                },
1✔
609
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
610
            )
1✔
611
        );
1✔
612

613
        assert_eq!(
1✔
614
            result[1].as_ref().unwrap(),
1✔
615
            &RasterTile2D::new_with_tile_info(
1✔
616
                TimeInterval::new_unchecked(0, 20),
1✔
617
                TileInformation {
1✔
618
                    global_tile_position: [-1, 1].into(),
1✔
619
                    tile_size_in_pixels: [3, 2].into(),
1✔
620
                    global_geo_transform: TestDefault::test_default(),
1✔
621
                },
1✔
622
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
623
            )
1✔
624
        );
1✔
625

626
        assert_eq!(
1✔
627
            result[2].as_ref().unwrap(),
1✔
628
            &RasterTile2D::new_with_tile_info(
1✔
629
                TimeInterval::new_unchecked(20, 40),
1✔
630
                TileInformation {
1✔
631
                    global_tile_position: [-1, 0].into(),
1✔
632
                    tile_size_in_pixels: [3, 2].into(),
1✔
633
                    global_geo_transform: TestDefault::test_default(),
1✔
634
                },
1✔
635
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
636
            )
1✔
637
        );
1✔
638

639
        assert_eq!(
1✔
640
            result[3].as_ref().unwrap(),
1✔
641
            &RasterTile2D::new_with_tile_info(
1✔
642
                TimeInterval::new_unchecked(20, 40),
1✔
643
                TileInformation {
1✔
644
                    global_tile_position: [-1, 1].into(),
1✔
645
                    tile_size_in_pixels: [3, 2].into(),
1✔
646
                    global_geo_transform: TestDefault::test_default(),
1✔
647
                },
1✔
648
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
649
            )
1✔
650
        );
1✔
651
    }
652

653
    #[tokio::test]
1✔
654
    #[allow(clippy::too_many_lines)]
655
    async fn test_max_with_no_data() {
1✔
656
        let raster_tiles = make_raster(); // TODO: switch to make_raster_with_no_data?
1✔
657

1✔
658
        let mrs = MockRasterSource {
1✔
659
            params: MockRasterSourceParams {
1✔
660
                data: raster_tiles,
1✔
661
                result_descriptor: RasterResultDescriptor {
1✔
662
                    data_type: RasterDataType::U8,
1✔
663
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
664
                    measurement: Measurement::Unitless,
1✔
665
                    time: None,
1✔
666
                    bbox: None,
1✔
667
                    resolution: None,
1✔
668
                },
1✔
669
            },
1✔
670
        }
1✔
671
        .boxed();
1✔
672

1✔
673
        let agg = TemporalRasterAggregation {
1✔
674
            params: TemporalRasterAggregationParameters {
1✔
675
                aggregation: Aggregation::Max {
1✔
676
                    ignore_no_data: false,
1✔
677
                },
1✔
678
                window: TimeStep {
1✔
679
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
680
                    step: 20,
1✔
681
                },
1✔
682
                window_reference: None,
1✔
683
                output_type: None,
1✔
684
            },
1✔
685
            sources: SingleRasterSource { raster: mrs },
1✔
686
        }
1✔
687
        .boxed();
1✔
688

1✔
689
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
690
            (0., 0.).into(),
1✔
691
            [3, 2].into(),
1✔
692
        ));
1✔
693
        let query_rect = RasterQueryRectangle {
1✔
694
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
695
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
696
            spatial_resolution: SpatialResolution::one(),
1✔
697
        };
1✔
698
        let query_ctx = MockQueryContext::test_default();
1✔
699

700
        let qp = agg
1✔
701
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
702
            .await
×
703
            .unwrap()
1✔
704
            .query_processor()
1✔
705
            .unwrap()
1✔
706
            .get_u8()
1✔
707
            .unwrap();
1✔
708

709
        let result = qp
1✔
710
            .query(query_rect, &query_ctx)
1✔
711
            .await
×
712
            .unwrap()
1✔
713
            .collect::<Vec<_>>()
1✔
714
            .await;
8✔
715

716
        assert_eq!(result.len(), 4);
1✔
717

718
        assert_eq!(
1✔
719
            result[0].as_ref().unwrap(),
1✔
720
            &RasterTile2D::new_with_tile_info(
1✔
721
                TimeInterval::new_unchecked(0, 20),
1✔
722
                TileInformation {
1✔
723
                    global_tile_position: [-1, 0].into(),
1✔
724
                    tile_size_in_pixels: [3, 2].into(),
1✔
725
                    global_geo_transform: TestDefault::test_default(),
1✔
726
                },
1✔
727
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7],).unwrap()),
1✔
728
            )
1✔
729
        );
1✔
730

731
        assert_eq!(
1✔
732
            result[1].as_ref().unwrap(),
1✔
733
            &RasterTile2D::new_with_tile_info(
1✔
734
                TimeInterval::new_unchecked(0, 20),
1✔
735
                TileInformation {
1✔
736
                    global_tile_position: [-1, 1].into(),
1✔
737
                    tile_size_in_pixels: [3, 2].into(),
1✔
738
                    global_geo_transform: TestDefault::test_default(),
1✔
739
                },
1✔
740
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
741
            )
1✔
742
        );
1✔
743

744
        assert_eq!(
1✔
745
            result[2].as_ref().unwrap(),
1✔
746
            &RasterTile2D::new_with_tile_info(
1✔
747
                TimeInterval::new_unchecked(20, 40),
1✔
748
                TileInformation {
1✔
749
                    global_tile_position: [-1, 0].into(),
1✔
750
                    tile_size_in_pixels: [3, 2].into(),
1✔
751
                    global_geo_transform: TestDefault::test_default(),
1✔
752
                },
1✔
753
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
754
            )
1✔
755
        );
1✔
756

757
        assert_eq!(
1✔
758
            result[3].as_ref().unwrap(),
1✔
759
            &RasterTile2D::new_with_tile_info(
1✔
760
                TimeInterval::new_unchecked(20, 40),
1✔
761
                TileInformation {
1✔
762
                    global_tile_position: [-1, 1].into(),
1✔
763
                    tile_size_in_pixels: [3, 2].into(),
1✔
764
                    global_geo_transform: TestDefault::test_default(),
1✔
765
                },
1✔
766
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
767
            )
1✔
768
        );
1✔
769
    }
770

771
    #[tokio::test]
1✔
772
    #[allow(clippy::too_many_lines)]
773
    async fn test_max_with_no_data_but_ignoring_it() {
1✔
774
        let raster_tiles = make_raster(); // TODO: switch to make_raster_with_no_data?
1✔
775

1✔
776
        let mrs = MockRasterSource {
1✔
777
            params: MockRasterSourceParams {
1✔
778
                data: raster_tiles,
1✔
779
                result_descriptor: RasterResultDescriptor {
1✔
780
                    data_type: RasterDataType::U8,
1✔
781
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
782
                    measurement: Measurement::Unitless,
1✔
783
                    time: None,
1✔
784
                    bbox: None,
1✔
785
                    resolution: None,
1✔
786
                },
1✔
787
            },
1✔
788
        }
1✔
789
        .boxed();
1✔
790

1✔
791
        let agg = TemporalRasterAggregation {
1✔
792
            params: TemporalRasterAggregationParameters {
1✔
793
                aggregation: Aggregation::Max {
1✔
794
                    ignore_no_data: true,
1✔
795
                },
1✔
796
                window: TimeStep {
1✔
797
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
798
                    step: 20,
1✔
799
                },
1✔
800
                window_reference: None,
1✔
801
                output_type: None,
1✔
802
            },
1✔
803
            sources: SingleRasterSource { raster: mrs },
1✔
804
        }
1✔
805
        .boxed();
1✔
806

1✔
807
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
808
            (0., 0.).into(),
1✔
809
            [3, 2].into(),
1✔
810
        ));
1✔
811
        let query_rect = RasterQueryRectangle {
1✔
812
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
813
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
814
            spatial_resolution: SpatialResolution::one(),
1✔
815
        };
1✔
816
        let query_ctx = MockQueryContext::test_default();
1✔
817

818
        let qp = agg
1✔
819
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
820
            .await
×
821
            .unwrap()
1✔
822
            .query_processor()
1✔
823
            .unwrap()
1✔
824
            .get_u8()
1✔
825
            .unwrap();
1✔
826

827
        let result = qp
1✔
828
            .query(query_rect, &query_ctx)
1✔
829
            .await
×
830
            .unwrap()
1✔
831
            .collect::<Vec<_>>()
1✔
832
            .await;
8✔
833

834
        assert_eq!(result.len(), 4);
1✔
835

836
        assert_eq!(
1✔
837
            result[0].as_ref().unwrap(),
1✔
838
            &RasterTile2D::new_with_tile_info(
1✔
839
                TimeInterval::new_unchecked(0, 20),
1✔
840
                TileInformation {
1✔
841
                    global_tile_position: [-1, 0].into(),
1✔
842
                    tile_size_in_pixels: [3, 2].into(),
1✔
843
                    global_geo_transform: TestDefault::test_default(),
1✔
844
                },
1✔
845
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
846
            )
1✔
847
        );
1✔
848

849
        assert_eq!(
1✔
850
            result[1].as_ref().unwrap(),
1✔
851
            &RasterTile2D::new_with_tile_info(
1✔
852
                TimeInterval::new_unchecked(0, 20),
1✔
853
                TileInformation {
1✔
854
                    global_tile_position: [-1, 1].into(),
1✔
855
                    tile_size_in_pixels: [3, 2].into(),
1✔
856
                    global_geo_transform: TestDefault::test_default(),
1✔
857
                },
1✔
858
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
859
            )
1✔
860
        );
1✔
861

862
        assert_eq!(
1✔
863
            result[2].as_ref().unwrap(),
1✔
864
            &RasterTile2D::new_with_tile_info(
1✔
865
                TimeInterval::new_unchecked(20, 40),
1✔
866
                TileInformation {
1✔
867
                    global_tile_position: [-1, 0].into(),
1✔
868
                    tile_size_in_pixels: [3, 2].into(),
1✔
869
                    global_geo_transform: TestDefault::test_default(),
1✔
870
                },
1✔
871
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
872
            )
1✔
873
        );
1✔
874

875
        assert_eq!(
1✔
876
            result[3].as_ref().unwrap(),
1✔
877
            &RasterTile2D::new_with_tile_info(
1✔
878
                TimeInterval::new_unchecked(20, 40),
1✔
879
                TileInformation {
1✔
880
                    global_tile_position: [-1, 1].into(),
1✔
881
                    tile_size_in_pixels: [3, 2].into(),
1✔
882
                    global_geo_transform: TestDefault::test_default(),
1✔
883
                },
1✔
884
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
885
            )
1✔
886
        );
1✔
887
    }
888

889
    #[tokio::test]
1✔
890
    #[allow(clippy::too_many_lines)]
891
    async fn test_only_no_data() {
1✔
892
        let mrs = MockRasterSource {
1✔
893
            params: MockRasterSourceParams {
1✔
894
                data: vec![RasterTile2D::new_with_tile_info(
1✔
895
                    TimeInterval::new_unchecked(0, 20),
1✔
896
                    TileInformation {
1✔
897
                        global_tile_position: [-1, 0].into(),
1✔
898
                        tile_size_in_pixels: [3, 2].into(),
1✔
899
                        global_geo_transform: TestDefault::test_default(),
1✔
900
                    },
1✔
901
                    GridOrEmpty::from(EmptyGrid2D::<u8>::new([3, 2].into())),
1✔
902
                )],
1✔
903
                result_descriptor: RasterResultDescriptor {
1✔
904
                    data_type: RasterDataType::U8,
1✔
905
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
906
                    measurement: Measurement::Unitless,
1✔
907
                    time: None,
1✔
908
                    bbox: None,
1✔
909
                    resolution: None,
1✔
910
                },
1✔
911
            },
1✔
912
        }
1✔
913
        .boxed();
1✔
914

1✔
915
        let agg = TemporalRasterAggregation {
1✔
916
            params: TemporalRasterAggregationParameters {
1✔
917
                aggregation: Aggregation::Max {
1✔
918
                    ignore_no_data: false,
1✔
919
                },
1✔
920
                window: TimeStep {
1✔
921
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
922
                    step: 20,
1✔
923
                },
1✔
924
                window_reference: None,
1✔
925
                output_type: None,
1✔
926
            },
1✔
927
            sources: SingleRasterSource { raster: mrs },
1✔
928
        }
1✔
929
        .boxed();
1✔
930

1✔
931
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
932
            (0., 0.).into(),
1✔
933
            [3, 2].into(),
1✔
934
        ));
1✔
935
        let query_rect = RasterQueryRectangle {
1✔
936
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (2., 0.).into()),
1✔
937
            time_interval: TimeInterval::new_unchecked(0, 20),
1✔
938
            spatial_resolution: SpatialResolution::one(),
1✔
939
        };
1✔
940
        let query_ctx = MockQueryContext::test_default();
1✔
941

942
        let qp = agg
1✔
943
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
944
            .await
×
945
            .unwrap()
1✔
946
            .query_processor()
1✔
947
            .unwrap()
1✔
948
            .get_u8()
1✔
949
            .unwrap();
1✔
950

951
        let result = qp
1✔
952
            .query(query_rect, &query_ctx)
1✔
953
            .await
×
954
            .unwrap()
1✔
955
            .collect::<Vec<_>>()
1✔
956
            .await;
1✔
957

958
        assert_eq!(result.len(), 1);
1✔
959

960
        assert_eq!(
1✔
961
            result[0].as_ref().unwrap(),
1✔
962
            &RasterTile2D::new_with_tile_info(
1✔
963
                TimeInterval::new_unchecked(0, 20),
1✔
964
                TileInformation {
1✔
965
                    global_tile_position: [-1, 0].into(),
1✔
966
                    tile_size_in_pixels: [3, 2].into(),
1✔
967
                    global_geo_transform: TestDefault::test_default(),
1✔
968
                },
1✔
969
                GridOrEmpty::Empty(EmptyGrid::new([3, 2].into())),
1✔
970
            )
1✔
971
        );
1✔
972
    }
973

974
    #[tokio::test]
1✔
975
    async fn test_first_with_no_data() {
1✔
976
        let raster_tiles = make_raster_with_no_data();
1✔
977

1✔
978
        let mrs = MockRasterSource {
1✔
979
            params: MockRasterSourceParams {
1✔
980
                data: raster_tiles,
1✔
981
                result_descriptor: RasterResultDescriptor {
1✔
982
                    data_type: RasterDataType::U8,
1✔
983
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
984
                    measurement: Measurement::Unitless,
1✔
985
                    time: None,
1✔
986
                    bbox: None,
1✔
987
                    resolution: None,
1✔
988
                },
1✔
989
            },
1✔
990
        }
1✔
991
        .boxed();
1✔
992

1✔
993
        let agg = TemporalRasterAggregation {
1✔
994
            params: TemporalRasterAggregationParameters {
1✔
995
                aggregation: Aggregation::First {
1✔
996
                    ignore_no_data: true,
1✔
997
                },
1✔
998
                window: TimeStep {
1✔
999
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1000
                    step: 30,
1✔
1001
                },
1✔
1002
                window_reference: None,
1✔
1003
                output_type: None,
1✔
1004
            },
1✔
1005
            sources: SingleRasterSource { raster: mrs },
1✔
1006
        }
1✔
1007
        .boxed();
1✔
1008

1✔
1009
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1010
            (0., 0.).into(),
1✔
1011
            [3, 2].into(),
1✔
1012
        ));
1✔
1013
        let query_rect = RasterQueryRectangle {
1✔
1014
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1015
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1016
            spatial_resolution: SpatialResolution::one(),
1✔
1017
        };
1✔
1018
        let query_ctx = MockQueryContext::test_default();
1✔
1019

1020
        let qp = agg
1✔
1021
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1022
            .await
×
1023
            .unwrap()
1✔
1024
            .query_processor()
1✔
1025
            .unwrap()
1✔
1026
            .get_u8()
1✔
1027
            .unwrap();
1✔
1028

1029
        let result = qp
1✔
1030
            .query(query_rect, &query_ctx)
1✔
1031
            .await
×
1032
            .unwrap()
1✔
1033
            .collect::<Vec<_>>()
1✔
1034
            .await;
6✔
1035

1036
        assert_eq!(result.len(), 2);
1✔
1037

1038
        assert_eq!(
1✔
1039
            result[0].as_ref().unwrap(),
1✔
1040
            &RasterTile2D::new_with_tile_info(
1✔
1041
                TimeInterval::new_unchecked(0, 30),
1✔
1042
                TileInformation {
1✔
1043
                    global_tile_position: [-1, 0].into(),
1✔
1044
                    tile_size_in_pixels: [3, 2].into(),
1✔
1045
                    global_geo_transform: TestDefault::test_default(),
1✔
1046
                },
1✔
1047
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 16, 11, 12]).unwrap()),
1✔
1048
            )
1✔
1049
        );
1✔
1050

1051
        assert_eq!(
1✔
1052
            result[1].as_ref().unwrap(),
1✔
1053
            &RasterTile2D::new_with_tile_info(
1✔
1054
                TimeInterval::new_unchecked(0, 30),
1✔
1055
                TileInformation {
1✔
1056
                    global_tile_position: [-1, 1].into(),
1✔
1057
                    tile_size_in_pixels: [3, 2].into(),
1✔
1058
                    global_geo_transform: TestDefault::test_default(),
1✔
1059
                },
1✔
1060
                GridOrEmpty::from(
1✔
1061
                    MaskedGrid2D::new(
1✔
1062
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1063
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1064
                            .unwrap()
1✔
1065
                    )
1✔
1066
                    .unwrap()
1✔
1067
                ),
1✔
1068
            )
1✔
1069
        );
1✔
1070
    }
1071

1072
    #[tokio::test]
1✔
1073
    async fn test_last_with_no_data() {
1✔
1074
        let raster_tiles = make_raster_with_no_data();
1✔
1075

1✔
1076
        let mrs = MockRasterSource {
1✔
1077
            params: MockRasterSourceParams {
1✔
1078
                data: raster_tiles,
1✔
1079
                result_descriptor: RasterResultDescriptor {
1✔
1080
                    data_type: RasterDataType::U8,
1✔
1081
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1082
                    measurement: Measurement::Unitless,
1✔
1083
                    time: None,
1✔
1084
                    bbox: None,
1✔
1085
                    resolution: None,
1✔
1086
                },
1✔
1087
            },
1✔
1088
        }
1✔
1089
        .boxed();
1✔
1090

1✔
1091
        let agg = TemporalRasterAggregation {
1✔
1092
            params: TemporalRasterAggregationParameters {
1✔
1093
                aggregation: Aggregation::Last {
1✔
1094
                    ignore_no_data: true,
1✔
1095
                },
1✔
1096
                window: TimeStep {
1✔
1097
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1098
                    step: 30,
1✔
1099
                },
1✔
1100
                window_reference: None,
1✔
1101
                output_type: None,
1✔
1102
            },
1✔
1103
            sources: SingleRasterSource { raster: mrs },
1✔
1104
        }
1✔
1105
        .boxed();
1✔
1106

1✔
1107
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1108
            (0., 0.).into(),
1✔
1109
            [3, 2].into(),
1✔
1110
        ));
1✔
1111
        let query_rect = RasterQueryRectangle {
1✔
1112
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1113
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1114
            spatial_resolution: SpatialResolution::one(),
1✔
1115
        };
1✔
1116
        let query_ctx = MockQueryContext::test_default();
1✔
1117

1118
        let qp = agg
1✔
1119
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1120
            .await
×
1121
            .unwrap()
1✔
1122
            .query_processor()
1✔
1123
            .unwrap()
1✔
1124
            .get_u8()
1✔
1125
            .unwrap();
1✔
1126

1127
        let result = qp
1✔
1128
            .query(query_rect, &query_ctx)
1✔
1129
            .await
×
1130
            .unwrap()
1✔
1131
            .collect::<Vec<_>>()
1✔
1132
            .await;
6✔
1133

1134
        assert_eq!(result.len(), 2);
1✔
1135

1136
        assert_eq!(
1✔
1137
            result[0].as_ref().unwrap(),
1✔
1138
            &RasterTile2D::new_with_tile_info(
1✔
1139
                TimeInterval::new_unchecked(0, 30),
1✔
1140
                TileInformation {
1✔
1141
                    global_tile_position: [-1, 0].into(),
1✔
1142
                    tile_size_in_pixels: [3, 2].into(),
1✔
1143
                    global_geo_transform: TestDefault::test_default(),
1✔
1144
                },
1✔
1145
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![13, 8, 15, 16, 17, 18]).unwrap()),
1✔
1146
            )
1✔
1147
        );
1✔
1148

1149
        assert_eq!(
1✔
1150
            result[1].as_ref().unwrap(),
1✔
1151
            &RasterTile2D::new_with_tile_info(
1✔
1152
                TimeInterval::new_unchecked(0, 30),
1✔
1153
                TileInformation {
1✔
1154
                    global_tile_position: [-1, 1].into(),
1✔
1155
                    tile_size_in_pixels: [3, 2].into(),
1✔
1156
                    global_geo_transform: TestDefault::test_default(),
1✔
1157
                },
1✔
1158
                GridOrEmpty::from(
1✔
1159
                    MaskedGrid2D::new(
1✔
1160
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1161
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1162
                            .unwrap()
1✔
1163
                    )
1✔
1164
                    .unwrap()
1✔
1165
                ),
1✔
1166
            )
1✔
1167
        );
1✔
1168
    }
1169

1170
    #[tokio::test]
1✔
1171
    async fn test_last() {
1✔
1172
        let raster_tiles = make_raster_with_no_data();
1✔
1173

1✔
1174
        let mrs = MockRasterSource {
1✔
1175
            params: MockRasterSourceParams {
1✔
1176
                data: raster_tiles,
1✔
1177
                result_descriptor: RasterResultDescriptor {
1✔
1178
                    data_type: RasterDataType::U8,
1✔
1179
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1180
                    measurement: Measurement::Unitless,
1✔
1181
                    time: None,
1✔
1182
                    bbox: None,
1✔
1183
                    resolution: None,
1✔
1184
                },
1✔
1185
            },
1✔
1186
        }
1✔
1187
        .boxed();
1✔
1188

1✔
1189
        let agg = TemporalRasterAggregation {
1✔
1190
            params: TemporalRasterAggregationParameters {
1✔
1191
                aggregation: Aggregation::Last {
1✔
1192
                    ignore_no_data: false,
1✔
1193
                },
1✔
1194
                window: TimeStep {
1✔
1195
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1196
                    step: 30,
1✔
1197
                },
1✔
1198
                window_reference: None,
1✔
1199
                output_type: None,
1✔
1200
            },
1✔
1201
            sources: SingleRasterSource { raster: mrs },
1✔
1202
        }
1✔
1203
        .boxed();
1✔
1204

1✔
1205
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1206
            (0., 0.).into(),
1✔
1207
            [3, 2].into(),
1✔
1208
        ));
1✔
1209
        let query_rect = RasterQueryRectangle {
1✔
1210
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1211
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1212
            spatial_resolution: SpatialResolution::one(),
1✔
1213
        };
1✔
1214
        let query_ctx = MockQueryContext::test_default();
1✔
1215

1216
        let qp = agg
1✔
1217
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1218
            .await
×
1219
            .unwrap()
1✔
1220
            .query_processor()
1✔
1221
            .unwrap()
1✔
1222
            .get_u8()
1✔
1223
            .unwrap();
1✔
1224

1225
        let result = qp
1✔
1226
            .query(query_rect, &query_ctx)
1✔
1227
            .await
×
1228
            .unwrap()
1✔
1229
            .collect::<Vec<_>>()
1✔
1230
            .await;
8✔
1231

1232
        assert_eq!(result.len(), 2);
1✔
1233

1234
        assert_eq!(
1✔
1235
            result[0].as_ref().unwrap(),
1✔
1236
            &RasterTile2D::new_with_tile_info(
1✔
1237
                TimeInterval::new_unchecked(0, 30),
1✔
1238
                TileInformation {
1✔
1239
                    global_tile_position: [-1, 0].into(),
1✔
1240
                    tile_size_in_pixels: [3, 2].into(),
1✔
1241
                    global_geo_transform: TestDefault::test_default(),
1✔
1242
                },
1✔
1243
                GridOrEmpty::from(
1✔
1244
                    MaskedGrid2D::new(
1✔
1245
                        Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
1✔
1246
                        Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
1✔
1247
                            .unwrap()
1✔
1248
                    )
1✔
1249
                    .unwrap()
1✔
1250
                )
1✔
1251
            )
1✔
1252
        );
1✔
1253

1254
        assert_eq!(
1✔
1255
            result[1].as_ref().unwrap(),
1✔
1256
            &RasterTile2D::new_with_tile_info(
1✔
1257
                TimeInterval::new_unchecked(0, 30),
1✔
1258
                TileInformation {
1✔
1259
                    global_tile_position: [-1, 1].into(),
1✔
1260
                    tile_size_in_pixels: [3, 2].into(),
1✔
1261
                    global_geo_transform: TestDefault::test_default(),
1✔
1262
                },
1✔
1263
                GridOrEmpty::Empty(EmptyGrid2D::new([3, 2].into()))
1✔
1264
            )
1✔
1265
        );
1✔
1266
    }
1267

1268
    #[tokio::test]
1✔
1269
    async fn test_first() {
1✔
1270
        let raster_tiles = make_raster_with_no_data();
1✔
1271

1✔
1272
        let mrs = MockRasterSource {
1✔
1273
            params: MockRasterSourceParams {
1✔
1274
                data: raster_tiles,
1✔
1275
                result_descriptor: RasterResultDescriptor {
1✔
1276
                    data_type: RasterDataType::U8,
1✔
1277
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1278
                    measurement: Measurement::Unitless,
1✔
1279
                    time: None,
1✔
1280
                    bbox: None,
1✔
1281
                    resolution: None,
1✔
1282
                },
1✔
1283
            },
1✔
1284
        }
1✔
1285
        .boxed();
1✔
1286

1✔
1287
        let agg = TemporalRasterAggregation {
1✔
1288
            params: TemporalRasterAggregationParameters {
1✔
1289
                aggregation: Aggregation::First {
1✔
1290
                    ignore_no_data: false,
1✔
1291
                },
1✔
1292
                window: TimeStep {
1✔
1293
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1294
                    step: 30,
1✔
1295
                },
1✔
1296
                window_reference: None,
1✔
1297
                output_type: None,
1✔
1298
            },
1✔
1299
            sources: SingleRasterSource { raster: mrs },
1✔
1300
        }
1✔
1301
        .boxed();
1✔
1302

1✔
1303
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1304
            (0., 0.).into(),
1✔
1305
            [3, 2].into(),
1✔
1306
        ));
1✔
1307
        let query_rect = RasterQueryRectangle {
1✔
1308
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1309
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1310
            spatial_resolution: SpatialResolution::one(),
1✔
1311
        };
1✔
1312
        let query_ctx = MockQueryContext::test_default();
1✔
1313

1314
        let qp = agg
1✔
1315
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1316
            .await
×
1317
            .unwrap()
1✔
1318
            .query_processor()
1✔
1319
            .unwrap()
1✔
1320
            .get_u8()
1✔
1321
            .unwrap();
1✔
1322

1323
        let result = qp
1✔
1324
            .query(query_rect, &query_ctx)
1✔
1325
            .await
×
1326
            .unwrap()
1✔
1327
            .collect::<Vec<_>>()
1✔
1328
            .await;
8✔
1329

1330
        assert_eq!(result.len(), 2);
1✔
1331

1332
        assert_eq!(
1✔
1333
            result[0].as_ref().unwrap(),
1✔
1334
            &RasterTile2D::new_with_tile_info(
1✔
1335
                TimeInterval::new_unchecked(0, 30),
1✔
1336
                TileInformation {
1✔
1337
                    global_tile_position: [-1, 0].into(),
1✔
1338
                    tile_size_in_pixels: [3, 2].into(),
1✔
1339
                    global_geo_transform: TestDefault::test_default(),
1✔
1340
                },
1✔
1341
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into()))
1✔
1342
            )
1✔
1343
        );
1✔
1344

1345
        assert_eq!(
1✔
1346
            result[1].as_ref().unwrap(),
1✔
1347
            &RasterTile2D::new_with_tile_info(
1✔
1348
                TimeInterval::new_unchecked(0, 30),
1✔
1349
                TileInformation {
1✔
1350
                    global_tile_position: [-1, 1].into(),
1✔
1351
                    tile_size_in_pixels: [3, 2].into(),
1✔
1352
                    global_geo_transform: TestDefault::test_default(),
1✔
1353
                },
1✔
1354
                GridOrEmpty::from(
1✔
1355
                    MaskedGrid2D::new(
1✔
1356
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 42, 5, 6]).unwrap(),
1✔
1357
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1358
                            .unwrap()
1✔
1359
                    )
1✔
1360
                    .unwrap()
1✔
1361
                ),
1✔
1362
            )
1✔
1363
        );
1✔
1364
    }
1365

1366
    #[tokio::test]
1✔
1367
    async fn test_mean_nodata() {
1✔
1368
        let raster_tiles = make_raster_with_no_data();
1✔
1369

1✔
1370
        let mrs = MockRasterSource {
1✔
1371
            params: MockRasterSourceParams {
1✔
1372
                data: raster_tiles,
1✔
1373
                result_descriptor: RasterResultDescriptor {
1✔
1374
                    data_type: RasterDataType::U8,
1✔
1375
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1376
                    measurement: Measurement::Unitless,
1✔
1377
                    time: None,
1✔
1378
                    bbox: None,
1✔
1379
                    resolution: None,
1✔
1380
                },
1✔
1381
            },
1✔
1382
        }
1✔
1383
        .boxed();
1✔
1384

1✔
1385
        let agg = TemporalRasterAggregation {
1✔
1386
            params: TemporalRasterAggregationParameters {
1✔
1387
                aggregation: Aggregation::Mean {
1✔
1388
                    ignore_no_data: false,
1✔
1389
                },
1✔
1390
                window: TimeStep {
1✔
1391
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1392
                    step: 30,
1✔
1393
                },
1✔
1394
                window_reference: None,
1✔
1395
                output_type: None,
1✔
1396
            },
1✔
1397
            sources: SingleRasterSource { raster: mrs },
1✔
1398
        }
1✔
1399
        .boxed();
1✔
1400

1✔
1401
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1402
            (0., 0.).into(),
1✔
1403
            [3, 2].into(),
1✔
1404
        ));
1✔
1405
        let query_rect = RasterQueryRectangle {
1✔
1406
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1407
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1408
            spatial_resolution: SpatialResolution::one(),
1✔
1409
        };
1✔
1410
        let query_ctx = MockQueryContext::test_default();
1✔
1411

1412
        let qp = agg
1✔
1413
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1414
            .await
×
1415
            .unwrap()
1✔
1416
            .query_processor()
1✔
1417
            .unwrap()
1✔
1418
            .get_u8()
1✔
1419
            .unwrap();
1✔
1420

1421
        let result = qp
1✔
1422
            .raster_query(query_rect, &query_ctx)
1✔
1423
            .await
×
1424
            .unwrap()
1✔
1425
            .collect::<Vec<_>>()
1✔
1426
            .await;
6✔
1427

1428
        assert_eq!(result.len(), 2);
1✔
1429

1430
        assert_eq!(
1✔
1431
            result[0].as_ref().unwrap(),
1✔
1432
            &RasterTile2D::new_with_tile_info(
1✔
1433
                TimeInterval::new_unchecked(0, 30),
1✔
1434
                TileInformation {
1✔
1435
                    global_tile_position: [-1, 0].into(),
1✔
1436
                    tile_size_in_pixels: [3, 2].into(),
1✔
1437
                    global_geo_transform: TestDefault::test_default(),
1✔
1438
                },
1✔
1439
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into()))
1✔
1440
            )
1✔
1441
        );
1✔
1442

1443
        assert_eq!(
1✔
1444
            result[1].as_ref().unwrap(),
1✔
1445
            &RasterTile2D::new_with_tile_info(
1✔
1446
                TimeInterval::new_unchecked(0, 30),
1✔
1447
                TileInformation {
1✔
1448
                    global_tile_position: [-1, 1].into(),
1✔
1449
                    tile_size_in_pixels: [3, 2].into(),
1✔
1450
                    global_geo_transform: TestDefault::test_default(),
1✔
1451
                },
1✔
1452
                GridOrEmpty::from(
1✔
1453
                    MaskedGrid2D::new(
1✔
1454
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1455
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1456
                            .unwrap()
1✔
1457
                    )
1✔
1458
                    .unwrap()
1✔
1459
                ),
1✔
1460
            )
1✔
1461
        );
1✔
1462
    }
1463

1464
    #[tokio::test]
1✔
1465
    async fn test_mean_ignore_nodata() {
1✔
1466
        let raster_tiles = make_raster_with_no_data();
1✔
1467

1✔
1468
        let mrs = MockRasterSource {
1✔
1469
            params: MockRasterSourceParams {
1✔
1470
                data: raster_tiles,
1✔
1471
                result_descriptor: RasterResultDescriptor {
1✔
1472
                    data_type: RasterDataType::U8,
1✔
1473
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1474
                    measurement: Measurement::Unitless,
1✔
1475
                    time: None,
1✔
1476
                    bbox: None,
1✔
1477
                    resolution: None,
1✔
1478
                },
1✔
1479
            },
1✔
1480
        }
1✔
1481
        .boxed();
1✔
1482

1✔
1483
        let agg = TemporalRasterAggregation {
1✔
1484
            params: TemporalRasterAggregationParameters {
1✔
1485
                aggregation: Aggregation::Mean {
1✔
1486
                    ignore_no_data: true,
1✔
1487
                },
1✔
1488
                window: TimeStep {
1✔
1489
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1490
                    step: 30,
1✔
1491
                },
1✔
1492
                window_reference: None,
1✔
1493
                output_type: None,
1✔
1494
            },
1✔
1495
            sources: SingleRasterSource { raster: mrs },
1✔
1496
        }
1✔
1497
        .boxed();
1✔
1498

1✔
1499
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1500
            (0., 0.).into(),
1✔
1501
            [3, 2].into(),
1✔
1502
        ));
1✔
1503
        let query_rect = RasterQueryRectangle {
1✔
1504
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1505
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1506
            spatial_resolution: SpatialResolution::one(),
1✔
1507
        };
1✔
1508
        let query_ctx = MockQueryContext::test_default();
1✔
1509

1510
        let qp = agg
1✔
1511
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1512
            .await
×
1513
            .unwrap()
1✔
1514
            .query_processor()
1✔
1515
            .unwrap()
1✔
1516
            .get_u8()
1✔
1517
            .unwrap();
1✔
1518

1519
        let result = qp
1✔
1520
            .raster_query(query_rect, &query_ctx)
1✔
1521
            .await
×
1522
            .unwrap()
1✔
1523
            .collect::<Vec<_>>()
1✔
1524
            .await;
6✔
1525

1526
        assert_eq!(result.len(), 2);
1✔
1527

1528
        assert_eq!(
1✔
1529
            result[0].as_ref().unwrap(),
1✔
1530
            &RasterTile2D::new_with_tile_info(
1✔
1531
                TimeInterval::new_unchecked(0, 30),
1✔
1532
                TileInformation {
1✔
1533
                    global_tile_position: [-1, 0].into(),
1✔
1534
                    tile_size_in_pixels: [3, 2].into(),
1✔
1535
                    global_geo_transform: TestDefault::test_default(),
1✔
1536
                },
1✔
1537
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![10, 8, 12, 16, 14, 15]).unwrap())
1✔
1538
            )
1✔
1539
        );
1✔
1540

1541
        assert_eq!(
1✔
1542
            result[1].as_ref().unwrap(),
1✔
1543
            &RasterTile2D::new_with_tile_info(
1✔
1544
                TimeInterval::new_unchecked(0, 30),
1✔
1545
                TileInformation {
1✔
1546
                    global_tile_position: [-1, 1].into(),
1✔
1547
                    tile_size_in_pixels: [3, 2].into(),
1✔
1548
                    global_geo_transform: TestDefault::test_default(),
1✔
1549
                },
1✔
1550
                GridOrEmpty::from(
1✔
1551
                    MaskedGrid2D::new(
1✔
1552
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1553
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1554
                            .unwrap()
1✔
1555
                    )
1✔
1556
                    .unwrap()
1✔
1557
                ),
1✔
1558
            )
1✔
1559
        );
1✔
1560
    }
1561

1562
    #[tokio::test]
1✔
1563
    #[allow(clippy::too_many_lines)]
1564
    async fn test_sum_without_nodata() {
1✔
1565
        let operator = TemporalRasterAggregation {
1✔
1566
            params: TemporalRasterAggregationParameters {
1✔
1567
                aggregation: Aggregation::Sum {
1✔
1568
                    ignore_no_data: false,
1✔
1569
                },
1✔
1570
                window: TimeStep {
1✔
1571
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1572
                    step: 20,
1✔
1573
                },
1✔
1574
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
1575
                output_type: None,
1✔
1576
            },
1✔
1577
            sources: SingleRasterSource {
1✔
1578
                raster: MockRasterSource {
1✔
1579
                    params: MockRasterSourceParams {
1✔
1580
                        data: make_raster(),
1✔
1581
                        result_descriptor: RasterResultDescriptor {
1✔
1582
                            data_type: RasterDataType::U8,
1✔
1583
                            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1584
                            measurement: Measurement::Unitless,
1✔
1585
                            time: None,
1✔
1586
                            bbox: None,
1✔
1587
                            resolution: None,
1✔
1588
                        },
1✔
1589
                    },
1✔
1590
                }
1✔
1591
                .boxed(),
1✔
1592
            },
1✔
1593
        }
1✔
1594
        .boxed();
1✔
1595

1✔
1596
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1597
            (0., 0.).into(),
1✔
1598
            [3, 2].into(),
1✔
1599
        ));
1✔
1600
        let query_rect = RasterQueryRectangle {
1✔
1601
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1602
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1603
            spatial_resolution: SpatialResolution::one(),
1✔
1604
        };
1✔
1605
        let query_ctx = MockQueryContext::test_default();
1✔
1606

1607
        let query_processor = operator
1✔
1608
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1609
            .await
×
1610
            .unwrap()
1✔
1611
            .query_processor()
1✔
1612
            .unwrap()
1✔
1613
            .get_u8()
1✔
1614
            .unwrap();
1✔
1615

1616
        let result = query_processor
1✔
1617
            .raster_query(query_rect, &query_ctx)
1✔
1618
            .await
×
1619
            .unwrap()
1✔
1620
            .map(Result::unwrap)
1✔
1621
            .collect::<Vec<_>>()
1✔
1622
            .await;
8✔
1623

1624
        assert_eq!(
1✔
1625
            result,
1✔
1626
            [
1✔
1627
                RasterTile2D::new_with_tile_info(
1✔
1628
                    TimeInterval::new_unchecked(0, 20),
1✔
1629
                    TileInformation {
1✔
1630
                        global_tile_position: [-1, 0].into(),
1✔
1631
                        tile_size_in_pixels: [3, 2].into(),
1✔
1632
                        global_geo_transform: TestDefault::test_default(),
1✔
1633
                    },
1✔
1634
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1635
                        .unwrap()
1✔
1636
                        .into()
1✔
1637
                ),
1✔
1638
                RasterTile2D::new_with_tile_info(
1✔
1639
                    TimeInterval::new_unchecked(0, 20),
1✔
1640
                    TileInformation {
1✔
1641
                        global_tile_position: [-1, 1].into(),
1✔
1642
                        tile_size_in_pixels: [3, 2].into(),
1✔
1643
                        global_geo_transform: TestDefault::test_default(),
1✔
1644
                    },
1✔
1645
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1646
                        .unwrap()
1✔
1647
                        .into(),
1✔
1648
                ),
1✔
1649
                RasterTile2D::new_with_tile_info(
1✔
1650
                    TimeInterval::new_unchecked(20, 40),
1✔
1651
                    TileInformation {
1✔
1652
                        global_tile_position: [-1, 0].into(),
1✔
1653
                        tile_size_in_pixels: [3, 2].into(),
1✔
1654
                        global_geo_transform: TestDefault::test_default(),
1✔
1655
                    },
1✔
1656
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1657
                        .unwrap()
1✔
1658
                        .into(),
1✔
1659
                ),
1✔
1660
                RasterTile2D::new_with_tile_info(
1✔
1661
                    TimeInterval::new_unchecked(20, 40),
1✔
1662
                    TileInformation {
1✔
1663
                        global_tile_position: [-1, 1].into(),
1✔
1664
                        tile_size_in_pixels: [3, 2].into(),
1✔
1665
                        global_geo_transform: TestDefault::test_default(),
1✔
1666
                    },
1✔
1667
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1668
                        .unwrap()
1✔
1669
                        .into(),
1✔
1670
                )
1✔
1671
            ]
1✔
1672
        );
1✔
1673
    }
1674

1675
    #[tokio::test]
1✔
1676
    async fn test_sum_nodata() {
1✔
1677
        let raster_tiles = make_raster_with_no_data();
1✔
1678

1✔
1679
        let mrs = MockRasterSource {
1✔
1680
            params: MockRasterSourceParams {
1✔
1681
                data: raster_tiles,
1✔
1682
                result_descriptor: RasterResultDescriptor {
1✔
1683
                    data_type: RasterDataType::U8,
1✔
1684
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1685
                    measurement: Measurement::Unitless,
1✔
1686
                    time: None,
1✔
1687
                    bbox: None,
1✔
1688
                    resolution: None,
1✔
1689
                },
1✔
1690
            },
1✔
1691
        }
1✔
1692
        .boxed();
1✔
1693

1✔
1694
        let agg = TemporalRasterAggregation {
1✔
1695
            params: TemporalRasterAggregationParameters {
1✔
1696
                aggregation: Aggregation::Sum {
1✔
1697
                    ignore_no_data: false,
1✔
1698
                },
1✔
1699
                window: TimeStep {
1✔
1700
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1701
                    step: 30,
1✔
1702
                },
1✔
1703
                window_reference: None,
1✔
1704
                output_type: None,
1✔
1705
            },
1✔
1706
            sources: SingleRasterSource { raster: mrs },
1✔
1707
        }
1✔
1708
        .boxed();
1✔
1709

1✔
1710
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1711
            (0., 0.).into(),
1✔
1712
            [3, 2].into(),
1✔
1713
        ));
1✔
1714
        let query_rect = RasterQueryRectangle {
1✔
1715
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1716
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1717
            spatial_resolution: SpatialResolution::one(),
1✔
1718
        };
1✔
1719
        let query_ctx = MockQueryContext::test_default();
1✔
1720

1721
        let qp = agg
1✔
1722
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1723
            .await
×
1724
            .unwrap()
1✔
1725
            .query_processor()
1✔
1726
            .unwrap()
1✔
1727
            .get_u8()
1✔
1728
            .unwrap();
1✔
1729

1730
        let result = qp
1✔
1731
            .raster_query(query_rect, &query_ctx)
1✔
1732
            .await
×
1733
            .unwrap()
1✔
1734
            .collect::<Vec<_>>()
1✔
1735
            .await;
6✔
1736

1737
        assert_eq!(result.len(), 2);
1✔
1738

1739
        assert_eq!(
1✔
1740
            result[0].as_ref().unwrap(),
1✔
1741
            &RasterTile2D::new_with_tile_info(
1✔
1742
                TimeInterval::new_unchecked(0, 30),
1✔
1743
                TileInformation {
1✔
1744
                    global_tile_position: [-1, 0].into(),
1✔
1745
                    tile_size_in_pixels: [3, 2].into(),
1✔
1746
                    global_geo_transform: TestDefault::test_default(),
1✔
1747
                },
1✔
1748
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into()))
1✔
1749
            )
1✔
1750
        );
1✔
1751

1752
        assert_eq!(
1✔
1753
            result[1].as_ref().unwrap(),
1✔
1754
            &RasterTile2D::new_with_tile_info(
1✔
1755
                TimeInterval::new_unchecked(0, 30),
1✔
1756
                TileInformation {
1✔
1757
                    global_tile_position: [-1, 1].into(),
1✔
1758
                    tile_size_in_pixels: [3, 2].into(),
1✔
1759
                    global_geo_transform: TestDefault::test_default(),
1✔
1760
                },
1✔
1761
                GridOrEmpty::from(
1✔
1762
                    MaskedGrid2D::new(
1✔
1763
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1764
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1765
                            .unwrap()
1✔
1766
                    )
1✔
1767
                    .unwrap()
1✔
1768
                ),
1✔
1769
            )
1✔
1770
        );
1✔
1771
    }
1772

1773
    #[tokio::test]
1✔
1774
    async fn test_sum_ignore_nodata() {
1✔
1775
        let raster_tiles = make_raster_with_no_data();
1✔
1776

1✔
1777
        let mrs = MockRasterSource {
1✔
1778
            params: MockRasterSourceParams {
1✔
1779
                data: raster_tiles,
1✔
1780
                result_descriptor: RasterResultDescriptor {
1✔
1781
                    data_type: RasterDataType::U8,
1✔
1782
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1783
                    measurement: Measurement::Unitless,
1✔
1784
                    time: None,
1✔
1785
                    bbox: None,
1✔
1786
                    resolution: None,
1✔
1787
                },
1✔
1788
            },
1✔
1789
        }
1✔
1790
        .boxed();
1✔
1791

1✔
1792
        let agg = TemporalRasterAggregation {
1✔
1793
            params: TemporalRasterAggregationParameters {
1✔
1794
                aggregation: Aggregation::Sum {
1✔
1795
                    ignore_no_data: true,
1✔
1796
                },
1✔
1797
                window: TimeStep {
1✔
1798
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1799
                    step: 30,
1✔
1800
                },
1✔
1801
                window_reference: None,
1✔
1802
                output_type: None,
1✔
1803
            },
1✔
1804
            sources: SingleRasterSource { raster: mrs },
1✔
1805
        }
1✔
1806
        .boxed();
1✔
1807

1✔
1808
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1809
            (0., 0.).into(),
1✔
1810
            [3, 2].into(),
1✔
1811
        ));
1✔
1812
        let query_rect = RasterQueryRectangle {
1✔
1813
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1814
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1815
            spatial_resolution: SpatialResolution::one(),
1✔
1816
        };
1✔
1817
        let query_ctx = MockQueryContext::test_default();
1✔
1818

1819
        let qp = agg
1✔
1820
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1821
            .await
×
1822
            .unwrap()
1✔
1823
            .query_processor()
1✔
1824
            .unwrap()
1✔
1825
            .get_u8()
1✔
1826
            .unwrap();
1✔
1827

1828
        let result = qp
1✔
1829
            .raster_query(query_rect, &query_ctx)
1✔
1830
            .await
×
1831
            .unwrap()
1✔
1832
            .collect::<Vec<_>>()
1✔
1833
            .await;
6✔
1834

1835
        assert_eq!(result.len(), 2);
1✔
1836

1837
        assert_eq!(
1✔
1838
            result[0].as_ref().unwrap(),
1✔
1839
            &RasterTile2D::new_with_tile_info(
1✔
1840
                TimeInterval::new_unchecked(0, 30),
1✔
1841
                TileInformation {
1✔
1842
                    global_tile_position: [-1, 0].into(),
1✔
1843
                    tile_size_in_pixels: [3, 2].into(),
1✔
1844
                    global_geo_transform: TestDefault::test_default(),
1✔
1845
                },
1✔
1846
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![20, 8, 24, 16, 28, 30]).unwrap())
1✔
1847
            )
1✔
1848
        );
1✔
1849

1850
        assert_eq!(
1✔
1851
            result[1].as_ref().unwrap(),
1✔
1852
            &RasterTile2D::new_with_tile_info(
1✔
1853
                TimeInterval::new_unchecked(0, 30),
1✔
1854
                TileInformation {
1✔
1855
                    global_tile_position: [-1, 1].into(),
1✔
1856
                    tile_size_in_pixels: [3, 2].into(),
1✔
1857
                    global_geo_transform: TestDefault::test_default(),
1✔
1858
                },
1✔
1859
                GridOrEmpty::from(
1✔
1860
                    MaskedGrid2D::new(
1✔
1861
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1862
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1863
                            .unwrap()
1✔
1864
                    )
1✔
1865
                    .unwrap()
1✔
1866
                ),
1✔
1867
            )
1✔
1868
        );
1✔
1869
    }
1870

1871
    #[tokio::test]
1✔
1872
    #[allow(clippy::too_many_lines)]
1873
    async fn test_sum_with_larger_data_type() {
1✔
1874
        let operator = TemporalRasterAggregation {
1✔
1875
            params: TemporalRasterAggregationParameters {
1✔
1876
                aggregation: Aggregation::Sum {
1✔
1877
                    ignore_no_data: false,
1✔
1878
                },
1✔
1879
                window: TimeStep {
1✔
1880
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1881
                    step: 20,
1✔
1882
                },
1✔
1883
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
1884
                output_type: Some(RasterDataType::U16),
1✔
1885
            },
1✔
1886
            sources: SingleRasterSource {
1✔
1887
                raster: Expression {
1✔
1888
                    params: ExpressionParams {
1✔
1889
                        expression: "20 * A".to_string(),
1✔
1890
                        output_type: RasterDataType::U8,
1✔
1891
                        output_measurement: Some(Measurement::Unitless),
1✔
1892
                        map_no_data: true,
1✔
1893
                    },
1✔
1894
                    sources: ExpressionSources::new_a(
1✔
1895
                        MockRasterSource {
1✔
1896
                            params: MockRasterSourceParams {
1✔
1897
                                data: make_raster(),
1✔
1898
                                result_descriptor: RasterResultDescriptor {
1✔
1899
                                    data_type: RasterDataType::U8,
1✔
1900
                                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1901
                                    measurement: Measurement::Unitless,
1✔
1902
                                    time: None,
1✔
1903
                                    bbox: None,
1✔
1904
                                    resolution: None,
1✔
1905
                                },
1✔
1906
                            },
1✔
1907
                        }
1✔
1908
                        .boxed(),
1✔
1909
                    ),
1✔
1910
                }
1✔
1911
                .boxed(),
1✔
1912
            },
1✔
1913
        }
1✔
1914
        .boxed();
1✔
1915

1✔
1916
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1917
            (0., 0.).into(),
1✔
1918
            [3, 2].into(),
1✔
1919
        ));
1✔
1920
        let query_rect = RasterQueryRectangle {
1✔
1921
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1922
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1923
            spatial_resolution: SpatialResolution::one(),
1✔
1924
        };
1✔
1925
        let query_ctx = MockQueryContext::test_default();
1✔
1926

1927
        let query_processor = operator
1✔
1928
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1929
            .await
×
1930
            .unwrap()
1✔
1931
            .query_processor()
1✔
1932
            .unwrap()
1✔
1933
            .get_u16()
1✔
1934
            .unwrap();
1✔
1935

1936
        let result = query_processor
1✔
1937
            .raster_query(query_rect, &query_ctx)
1✔
1938
            .await
×
1939
            .unwrap()
1✔
1940
            .map(Result::unwrap)
1✔
1941
            .collect::<Vec<_>>()
1✔
1942
            .await;
32✔
1943

1944
        assert_eq!(
1✔
1945
            result,
1✔
1946
            [
1✔
1947
                RasterTile2D::new_with_tile_info(
1✔
1948
                    TimeInterval::new_unchecked(0, 20),
1✔
1949
                    TileInformation {
1✔
1950
                        global_tile_position: [-1, 0].into(),
1✔
1951
                        tile_size_in_pixels: [3, 2].into(),
1✔
1952
                        global_geo_transform: TestDefault::test_default(),
1✔
1953
                    },
1✔
1954
                    Grid2D::new(
1✔
1955
                        [3, 2].into(),
1✔
1956
                        vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
1957
                    )
1✔
1958
                    .unwrap()
1✔
1959
                    .into()
1✔
1960
                ),
1✔
1961
                RasterTile2D::new_with_tile_info(
1✔
1962
                    TimeInterval::new_unchecked(0, 20),
1✔
1963
                    TileInformation {
1✔
1964
                        global_tile_position: [-1, 1].into(),
1✔
1965
                        tile_size_in_pixels: [3, 2].into(),
1✔
1966
                        global_geo_transform: TestDefault::test_default(),
1✔
1967
                    },
1✔
1968
                    Grid2D::new(
1✔
1969
                        [3, 2].into(),
1✔
1970
                        vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
1971
                    )
1✔
1972
                    .unwrap()
1✔
1973
                    .into(),
1✔
1974
                ),
1✔
1975
                RasterTile2D::new_with_tile_info(
1✔
1976
                    TimeInterval::new_unchecked(20, 40),
1✔
1977
                    TileInformation {
1✔
1978
                        global_tile_position: [-1, 0].into(),
1✔
1979
                        tile_size_in_pixels: [3, 2].into(),
1✔
1980
                        global_geo_transform: TestDefault::test_default(),
1✔
1981
                    },
1✔
1982
                    Grid2D::new(
1✔
1983
                        [3, 2].into(),
1✔
1984
                        vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
1985
                    )
1✔
1986
                    .unwrap()
1✔
1987
                    .into(),
1✔
1988
                ),
1✔
1989
                RasterTile2D::new_with_tile_info(
1✔
1990
                    TimeInterval::new_unchecked(20, 40),
1✔
1991
                    TileInformation {
1✔
1992
                        global_tile_position: [-1, 1].into(),
1✔
1993
                        tile_size_in_pixels: [3, 2].into(),
1✔
1994
                        global_geo_transform: TestDefault::test_default(),
1✔
1995
                    },
1✔
1996
                    Grid2D::new(
1✔
1997
                        [3, 2].into(),
1✔
1998
                        vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
1999
                    )
1✔
2000
                    .unwrap()
1✔
2001
                    .into(),
1✔
2002
                )
1✔
2003
            ]
1✔
2004
        );
1✔
2005
    }
2006

2007
    #[tokio::test]
1✔
2008
    #[allow(clippy::too_many_lines)]
2009
    async fn test_count_without_nodata() {
1✔
2010
        let operator = TemporalRasterAggregation {
1✔
2011
            params: TemporalRasterAggregationParameters {
1✔
2012
                aggregation: Aggregation::Count {
1✔
2013
                    ignore_no_data: false,
1✔
2014
                },
1✔
2015
                window: TimeStep {
1✔
2016
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2017
                    step: 20,
1✔
2018
                },
1✔
2019
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
2020
                output_type: None,
1✔
2021
            },
1✔
2022
            sources: SingleRasterSource {
1✔
2023
                raster: MockRasterSource {
1✔
2024
                    params: MockRasterSourceParams {
1✔
2025
                        data: make_raster(),
1✔
2026
                        result_descriptor: RasterResultDescriptor {
1✔
2027
                            data_type: RasterDataType::U8,
1✔
2028
                            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2029
                            measurement: Measurement::Unitless,
1✔
2030
                            time: None,
1✔
2031
                            bbox: None,
1✔
2032
                            resolution: None,
1✔
2033
                        },
1✔
2034
                    },
1✔
2035
                }
1✔
2036
                .boxed(),
1✔
2037
            },
1✔
2038
        }
1✔
2039
        .boxed();
1✔
2040

1✔
2041
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2042
            (0., 0.).into(),
1✔
2043
            [3, 2].into(),
1✔
2044
        ));
1✔
2045
        let query_rect = RasterQueryRectangle {
1✔
2046
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2047
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2048
            spatial_resolution: SpatialResolution::one(),
1✔
2049
        };
1✔
2050
        let query_ctx = MockQueryContext::test_default();
1✔
2051

2052
        let query_processor = operator
1✔
2053
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2054
            .await
×
2055
            .unwrap()
1✔
2056
            .query_processor()
1✔
2057
            .unwrap()
1✔
2058
            .get_u8()
1✔
2059
            .unwrap();
1✔
2060

2061
        let result = query_processor
1✔
2062
            .raster_query(query_rect, &query_ctx)
1✔
2063
            .await
×
2064
            .unwrap()
1✔
2065
            .map(Result::unwrap)
1✔
2066
            .collect::<Vec<_>>()
1✔
2067
            .await;
8✔
2068

2069
        assert_eq!(
1✔
2070
            result,
1✔
2071
            [
1✔
2072
                RasterTile2D::new_with_tile_info(
1✔
2073
                    TimeInterval::new_unchecked(0, 20),
1✔
2074
                    TileInformation {
1✔
2075
                        global_tile_position: [-1, 0].into(),
1✔
2076
                        tile_size_in_pixels: [3, 2].into(),
1✔
2077
                        global_geo_transform: TestDefault::test_default(),
1✔
2078
                    },
1✔
2079
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2080
                        .unwrap()
1✔
2081
                        .into()
1✔
2082
                ),
1✔
2083
                RasterTile2D::new_with_tile_info(
1✔
2084
                    TimeInterval::new_unchecked(0, 20),
1✔
2085
                    TileInformation {
1✔
2086
                        global_tile_position: [-1, 1].into(),
1✔
2087
                        tile_size_in_pixels: [3, 2].into(),
1✔
2088
                        global_geo_transform: TestDefault::test_default(),
1✔
2089
                    },
1✔
2090
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2091
                        .unwrap()
1✔
2092
                        .into(),
1✔
2093
                ),
1✔
2094
                RasterTile2D::new_with_tile_info(
1✔
2095
                    TimeInterval::new_unchecked(20, 40),
1✔
2096
                    TileInformation {
1✔
2097
                        global_tile_position: [-1, 0].into(),
1✔
2098
                        tile_size_in_pixels: [3, 2].into(),
1✔
2099
                        global_geo_transform: TestDefault::test_default(),
1✔
2100
                    },
1✔
2101
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2102
                        .unwrap()
1✔
2103
                        .into(),
1✔
2104
                ),
1✔
2105
                RasterTile2D::new_with_tile_info(
1✔
2106
                    TimeInterval::new_unchecked(20, 40),
1✔
2107
                    TileInformation {
1✔
2108
                        global_tile_position: [-1, 1].into(),
1✔
2109
                        tile_size_in_pixels: [3, 2].into(),
1✔
2110
                        global_geo_transform: TestDefault::test_default(),
1✔
2111
                    },
1✔
2112
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2113
                        .unwrap()
1✔
2114
                        .into(),
1✔
2115
                )
1✔
2116
            ]
1✔
2117
        );
1✔
2118
    }
2119

2120
    #[tokio::test]
1✔
2121
    async fn test_count_nodata() {
1✔
2122
        let raster_tiles = make_raster_with_no_data();
1✔
2123

1✔
2124
        let mrs = MockRasterSource {
1✔
2125
            params: MockRasterSourceParams {
1✔
2126
                data: raster_tiles,
1✔
2127
                result_descriptor: RasterResultDescriptor {
1✔
2128
                    data_type: RasterDataType::U8,
1✔
2129
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2130
                    measurement: Measurement::Unitless,
1✔
2131
                    time: None,
1✔
2132
                    bbox: None,
1✔
2133
                    resolution: None,
1✔
2134
                },
1✔
2135
            },
1✔
2136
        }
1✔
2137
        .boxed();
1✔
2138

1✔
2139
        let agg = TemporalRasterAggregation {
1✔
2140
            params: TemporalRasterAggregationParameters {
1✔
2141
                aggregation: Aggregation::Count {
1✔
2142
                    ignore_no_data: false,
1✔
2143
                },
1✔
2144
                window: TimeStep {
1✔
2145
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2146
                    step: 30,
1✔
2147
                },
1✔
2148
                window_reference: None,
1✔
2149
                output_type: None,
1✔
2150
            },
1✔
2151
            sources: SingleRasterSource { raster: mrs },
1✔
2152
        }
1✔
2153
        .boxed();
1✔
2154

1✔
2155
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2156
            (0., 0.).into(),
1✔
2157
            [3, 2].into(),
1✔
2158
        ));
1✔
2159
        let query_rect = RasterQueryRectangle {
1✔
2160
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2161
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2162
            spatial_resolution: SpatialResolution::one(),
1✔
2163
        };
1✔
2164
        let query_ctx = MockQueryContext::test_default();
1✔
2165

2166
        let qp = agg
1✔
2167
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2168
            .await
×
2169
            .unwrap()
1✔
2170
            .query_processor()
1✔
2171
            .unwrap()
1✔
2172
            .get_u8()
1✔
2173
            .unwrap();
1✔
2174

2175
        let result = qp
1✔
2176
            .raster_query(query_rect, &query_ctx)
1✔
2177
            .await
×
2178
            .unwrap()
1✔
2179
            .collect::<Vec<_>>()
1✔
2180
            .await;
6✔
2181

2182
        assert_eq!(result.len(), 2);
1✔
2183

2184
        assert_eq!(
1✔
2185
            result[0].as_ref().unwrap(),
1✔
2186
            &RasterTile2D::new_with_tile_info(
1✔
2187
                TimeInterval::new_unchecked(0, 30),
1✔
2188
                TileInformation {
1✔
2189
                    global_tile_position: [-1, 0].into(),
1✔
2190
                    tile_size_in_pixels: [3, 2].into(),
1✔
2191
                    global_geo_transform: TestDefault::test_default(),
1✔
2192
                },
1✔
2193
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into()))
1✔
2194
            )
1✔
2195
        );
1✔
2196

2197
        assert_eq!(
1✔
2198
            result[1].as_ref().unwrap(),
1✔
2199
            &RasterTile2D::new_with_tile_info(
1✔
2200
                TimeInterval::new_unchecked(0, 30),
1✔
2201
                TileInformation {
1✔
2202
                    global_tile_position: [-1, 1].into(),
1✔
2203
                    tile_size_in_pixels: [3, 2].into(),
1✔
2204
                    global_geo_transform: TestDefault::test_default(),
1✔
2205
                },
1✔
2206
                GridOrEmpty::from(
1✔
2207
                    MaskedGrid2D::new(
1✔
2208
                        Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
1✔
2209
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2210
                            .unwrap()
1✔
2211
                    )
1✔
2212
                    .unwrap()
1✔
2213
                ),
1✔
2214
            )
1✔
2215
        );
1✔
2216
    }
2217

2218
    #[tokio::test]
1✔
2219
    async fn test_count_ignore_nodata() {
1✔
2220
        let raster_tiles = make_raster_with_no_data();
1✔
2221

1✔
2222
        let mrs = MockRasterSource {
1✔
2223
            params: MockRasterSourceParams {
1✔
2224
                data: raster_tiles,
1✔
2225
                result_descriptor: RasterResultDescriptor {
1✔
2226
                    data_type: RasterDataType::U8,
1✔
2227
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2228
                    measurement: Measurement::Unitless,
1✔
2229
                    time: None,
1✔
2230
                    bbox: None,
1✔
2231
                    resolution: None,
1✔
2232
                },
1✔
2233
            },
1✔
2234
        }
1✔
2235
        .boxed();
1✔
2236

1✔
2237
        let agg = TemporalRasterAggregation {
1✔
2238
            params: TemporalRasterAggregationParameters {
1✔
2239
                aggregation: Aggregation::Count {
1✔
2240
                    ignore_no_data: true,
1✔
2241
                },
1✔
2242
                window: TimeStep {
1✔
2243
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2244
                    step: 30,
1✔
2245
                },
1✔
2246
                window_reference: None,
1✔
2247
                output_type: None,
1✔
2248
            },
1✔
2249
            sources: SingleRasterSource { raster: mrs },
1✔
2250
        }
1✔
2251
        .boxed();
1✔
2252

1✔
2253
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2254
            (0., 0.).into(),
1✔
2255
            [3, 2].into(),
1✔
2256
        ));
1✔
2257
        let query_rect = RasterQueryRectangle {
1✔
2258
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2259
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2260
            spatial_resolution: SpatialResolution::one(),
1✔
2261
        };
1✔
2262
        let query_ctx = MockQueryContext::test_default();
1✔
2263

2264
        let qp = agg
1✔
2265
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2266
            .await
×
2267
            .unwrap()
1✔
2268
            .query_processor()
1✔
2269
            .unwrap()
1✔
2270
            .get_u8()
1✔
2271
            .unwrap();
1✔
2272

2273
        let result = qp
1✔
2274
            .raster_query(query_rect, &query_ctx)
1✔
2275
            .await
×
2276
            .unwrap()
1✔
2277
            .collect::<Vec<_>>()
1✔
2278
            .await;
6✔
2279

2280
        assert_eq!(result.len(), 2);
1✔
2281

2282
        assert_eq!(
1✔
2283
            result[0].as_ref().unwrap(),
1✔
2284
            &RasterTile2D::new_with_tile_info(
1✔
2285
                TimeInterval::new_unchecked(0, 30),
1✔
2286
                TileInformation {
1✔
2287
                    global_tile_position: [-1, 0].into(),
1✔
2288
                    tile_size_in_pixels: [3, 2].into(),
1✔
2289
                    global_geo_transform: TestDefault::test_default(),
1✔
2290
                },
1✔
2291
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![2, 1, 2, 1, 2, 2]).unwrap())
1✔
2292
            )
1✔
2293
        );
1✔
2294

2295
        assert_eq!(
1✔
2296
            result[1].as_ref().unwrap(),
1✔
2297
            &RasterTile2D::new_with_tile_info(
1✔
2298
                TimeInterval::new_unchecked(0, 30),
1✔
2299
                TileInformation {
1✔
2300
                    global_tile_position: [-1, 1].into(),
1✔
2301
                    tile_size_in_pixels: [3, 2].into(),
1✔
2302
                    global_geo_transform: TestDefault::test_default(),
1✔
2303
                },
1✔
2304
                GridOrEmpty::from(
1✔
2305
                    MaskedGrid2D::new(
1✔
2306
                        Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
1✔
2307
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2308
                            .unwrap()
1✔
2309
                    )
1✔
2310
                    .unwrap()
1✔
2311
                ),
1✔
2312
            )
1✔
2313
        );
1✔
2314
    }
2315

2316
    #[tokio::test]
1✔
2317
    async fn test_query_not_aligned_with_window_reference() {
1✔
2318
        let raster_tiles = make_raster();
1✔
2319

1✔
2320
        let mrs = MockRasterSource {
1✔
2321
            params: MockRasterSourceParams {
1✔
2322
                data: raster_tiles,
1✔
2323
                result_descriptor: RasterResultDescriptor {
1✔
2324
                    data_type: RasterDataType::U8,
1✔
2325
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2326
                    measurement: Measurement::Unitless,
1✔
2327
                    time: None,
1✔
2328
                    bbox: None,
1✔
2329
                    resolution: None,
1✔
2330
                },
1✔
2331
            },
1✔
2332
        }
1✔
2333
        .boxed();
1✔
2334

1✔
2335
        let agg = TemporalRasterAggregation {
1✔
2336
            params: TemporalRasterAggregationParameters {
1✔
2337
                aggregation: Aggregation::Last {
1✔
2338
                    ignore_no_data: false,
1✔
2339
                },
1✔
2340
                window: TimeStep {
1✔
2341
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2342
                    step: 30,
1✔
2343
                },
1✔
2344
                window_reference: Some(TimeInstance::EPOCH_START),
1✔
2345
                output_type: None,
1✔
2346
            },
1✔
2347
            sources: SingleRasterSource { raster: mrs },
1✔
2348
        }
1✔
2349
        .boxed();
1✔
2350

1✔
2351
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2352
            (0., 0.).into(),
1✔
2353
            [3, 2].into(),
1✔
2354
        ));
1✔
2355
        let query_rect = RasterQueryRectangle {
1✔
2356
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2357
            time_interval: TimeInterval::new_unchecked(5, 5),
1✔
2358
            spatial_resolution: SpatialResolution::one(),
1✔
2359
        };
1✔
2360
        let query_ctx = MockQueryContext::test_default();
1✔
2361

2362
        let qp = agg
1✔
2363
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2364
            .await
×
2365
            .unwrap()
1✔
2366
            .query_processor()
1✔
2367
            .unwrap()
1✔
2368
            .get_u8()
1✔
2369
            .unwrap();
1✔
2370

2371
        let result = qp
1✔
2372
            .query(query_rect, &query_ctx)
1✔
2373
            .await
×
2374
            .unwrap()
1✔
2375
            .collect::<Vec<_>>()
1✔
2376
            .await;
8✔
2377

2378
        assert_eq!(result.len(), 2);
1✔
2379
        assert_eq!(
1✔
2380
            result[0].as_ref().unwrap(),
1✔
2381
            &RasterTile2D::new_with_tile_info(
1✔
2382
                TimeInterval::new_unchecked(0, 30),
1✔
2383
                TileInformation {
1✔
2384
                    global_tile_position: [-1, 0].into(),
1✔
2385
                    tile_size_in_pixels: [3, 2].into(),
1✔
2386
                    global_geo_transform: TestDefault::test_default(),
1✔
2387
                },
1✔
2388
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
2389
            )
1✔
2390
        );
1✔
2391

2392
        assert_eq!(
1✔
2393
            result[1].as_ref().unwrap(),
1✔
2394
            &RasterTile2D::new_with_tile_info(
1✔
2395
                TimeInterval::new_unchecked(0, 30),
1✔
2396
                TileInformation {
1✔
2397
                    global_tile_position: [-1, 1].into(),
1✔
2398
                    tile_size_in_pixels: [3, 2].into(),
1✔
2399
                    global_geo_transform: TestDefault::test_default(),
1✔
2400
                },
1✔
2401
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
2402
            )
1✔
2403
        );
1✔
2404
    }
2405

2406
    fn make_raster() -> Vec<geoengine_datatypes::raster::RasterTile2D<u8>> {
8✔
2407
        let raster_tiles = vec![
8✔
2408
            RasterTile2D::new_with_tile_info(
8✔
2409
                TimeInterval::new_unchecked(0, 10),
8✔
2410
                TileInformation {
8✔
2411
                    global_tile_position: [-1, 0].into(),
8✔
2412
                    tile_size_in_pixels: [3, 2].into(),
8✔
2413
                    global_geo_transform: TestDefault::test_default(),
8✔
2414
                },
8✔
2415
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
8✔
2416
            ),
8✔
2417
            RasterTile2D::new_with_tile_info(
8✔
2418
                TimeInterval::new_unchecked(0, 10),
8✔
2419
                TileInformation {
8✔
2420
                    global_tile_position: [-1, 1].into(),
8✔
2421
                    tile_size_in_pixels: [3, 2].into(),
8✔
2422
                    global_geo_transform: TestDefault::test_default(),
8✔
2423
                },
8✔
2424
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
8✔
2425
            ),
8✔
2426
            RasterTile2D::new_with_tile_info(
8✔
2427
                TimeInterval::new_unchecked(10, 20),
8✔
2428
                TileInformation {
8✔
2429
                    global_tile_position: [-1, 0].into(),
8✔
2430
                    tile_size_in_pixels: [3, 2].into(),
8✔
2431
                    global_geo_transform: TestDefault::test_default(),
8✔
2432
                },
8✔
2433
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
8✔
2434
            ),
8✔
2435
            RasterTile2D::new_with_tile_info(
8✔
2436
                TimeInterval::new_unchecked(10, 20),
8✔
2437
                TileInformation {
8✔
2438
                    global_tile_position: [-1, 1].into(),
8✔
2439
                    tile_size_in_pixels: [3, 2].into(),
8✔
2440
                    global_geo_transform: TestDefault::test_default(),
8✔
2441
                },
8✔
2442
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
8✔
2443
            ),
8✔
2444
            RasterTile2D::new_with_tile_info(
8✔
2445
                TimeInterval::new_unchecked(20, 30),
8✔
2446
                TileInformation {
8✔
2447
                    global_tile_position: [-1, 0].into(),
8✔
2448
                    tile_size_in_pixels: [3, 2].into(),
8✔
2449
                    global_geo_transform: TestDefault::test_default(),
8✔
2450
                },
8✔
2451
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
8✔
2452
            ),
8✔
2453
            RasterTile2D::new_with_tile_info(
8✔
2454
                TimeInterval::new_unchecked(20, 30),
8✔
2455
                TileInformation {
8✔
2456
                    global_tile_position: [-1, 1].into(),
8✔
2457
                    tile_size_in_pixels: [3, 2].into(),
8✔
2458
                    global_geo_transform: TestDefault::test_default(),
8✔
2459
                },
8✔
2460
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
8✔
2461
            ),
8✔
2462
            RasterTile2D::new_with_tile_info(
8✔
2463
                TimeInterval::new_unchecked(30, 40),
8✔
2464
                TileInformation {
8✔
2465
                    global_tile_position: [-1, 0].into(),
8✔
2466
                    tile_size_in_pixels: [3, 2].into(),
8✔
2467
                    global_geo_transform: TestDefault::test_default(),
8✔
2468
                },
8✔
2469
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
8✔
2470
            ),
8✔
2471
            RasterTile2D::new_with_tile_info(
8✔
2472
                TimeInterval::new_unchecked(30, 40),
8✔
2473
                TileInformation {
8✔
2474
                    global_tile_position: [-1, 1].into(),
8✔
2475
                    tile_size_in_pixels: [3, 2].into(),
8✔
2476
                    global_geo_transform: TestDefault::test_default(),
8✔
2477
                },
8✔
2478
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
8✔
2479
            ),
8✔
2480
        ];
8✔
2481
        raster_tiles
8✔
2482
    }
8✔
2483

2484
    fn make_raster_with_no_data() -> Vec<geoengine_datatypes::raster::RasterTile2D<u8>> {
10✔
2485
        let raster_tiles = vec![
10✔
2486
            RasterTile2D::new_with_tile_info(
10✔
2487
                TimeInterval::new_unchecked(0, 10),
10✔
2488
                TileInformation {
10✔
2489
                    global_tile_position: [-1, 0].into(),
10✔
2490
                    tile_size_in_pixels: [3, 2].into(),
10✔
2491
                    global_geo_transform: TestDefault::test_default(),
10✔
2492
                },
10✔
2493
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2494
            ),
10✔
2495
            RasterTile2D::new_with_tile_info(
10✔
2496
                TimeInterval::new_unchecked(0, 10),
10✔
2497
                TileInformation {
10✔
2498
                    global_tile_position: [-1, 1].into(),
10✔
2499
                    tile_size_in_pixels: [3, 2].into(),
10✔
2500
                    global_geo_transform: TestDefault::test_default(),
10✔
2501
                },
10✔
2502
                GridOrEmpty::from(
10✔
2503
                    MaskedGrid2D::new(
10✔
2504
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 42, 5, 6]).unwrap(),
10✔
2505
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
10✔
2506
                            .unwrap(),
10✔
2507
                    )
10✔
2508
                    .unwrap(),
10✔
2509
                ),
10✔
2510
            ),
10✔
2511
            RasterTile2D::new_with_tile_info(
10✔
2512
                TimeInterval::new_unchecked(10, 20),
10✔
2513
                TileInformation {
10✔
2514
                    global_tile_position: [-1, 0].into(),
10✔
2515
                    tile_size_in_pixels: [3, 2].into(),
10✔
2516
                    global_geo_transform: TestDefault::test_default(),
10✔
2517
                },
10✔
2518
                GridOrEmpty::from(
10✔
2519
                    MaskedGrid2D::new(
10✔
2520
                        Grid2D::new([3, 2].into(), vec![7, 8, 9, 42, 11, 12]).unwrap(),
10✔
2521
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
10✔
2522
                            .unwrap(),
10✔
2523
                    )
10✔
2524
                    .unwrap(),
10✔
2525
                ),
10✔
2526
            ),
10✔
2527
            RasterTile2D::new_with_tile_info(
10✔
2528
                TimeInterval::new_unchecked(10, 20),
10✔
2529
                TileInformation {
10✔
2530
                    global_tile_position: [-1, 1].into(),
10✔
2531
                    tile_size_in_pixels: [3, 2].into(),
10✔
2532
                    global_geo_transform: TestDefault::test_default(),
10✔
2533
                },
10✔
2534
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2535
            ),
10✔
2536
            RasterTile2D::new_with_tile_info(
10✔
2537
                TimeInterval::new_unchecked(20, 30),
10✔
2538
                TileInformation {
10✔
2539
                    global_tile_position: [-1, 0].into(),
10✔
2540
                    tile_size_in_pixels: [3, 2].into(),
10✔
2541
                    global_geo_transform: TestDefault::test_default(),
10✔
2542
                },
10✔
2543
                GridOrEmpty::from(
10✔
2544
                    MaskedGrid2D::new(
10✔
2545
                        Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
10✔
2546
                        Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
10✔
2547
                            .unwrap(),
10✔
2548
                    )
10✔
2549
                    .unwrap(),
10✔
2550
                ),
10✔
2551
            ),
10✔
2552
            RasterTile2D::new_with_tile_info(
10✔
2553
                TimeInterval::new_unchecked(20, 30),
10✔
2554
                TileInformation {
10✔
2555
                    global_tile_position: [-1, 1].into(),
10✔
2556
                    tile_size_in_pixels: [3, 2].into(),
10✔
2557
                    global_geo_transform: TestDefault::test_default(),
10✔
2558
                },
10✔
2559
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2560
            ),
10✔
2561
        ];
10✔
2562
        raster_tiles
10✔
2563
    }
10✔
2564
}
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