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

geo-engine / geoengine / 10178074589

31 Jul 2024 09:34AM UTC coverage: 91.068% (+0.4%) from 90.682%
10178074589

push

github

web-flow
Merge pull request #973 from geo-engine/remove-XGB-update-toolchain

Remove-XGB-update-toolchain

81 of 88 new or added lines in 29 files covered. (92.05%)

456 existing lines in 119 files now uncovered.

131088 of 143945 relevant lines covered (91.07%)

53581.03 hits per line

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

98.54
/operators/src/processing/temporal_raster_aggregation/temporal_aggregation_operator.rs
1
use super::aggregators::{
2
    CountPixelAggregator, CountPixelAggregatorIngoringNoData, FirstPixelAggregatorIngoringNoData,
3
    GlobalStateTemporalRasterPixelAggregator, LastPixelAggregatorIngoringNoData,
4
    MaxPixelAggregator, MaxPixelAggregatorIngoringNoData, MeanPixelAggregator, 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 super::subquery::GlobalStateTemporalRasterAggregationSubQuery;
12
use crate::adapters::stack_individual_aligned_raster_bands;
13
use crate::engine::{
14
    CanonicOperatorName, ExecutionContext, InitializedSources, Operator, QueryProcessor,
15
    RasterOperator, SingleRasterSource, WorkflowOperatorPath,
16
};
17
use crate::processing::temporal_raster_aggregation::aggregators::PercentileEstimateAggregator;
18
use crate::{
19
    adapters::SubQueryTileAggregator,
20
    engine::{
21
        InitializedRasterOperator, OperatorName, RasterQueryProcessor, RasterResultDescriptor,
22
        TypedRasterQueryProcessor,
23
    },
24
    error,
25
    util::Result,
26
};
27
use async_trait::async_trait;
28
use geoengine_datatypes::primitives::{
29
    BandSelection, RasterQueryRectangle, SpatialPartition2D, TimeInstance,
30
};
31
use geoengine_datatypes::raster::{Pixel, RasterDataType, RasterTile2D};
32
use geoengine_datatypes::{primitives::TimeStep, raster::TilingSpecification};
33
use log::debug;
34
use serde::{Deserialize, Serialize};
35
use snafu::ensure;
36
use std::marker::PhantomData;
37
use std::sync::Arc;
38

39
use typetag;
40

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

UNCOV
54
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
×
55
#[serde(rename_all = "camelCase")]
56
#[serde(tag = "type")]
57
pub enum Aggregation {
58
    #[serde(rename_all = "camelCase")]
59
    Min { ignore_no_data: bool },
60
    #[serde(rename_all = "camelCase")]
61
    Max { ignore_no_data: bool },
62
    #[serde(rename_all = "camelCase")]
63
    First { ignore_no_data: bool },
64
    #[serde(rename_all = "camelCase")]
65
    Last { ignore_no_data: bool },
66
    #[serde(rename_all = "camelCase")]
67
    Mean { ignore_no_data: bool },
68
    #[serde(rename_all = "camelCase")]
69
    Sum { ignore_no_data: bool },
70
    #[serde(rename_all = "camelCase")]
71
    Count { ignore_no_data: bool },
72
    #[serde(rename_all = "camelCase")]
73
    PercentileEstimate {
74
        ignore_no_data: bool,
75
        /// Must in in range [0, 1]
76
        percentile: f64,
77
    },
78
}
79

80
pub type TemporalRasterAggregation =
81
    Operator<TemporalRasterAggregationParameters, SingleRasterSource>;
82

83
impl OperatorName for TemporalRasterAggregation {
84
    const TYPE_NAME: &'static str = "TemporalRasterAggregation";
85
}
86

87
#[typetag::serde]
×
88
#[async_trait]
89
impl RasterOperator for TemporalRasterAggregation {
90
    async fn _initialize(
91
        self: Box<Self>,
92
        path: WorkflowOperatorPath,
93
        context: &dyn ExecutionContext,
94
    ) -> Result<Box<dyn InitializedRasterOperator>> {
21✔
95
        ensure!(self.params.window.step > 0, error::WindowSizeMustNotBeZero);
21✔
96

21✔
97
        let name = CanonicOperatorName::from(&self);
21✔
98

21✔
99
        let initialized_source = self.sources.initialize_sources(path, context).await?;
21✔
100
        let source = initialized_source.raster;
21✔
101

21✔
102
        debug!(
21✔
103
            "Initializing TemporalRasterAggregation with {:?}.",
21✔
104
            &self.params
×
105
        );
21✔
106

21✔
107
        let mut out_result_descriptor = source.result_descriptor().clone();
21✔
108

21✔
109
        if let Some(output_type) = self.params.output_type {
21✔
110
            out_result_descriptor.data_type = output_type;
1✔
111
        };
20✔
112

21✔
113
        let initialized_operator = InitializedTemporalRasterAggregation {
21✔
114
            name,
21✔
115
            aggregation_type: self.params.aggregation,
21✔
116
            window: self.params.window,
21✔
117
            window_reference: self
21✔
118
                .params
21✔
119
                .window_reference
21✔
120
                .unwrap_or(TimeInstance::EPOCH_START),
21✔
121
            result_descriptor: out_result_descriptor,
21✔
122
            source,
21✔
123
            tiling_specification: context.tiling_specification(),
21✔
124
            output_type: self.params.output_type,
21✔
125
        };
21✔
126

21✔
127
        Ok(initialized_operator.boxed())
21✔
128
    }
21✔
129

130
    span_fn!(TemporalRasterAggregation);
131
}
132

133
pub struct InitializedTemporalRasterAggregation {
134
    name: CanonicOperatorName,
135
    aggregation_type: Aggregation,
136
    window: TimeStep,
137
    window_reference: TimeInstance,
138
    source: Box<dyn InitializedRasterOperator>,
139
    result_descriptor: RasterResultDescriptor,
140
    tiling_specification: TilingSpecification,
141
    output_type: Option<RasterDataType>,
142
}
143

144
impl InitializedRasterOperator for InitializedTemporalRasterAggregation {
145
    fn result_descriptor(&self) -> &RasterResultDescriptor {
×
146
        &self.result_descriptor
×
147
    }
×
148

149
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor> {
21✔
150
        let source_processor = self.source.query_processor()?;
21✔
151

152
        let source_processor: TypedRasterQueryProcessor = match self.output_type {
21✔
153
            Some(RasterDataType::U8) => source_processor.into_u8().into(),
×
154
            Some(RasterDataType::U16) => source_processor.into_u16().into(),
1✔
155
            Some(RasterDataType::U32) => source_processor.into_u32().into(),
×
156
            Some(RasterDataType::U64) => source_processor.into_u64().into(),
×
157
            Some(RasterDataType::I8) => source_processor.into_i8().into(),
×
158
            Some(RasterDataType::I16) => source_processor.into_i16().into(),
×
159
            Some(RasterDataType::I32) => source_processor.into_i32().into(),
×
160
            Some(RasterDataType::I64) => source_processor.into_i64().into(),
×
161
            Some(RasterDataType::F32) => source_processor.into_f32().into(),
×
162
            Some(RasterDataType::F64) => source_processor.into_f64().into(),
×
163
            // use the same output type as the input type
164
            None => source_processor,
20✔
165
        };
166

167
        let res = call_on_generic_raster_processor!(
21✔
168
            source_processor, p =>
21✔
169
            TemporalRasterAggregationProcessor::new(
20✔
170
                self.result_descriptor.clone(),
20✔
171
                self.aggregation_type,
20✔
172
                self.window,
20✔
173
                self.window_reference,
20✔
174
                p,
20✔
175
                self.tiling_specification,
20✔
176
            ).boxed()
20✔
177
            .into()
20✔
178
        );
179

180
        Ok(res)
21✔
181
    }
21✔
182

183
    fn canonic_name(&self) -> CanonicOperatorName {
×
184
        self.name.clone()
×
185
    }
×
186
}
187

188
pub struct TemporalRasterAggregationProcessor<Q, P>
189
where
190
    Q: RasterQueryProcessor<RasterType = P>,
191
    P: Pixel,
192
{
193
    result_descriptor: RasterResultDescriptor,
194
    aggregation_type: Aggregation,
195
    window: TimeStep,
196
    window_reference: TimeInstance,
197
    source: Q,
198
    tiling_specification: TilingSpecification,
199
}
200

201
impl<Q, P> TemporalRasterAggregationProcessor<Q, P>
202
where
203
    Q: RasterQueryProcessor<RasterType = P>
204
        + QueryProcessor<
205
            Output = RasterTile2D<P>,
206
            SpatialBounds = SpatialPartition2D,
207
            Selection = BandSelection,
208
            ResultDescription = RasterResultDescriptor,
209
        >,
210
    P: Pixel,
211
{
212
    fn new(
21✔
213
        result_descriptor: RasterResultDescriptor,
21✔
214
        aggregation_type: Aggregation,
21✔
215
        window: TimeStep,
21✔
216
        window_reference: TimeInstance,
21✔
217
        source: Q,
21✔
218
        tiling_specification: TilingSpecification,
21✔
219
    ) -> Self {
21✔
220
        Self {
21✔
221
            result_descriptor,
21✔
222
            aggregation_type,
21✔
223
            window,
21✔
224
            window_reference,
21✔
225
            source,
21✔
226
            tiling_specification,
21✔
227
        }
21✔
228
    }
21✔
229

230
    fn create_subquery<F: TemporalRasterPixelAggregator<P> + 'static, FoldFn>(
18✔
231
        &self,
18✔
232
        fold_fn: FoldFn,
18✔
233
    ) -> super::subquery::TemporalRasterAggregationSubQuery<FoldFn, P, F> {
18✔
234
        super::subquery::TemporalRasterAggregationSubQuery {
18✔
235
            fold_fn,
18✔
236
            step: self.window,
18✔
237
            step_reference: self.window_reference,
18✔
238
            _phantom_pixel_type: PhantomData,
18✔
239
        }
18✔
240
    }
18✔
241

242
    fn create_global_state_subquery<
1✔
243
        F: GlobalStateTemporalRasterPixelAggregator<P> + 'static,
1✔
244
        FoldFn,
1✔
245
    >(
1✔
246
        &self,
1✔
247
        aggregator: F,
1✔
248
        fold_fn: FoldFn,
1✔
249
    ) -> GlobalStateTemporalRasterAggregationSubQuery<FoldFn, P, F> {
1✔
250
        GlobalStateTemporalRasterAggregationSubQuery {
1✔
251
            aggregator: Arc::new(aggregator),
1✔
252
            fold_fn,
1✔
253
            step: self.window,
1✔
254
            step_reference: self.window_reference,
1✔
255
            _phantom_pixel_type: PhantomData,
1✔
256
        }
1✔
257
    }
1✔
258

259
    fn create_subquery_first<F>(
1✔
260
        &self,
1✔
261
        fold_fn: F,
1✔
262
    ) -> TemporalRasterAggregationSubQueryNoDataOnly<F, P> {
1✔
263
        TemporalRasterAggregationSubQueryNoDataOnly {
1✔
264
            fold_fn,
1✔
265
            step: self.window,
1✔
266
            step_reference: self.window_reference,
1✔
267
            _phantom_pixel_type: PhantomData,
1✔
268
        }
1✔
269
    }
1✔
270

271
    fn create_subquery_last<F>(
2✔
272
        &self,
2✔
273
        fold_fn: F,
2✔
274
    ) -> TemporalRasterAggregationSubQueryNoDataOnly<F, P> {
2✔
275
        TemporalRasterAggregationSubQueryNoDataOnly {
2✔
276
            fold_fn,
2✔
277
            step: self.window,
2✔
278
            step_reference: self.window_reference,
2✔
279
            _phantom_pixel_type: PhantomData,
2✔
280
        }
2✔
281
    }
2✔
282

283
    #[allow(clippy::too_many_lines)]
284
    fn create_subquery_adapter_stream_for_single_band<'a>(
22✔
285
        &'a self,
22✔
286
        query: RasterQueryRectangle,
22✔
287
        ctx: &'a dyn crate::engine::QueryContext,
22✔
288
    ) -> Result<futures::stream::BoxStream<'a, Result<RasterTile2D<P>>>> {
22✔
289
        ensure!(
22✔
290
            query.attributes.count() == 1,
22✔
291
            error::InvalidBandCount {
×
292
                expected: 1u32,
×
293
                found: query.attributes.count()
×
294
            }
×
295
        );
296

297
        Ok(match self.aggregation_type {
22✔
298
            Aggregation::Min {
299
                ignore_no_data: true,
300
            } => self
×
301
                .create_subquery(
×
302
                    super::subquery::subquery_all_tiles_fold_fn::<
×
303
                        P,
×
304
                        MinPixelAggregatorIngoringNoData,
×
305
                    >,
×
306
                )
×
307
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
×
308
                .expect("no tiles must be skipped in Aggregation::Min"),
×
309
            Aggregation::Min {
310
                ignore_no_data: false,
311
            } => self
1✔
312
                .create_subquery(
1✔
313
                    super::subquery::subquery_all_tiles_fold_fn::<P, MinPixelAggregator>,
1✔
314
                )
1✔
315
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
316
                .expect("no tiles must be skipped in Aggregation::Min"),
1✔
317
            Aggregation::Max {
318
                ignore_no_data: true,
319
            } => self
1✔
320
                .create_subquery(
1✔
321
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
322
                        P,
1✔
323
                        MaxPixelAggregatorIngoringNoData,
1✔
324
                    >,
1✔
325
                )
1✔
326
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
327
                .expect("no tiles must be skipped in Aggregation::Max"),
1✔
328
            Aggregation::Max {
329
                ignore_no_data: false,
330
            } => self
3✔
331
                .create_subquery(
3✔
332
                    super::subquery::subquery_all_tiles_fold_fn::<P, MaxPixelAggregator>,
3✔
333
                )
3✔
334
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
3✔
335
                .expect("no tiles must be skipped in Aggregation::Max"),
3✔
336

337
            Aggregation::First {
338
                ignore_no_data: true,
339
            } => self
1✔
340
                .create_subquery(
1✔
341
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
342
                        P,
1✔
343
                        FirstPixelAggregatorIngoringNoData,
1✔
344
                    >,
1✔
345
                )
1✔
346
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
347
                .expect("no tiles must be skipped in Aggregation::First"),
1✔
348
            Aggregation::First {
349
                ignore_no_data: false,
350
            } => self
1✔
351
                .create_subquery_first(first_tile_fold_future::<P>)
1✔
352
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
353
                .expect("no tiles must be skipped in Aggregation::First"),
1✔
354
            Aggregation::Last {
355
                ignore_no_data: true,
356
            } => self
1✔
357
                .create_subquery(
1✔
358
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
359
                        P,
1✔
360
                        LastPixelAggregatorIngoringNoData,
1✔
361
                    >,
1✔
362
                )
1✔
363
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
364
                .expect("no tiles must be skipped in Aggregation::Last"),
1✔
365

366
            Aggregation::Last {
367
                ignore_no_data: false,
368
            } => self
2✔
369
                .create_subquery_last(last_tile_fold_future::<P>)
2✔
370
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
2✔
371
                .expect("no tiles must be skipped in Aggregation::Last"),
2✔
372

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

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

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

403
            Aggregation::Sum {
404
                ignore_no_data: false,
405
            } => self
5✔
406
                .create_subquery(
5✔
407
                    super::subquery::subquery_all_tiles_fold_fn::<P, SumPixelAggregator>,
5✔
408
                )
5✔
409
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
5✔
410
                .expect("no tiles must be skipped in Aggregation::Sum"),
5✔
411

412
            Aggregation::Count {
413
                ignore_no_data: true,
414
            } => self
1✔
415
                .create_subquery(
1✔
416
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
417
                        P,
1✔
418
                        CountPixelAggregatorIngoringNoData,
1✔
419
                    >,
1✔
420
                )
1✔
421
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
422
                .expect("no tiles must be skipped in Aggregation::Sum"),
1✔
423

424
            Aggregation::Count {
425
                ignore_no_data: false,
426
            } => self
2✔
427
                .create_subquery(
2✔
428
                    super::subquery::subquery_all_tiles_fold_fn::<P, CountPixelAggregator>,
2✔
429
                )
2✔
430
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
2✔
431
                .expect("no tiles must be skipped in Aggregation::Sum"),
2✔
432
            Aggregation::PercentileEstimate {
433
                ignore_no_data: true,
434
                percentile,
×
435
            } => self
×
436
                .create_global_state_subquery(
×
437
                    PercentileEstimateAggregator::<true>::new(percentile),
×
438
                    super::subquery::subquery_all_tiles_global_state_fold_fn,
×
439
                )
×
440
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
×
441
                .expect("no tiles must be skipped in Aggregation::PercentileEstimate"),
×
442
            Aggregation::PercentileEstimate {
443
                ignore_no_data: false,
444
                percentile,
1✔
445
            } => self
1✔
446
                .create_global_state_subquery(
1✔
447
                    PercentileEstimateAggregator::<false>::new(percentile),
1✔
448
                    super::subquery::subquery_all_tiles_global_state_fold_fn,
1✔
449
                )
1✔
450
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
451
                .expect("no tiles must be skipped in Aggregation::PercentileEstimate"),
1✔
452
        })
453
    }
22✔
454
}
455

456
#[async_trait]
457
impl<Q, P> QueryProcessor for TemporalRasterAggregationProcessor<Q, P>
458
where
459
    Q: QueryProcessor<
460
        Output = RasterTile2D<P>,
461
        SpatialBounds = SpatialPartition2D,
462
        Selection = BandSelection,
463
        ResultDescription = RasterResultDescriptor,
464
    >,
465
    P: Pixel,
466
{
467
    type Output = RasterTile2D<P>;
468
    type SpatialBounds = SpatialPartition2D;
469
    type Selection = BandSelection;
470
    type ResultDescription = RasterResultDescriptor;
471

472
    async fn _query<'a>(
473
        &'a self,
474
        query: RasterQueryRectangle,
475
        ctx: &'a dyn crate::engine::QueryContext,
476
    ) -> Result<futures::stream::BoxStream<'a, Result<Self::Output>>> {
21✔
477
        stack_individual_aligned_raster_bands(&query, ctx, |query, ctx| async {
22✔
478
            self.create_subquery_adapter_stream_for_single_band(query, ctx)
22✔
479
        })
22✔
480
        .await
21✔
481
    }
21✔
482

483
    fn result_descriptor(&self) -> &Self::ResultDescription {
42✔
484
        &self.result_descriptor
42✔
485
    }
42✔
486
}
487

488
#[cfg(test)]
489
mod tests {
490
    use futures::stream::StreamExt;
491
    use geoengine_datatypes::{
492
        primitives::{CacheHint, SpatialResolution, TimeInterval},
493
        raster::{
494
            EmptyGrid, EmptyGrid2D, Grid2D, GridOrEmpty, MaskedGrid2D, RasterDataType, RenameBands,
495
            TileInformation, TilesEqualIgnoringCacheHint,
496
        },
497
        spatial_reference::SpatialReference,
498
        util::test::TestDefault,
499
    };
500

501
    use crate::{
502
        engine::{
503
            MockExecutionContext, MockQueryContext, MultipleRasterSources, RasterBandDescriptors,
504
        },
505
        mock::{MockRasterSource, MockRasterSourceParams},
506
        processing::{
507
            raster_stacker::{RasterStacker, RasterStackerParams},
508
            Expression, ExpressionParams,
509
        },
510
    };
511

512
    use super::*;
513

514
    #[tokio::test]
515
    #[allow(clippy::too_many_lines)]
516
    async fn test_min() {
1✔
517
        let raster_tiles = make_raster();
1✔
518

1✔
519
        let mrs = MockRasterSource {
1✔
520
            params: MockRasterSourceParams {
1✔
521
                data: raster_tiles,
1✔
522
                result_descriptor: RasterResultDescriptor {
1✔
523
                    data_type: RasterDataType::U8,
1✔
524
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
525
                    time: None,
1✔
526
                    bbox: None,
1✔
527
                    resolution: None,
1✔
528
                    bands: RasterBandDescriptors::new_single_band(),
1✔
529
                },
1✔
530
            },
1✔
531
        }
1✔
532
        .boxed();
1✔
533

1✔
534
        let agg = TemporalRasterAggregation {
1✔
535
            params: TemporalRasterAggregationParameters {
1✔
536
                aggregation: Aggregation::Min {
1✔
537
                    ignore_no_data: false,
1✔
538
                },
1✔
539
                window: TimeStep {
1✔
540
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
541
                    step: 20,
1✔
542
                },
1✔
543
                window_reference: None,
1✔
544
                output_type: None,
1✔
545
            },
1✔
546
            sources: SingleRasterSource { raster: mrs },
1✔
547
        }
1✔
548
        .boxed();
1✔
549

1✔
550
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
551
            (0., 0.).into(),
1✔
552
            [3, 2].into(),
1✔
553
        ));
1✔
554
        let query_rect = RasterQueryRectangle {
1✔
555
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
556
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
557
            spatial_resolution: SpatialResolution::one(),
1✔
558
            attributes: BandSelection::first(),
1✔
559
        };
1✔
560
        let query_ctx = MockQueryContext::test_default();
1✔
561

1✔
562
        let qp = agg
1✔
563
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
564
            .await
1✔
565
            .unwrap()
1✔
566
            .query_processor()
1✔
567
            .unwrap()
1✔
568
            .get_u8()
1✔
569
            .unwrap();
1✔
570

1✔
571
        let result = qp
1✔
572
            .query(query_rect, &query_ctx)
1✔
573
            .await
1✔
574
            .unwrap()
1✔
575
            .collect::<Vec<_>>()
1✔
576
            .await;
8✔
577

1✔
578
        assert_eq!(result.len(), 4);
1✔
579

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

1✔
594
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
595
            &RasterTile2D::new_with_tile_info(
1✔
596
                TimeInterval::new_unchecked(0, 20),
1✔
597
                TileInformation {
1✔
598
                    global_tile_position: [-1, 1].into(),
1✔
599
                    tile_size_in_pixels: [3, 2].into(),
1✔
600
                    global_geo_transform: TestDefault::test_default(),
1✔
601
                },
1✔
602
                0,
1✔
603
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
1✔
604
                CacheHint::default()
1✔
605
            )
1✔
606
        ));
1✔
607

1✔
608
        assert!(result[2].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
609
            &RasterTile2D::new_with_tile_info(
1✔
610
                TimeInterval::new_unchecked(20, 40),
1✔
611
                TileInformation {
1✔
612
                    global_tile_position: [-1, 0].into(),
1✔
613
                    tile_size_in_pixels: [3, 2].into(),
1✔
614
                    global_geo_transform: TestDefault::test_default(),
1✔
615
                },
1✔
616
                0,
1✔
617
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
618
                CacheHint::default()
1✔
619
            )
1✔
620
        ));
1✔
621

1✔
622
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
623
            &RasterTile2D::new_with_tile_info(
1✔
624
                TimeInterval::new_unchecked(20, 40),
1✔
625
                TileInformation {
1✔
626
                    global_tile_position: [-1, 1].into(),
1✔
627
                    tile_size_in_pixels: [3, 2].into(),
1✔
628
                    global_geo_transform: TestDefault::test_default(),
1✔
629
                },
1✔
630
                0,
1✔
631
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
1✔
632
                CacheHint::default()
1✔
633
            )
1✔
634
        ));
1✔
635
    }
1✔
636

637
    #[tokio::test]
638
    #[allow(clippy::too_many_lines)]
639
    async fn test_max() {
1✔
640
        let raster_tiles = make_raster();
1✔
641

1✔
642
        let mrs = MockRasterSource {
1✔
643
            params: MockRasterSourceParams {
1✔
644
                data: raster_tiles,
1✔
645
                result_descriptor: RasterResultDescriptor {
1✔
646
                    data_type: RasterDataType::U8,
1✔
647
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
648
                    time: None,
1✔
649
                    bbox: None,
1✔
650
                    resolution: None,
1✔
651
                    bands: RasterBandDescriptors::new_single_band(),
1✔
652
                },
1✔
653
            },
1✔
654
        }
1✔
655
        .boxed();
1✔
656

1✔
657
        let agg = TemporalRasterAggregation {
1✔
658
            params: TemporalRasterAggregationParameters {
1✔
659
                aggregation: Aggregation::Max {
1✔
660
                    ignore_no_data: false,
1✔
661
                },
1✔
662
                window: TimeStep {
1✔
663
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
664
                    step: 20,
1✔
665
                },
1✔
666
                window_reference: None,
1✔
667
                output_type: None,
1✔
668
            },
1✔
669
            sources: SingleRasterSource { raster: mrs },
1✔
670
        }
1✔
671
        .boxed();
1✔
672

1✔
673
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
674
            (0., 0.).into(),
1✔
675
            [3, 2].into(),
1✔
676
        ));
1✔
677
        let query_rect = RasterQueryRectangle {
1✔
678
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
679
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
680
            spatial_resolution: SpatialResolution::one(),
1✔
681
            attributes: BandSelection::first(),
1✔
682
        };
1✔
683
        let query_ctx = MockQueryContext::test_default();
1✔
684

1✔
685
        let qp = agg
1✔
686
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
687
            .await
1✔
688
            .unwrap()
1✔
689
            .query_processor()
1✔
690
            .unwrap()
1✔
691
            .get_u8()
1✔
692
            .unwrap();
1✔
693

1✔
694
        let result = qp
1✔
695
            .query(query_rect, &query_ctx)
1✔
696
            .await
1✔
697
            .unwrap()
1✔
698
            .collect::<Vec<_>>()
1✔
699
            .await;
8✔
700

1✔
701
        assert_eq!(result.len(), 4);
1✔
702

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

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

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

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

760
    #[tokio::test]
761
    #[allow(clippy::too_many_lines)]
762
    async fn test_max_with_no_data() {
1✔
763
        let raster_tiles = make_raster(); // TODO: switch to make_raster_with_no_data?
1✔
764

1✔
765
        let mrs = MockRasterSource {
1✔
766
            params: MockRasterSourceParams {
1✔
767
                data: raster_tiles,
1✔
768
                result_descriptor: RasterResultDescriptor {
1✔
769
                    data_type: RasterDataType::U8,
1✔
770
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
771
                    time: None,
1✔
772
                    bbox: None,
1✔
773
                    resolution: None,
1✔
774
                    bands: RasterBandDescriptors::new_single_band(),
1✔
775
                },
1✔
776
            },
1✔
777
        }
1✔
778
        .boxed();
1✔
779

1✔
780
        let agg = TemporalRasterAggregation {
1✔
781
            params: TemporalRasterAggregationParameters {
1✔
782
                aggregation: Aggregation::Max {
1✔
783
                    ignore_no_data: false,
1✔
784
                },
1✔
785
                window: TimeStep {
1✔
786
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
787
                    step: 20,
1✔
788
                },
1✔
789
                window_reference: None,
1✔
790
                output_type: None,
1✔
791
            },
1✔
792
            sources: SingleRasterSource { raster: mrs },
1✔
793
        }
1✔
794
        .boxed();
1✔
795

1✔
796
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
797
            (0., 0.).into(),
1✔
798
            [3, 2].into(),
1✔
799
        ));
1✔
800
        let query_rect = RasterQueryRectangle {
1✔
801
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
802
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
803
            spatial_resolution: SpatialResolution::one(),
1✔
804
            attributes: BandSelection::first(),
1✔
805
        };
1✔
806
        let query_ctx = MockQueryContext::test_default();
1✔
807

1✔
808
        let qp = agg
1✔
809
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
810
            .await
1✔
811
            .unwrap()
1✔
812
            .query_processor()
1✔
813
            .unwrap()
1✔
814
            .get_u8()
1✔
815
            .unwrap();
1✔
816

1✔
817
        let result = qp
1✔
818
            .query(query_rect, &query_ctx)
1✔
819
            .await
1✔
820
            .unwrap()
1✔
821
            .collect::<Vec<_>>()
1✔
822
            .await;
8✔
823

1✔
824
        assert_eq!(result.len(), 4);
1✔
825

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

1✔
840
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
841
            &RasterTile2D::new_with_tile_info(
1✔
842
                TimeInterval::new_unchecked(0, 20),
1✔
843
                TileInformation {
1✔
844
                    global_tile_position: [-1, 1].into(),
1✔
845
                    tile_size_in_pixels: [3, 2].into(),
1✔
846
                    global_geo_transform: TestDefault::test_default(),
1✔
847
                },
1✔
848
                0,
1✔
849
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
850
                CacheHint::default()
1✔
851
            )
1✔
852
        ));
1✔
853

1✔
854
        assert!(result[2].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
855
            &RasterTile2D::new_with_tile_info(
1✔
856
                TimeInterval::new_unchecked(20, 40),
1✔
857
                TileInformation {
1✔
858
                    global_tile_position: [-1, 0].into(),
1✔
859
                    tile_size_in_pixels: [3, 2].into(),
1✔
860
                    global_geo_transform: TestDefault::test_default(),
1✔
861
                },
1✔
862
                0,
1✔
863
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
864
                CacheHint::default()
1✔
865
            )
1✔
866
        ));
1✔
867

1✔
868
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
869
            &RasterTile2D::new_with_tile_info(
1✔
870
                TimeInterval::new_unchecked(20, 40),
1✔
871
                TileInformation {
1✔
872
                    global_tile_position: [-1, 1].into(),
1✔
873
                    tile_size_in_pixels: [3, 2].into(),
1✔
874
                    global_geo_transform: TestDefault::test_default(),
1✔
875
                },
1✔
876
                0,
1✔
877
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
878
                CacheHint::default()
1✔
879
            )
1✔
880
        ));
1✔
881
    }
1✔
882

883
    #[tokio::test]
884
    #[allow(clippy::too_many_lines)]
885
    async fn test_max_with_no_data_but_ignoring_it() {
1✔
886
        let raster_tiles = make_raster(); // TODO: switch to make_raster_with_no_data?
1✔
887

1✔
888
        let mrs = MockRasterSource {
1✔
889
            params: MockRasterSourceParams {
1✔
890
                data: raster_tiles,
1✔
891
                result_descriptor: RasterResultDescriptor {
1✔
892
                    data_type: RasterDataType::U8,
1✔
893
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
894
                    time: None,
1✔
895
                    bbox: None,
1✔
896
                    resolution: None,
1✔
897
                    bands: RasterBandDescriptors::new_single_band(),
1✔
898
                },
1✔
899
            },
1✔
900
        }
1✔
901
        .boxed();
1✔
902

1✔
903
        let agg = TemporalRasterAggregation {
1✔
904
            params: TemporalRasterAggregationParameters {
1✔
905
                aggregation: Aggregation::Max {
1✔
906
                    ignore_no_data: true,
1✔
907
                },
1✔
908
                window: TimeStep {
1✔
909
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
910
                    step: 20,
1✔
911
                },
1✔
912
                window_reference: None,
1✔
913
                output_type: None,
1✔
914
            },
1✔
915
            sources: SingleRasterSource { raster: mrs },
1✔
916
        }
1✔
917
        .boxed();
1✔
918

1✔
919
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
920
            (0., 0.).into(),
1✔
921
            [3, 2].into(),
1✔
922
        ));
1✔
923
        let query_rect = RasterQueryRectangle {
1✔
924
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
925
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
926
            spatial_resolution: SpatialResolution::one(),
1✔
927
            attributes: BandSelection::first(),
1✔
928
        };
1✔
929
        let query_ctx = MockQueryContext::test_default();
1✔
930

1✔
931
        let qp = agg
1✔
932
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
933
            .await
1✔
934
            .unwrap()
1✔
935
            .query_processor()
1✔
936
            .unwrap()
1✔
937
            .get_u8()
1✔
938
            .unwrap();
1✔
939

1✔
940
        let result = qp
1✔
941
            .query(query_rect, &query_ctx)
1✔
942
            .await
1✔
943
            .unwrap()
1✔
944
            .collect::<Vec<_>>()
1✔
945
            .await;
8✔
946

1✔
947
        assert_eq!(result.len(), 4);
1✔
948

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

1✔
963
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
964
            &RasterTile2D::new_with_tile_info(
1✔
965
                TimeInterval::new_unchecked(0, 20),
1✔
966
                TileInformation {
1✔
967
                    global_tile_position: [-1, 1].into(),
1✔
968
                    tile_size_in_pixels: [3, 2].into(),
1✔
969
                    global_geo_transform: TestDefault::test_default(),
1✔
970
                },
1✔
971
                0,
1✔
972
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
973
                CacheHint::default()
1✔
974
            )
1✔
975
        ));
1✔
976

1✔
977
        assert!(result[2].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
978
            &RasterTile2D::new_with_tile_info(
1✔
979
                TimeInterval::new_unchecked(20, 40),
1✔
980
                TileInformation {
1✔
981
                    global_tile_position: [-1, 0].into(),
1✔
982
                    tile_size_in_pixels: [3, 2].into(),
1✔
983
                    global_geo_transform: TestDefault::test_default(),
1✔
984
                },
1✔
985
                0,
1✔
986
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
987
                CacheHint::default()
1✔
988
            )
1✔
989
        ));
1✔
990

1✔
991
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
992
            &RasterTile2D::new_with_tile_info(
1✔
993
                TimeInterval::new_unchecked(20, 40),
1✔
994
                TileInformation {
1✔
995
                    global_tile_position: [-1, 1].into(),
1✔
996
                    tile_size_in_pixels: [3, 2].into(),
1✔
997
                    global_geo_transform: TestDefault::test_default(),
1✔
998
                },
1✔
999
                0,
1✔
1000
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
1001
                CacheHint::default()
1✔
1002
            )
1✔
1003
        ));
1✔
1004
    }
1✔
1005

1006
    #[tokio::test]
1007
    #[allow(clippy::too_many_lines)]
1008
    async fn test_only_no_data() {
1✔
1009
        let mrs = MockRasterSource {
1✔
1010
            params: MockRasterSourceParams {
1✔
1011
                data: vec![RasterTile2D::new_with_tile_info(
1✔
1012
                    TimeInterval::new_unchecked(0, 20),
1✔
1013
                    TileInformation {
1✔
1014
                        global_tile_position: [-1, 0].into(),
1✔
1015
                        tile_size_in_pixels: [3, 2].into(),
1✔
1016
                        global_geo_transform: TestDefault::test_default(),
1✔
1017
                    },
1✔
1018
                    0,
1✔
1019
                    GridOrEmpty::from(EmptyGrid2D::<u8>::new([3, 2].into())),
1✔
1020
                    CacheHint::default(),
1✔
1021
                )],
1✔
1022
                result_descriptor: RasterResultDescriptor {
1✔
1023
                    data_type: RasterDataType::U8,
1✔
1024
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1025
                    time: None,
1✔
1026
                    bbox: None,
1✔
1027
                    resolution: None,
1✔
1028
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1029
                },
1✔
1030
            },
1✔
1031
        }
1✔
1032
        .boxed();
1✔
1033

1✔
1034
        let agg = TemporalRasterAggregation {
1✔
1035
            params: TemporalRasterAggregationParameters {
1✔
1036
                aggregation: Aggregation::Max {
1✔
1037
                    ignore_no_data: false,
1✔
1038
                },
1✔
1039
                window: TimeStep {
1✔
1040
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1041
                    step: 20,
1✔
1042
                },
1✔
1043
                window_reference: None,
1✔
1044
                output_type: None,
1✔
1045
            },
1✔
1046
            sources: SingleRasterSource { raster: mrs },
1✔
1047
        }
1✔
1048
        .boxed();
1✔
1049

1✔
1050
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1051
            (0., 0.).into(),
1✔
1052
            [3, 2].into(),
1✔
1053
        ));
1✔
1054
        let query_rect = RasterQueryRectangle {
1✔
1055
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (2., 0.).into()),
1✔
1056
            time_interval: TimeInterval::new_unchecked(0, 20),
1✔
1057
            spatial_resolution: SpatialResolution::one(),
1✔
1058
            attributes: BandSelection::first(),
1✔
1059
        };
1✔
1060
        let query_ctx = MockQueryContext::test_default();
1✔
1061

1✔
1062
        let qp = agg
1✔
1063
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1064
            .await
1✔
1065
            .unwrap()
1✔
1066
            .query_processor()
1✔
1067
            .unwrap()
1✔
1068
            .get_u8()
1✔
1069
            .unwrap();
1✔
1070

1✔
1071
        let result = qp
1✔
1072
            .query(query_rect, &query_ctx)
1✔
1073
            .await
1✔
1074
            .unwrap()
1✔
1075
            .collect::<Vec<_>>()
1✔
1076
            .await;
1✔
1077

1✔
1078
        assert_eq!(result.len(), 1);
1✔
1079

1✔
1080
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1081
            &RasterTile2D::new_with_tile_info(
1✔
1082
                TimeInterval::new_unchecked(0, 20),
1✔
1083
                TileInformation {
1✔
1084
                    global_tile_position: [-1, 0].into(),
1✔
1085
                    tile_size_in_pixels: [3, 2].into(),
1✔
1086
                    global_geo_transform: TestDefault::test_default(),
1✔
1087
                },
1✔
1088
                0,
1✔
1089
                GridOrEmpty::Empty(EmptyGrid::new([3, 2].into())),
1✔
1090
                CacheHint::default()
1✔
1091
            )
1✔
1092
        ));
1✔
1093
    }
1✔
1094

1095
    #[tokio::test]
1096
    async fn test_first_with_no_data() {
1✔
1097
        let raster_tiles = make_raster_with_no_data();
1✔
1098

1✔
1099
        let mrs = MockRasterSource {
1✔
1100
            params: MockRasterSourceParams {
1✔
1101
                data: raster_tiles,
1✔
1102
                result_descriptor: RasterResultDescriptor {
1✔
1103
                    data_type: RasterDataType::U8,
1✔
1104
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1105
                    time: None,
1✔
1106
                    bbox: None,
1✔
1107
                    resolution: None,
1✔
1108
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1109
                },
1✔
1110
            },
1✔
1111
        }
1✔
1112
        .boxed();
1✔
1113

1✔
1114
        let agg = TemporalRasterAggregation {
1✔
1115
            params: TemporalRasterAggregationParameters {
1✔
1116
                aggregation: Aggregation::First {
1✔
1117
                    ignore_no_data: true,
1✔
1118
                },
1✔
1119
                window: TimeStep {
1✔
1120
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1121
                    step: 30,
1✔
1122
                },
1✔
1123
                window_reference: None,
1✔
1124
                output_type: None,
1✔
1125
            },
1✔
1126
            sources: SingleRasterSource { raster: mrs },
1✔
1127
        }
1✔
1128
        .boxed();
1✔
1129

1✔
1130
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1131
            (0., 0.).into(),
1✔
1132
            [3, 2].into(),
1✔
1133
        ));
1✔
1134
        let query_rect = RasterQueryRectangle {
1✔
1135
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1136
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1137
            spatial_resolution: SpatialResolution::one(),
1✔
1138
            attributes: BandSelection::first(),
1✔
1139
        };
1✔
1140
        let query_ctx = MockQueryContext::test_default();
1✔
1141

1✔
1142
        let qp = agg
1✔
1143
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1144
            .await
1✔
1145
            .unwrap()
1✔
1146
            .query_processor()
1✔
1147
            .unwrap()
1✔
1148
            .get_u8()
1✔
1149
            .unwrap();
1✔
1150

1✔
1151
        let result = qp
1✔
1152
            .query(query_rect, &query_ctx)
1✔
1153
            .await
1✔
1154
            .unwrap()
1✔
1155
            .collect::<Vec<_>>()
1✔
1156
            .await;
6✔
1157

1✔
1158
        assert_eq!(result.len(), 2);
1✔
1159

1✔
1160
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1161
            &RasterTile2D::new_with_tile_info(
1✔
1162
                TimeInterval::new_unchecked(0, 30),
1✔
1163
                TileInformation {
1✔
1164
                    global_tile_position: [-1, 0].into(),
1✔
1165
                    tile_size_in_pixels: [3, 2].into(),
1✔
1166
                    global_geo_transform: TestDefault::test_default(),
1✔
1167
                },
1✔
1168
                0,
1✔
1169
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 16, 11, 12]).unwrap()),
1✔
1170
                CacheHint::default()
1✔
1171
            )
1✔
1172
        ));
1✔
1173

1✔
1174
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1175
            &RasterTile2D::new_with_tile_info(
1✔
1176
                TimeInterval::new_unchecked(0, 30),
1✔
1177
                TileInformation {
1✔
1178
                    global_tile_position: [-1, 1].into(),
1✔
1179
                    tile_size_in_pixels: [3, 2].into(),
1✔
1180
                    global_geo_transform: TestDefault::test_default(),
1✔
1181
                },
1✔
1182
                0,
1✔
1183
                GridOrEmpty::from(
1✔
1184
                    MaskedGrid2D::new(
1✔
1185
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1186
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1187
                            .unwrap()
1✔
1188
                    )
1✔
1189
                    .unwrap()
1✔
1190
                ),
1✔
1191
                CacheHint::default()
1✔
1192
            )
1✔
1193
        ));
1✔
1194
    }
1✔
1195

1196
    #[tokio::test]
1197
    async fn test_last_with_no_data() {
1✔
1198
        let raster_tiles = make_raster_with_no_data();
1✔
1199

1✔
1200
        let mrs = MockRasterSource {
1✔
1201
            params: MockRasterSourceParams {
1✔
1202
                data: raster_tiles,
1✔
1203
                result_descriptor: RasterResultDescriptor {
1✔
1204
                    data_type: RasterDataType::U8,
1✔
1205
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1206
                    time: None,
1✔
1207
                    bbox: None,
1✔
1208
                    resolution: None,
1✔
1209
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1210
                },
1✔
1211
            },
1✔
1212
        }
1✔
1213
        .boxed();
1✔
1214

1✔
1215
        let agg = TemporalRasterAggregation {
1✔
1216
            params: TemporalRasterAggregationParameters {
1✔
1217
                aggregation: Aggregation::Last {
1✔
1218
                    ignore_no_data: true,
1✔
1219
                },
1✔
1220
                window: TimeStep {
1✔
1221
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1222
                    step: 30,
1✔
1223
                },
1✔
1224
                window_reference: None,
1✔
1225
                output_type: None,
1✔
1226
            },
1✔
1227
            sources: SingleRasterSource { raster: mrs },
1✔
1228
        }
1✔
1229
        .boxed();
1✔
1230

1✔
1231
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1232
            (0., 0.).into(),
1✔
1233
            [3, 2].into(),
1✔
1234
        ));
1✔
1235
        let query_rect = RasterQueryRectangle {
1✔
1236
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1237
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1238
            spatial_resolution: SpatialResolution::one(),
1✔
1239
            attributes: BandSelection::first(),
1✔
1240
        };
1✔
1241
        let query_ctx = MockQueryContext::test_default();
1✔
1242

1✔
1243
        let qp = agg
1✔
1244
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1245
            .await
1✔
1246
            .unwrap()
1✔
1247
            .query_processor()
1✔
1248
            .unwrap()
1✔
1249
            .get_u8()
1✔
1250
            .unwrap();
1✔
1251

1✔
1252
        let result = qp
1✔
1253
            .query(query_rect, &query_ctx)
1✔
1254
            .await
1✔
1255
            .unwrap()
1✔
1256
            .collect::<Vec<_>>()
1✔
1257
            .await;
6✔
1258

1✔
1259
        assert_eq!(result.len(), 2);
1✔
1260

1✔
1261
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1262
            &RasterTile2D::new_with_tile_info(
1✔
1263
                TimeInterval::new_unchecked(0, 30),
1✔
1264
                TileInformation {
1✔
1265
                    global_tile_position: [-1, 0].into(),
1✔
1266
                    tile_size_in_pixels: [3, 2].into(),
1✔
1267
                    global_geo_transform: TestDefault::test_default(),
1✔
1268
                },
1✔
1269
                0,
1✔
1270
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![13, 8, 15, 16, 17, 18]).unwrap()),
1✔
1271
                CacheHint::default()
1✔
1272
            )
1✔
1273
        ));
1✔
1274

1✔
1275
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1276
            &RasterTile2D::new_with_tile_info(
1✔
1277
                TimeInterval::new_unchecked(0, 30),
1✔
1278
                TileInformation {
1✔
1279
                    global_tile_position: [-1, 1].into(),
1✔
1280
                    tile_size_in_pixels: [3, 2].into(),
1✔
1281
                    global_geo_transform: TestDefault::test_default(),
1✔
1282
                },
1✔
1283
                0,
1✔
1284
                GridOrEmpty::from(
1✔
1285
                    MaskedGrid2D::new(
1✔
1286
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1287
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1288
                            .unwrap()
1✔
1289
                    )
1✔
1290
                    .unwrap()
1✔
1291
                ),
1✔
1292
                CacheHint::default()
1✔
1293
            )
1✔
1294
        ));
1✔
1295
    }
1✔
1296

1297
    #[tokio::test]
1298
    async fn test_last() {
1✔
1299
        let raster_tiles = make_raster_with_no_data();
1✔
1300

1✔
1301
        let mrs = MockRasterSource {
1✔
1302
            params: MockRasterSourceParams {
1✔
1303
                data: raster_tiles,
1✔
1304
                result_descriptor: RasterResultDescriptor {
1✔
1305
                    data_type: RasterDataType::U8,
1✔
1306
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1307
                    time: None,
1✔
1308
                    bbox: None,
1✔
1309
                    resolution: None,
1✔
1310
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1311
                },
1✔
1312
            },
1✔
1313
        }
1✔
1314
        .boxed();
1✔
1315

1✔
1316
        let agg = TemporalRasterAggregation {
1✔
1317
            params: TemporalRasterAggregationParameters {
1✔
1318
                aggregation: Aggregation::Last {
1✔
1319
                    ignore_no_data: false,
1✔
1320
                },
1✔
1321
                window: TimeStep {
1✔
1322
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1323
                    step: 30,
1✔
1324
                },
1✔
1325
                window_reference: None,
1✔
1326
                output_type: None,
1✔
1327
            },
1✔
1328
            sources: SingleRasterSource { raster: mrs },
1✔
1329
        }
1✔
1330
        .boxed();
1✔
1331

1✔
1332
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1333
            (0., 0.).into(),
1✔
1334
            [3, 2].into(),
1✔
1335
        ));
1✔
1336
        let query_rect = RasterQueryRectangle {
1✔
1337
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1338
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1339
            spatial_resolution: SpatialResolution::one(),
1✔
1340
            attributes: BandSelection::first(),
1✔
1341
        };
1✔
1342
        let query_ctx = MockQueryContext::test_default();
1✔
1343

1✔
1344
        let qp = agg
1✔
1345
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1346
            .await
1✔
1347
            .unwrap()
1✔
1348
            .query_processor()
1✔
1349
            .unwrap()
1✔
1350
            .get_u8()
1✔
1351
            .unwrap();
1✔
1352

1✔
1353
        let result = qp
1✔
1354
            .query(query_rect, &query_ctx)
1✔
1355
            .await
1✔
1356
            .unwrap()
1✔
1357
            .collect::<Vec<_>>()
1✔
1358
            .await;
8✔
1359

1✔
1360
        assert_eq!(result.len(), 2);
1✔
1361

1✔
1362
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1363
            &RasterTile2D::new_with_tile_info(
1✔
1364
                TimeInterval::new_unchecked(0, 30),
1✔
1365
                TileInformation {
1✔
1366
                    global_tile_position: [-1, 0].into(),
1✔
1367
                    tile_size_in_pixels: [3, 2].into(),
1✔
1368
                    global_geo_transform: TestDefault::test_default(),
1✔
1369
                },
1✔
1370
                0,
1✔
1371
                GridOrEmpty::from(
1✔
1372
                    MaskedGrid2D::new(
1✔
1373
                        Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
1✔
1374
                        Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
1✔
1375
                            .unwrap()
1✔
1376
                    )
1✔
1377
                    .unwrap()
1✔
1378
                ),
1✔
1379
                CacheHint::default()
1✔
1380
            )
1✔
1381
        ));
1✔
1382

1✔
1383
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1384
            &RasterTile2D::new_with_tile_info(
1✔
1385
                TimeInterval::new_unchecked(0, 30),
1✔
1386
                TileInformation {
1✔
1387
                    global_tile_position: [-1, 1].into(),
1✔
1388
                    tile_size_in_pixels: [3, 2].into(),
1✔
1389
                    global_geo_transform: TestDefault::test_default(),
1✔
1390
                },
1✔
1391
                0,
1✔
1392
                GridOrEmpty::Empty(EmptyGrid2D::new([3, 2].into())),
1✔
1393
                CacheHint::default()
1✔
1394
            )
1✔
1395
        ));
1✔
1396
    }
1✔
1397

1398
    #[tokio::test]
1399
    async fn test_first() {
1✔
1400
        let raster_tiles = make_raster_with_no_data();
1✔
1401

1✔
1402
        let mrs = MockRasterSource {
1✔
1403
            params: MockRasterSourceParams {
1✔
1404
                data: raster_tiles,
1✔
1405
                result_descriptor: RasterResultDescriptor {
1✔
1406
                    data_type: RasterDataType::U8,
1✔
1407
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1408
                    time: None,
1✔
1409
                    bbox: None,
1✔
1410
                    resolution: None,
1✔
1411
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1412
                },
1✔
1413
            },
1✔
1414
        }
1✔
1415
        .boxed();
1✔
1416

1✔
1417
        let agg = TemporalRasterAggregation {
1✔
1418
            params: TemporalRasterAggregationParameters {
1✔
1419
                aggregation: Aggregation::First {
1✔
1420
                    ignore_no_data: false,
1✔
1421
                },
1✔
1422
                window: TimeStep {
1✔
1423
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1424
                    step: 30,
1✔
1425
                },
1✔
1426
                window_reference: None,
1✔
1427
                output_type: None,
1✔
1428
            },
1✔
1429
            sources: SingleRasterSource { raster: mrs },
1✔
1430
        }
1✔
1431
        .boxed();
1✔
1432

1✔
1433
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1434
            (0., 0.).into(),
1✔
1435
            [3, 2].into(),
1✔
1436
        ));
1✔
1437
        let query_rect = RasterQueryRectangle {
1✔
1438
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1439
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1440
            spatial_resolution: SpatialResolution::one(),
1✔
1441
            attributes: BandSelection::first(),
1✔
1442
        };
1✔
1443
        let query_ctx = MockQueryContext::test_default();
1✔
1444

1✔
1445
        let qp = agg
1✔
1446
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1447
            .await
1✔
1448
            .unwrap()
1✔
1449
            .query_processor()
1✔
1450
            .unwrap()
1✔
1451
            .get_u8()
1✔
1452
            .unwrap();
1✔
1453

1✔
1454
        let result = qp
1✔
1455
            .query(query_rect, &query_ctx)
1✔
1456
            .await
1✔
1457
            .unwrap()
1✔
1458
            .collect::<Vec<_>>()
1✔
1459
            .await;
7✔
1460

1✔
1461
        assert_eq!(result.len(), 2);
1✔
1462

1✔
1463
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1464
            &RasterTile2D::new_with_tile_info(
1✔
1465
                TimeInterval::new_unchecked(0, 30),
1✔
1466
                TileInformation {
1✔
1467
                    global_tile_position: [-1, 0].into(),
1✔
1468
                    tile_size_in_pixels: [3, 2].into(),
1✔
1469
                    global_geo_transform: TestDefault::test_default(),
1✔
1470
                },
1✔
1471
                0,
1✔
1472
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
1473
                CacheHint::default()
1✔
1474
            )
1✔
1475
        ));
1✔
1476

1✔
1477
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1478
            &RasterTile2D::new_with_tile_info(
1✔
1479
                TimeInterval::new_unchecked(0, 30),
1✔
1480
                TileInformation {
1✔
1481
                    global_tile_position: [-1, 1].into(),
1✔
1482
                    tile_size_in_pixels: [3, 2].into(),
1✔
1483
                    global_geo_transform: TestDefault::test_default(),
1✔
1484
                },
1✔
1485
                0,
1✔
1486
                GridOrEmpty::from(
1✔
1487
                    MaskedGrid2D::new(
1✔
1488
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 42, 5, 6]).unwrap(),
1✔
1489
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1490
                            .unwrap()
1✔
1491
                    )
1✔
1492
                    .unwrap()
1✔
1493
                ),
1✔
1494
                CacheHint::default()
1✔
1495
            )
1✔
1496
        ));
1✔
1497
    }
1✔
1498

1499
    #[tokio::test]
1500
    async fn test_mean_nodata() {
1✔
1501
        let raster_tiles = make_raster_with_no_data();
1✔
1502

1✔
1503
        let mrs = MockRasterSource {
1✔
1504
            params: MockRasterSourceParams {
1✔
1505
                data: raster_tiles,
1✔
1506
                result_descriptor: RasterResultDescriptor {
1✔
1507
                    data_type: RasterDataType::U8,
1✔
1508
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1509
                    time: None,
1✔
1510
                    bbox: None,
1✔
1511
                    resolution: None,
1✔
1512
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1513
                },
1✔
1514
            },
1✔
1515
        }
1✔
1516
        .boxed();
1✔
1517

1✔
1518
        let agg = TemporalRasterAggregation {
1✔
1519
            params: TemporalRasterAggregationParameters {
1✔
1520
                aggregation: Aggregation::Mean {
1✔
1521
                    ignore_no_data: false,
1✔
1522
                },
1✔
1523
                window: TimeStep {
1✔
1524
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1525
                    step: 30,
1✔
1526
                },
1✔
1527
                window_reference: None,
1✔
1528
                output_type: None,
1✔
1529
            },
1✔
1530
            sources: SingleRasterSource { raster: mrs },
1✔
1531
        }
1✔
1532
        .boxed();
1✔
1533

1✔
1534
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1535
            (0., 0.).into(),
1✔
1536
            [3, 2].into(),
1✔
1537
        ));
1✔
1538
        let query_rect = RasterQueryRectangle {
1✔
1539
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1540
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1541
            spatial_resolution: SpatialResolution::one(),
1✔
1542
            attributes: BandSelection::first(),
1✔
1543
        };
1✔
1544
        let query_ctx = MockQueryContext::test_default();
1✔
1545

1✔
1546
        let qp = agg
1✔
1547
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1548
            .await
1✔
1549
            .unwrap()
1✔
1550
            .query_processor()
1✔
1551
            .unwrap()
1✔
1552
            .get_u8()
1✔
1553
            .unwrap();
1✔
1554

1✔
1555
        let result = qp
1✔
1556
            .raster_query(query_rect, &query_ctx)
1✔
1557
            .await
1✔
1558
            .unwrap()
1✔
1559
            .collect::<Vec<_>>()
1✔
1560
            .await;
6✔
1561

1✔
1562
        assert_eq!(result.len(), 2);
1✔
1563

1✔
1564
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1565
            &RasterTile2D::new_with_tile_info(
1✔
1566
                TimeInterval::new_unchecked(0, 30),
1✔
1567
                TileInformation {
1✔
1568
                    global_tile_position: [-1, 0].into(),
1✔
1569
                    tile_size_in_pixels: [3, 2].into(),
1✔
1570
                    global_geo_transform: TestDefault::test_default(),
1✔
1571
                },
1✔
1572
                0,
1✔
1573
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
1574
                CacheHint::default()
1✔
1575
            )
1✔
1576
        ));
1✔
1577

1✔
1578
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1579
            &RasterTile2D::new_with_tile_info(
1✔
1580
                TimeInterval::new_unchecked(0, 30),
1✔
1581
                TileInformation {
1✔
1582
                    global_tile_position: [-1, 1].into(),
1✔
1583
                    tile_size_in_pixels: [3, 2].into(),
1✔
1584
                    global_geo_transform: TestDefault::test_default(),
1✔
1585
                },
1✔
1586
                0,
1✔
1587
                GridOrEmpty::from(
1✔
1588
                    MaskedGrid2D::new(
1✔
1589
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1590
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1591
                            .unwrap()
1✔
1592
                    )
1✔
1593
                    .unwrap()
1✔
1594
                ),
1✔
1595
                CacheHint::default()
1✔
1596
            )
1✔
1597
        ));
1✔
1598
    }
1✔
1599

1600
    #[tokio::test]
1601
    async fn test_mean_ignore_no_data() {
1✔
1602
        let raster_tiles = make_raster_with_no_data();
1✔
1603

1✔
1604
        let mrs = MockRasterSource {
1✔
1605
            params: MockRasterSourceParams {
1✔
1606
                data: raster_tiles,
1✔
1607
                result_descriptor: RasterResultDescriptor {
1✔
1608
                    data_type: RasterDataType::U8,
1✔
1609
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1610
                    time: None,
1✔
1611
                    bbox: None,
1✔
1612
                    resolution: None,
1✔
1613
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1614
                },
1✔
1615
            },
1✔
1616
        }
1✔
1617
        .boxed();
1✔
1618

1✔
1619
        let agg = TemporalRasterAggregation {
1✔
1620
            params: TemporalRasterAggregationParameters {
1✔
1621
                aggregation: Aggregation::Mean {
1✔
1622
                    ignore_no_data: true,
1✔
1623
                },
1✔
1624
                window: TimeStep {
1✔
1625
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1626
                    step: 30,
1✔
1627
                },
1✔
1628
                window_reference: None,
1✔
1629
                output_type: None,
1✔
1630
            },
1✔
1631
            sources: SingleRasterSource { raster: mrs },
1✔
1632
        }
1✔
1633
        .boxed();
1✔
1634

1✔
1635
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1636
            (0., 0.).into(),
1✔
1637
            [3, 2].into(),
1✔
1638
        ));
1✔
1639
        let query_rect = RasterQueryRectangle {
1✔
1640
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1641
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1642
            spatial_resolution: SpatialResolution::one(),
1✔
1643
            attributes: BandSelection::first(),
1✔
1644
        };
1✔
1645
        let query_ctx = MockQueryContext::test_default();
1✔
1646

1✔
1647
        let qp = agg
1✔
1648
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1649
            .await
1✔
1650
            .unwrap()
1✔
1651
            .query_processor()
1✔
1652
            .unwrap()
1✔
1653
            .get_u8()
1✔
1654
            .unwrap();
1✔
1655

1✔
1656
        let result = qp
1✔
1657
            .raster_query(query_rect, &query_ctx)
1✔
1658
            .await
1✔
1659
            .unwrap()
1✔
1660
            .collect::<Vec<_>>()
1✔
1661
            .await;
6✔
1662

1✔
1663
        assert_eq!(result.len(), 2);
1✔
1664

1✔
1665
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1666
            &RasterTile2D::new_with_tile_info(
1✔
1667
                TimeInterval::new_unchecked(0, 30),
1✔
1668
                TileInformation {
1✔
1669
                    global_tile_position: [-1, 0].into(),
1✔
1670
                    tile_size_in_pixels: [3, 2].into(),
1✔
1671
                    global_geo_transform: TestDefault::test_default(),
1✔
1672
                },
1✔
1673
                0,
1✔
1674
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![10, 8, 12, 16, 14, 15]).unwrap()),
1✔
1675
                CacheHint::default()
1✔
1676
            )
1✔
1677
        ));
1✔
1678

1✔
1679
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1680
            &RasterTile2D::new_with_tile_info(
1✔
1681
                TimeInterval::new_unchecked(0, 30),
1✔
1682
                TileInformation {
1✔
1683
                    global_tile_position: [-1, 1].into(),
1✔
1684
                    tile_size_in_pixels: [3, 2].into(),
1✔
1685
                    global_geo_transform: TestDefault::test_default(),
1✔
1686
                },
1✔
1687
                0,
1✔
1688
                GridOrEmpty::from(
1✔
1689
                    MaskedGrid2D::new(
1✔
1690
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1691
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1692
                            .unwrap()
1✔
1693
                    )
1✔
1694
                    .unwrap()
1✔
1695
                ),
1✔
1696
                CacheHint::default()
1✔
1697
            )
1✔
1698
        ));
1✔
1699
    }
1✔
1700

1701
    #[tokio::test]
1702
    #[allow(clippy::too_many_lines)]
1703
    async fn test_sum_without_nodata() {
1✔
1704
        let operator = TemporalRasterAggregation {
1✔
1705
            params: TemporalRasterAggregationParameters {
1✔
1706
                aggregation: Aggregation::Sum {
1✔
1707
                    ignore_no_data: false,
1✔
1708
                },
1✔
1709
                window: TimeStep {
1✔
1710
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1711
                    step: 20,
1✔
1712
                },
1✔
1713
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
1714
                output_type: None,
1✔
1715
            },
1✔
1716
            sources: SingleRasterSource {
1✔
1717
                raster: MockRasterSource {
1✔
1718
                    params: MockRasterSourceParams {
1✔
1719
                        data: make_raster(),
1✔
1720
                        result_descriptor: RasterResultDescriptor {
1✔
1721
                            data_type: RasterDataType::U8,
1✔
1722
                            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1723
                            time: None,
1✔
1724
                            bbox: None,
1✔
1725
                            resolution: None,
1✔
1726
                            bands: RasterBandDescriptors::new_single_band(),
1✔
1727
                        },
1✔
1728
                    },
1✔
1729
                }
1✔
1730
                .boxed(),
1✔
1731
            },
1✔
1732
        }
1✔
1733
        .boxed();
1✔
1734

1✔
1735
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1736
            (0., 0.).into(),
1✔
1737
            [3, 2].into(),
1✔
1738
        ));
1✔
1739
        let query_rect = RasterQueryRectangle {
1✔
1740
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1741
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1742
            spatial_resolution: SpatialResolution::one(),
1✔
1743
            attributes: BandSelection::first(),
1✔
1744
        };
1✔
1745
        let query_ctx = MockQueryContext::test_default();
1✔
1746

1✔
1747
        let query_processor = operator
1✔
1748
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1749
            .await
1✔
1750
            .unwrap()
1✔
1751
            .query_processor()
1✔
1752
            .unwrap()
1✔
1753
            .get_u8()
1✔
1754
            .unwrap();
1✔
1755

1✔
1756
        let result = query_processor
1✔
1757
            .raster_query(query_rect, &query_ctx)
1✔
1758
            .await
1✔
1759
            .unwrap()
1✔
1760
            .map(Result::unwrap)
1✔
1761
            .collect::<Vec<_>>()
1✔
1762
            .await;
8✔
1763

1✔
1764
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
1765
            RasterTile2D::new_with_tile_info(
1✔
1766
                TimeInterval::new_unchecked(0, 20),
1✔
1767
                TileInformation {
1✔
1768
                    global_tile_position: [-1, 0].into(),
1✔
1769
                    tile_size_in_pixels: [3, 2].into(),
1✔
1770
                    global_geo_transform: TestDefault::test_default(),
1✔
1771
                },
1✔
1772
                0,
1✔
1773
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1774
                    .unwrap()
1✔
1775
                    .into(),
1✔
1776
                CacheHint::default()
1✔
1777
            ),
1✔
1778
            RasterTile2D::new_with_tile_info(
1✔
1779
                TimeInterval::new_unchecked(0, 20),
1✔
1780
                TileInformation {
1✔
1781
                    global_tile_position: [-1, 1].into(),
1✔
1782
                    tile_size_in_pixels: [3, 2].into(),
1✔
1783
                    global_geo_transform: TestDefault::test_default(),
1✔
1784
                },
1✔
1785
                0,
1✔
1786
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1787
                    .unwrap()
1✔
1788
                    .into(),
1✔
1789
                CacheHint::default()
1✔
1790
            ),
1✔
1791
            RasterTile2D::new_with_tile_info(
1✔
1792
                TimeInterval::new_unchecked(20, 40),
1✔
1793
                TileInformation {
1✔
1794
                    global_tile_position: [-1, 0].into(),
1✔
1795
                    tile_size_in_pixels: [3, 2].into(),
1✔
1796
                    global_geo_transform: TestDefault::test_default(),
1✔
1797
                },
1✔
1798
                0,
1✔
1799
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1800
                    .unwrap()
1✔
1801
                    .into(),
1✔
1802
                CacheHint::default()
1✔
1803
            ),
1✔
1804
            RasterTile2D::new_with_tile_info(
1✔
1805
                TimeInterval::new_unchecked(20, 40),
1✔
1806
                TileInformation {
1✔
1807
                    global_tile_position: [-1, 1].into(),
1✔
1808
                    tile_size_in_pixels: [3, 2].into(),
1✔
1809
                    global_geo_transform: TestDefault::test_default(),
1✔
1810
                },
1✔
1811
                0,
1✔
1812
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1813
                    .unwrap()
1✔
1814
                    .into(),
1✔
1815
                CacheHint::default()
1✔
1816
            )
1✔
1817
        ]));
1✔
1818
    }
1✔
1819

1820
    #[tokio::test]
1821
    async fn test_sum_nodata() {
1✔
1822
        let raster_tiles = make_raster_with_no_data();
1✔
1823

1✔
1824
        let mrs = MockRasterSource {
1✔
1825
            params: MockRasterSourceParams {
1✔
1826
                data: raster_tiles,
1✔
1827
                result_descriptor: RasterResultDescriptor {
1✔
1828
                    data_type: RasterDataType::U8,
1✔
1829
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1830
                    time: None,
1✔
1831
                    bbox: None,
1✔
1832
                    resolution: None,
1✔
1833
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1834
                },
1✔
1835
            },
1✔
1836
        }
1✔
1837
        .boxed();
1✔
1838

1✔
1839
        let agg = TemporalRasterAggregation {
1✔
1840
            params: TemporalRasterAggregationParameters {
1✔
1841
                aggregation: Aggregation::Sum {
1✔
1842
                    ignore_no_data: false,
1✔
1843
                },
1✔
1844
                window: TimeStep {
1✔
1845
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1846
                    step: 30,
1✔
1847
                },
1✔
1848
                window_reference: None,
1✔
1849
                output_type: None,
1✔
1850
            },
1✔
1851
            sources: SingleRasterSource { raster: mrs },
1✔
1852
        }
1✔
1853
        .boxed();
1✔
1854

1✔
1855
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1856
            (0., 0.).into(),
1✔
1857
            [3, 2].into(),
1✔
1858
        ));
1✔
1859
        let query_rect = RasterQueryRectangle {
1✔
1860
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1861
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1862
            spatial_resolution: SpatialResolution::one(),
1✔
1863
            attributes: BandSelection::first(),
1✔
1864
        };
1✔
1865
        let query_ctx = MockQueryContext::test_default();
1✔
1866

1✔
1867
        let qp = agg
1✔
1868
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1869
            .await
1✔
1870
            .unwrap()
1✔
1871
            .query_processor()
1✔
1872
            .unwrap()
1✔
1873
            .get_u8()
1✔
1874
            .unwrap();
1✔
1875

1✔
1876
        let result = qp
1✔
1877
            .raster_query(query_rect, &query_ctx)
1✔
1878
            .await
1✔
1879
            .unwrap()
1✔
1880
            .collect::<Vec<_>>()
1✔
1881
            .await;
6✔
1882

1✔
1883
        assert_eq!(result.len(), 2);
1✔
1884

1✔
1885
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1886
            &RasterTile2D::new_with_tile_info(
1✔
1887
                TimeInterval::new_unchecked(0, 30),
1✔
1888
                TileInformation {
1✔
1889
                    global_tile_position: [-1, 0].into(),
1✔
1890
                    tile_size_in_pixels: [3, 2].into(),
1✔
1891
                    global_geo_transform: TestDefault::test_default(),
1✔
1892
                },
1✔
1893
                0,
1✔
1894
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
1895
                CacheHint::default()
1✔
1896
            )
1✔
1897
        ));
1✔
1898

1✔
1899
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1900
            &RasterTile2D::new_with_tile_info(
1✔
1901
                TimeInterval::new_unchecked(0, 30),
1✔
1902
                TileInformation {
1✔
1903
                    global_tile_position: [-1, 1].into(),
1✔
1904
                    tile_size_in_pixels: [3, 2].into(),
1✔
1905
                    global_geo_transform: TestDefault::test_default(),
1✔
1906
                },
1✔
1907
                0,
1✔
1908
                GridOrEmpty::from(
1✔
1909
                    MaskedGrid2D::new(
1✔
1910
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1911
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1912
                            .unwrap()
1✔
1913
                    )
1✔
1914
                    .unwrap(),
1✔
1915
                ),
1✔
1916
                CacheHint::default()
1✔
1917
            )
1✔
1918
        ));
1✔
1919
    }
1✔
1920

1921
    #[tokio::test]
1922
    async fn test_sum_ignore_no_data() {
1✔
1923
        let raster_tiles = make_raster_with_no_data();
1✔
1924

1✔
1925
        let mrs = MockRasterSource {
1✔
1926
            params: MockRasterSourceParams {
1✔
1927
                data: raster_tiles,
1✔
1928
                result_descriptor: RasterResultDescriptor {
1✔
1929
                    data_type: RasterDataType::U8,
1✔
1930
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1931
                    time: None,
1✔
1932
                    bbox: None,
1✔
1933
                    resolution: None,
1✔
1934
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1935
                },
1✔
1936
            },
1✔
1937
        }
1✔
1938
        .boxed();
1✔
1939

1✔
1940
        let agg = TemporalRasterAggregation {
1✔
1941
            params: TemporalRasterAggregationParameters {
1✔
1942
                aggregation: Aggregation::Sum {
1✔
1943
                    ignore_no_data: true,
1✔
1944
                },
1✔
1945
                window: TimeStep {
1✔
1946
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1947
                    step: 30,
1✔
1948
                },
1✔
1949
                window_reference: None,
1✔
1950
                output_type: None,
1✔
1951
            },
1✔
1952
            sources: SingleRasterSource { raster: mrs },
1✔
1953
        }
1✔
1954
        .boxed();
1✔
1955

1✔
1956
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1957
            (0., 0.).into(),
1✔
1958
            [3, 2].into(),
1✔
1959
        ));
1✔
1960
        let query_rect = RasterQueryRectangle {
1✔
1961
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1962
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1963
            spatial_resolution: SpatialResolution::one(),
1✔
1964
            attributes: BandSelection::first(),
1✔
1965
        };
1✔
1966
        let query_ctx = MockQueryContext::test_default();
1✔
1967

1✔
1968
        let qp = agg
1✔
1969
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1970
            .await
1✔
1971
            .unwrap()
1✔
1972
            .query_processor()
1✔
1973
            .unwrap()
1✔
1974
            .get_u8()
1✔
1975
            .unwrap();
1✔
1976

1✔
1977
        let result = qp
1✔
1978
            .raster_query(query_rect, &query_ctx)
1✔
1979
            .await
1✔
1980
            .unwrap()
1✔
1981
            .collect::<Vec<_>>()
1✔
1982
            .await;
6✔
1983

1✔
1984
        assert_eq!(result.len(), 2);
1✔
1985

1✔
1986
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1987
            &RasterTile2D::new_with_tile_info(
1✔
1988
                TimeInterval::new_unchecked(0, 30),
1✔
1989
                TileInformation {
1✔
1990
                    global_tile_position: [-1, 0].into(),
1✔
1991
                    tile_size_in_pixels: [3, 2].into(),
1✔
1992
                    global_geo_transform: TestDefault::test_default(),
1✔
1993
                },
1✔
1994
                0,
1✔
1995
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![20, 8, 24, 16, 28, 30]).unwrap()),
1✔
1996
                CacheHint::default()
1✔
1997
            )
1✔
1998
        ));
1✔
1999

1✔
2000
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2001
            &RasterTile2D::new_with_tile_info(
1✔
2002
                TimeInterval::new_unchecked(0, 30),
1✔
2003
                TileInformation {
1✔
2004
                    global_tile_position: [-1, 1].into(),
1✔
2005
                    tile_size_in_pixels: [3, 2].into(),
1✔
2006
                    global_geo_transform: TestDefault::test_default(),
1✔
2007
                },
1✔
2008
                0,
1✔
2009
                GridOrEmpty::from(
1✔
2010
                    MaskedGrid2D::new(
1✔
2011
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
2012
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2013
                            .unwrap()
1✔
2014
                    )
1✔
2015
                    .unwrap()
1✔
2016
                ),
1✔
2017
                CacheHint::default()
1✔
2018
            )
1✔
2019
        ));
1✔
2020
    }
1✔
2021

2022
    #[tokio::test]
2023
    #[allow(clippy::too_many_lines)]
2024
    async fn test_sum_with_larger_data_type() {
1✔
2025
        let operator = TemporalRasterAggregation {
1✔
2026
            params: TemporalRasterAggregationParameters {
1✔
2027
                aggregation: Aggregation::Sum {
1✔
2028
                    ignore_no_data: false,
1✔
2029
                },
1✔
2030
                window: TimeStep {
1✔
2031
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2032
                    step: 20,
1✔
2033
                },
1✔
2034
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
2035
                output_type: Some(RasterDataType::U16),
1✔
2036
            },
1✔
2037
            sources: SingleRasterSource {
1✔
2038
                raster: Expression {
1✔
2039
                    params: ExpressionParams {
1✔
2040
                        expression: "20 * A".to_string(),
1✔
2041
                        output_type: RasterDataType::U8,
1✔
2042
                        output_band: None,
1✔
2043
                        map_no_data: true,
1✔
2044
                    },
1✔
2045
                    sources: SingleRasterSource {
1✔
2046
                        raster: MockRasterSource {
1✔
2047
                            params: MockRasterSourceParams {
1✔
2048
                                data: make_raster(),
1✔
2049
                                result_descriptor: RasterResultDescriptor {
1✔
2050
                                    data_type: RasterDataType::U8,
1✔
2051
                                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2052
                                    time: None,
1✔
2053
                                    bbox: None,
1✔
2054
                                    resolution: None,
1✔
2055
                                    bands: RasterBandDescriptors::new_single_band(),
1✔
2056
                                },
1✔
2057
                            },
1✔
2058
                        }
1✔
2059
                        .boxed(),
1✔
2060
                    },
1✔
2061
                }
1✔
2062
                .boxed(),
1✔
2063
            },
1✔
2064
        }
1✔
2065
        .boxed();
1✔
2066

1✔
2067
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2068
            (0., 0.).into(),
1✔
2069
            [3, 2].into(),
1✔
2070
        ));
1✔
2071
        let query_rect = RasterQueryRectangle {
1✔
2072
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2073
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2074
            spatial_resolution: SpatialResolution::one(),
1✔
2075
            attributes: BandSelection::first(),
1✔
2076
        };
1✔
2077
        let query_ctx = MockQueryContext::test_default();
1✔
2078

1✔
2079
        let query_processor = operator
1✔
2080
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2081
            .await
1✔
2082
            .unwrap()
1✔
2083
            .query_processor()
1✔
2084
            .unwrap()
1✔
2085
            .get_u16()
1✔
2086
            .unwrap();
1✔
2087

1✔
2088
        let result = query_processor
1✔
2089
            .raster_query(query_rect, &query_ctx)
1✔
2090
            .await
1✔
2091
            .unwrap()
1✔
2092
            .map(Result::unwrap)
1✔
2093
            .collect::<Vec<_>>()
1✔
2094
            .await;
32✔
2095

1✔
2096
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
2097
            RasterTile2D::new_with_tile_info(
1✔
2098
                TimeInterval::new_unchecked(0, 20),
1✔
2099
                TileInformation {
1✔
2100
                    global_tile_position: [-1, 0].into(),
1✔
2101
                    tile_size_in_pixels: [3, 2].into(),
1✔
2102
                    global_geo_transform: TestDefault::test_default(),
1✔
2103
                },
1✔
2104
                0,
1✔
2105
                Grid2D::new(
1✔
2106
                    [3, 2].into(),
1✔
2107
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2108
                )
1✔
2109
                .unwrap()
1✔
2110
                .into(),
1✔
2111
                CacheHint::default()
1✔
2112
            ),
1✔
2113
            RasterTile2D::new_with_tile_info(
1✔
2114
                TimeInterval::new_unchecked(0, 20),
1✔
2115
                TileInformation {
1✔
2116
                    global_tile_position: [-1, 1].into(),
1✔
2117
                    tile_size_in_pixels: [3, 2].into(),
1✔
2118
                    global_geo_transform: TestDefault::test_default(),
1✔
2119
                },
1✔
2120
                0,
1✔
2121
                Grid2D::new(
1✔
2122
                    [3, 2].into(),
1✔
2123
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2124
                )
1✔
2125
                .unwrap()
1✔
2126
                .into(),
1✔
2127
                CacheHint::default()
1✔
2128
            ),
1✔
2129
            RasterTile2D::new_with_tile_info(
1✔
2130
                TimeInterval::new_unchecked(20, 40),
1✔
2131
                TileInformation {
1✔
2132
                    global_tile_position: [-1, 0].into(),
1✔
2133
                    tile_size_in_pixels: [3, 2].into(),
1✔
2134
                    global_geo_transform: TestDefault::test_default(),
1✔
2135
                },
1✔
2136
                0,
1✔
2137
                Grid2D::new(
1✔
2138
                    [3, 2].into(),
1✔
2139
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2140
                )
1✔
2141
                .unwrap()
1✔
2142
                .into(),
1✔
2143
                CacheHint::default()
1✔
2144
            ),
1✔
2145
            RasterTile2D::new_with_tile_info(
1✔
2146
                TimeInterval::new_unchecked(20, 40),
1✔
2147
                TileInformation {
1✔
2148
                    global_tile_position: [-1, 1].into(),
1✔
2149
                    tile_size_in_pixels: [3, 2].into(),
1✔
2150
                    global_geo_transform: TestDefault::test_default(),
1✔
2151
                },
1✔
2152
                0,
1✔
2153
                Grid2D::new(
1✔
2154
                    [3, 2].into(),
1✔
2155
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2156
                )
1✔
2157
                .unwrap()
1✔
2158
                .into(),
1✔
2159
                CacheHint::default()
1✔
2160
            )
1✔
2161
        ]),);
1✔
2162
    }
1✔
2163

2164
    #[tokio::test]
2165
    #[allow(clippy::too_many_lines)]
2166
    async fn test_count_without_nodata() {
1✔
2167
        let operator = TemporalRasterAggregation {
1✔
2168
            params: TemporalRasterAggregationParameters {
1✔
2169
                aggregation: Aggregation::Count {
1✔
2170
                    ignore_no_data: false,
1✔
2171
                },
1✔
2172
                window: TimeStep {
1✔
2173
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2174
                    step: 20,
1✔
2175
                },
1✔
2176
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
2177
                output_type: None,
1✔
2178
            },
1✔
2179
            sources: SingleRasterSource {
1✔
2180
                raster: MockRasterSource {
1✔
2181
                    params: MockRasterSourceParams {
1✔
2182
                        data: make_raster(),
1✔
2183
                        result_descriptor: RasterResultDescriptor {
1✔
2184
                            data_type: RasterDataType::U8,
1✔
2185
                            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2186
                            time: None,
1✔
2187
                            bbox: None,
1✔
2188
                            resolution: None,
1✔
2189
                            bands: RasterBandDescriptors::new_single_band(),
1✔
2190
                        },
1✔
2191
                    },
1✔
2192
                }
1✔
2193
                .boxed(),
1✔
2194
            },
1✔
2195
        }
1✔
2196
        .boxed();
1✔
2197

1✔
2198
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2199
            (0., 0.).into(),
1✔
2200
            [3, 2].into(),
1✔
2201
        ));
1✔
2202
        let query_rect = RasterQueryRectangle {
1✔
2203
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2204
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2205
            spatial_resolution: SpatialResolution::one(),
1✔
2206
            attributes: BandSelection::first(),
1✔
2207
        };
1✔
2208
        let query_ctx = MockQueryContext::test_default();
1✔
2209

1✔
2210
        let query_processor = operator
1✔
2211
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2212
            .await
1✔
2213
            .unwrap()
1✔
2214
            .query_processor()
1✔
2215
            .unwrap()
1✔
2216
            .get_u8()
1✔
2217
            .unwrap();
1✔
2218

1✔
2219
        let result = query_processor
1✔
2220
            .raster_query(query_rect, &query_ctx)
1✔
2221
            .await
1✔
2222
            .unwrap()
1✔
2223
            .map(Result::unwrap)
1✔
2224
            .collect::<Vec<_>>()
1✔
2225
            .await;
8✔
2226

1✔
2227
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
2228
            RasterTile2D::new_with_tile_info(
1✔
2229
                TimeInterval::new_unchecked(0, 20),
1✔
2230
                TileInformation {
1✔
2231
                    global_tile_position: [-1, 0].into(),
1✔
2232
                    tile_size_in_pixels: [3, 2].into(),
1✔
2233
                    global_geo_transform: TestDefault::test_default(),
1✔
2234
                },
1✔
2235
                0,
1✔
2236
                Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2237
                    .unwrap()
1✔
2238
                    .into(),
1✔
2239
                CacheHint::default()
1✔
2240
            ),
1✔
2241
            RasterTile2D::new_with_tile_info(
1✔
2242
                TimeInterval::new_unchecked(0, 20),
1✔
2243
                TileInformation {
1✔
2244
                    global_tile_position: [-1, 1].into(),
1✔
2245
                    tile_size_in_pixels: [3, 2].into(),
1✔
2246
                    global_geo_transform: TestDefault::test_default(),
1✔
2247
                },
1✔
2248
                0,
1✔
2249
                Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2250
                    .unwrap()
1✔
2251
                    .into(),
1✔
2252
                CacheHint::default()
1✔
2253
            ),
1✔
2254
            RasterTile2D::new_with_tile_info(
1✔
2255
                TimeInterval::new_unchecked(20, 40),
1✔
2256
                TileInformation {
1✔
2257
                    global_tile_position: [-1, 0].into(),
1✔
2258
                    tile_size_in_pixels: [3, 2].into(),
1✔
2259
                    global_geo_transform: TestDefault::test_default(),
1✔
2260
                },
1✔
2261
                0,
1✔
2262
                Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2263
                    .unwrap()
1✔
2264
                    .into(),
1✔
2265
                CacheHint::default()
1✔
2266
            ),
1✔
2267
            RasterTile2D::new_with_tile_info(
1✔
2268
                TimeInterval::new_unchecked(20, 40),
1✔
2269
                TileInformation {
1✔
2270
                    global_tile_position: [-1, 1].into(),
1✔
2271
                    tile_size_in_pixels: [3, 2].into(),
1✔
2272
                    global_geo_transform: TestDefault::test_default(),
1✔
2273
                },
1✔
2274
                0,
1✔
2275
                Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2276
                    .unwrap()
1✔
2277
                    .into(),
1✔
2278
                CacheHint::default()
1✔
2279
            )
1✔
2280
        ]));
1✔
2281
    }
1✔
2282

2283
    #[tokio::test]
2284
    async fn test_count_nodata() {
1✔
2285
        let raster_tiles = make_raster_with_no_data();
1✔
2286

1✔
2287
        let mrs = MockRasterSource {
1✔
2288
            params: MockRasterSourceParams {
1✔
2289
                data: raster_tiles,
1✔
2290
                result_descriptor: RasterResultDescriptor {
1✔
2291
                    data_type: RasterDataType::U8,
1✔
2292
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2293
                    time: None,
1✔
2294
                    bbox: None,
1✔
2295
                    resolution: None,
1✔
2296
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2297
                },
1✔
2298
            },
1✔
2299
        }
1✔
2300
        .boxed();
1✔
2301

1✔
2302
        let agg = TemporalRasterAggregation {
1✔
2303
            params: TemporalRasterAggregationParameters {
1✔
2304
                aggregation: Aggregation::Count {
1✔
2305
                    ignore_no_data: false,
1✔
2306
                },
1✔
2307
                window: TimeStep {
1✔
2308
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2309
                    step: 30,
1✔
2310
                },
1✔
2311
                window_reference: None,
1✔
2312
                output_type: None,
1✔
2313
            },
1✔
2314
            sources: SingleRasterSource { raster: mrs },
1✔
2315
        }
1✔
2316
        .boxed();
1✔
2317

1✔
2318
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2319
            (0., 0.).into(),
1✔
2320
            [3, 2].into(),
1✔
2321
        ));
1✔
2322
        let query_rect = RasterQueryRectangle {
1✔
2323
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2324
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2325
            spatial_resolution: SpatialResolution::one(),
1✔
2326
            attributes: BandSelection::first(),
1✔
2327
        };
1✔
2328
        let query_ctx = MockQueryContext::test_default();
1✔
2329

1✔
2330
        let qp = agg
1✔
2331
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2332
            .await
1✔
2333
            .unwrap()
1✔
2334
            .query_processor()
1✔
2335
            .unwrap()
1✔
2336
            .get_u8()
1✔
2337
            .unwrap();
1✔
2338

1✔
2339
        let result = qp
1✔
2340
            .raster_query(query_rect, &query_ctx)
1✔
2341
            .await
1✔
2342
            .unwrap()
1✔
2343
            .collect::<Vec<_>>()
1✔
2344
            .await;
6✔
2345

1✔
2346
        assert_eq!(result.len(), 2);
1✔
2347

1✔
2348
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2349
            &RasterTile2D::new_with_tile_info(
1✔
2350
                TimeInterval::new_unchecked(0, 30),
1✔
2351
                TileInformation {
1✔
2352
                    global_tile_position: [-1, 0].into(),
1✔
2353
                    tile_size_in_pixels: [3, 2].into(),
1✔
2354
                    global_geo_transform: TestDefault::test_default(),
1✔
2355
                },
1✔
2356
                0,
1✔
2357
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
2358
                CacheHint::default()
1✔
2359
            )
1✔
2360
        ));
1✔
2361

1✔
2362
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2363
            &RasterTile2D::new_with_tile_info(
1✔
2364
                TimeInterval::new_unchecked(0, 30),
1✔
2365
                TileInformation {
1✔
2366
                    global_tile_position: [-1, 1].into(),
1✔
2367
                    tile_size_in_pixels: [3, 2].into(),
1✔
2368
                    global_geo_transform: TestDefault::test_default(),
1✔
2369
                },
1✔
2370
                0,
1✔
2371
                GridOrEmpty::from(
1✔
2372
                    MaskedGrid2D::new(
1✔
2373
                        Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
1✔
2374
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2375
                            .unwrap()
1✔
2376
                    )
1✔
2377
                    .unwrap()
1✔
2378
                ),
1✔
2379
                CacheHint::default()
1✔
2380
            )
1✔
2381
        ));
1✔
2382
    }
1✔
2383

2384
    #[tokio::test]
2385
    async fn test_count_ignore_no_data() {
1✔
2386
        let raster_tiles = make_raster_with_no_data();
1✔
2387

1✔
2388
        let mrs = MockRasterSource {
1✔
2389
            params: MockRasterSourceParams {
1✔
2390
                data: raster_tiles,
1✔
2391
                result_descriptor: RasterResultDescriptor {
1✔
2392
                    data_type: RasterDataType::U8,
1✔
2393
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2394
                    time: None,
1✔
2395
                    bbox: None,
1✔
2396
                    resolution: None,
1✔
2397
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2398
                },
1✔
2399
            },
1✔
2400
        }
1✔
2401
        .boxed();
1✔
2402

1✔
2403
        let agg = TemporalRasterAggregation {
1✔
2404
            params: TemporalRasterAggregationParameters {
1✔
2405
                aggregation: Aggregation::Count {
1✔
2406
                    ignore_no_data: true,
1✔
2407
                },
1✔
2408
                window: TimeStep {
1✔
2409
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2410
                    step: 30,
1✔
2411
                },
1✔
2412
                window_reference: None,
1✔
2413
                output_type: None,
1✔
2414
            },
1✔
2415
            sources: SingleRasterSource { raster: mrs },
1✔
2416
        }
1✔
2417
        .boxed();
1✔
2418

1✔
2419
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2420
            (0., 0.).into(),
1✔
2421
            [3, 2].into(),
1✔
2422
        ));
1✔
2423
        let query_rect = RasterQueryRectangle {
1✔
2424
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2425
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2426
            spatial_resolution: SpatialResolution::one(),
1✔
2427
            attributes: BandSelection::first(),
1✔
2428
        };
1✔
2429
        let query_ctx = MockQueryContext::test_default();
1✔
2430

1✔
2431
        let qp = agg
1✔
2432
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2433
            .await
1✔
2434
            .unwrap()
1✔
2435
            .query_processor()
1✔
2436
            .unwrap()
1✔
2437
            .get_u8()
1✔
2438
            .unwrap();
1✔
2439

1✔
2440
        let result = qp
1✔
2441
            .raster_query(query_rect, &query_ctx)
1✔
2442
            .await
1✔
2443
            .unwrap()
1✔
2444
            .collect::<Vec<_>>()
1✔
2445
            .await;
6✔
2446

1✔
2447
        assert_eq!(result.len(), 2);
1✔
2448

1✔
2449
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2450
            &RasterTile2D::new_with_tile_info(
1✔
2451
                TimeInterval::new_unchecked(0, 30),
1✔
2452
                TileInformation {
1✔
2453
                    global_tile_position: [-1, 0].into(),
1✔
2454
                    tile_size_in_pixels: [3, 2].into(),
1✔
2455
                    global_geo_transform: TestDefault::test_default(),
1✔
2456
                },
1✔
2457
                0,
1✔
2458
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![2, 1, 2, 1, 2, 2]).unwrap()),
1✔
2459
                CacheHint::default()
1✔
2460
            )
1✔
2461
        ));
1✔
2462

1✔
2463
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2464
            &RasterTile2D::new_with_tile_info(
1✔
2465
                TimeInterval::new_unchecked(0, 30),
1✔
2466
                TileInformation {
1✔
2467
                    global_tile_position: [-1, 1].into(),
1✔
2468
                    tile_size_in_pixels: [3, 2].into(),
1✔
2469
                    global_geo_transform: TestDefault::test_default(),
1✔
2470
                },
1✔
2471
                0,
1✔
2472
                GridOrEmpty::from(
1✔
2473
                    MaskedGrid2D::new(
1✔
2474
                        Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
1✔
2475
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2476
                            .unwrap()
1✔
2477
                    )
1✔
2478
                    .unwrap()
1✔
2479
                ),
1✔
2480
                CacheHint::default()
1✔
2481
            )
1✔
2482
        ));
1✔
2483
    }
1✔
2484

2485
    #[tokio::test]
2486
    async fn test_query_not_aligned_with_window_reference() {
1✔
2487
        let raster_tiles = make_raster();
1✔
2488

1✔
2489
        let mrs = MockRasterSource {
1✔
2490
            params: MockRasterSourceParams {
1✔
2491
                data: raster_tiles,
1✔
2492
                result_descriptor: RasterResultDescriptor {
1✔
2493
                    data_type: RasterDataType::U8,
1✔
2494
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2495
                    time: None,
1✔
2496
                    bbox: None,
1✔
2497
                    resolution: None,
1✔
2498
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2499
                },
1✔
2500
            },
1✔
2501
        }
1✔
2502
        .boxed();
1✔
2503

1✔
2504
        let agg = TemporalRasterAggregation {
1✔
2505
            params: TemporalRasterAggregationParameters {
1✔
2506
                aggregation: Aggregation::Last {
1✔
2507
                    ignore_no_data: false,
1✔
2508
                },
1✔
2509
                window: TimeStep {
1✔
2510
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2511
                    step: 30,
1✔
2512
                },
1✔
2513
                window_reference: Some(TimeInstance::EPOCH_START),
1✔
2514
                output_type: None,
1✔
2515
            },
1✔
2516
            sources: SingleRasterSource { raster: mrs },
1✔
2517
        }
1✔
2518
        .boxed();
1✔
2519

1✔
2520
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2521
            (0., 0.).into(),
1✔
2522
            [3, 2].into(),
1✔
2523
        ));
1✔
2524
        let query_rect = RasterQueryRectangle {
1✔
2525
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2526
            time_interval: TimeInterval::new_unchecked(5, 5),
1✔
2527
            spatial_resolution: SpatialResolution::one(),
1✔
2528
            attributes: BandSelection::first(),
1✔
2529
        };
1✔
2530
        let query_ctx = MockQueryContext::test_default();
1✔
2531

1✔
2532
        let qp = agg
1✔
2533
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2534
            .await
1✔
2535
            .unwrap()
1✔
2536
            .query_processor()
1✔
2537
            .unwrap()
1✔
2538
            .get_u8()
1✔
2539
            .unwrap();
1✔
2540

1✔
2541
        let result = qp
1✔
2542
            .query(query_rect, &query_ctx)
1✔
2543
            .await
1✔
2544
            .unwrap()
1✔
2545
            .collect::<Vec<_>>()
1✔
2546
            .await;
8✔
2547

1✔
2548
        assert_eq!(result.len(), 2);
1✔
2549
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2550
            &RasterTile2D::new_with_tile_info(
1✔
2551
                TimeInterval::new_unchecked(0, 30),
1✔
2552
                TileInformation {
1✔
2553
                    global_tile_position: [-1, 0].into(),
1✔
2554
                    tile_size_in_pixels: [3, 2].into(),
1✔
2555
                    global_geo_transform: TestDefault::test_default(),
1✔
2556
                },
1✔
2557
                0,
1✔
2558
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
2559
                CacheHint::default()
1✔
2560
            )
1✔
2561
        ));
1✔
2562

1✔
2563
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2564
            &RasterTile2D::new_with_tile_info(
1✔
2565
                TimeInterval::new_unchecked(0, 30),
1✔
2566
                TileInformation {
1✔
2567
                    global_tile_position: [-1, 1].into(),
1✔
2568
                    tile_size_in_pixels: [3, 2].into(),
1✔
2569
                    global_geo_transform: TestDefault::test_default(),
1✔
2570
                },
1✔
2571
                0,
1✔
2572
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
2573
                CacheHint::default()
1✔
2574
            )
1✔
2575
        ));
1✔
2576
    }
1✔
2577

2578
    fn make_raster() -> Vec<geoengine_datatypes::raster::RasterTile2D<u8>> {
11✔
2579
        let raster_tiles = vec![
11✔
2580
            RasterTile2D::new_with_tile_info(
11✔
2581
                TimeInterval::new_unchecked(0, 10),
11✔
2582
                TileInformation {
11✔
2583
                    global_tile_position: [-1, 0].into(),
11✔
2584
                    tile_size_in_pixels: [3, 2].into(),
11✔
2585
                    global_geo_transform: TestDefault::test_default(),
11✔
2586
                },
11✔
2587
                0,
11✔
2588
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
11✔
2589
                CacheHint::default(),
11✔
2590
            ),
11✔
2591
            RasterTile2D::new_with_tile_info(
11✔
2592
                TimeInterval::new_unchecked(0, 10),
11✔
2593
                TileInformation {
11✔
2594
                    global_tile_position: [-1, 1].into(),
11✔
2595
                    tile_size_in_pixels: [3, 2].into(),
11✔
2596
                    global_geo_transform: TestDefault::test_default(),
11✔
2597
                },
11✔
2598
                0,
11✔
2599
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
11✔
2600
                CacheHint::default(),
11✔
2601
            ),
11✔
2602
            RasterTile2D::new_with_tile_info(
11✔
2603
                TimeInterval::new_unchecked(10, 20),
11✔
2604
                TileInformation {
11✔
2605
                    global_tile_position: [-1, 0].into(),
11✔
2606
                    tile_size_in_pixels: [3, 2].into(),
11✔
2607
                    global_geo_transform: TestDefault::test_default(),
11✔
2608
                },
11✔
2609
                0,
11✔
2610
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
11✔
2611
                CacheHint::default(),
11✔
2612
            ),
11✔
2613
            RasterTile2D::new_with_tile_info(
11✔
2614
                TimeInterval::new_unchecked(10, 20),
11✔
2615
                TileInformation {
11✔
2616
                    global_tile_position: [-1, 1].into(),
11✔
2617
                    tile_size_in_pixels: [3, 2].into(),
11✔
2618
                    global_geo_transform: TestDefault::test_default(),
11✔
2619
                },
11✔
2620
                0,
11✔
2621
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
11✔
2622
                CacheHint::default(),
11✔
2623
            ),
11✔
2624
            RasterTile2D::new_with_tile_info(
11✔
2625
                TimeInterval::new_unchecked(20, 30),
11✔
2626
                TileInformation {
11✔
2627
                    global_tile_position: [-1, 0].into(),
11✔
2628
                    tile_size_in_pixels: [3, 2].into(),
11✔
2629
                    global_geo_transform: TestDefault::test_default(),
11✔
2630
                },
11✔
2631
                0,
11✔
2632
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
11✔
2633
                CacheHint::default(),
11✔
2634
            ),
11✔
2635
            RasterTile2D::new_with_tile_info(
11✔
2636
                TimeInterval::new_unchecked(20, 30),
11✔
2637
                TileInformation {
11✔
2638
                    global_tile_position: [-1, 1].into(),
11✔
2639
                    tile_size_in_pixels: [3, 2].into(),
11✔
2640
                    global_geo_transform: TestDefault::test_default(),
11✔
2641
                },
11✔
2642
                0,
11✔
2643
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
11✔
2644
                CacheHint::default(),
11✔
2645
            ),
11✔
2646
            RasterTile2D::new_with_tile_info(
11✔
2647
                TimeInterval::new_unchecked(30, 40),
11✔
2648
                TileInformation {
11✔
2649
                    global_tile_position: [-1, 0].into(),
11✔
2650
                    tile_size_in_pixels: [3, 2].into(),
11✔
2651
                    global_geo_transform: TestDefault::test_default(),
11✔
2652
                },
11✔
2653
                0,
11✔
2654
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
11✔
2655
                CacheHint::default(),
11✔
2656
            ),
11✔
2657
            RasterTile2D::new_with_tile_info(
11✔
2658
                TimeInterval::new_unchecked(30, 40),
11✔
2659
                TileInformation {
11✔
2660
                    global_tile_position: [-1, 1].into(),
11✔
2661
                    tile_size_in_pixels: [3, 2].into(),
11✔
2662
                    global_geo_transform: TestDefault::test_default(),
11✔
2663
                },
11✔
2664
                0,
11✔
2665
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
11✔
2666
                CacheHint::default(),
11✔
2667
            ),
11✔
2668
        ];
11✔
2669
        raster_tiles
11✔
2670
    }
11✔
2671

2672
    fn make_raster_with_no_data() -> Vec<geoengine_datatypes::raster::RasterTile2D<u8>> {
10✔
2673
        let raster_tiles = vec![
10✔
2674
            RasterTile2D::new_with_tile_info(
10✔
2675
                TimeInterval::new_unchecked(0, 10),
10✔
2676
                TileInformation {
10✔
2677
                    global_tile_position: [-1, 0].into(),
10✔
2678
                    tile_size_in_pixels: [3, 2].into(),
10✔
2679
                    global_geo_transform: TestDefault::test_default(),
10✔
2680
                },
10✔
2681
                0,
10✔
2682
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2683
                CacheHint::default(),
10✔
2684
            ),
10✔
2685
            RasterTile2D::new_with_tile_info(
10✔
2686
                TimeInterval::new_unchecked(0, 10),
10✔
2687
                TileInformation {
10✔
2688
                    global_tile_position: [-1, 1].into(),
10✔
2689
                    tile_size_in_pixels: [3, 2].into(),
10✔
2690
                    global_geo_transform: TestDefault::test_default(),
10✔
2691
                },
10✔
2692
                0,
10✔
2693
                GridOrEmpty::from(
10✔
2694
                    MaskedGrid2D::new(
10✔
2695
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 42, 5, 6]).unwrap(),
10✔
2696
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
10✔
2697
                            .unwrap(),
10✔
2698
                    )
10✔
2699
                    .unwrap(),
10✔
2700
                ),
10✔
2701
                CacheHint::default(),
10✔
2702
            ),
10✔
2703
            RasterTile2D::new_with_tile_info(
10✔
2704
                TimeInterval::new_unchecked(10, 20),
10✔
2705
                TileInformation {
10✔
2706
                    global_tile_position: [-1, 0].into(),
10✔
2707
                    tile_size_in_pixels: [3, 2].into(),
10✔
2708
                    global_geo_transform: TestDefault::test_default(),
10✔
2709
                },
10✔
2710
                0,
10✔
2711
                GridOrEmpty::from(
10✔
2712
                    MaskedGrid2D::new(
10✔
2713
                        Grid2D::new([3, 2].into(), vec![7, 8, 9, 42, 11, 12]).unwrap(),
10✔
2714
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
10✔
2715
                            .unwrap(),
10✔
2716
                    )
10✔
2717
                    .unwrap(),
10✔
2718
                ),
10✔
2719
                CacheHint::default(),
10✔
2720
            ),
10✔
2721
            RasterTile2D::new_with_tile_info(
10✔
2722
                TimeInterval::new_unchecked(10, 20),
10✔
2723
                TileInformation {
10✔
2724
                    global_tile_position: [-1, 1].into(),
10✔
2725
                    tile_size_in_pixels: [3, 2].into(),
10✔
2726
                    global_geo_transform: TestDefault::test_default(),
10✔
2727
                },
10✔
2728
                0,
10✔
2729
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2730
                CacheHint::default(),
10✔
2731
            ),
10✔
2732
            RasterTile2D::new_with_tile_info(
10✔
2733
                TimeInterval::new_unchecked(20, 30),
10✔
2734
                TileInformation {
10✔
2735
                    global_tile_position: [-1, 0].into(),
10✔
2736
                    tile_size_in_pixels: [3, 2].into(),
10✔
2737
                    global_geo_transform: TestDefault::test_default(),
10✔
2738
                },
10✔
2739
                0,
10✔
2740
                GridOrEmpty::from(
10✔
2741
                    MaskedGrid2D::new(
10✔
2742
                        Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
10✔
2743
                        Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
10✔
2744
                            .unwrap(),
10✔
2745
                    )
10✔
2746
                    .unwrap(),
10✔
2747
                ),
10✔
2748
                CacheHint::default(),
10✔
2749
            ),
10✔
2750
            RasterTile2D::new_with_tile_info(
10✔
2751
                TimeInterval::new_unchecked(20, 30),
10✔
2752
                TileInformation {
10✔
2753
                    global_tile_position: [-1, 1].into(),
10✔
2754
                    tile_size_in_pixels: [3, 2].into(),
10✔
2755
                    global_geo_transform: TestDefault::test_default(),
10✔
2756
                },
10✔
2757
                0,
10✔
2758
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2759
                CacheHint::default(),
10✔
2760
            ),
10✔
2761
        ];
10✔
2762
        raster_tiles
10✔
2763
    }
10✔
2764

2765
    #[tokio::test]
2766
    #[allow(clippy::too_many_lines)]
2767
    async fn it_sums_multiple_bands() {
1✔
2768
        let operator = TemporalRasterAggregation {
1✔
2769
            params: TemporalRasterAggregationParameters {
1✔
2770
                aggregation: Aggregation::Sum {
1✔
2771
                    ignore_no_data: false,
1✔
2772
                },
1✔
2773
                window: TimeStep {
1✔
2774
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2775
                    step: 20,
1✔
2776
                },
1✔
2777
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
2778
                output_type: None,
1✔
2779
            },
1✔
2780
            sources: SingleRasterSource {
1✔
2781
                raster: RasterStacker {
1✔
2782
                    params: RasterStackerParams {
1✔
2783
                        rename_bands: RenameBands::Default,
1✔
2784
                    },
1✔
2785
                    sources: MultipleRasterSources {
1✔
2786
                        rasters: vec![
1✔
2787
                            MockRasterSource {
1✔
2788
                                params: MockRasterSourceParams {
1✔
2789
                                    data: make_raster(),
1✔
2790
                                    result_descriptor: RasterResultDescriptor {
1✔
2791
                                        data_type: RasterDataType::U8,
1✔
2792
                                        spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2793
                                        time: None,
1✔
2794
                                        bbox: None,
1✔
2795
                                        resolution: None,
1✔
2796
                                        bands: RasterBandDescriptors::new_single_band(),
1✔
2797
                                    },
1✔
2798
                                },
1✔
2799
                            }
1✔
2800
                            .boxed(),
1✔
2801
                            MockRasterSource {
1✔
2802
                                params: MockRasterSourceParams {
1✔
2803
                                    data: make_raster(),
1✔
2804
                                    result_descriptor: RasterResultDescriptor {
1✔
2805
                                        data_type: RasterDataType::U8,
1✔
2806
                                        spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2807
                                        time: None,
1✔
2808
                                        bbox: None,
1✔
2809
                                        resolution: None,
1✔
2810
                                        bands: RasterBandDescriptors::new_single_band(),
1✔
2811
                                    },
1✔
2812
                                },
1✔
2813
                            }
1✔
2814
                            .boxed(),
1✔
2815
                        ],
1✔
2816
                    },
1✔
2817
                }
1✔
2818
                .boxed(),
1✔
2819
            },
1✔
2820
        }
1✔
2821
        .boxed();
1✔
2822

1✔
2823
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2824
            (0., 0.).into(),
1✔
2825
            [3, 2].into(),
1✔
2826
        ));
1✔
2827
        let query_rect = RasterQueryRectangle {
1✔
2828
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2829
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2830
            spatial_resolution: SpatialResolution::one(),
1✔
2831
            attributes: [0, 1].try_into().unwrap(),
1✔
2832
        };
1✔
2833
        let query_ctx = MockQueryContext::test_default();
1✔
2834

1✔
2835
        let query_processor = operator
1✔
2836
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2837
            .await
1✔
2838
            .unwrap()
1✔
2839
            .query_processor()
1✔
2840
            .unwrap()
1✔
2841
            .get_u8()
1✔
2842
            .unwrap();
1✔
2843

1✔
2844
        let result = query_processor
1✔
2845
            .raster_query(query_rect, &query_ctx)
1✔
2846
            .await
1✔
2847
            .unwrap()
1✔
2848
            .map(Result::unwrap)
1✔
2849
            .collect::<Vec<_>>()
1✔
2850
            .await;
1✔
2851

1✔
2852
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
2853
            RasterTile2D::new_with_tile_info(
1✔
2854
                TimeInterval::new_unchecked(0, 20),
1✔
2855
                TileInformation {
1✔
2856
                    global_tile_position: [-1, 0].into(),
1✔
2857
                    tile_size_in_pixels: [3, 2].into(),
1✔
2858
                    global_geo_transform: TestDefault::test_default(),
1✔
2859
                },
1✔
2860
                0,
1✔
2861
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2862
                    .unwrap()
1✔
2863
                    .into(),
1✔
2864
                CacheHint::default()
1✔
2865
            ),
1✔
2866
            RasterTile2D::new_with_tile_info(
1✔
2867
                TimeInterval::new_unchecked(0, 20),
1✔
2868
                TileInformation {
1✔
2869
                    global_tile_position: [-1, 0].into(),
1✔
2870
                    tile_size_in_pixels: [3, 2].into(),
1✔
2871
                    global_geo_transform: TestDefault::test_default(),
1✔
2872
                },
1✔
2873
                1,
1✔
2874
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2875
                    .unwrap()
1✔
2876
                    .into(),
1✔
2877
                CacheHint::default()
1✔
2878
            ),
1✔
2879
            RasterTile2D::new_with_tile_info(
1✔
2880
                TimeInterval::new_unchecked(0, 20),
1✔
2881
                TileInformation {
1✔
2882
                    global_tile_position: [-1, 1].into(),
1✔
2883
                    tile_size_in_pixels: [3, 2].into(),
1✔
2884
                    global_geo_transform: TestDefault::test_default(),
1✔
2885
                },
1✔
2886
                0,
1✔
2887
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2888
                    .unwrap()
1✔
2889
                    .into(),
1✔
2890
                CacheHint::default()
1✔
2891
            ),
1✔
2892
            RasterTile2D::new_with_tile_info(
1✔
2893
                TimeInterval::new_unchecked(0, 20),
1✔
2894
                TileInformation {
1✔
2895
                    global_tile_position: [-1, 1].into(),
1✔
2896
                    tile_size_in_pixels: [3, 2].into(),
1✔
2897
                    global_geo_transform: TestDefault::test_default(),
1✔
2898
                },
1✔
2899
                1,
1✔
2900
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2901
                    .unwrap()
1✔
2902
                    .into(),
1✔
2903
                CacheHint::default()
1✔
2904
            ),
1✔
2905
            RasterTile2D::new_with_tile_info(
1✔
2906
                TimeInterval::new_unchecked(20, 40),
1✔
2907
                TileInformation {
1✔
2908
                    global_tile_position: [-1, 0].into(),
1✔
2909
                    tile_size_in_pixels: [3, 2].into(),
1✔
2910
                    global_geo_transform: TestDefault::test_default(),
1✔
2911
                },
1✔
2912
                0,
1✔
2913
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2914
                    .unwrap()
1✔
2915
                    .into(),
1✔
2916
                CacheHint::default()
1✔
2917
            ),
1✔
2918
            RasterTile2D::new_with_tile_info(
1✔
2919
                TimeInterval::new_unchecked(20, 40),
1✔
2920
                TileInformation {
1✔
2921
                    global_tile_position: [-1, 0].into(),
1✔
2922
                    tile_size_in_pixels: [3, 2].into(),
1✔
2923
                    global_geo_transform: TestDefault::test_default(),
1✔
2924
                },
1✔
2925
                1,
1✔
2926
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2927
                    .unwrap()
1✔
2928
                    .into(),
1✔
2929
                CacheHint::default()
1✔
2930
            ),
1✔
2931
            RasterTile2D::new_with_tile_info(
1✔
2932
                TimeInterval::new_unchecked(20, 40),
1✔
2933
                TileInformation {
1✔
2934
                    global_tile_position: [-1, 1].into(),
1✔
2935
                    tile_size_in_pixels: [3, 2].into(),
1✔
2936
                    global_geo_transform: TestDefault::test_default(),
1✔
2937
                },
1✔
2938
                0,
1✔
2939
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2940
                    .unwrap()
1✔
2941
                    .into(),
1✔
2942
                CacheHint::default()
1✔
2943
            ),
1✔
2944
            RasterTile2D::new_with_tile_info(
1✔
2945
                TimeInterval::new_unchecked(20, 40),
1✔
2946
                TileInformation {
1✔
2947
                    global_tile_position: [-1, 1].into(),
1✔
2948
                    tile_size_in_pixels: [3, 2].into(),
1✔
2949
                    global_geo_transform: TestDefault::test_default(),
1✔
2950
                },
1✔
2951
                1,
1✔
2952
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2953
                    .unwrap()
1✔
2954
                    .into(),
1✔
2955
                CacheHint::default()
1✔
2956
            )
1✔
2957
        ]));
1✔
2958
    }
1✔
2959

2960
    #[tokio::test]
2961
    async fn it_estimates_a_median() {
1✔
2962
        let raster_tiles = make_raster();
1✔
2963

1✔
2964
        let mrs = MockRasterSource {
1✔
2965
            params: MockRasterSourceParams {
1✔
2966
                data: raster_tiles,
1✔
2967
                result_descriptor: RasterResultDescriptor {
1✔
2968
                    data_type: RasterDataType::U8,
1✔
2969
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2970
                    time: None,
1✔
2971
                    bbox: None,
1✔
2972
                    resolution: None,
1✔
2973
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2974
                },
1✔
2975
            },
1✔
2976
        }
1✔
2977
        .boxed();
1✔
2978

1✔
2979
        let agg = TemporalRasterAggregation {
1✔
2980
            params: TemporalRasterAggregationParameters {
1✔
2981
                aggregation: Aggregation::PercentileEstimate {
1✔
2982
                    percentile: 0.5,
1✔
2983
                    ignore_no_data: false,
1✔
2984
                },
1✔
2985
                window: TimeStep {
1✔
2986
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2987
                    step: 40,
1✔
2988
                },
1✔
2989
                window_reference: None,
1✔
2990
                output_type: None,
1✔
2991
            },
1✔
2992
            sources: SingleRasterSource { raster: mrs },
1✔
2993
        }
1✔
2994
        .boxed();
1✔
2995

1✔
2996
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2997
            (0., 0.).into(),
1✔
2998
            [3, 2].into(),
1✔
2999
        ));
1✔
3000
        let query_rect = RasterQueryRectangle {
1✔
3001
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
3002
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
3003
            spatial_resolution: SpatialResolution::one(),
1✔
3004
            attributes: BandSelection::first(),
1✔
3005
        };
1✔
3006
        let query_ctx = MockQueryContext::test_default();
1✔
3007

1✔
3008
        let qp = agg
1✔
3009
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
3010
            .await
1✔
3011
            .unwrap()
1✔
3012
            .query_processor()
1✔
3013
            .unwrap()
1✔
3014
            .get_u8()
1✔
3015
            .unwrap();
1✔
3016

1✔
3017
        let result = qp
1✔
3018
            .query(query_rect, &query_ctx)
1✔
3019
            .await
1✔
3020
            .unwrap()
1✔
3021
            .map(Result::unwrap)
1✔
3022
            .collect::<Vec<_>>()
1✔
3023
            .await;
8✔
3024

1✔
3025
        assert_eq!(result.len(), 2);
1✔
3026

1✔
3027
        assert_eq!(
1✔
3028
            result[0].grid_array,
1✔
3029
            GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 6, 6, 6, 6, 6]).unwrap())
1✔
3030
        );
1✔
3031
    }
1✔
3032
}
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