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

geo-engine / geoengine / 7006568925

27 Nov 2023 02:07PM UTC coverage: 89.651% (+0.2%) from 89.498%
7006568925

push

github

web-flow
Merge pull request #888 from geo-engine/raster_stacks

raster stacking

4032 of 4274 new or added lines in 107 files covered. (94.34%)

12 existing lines in 8 files now uncovered.

113020 of 126066 relevant lines covered (89.65%)

59901.79 hits per line

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

97.34
/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::adapters::{RasterStackerAdapter, StreamBundle};
12
use crate::engine::{
13
    CanonicOperatorName, ExecutionContext, InitializedSources, Operator, QueryProcessor,
14
    RasterOperator, SingleRasterSource, WorkflowOperatorPath,
15
};
16
use crate::{
17
    adapters::SubQueryTileAggregator,
18
    engine::{
19
        InitializedRasterOperator, OperatorName, RasterQueryProcessor, RasterResultDescriptor,
20
        TypedRasterQueryProcessor,
21
    },
22
    error,
23
    util::Result,
24
};
25
use async_trait::async_trait;
26
use geoengine_datatypes::primitives::{
27
    BandSelection, RasterQueryRectangle, SpatialPartition2D, TimeInstance,
28
};
29
use geoengine_datatypes::raster::{Pixel, RasterDataType, RasterTile2D};
30
use geoengine_datatypes::{primitives::TimeStep, raster::TilingSpecification};
31
use log::debug;
32
use serde::{Deserialize, Serialize};
33
use snafu::ensure;
34
use std::marker::PhantomData;
35

36
use typetag;
37

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

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

71
pub type TemporalRasterAggregation =
72
    Operator<TemporalRasterAggregationParameters, SingleRasterSource>;
73

74
impl OperatorName for TemporalRasterAggregation {
75
    const TYPE_NAME: &'static str = "TemporalRasterAggregation";
76
}
77

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

88
        let name = CanonicOperatorName::from(&self);
20✔
89

90
        let initialized_source = self.sources.initialize_sources(path, context).await?;
20✔
91
        let source = initialized_source.raster;
20✔
92

20✔
93
        debug!(
20✔
94
            "Initializing TemporalRasterAggregation with {:?}.",
×
95
            &self.params
×
96
        );
97

98
        let mut out_result_descriptor = source.result_descriptor().clone();
20✔
99

100
        if let Some(output_type) = self.params.output_type {
20✔
101
            out_result_descriptor.data_type = output_type;
1✔
102
        };
19✔
103

104
        let initialized_operator = InitializedTemporalRasterAggregation {
20✔
105
            name,
20✔
106
            aggregation_type: self.params.aggregation,
20✔
107
            window: self.params.window,
20✔
108
            window_reference: self
20✔
109
                .params
20✔
110
                .window_reference
20✔
111
                .unwrap_or(TimeInstance::EPOCH_START),
20✔
112
            result_descriptor: out_result_descriptor,
20✔
113
            source,
20✔
114
            tiling_specification: context.tiling_specification(),
20✔
115
            output_type: self.params.output_type,
20✔
116
        };
20✔
117

20✔
118
        Ok(initialized_operator.boxed())
20✔
119
    }
40✔
120

121
    span_fn!(TemporalRasterAggregation);
×
122
}
123

124
pub struct InitializedTemporalRasterAggregation {
125
    name: CanonicOperatorName,
126
    aggregation_type: Aggregation,
127
    window: TimeStep,
128
    window_reference: TimeInstance,
129
    source: Box<dyn InitializedRasterOperator>,
130
    result_descriptor: RasterResultDescriptor,
131
    tiling_specification: TilingSpecification,
132
    output_type: Option<RasterDataType>,
133
}
134

135
impl InitializedRasterOperator for InitializedTemporalRasterAggregation {
136
    fn result_descriptor(&self) -> &RasterResultDescriptor {
×
137
        &self.result_descriptor
×
138
    }
×
139

140
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor> {
20✔
141
        let source_processor = self.source.query_processor()?;
20✔
142

143
        let source_processor: TypedRasterQueryProcessor = match self.output_type {
20✔
144
            Some(RasterDataType::U8) => source_processor.into_u8().into(),
×
145
            Some(RasterDataType::U16) => source_processor.into_u16().into(),
1✔
146
            Some(RasterDataType::U32) => source_processor.into_u32().into(),
×
147
            Some(RasterDataType::U64) => source_processor.into_u64().into(),
×
148
            Some(RasterDataType::I8) => source_processor.into_i8().into(),
×
149
            Some(RasterDataType::I16) => source_processor.into_i16().into(),
×
150
            Some(RasterDataType::I32) => source_processor.into_i32().into(),
×
151
            Some(RasterDataType::I64) => source_processor.into_i64().into(),
×
152
            Some(RasterDataType::F32) => source_processor.into_f32().into(),
×
153
            Some(RasterDataType::F64) => source_processor.into_f64().into(),
×
154
            // use the same output type as the input type
155
            None => source_processor,
19✔
156
        };
157

158
        let res = call_on_generic_raster_processor!(
20✔
159
            source_processor, p =>
20✔
160
            TemporalRasterAggregationProcessor::new(
161
                self.aggregation_type,
20✔
162
                self.window,
20✔
163
                self.window_reference,
20✔
164
                p,
20✔
165
                self.tiling_specification,
20✔
166
            ).boxed()
20✔
167
            .into()
20✔
168
        );
169

170
        Ok(res)
20✔
171
    }
20✔
172

173
    fn canonic_name(&self) -> CanonicOperatorName {
×
174
        self.name.clone()
×
175
    }
×
176
}
177

178
pub struct TemporalRasterAggregationProcessor<Q, P>
179
where
180
    Q: RasterQueryProcessor<RasterType = P>,
181
    P: Pixel,
182
{
183
    aggregation_type: Aggregation,
184
    window: TimeStep,
185
    window_reference: TimeInstance,
186
    source: Q,
187
    tiling_specification: TilingSpecification,
188
}
189

190
impl<Q, P> TemporalRasterAggregationProcessor<Q, P>
191
where
192
    Q: RasterQueryProcessor<RasterType = P>
193
        + QueryProcessor<
194
            Output = RasterTile2D<P>,
195
            SpatialBounds = SpatialPartition2D,
196
            Selection = BandSelection,
197
        >,
198
    P: Pixel,
199
{
200
    fn new(
20✔
201
        aggregation_type: Aggregation,
20✔
202
        window: TimeStep,
20✔
203
        window_reference: TimeInstance,
20✔
204
        source: Q,
20✔
205
        tiling_specification: TilingSpecification,
20✔
206
    ) -> Self {
20✔
207
        Self {
20✔
208
            aggregation_type,
20✔
209
            window,
20✔
210
            window_reference,
20✔
211
            source,
20✔
212
            tiling_specification,
20✔
213
        }
20✔
214
    }
20✔
215

216
    fn create_subquery<F: TemporalRasterPixelAggregator<P> + 'static, FoldFn>(
18✔
217
        &self,
18✔
218
        fold_fn: FoldFn,
18✔
219
    ) -> super::subquery::TemporalRasterAggregationSubQuery<FoldFn, P, F> {
18✔
220
        super::subquery::TemporalRasterAggregationSubQuery {
18✔
221
            fold_fn,
18✔
222
            step: self.window,
18✔
223
            step_reference: self.window_reference,
18✔
224
            _phantom_pixel_type: PhantomData,
18✔
225
        }
18✔
226
    }
18✔
227

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

240
    fn create_subquery_last<F>(
2✔
241
        &self,
2✔
242
        fold_fn: F,
2✔
243
    ) -> TemporalRasterAggregationSubQueryNoDataOnly<F, P> {
2✔
244
        TemporalRasterAggregationSubQueryNoDataOnly {
2✔
245
            fold_fn,
2✔
246
            step: self.window,
2✔
247
            step_reference: self.window_reference,
2✔
248
            _phantom_pixel_type: PhantomData,
2✔
249
        }
2✔
250
    }
2✔
251

252
    #[allow(clippy::too_many_lines)]
253
    fn create_subquery_adapter_stream_for_single_band<'a>(
21✔
254
        &'a self,
21✔
255
        query_rect_to_answer: RasterQueryRectangle,
21✔
256
        band: usize,
21✔
257
        ctx: &'a dyn crate::engine::QueryContext,
21✔
258
    ) -> futures::stream::BoxStream<'a, Result<RasterTile2D<P>>> {
21✔
259
        let mut query = query_rect_to_answer;
21✔
260
        query.attributes = band.into();
21✔
261

21✔
262
        match self.aggregation_type {
21✔
263
            Aggregation::Min {
264
                ignore_no_data: true,
NEW
265
            } => self
×
266
                .create_subquery(
×
267
                    super::subquery::subquery_all_tiles_fold_fn::<
×
268
                        P,
×
269
                        MinPixelAggregatorIngoringNoData,
×
270
                    >,
×
271
                )
×
272
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
×
NEW
273
                .expect("no tiles must be skipped in Aggregation::Min"),
×
274
            Aggregation::Min {
275
                ignore_no_data: false,
276
            } => self
1✔
277
                .create_subquery(
1✔
278
                    super::subquery::subquery_all_tiles_fold_fn::<P, MinPixelAggregator>,
1✔
279
                )
1✔
280
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
281
                .expect("no tiles must be skipped in Aggregation::Min"),
1✔
282
            Aggregation::Max {
283
                ignore_no_data: true,
284
            } => self
1✔
285
                .create_subquery(
1✔
286
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
287
                        P,
1✔
288
                        MaxPixelAggregatorIngoringNoData,
1✔
289
                    >,
1✔
290
                )
1✔
291
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
292
                .expect("no tiles must be skipped in Aggregation::Max"),
1✔
293
            Aggregation::Max {
294
                ignore_no_data: false,
295
            } => self
3✔
296
                .create_subquery(
3✔
297
                    super::subquery::subquery_all_tiles_fold_fn::<P, MaxPixelAggregator>,
3✔
298
                )
3✔
299
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
3✔
300
                .expect("no tiles must be skipped in Aggregation::Max"),
3✔
301

302
            Aggregation::First {
303
                ignore_no_data: true,
304
            } => self
1✔
305
                .create_subquery(
1✔
306
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
307
                        P,
1✔
308
                        FirstPixelAggregatorIngoringNoData,
1✔
309
                    >,
1✔
310
                )
1✔
311
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
312
                .expect("no tiles must be skipped in Aggregation::First"),
1✔
313
            Aggregation::First {
314
                ignore_no_data: false,
315
            } => self
1✔
316
                .create_subquery_first(first_tile_fold_future::<P>)
1✔
317
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
318
                .expect("no tiles must be skipped in Aggregation::First"),
1✔
319
            Aggregation::Last {
320
                ignore_no_data: true,
321
            } => self
1✔
322
                .create_subquery(
1✔
323
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
324
                        P,
1✔
325
                        LastPixelAggregatorIngoringNoData,
1✔
326
                    >,
1✔
327
                )
1✔
328
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
329
                .expect("no tiles must be skipped in Aggregation::Last"),
1✔
330

331
            Aggregation::Last {
332
                ignore_no_data: false,
333
            } => self
2✔
334
                .create_subquery_last(last_tile_fold_future::<P>)
2✔
335
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
2✔
336
                .expect("no tiles must be skipped in Aggregation::Last"),
2✔
337

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

350
            Aggregation::Mean {
351
                ignore_no_data: false,
352
            } => self
1✔
353
                .create_subquery(
1✔
354
                    super::subquery::subquery_all_tiles_fold_fn::<P, MeanPixelAggregator>,
1✔
355
                )
1✔
356
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
357
                .expect("no tiles must be skipped in Aggregation::Mean"),
1✔
358

359
            Aggregation::Sum {
360
                ignore_no_data: true,
361
            } => self
1✔
362
                .create_subquery(
1✔
363
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
364
                        P,
1✔
365
                        SumPixelAggregatorIngoringNoData,
1✔
366
                    >,
1✔
367
                )
1✔
368
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
369
                .expect("no tiles must be skipped in Aggregation::Sum"),
1✔
370

371
            Aggregation::Sum {
372
                ignore_no_data: false,
373
            } => self
5✔
374
                .create_subquery(
5✔
375
                    super::subquery::subquery_all_tiles_fold_fn::<P, SumPixelAggregator>,
5✔
376
                )
5✔
377
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
5✔
378
                .expect("no tiles must be skipped in Aggregation::Sum"),
5✔
379

380
            Aggregation::Count {
381
                ignore_no_data: true,
382
            } => self
1✔
383
                .create_subquery(
1✔
384
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
385
                        P,
1✔
386
                        CountPixelAggregatorIngoringNoData,
1✔
387
                    >,
1✔
388
                )
1✔
389
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
390
                .expect("no tiles must be skipped in Aggregation::Sum"),
1✔
391

392
            Aggregation::Count {
393
                ignore_no_data: false,
394
            } => self
2✔
395
                .create_subquery(
2✔
396
                    super::subquery::subquery_all_tiles_fold_fn::<P, CountPixelAggregator>,
2✔
397
                )
2✔
398
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
2✔
399
                .expect("no tiles must be skipped in Aggregation::Sum"),
2✔
400
        }
401
    }
21✔
402
}
403

404
#[async_trait]
405
impl<Q, P> QueryProcessor for TemporalRasterAggregationProcessor<Q, P>
406
where
407
    Q: QueryProcessor<
408
        Output = RasterTile2D<P>,
409
        SpatialBounds = SpatialPartition2D,
410
        Selection = BandSelection,
411
    >,
412
    P: Pixel,
413
{
414
    type Output = RasterTile2D<P>;
415
    type SpatialBounds = SpatialPartition2D;
416
    type Selection = BandSelection;
417
    async fn _query<'a>(
20✔
418
        &'a self,
20✔
419
        query: RasterQueryRectangle,
20✔
420
        ctx: &'a dyn crate::engine::QueryContext,
20✔
421
    ) -> Result<futures::stream::BoxStream<'a, Result<Self::Output>>> {
20✔
422
        if query.attributes.count() == 1 {
20✔
423
            // special case of single band query requires no tile stacking
424
            return Ok(self.create_subquery_adapter_stream_for_single_band(
19✔
425
                query.clone(),
19✔
426
                query.attributes.as_slice()[0],
19✔
427
                ctx,
19✔
428
            ));
19✔
429
        }
1✔
430

1✔
431
        // compute the aggreation for each band separately and stack the streams to get a multi band raster tile stream
1✔
432
        let band_streams = query
1✔
433
            .attributes
1✔
434
            .as_slice()
1✔
435
            .iter()
1✔
436
            .map(|band| StreamBundle {
2✔
437
                stream: self.create_subquery_adapter_stream_for_single_band(
2✔
438
                    query.clone(),
2✔
439
                    *band,
2✔
440
                    ctx,
2✔
441
                ),
2✔
442
                bands: 1,
2✔
443
            })
2✔
444
            .collect::<Vec<_>>();
1✔
445

1✔
446
        Ok(Box::pin(RasterStackerAdapter::new(band_streams)?))
1✔
447
    }
40✔
448
}
449

450
#[cfg(test)]
451
mod tests {
452
    use futures::stream::StreamExt;
453
    use geoengine_datatypes::{
454
        primitives::{CacheHint, Measurement, SpatialResolution, TimeInterval},
455
        raster::{
456
            EmptyGrid, EmptyGrid2D, Grid2D, GridOrEmpty, MaskedGrid2D, RasterDataType,
457
            TileInformation, TilesEqualIgnoringCacheHint,
458
        },
459
        spatial_reference::SpatialReference,
460
        util::test::TestDefault,
461
    };
462

463
    use crate::{
464
        engine::{
465
            MockExecutionContext, MockQueryContext, MultipleRasterSources, RasterBandDescriptors,
466
        },
467
        mock::{MockRasterSource, MockRasterSourceParams},
468
        processing::{
469
            raster_stacker::{RasterStacker, RasterStackerParams},
470
            Expression, ExpressionParams, ExpressionSources,
471
        },
472
    };
473

474
    use super::*;
475

476
    #[tokio::test]
1✔
477
    #[allow(clippy::too_many_lines)]
478
    async fn test_min() {
1✔
479
        let raster_tiles = make_raster();
1✔
480

1✔
481
        let mrs = MockRasterSource {
1✔
482
            params: MockRasterSourceParams {
1✔
483
                data: raster_tiles,
1✔
484
                result_descriptor: RasterResultDescriptor {
1✔
485
                    data_type: RasterDataType::U8,
1✔
486
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
487
                    time: None,
1✔
488
                    bbox: None,
1✔
489
                    resolution: None,
1✔
490
                    bands: RasterBandDescriptors::new_single_band(),
1✔
491
                },
1✔
492
            },
1✔
493
        }
1✔
494
        .boxed();
1✔
495

1✔
496
        let agg = TemporalRasterAggregation {
1✔
497
            params: TemporalRasterAggregationParameters {
1✔
498
                aggregation: Aggregation::Min {
1✔
499
                    ignore_no_data: false,
1✔
500
                },
1✔
501
                window: TimeStep {
1✔
502
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
503
                    step: 20,
1✔
504
                },
1✔
505
                window_reference: None,
1✔
506
                output_type: None,
1✔
507
            },
1✔
508
            sources: SingleRasterSource { raster: mrs },
1✔
509
        }
1✔
510
        .boxed();
1✔
511

1✔
512
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
513
            (0., 0.).into(),
1✔
514
            [3, 2].into(),
1✔
515
        ));
1✔
516
        let query_rect = RasterQueryRectangle {
1✔
517
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
518
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
519
            spatial_resolution: SpatialResolution::one(),
1✔
520
            attributes: BandSelection::first(),
1✔
521
        };
1✔
522
        let query_ctx = MockQueryContext::test_default();
1✔
523

524
        let qp = agg
1✔
525
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
526
            .await
×
527
            .unwrap()
1✔
528
            .query_processor()
1✔
529
            .unwrap()
1✔
530
            .get_u8()
1✔
531
            .unwrap();
1✔
532

533
        let result = qp
1✔
534
            .query(query_rect, &query_ctx)
1✔
535
            .await
×
536
            .unwrap()
1✔
537
            .collect::<Vec<_>>()
1✔
538
            .await;
8✔
539

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

542
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
543
            &RasterTile2D::new_with_tile_info(
1✔
544
                TimeInterval::new_unchecked(0, 20),
1✔
545
                TileInformation {
1✔
546
                    global_tile_position: [-1, 0].into(),
1✔
547
                    tile_size_in_pixels: [3, 2].into(),
1✔
548
                    global_geo_transform: TestDefault::test_default(),
1✔
549
                },
1✔
550
                0,
1✔
551
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
552
                CacheHint::default()
1✔
553
            )
1✔
554
        ));
1✔
555

556
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
557
            &RasterTile2D::new_with_tile_info(
1✔
558
                TimeInterval::new_unchecked(0, 20),
1✔
559
                TileInformation {
1✔
560
                    global_tile_position: [-1, 1].into(),
1✔
561
                    tile_size_in_pixels: [3, 2].into(),
1✔
562
                    global_geo_transform: TestDefault::test_default(),
1✔
563
                },
1✔
564
                0,
1✔
565
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
1✔
566
                CacheHint::default()
1✔
567
            )
1✔
568
        ));
1✔
569

570
        assert!(result[2].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
571
            &RasterTile2D::new_with_tile_info(
1✔
572
                TimeInterval::new_unchecked(20, 40),
1✔
573
                TileInformation {
1✔
574
                    global_tile_position: [-1, 0].into(),
1✔
575
                    tile_size_in_pixels: [3, 2].into(),
1✔
576
                    global_geo_transform: TestDefault::test_default(),
1✔
577
                },
1✔
578
                0,
1✔
579
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
580
                CacheHint::default()
1✔
581
            )
1✔
582
        ));
1✔
583

584
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
585
            &RasterTile2D::new_with_tile_info(
1✔
586
                TimeInterval::new_unchecked(20, 40),
1✔
587
                TileInformation {
1✔
588
                    global_tile_position: [-1, 1].into(),
1✔
589
                    tile_size_in_pixels: [3, 2].into(),
1✔
590
                    global_geo_transform: TestDefault::test_default(),
1✔
591
                },
1✔
592
                0,
1✔
593
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
1✔
594
                CacheHint::default()
1✔
595
            )
1✔
596
        ));
1✔
597
    }
598

599
    #[tokio::test]
1✔
600
    #[allow(clippy::too_many_lines)]
601
    async fn test_max() {
1✔
602
        let raster_tiles = make_raster();
1✔
603

1✔
604
        let mrs = MockRasterSource {
1✔
605
            params: MockRasterSourceParams {
1✔
606
                data: raster_tiles,
1✔
607
                result_descriptor: RasterResultDescriptor {
1✔
608
                    data_type: RasterDataType::U8,
1✔
609
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
610
                    time: None,
1✔
611
                    bbox: None,
1✔
612
                    resolution: None,
1✔
613
                    bands: RasterBandDescriptors::new_single_band(),
1✔
614
                },
1✔
615
            },
1✔
616
        }
1✔
617
        .boxed();
1✔
618

1✔
619
        let agg = TemporalRasterAggregation {
1✔
620
            params: TemporalRasterAggregationParameters {
1✔
621
                aggregation: Aggregation::Max {
1✔
622
                    ignore_no_data: false,
1✔
623
                },
1✔
624
                window: TimeStep {
1✔
625
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
626
                    step: 20,
1✔
627
                },
1✔
628
                window_reference: None,
1✔
629
                output_type: None,
1✔
630
            },
1✔
631
            sources: SingleRasterSource { raster: mrs },
1✔
632
        }
1✔
633
        .boxed();
1✔
634

1✔
635
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
636
            (0., 0.).into(),
1✔
637
            [3, 2].into(),
1✔
638
        ));
1✔
639
        let query_rect = RasterQueryRectangle {
1✔
640
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
641
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
642
            spatial_resolution: SpatialResolution::one(),
1✔
643
            attributes: BandSelection::first(),
1✔
644
        };
1✔
645
        let query_ctx = MockQueryContext::test_default();
1✔
646

647
        let qp = agg
1✔
648
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
649
            .await
×
650
            .unwrap()
1✔
651
            .query_processor()
1✔
652
            .unwrap()
1✔
653
            .get_u8()
1✔
654
            .unwrap();
1✔
655

656
        let result = qp
1✔
657
            .query(query_rect, &query_ctx)
1✔
658
            .await
×
659
            .unwrap()
1✔
660
            .collect::<Vec<_>>()
1✔
661
            .await;
8✔
662

663
        assert_eq!(result.len(), 4);
1✔
664

665
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
666
            &RasterTile2D::new_with_tile_info(
1✔
667
                TimeInterval::new_unchecked(0, 20),
1✔
668
                TileInformation {
1✔
669
                    global_tile_position: [-1, 0].into(),
1✔
670
                    tile_size_in_pixels: [3, 2].into(),
1✔
671
                    global_geo_transform: TestDefault::test_default(),
1✔
672
                },
1✔
673
                0,
1✔
674
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
675
                CacheHint::default()
1✔
676
            )
1✔
677
        ));
1✔
678

679
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
680
            &RasterTile2D::new_with_tile_info(
1✔
681
                TimeInterval::new_unchecked(0, 20),
1✔
682
                TileInformation {
1✔
683
                    global_tile_position: [-1, 1].into(),
1✔
684
                    tile_size_in_pixels: [3, 2].into(),
1✔
685
                    global_geo_transform: TestDefault::test_default(),
1✔
686
                },
1✔
687
                0,
1✔
688
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
689
                CacheHint::default()
1✔
690
            )
1✔
691
        ));
1✔
692

693
        assert!(result[2].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
694
            &RasterTile2D::new_with_tile_info(
1✔
695
                TimeInterval::new_unchecked(20, 40),
1✔
696
                TileInformation {
1✔
697
                    global_tile_position: [-1, 0].into(),
1✔
698
                    tile_size_in_pixels: [3, 2].into(),
1✔
699
                    global_geo_transform: TestDefault::test_default(),
1✔
700
                },
1✔
701
                0,
1✔
702
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
703
                CacheHint::default()
1✔
704
            )
1✔
705
        ));
1✔
706

707
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
708
            &RasterTile2D::new_with_tile_info(
1✔
709
                TimeInterval::new_unchecked(20, 40),
1✔
710
                TileInformation {
1✔
711
                    global_tile_position: [-1, 1].into(),
1✔
712
                    tile_size_in_pixels: [3, 2].into(),
1✔
713
                    global_geo_transform: TestDefault::test_default(),
1✔
714
                },
1✔
715
                0,
1✔
716
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
717
                CacheHint::default()
1✔
718
            )
1✔
719
        ));
1✔
720
    }
721

722
    #[tokio::test]
1✔
723
    #[allow(clippy::too_many_lines)]
724
    async fn test_max_with_no_data() {
1✔
725
        let raster_tiles = make_raster(); // TODO: switch to make_raster_with_no_data?
1✔
726

1✔
727
        let mrs = MockRasterSource {
1✔
728
            params: MockRasterSourceParams {
1✔
729
                data: raster_tiles,
1✔
730
                result_descriptor: RasterResultDescriptor {
1✔
731
                    data_type: RasterDataType::U8,
1✔
732
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
733
                    time: None,
1✔
734
                    bbox: None,
1✔
735
                    resolution: None,
1✔
736
                    bands: RasterBandDescriptors::new_single_band(),
1✔
737
                },
1✔
738
            },
1✔
739
        }
1✔
740
        .boxed();
1✔
741

1✔
742
        let agg = TemporalRasterAggregation {
1✔
743
            params: TemporalRasterAggregationParameters {
1✔
744
                aggregation: Aggregation::Max {
1✔
745
                    ignore_no_data: false,
1✔
746
                },
1✔
747
                window: TimeStep {
1✔
748
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
749
                    step: 20,
1✔
750
                },
1✔
751
                window_reference: None,
1✔
752
                output_type: None,
1✔
753
            },
1✔
754
            sources: SingleRasterSource { raster: mrs },
1✔
755
        }
1✔
756
        .boxed();
1✔
757

1✔
758
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
759
            (0., 0.).into(),
1✔
760
            [3, 2].into(),
1✔
761
        ));
1✔
762
        let query_rect = RasterQueryRectangle {
1✔
763
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
764
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
765
            spatial_resolution: SpatialResolution::one(),
1✔
766
            attributes: BandSelection::first(),
1✔
767
        };
1✔
768
        let query_ctx = MockQueryContext::test_default();
1✔
769

770
        let qp = agg
1✔
771
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
772
            .await
×
773
            .unwrap()
1✔
774
            .query_processor()
1✔
775
            .unwrap()
1✔
776
            .get_u8()
1✔
777
            .unwrap();
1✔
778

779
        let result = qp
1✔
780
            .query(query_rect, &query_ctx)
1✔
781
            .await
×
782
            .unwrap()
1✔
783
            .collect::<Vec<_>>()
1✔
784
            .await;
8✔
785

786
        assert_eq!(result.len(), 4);
1✔
787

788
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
789
            &RasterTile2D::new_with_tile_info(
1✔
790
                TimeInterval::new_unchecked(0, 20),
1✔
791
                TileInformation {
1✔
792
                    global_tile_position: [-1, 0].into(),
1✔
793
                    tile_size_in_pixels: [3, 2].into(),
1✔
794
                    global_geo_transform: TestDefault::test_default(),
1✔
795
                },
1✔
796
                0,
1✔
797
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7],).unwrap()),
1✔
798
                CacheHint::default()
1✔
799
            )
1✔
800
        ));
1✔
801

802
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
803
            &RasterTile2D::new_with_tile_info(
1✔
804
                TimeInterval::new_unchecked(0, 20),
1✔
805
                TileInformation {
1✔
806
                    global_tile_position: [-1, 1].into(),
1✔
807
                    tile_size_in_pixels: [3, 2].into(),
1✔
808
                    global_geo_transform: TestDefault::test_default(),
1✔
809
                },
1✔
810
                0,
1✔
811
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
812
                CacheHint::default()
1✔
813
            )
1✔
814
        ));
1✔
815

816
        assert!(result[2].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
817
            &RasterTile2D::new_with_tile_info(
1✔
818
                TimeInterval::new_unchecked(20, 40),
1✔
819
                TileInformation {
1✔
820
                    global_tile_position: [-1, 0].into(),
1✔
821
                    tile_size_in_pixels: [3, 2].into(),
1✔
822
                    global_geo_transform: TestDefault::test_default(),
1✔
823
                },
1✔
824
                0,
1✔
825
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
826
                CacheHint::default()
1✔
827
            )
1✔
828
        ));
1✔
829

830
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
831
            &RasterTile2D::new_with_tile_info(
1✔
832
                TimeInterval::new_unchecked(20, 40),
1✔
833
                TileInformation {
1✔
834
                    global_tile_position: [-1, 1].into(),
1✔
835
                    tile_size_in_pixels: [3, 2].into(),
1✔
836
                    global_geo_transform: TestDefault::test_default(),
1✔
837
                },
1✔
838
                0,
1✔
839
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
840
                CacheHint::default()
1✔
841
            )
1✔
842
        ));
1✔
843
    }
844

845
    #[tokio::test]
1✔
846
    #[allow(clippy::too_many_lines)]
847
    async fn test_max_with_no_data_but_ignoring_it() {
1✔
848
        let raster_tiles = make_raster(); // TODO: switch to make_raster_with_no_data?
1✔
849

1✔
850
        let mrs = MockRasterSource {
1✔
851
            params: MockRasterSourceParams {
1✔
852
                data: raster_tiles,
1✔
853
                result_descriptor: RasterResultDescriptor {
1✔
854
                    data_type: RasterDataType::U8,
1✔
855
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
856
                    time: None,
1✔
857
                    bbox: None,
1✔
858
                    resolution: None,
1✔
859
                    bands: RasterBandDescriptors::new_single_band(),
1✔
860
                },
1✔
861
            },
1✔
862
        }
1✔
863
        .boxed();
1✔
864

1✔
865
        let agg = TemporalRasterAggregation {
1✔
866
            params: TemporalRasterAggregationParameters {
1✔
867
                aggregation: Aggregation::Max {
1✔
868
                    ignore_no_data: true,
1✔
869
                },
1✔
870
                window: TimeStep {
1✔
871
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
872
                    step: 20,
1✔
873
                },
1✔
874
                window_reference: None,
1✔
875
                output_type: None,
1✔
876
            },
1✔
877
            sources: SingleRasterSource { raster: mrs },
1✔
878
        }
1✔
879
        .boxed();
1✔
880

1✔
881
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
882
            (0., 0.).into(),
1✔
883
            [3, 2].into(),
1✔
884
        ));
1✔
885
        let query_rect = RasterQueryRectangle {
1✔
886
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
887
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
888
            spatial_resolution: SpatialResolution::one(),
1✔
889
            attributes: BandSelection::first(),
1✔
890
        };
1✔
891
        let query_ctx = MockQueryContext::test_default();
1✔
892

893
        let qp = agg
1✔
894
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
895
            .await
×
896
            .unwrap()
1✔
897
            .query_processor()
1✔
898
            .unwrap()
1✔
899
            .get_u8()
1✔
900
            .unwrap();
1✔
901

902
        let result = qp
1✔
903
            .query(query_rect, &query_ctx)
1✔
904
            .await
×
905
            .unwrap()
1✔
906
            .collect::<Vec<_>>()
1✔
907
            .await;
8✔
908

909
        assert_eq!(result.len(), 4);
1✔
910

911
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
912
            &RasterTile2D::new_with_tile_info(
1✔
913
                TimeInterval::new_unchecked(0, 20),
1✔
914
                TileInformation {
1✔
915
                    global_tile_position: [-1, 0].into(),
1✔
916
                    tile_size_in_pixels: [3, 2].into(),
1✔
917
                    global_geo_transform: TestDefault::test_default(),
1✔
918
                },
1✔
919
                0,
1✔
920
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
921
                CacheHint::default()
1✔
922
            )
1✔
923
        ));
1✔
924

925
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
926
            &RasterTile2D::new_with_tile_info(
1✔
927
                TimeInterval::new_unchecked(0, 20),
1✔
928
                TileInformation {
1✔
929
                    global_tile_position: [-1, 1].into(),
1✔
930
                    tile_size_in_pixels: [3, 2].into(),
1✔
931
                    global_geo_transform: TestDefault::test_default(),
1✔
932
                },
1✔
933
                0,
1✔
934
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
935
                CacheHint::default()
1✔
936
            )
1✔
937
        ));
1✔
938

939
        assert!(result[2].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
940
            &RasterTile2D::new_with_tile_info(
1✔
941
                TimeInterval::new_unchecked(20, 40),
1✔
942
                TileInformation {
1✔
943
                    global_tile_position: [-1, 0].into(),
1✔
944
                    tile_size_in_pixels: [3, 2].into(),
1✔
945
                    global_geo_transform: TestDefault::test_default(),
1✔
946
                },
1✔
947
                0,
1✔
948
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
949
                CacheHint::default()
1✔
950
            )
1✔
951
        ));
1✔
952

953
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
954
            &RasterTile2D::new_with_tile_info(
1✔
955
                TimeInterval::new_unchecked(20, 40),
1✔
956
                TileInformation {
1✔
957
                    global_tile_position: [-1, 1].into(),
1✔
958
                    tile_size_in_pixels: [3, 2].into(),
1✔
959
                    global_geo_transform: TestDefault::test_default(),
1✔
960
                },
1✔
961
                0,
1✔
962
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
963
                CacheHint::default()
1✔
964
            )
1✔
965
        ));
1✔
966
    }
967

968
    #[tokio::test]
1✔
969
    #[allow(clippy::too_many_lines)]
970
    async fn test_only_no_data() {
1✔
971
        let mrs = MockRasterSource {
1✔
972
            params: MockRasterSourceParams {
1✔
973
                data: vec![RasterTile2D::new_with_tile_info(
1✔
974
                    TimeInterval::new_unchecked(0, 20),
1✔
975
                    TileInformation {
1✔
976
                        global_tile_position: [-1, 0].into(),
1✔
977
                        tile_size_in_pixels: [3, 2].into(),
1✔
978
                        global_geo_transform: TestDefault::test_default(),
1✔
979
                    },
1✔
980
                    0,
1✔
981
                    GridOrEmpty::from(EmptyGrid2D::<u8>::new([3, 2].into())),
1✔
982
                    CacheHint::default(),
1✔
983
                )],
1✔
984
                result_descriptor: RasterResultDescriptor {
1✔
985
                    data_type: RasterDataType::U8,
1✔
986
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
987
                    time: None,
1✔
988
                    bbox: None,
1✔
989
                    resolution: None,
1✔
990
                    bands: RasterBandDescriptors::new_single_band(),
1✔
991
                },
1✔
992
            },
1✔
993
        }
1✔
994
        .boxed();
1✔
995

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

1✔
1012
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1013
            (0., 0.).into(),
1✔
1014
            [3, 2].into(),
1✔
1015
        ));
1✔
1016
        let query_rect = RasterQueryRectangle {
1✔
1017
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (2., 0.).into()),
1✔
1018
            time_interval: TimeInterval::new_unchecked(0, 20),
1✔
1019
            spatial_resolution: SpatialResolution::one(),
1✔
1020
            attributes: BandSelection::first(),
1✔
1021
        };
1✔
1022
        let query_ctx = MockQueryContext::test_default();
1✔
1023

1024
        let qp = agg
1✔
1025
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1026
            .await
×
1027
            .unwrap()
1✔
1028
            .query_processor()
1✔
1029
            .unwrap()
1✔
1030
            .get_u8()
1✔
1031
            .unwrap();
1✔
1032

1033
        let result = qp
1✔
1034
            .query(query_rect, &query_ctx)
1✔
1035
            .await
×
1036
            .unwrap()
1✔
1037
            .collect::<Vec<_>>()
1✔
1038
            .await;
1✔
1039

1040
        assert_eq!(result.len(), 1);
1✔
1041

1042
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1043
            &RasterTile2D::new_with_tile_info(
1✔
1044
                TimeInterval::new_unchecked(0, 20),
1✔
1045
                TileInformation {
1✔
1046
                    global_tile_position: [-1, 0].into(),
1✔
1047
                    tile_size_in_pixels: [3, 2].into(),
1✔
1048
                    global_geo_transform: TestDefault::test_default(),
1✔
1049
                },
1✔
1050
                0,
1✔
1051
                GridOrEmpty::Empty(EmptyGrid::new([3, 2].into())),
1✔
1052
                CacheHint::default()
1✔
1053
            )
1✔
1054
        ));
1✔
1055
    }
1056

1057
    #[tokio::test]
1✔
1058
    async fn test_first_with_no_data() {
1✔
1059
        let raster_tiles = make_raster_with_no_data();
1✔
1060

1✔
1061
        let mrs = MockRasterSource {
1✔
1062
            params: MockRasterSourceParams {
1✔
1063
                data: raster_tiles,
1✔
1064
                result_descriptor: RasterResultDescriptor {
1✔
1065
                    data_type: RasterDataType::U8,
1✔
1066
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1067
                    time: None,
1✔
1068
                    bbox: None,
1✔
1069
                    resolution: None,
1✔
1070
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1071
                },
1✔
1072
            },
1✔
1073
        }
1✔
1074
        .boxed();
1✔
1075

1✔
1076
        let agg = TemporalRasterAggregation {
1✔
1077
            params: TemporalRasterAggregationParameters {
1✔
1078
                aggregation: Aggregation::First {
1✔
1079
                    ignore_no_data: true,
1✔
1080
                },
1✔
1081
                window: TimeStep {
1✔
1082
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1083
                    step: 30,
1✔
1084
                },
1✔
1085
                window_reference: None,
1✔
1086
                output_type: None,
1✔
1087
            },
1✔
1088
            sources: SingleRasterSource { raster: mrs },
1✔
1089
        }
1✔
1090
        .boxed();
1✔
1091

1✔
1092
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1093
            (0., 0.).into(),
1✔
1094
            [3, 2].into(),
1✔
1095
        ));
1✔
1096
        let query_rect = RasterQueryRectangle {
1✔
1097
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1098
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1099
            spatial_resolution: SpatialResolution::one(),
1✔
1100
            attributes: BandSelection::first(),
1✔
1101
        };
1✔
1102
        let query_ctx = MockQueryContext::test_default();
1✔
1103

1104
        let qp = agg
1✔
1105
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1106
            .await
×
1107
            .unwrap()
1✔
1108
            .query_processor()
1✔
1109
            .unwrap()
1✔
1110
            .get_u8()
1✔
1111
            .unwrap();
1✔
1112

1113
        let result = qp
1✔
1114
            .query(query_rect, &query_ctx)
1✔
1115
            .await
×
1116
            .unwrap()
1✔
1117
            .collect::<Vec<_>>()
1✔
1118
            .await;
6✔
1119

1120
        assert_eq!(result.len(), 2);
1✔
1121

1122
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1123
            &RasterTile2D::new_with_tile_info(
1✔
1124
                TimeInterval::new_unchecked(0, 30),
1✔
1125
                TileInformation {
1✔
1126
                    global_tile_position: [-1, 0].into(),
1✔
1127
                    tile_size_in_pixels: [3, 2].into(),
1✔
1128
                    global_geo_transform: TestDefault::test_default(),
1✔
1129
                },
1✔
1130
                0,
1✔
1131
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 16, 11, 12]).unwrap()),
1✔
1132
                CacheHint::default()
1✔
1133
            )
1✔
1134
        ));
1✔
1135

1136
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1137
            &RasterTile2D::new_with_tile_info(
1✔
1138
                TimeInterval::new_unchecked(0, 30),
1✔
1139
                TileInformation {
1✔
1140
                    global_tile_position: [-1, 1].into(),
1✔
1141
                    tile_size_in_pixels: [3, 2].into(),
1✔
1142
                    global_geo_transform: TestDefault::test_default(),
1✔
1143
                },
1✔
1144
                0,
1✔
1145
                GridOrEmpty::from(
1✔
1146
                    MaskedGrid2D::new(
1✔
1147
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1148
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1149
                            .unwrap()
1✔
1150
                    )
1✔
1151
                    .unwrap()
1✔
1152
                ),
1✔
1153
                CacheHint::default()
1✔
1154
            )
1✔
1155
        ));
1✔
1156
    }
1157

1158
    #[tokio::test]
1✔
1159
    async fn test_last_with_no_data() {
1✔
1160
        let raster_tiles = make_raster_with_no_data();
1✔
1161

1✔
1162
        let mrs = MockRasterSource {
1✔
1163
            params: MockRasterSourceParams {
1✔
1164
                data: raster_tiles,
1✔
1165
                result_descriptor: RasterResultDescriptor {
1✔
1166
                    data_type: RasterDataType::U8,
1✔
1167
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1168
                    time: None,
1✔
1169
                    bbox: None,
1✔
1170
                    resolution: None,
1✔
1171
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1172
                },
1✔
1173
            },
1✔
1174
        }
1✔
1175
        .boxed();
1✔
1176

1✔
1177
        let agg = TemporalRasterAggregation {
1✔
1178
            params: TemporalRasterAggregationParameters {
1✔
1179
                aggregation: Aggregation::Last {
1✔
1180
                    ignore_no_data: true,
1✔
1181
                },
1✔
1182
                window: TimeStep {
1✔
1183
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1184
                    step: 30,
1✔
1185
                },
1✔
1186
                window_reference: None,
1✔
1187
                output_type: None,
1✔
1188
            },
1✔
1189
            sources: SingleRasterSource { raster: mrs },
1✔
1190
        }
1✔
1191
        .boxed();
1✔
1192

1✔
1193
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1194
            (0., 0.).into(),
1✔
1195
            [3, 2].into(),
1✔
1196
        ));
1✔
1197
        let query_rect = RasterQueryRectangle {
1✔
1198
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1199
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1200
            spatial_resolution: SpatialResolution::one(),
1✔
1201
            attributes: BandSelection::first(),
1✔
1202
        };
1✔
1203
        let query_ctx = MockQueryContext::test_default();
1✔
1204

1205
        let qp = agg
1✔
1206
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1207
            .await
×
1208
            .unwrap()
1✔
1209
            .query_processor()
1✔
1210
            .unwrap()
1✔
1211
            .get_u8()
1✔
1212
            .unwrap();
1✔
1213

1214
        let result = qp
1✔
1215
            .query(query_rect, &query_ctx)
1✔
1216
            .await
×
1217
            .unwrap()
1✔
1218
            .collect::<Vec<_>>()
1✔
1219
            .await;
6✔
1220

1221
        assert_eq!(result.len(), 2);
1✔
1222

1223
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1224
            &RasterTile2D::new_with_tile_info(
1✔
1225
                TimeInterval::new_unchecked(0, 30),
1✔
1226
                TileInformation {
1✔
1227
                    global_tile_position: [-1, 0].into(),
1✔
1228
                    tile_size_in_pixels: [3, 2].into(),
1✔
1229
                    global_geo_transform: TestDefault::test_default(),
1✔
1230
                },
1✔
1231
                0,
1✔
1232
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![13, 8, 15, 16, 17, 18]).unwrap()),
1✔
1233
                CacheHint::default()
1✔
1234
            )
1✔
1235
        ));
1✔
1236

1237
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1238
            &RasterTile2D::new_with_tile_info(
1✔
1239
                TimeInterval::new_unchecked(0, 30),
1✔
1240
                TileInformation {
1✔
1241
                    global_tile_position: [-1, 1].into(),
1✔
1242
                    tile_size_in_pixels: [3, 2].into(),
1✔
1243
                    global_geo_transform: TestDefault::test_default(),
1✔
1244
                },
1✔
1245
                0,
1✔
1246
                GridOrEmpty::from(
1✔
1247
                    MaskedGrid2D::new(
1✔
1248
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1249
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1250
                            .unwrap()
1✔
1251
                    )
1✔
1252
                    .unwrap()
1✔
1253
                ),
1✔
1254
                CacheHint::default()
1✔
1255
            )
1✔
1256
        ));
1✔
1257
    }
1258

1259
    #[tokio::test]
1✔
1260
    async fn test_last() {
1✔
1261
        let raster_tiles = make_raster_with_no_data();
1✔
1262

1✔
1263
        let mrs = MockRasterSource {
1✔
1264
            params: MockRasterSourceParams {
1✔
1265
                data: raster_tiles,
1✔
1266
                result_descriptor: RasterResultDescriptor {
1✔
1267
                    data_type: RasterDataType::U8,
1✔
1268
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1269
                    time: None,
1✔
1270
                    bbox: None,
1✔
1271
                    resolution: None,
1✔
1272
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1273
                },
1✔
1274
            },
1✔
1275
        }
1✔
1276
        .boxed();
1✔
1277

1✔
1278
        let agg = TemporalRasterAggregation {
1✔
1279
            params: TemporalRasterAggregationParameters {
1✔
1280
                aggregation: Aggregation::Last {
1✔
1281
                    ignore_no_data: false,
1✔
1282
                },
1✔
1283
                window: TimeStep {
1✔
1284
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1285
                    step: 30,
1✔
1286
                },
1✔
1287
                window_reference: None,
1✔
1288
                output_type: None,
1✔
1289
            },
1✔
1290
            sources: SingleRasterSource { raster: mrs },
1✔
1291
        }
1✔
1292
        .boxed();
1✔
1293

1✔
1294
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1295
            (0., 0.).into(),
1✔
1296
            [3, 2].into(),
1✔
1297
        ));
1✔
1298
        let query_rect = RasterQueryRectangle {
1✔
1299
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1300
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1301
            spatial_resolution: SpatialResolution::one(),
1✔
1302
            attributes: BandSelection::first(),
1✔
1303
        };
1✔
1304
        let query_ctx = MockQueryContext::test_default();
1✔
1305

1306
        let qp = agg
1✔
1307
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1308
            .await
×
1309
            .unwrap()
1✔
1310
            .query_processor()
1✔
1311
            .unwrap()
1✔
1312
            .get_u8()
1✔
1313
            .unwrap();
1✔
1314

1315
        let result = qp
1✔
1316
            .query(query_rect, &query_ctx)
1✔
1317
            .await
×
1318
            .unwrap()
1✔
1319
            .collect::<Vec<_>>()
1✔
1320
            .await;
8✔
1321

1322
        assert_eq!(result.len(), 2);
1✔
1323

1324
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1325
            &RasterTile2D::new_with_tile_info(
1✔
1326
                TimeInterval::new_unchecked(0, 30),
1✔
1327
                TileInformation {
1✔
1328
                    global_tile_position: [-1, 0].into(),
1✔
1329
                    tile_size_in_pixels: [3, 2].into(),
1✔
1330
                    global_geo_transform: TestDefault::test_default(),
1✔
1331
                },
1✔
1332
                0,
1✔
1333
                GridOrEmpty::from(
1✔
1334
                    MaskedGrid2D::new(
1✔
1335
                        Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
1✔
1336
                        Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
1✔
1337
                            .unwrap()
1✔
1338
                    )
1✔
1339
                    .unwrap()
1✔
1340
                ),
1✔
1341
                CacheHint::default()
1✔
1342
            )
1✔
1343
        ));
1✔
1344

1345
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1346
            &RasterTile2D::new_with_tile_info(
1✔
1347
                TimeInterval::new_unchecked(0, 30),
1✔
1348
                TileInformation {
1✔
1349
                    global_tile_position: [-1, 1].into(),
1✔
1350
                    tile_size_in_pixels: [3, 2].into(),
1✔
1351
                    global_geo_transform: TestDefault::test_default(),
1✔
1352
                },
1✔
1353
                0,
1✔
1354
                GridOrEmpty::Empty(EmptyGrid2D::new([3, 2].into())),
1✔
1355
                CacheHint::default()
1✔
1356
            )
1✔
1357
        ));
1✔
1358
    }
1359

1360
    #[tokio::test]
1✔
1361
    async fn test_first() {
1✔
1362
        let raster_tiles = make_raster_with_no_data();
1✔
1363

1✔
1364
        let mrs = MockRasterSource {
1✔
1365
            params: MockRasterSourceParams {
1✔
1366
                data: raster_tiles,
1✔
1367
                result_descriptor: RasterResultDescriptor {
1✔
1368
                    data_type: RasterDataType::U8,
1✔
1369
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1370
                    time: None,
1✔
1371
                    bbox: None,
1✔
1372
                    resolution: None,
1✔
1373
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1374
                },
1✔
1375
            },
1✔
1376
        }
1✔
1377
        .boxed();
1✔
1378

1✔
1379
        let agg = TemporalRasterAggregation {
1✔
1380
            params: TemporalRasterAggregationParameters {
1✔
1381
                aggregation: Aggregation::First {
1✔
1382
                    ignore_no_data: false,
1✔
1383
                },
1✔
1384
                window: TimeStep {
1✔
1385
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1386
                    step: 30,
1✔
1387
                },
1✔
1388
                window_reference: None,
1✔
1389
                output_type: None,
1✔
1390
            },
1✔
1391
            sources: SingleRasterSource { raster: mrs },
1✔
1392
        }
1✔
1393
        .boxed();
1✔
1394

1✔
1395
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1396
            (0., 0.).into(),
1✔
1397
            [3, 2].into(),
1✔
1398
        ));
1✔
1399
        let query_rect = RasterQueryRectangle {
1✔
1400
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1401
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1402
            spatial_resolution: SpatialResolution::one(),
1✔
1403
            attributes: BandSelection::first(),
1✔
1404
        };
1✔
1405
        let query_ctx = MockQueryContext::test_default();
1✔
1406

1407
        let qp = agg
1✔
1408
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1409
            .await
×
1410
            .unwrap()
1✔
1411
            .query_processor()
1✔
1412
            .unwrap()
1✔
1413
            .get_u8()
1✔
1414
            .unwrap();
1✔
1415

1416
        let result = qp
1✔
1417
            .query(query_rect, &query_ctx)
1✔
1418
            .await
×
1419
            .unwrap()
1✔
1420
            .collect::<Vec<_>>()
1✔
1421
            .await;
8✔
1422

1423
        assert_eq!(result.len(), 2);
1✔
1424

1425
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1426
            &RasterTile2D::new_with_tile_info(
1✔
1427
                TimeInterval::new_unchecked(0, 30),
1✔
1428
                TileInformation {
1✔
1429
                    global_tile_position: [-1, 0].into(),
1✔
1430
                    tile_size_in_pixels: [3, 2].into(),
1✔
1431
                    global_geo_transform: TestDefault::test_default(),
1✔
1432
                },
1✔
1433
                0,
1✔
1434
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
1435
                CacheHint::default()
1✔
1436
            )
1✔
1437
        ));
1✔
1438

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

1461
    #[tokio::test]
1✔
1462
    async fn test_mean_nodata() {
1✔
1463
        let raster_tiles = make_raster_with_no_data();
1✔
1464

1✔
1465
        let mrs = MockRasterSource {
1✔
1466
            params: MockRasterSourceParams {
1✔
1467
                data: raster_tiles,
1✔
1468
                result_descriptor: RasterResultDescriptor {
1✔
1469
                    data_type: RasterDataType::U8,
1✔
1470
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1471
                    time: None,
1✔
1472
                    bbox: None,
1✔
1473
                    resolution: None,
1✔
1474
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1475
                },
1✔
1476
            },
1✔
1477
        }
1✔
1478
        .boxed();
1✔
1479

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

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

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

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

1524
        assert_eq!(result.len(), 2);
1✔
1525

1526
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1527
            &RasterTile2D::new_with_tile_info(
1✔
1528
                TimeInterval::new_unchecked(0, 30),
1✔
1529
                TileInformation {
1✔
1530
                    global_tile_position: [-1, 0].into(),
1✔
1531
                    tile_size_in_pixels: [3, 2].into(),
1✔
1532
                    global_geo_transform: TestDefault::test_default(),
1✔
1533
                },
1✔
1534
                0,
1✔
1535
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
1536
                CacheHint::default()
1✔
1537
            )
1✔
1538
        ));
1✔
1539

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

1562
    #[tokio::test]
1✔
1563
    async fn test_mean_ignore_no_data() {
1✔
1564
        let raster_tiles = make_raster_with_no_data();
1✔
1565

1✔
1566
        let mrs = MockRasterSource {
1✔
1567
            params: MockRasterSourceParams {
1✔
1568
                data: raster_tiles,
1✔
1569
                result_descriptor: RasterResultDescriptor {
1✔
1570
                    data_type: RasterDataType::U8,
1✔
1571
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1572
                    time: None,
1✔
1573
                    bbox: None,
1✔
1574
                    resolution: None,
1✔
1575
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1576
                },
1✔
1577
            },
1✔
1578
        }
1✔
1579
        .boxed();
1✔
1580

1✔
1581
        let agg = TemporalRasterAggregation {
1✔
1582
            params: TemporalRasterAggregationParameters {
1✔
1583
                aggregation: Aggregation::Mean {
1✔
1584
                    ignore_no_data: true,
1✔
1585
                },
1✔
1586
                window: TimeStep {
1✔
1587
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1588
                    step: 30,
1✔
1589
                },
1✔
1590
                window_reference: None,
1✔
1591
                output_type: None,
1✔
1592
            },
1✔
1593
            sources: SingleRasterSource { raster: mrs },
1✔
1594
        }
1✔
1595
        .boxed();
1✔
1596

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

1609
        let qp = agg
1✔
1610
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1611
            .await
×
1612
            .unwrap()
1✔
1613
            .query_processor()
1✔
1614
            .unwrap()
1✔
1615
            .get_u8()
1✔
1616
            .unwrap();
1✔
1617

1618
        let result = qp
1✔
1619
            .raster_query(query_rect, &query_ctx)
1✔
1620
            .await
×
1621
            .unwrap()
1✔
1622
            .collect::<Vec<_>>()
1✔
1623
            .await;
6✔
1624

1625
        assert_eq!(result.len(), 2);
1✔
1626

1627
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1628
            &RasterTile2D::new_with_tile_info(
1✔
1629
                TimeInterval::new_unchecked(0, 30),
1✔
1630
                TileInformation {
1✔
1631
                    global_tile_position: [-1, 0].into(),
1✔
1632
                    tile_size_in_pixels: [3, 2].into(),
1✔
1633
                    global_geo_transform: TestDefault::test_default(),
1✔
1634
                },
1✔
1635
                0,
1✔
1636
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![10, 8, 12, 16, 14, 15]).unwrap()),
1✔
1637
                CacheHint::default()
1✔
1638
            )
1✔
1639
        ));
1✔
1640

1641
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1642
            &RasterTile2D::new_with_tile_info(
1✔
1643
                TimeInterval::new_unchecked(0, 30),
1✔
1644
                TileInformation {
1✔
1645
                    global_tile_position: [-1, 1].into(),
1✔
1646
                    tile_size_in_pixels: [3, 2].into(),
1✔
1647
                    global_geo_transform: TestDefault::test_default(),
1✔
1648
                },
1✔
1649
                0,
1✔
1650
                GridOrEmpty::from(
1✔
1651
                    MaskedGrid2D::new(
1✔
1652
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1653
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1654
                            .unwrap()
1✔
1655
                    )
1✔
1656
                    .unwrap()
1✔
1657
                ),
1✔
1658
                CacheHint::default()
1✔
1659
            )
1✔
1660
        ));
1✔
1661
    }
1662

1663
    #[tokio::test]
1✔
1664
    #[allow(clippy::too_many_lines)]
1665
    async fn test_sum_without_nodata() {
1✔
1666
        let operator = TemporalRasterAggregation {
1✔
1667
            params: TemporalRasterAggregationParameters {
1✔
1668
                aggregation: Aggregation::Sum {
1✔
1669
                    ignore_no_data: false,
1✔
1670
                },
1✔
1671
                window: TimeStep {
1✔
1672
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1673
                    step: 20,
1✔
1674
                },
1✔
1675
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
1676
                output_type: None,
1✔
1677
            },
1✔
1678
            sources: SingleRasterSource {
1✔
1679
                raster: MockRasterSource {
1✔
1680
                    params: MockRasterSourceParams {
1✔
1681
                        data: make_raster(),
1✔
1682
                        result_descriptor: RasterResultDescriptor {
1✔
1683
                            data_type: RasterDataType::U8,
1✔
1684
                            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1685
                            time: None,
1✔
1686
                            bbox: None,
1✔
1687
                            resolution: None,
1✔
1688
                            bands: RasterBandDescriptors::new_single_band(),
1✔
1689
                        },
1✔
1690
                    },
1✔
1691
                }
1✔
1692
                .boxed(),
1✔
1693
            },
1✔
1694
        }
1✔
1695
        .boxed();
1✔
1696

1✔
1697
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1698
            (0., 0.).into(),
1✔
1699
            [3, 2].into(),
1✔
1700
        ));
1✔
1701
        let query_rect = RasterQueryRectangle {
1✔
1702
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1703
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1704
            spatial_resolution: SpatialResolution::one(),
1✔
1705
            attributes: BandSelection::first(),
1✔
1706
        };
1✔
1707
        let query_ctx = MockQueryContext::test_default();
1✔
1708

1709
        let query_processor = operator
1✔
1710
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1711
            .await
×
1712
            .unwrap()
1✔
1713
            .query_processor()
1✔
1714
            .unwrap()
1✔
1715
            .get_u8()
1✔
1716
            .unwrap();
1✔
1717

1718
        let result = query_processor
1✔
1719
            .raster_query(query_rect, &query_ctx)
1✔
1720
            .await
×
1721
            .unwrap()
1✔
1722
            .map(Result::unwrap)
1✔
1723
            .collect::<Vec<_>>()
1✔
1724
            .await;
8✔
1725

1726
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
1727
            RasterTile2D::new_with_tile_info(
1✔
1728
                TimeInterval::new_unchecked(0, 20),
1✔
1729
                TileInformation {
1✔
1730
                    global_tile_position: [-1, 0].into(),
1✔
1731
                    tile_size_in_pixels: [3, 2].into(),
1✔
1732
                    global_geo_transform: TestDefault::test_default(),
1✔
1733
                },
1✔
1734
                0,
1✔
1735
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1736
                    .unwrap()
1✔
1737
                    .into(),
1✔
1738
                CacheHint::default()
1✔
1739
            ),
1✔
1740
            RasterTile2D::new_with_tile_info(
1✔
1741
                TimeInterval::new_unchecked(0, 20),
1✔
1742
                TileInformation {
1✔
1743
                    global_tile_position: [-1, 1].into(),
1✔
1744
                    tile_size_in_pixels: [3, 2].into(),
1✔
1745
                    global_geo_transform: TestDefault::test_default(),
1✔
1746
                },
1✔
1747
                0,
1✔
1748
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1749
                    .unwrap()
1✔
1750
                    .into(),
1✔
1751
                CacheHint::default()
1✔
1752
            ),
1✔
1753
            RasterTile2D::new_with_tile_info(
1✔
1754
                TimeInterval::new_unchecked(20, 40),
1✔
1755
                TileInformation {
1✔
1756
                    global_tile_position: [-1, 0].into(),
1✔
1757
                    tile_size_in_pixels: [3, 2].into(),
1✔
1758
                    global_geo_transform: TestDefault::test_default(),
1✔
1759
                },
1✔
1760
                0,
1✔
1761
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1762
                    .unwrap()
1✔
1763
                    .into(),
1✔
1764
                CacheHint::default()
1✔
1765
            ),
1✔
1766
            RasterTile2D::new_with_tile_info(
1✔
1767
                TimeInterval::new_unchecked(20, 40),
1✔
1768
                TileInformation {
1✔
1769
                    global_tile_position: [-1, 1].into(),
1✔
1770
                    tile_size_in_pixels: [3, 2].into(),
1✔
1771
                    global_geo_transform: TestDefault::test_default(),
1✔
1772
                },
1✔
1773
                0,
1✔
1774
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1775
                    .unwrap()
1✔
1776
                    .into(),
1✔
1777
                CacheHint::default()
1✔
1778
            )
1✔
1779
        ]));
1✔
1780
    }
1781

1782
    #[tokio::test]
1✔
1783
    async fn test_sum_nodata() {
1✔
1784
        let raster_tiles = make_raster_with_no_data();
1✔
1785

1✔
1786
        let mrs = MockRasterSource {
1✔
1787
            params: MockRasterSourceParams {
1✔
1788
                data: raster_tiles,
1✔
1789
                result_descriptor: RasterResultDescriptor {
1✔
1790
                    data_type: RasterDataType::U8,
1✔
1791
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1792
                    time: None,
1✔
1793
                    bbox: None,
1✔
1794
                    resolution: None,
1✔
1795
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1796
                },
1✔
1797
            },
1✔
1798
        }
1✔
1799
        .boxed();
1✔
1800

1✔
1801
        let agg = TemporalRasterAggregation {
1✔
1802
            params: TemporalRasterAggregationParameters {
1✔
1803
                aggregation: Aggregation::Sum {
1✔
1804
                    ignore_no_data: false,
1✔
1805
                },
1✔
1806
                window: TimeStep {
1✔
1807
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1808
                    step: 30,
1✔
1809
                },
1✔
1810
                window_reference: None,
1✔
1811
                output_type: None,
1✔
1812
            },
1✔
1813
            sources: SingleRasterSource { raster: mrs },
1✔
1814
        }
1✔
1815
        .boxed();
1✔
1816

1✔
1817
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1818
            (0., 0.).into(),
1✔
1819
            [3, 2].into(),
1✔
1820
        ));
1✔
1821
        let query_rect = RasterQueryRectangle {
1✔
1822
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1823
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1824
            spatial_resolution: SpatialResolution::one(),
1✔
1825
            attributes: BandSelection::first(),
1✔
1826
        };
1✔
1827
        let query_ctx = MockQueryContext::test_default();
1✔
1828

1829
        let qp = agg
1✔
1830
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1831
            .await
×
1832
            .unwrap()
1✔
1833
            .query_processor()
1✔
1834
            .unwrap()
1✔
1835
            .get_u8()
1✔
1836
            .unwrap();
1✔
1837

1838
        let result = qp
1✔
1839
            .raster_query(query_rect, &query_ctx)
1✔
1840
            .await
×
1841
            .unwrap()
1✔
1842
            .collect::<Vec<_>>()
1✔
1843
            .await;
6✔
1844

1845
        assert_eq!(result.len(), 2);
1✔
1846

1847
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1848
            &RasterTile2D::new_with_tile_info(
1✔
1849
                TimeInterval::new_unchecked(0, 30),
1✔
1850
                TileInformation {
1✔
1851
                    global_tile_position: [-1, 0].into(),
1✔
1852
                    tile_size_in_pixels: [3, 2].into(),
1✔
1853
                    global_geo_transform: TestDefault::test_default(),
1✔
1854
                },
1✔
1855
                0,
1✔
1856
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
1857
                CacheHint::default()
1✔
1858
            )
1✔
1859
        ));
1✔
1860

1861
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1862
            &RasterTile2D::new_with_tile_info(
1✔
1863
                TimeInterval::new_unchecked(0, 30),
1✔
1864
                TileInformation {
1✔
1865
                    global_tile_position: [-1, 1].into(),
1✔
1866
                    tile_size_in_pixels: [3, 2].into(),
1✔
1867
                    global_geo_transform: TestDefault::test_default(),
1✔
1868
                },
1✔
1869
                0,
1✔
1870
                GridOrEmpty::from(
1✔
1871
                    MaskedGrid2D::new(
1✔
1872
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1873
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1874
                            .unwrap()
1✔
1875
                    )
1✔
1876
                    .unwrap(),
1✔
1877
                ),
1✔
1878
                CacheHint::default()
1✔
1879
            )
1✔
1880
        ));
1✔
1881
    }
1882

1883
    #[tokio::test]
1✔
1884
    async fn test_sum_ignore_no_data() {
1✔
1885
        let raster_tiles = make_raster_with_no_data();
1✔
1886

1✔
1887
        let mrs = MockRasterSource {
1✔
1888
            params: MockRasterSourceParams {
1✔
1889
                data: raster_tiles,
1✔
1890
                result_descriptor: RasterResultDescriptor {
1✔
1891
                    data_type: RasterDataType::U8,
1✔
1892
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1893
                    time: None,
1✔
1894
                    bbox: None,
1✔
1895
                    resolution: None,
1✔
1896
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1897
                },
1✔
1898
            },
1✔
1899
        }
1✔
1900
        .boxed();
1✔
1901

1✔
1902
        let agg = TemporalRasterAggregation {
1✔
1903
            params: TemporalRasterAggregationParameters {
1✔
1904
                aggregation: Aggregation::Sum {
1✔
1905
                    ignore_no_data: true,
1✔
1906
                },
1✔
1907
                window: TimeStep {
1✔
1908
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1909
                    step: 30,
1✔
1910
                },
1✔
1911
                window_reference: None,
1✔
1912
                output_type: None,
1✔
1913
            },
1✔
1914
            sources: SingleRasterSource { raster: mrs },
1✔
1915
        }
1✔
1916
        .boxed();
1✔
1917

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

1930
        let qp = agg
1✔
1931
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1932
            .await
×
1933
            .unwrap()
1✔
1934
            .query_processor()
1✔
1935
            .unwrap()
1✔
1936
            .get_u8()
1✔
1937
            .unwrap();
1✔
1938

1939
        let result = qp
1✔
1940
            .raster_query(query_rect, &query_ctx)
1✔
1941
            .await
×
1942
            .unwrap()
1✔
1943
            .collect::<Vec<_>>()
1✔
1944
            .await;
6✔
1945

1946
        assert_eq!(result.len(), 2);
1✔
1947

1948
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1949
            &RasterTile2D::new_with_tile_info(
1✔
1950
                TimeInterval::new_unchecked(0, 30),
1✔
1951
                TileInformation {
1✔
1952
                    global_tile_position: [-1, 0].into(),
1✔
1953
                    tile_size_in_pixels: [3, 2].into(),
1✔
1954
                    global_geo_transform: TestDefault::test_default(),
1✔
1955
                },
1✔
1956
                0,
1✔
1957
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![20, 8, 24, 16, 28, 30]).unwrap()),
1✔
1958
                CacheHint::default()
1✔
1959
            )
1✔
1960
        ));
1✔
1961

1962
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1963
            &RasterTile2D::new_with_tile_info(
1✔
1964
                TimeInterval::new_unchecked(0, 30),
1✔
1965
                TileInformation {
1✔
1966
                    global_tile_position: [-1, 1].into(),
1✔
1967
                    tile_size_in_pixels: [3, 2].into(),
1✔
1968
                    global_geo_transform: TestDefault::test_default(),
1✔
1969
                },
1✔
1970
                0,
1✔
1971
                GridOrEmpty::from(
1✔
1972
                    MaskedGrid2D::new(
1✔
1973
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1974
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1975
                            .unwrap()
1✔
1976
                    )
1✔
1977
                    .unwrap()
1✔
1978
                ),
1✔
1979
                CacheHint::default()
1✔
1980
            )
1✔
1981
        ));
1✔
1982
    }
1983

1984
    #[tokio::test]
1✔
1985
    #[allow(clippy::too_many_lines)]
1986
    async fn test_sum_with_larger_data_type() {
1✔
1987
        let operator = TemporalRasterAggregation {
1✔
1988
            params: TemporalRasterAggregationParameters {
1✔
1989
                aggregation: Aggregation::Sum {
1✔
1990
                    ignore_no_data: false,
1✔
1991
                },
1✔
1992
                window: TimeStep {
1✔
1993
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1994
                    step: 20,
1✔
1995
                },
1✔
1996
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
1997
                output_type: Some(RasterDataType::U16),
1✔
1998
            },
1✔
1999
            sources: SingleRasterSource {
1✔
2000
                raster: Expression {
1✔
2001
                    params: ExpressionParams {
1✔
2002
                        expression: "20 * A".to_string(),
1✔
2003
                        output_type: RasterDataType::U8,
1✔
2004
                        output_measurement: Some(Measurement::Unitless),
1✔
2005
                        map_no_data: true,
1✔
2006
                    },
1✔
2007
                    sources: ExpressionSources::new_a(
1✔
2008
                        MockRasterSource {
1✔
2009
                            params: MockRasterSourceParams {
1✔
2010
                                data: make_raster(),
1✔
2011
                                result_descriptor: RasterResultDescriptor {
1✔
2012
                                    data_type: RasterDataType::U8,
1✔
2013
                                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2014
                                    time: None,
1✔
2015
                                    bbox: None,
1✔
2016
                                    resolution: None,
1✔
2017
                                    bands: RasterBandDescriptors::new_single_band(),
1✔
2018
                                },
1✔
2019
                            },
1✔
2020
                        }
1✔
2021
                        .boxed(),
1✔
2022
                    ),
1✔
2023
                }
1✔
2024
                .boxed(),
1✔
2025
            },
1✔
2026
        }
1✔
2027
        .boxed();
1✔
2028

1✔
2029
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2030
            (0., 0.).into(),
1✔
2031
            [3, 2].into(),
1✔
2032
        ));
1✔
2033
        let query_rect = RasterQueryRectangle {
1✔
2034
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2035
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2036
            spatial_resolution: SpatialResolution::one(),
1✔
2037
            attributes: BandSelection::first(),
1✔
2038
        };
1✔
2039
        let query_ctx = MockQueryContext::test_default();
1✔
2040

2041
        let query_processor = operator
1✔
2042
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2043
            .await
×
2044
            .unwrap()
1✔
2045
            .query_processor()
1✔
2046
            .unwrap()
1✔
2047
            .get_u16()
1✔
2048
            .unwrap();
1✔
2049

2050
        let result = query_processor
1✔
2051
            .raster_query(query_rect, &query_ctx)
1✔
2052
            .await
×
2053
            .unwrap()
1✔
2054
            .map(Result::unwrap)
1✔
2055
            .collect::<Vec<_>>()
1✔
2056
            .await;
32✔
2057

2058
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
2059
            RasterTile2D::new_with_tile_info(
1✔
2060
                TimeInterval::new_unchecked(0, 20),
1✔
2061
                TileInformation {
1✔
2062
                    global_tile_position: [-1, 0].into(),
1✔
2063
                    tile_size_in_pixels: [3, 2].into(),
1✔
2064
                    global_geo_transform: TestDefault::test_default(),
1✔
2065
                },
1✔
2066
                0,
1✔
2067
                Grid2D::new(
1✔
2068
                    [3, 2].into(),
1✔
2069
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2070
                )
1✔
2071
                .unwrap()
1✔
2072
                .into(),
1✔
2073
                CacheHint::default()
1✔
2074
            ),
1✔
2075
            RasterTile2D::new_with_tile_info(
1✔
2076
                TimeInterval::new_unchecked(0, 20),
1✔
2077
                TileInformation {
1✔
2078
                    global_tile_position: [-1, 1].into(),
1✔
2079
                    tile_size_in_pixels: [3, 2].into(),
1✔
2080
                    global_geo_transform: TestDefault::test_default(),
1✔
2081
                },
1✔
2082
                0,
1✔
2083
                Grid2D::new(
1✔
2084
                    [3, 2].into(),
1✔
2085
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2086
                )
1✔
2087
                .unwrap()
1✔
2088
                .into(),
1✔
2089
                CacheHint::default()
1✔
2090
            ),
1✔
2091
            RasterTile2D::new_with_tile_info(
1✔
2092
                TimeInterval::new_unchecked(20, 40),
1✔
2093
                TileInformation {
1✔
2094
                    global_tile_position: [-1, 0].into(),
1✔
2095
                    tile_size_in_pixels: [3, 2].into(),
1✔
2096
                    global_geo_transform: TestDefault::test_default(),
1✔
2097
                },
1✔
2098
                0,
1✔
2099
                Grid2D::new(
1✔
2100
                    [3, 2].into(),
1✔
2101
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2102
                )
1✔
2103
                .unwrap()
1✔
2104
                .into(),
1✔
2105
                CacheHint::default()
1✔
2106
            ),
1✔
2107
            RasterTile2D::new_with_tile_info(
1✔
2108
                TimeInterval::new_unchecked(20, 40),
1✔
2109
                TileInformation {
1✔
2110
                    global_tile_position: [-1, 1].into(),
1✔
2111
                    tile_size_in_pixels: [3, 2].into(),
1✔
2112
                    global_geo_transform: TestDefault::test_default(),
1✔
2113
                },
1✔
2114
                0,
1✔
2115
                Grid2D::new(
1✔
2116
                    [3, 2].into(),
1✔
2117
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2118
                )
1✔
2119
                .unwrap()
1✔
2120
                .into(),
1✔
2121
                CacheHint::default()
1✔
2122
            )
1✔
2123
        ]),);
1✔
2124
    }
2125

2126
    #[tokio::test]
1✔
2127
    #[allow(clippy::too_many_lines)]
2128
    async fn test_count_without_nodata() {
1✔
2129
        let operator = TemporalRasterAggregation {
1✔
2130
            params: TemporalRasterAggregationParameters {
1✔
2131
                aggregation: Aggregation::Count {
1✔
2132
                    ignore_no_data: false,
1✔
2133
                },
1✔
2134
                window: TimeStep {
1✔
2135
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2136
                    step: 20,
1✔
2137
                },
1✔
2138
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
2139
                output_type: None,
1✔
2140
            },
1✔
2141
            sources: SingleRasterSource {
1✔
2142
                raster: MockRasterSource {
1✔
2143
                    params: MockRasterSourceParams {
1✔
2144
                        data: make_raster(),
1✔
2145
                        result_descriptor: RasterResultDescriptor {
1✔
2146
                            data_type: RasterDataType::U8,
1✔
2147
                            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2148
                            time: None,
1✔
2149
                            bbox: None,
1✔
2150
                            resolution: None,
1✔
2151
                            bands: RasterBandDescriptors::new_single_band(),
1✔
2152
                        },
1✔
2153
                    },
1✔
2154
                }
1✔
2155
                .boxed(),
1✔
2156
            },
1✔
2157
        }
1✔
2158
        .boxed();
1✔
2159

1✔
2160
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2161
            (0., 0.).into(),
1✔
2162
            [3, 2].into(),
1✔
2163
        ));
1✔
2164
        let query_rect = RasterQueryRectangle {
1✔
2165
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2166
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2167
            spatial_resolution: SpatialResolution::one(),
1✔
2168
            attributes: BandSelection::first(),
1✔
2169
        };
1✔
2170
        let query_ctx = MockQueryContext::test_default();
1✔
2171

2172
        let query_processor = operator
1✔
2173
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2174
            .await
×
2175
            .unwrap()
1✔
2176
            .query_processor()
1✔
2177
            .unwrap()
1✔
2178
            .get_u8()
1✔
2179
            .unwrap();
1✔
2180

2181
        let result = query_processor
1✔
2182
            .raster_query(query_rect, &query_ctx)
1✔
2183
            .await
×
2184
            .unwrap()
1✔
2185
            .map(Result::unwrap)
1✔
2186
            .collect::<Vec<_>>()
1✔
2187
            .await;
8✔
2188

2189
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
2190
            RasterTile2D::new_with_tile_info(
1✔
2191
                TimeInterval::new_unchecked(0, 20),
1✔
2192
                TileInformation {
1✔
2193
                    global_tile_position: [-1, 0].into(),
1✔
2194
                    tile_size_in_pixels: [3, 2].into(),
1✔
2195
                    global_geo_transform: TestDefault::test_default(),
1✔
2196
                },
1✔
2197
                0,
1✔
2198
                Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2199
                    .unwrap()
1✔
2200
                    .into(),
1✔
2201
                CacheHint::default()
1✔
2202
            ),
1✔
2203
            RasterTile2D::new_with_tile_info(
1✔
2204
                TimeInterval::new_unchecked(0, 20),
1✔
2205
                TileInformation {
1✔
2206
                    global_tile_position: [-1, 1].into(),
1✔
2207
                    tile_size_in_pixels: [3, 2].into(),
1✔
2208
                    global_geo_transform: TestDefault::test_default(),
1✔
2209
                },
1✔
2210
                0,
1✔
2211
                Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2212
                    .unwrap()
1✔
2213
                    .into(),
1✔
2214
                CacheHint::default()
1✔
2215
            ),
1✔
2216
            RasterTile2D::new_with_tile_info(
1✔
2217
                TimeInterval::new_unchecked(20, 40),
1✔
2218
                TileInformation {
1✔
2219
                    global_tile_position: [-1, 0].into(),
1✔
2220
                    tile_size_in_pixels: [3, 2].into(),
1✔
2221
                    global_geo_transform: TestDefault::test_default(),
1✔
2222
                },
1✔
2223
                0,
1✔
2224
                Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2225
                    .unwrap()
1✔
2226
                    .into(),
1✔
2227
                CacheHint::default()
1✔
2228
            ),
1✔
2229
            RasterTile2D::new_with_tile_info(
1✔
2230
                TimeInterval::new_unchecked(20, 40),
1✔
2231
                TileInformation {
1✔
2232
                    global_tile_position: [-1, 1].into(),
1✔
2233
                    tile_size_in_pixels: [3, 2].into(),
1✔
2234
                    global_geo_transform: TestDefault::test_default(),
1✔
2235
                },
1✔
2236
                0,
1✔
2237
                Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2238
                    .unwrap()
1✔
2239
                    .into(),
1✔
2240
                CacheHint::default()
1✔
2241
            )
1✔
2242
        ]));
1✔
2243
    }
2244

2245
    #[tokio::test]
1✔
2246
    async fn test_count_nodata() {
1✔
2247
        let raster_tiles = make_raster_with_no_data();
1✔
2248

1✔
2249
        let mrs = MockRasterSource {
1✔
2250
            params: MockRasterSourceParams {
1✔
2251
                data: raster_tiles,
1✔
2252
                result_descriptor: RasterResultDescriptor {
1✔
2253
                    data_type: RasterDataType::U8,
1✔
2254
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2255
                    time: None,
1✔
2256
                    bbox: None,
1✔
2257
                    resolution: None,
1✔
2258
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2259
                },
1✔
2260
            },
1✔
2261
        }
1✔
2262
        .boxed();
1✔
2263

1✔
2264
        let agg = TemporalRasterAggregation {
1✔
2265
            params: TemporalRasterAggregationParameters {
1✔
2266
                aggregation: Aggregation::Count {
1✔
2267
                    ignore_no_data: false,
1✔
2268
                },
1✔
2269
                window: TimeStep {
1✔
2270
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2271
                    step: 30,
1✔
2272
                },
1✔
2273
                window_reference: None,
1✔
2274
                output_type: None,
1✔
2275
            },
1✔
2276
            sources: SingleRasterSource { raster: mrs },
1✔
2277
        }
1✔
2278
        .boxed();
1✔
2279

1✔
2280
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2281
            (0., 0.).into(),
1✔
2282
            [3, 2].into(),
1✔
2283
        ));
1✔
2284
        let query_rect = RasterQueryRectangle {
1✔
2285
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2286
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2287
            spatial_resolution: SpatialResolution::one(),
1✔
2288
            attributes: BandSelection::first(),
1✔
2289
        };
1✔
2290
        let query_ctx = MockQueryContext::test_default();
1✔
2291

2292
        let qp = agg
1✔
2293
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2294
            .await
×
2295
            .unwrap()
1✔
2296
            .query_processor()
1✔
2297
            .unwrap()
1✔
2298
            .get_u8()
1✔
2299
            .unwrap();
1✔
2300

2301
        let result = qp
1✔
2302
            .raster_query(query_rect, &query_ctx)
1✔
2303
            .await
×
2304
            .unwrap()
1✔
2305
            .collect::<Vec<_>>()
1✔
2306
            .await;
6✔
2307

2308
        assert_eq!(result.len(), 2);
1✔
2309

2310
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2311
            &RasterTile2D::new_with_tile_info(
1✔
2312
                TimeInterval::new_unchecked(0, 30),
1✔
2313
                TileInformation {
1✔
2314
                    global_tile_position: [-1, 0].into(),
1✔
2315
                    tile_size_in_pixels: [3, 2].into(),
1✔
2316
                    global_geo_transform: TestDefault::test_default(),
1✔
2317
                },
1✔
2318
                0,
1✔
2319
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
2320
                CacheHint::default()
1✔
2321
            )
1✔
2322
        ));
1✔
2323

2324
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2325
            &RasterTile2D::new_with_tile_info(
1✔
2326
                TimeInterval::new_unchecked(0, 30),
1✔
2327
                TileInformation {
1✔
2328
                    global_tile_position: [-1, 1].into(),
1✔
2329
                    tile_size_in_pixels: [3, 2].into(),
1✔
2330
                    global_geo_transform: TestDefault::test_default(),
1✔
2331
                },
1✔
2332
                0,
1✔
2333
                GridOrEmpty::from(
1✔
2334
                    MaskedGrid2D::new(
1✔
2335
                        Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
1✔
2336
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2337
                            .unwrap()
1✔
2338
                    )
1✔
2339
                    .unwrap()
1✔
2340
                ),
1✔
2341
                CacheHint::default()
1✔
2342
            )
1✔
2343
        ));
1✔
2344
    }
2345

2346
    #[tokio::test]
1✔
2347
    async fn test_count_ignore_no_data() {
1✔
2348
        let raster_tiles = make_raster_with_no_data();
1✔
2349

1✔
2350
        let mrs = MockRasterSource {
1✔
2351
            params: MockRasterSourceParams {
1✔
2352
                data: raster_tiles,
1✔
2353
                result_descriptor: RasterResultDescriptor {
1✔
2354
                    data_type: RasterDataType::U8,
1✔
2355
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2356
                    time: None,
1✔
2357
                    bbox: None,
1✔
2358
                    resolution: None,
1✔
2359
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2360
                },
1✔
2361
            },
1✔
2362
        }
1✔
2363
        .boxed();
1✔
2364

1✔
2365
        let agg = TemporalRasterAggregation {
1✔
2366
            params: TemporalRasterAggregationParameters {
1✔
2367
                aggregation: Aggregation::Count {
1✔
2368
                    ignore_no_data: true,
1✔
2369
                },
1✔
2370
                window: TimeStep {
1✔
2371
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2372
                    step: 30,
1✔
2373
                },
1✔
2374
                window_reference: None,
1✔
2375
                output_type: None,
1✔
2376
            },
1✔
2377
            sources: SingleRasterSource { raster: mrs },
1✔
2378
        }
1✔
2379
        .boxed();
1✔
2380

1✔
2381
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2382
            (0., 0.).into(),
1✔
2383
            [3, 2].into(),
1✔
2384
        ));
1✔
2385
        let query_rect = RasterQueryRectangle {
1✔
2386
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2387
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2388
            spatial_resolution: SpatialResolution::one(),
1✔
2389
            attributes: BandSelection::first(),
1✔
2390
        };
1✔
2391
        let query_ctx = MockQueryContext::test_default();
1✔
2392

2393
        let qp = agg
1✔
2394
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2395
            .await
×
2396
            .unwrap()
1✔
2397
            .query_processor()
1✔
2398
            .unwrap()
1✔
2399
            .get_u8()
1✔
2400
            .unwrap();
1✔
2401

2402
        let result = qp
1✔
2403
            .raster_query(query_rect, &query_ctx)
1✔
2404
            .await
×
2405
            .unwrap()
1✔
2406
            .collect::<Vec<_>>()
1✔
2407
            .await;
6✔
2408

2409
        assert_eq!(result.len(), 2);
1✔
2410

2411
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2412
            &RasterTile2D::new_with_tile_info(
1✔
2413
                TimeInterval::new_unchecked(0, 30),
1✔
2414
                TileInformation {
1✔
2415
                    global_tile_position: [-1, 0].into(),
1✔
2416
                    tile_size_in_pixels: [3, 2].into(),
1✔
2417
                    global_geo_transform: TestDefault::test_default(),
1✔
2418
                },
1✔
2419
                0,
1✔
2420
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![2, 1, 2, 1, 2, 2]).unwrap()),
1✔
2421
                CacheHint::default()
1✔
2422
            )
1✔
2423
        ));
1✔
2424

2425
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2426
            &RasterTile2D::new_with_tile_info(
1✔
2427
                TimeInterval::new_unchecked(0, 30),
1✔
2428
                TileInformation {
1✔
2429
                    global_tile_position: [-1, 1].into(),
1✔
2430
                    tile_size_in_pixels: [3, 2].into(),
1✔
2431
                    global_geo_transform: TestDefault::test_default(),
1✔
2432
                },
1✔
2433
                0,
1✔
2434
                GridOrEmpty::from(
1✔
2435
                    MaskedGrid2D::new(
1✔
2436
                        Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
1✔
2437
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2438
                            .unwrap()
1✔
2439
                    )
1✔
2440
                    .unwrap()
1✔
2441
                ),
1✔
2442
                CacheHint::default()
1✔
2443
            )
1✔
2444
        ));
1✔
2445
    }
2446

2447
    #[tokio::test]
1✔
2448
    async fn test_query_not_aligned_with_window_reference() {
1✔
2449
        let raster_tiles = make_raster();
1✔
2450

1✔
2451
        let mrs = MockRasterSource {
1✔
2452
            params: MockRasterSourceParams {
1✔
2453
                data: raster_tiles,
1✔
2454
                result_descriptor: RasterResultDescriptor {
1✔
2455
                    data_type: RasterDataType::U8,
1✔
2456
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2457
                    time: None,
1✔
2458
                    bbox: None,
1✔
2459
                    resolution: None,
1✔
2460
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2461
                },
1✔
2462
            },
1✔
2463
        }
1✔
2464
        .boxed();
1✔
2465

1✔
2466
        let agg = TemporalRasterAggregation {
1✔
2467
            params: TemporalRasterAggregationParameters {
1✔
2468
                aggregation: Aggregation::Last {
1✔
2469
                    ignore_no_data: false,
1✔
2470
                },
1✔
2471
                window: TimeStep {
1✔
2472
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2473
                    step: 30,
1✔
2474
                },
1✔
2475
                window_reference: Some(TimeInstance::EPOCH_START),
1✔
2476
                output_type: None,
1✔
2477
            },
1✔
2478
            sources: SingleRasterSource { raster: mrs },
1✔
2479
        }
1✔
2480
        .boxed();
1✔
2481

1✔
2482
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2483
            (0., 0.).into(),
1✔
2484
            [3, 2].into(),
1✔
2485
        ));
1✔
2486
        let query_rect = RasterQueryRectangle {
1✔
2487
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2488
            time_interval: TimeInterval::new_unchecked(5, 5),
1✔
2489
            spatial_resolution: SpatialResolution::one(),
1✔
2490
            attributes: BandSelection::first(),
1✔
2491
        };
1✔
2492
        let query_ctx = MockQueryContext::test_default();
1✔
2493

2494
        let qp = agg
1✔
2495
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2496
            .await
×
2497
            .unwrap()
1✔
2498
            .query_processor()
1✔
2499
            .unwrap()
1✔
2500
            .get_u8()
1✔
2501
            .unwrap();
1✔
2502

2503
        let result = qp
1✔
2504
            .query(query_rect, &query_ctx)
1✔
2505
            .await
×
2506
            .unwrap()
1✔
2507
            .collect::<Vec<_>>()
1✔
2508
            .await;
8✔
2509

2510
        assert_eq!(result.len(), 2);
1✔
2511
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2512
            &RasterTile2D::new_with_tile_info(
1✔
2513
                TimeInterval::new_unchecked(0, 30),
1✔
2514
                TileInformation {
1✔
2515
                    global_tile_position: [-1, 0].into(),
1✔
2516
                    tile_size_in_pixels: [3, 2].into(),
1✔
2517
                    global_geo_transform: TestDefault::test_default(),
1✔
2518
                },
1✔
2519
                0,
1✔
2520
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
2521
                CacheHint::default()
1✔
2522
            )
1✔
2523
        ));
1✔
2524

2525
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2526
            &RasterTile2D::new_with_tile_info(
1✔
2527
                TimeInterval::new_unchecked(0, 30),
1✔
2528
                TileInformation {
1✔
2529
                    global_tile_position: [-1, 1].into(),
1✔
2530
                    tile_size_in_pixels: [3, 2].into(),
1✔
2531
                    global_geo_transform: TestDefault::test_default(),
1✔
2532
                },
1✔
2533
                0,
1✔
2534
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
2535
                CacheHint::default()
1✔
2536
            )
1✔
2537
        ));
1✔
2538
    }
2539

2540
    fn make_raster() -> Vec<geoengine_datatypes::raster::RasterTile2D<u8>> {
10✔
2541
        let raster_tiles = vec![
10✔
2542
            RasterTile2D::new_with_tile_info(
10✔
2543
                TimeInterval::new_unchecked(0, 10),
10✔
2544
                TileInformation {
10✔
2545
                    global_tile_position: [-1, 0].into(),
10✔
2546
                    tile_size_in_pixels: [3, 2].into(),
10✔
2547
                    global_geo_transform: TestDefault::test_default(),
10✔
2548
                },
10✔
2549
                0,
10✔
2550
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
10✔
2551
                CacheHint::default(),
10✔
2552
            ),
10✔
2553
            RasterTile2D::new_with_tile_info(
10✔
2554
                TimeInterval::new_unchecked(0, 10),
10✔
2555
                TileInformation {
10✔
2556
                    global_tile_position: [-1, 1].into(),
10✔
2557
                    tile_size_in_pixels: [3, 2].into(),
10✔
2558
                    global_geo_transform: TestDefault::test_default(),
10✔
2559
                },
10✔
2560
                0,
10✔
2561
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
10✔
2562
                CacheHint::default(),
10✔
2563
            ),
10✔
2564
            RasterTile2D::new_with_tile_info(
10✔
2565
                TimeInterval::new_unchecked(10, 20),
10✔
2566
                TileInformation {
10✔
2567
                    global_tile_position: [-1, 0].into(),
10✔
2568
                    tile_size_in_pixels: [3, 2].into(),
10✔
2569
                    global_geo_transform: TestDefault::test_default(),
10✔
2570
                },
10✔
2571
                0,
10✔
2572
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
10✔
2573
                CacheHint::default(),
10✔
2574
            ),
10✔
2575
            RasterTile2D::new_with_tile_info(
10✔
2576
                TimeInterval::new_unchecked(10, 20),
10✔
2577
                TileInformation {
10✔
2578
                    global_tile_position: [-1, 1].into(),
10✔
2579
                    tile_size_in_pixels: [3, 2].into(),
10✔
2580
                    global_geo_transform: TestDefault::test_default(),
10✔
2581
                },
10✔
2582
                0,
10✔
2583
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
10✔
2584
                CacheHint::default(),
10✔
2585
            ),
10✔
2586
            RasterTile2D::new_with_tile_info(
10✔
2587
                TimeInterval::new_unchecked(20, 30),
10✔
2588
                TileInformation {
10✔
2589
                    global_tile_position: [-1, 0].into(),
10✔
2590
                    tile_size_in_pixels: [3, 2].into(),
10✔
2591
                    global_geo_transform: TestDefault::test_default(),
10✔
2592
                },
10✔
2593
                0,
10✔
2594
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
10✔
2595
                CacheHint::default(),
10✔
2596
            ),
10✔
2597
            RasterTile2D::new_with_tile_info(
10✔
2598
                TimeInterval::new_unchecked(20, 30),
10✔
2599
                TileInformation {
10✔
2600
                    global_tile_position: [-1, 1].into(),
10✔
2601
                    tile_size_in_pixels: [3, 2].into(),
10✔
2602
                    global_geo_transform: TestDefault::test_default(),
10✔
2603
                },
10✔
2604
                0,
10✔
2605
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
10✔
2606
                CacheHint::default(),
10✔
2607
            ),
10✔
2608
            RasterTile2D::new_with_tile_info(
10✔
2609
                TimeInterval::new_unchecked(30, 40),
10✔
2610
                TileInformation {
10✔
2611
                    global_tile_position: [-1, 0].into(),
10✔
2612
                    tile_size_in_pixels: [3, 2].into(),
10✔
2613
                    global_geo_transform: TestDefault::test_default(),
10✔
2614
                },
10✔
2615
                0,
10✔
2616
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
10✔
2617
                CacheHint::default(),
10✔
2618
            ),
10✔
2619
            RasterTile2D::new_with_tile_info(
10✔
2620
                TimeInterval::new_unchecked(30, 40),
10✔
2621
                TileInformation {
10✔
2622
                    global_tile_position: [-1, 1].into(),
10✔
2623
                    tile_size_in_pixels: [3, 2].into(),
10✔
2624
                    global_geo_transform: TestDefault::test_default(),
10✔
2625
                },
10✔
2626
                0,
10✔
2627
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
10✔
2628
                CacheHint::default(),
10✔
2629
            ),
10✔
2630
        ];
10✔
2631
        raster_tiles
10✔
2632
    }
10✔
2633

2634
    fn make_raster_with_no_data() -> Vec<geoengine_datatypes::raster::RasterTile2D<u8>> {
10✔
2635
        let raster_tiles = vec![
10✔
2636
            RasterTile2D::new_with_tile_info(
10✔
2637
                TimeInterval::new_unchecked(0, 10),
10✔
2638
                TileInformation {
10✔
2639
                    global_tile_position: [-1, 0].into(),
10✔
2640
                    tile_size_in_pixels: [3, 2].into(),
10✔
2641
                    global_geo_transform: TestDefault::test_default(),
10✔
2642
                },
10✔
2643
                0,
10✔
2644
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2645
                CacheHint::default(),
10✔
2646
            ),
10✔
2647
            RasterTile2D::new_with_tile_info(
10✔
2648
                TimeInterval::new_unchecked(0, 10),
10✔
2649
                TileInformation {
10✔
2650
                    global_tile_position: [-1, 1].into(),
10✔
2651
                    tile_size_in_pixels: [3, 2].into(),
10✔
2652
                    global_geo_transform: TestDefault::test_default(),
10✔
2653
                },
10✔
2654
                0,
10✔
2655
                GridOrEmpty::from(
10✔
2656
                    MaskedGrid2D::new(
10✔
2657
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 42, 5, 6]).unwrap(),
10✔
2658
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
10✔
2659
                            .unwrap(),
10✔
2660
                    )
10✔
2661
                    .unwrap(),
10✔
2662
                ),
10✔
2663
                CacheHint::default(),
10✔
2664
            ),
10✔
2665
            RasterTile2D::new_with_tile_info(
10✔
2666
                TimeInterval::new_unchecked(10, 20),
10✔
2667
                TileInformation {
10✔
2668
                    global_tile_position: [-1, 0].into(),
10✔
2669
                    tile_size_in_pixels: [3, 2].into(),
10✔
2670
                    global_geo_transform: TestDefault::test_default(),
10✔
2671
                },
10✔
2672
                0,
10✔
2673
                GridOrEmpty::from(
10✔
2674
                    MaskedGrid2D::new(
10✔
2675
                        Grid2D::new([3, 2].into(), vec![7, 8, 9, 42, 11, 12]).unwrap(),
10✔
2676
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
10✔
2677
                            .unwrap(),
10✔
2678
                    )
10✔
2679
                    .unwrap(),
10✔
2680
                ),
10✔
2681
                CacheHint::default(),
10✔
2682
            ),
10✔
2683
            RasterTile2D::new_with_tile_info(
10✔
2684
                TimeInterval::new_unchecked(10, 20),
10✔
2685
                TileInformation {
10✔
2686
                    global_tile_position: [-1, 1].into(),
10✔
2687
                    tile_size_in_pixels: [3, 2].into(),
10✔
2688
                    global_geo_transform: TestDefault::test_default(),
10✔
2689
                },
10✔
2690
                0,
10✔
2691
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2692
                CacheHint::default(),
10✔
2693
            ),
10✔
2694
            RasterTile2D::new_with_tile_info(
10✔
2695
                TimeInterval::new_unchecked(20, 30),
10✔
2696
                TileInformation {
10✔
2697
                    global_tile_position: [-1, 0].into(),
10✔
2698
                    tile_size_in_pixels: [3, 2].into(),
10✔
2699
                    global_geo_transform: TestDefault::test_default(),
10✔
2700
                },
10✔
2701
                0,
10✔
2702
                GridOrEmpty::from(
10✔
2703
                    MaskedGrid2D::new(
10✔
2704
                        Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
10✔
2705
                        Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
10✔
2706
                            .unwrap(),
10✔
2707
                    )
10✔
2708
                    .unwrap(),
10✔
2709
                ),
10✔
2710
                CacheHint::default(),
10✔
2711
            ),
10✔
2712
            RasterTile2D::new_with_tile_info(
10✔
2713
                TimeInterval::new_unchecked(20, 30),
10✔
2714
                TileInformation {
10✔
2715
                    global_tile_position: [-1, 1].into(),
10✔
2716
                    tile_size_in_pixels: [3, 2].into(),
10✔
2717
                    global_geo_transform: TestDefault::test_default(),
10✔
2718
                },
10✔
2719
                0,
10✔
2720
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2721
                CacheHint::default(),
10✔
2722
            ),
10✔
2723
        ];
10✔
2724
        raster_tiles
10✔
2725
    }
10✔
2726

2727
    #[tokio::test]
1✔
2728
    #[allow(clippy::too_many_lines)]
2729
    async fn it_sums_multiple_bands() {
1✔
2730
        let operator = TemporalRasterAggregation {
1✔
2731
            params: TemporalRasterAggregationParameters {
1✔
2732
                aggregation: Aggregation::Sum {
1✔
2733
                    ignore_no_data: false,
1✔
2734
                },
1✔
2735
                window: TimeStep {
1✔
2736
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2737
                    step: 20,
1✔
2738
                },
1✔
2739
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
2740
                output_type: None,
1✔
2741
            },
1✔
2742
            sources: SingleRasterSource {
1✔
2743
                raster: RasterStacker {
1✔
2744
                    params: RasterStackerParams {},
1✔
2745
                    sources: MultipleRasterSources {
1✔
2746
                        rasters: vec![
1✔
2747
                            MockRasterSource {
1✔
2748
                                params: MockRasterSourceParams {
1✔
2749
                                    data: make_raster(),
1✔
2750
                                    result_descriptor: RasterResultDescriptor {
1✔
2751
                                        data_type: RasterDataType::U8,
1✔
2752
                                        spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2753
                                        time: None,
1✔
2754
                                        bbox: None,
1✔
2755
                                        resolution: None,
1✔
2756
                                        bands: RasterBandDescriptors::new_single_band(),
1✔
2757
                                    },
1✔
2758
                                },
1✔
2759
                            }
1✔
2760
                            .boxed(),
1✔
2761
                            MockRasterSource {
1✔
2762
                                params: MockRasterSourceParams {
1✔
2763
                                    data: make_raster(),
1✔
2764
                                    result_descriptor: RasterResultDescriptor {
1✔
2765
                                        data_type: RasterDataType::U8,
1✔
2766
                                        spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2767
                                        time: None,
1✔
2768
                                        bbox: None,
1✔
2769
                                        resolution: None,
1✔
2770
                                        bands: RasterBandDescriptors::new_single_band(),
1✔
2771
                                    },
1✔
2772
                                },
1✔
2773
                            }
1✔
2774
                            .boxed(),
1✔
2775
                        ],
1✔
2776
                    },
1✔
2777
                }
1✔
2778
                .boxed(),
1✔
2779
            },
1✔
2780
        }
1✔
2781
        .boxed();
1✔
2782

1✔
2783
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2784
            (0., 0.).into(),
1✔
2785
            [3, 2].into(),
1✔
2786
        ));
1✔
2787
        let query_rect = RasterQueryRectangle {
1✔
2788
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2789
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2790
            spatial_resolution: SpatialResolution::one(),
1✔
2791
            attributes: [0, 1].into(),
1✔
2792
        };
1✔
2793
        let query_ctx = MockQueryContext::test_default();
1✔
2794

2795
        let query_processor = operator
1✔
2796
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
NEW
2797
            .await
×
2798
            .unwrap()
1✔
2799
            .query_processor()
1✔
2800
            .unwrap()
1✔
2801
            .get_u8()
1✔
2802
            .unwrap();
1✔
2803

2804
        let result = query_processor
1✔
2805
            .raster_query(query_rect, &query_ctx)
1✔
NEW
2806
            .await
×
2807
            .unwrap()
1✔
2808
            .map(Result::unwrap)
1✔
2809
            .collect::<Vec<_>>()
1✔
2810
            .await;
16✔
2811

2812
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
2813
            RasterTile2D::new_with_tile_info(
1✔
2814
                TimeInterval::new_unchecked(0, 20),
1✔
2815
                TileInformation {
1✔
2816
                    global_tile_position: [-1, 0].into(),
1✔
2817
                    tile_size_in_pixels: [3, 2].into(),
1✔
2818
                    global_geo_transform: TestDefault::test_default(),
1✔
2819
                },
1✔
2820
                0,
1✔
2821
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2822
                    .unwrap()
1✔
2823
                    .into(),
1✔
2824
                CacheHint::default()
1✔
2825
            ),
1✔
2826
            RasterTile2D::new_with_tile_info(
1✔
2827
                TimeInterval::new_unchecked(0, 20),
1✔
2828
                TileInformation {
1✔
2829
                    global_tile_position: [-1, 0].into(),
1✔
2830
                    tile_size_in_pixels: [3, 2].into(),
1✔
2831
                    global_geo_transform: TestDefault::test_default(),
1✔
2832
                },
1✔
2833
                1,
1✔
2834
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2835
                    .unwrap()
1✔
2836
                    .into(),
1✔
2837
                CacheHint::default()
1✔
2838
            ),
1✔
2839
            RasterTile2D::new_with_tile_info(
1✔
2840
                TimeInterval::new_unchecked(0, 20),
1✔
2841
                TileInformation {
1✔
2842
                    global_tile_position: [-1, 1].into(),
1✔
2843
                    tile_size_in_pixels: [3, 2].into(),
1✔
2844
                    global_geo_transform: TestDefault::test_default(),
1✔
2845
                },
1✔
2846
                0,
1✔
2847
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2848
                    .unwrap()
1✔
2849
                    .into(),
1✔
2850
                CacheHint::default()
1✔
2851
            ),
1✔
2852
            RasterTile2D::new_with_tile_info(
1✔
2853
                TimeInterval::new_unchecked(0, 20),
1✔
2854
                TileInformation {
1✔
2855
                    global_tile_position: [-1, 1].into(),
1✔
2856
                    tile_size_in_pixels: [3, 2].into(),
1✔
2857
                    global_geo_transform: TestDefault::test_default(),
1✔
2858
                },
1✔
2859
                1,
1✔
2860
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2861
                    .unwrap()
1✔
2862
                    .into(),
1✔
2863
                CacheHint::default()
1✔
2864
            ),
1✔
2865
            RasterTile2D::new_with_tile_info(
1✔
2866
                TimeInterval::new_unchecked(20, 40),
1✔
2867
                TileInformation {
1✔
2868
                    global_tile_position: [-1, 0].into(),
1✔
2869
                    tile_size_in_pixels: [3, 2].into(),
1✔
2870
                    global_geo_transform: TestDefault::test_default(),
1✔
2871
                },
1✔
2872
                0,
1✔
2873
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2874
                    .unwrap()
1✔
2875
                    .into(),
1✔
2876
                CacheHint::default()
1✔
2877
            ),
1✔
2878
            RasterTile2D::new_with_tile_info(
1✔
2879
                TimeInterval::new_unchecked(20, 40),
1✔
2880
                TileInformation {
1✔
2881
                    global_tile_position: [-1, 0].into(),
1✔
2882
                    tile_size_in_pixels: [3, 2].into(),
1✔
2883
                    global_geo_transform: TestDefault::test_default(),
1✔
2884
                },
1✔
2885
                1,
1✔
2886
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2887
                    .unwrap()
1✔
2888
                    .into(),
1✔
2889
                CacheHint::default()
1✔
2890
            ),
1✔
2891
            RasterTile2D::new_with_tile_info(
1✔
2892
                TimeInterval::new_unchecked(20, 40),
1✔
2893
                TileInformation {
1✔
2894
                    global_tile_position: [-1, 1].into(),
1✔
2895
                    tile_size_in_pixels: [3, 2].into(),
1✔
2896
                    global_geo_transform: TestDefault::test_default(),
1✔
2897
                },
1✔
2898
                0,
1✔
2899
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2900
                    .unwrap()
1✔
2901
                    .into(),
1✔
2902
                CacheHint::default()
1✔
2903
            ),
1✔
2904
            RasterTile2D::new_with_tile_info(
1✔
2905
                TimeInterval::new_unchecked(20, 40),
1✔
2906
                TileInformation {
1✔
2907
                    global_tile_position: [-1, 1].into(),
1✔
2908
                    tile_size_in_pixels: [3, 2].into(),
1✔
2909
                    global_geo_transform: TestDefault::test_default(),
1✔
2910
                },
1✔
2911
                1,
1✔
2912
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2913
                    .unwrap()
1✔
2914
                    .into(),
1✔
2915
                CacheHint::default()
1✔
2916
            )
1✔
2917
        ]));
1✔
2918
    }
2919
}
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