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

geo-engine / geoengine / 12469296660

23 Dec 2024 03:15PM UTC coverage: 90.56% (-0.1%) from 90.695%
12469296660

push

github

web-flow
Merge pull request #998 from geo-engine/quota_log_wip

Quota and Data usage Logging

859 of 1214 new or added lines in 66 files covered. (70.76%)

3 existing lines in 2 files now uncovered.

133923 of 147883 relevant lines covered (90.56%)

54439.32 hits per line

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

98.36
/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

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

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

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

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

21✔
105
        debug!(
21✔
106
            "Initializing TemporalRasterAggregation with {:?}.",
×
107
            &self.params
×
108
        );
109

110
        let mut out_result_descriptor = source.result_descriptor().clone();
21✔
111

112
        if let Some(output_type) = self.params.output_type {
21✔
113
            out_result_descriptor.data_type = output_type;
1✔
114
        };
20✔
115

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

21✔
131
        Ok(initialized_operator.boxed())
21✔
132
    }
42✔
133

134
    span_fn!(TemporalRasterAggregation);
135
}
136

137
pub struct InitializedTemporalRasterAggregation {
138
    name: CanonicOperatorName,
139
    path: WorkflowOperatorPath,
140
    aggregation_type: Aggregation,
141
    window: TimeStep,
142
    window_reference: TimeInstance,
143
    source: Box<dyn InitializedRasterOperator>,
144
    result_descriptor: RasterResultDescriptor,
145
    tiling_specification: TilingSpecification,
146
    output_type: Option<RasterDataType>,
147
}
148

149
impl InitializedRasterOperator for InitializedTemporalRasterAggregation {
150
    fn result_descriptor(&self) -> &RasterResultDescriptor {
×
151
        &self.result_descriptor
×
152
    }
×
153

154
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor> {
21✔
155
        let source_processor = self.source.query_processor()?;
21✔
156

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

172
        let res = call_on_generic_raster_processor!(
21✔
173
            source_processor, p =>
21✔
174
            TemporalRasterAggregationProcessor::new(
20✔
175
                self.result_descriptor.clone(),
20✔
176
                self.aggregation_type,
20✔
177
                self.window,
20✔
178
                self.window_reference,
20✔
179
                p,
20✔
180
                self.tiling_specification,
20✔
181
            ).boxed()
20✔
182
            .into()
20✔
183
        );
184

185
        Ok(res)
21✔
186
    }
21✔
187

188
    fn canonic_name(&self) -> CanonicOperatorName {
×
189
        self.name.clone()
×
190
    }
×
191

NEW
192
    fn name(&self) -> &'static str {
×
NEW
193
        TemporalRasterAggregation::TYPE_NAME
×
NEW
194
    }
×
195

NEW
196
    fn path(&self) -> WorkflowOperatorPath {
×
NEW
197
        self.path.clone()
×
NEW
198
    }
×
199
}
200

201
pub struct TemporalRasterAggregationProcessor<Q, P>
202
where
203
    Q: RasterQueryProcessor<RasterType = P>,
204
    P: Pixel,
205
{
206
    result_descriptor: RasterResultDescriptor,
207
    aggregation_type: Aggregation,
208
    window: TimeStep,
209
    window_reference: TimeInstance,
210
    source: Q,
211
    tiling_specification: TilingSpecification,
212
}
213

214
impl<Q, P> TemporalRasterAggregationProcessor<Q, P>
215
where
216
    Q: RasterQueryProcessor<RasterType = P>
217
        + QueryProcessor<
218
            Output = RasterTile2D<P>,
219
            SpatialBounds = SpatialPartition2D,
220
            Selection = BandSelection,
221
            ResultDescription = RasterResultDescriptor,
222
        >,
223
    P: Pixel,
224
{
225
    fn new(
21✔
226
        result_descriptor: RasterResultDescriptor,
21✔
227
        aggregation_type: Aggregation,
21✔
228
        window: TimeStep,
21✔
229
        window_reference: TimeInstance,
21✔
230
        source: Q,
21✔
231
        tiling_specification: TilingSpecification,
21✔
232
    ) -> Self {
21✔
233
        Self {
21✔
234
            result_descriptor,
21✔
235
            aggregation_type,
21✔
236
            window,
21✔
237
            window_reference,
21✔
238
            source,
21✔
239
            tiling_specification,
21✔
240
        }
21✔
241
    }
21✔
242

243
    fn create_subquery<F: TemporalRasterPixelAggregator<P> + 'static, FoldFn>(
18✔
244
        &self,
18✔
245
        fold_fn: FoldFn,
18✔
246
    ) -> super::subquery::TemporalRasterAggregationSubQuery<FoldFn, P, F> {
18✔
247
        super::subquery::TemporalRasterAggregationSubQuery {
18✔
248
            fold_fn,
18✔
249
            step: self.window,
18✔
250
            step_reference: self.window_reference,
18✔
251
            _phantom_pixel_type: PhantomData,
18✔
252
        }
18✔
253
    }
18✔
254

255
    fn create_global_state_subquery<
1✔
256
        F: GlobalStateTemporalRasterPixelAggregator<P> + 'static,
1✔
257
        FoldFn,
1✔
258
    >(
1✔
259
        &self,
1✔
260
        aggregator: F,
1✔
261
        fold_fn: FoldFn,
1✔
262
    ) -> GlobalStateTemporalRasterAggregationSubQuery<FoldFn, P, F> {
1✔
263
        GlobalStateTemporalRasterAggregationSubQuery {
1✔
264
            aggregator: Arc::new(aggregator),
1✔
265
            fold_fn,
1✔
266
            step: self.window,
1✔
267
            step_reference: self.window_reference,
1✔
268
            _phantom_pixel_type: PhantomData,
1✔
269
        }
1✔
270
    }
1✔
271

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

284
    fn create_subquery_last<F>(
2✔
285
        &self,
2✔
286
        fold_fn: F,
2✔
287
    ) -> TemporalRasterAggregationSubQueryNoDataOnly<F, P> {
2✔
288
        TemporalRasterAggregationSubQueryNoDataOnly {
2✔
289
            fold_fn,
2✔
290
            step: self.window,
2✔
291
            step_reference: self.window_reference,
2✔
292
            _phantom_pixel_type: PhantomData,
2✔
293
        }
2✔
294
    }
2✔
295

296
    #[allow(clippy::too_many_lines)]
297
    fn create_subquery_adapter_stream_for_single_band<'a>(
22✔
298
        &'a self,
22✔
299
        query: RasterQueryRectangle,
22✔
300
        ctx: &'a dyn crate::engine::QueryContext,
22✔
301
    ) -> Result<futures::stream::BoxStream<'a, Result<RasterTile2D<P>>>> {
22✔
302
        ensure!(
22✔
303
            query.attributes.count() == 1,
22✔
304
            error::InvalidBandCount {
×
305
                expected: 1u32,
×
306
                found: query.attributes.count()
×
307
            }
×
308
        );
309

310
        Ok(match self.aggregation_type {
22✔
311
            Aggregation::Min {
312
                ignore_no_data: true,
313
            } => self
×
314
                .create_subquery(
×
315
                    super::subquery::subquery_all_tiles_fold_fn::<
×
316
                        P,
×
317
                        MinPixelAggregatorIngoringNoData,
×
318
                    >,
×
319
                )
×
320
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
×
321
                .expect("no tiles must be skipped in Aggregation::Min"),
×
322
            Aggregation::Min {
323
                ignore_no_data: false,
324
            } => self
1✔
325
                .create_subquery(
1✔
326
                    super::subquery::subquery_all_tiles_fold_fn::<P, MinPixelAggregator>,
1✔
327
                )
1✔
328
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
329
                .expect("no tiles must be skipped in Aggregation::Min"),
1✔
330
            Aggregation::Max {
331
                ignore_no_data: true,
332
            } => self
1✔
333
                .create_subquery(
1✔
334
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
335
                        P,
1✔
336
                        MaxPixelAggregatorIngoringNoData,
1✔
337
                    >,
1✔
338
                )
1✔
339
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
340
                .expect("no tiles must be skipped in Aggregation::Max"),
1✔
341
            Aggregation::Max {
342
                ignore_no_data: false,
343
            } => self
3✔
344
                .create_subquery(
3✔
345
                    super::subquery::subquery_all_tiles_fold_fn::<P, MaxPixelAggregator>,
3✔
346
                )
3✔
347
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
3✔
348
                .expect("no tiles must be skipped in Aggregation::Max"),
3✔
349

350
            Aggregation::First {
351
                ignore_no_data: true,
352
            } => self
1✔
353
                .create_subquery(
1✔
354
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
355
                        P,
1✔
356
                        FirstPixelAggregatorIngoringNoData,
1✔
357
                    >,
1✔
358
                )
1✔
359
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
360
                .expect("no tiles must be skipped in Aggregation::First"),
1✔
361
            Aggregation::First {
362
                ignore_no_data: false,
363
            } => self
1✔
364
                .create_subquery_first(first_tile_fold_future::<P>)
1✔
365
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
366
                .expect("no tiles must be skipped in Aggregation::First"),
1✔
367
            Aggregation::Last {
368
                ignore_no_data: true,
369
            } => self
1✔
370
                .create_subquery(
1✔
371
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
372
                        P,
1✔
373
                        LastPixelAggregatorIngoringNoData,
1✔
374
                    >,
1✔
375
                )
1✔
376
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
377
                .expect("no tiles must be skipped in Aggregation::Last"),
1✔
378

379
            Aggregation::Last {
380
                ignore_no_data: false,
381
            } => self
2✔
382
                .create_subquery_last(last_tile_fold_future::<P>)
2✔
383
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
2✔
384
                .expect("no tiles must be skipped in Aggregation::Last"),
2✔
385

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

395
            Aggregation::Mean {
396
                ignore_no_data: false,
397
            } => self
1✔
398
                .create_subquery(
1✔
399
                    super::subquery::subquery_all_tiles_fold_fn::<P, MeanPixelAggregator<false>>,
1✔
400
                )
1✔
401
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
402
                .expect("no tiles must be skipped in Aggregation::Mean"),
1✔
403

404
            Aggregation::Sum {
405
                ignore_no_data: true,
406
            } => self
1✔
407
                .create_subquery(
1✔
408
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
409
                        P,
1✔
410
                        SumPixelAggregatorIngoringNoData,
1✔
411
                    >,
1✔
412
                )
1✔
413
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
414
                .expect("no tiles must be skipped in Aggregation::Sum"),
1✔
415

416
            Aggregation::Sum {
417
                ignore_no_data: false,
418
            } => self
5✔
419
                .create_subquery(
5✔
420
                    super::subquery::subquery_all_tiles_fold_fn::<P, SumPixelAggregator>,
5✔
421
                )
5✔
422
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
5✔
423
                .expect("no tiles must be skipped in Aggregation::Sum"),
5✔
424

425
            Aggregation::Count {
426
                ignore_no_data: true,
427
            } => self
1✔
428
                .create_subquery(
1✔
429
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
430
                        P,
1✔
431
                        CountPixelAggregatorIngoringNoData,
1✔
432
                    >,
1✔
433
                )
1✔
434
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
435
                .expect("no tiles must be skipped in Aggregation::Sum"),
1✔
436

437
            Aggregation::Count {
438
                ignore_no_data: false,
439
            } => self
2✔
440
                .create_subquery(
2✔
441
                    super::subquery::subquery_all_tiles_fold_fn::<P, CountPixelAggregator>,
2✔
442
                )
2✔
443
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
2✔
444
                .expect("no tiles must be skipped in Aggregation::Sum"),
2✔
445
            Aggregation::PercentileEstimate {
446
                ignore_no_data: true,
447
                percentile,
×
448
            } => self
×
449
                .create_global_state_subquery(
×
450
                    PercentileEstimateAggregator::<true>::new(percentile),
×
451
                    super::subquery::subquery_all_tiles_global_state_fold_fn,
×
452
                )
×
453
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
×
454
                .expect("no tiles must be skipped in Aggregation::PercentileEstimate"),
×
455
            Aggregation::PercentileEstimate {
456
                ignore_no_data: false,
457
                percentile,
1✔
458
            } => self
1✔
459
                .create_global_state_subquery(
1✔
460
                    PercentileEstimateAggregator::<false>::new(percentile),
1✔
461
                    super::subquery::subquery_all_tiles_global_state_fold_fn,
1✔
462
                )
1✔
463
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
464
                .expect("no tiles must be skipped in Aggregation::PercentileEstimate"),
1✔
465
        })
466
    }
22✔
467
}
468

469
#[async_trait]
470
impl<Q, P> QueryProcessor for TemporalRasterAggregationProcessor<Q, P>
471
where
472
    Q: QueryProcessor<
473
        Output = RasterTile2D<P>,
474
        SpatialBounds = SpatialPartition2D,
475
        Selection = BandSelection,
476
        ResultDescription = RasterResultDescriptor,
477
    >,
478
    P: Pixel,
479
{
480
    type Output = RasterTile2D<P>;
481
    type SpatialBounds = SpatialPartition2D;
482
    type Selection = BandSelection;
483
    type ResultDescription = RasterResultDescriptor;
484

485
    async fn _query<'a>(
486
        &'a self,
487
        query: RasterQueryRectangle,
488
        ctx: &'a dyn crate::engine::QueryContext,
489
    ) -> Result<futures::stream::BoxStream<'a, Result<Self::Output>>> {
21✔
490
        stack_individual_aligned_raster_bands(&query, ctx, |query, ctx| async {
22✔
491
            self.create_subquery_adapter_stream_for_single_band(query, ctx)
22✔
492
        })
22✔
493
        .await
21✔
494
    }
42✔
495

496
    fn result_descriptor(&self) -> &Self::ResultDescription {
42✔
497
        &self.result_descriptor
42✔
498
    }
42✔
499
}
500

501
#[cfg(test)]
502
mod tests {
503
    use futures::stream::StreamExt;
504
    use geoengine_datatypes::{
505
        primitives::{CacheHint, SpatialResolution, TimeInterval},
506
        raster::{
507
            EmptyGrid, EmptyGrid2D, Grid2D, GridOrEmpty, MaskedGrid2D, RasterDataType, RenameBands,
508
            TileInformation, TilesEqualIgnoringCacheHint,
509
        },
510
        spatial_reference::SpatialReference,
511
        util::test::TestDefault,
512
    };
513

514
    use crate::{
515
        engine::{
516
            MockExecutionContext, MockQueryContext, MultipleRasterSources, RasterBandDescriptors,
517
        },
518
        mock::{MockRasterSource, MockRasterSourceParams},
519
        processing::{
520
            raster_stacker::{RasterStacker, RasterStackerParams},
521
            Expression, ExpressionParams,
522
        },
523
    };
524

525
    use super::*;
526

527
    #[tokio::test]
528
    #[allow(clippy::too_many_lines)]
529
    async fn test_min() {
1✔
530
        let raster_tiles = make_raster();
1✔
531

1✔
532
        let mrs = MockRasterSource {
1✔
533
            params: MockRasterSourceParams {
1✔
534
                data: raster_tiles,
1✔
535
                result_descriptor: RasterResultDescriptor {
1✔
536
                    data_type: RasterDataType::U8,
1✔
537
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
538
                    time: None,
1✔
539
                    bbox: None,
1✔
540
                    resolution: None,
1✔
541
                    bands: RasterBandDescriptors::new_single_band(),
1✔
542
                },
1✔
543
            },
1✔
544
        }
1✔
545
        .boxed();
1✔
546

1✔
547
        let agg = TemporalRasterAggregation {
1✔
548
            params: TemporalRasterAggregationParameters {
1✔
549
                aggregation: Aggregation::Min {
1✔
550
                    ignore_no_data: false,
1✔
551
                },
1✔
552
                window: TimeStep {
1✔
553
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
554
                    step: 20,
1✔
555
                },
1✔
556
                window_reference: None,
1✔
557
                output_type: None,
1✔
558
            },
1✔
559
            sources: SingleRasterSource { raster: mrs },
1✔
560
        }
1✔
561
        .boxed();
1✔
562

1✔
563
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
564
            (0., 0.).into(),
1✔
565
            [3, 2].into(),
1✔
566
        ));
1✔
567
        let query_rect = RasterQueryRectangle {
1✔
568
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
569
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
570
            spatial_resolution: SpatialResolution::one(),
1✔
571
            attributes: BandSelection::first(),
1✔
572
        };
1✔
573
        let query_ctx = MockQueryContext::test_default();
1✔
574

1✔
575
        let qp = agg
1✔
576
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
577
            .await
1✔
578
            .unwrap()
1✔
579
            .query_processor()
1✔
580
            .unwrap()
1✔
581
            .get_u8()
1✔
582
            .unwrap();
1✔
583

1✔
584
        let result = qp
1✔
585
            .query(query_rect, &query_ctx)
1✔
586
            .await
1✔
587
            .unwrap()
1✔
588
            .collect::<Vec<_>>()
1✔
589
            .await;
1✔
590

1✔
591
        assert_eq!(result.len(), 4);
1✔
592

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

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

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

1✔
635
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
636
            &RasterTile2D::new_with_tile_info(
1✔
637
                TimeInterval::new_unchecked(20, 40),
1✔
638
                TileInformation {
1✔
639
                    global_tile_position: [-1, 1].into(),
1✔
640
                    tile_size_in_pixels: [3, 2].into(),
1✔
641
                    global_geo_transform: TestDefault::test_default(),
1✔
642
                },
1✔
643
                0,
1✔
644
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
1✔
645
                CacheHint::default()
1✔
646
            )
1✔
647
        ));
1✔
648
    }
1✔
649

650
    #[tokio::test]
651
    #[allow(clippy::too_many_lines)]
652
    async fn test_max() {
1✔
653
        let raster_tiles = make_raster();
1✔
654

1✔
655
        let mrs = MockRasterSource {
1✔
656
            params: MockRasterSourceParams {
1✔
657
                data: raster_tiles,
1✔
658
                result_descriptor: RasterResultDescriptor {
1✔
659
                    data_type: RasterDataType::U8,
1✔
660
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
661
                    time: None,
1✔
662
                    bbox: None,
1✔
663
                    resolution: None,
1✔
664
                    bands: RasterBandDescriptors::new_single_band(),
1✔
665
                },
1✔
666
            },
1✔
667
        }
1✔
668
        .boxed();
1✔
669

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

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

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

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

1✔
714
        assert_eq!(result.len(), 4);
1✔
715

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

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

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

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

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

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

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

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

1✔
821
        let qp = agg
1✔
822
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
823
            .await
1✔
824
            .unwrap()
1✔
825
            .query_processor()
1✔
826
            .unwrap()
1✔
827
            .get_u8()
1✔
828
            .unwrap();
1✔
829

1✔
830
        let result = qp
1✔
831
            .query(query_rect, &query_ctx)
1✔
832
            .await
1✔
833
            .unwrap()
1✔
834
            .collect::<Vec<_>>()
1✔
835
            .await;
1✔
836

1✔
837
        assert_eq!(result.len(), 4);
1✔
838

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

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

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

1✔
881
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
882
            &RasterTile2D::new_with_tile_info(
1✔
883
                TimeInterval::new_unchecked(20, 40),
1✔
884
                TileInformation {
1✔
885
                    global_tile_position: [-1, 1].into(),
1✔
886
                    tile_size_in_pixels: [3, 2].into(),
1✔
887
                    global_geo_transform: TestDefault::test_default(),
1✔
888
                },
1✔
889
                0,
1✔
890
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
891
                CacheHint::default()
1✔
892
            )
1✔
893
        ));
1✔
894
    }
1✔
895

896
    #[tokio::test]
897
    #[allow(clippy::too_many_lines)]
898
    async fn test_max_with_no_data_but_ignoring_it() {
1✔
899
        let raster_tiles = make_raster(); // TODO: switch to make_raster_with_no_data?
1✔
900

1✔
901
        let mrs = MockRasterSource {
1✔
902
            params: MockRasterSourceParams {
1✔
903
                data: raster_tiles,
1✔
904
                result_descriptor: RasterResultDescriptor {
1✔
905
                    data_type: RasterDataType::U8,
1✔
906
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
907
                    time: None,
1✔
908
                    bbox: None,
1✔
909
                    resolution: None,
1✔
910
                    bands: RasterBandDescriptors::new_single_band(),
1✔
911
                },
1✔
912
            },
1✔
913
        }
1✔
914
        .boxed();
1✔
915

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

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

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

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

1✔
960
        assert_eq!(result.len(), 4);
1✔
961

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

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

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

1✔
1004
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1005
            &RasterTile2D::new_with_tile_info(
1✔
1006
                TimeInterval::new_unchecked(20, 40),
1✔
1007
                TileInformation {
1✔
1008
                    global_tile_position: [-1, 1].into(),
1✔
1009
                    tile_size_in_pixels: [3, 2].into(),
1✔
1010
                    global_geo_transform: TestDefault::test_default(),
1✔
1011
                },
1✔
1012
                0,
1✔
1013
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
1014
                CacheHint::default()
1✔
1015
            )
1✔
1016
        ));
1✔
1017
    }
1✔
1018

1019
    #[tokio::test]
1020
    #[allow(clippy::too_many_lines)]
1021
    async fn test_only_no_data() {
1✔
1022
        let mrs = MockRasterSource {
1✔
1023
            params: MockRasterSourceParams {
1✔
1024
                data: vec![RasterTile2D::new_with_tile_info(
1✔
1025
                    TimeInterval::new_unchecked(0, 20),
1✔
1026
                    TileInformation {
1✔
1027
                        global_tile_position: [-1, 0].into(),
1✔
1028
                        tile_size_in_pixels: [3, 2].into(),
1✔
1029
                        global_geo_transform: TestDefault::test_default(),
1✔
1030
                    },
1✔
1031
                    0,
1✔
1032
                    GridOrEmpty::from(EmptyGrid2D::<u8>::new([3, 2].into())),
1✔
1033
                    CacheHint::default(),
1✔
1034
                )],
1✔
1035
                result_descriptor: RasterResultDescriptor {
1✔
1036
                    data_type: RasterDataType::U8,
1✔
1037
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1038
                    time: None,
1✔
1039
                    bbox: None,
1✔
1040
                    resolution: None,
1✔
1041
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1042
                },
1✔
1043
            },
1✔
1044
        }
1✔
1045
        .boxed();
1✔
1046

1✔
1047
        let agg = TemporalRasterAggregation {
1✔
1048
            params: TemporalRasterAggregationParameters {
1✔
1049
                aggregation: Aggregation::Max {
1✔
1050
                    ignore_no_data: false,
1✔
1051
                },
1✔
1052
                window: TimeStep {
1✔
1053
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1054
                    step: 20,
1✔
1055
                },
1✔
1056
                window_reference: None,
1✔
1057
                output_type: None,
1✔
1058
            },
1✔
1059
            sources: SingleRasterSource { raster: mrs },
1✔
1060
        }
1✔
1061
        .boxed();
1✔
1062

1✔
1063
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1064
            (0., 0.).into(),
1✔
1065
            [3, 2].into(),
1✔
1066
        ));
1✔
1067
        let query_rect = RasterQueryRectangle {
1✔
1068
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (2., 0.).into()),
1✔
1069
            time_interval: TimeInterval::new_unchecked(0, 20),
1✔
1070
            spatial_resolution: SpatialResolution::one(),
1✔
1071
            attributes: BandSelection::first(),
1✔
1072
        };
1✔
1073
        let query_ctx = MockQueryContext::test_default();
1✔
1074

1✔
1075
        let qp = agg
1✔
1076
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1077
            .await
1✔
1078
            .unwrap()
1✔
1079
            .query_processor()
1✔
1080
            .unwrap()
1✔
1081
            .get_u8()
1✔
1082
            .unwrap();
1✔
1083

1✔
1084
        let result = qp
1✔
1085
            .query(query_rect, &query_ctx)
1✔
1086
            .await
1✔
1087
            .unwrap()
1✔
1088
            .collect::<Vec<_>>()
1✔
1089
            .await;
1✔
1090

1✔
1091
        assert_eq!(result.len(), 1);
1✔
1092

1✔
1093
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1094
            &RasterTile2D::new_with_tile_info(
1✔
1095
                TimeInterval::new_unchecked(0, 20),
1✔
1096
                TileInformation {
1✔
1097
                    global_tile_position: [-1, 0].into(),
1✔
1098
                    tile_size_in_pixels: [3, 2].into(),
1✔
1099
                    global_geo_transform: TestDefault::test_default(),
1✔
1100
                },
1✔
1101
                0,
1✔
1102
                GridOrEmpty::Empty(EmptyGrid::new([3, 2].into())),
1✔
1103
                CacheHint::default()
1✔
1104
            )
1✔
1105
        ));
1✔
1106
    }
1✔
1107

1108
    #[tokio::test]
1109
    async fn test_first_with_no_data() {
1✔
1110
        let raster_tiles = make_raster_with_no_data();
1✔
1111

1✔
1112
        let mrs = MockRasterSource {
1✔
1113
            params: MockRasterSourceParams {
1✔
1114
                data: raster_tiles,
1✔
1115
                result_descriptor: RasterResultDescriptor {
1✔
1116
                    data_type: RasterDataType::U8,
1✔
1117
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1118
                    time: None,
1✔
1119
                    bbox: None,
1✔
1120
                    resolution: None,
1✔
1121
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1122
                },
1✔
1123
            },
1✔
1124
        }
1✔
1125
        .boxed();
1✔
1126

1✔
1127
        let agg = TemporalRasterAggregation {
1✔
1128
            params: TemporalRasterAggregationParameters {
1✔
1129
                aggregation: Aggregation::First {
1✔
1130
                    ignore_no_data: true,
1✔
1131
                },
1✔
1132
                window: TimeStep {
1✔
1133
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1134
                    step: 30,
1✔
1135
                },
1✔
1136
                window_reference: None,
1✔
1137
                output_type: None,
1✔
1138
            },
1✔
1139
            sources: SingleRasterSource { raster: mrs },
1✔
1140
        }
1✔
1141
        .boxed();
1✔
1142

1✔
1143
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1144
            (0., 0.).into(),
1✔
1145
            [3, 2].into(),
1✔
1146
        ));
1✔
1147
        let query_rect = RasterQueryRectangle {
1✔
1148
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1149
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1150
            spatial_resolution: SpatialResolution::one(),
1✔
1151
            attributes: BandSelection::first(),
1✔
1152
        };
1✔
1153
        let query_ctx = MockQueryContext::test_default();
1✔
1154

1✔
1155
        let qp = agg
1✔
1156
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1157
            .await
1✔
1158
            .unwrap()
1✔
1159
            .query_processor()
1✔
1160
            .unwrap()
1✔
1161
            .get_u8()
1✔
1162
            .unwrap();
1✔
1163

1✔
1164
        let result = qp
1✔
1165
            .query(query_rect, &query_ctx)
1✔
1166
            .await
1✔
1167
            .unwrap()
1✔
1168
            .collect::<Vec<_>>()
1✔
1169
            .await;
1✔
1170

1✔
1171
        assert_eq!(result.len(), 2);
1✔
1172

1✔
1173
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1174
            &RasterTile2D::new_with_tile_info(
1✔
1175
                TimeInterval::new_unchecked(0, 30),
1✔
1176
                TileInformation {
1✔
1177
                    global_tile_position: [-1, 0].into(),
1✔
1178
                    tile_size_in_pixels: [3, 2].into(),
1✔
1179
                    global_geo_transform: TestDefault::test_default(),
1✔
1180
                },
1✔
1181
                0,
1✔
1182
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 16, 11, 12]).unwrap()),
1✔
1183
                CacheHint::default()
1✔
1184
            )
1✔
1185
        ));
1✔
1186

1✔
1187
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1188
            &RasterTile2D::new_with_tile_info(
1✔
1189
                TimeInterval::new_unchecked(0, 30),
1✔
1190
                TileInformation {
1✔
1191
                    global_tile_position: [-1, 1].into(),
1✔
1192
                    tile_size_in_pixels: [3, 2].into(),
1✔
1193
                    global_geo_transform: TestDefault::test_default(),
1✔
1194
                },
1✔
1195
                0,
1✔
1196
                GridOrEmpty::from(
1✔
1197
                    MaskedGrid2D::new(
1✔
1198
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1199
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1200
                            .unwrap()
1✔
1201
                    )
1✔
1202
                    .unwrap()
1✔
1203
                ),
1✔
1204
                CacheHint::default()
1✔
1205
            )
1✔
1206
        ));
1✔
1207
    }
1✔
1208

1209
    #[tokio::test]
1210
    async fn test_last_with_no_data() {
1✔
1211
        let raster_tiles = make_raster_with_no_data();
1✔
1212

1✔
1213
        let mrs = MockRasterSource {
1✔
1214
            params: MockRasterSourceParams {
1✔
1215
                data: raster_tiles,
1✔
1216
                result_descriptor: RasterResultDescriptor {
1✔
1217
                    data_type: RasterDataType::U8,
1✔
1218
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1219
                    time: None,
1✔
1220
                    bbox: None,
1✔
1221
                    resolution: None,
1✔
1222
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1223
                },
1✔
1224
            },
1✔
1225
        }
1✔
1226
        .boxed();
1✔
1227

1✔
1228
        let agg = TemporalRasterAggregation {
1✔
1229
            params: TemporalRasterAggregationParameters {
1✔
1230
                aggregation: Aggregation::Last {
1✔
1231
                    ignore_no_data: true,
1✔
1232
                },
1✔
1233
                window: TimeStep {
1✔
1234
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1235
                    step: 30,
1✔
1236
                },
1✔
1237
                window_reference: None,
1✔
1238
                output_type: None,
1✔
1239
            },
1✔
1240
            sources: SingleRasterSource { raster: mrs },
1✔
1241
        }
1✔
1242
        .boxed();
1✔
1243

1✔
1244
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1245
            (0., 0.).into(),
1✔
1246
            [3, 2].into(),
1✔
1247
        ));
1✔
1248
        let query_rect = RasterQueryRectangle {
1✔
1249
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1250
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1251
            spatial_resolution: SpatialResolution::one(),
1✔
1252
            attributes: BandSelection::first(),
1✔
1253
        };
1✔
1254
        let query_ctx = MockQueryContext::test_default();
1✔
1255

1✔
1256
        let qp = agg
1✔
1257
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1258
            .await
1✔
1259
            .unwrap()
1✔
1260
            .query_processor()
1✔
1261
            .unwrap()
1✔
1262
            .get_u8()
1✔
1263
            .unwrap();
1✔
1264

1✔
1265
        let result = qp
1✔
1266
            .query(query_rect, &query_ctx)
1✔
1267
            .await
1✔
1268
            .unwrap()
1✔
1269
            .collect::<Vec<_>>()
1✔
1270
            .await;
1✔
1271

1✔
1272
        assert_eq!(result.len(), 2);
1✔
1273

1✔
1274
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1275
            &RasterTile2D::new_with_tile_info(
1✔
1276
                TimeInterval::new_unchecked(0, 30),
1✔
1277
                TileInformation {
1✔
1278
                    global_tile_position: [-1, 0].into(),
1✔
1279
                    tile_size_in_pixels: [3, 2].into(),
1✔
1280
                    global_geo_transform: TestDefault::test_default(),
1✔
1281
                },
1✔
1282
                0,
1✔
1283
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![13, 8, 15, 16, 17, 18]).unwrap()),
1✔
1284
                CacheHint::default()
1✔
1285
            )
1✔
1286
        ));
1✔
1287

1✔
1288
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1289
            &RasterTile2D::new_with_tile_info(
1✔
1290
                TimeInterval::new_unchecked(0, 30),
1✔
1291
                TileInformation {
1✔
1292
                    global_tile_position: [-1, 1].into(),
1✔
1293
                    tile_size_in_pixels: [3, 2].into(),
1✔
1294
                    global_geo_transform: TestDefault::test_default(),
1✔
1295
                },
1✔
1296
                0,
1✔
1297
                GridOrEmpty::from(
1✔
1298
                    MaskedGrid2D::new(
1✔
1299
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1300
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1301
                            .unwrap()
1✔
1302
                    )
1✔
1303
                    .unwrap()
1✔
1304
                ),
1✔
1305
                CacheHint::default()
1✔
1306
            )
1✔
1307
        ));
1✔
1308
    }
1✔
1309

1310
    #[tokio::test]
1311
    async fn test_last() {
1✔
1312
        let raster_tiles = make_raster_with_no_data();
1✔
1313

1✔
1314
        let mrs = MockRasterSource {
1✔
1315
            params: MockRasterSourceParams {
1✔
1316
                data: raster_tiles,
1✔
1317
                result_descriptor: RasterResultDescriptor {
1✔
1318
                    data_type: RasterDataType::U8,
1✔
1319
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1320
                    time: None,
1✔
1321
                    bbox: None,
1✔
1322
                    resolution: None,
1✔
1323
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1324
                },
1✔
1325
            },
1✔
1326
        }
1✔
1327
        .boxed();
1✔
1328

1✔
1329
        let agg = TemporalRasterAggregation {
1✔
1330
            params: TemporalRasterAggregationParameters {
1✔
1331
                aggregation: Aggregation::Last {
1✔
1332
                    ignore_no_data: false,
1✔
1333
                },
1✔
1334
                window: TimeStep {
1✔
1335
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1336
                    step: 30,
1✔
1337
                },
1✔
1338
                window_reference: None,
1✔
1339
                output_type: None,
1✔
1340
            },
1✔
1341
            sources: SingleRasterSource { raster: mrs },
1✔
1342
        }
1✔
1343
        .boxed();
1✔
1344

1✔
1345
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1346
            (0., 0.).into(),
1✔
1347
            [3, 2].into(),
1✔
1348
        ));
1✔
1349
        let query_rect = RasterQueryRectangle {
1✔
1350
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1351
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1352
            spatial_resolution: SpatialResolution::one(),
1✔
1353
            attributes: BandSelection::first(),
1✔
1354
        };
1✔
1355
        let query_ctx = MockQueryContext::test_default();
1✔
1356

1✔
1357
        let qp = agg
1✔
1358
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1359
            .await
1✔
1360
            .unwrap()
1✔
1361
            .query_processor()
1✔
1362
            .unwrap()
1✔
1363
            .get_u8()
1✔
1364
            .unwrap();
1✔
1365

1✔
1366
        let result = qp
1✔
1367
            .query(query_rect, &query_ctx)
1✔
1368
            .await
1✔
1369
            .unwrap()
1✔
1370
            .collect::<Vec<_>>()
1✔
1371
            .await;
1✔
1372

1✔
1373
        assert_eq!(result.len(), 2);
1✔
1374

1✔
1375
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1376
            &RasterTile2D::new_with_tile_info(
1✔
1377
                TimeInterval::new_unchecked(0, 30),
1✔
1378
                TileInformation {
1✔
1379
                    global_tile_position: [-1, 0].into(),
1✔
1380
                    tile_size_in_pixels: [3, 2].into(),
1✔
1381
                    global_geo_transform: TestDefault::test_default(),
1✔
1382
                },
1✔
1383
                0,
1✔
1384
                GridOrEmpty::from(
1✔
1385
                    MaskedGrid2D::new(
1✔
1386
                        Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
1✔
1387
                        Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
1✔
1388
                            .unwrap()
1✔
1389
                    )
1✔
1390
                    .unwrap()
1✔
1391
                ),
1✔
1392
                CacheHint::default()
1✔
1393
            )
1✔
1394
        ));
1✔
1395

1✔
1396
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1397
            &RasterTile2D::new_with_tile_info(
1✔
1398
                TimeInterval::new_unchecked(0, 30),
1✔
1399
                TileInformation {
1✔
1400
                    global_tile_position: [-1, 1].into(),
1✔
1401
                    tile_size_in_pixels: [3, 2].into(),
1✔
1402
                    global_geo_transform: TestDefault::test_default(),
1✔
1403
                },
1✔
1404
                0,
1✔
1405
                GridOrEmpty::Empty(EmptyGrid2D::new([3, 2].into())),
1✔
1406
                CacheHint::default()
1✔
1407
            )
1✔
1408
        ));
1✔
1409
    }
1✔
1410

1411
    #[tokio::test]
1412
    async fn test_first() {
1✔
1413
        let raster_tiles = make_raster_with_no_data();
1✔
1414

1✔
1415
        let mrs = MockRasterSource {
1✔
1416
            params: MockRasterSourceParams {
1✔
1417
                data: raster_tiles,
1✔
1418
                result_descriptor: RasterResultDescriptor {
1✔
1419
                    data_type: RasterDataType::U8,
1✔
1420
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1421
                    time: None,
1✔
1422
                    bbox: None,
1✔
1423
                    resolution: None,
1✔
1424
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1425
                },
1✔
1426
            },
1✔
1427
        }
1✔
1428
        .boxed();
1✔
1429

1✔
1430
        let agg = TemporalRasterAggregation {
1✔
1431
            params: TemporalRasterAggregationParameters {
1✔
1432
                aggregation: Aggregation::First {
1✔
1433
                    ignore_no_data: false,
1✔
1434
                },
1✔
1435
                window: TimeStep {
1✔
1436
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1437
                    step: 30,
1✔
1438
                },
1✔
1439
                window_reference: None,
1✔
1440
                output_type: None,
1✔
1441
            },
1✔
1442
            sources: SingleRasterSource { raster: mrs },
1✔
1443
        }
1✔
1444
        .boxed();
1✔
1445

1✔
1446
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1447
            (0., 0.).into(),
1✔
1448
            [3, 2].into(),
1✔
1449
        ));
1✔
1450
        let query_rect = RasterQueryRectangle {
1✔
1451
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1452
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1453
            spatial_resolution: SpatialResolution::one(),
1✔
1454
            attributes: BandSelection::first(),
1✔
1455
        };
1✔
1456
        let query_ctx = MockQueryContext::test_default();
1✔
1457

1✔
1458
        let qp = agg
1✔
1459
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1460
            .await
1✔
1461
            .unwrap()
1✔
1462
            .query_processor()
1✔
1463
            .unwrap()
1✔
1464
            .get_u8()
1✔
1465
            .unwrap();
1✔
1466

1✔
1467
        let result = qp
1✔
1468
            .query(query_rect, &query_ctx)
1✔
1469
            .await
1✔
1470
            .unwrap()
1✔
1471
            .collect::<Vec<_>>()
1✔
1472
            .await;
1✔
1473

1✔
1474
        assert_eq!(result.len(), 2);
1✔
1475

1✔
1476
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1477
            &RasterTile2D::new_with_tile_info(
1✔
1478
                TimeInterval::new_unchecked(0, 30),
1✔
1479
                TileInformation {
1✔
1480
                    global_tile_position: [-1, 0].into(),
1✔
1481
                    tile_size_in_pixels: [3, 2].into(),
1✔
1482
                    global_geo_transform: TestDefault::test_default(),
1✔
1483
                },
1✔
1484
                0,
1✔
1485
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
1486
                CacheHint::default()
1✔
1487
            )
1✔
1488
        ));
1✔
1489

1✔
1490
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1491
            &RasterTile2D::new_with_tile_info(
1✔
1492
                TimeInterval::new_unchecked(0, 30),
1✔
1493
                TileInformation {
1✔
1494
                    global_tile_position: [-1, 1].into(),
1✔
1495
                    tile_size_in_pixels: [3, 2].into(),
1✔
1496
                    global_geo_transform: TestDefault::test_default(),
1✔
1497
                },
1✔
1498
                0,
1✔
1499
                GridOrEmpty::from(
1✔
1500
                    MaskedGrid2D::new(
1✔
1501
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 42, 5, 6]).unwrap(),
1✔
1502
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1503
                            .unwrap()
1✔
1504
                    )
1✔
1505
                    .unwrap()
1✔
1506
                ),
1✔
1507
                CacheHint::default()
1✔
1508
            )
1✔
1509
        ));
1✔
1510
    }
1✔
1511

1512
    #[tokio::test]
1513
    async fn test_mean_nodata() {
1✔
1514
        let raster_tiles = make_raster_with_no_data();
1✔
1515

1✔
1516
        let mrs = MockRasterSource {
1✔
1517
            params: MockRasterSourceParams {
1✔
1518
                data: raster_tiles,
1✔
1519
                result_descriptor: RasterResultDescriptor {
1✔
1520
                    data_type: RasterDataType::U8,
1✔
1521
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1522
                    time: None,
1✔
1523
                    bbox: None,
1✔
1524
                    resolution: None,
1✔
1525
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1526
                },
1✔
1527
            },
1✔
1528
        }
1✔
1529
        .boxed();
1✔
1530

1✔
1531
        let agg = TemporalRasterAggregation {
1✔
1532
            params: TemporalRasterAggregationParameters {
1✔
1533
                aggregation: Aggregation::Mean {
1✔
1534
                    ignore_no_data: false,
1✔
1535
                },
1✔
1536
                window: TimeStep {
1✔
1537
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1538
                    step: 30,
1✔
1539
                },
1✔
1540
                window_reference: None,
1✔
1541
                output_type: None,
1✔
1542
            },
1✔
1543
            sources: SingleRasterSource { raster: mrs },
1✔
1544
        }
1✔
1545
        .boxed();
1✔
1546

1✔
1547
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1548
            (0., 0.).into(),
1✔
1549
            [3, 2].into(),
1✔
1550
        ));
1✔
1551
        let query_rect = RasterQueryRectangle {
1✔
1552
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1553
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1554
            spatial_resolution: SpatialResolution::one(),
1✔
1555
            attributes: BandSelection::first(),
1✔
1556
        };
1✔
1557
        let query_ctx = MockQueryContext::test_default();
1✔
1558

1✔
1559
        let qp = agg
1✔
1560
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1561
            .await
1✔
1562
            .unwrap()
1✔
1563
            .query_processor()
1✔
1564
            .unwrap()
1✔
1565
            .get_u8()
1✔
1566
            .unwrap();
1✔
1567

1✔
1568
        let result = qp
1✔
1569
            .raster_query(query_rect, &query_ctx)
1✔
1570
            .await
1✔
1571
            .unwrap()
1✔
1572
            .collect::<Vec<_>>()
1✔
1573
            .await;
1✔
1574

1✔
1575
        assert_eq!(result.len(), 2);
1✔
1576

1✔
1577
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1578
            &RasterTile2D::new_with_tile_info(
1✔
1579
                TimeInterval::new_unchecked(0, 30),
1✔
1580
                TileInformation {
1✔
1581
                    global_tile_position: [-1, 0].into(),
1✔
1582
                    tile_size_in_pixels: [3, 2].into(),
1✔
1583
                    global_geo_transform: TestDefault::test_default(),
1✔
1584
                },
1✔
1585
                0,
1✔
1586
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
1587
                CacheHint::default()
1✔
1588
            )
1✔
1589
        ));
1✔
1590

1✔
1591
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1592
            &RasterTile2D::new_with_tile_info(
1✔
1593
                TimeInterval::new_unchecked(0, 30),
1✔
1594
                TileInformation {
1✔
1595
                    global_tile_position: [-1, 1].into(),
1✔
1596
                    tile_size_in_pixels: [3, 2].into(),
1✔
1597
                    global_geo_transform: TestDefault::test_default(),
1✔
1598
                },
1✔
1599
                0,
1✔
1600
                GridOrEmpty::from(
1✔
1601
                    MaskedGrid2D::new(
1✔
1602
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1603
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1604
                            .unwrap()
1✔
1605
                    )
1✔
1606
                    .unwrap()
1✔
1607
                ),
1✔
1608
                CacheHint::default()
1✔
1609
            )
1✔
1610
        ));
1✔
1611
    }
1✔
1612

1613
    #[tokio::test]
1614
    async fn test_mean_ignore_no_data() {
1✔
1615
        let raster_tiles = make_raster_with_no_data();
1✔
1616

1✔
1617
        let mrs = MockRasterSource {
1✔
1618
            params: MockRasterSourceParams {
1✔
1619
                data: raster_tiles,
1✔
1620
                result_descriptor: RasterResultDescriptor {
1✔
1621
                    data_type: RasterDataType::U8,
1✔
1622
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1623
                    time: None,
1✔
1624
                    bbox: None,
1✔
1625
                    resolution: None,
1✔
1626
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1627
                },
1✔
1628
            },
1✔
1629
        }
1✔
1630
        .boxed();
1✔
1631

1✔
1632
        let agg = TemporalRasterAggregation {
1✔
1633
            params: TemporalRasterAggregationParameters {
1✔
1634
                aggregation: Aggregation::Mean {
1✔
1635
                    ignore_no_data: true,
1✔
1636
                },
1✔
1637
                window: TimeStep {
1✔
1638
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1639
                    step: 30,
1✔
1640
                },
1✔
1641
                window_reference: None,
1✔
1642
                output_type: None,
1✔
1643
            },
1✔
1644
            sources: SingleRasterSource { raster: mrs },
1✔
1645
        }
1✔
1646
        .boxed();
1✔
1647

1✔
1648
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1649
            (0., 0.).into(),
1✔
1650
            [3, 2].into(),
1✔
1651
        ));
1✔
1652
        let query_rect = RasterQueryRectangle {
1✔
1653
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1654
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1655
            spatial_resolution: SpatialResolution::one(),
1✔
1656
            attributes: BandSelection::first(),
1✔
1657
        };
1✔
1658
        let query_ctx = MockQueryContext::test_default();
1✔
1659

1✔
1660
        let qp = agg
1✔
1661
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1662
            .await
1✔
1663
            .unwrap()
1✔
1664
            .query_processor()
1✔
1665
            .unwrap()
1✔
1666
            .get_u8()
1✔
1667
            .unwrap();
1✔
1668

1✔
1669
        let result = qp
1✔
1670
            .raster_query(query_rect, &query_ctx)
1✔
1671
            .await
1✔
1672
            .unwrap()
1✔
1673
            .collect::<Vec<_>>()
1✔
1674
            .await;
1✔
1675

1✔
1676
        assert_eq!(result.len(), 2);
1✔
1677

1✔
1678
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1679
            &RasterTile2D::new_with_tile_info(
1✔
1680
                TimeInterval::new_unchecked(0, 30),
1✔
1681
                TileInformation {
1✔
1682
                    global_tile_position: [-1, 0].into(),
1✔
1683
                    tile_size_in_pixels: [3, 2].into(),
1✔
1684
                    global_geo_transform: TestDefault::test_default(),
1✔
1685
                },
1✔
1686
                0,
1✔
1687
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![10, 8, 12, 16, 14, 15]).unwrap()),
1✔
1688
                CacheHint::default()
1✔
1689
            )
1✔
1690
        ));
1✔
1691

1✔
1692
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1693
            &RasterTile2D::new_with_tile_info(
1✔
1694
                TimeInterval::new_unchecked(0, 30),
1✔
1695
                TileInformation {
1✔
1696
                    global_tile_position: [-1, 1].into(),
1✔
1697
                    tile_size_in_pixels: [3, 2].into(),
1✔
1698
                    global_geo_transform: TestDefault::test_default(),
1✔
1699
                },
1✔
1700
                0,
1✔
1701
                GridOrEmpty::from(
1✔
1702
                    MaskedGrid2D::new(
1✔
1703
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1704
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1705
                            .unwrap()
1✔
1706
                    )
1✔
1707
                    .unwrap()
1✔
1708
                ),
1✔
1709
                CacheHint::default()
1✔
1710
            )
1✔
1711
        ));
1✔
1712
    }
1✔
1713

1714
    #[tokio::test]
1715
    #[allow(clippy::too_many_lines)]
1716
    async fn test_sum_without_nodata() {
1✔
1717
        let operator = TemporalRasterAggregation {
1✔
1718
            params: TemporalRasterAggregationParameters {
1✔
1719
                aggregation: Aggregation::Sum {
1✔
1720
                    ignore_no_data: false,
1✔
1721
                },
1✔
1722
                window: TimeStep {
1✔
1723
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1724
                    step: 20,
1✔
1725
                },
1✔
1726
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
1727
                output_type: None,
1✔
1728
            },
1✔
1729
            sources: SingleRasterSource {
1✔
1730
                raster: MockRasterSource {
1✔
1731
                    params: MockRasterSourceParams {
1✔
1732
                        data: make_raster(),
1✔
1733
                        result_descriptor: RasterResultDescriptor {
1✔
1734
                            data_type: RasterDataType::U8,
1✔
1735
                            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1736
                            time: None,
1✔
1737
                            bbox: None,
1✔
1738
                            resolution: None,
1✔
1739
                            bands: RasterBandDescriptors::new_single_band(),
1✔
1740
                        },
1✔
1741
                    },
1✔
1742
                }
1✔
1743
                .boxed(),
1✔
1744
            },
1✔
1745
        }
1✔
1746
        .boxed();
1✔
1747

1✔
1748
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1749
            (0., 0.).into(),
1✔
1750
            [3, 2].into(),
1✔
1751
        ));
1✔
1752
        let query_rect = RasterQueryRectangle {
1✔
1753
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1754
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1755
            spatial_resolution: SpatialResolution::one(),
1✔
1756
            attributes: BandSelection::first(),
1✔
1757
        };
1✔
1758
        let query_ctx = MockQueryContext::test_default();
1✔
1759

1✔
1760
        let query_processor = operator
1✔
1761
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1762
            .await
1✔
1763
            .unwrap()
1✔
1764
            .query_processor()
1✔
1765
            .unwrap()
1✔
1766
            .get_u8()
1✔
1767
            .unwrap();
1✔
1768

1✔
1769
        let result = query_processor
1✔
1770
            .raster_query(query_rect, &query_ctx)
1✔
1771
            .await
1✔
1772
            .unwrap()
1✔
1773
            .map(Result::unwrap)
1✔
1774
            .collect::<Vec<_>>()
1✔
1775
            .await;
1✔
1776

1✔
1777
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
1778
            RasterTile2D::new_with_tile_info(
1✔
1779
                TimeInterval::new_unchecked(0, 20),
1✔
1780
                TileInformation {
1✔
1781
                    global_tile_position: [-1, 0].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(0, 20),
1✔
1793
                TileInformation {
1✔
1794
                    global_tile_position: [-1, 1].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, 0].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
            RasterTile2D::new_with_tile_info(
1✔
1818
                TimeInterval::new_unchecked(20, 40),
1✔
1819
                TileInformation {
1✔
1820
                    global_tile_position: [-1, 1].into(),
1✔
1821
                    tile_size_in_pixels: [3, 2].into(),
1✔
1822
                    global_geo_transform: TestDefault::test_default(),
1✔
1823
                },
1✔
1824
                0,
1✔
1825
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1826
                    .unwrap()
1✔
1827
                    .into(),
1✔
1828
                CacheHint::default()
1✔
1829
            )
1✔
1830
        ]));
1✔
1831
    }
1✔
1832

1833
    #[tokio::test]
1834
    async fn test_sum_nodata() {
1✔
1835
        let raster_tiles = make_raster_with_no_data();
1✔
1836

1✔
1837
        let mrs = MockRasterSource {
1✔
1838
            params: MockRasterSourceParams {
1✔
1839
                data: raster_tiles,
1✔
1840
                result_descriptor: RasterResultDescriptor {
1✔
1841
                    data_type: RasterDataType::U8,
1✔
1842
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1843
                    time: None,
1✔
1844
                    bbox: None,
1✔
1845
                    resolution: None,
1✔
1846
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1847
                },
1✔
1848
            },
1✔
1849
        }
1✔
1850
        .boxed();
1✔
1851

1✔
1852
        let agg = TemporalRasterAggregation {
1✔
1853
            params: TemporalRasterAggregationParameters {
1✔
1854
                aggregation: Aggregation::Sum {
1✔
1855
                    ignore_no_data: false,
1✔
1856
                },
1✔
1857
                window: TimeStep {
1✔
1858
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1859
                    step: 30,
1✔
1860
                },
1✔
1861
                window_reference: None,
1✔
1862
                output_type: None,
1✔
1863
            },
1✔
1864
            sources: SingleRasterSource { raster: mrs },
1✔
1865
        }
1✔
1866
        .boxed();
1✔
1867

1✔
1868
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1869
            (0., 0.).into(),
1✔
1870
            [3, 2].into(),
1✔
1871
        ));
1✔
1872
        let query_rect = RasterQueryRectangle {
1✔
1873
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1874
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1875
            spatial_resolution: SpatialResolution::one(),
1✔
1876
            attributes: BandSelection::first(),
1✔
1877
        };
1✔
1878
        let query_ctx = MockQueryContext::test_default();
1✔
1879

1✔
1880
        let qp = agg
1✔
1881
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1882
            .await
1✔
1883
            .unwrap()
1✔
1884
            .query_processor()
1✔
1885
            .unwrap()
1✔
1886
            .get_u8()
1✔
1887
            .unwrap();
1✔
1888

1✔
1889
        let result = qp
1✔
1890
            .raster_query(query_rect, &query_ctx)
1✔
1891
            .await
1✔
1892
            .unwrap()
1✔
1893
            .collect::<Vec<_>>()
1✔
1894
            .await;
1✔
1895

1✔
1896
        assert_eq!(result.len(), 2);
1✔
1897

1✔
1898
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1899
            &RasterTile2D::new_with_tile_info(
1✔
1900
                TimeInterval::new_unchecked(0, 30),
1✔
1901
                TileInformation {
1✔
1902
                    global_tile_position: [-1, 0].into(),
1✔
1903
                    tile_size_in_pixels: [3, 2].into(),
1✔
1904
                    global_geo_transform: TestDefault::test_default(),
1✔
1905
                },
1✔
1906
                0,
1✔
1907
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
1908
                CacheHint::default()
1✔
1909
            )
1✔
1910
        ));
1✔
1911

1✔
1912
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1913
            &RasterTile2D::new_with_tile_info(
1✔
1914
                TimeInterval::new_unchecked(0, 30),
1✔
1915
                TileInformation {
1✔
1916
                    global_tile_position: [-1, 1].into(),
1✔
1917
                    tile_size_in_pixels: [3, 2].into(),
1✔
1918
                    global_geo_transform: TestDefault::test_default(),
1✔
1919
                },
1✔
1920
                0,
1✔
1921
                GridOrEmpty::from(
1✔
1922
                    MaskedGrid2D::new(
1✔
1923
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1924
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1925
                            .unwrap()
1✔
1926
                    )
1✔
1927
                    .unwrap(),
1✔
1928
                ),
1✔
1929
                CacheHint::default()
1✔
1930
            )
1✔
1931
        ));
1✔
1932
    }
1✔
1933

1934
    #[tokio::test]
1935
    async fn test_sum_ignore_no_data() {
1✔
1936
        let raster_tiles = make_raster_with_no_data();
1✔
1937

1✔
1938
        let mrs = MockRasterSource {
1✔
1939
            params: MockRasterSourceParams {
1✔
1940
                data: raster_tiles,
1✔
1941
                result_descriptor: RasterResultDescriptor {
1✔
1942
                    data_type: RasterDataType::U8,
1✔
1943
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1944
                    time: None,
1✔
1945
                    bbox: None,
1✔
1946
                    resolution: None,
1✔
1947
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1948
                },
1✔
1949
            },
1✔
1950
        }
1✔
1951
        .boxed();
1✔
1952

1✔
1953
        let agg = TemporalRasterAggregation {
1✔
1954
            params: TemporalRasterAggregationParameters {
1✔
1955
                aggregation: Aggregation::Sum {
1✔
1956
                    ignore_no_data: true,
1✔
1957
                },
1✔
1958
                window: TimeStep {
1✔
1959
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1960
                    step: 30,
1✔
1961
                },
1✔
1962
                window_reference: None,
1✔
1963
                output_type: None,
1✔
1964
            },
1✔
1965
            sources: SingleRasterSource { raster: mrs },
1✔
1966
        }
1✔
1967
        .boxed();
1✔
1968

1✔
1969
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1970
            (0., 0.).into(),
1✔
1971
            [3, 2].into(),
1✔
1972
        ));
1✔
1973
        let query_rect = RasterQueryRectangle {
1✔
1974
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
1975
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1976
            spatial_resolution: SpatialResolution::one(),
1✔
1977
            attributes: BandSelection::first(),
1✔
1978
        };
1✔
1979
        let query_ctx = MockQueryContext::test_default();
1✔
1980

1✔
1981
        let qp = agg
1✔
1982
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1983
            .await
1✔
1984
            .unwrap()
1✔
1985
            .query_processor()
1✔
1986
            .unwrap()
1✔
1987
            .get_u8()
1✔
1988
            .unwrap();
1✔
1989

1✔
1990
        let result = qp
1✔
1991
            .raster_query(query_rect, &query_ctx)
1✔
1992
            .await
1✔
1993
            .unwrap()
1✔
1994
            .collect::<Vec<_>>()
1✔
1995
            .await;
1✔
1996

1✔
1997
        assert_eq!(result.len(), 2);
1✔
1998

1✔
1999
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2000
            &RasterTile2D::new_with_tile_info(
1✔
2001
                TimeInterval::new_unchecked(0, 30),
1✔
2002
                TileInformation {
1✔
2003
                    global_tile_position: [-1, 0].into(),
1✔
2004
                    tile_size_in_pixels: [3, 2].into(),
1✔
2005
                    global_geo_transform: TestDefault::test_default(),
1✔
2006
                },
1✔
2007
                0,
1✔
2008
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![20, 8, 24, 16, 28, 30]).unwrap()),
1✔
2009
                CacheHint::default()
1✔
2010
            )
1✔
2011
        ));
1✔
2012

1✔
2013
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2014
            &RasterTile2D::new_with_tile_info(
1✔
2015
                TimeInterval::new_unchecked(0, 30),
1✔
2016
                TileInformation {
1✔
2017
                    global_tile_position: [-1, 1].into(),
1✔
2018
                    tile_size_in_pixels: [3, 2].into(),
1✔
2019
                    global_geo_transform: TestDefault::test_default(),
1✔
2020
                },
1✔
2021
                0,
1✔
2022
                GridOrEmpty::from(
1✔
2023
                    MaskedGrid2D::new(
1✔
2024
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
2025
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2026
                            .unwrap()
1✔
2027
                    )
1✔
2028
                    .unwrap()
1✔
2029
                ),
1✔
2030
                CacheHint::default()
1✔
2031
            )
1✔
2032
        ));
1✔
2033
    }
1✔
2034

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

1✔
2080
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2081
            (0., 0.).into(),
1✔
2082
            [3, 2].into(),
1✔
2083
        ));
1✔
2084
        let query_rect = RasterQueryRectangle {
1✔
2085
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2086
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2087
            spatial_resolution: SpatialResolution::one(),
1✔
2088
            attributes: BandSelection::first(),
1✔
2089
        };
1✔
2090
        let query_ctx = MockQueryContext::test_default();
1✔
2091

1✔
2092
        let query_processor = operator
1✔
2093
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2094
            .await
1✔
2095
            .unwrap()
1✔
2096
            .query_processor()
1✔
2097
            .unwrap()
1✔
2098
            .get_u16()
1✔
2099
            .unwrap();
1✔
2100

1✔
2101
        let result = query_processor
1✔
2102
            .raster_query(query_rect, &query_ctx)
1✔
2103
            .await
1✔
2104
            .unwrap()
1✔
2105
            .map(Result::unwrap)
1✔
2106
            .collect::<Vec<_>>()
1✔
2107
            .await;
1✔
2108

1✔
2109
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
2110
            RasterTile2D::new_with_tile_info(
1✔
2111
                TimeInterval::new_unchecked(0, 20),
1✔
2112
                TileInformation {
1✔
2113
                    global_tile_position: [-1, 0].into(),
1✔
2114
                    tile_size_in_pixels: [3, 2].into(),
1✔
2115
                    global_geo_transform: TestDefault::test_default(),
1✔
2116
                },
1✔
2117
                0,
1✔
2118
                Grid2D::new(
1✔
2119
                    [3, 2].into(),
1✔
2120
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2121
                )
1✔
2122
                .unwrap()
1✔
2123
                .into(),
1✔
2124
                CacheHint::default()
1✔
2125
            ),
1✔
2126
            RasterTile2D::new_with_tile_info(
1✔
2127
                TimeInterval::new_unchecked(0, 20),
1✔
2128
                TileInformation {
1✔
2129
                    global_tile_position: [-1, 1].into(),
1✔
2130
                    tile_size_in_pixels: [3, 2].into(),
1✔
2131
                    global_geo_transform: TestDefault::test_default(),
1✔
2132
                },
1✔
2133
                0,
1✔
2134
                Grid2D::new(
1✔
2135
                    [3, 2].into(),
1✔
2136
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2137
                )
1✔
2138
                .unwrap()
1✔
2139
                .into(),
1✔
2140
                CacheHint::default()
1✔
2141
            ),
1✔
2142
            RasterTile2D::new_with_tile_info(
1✔
2143
                TimeInterval::new_unchecked(20, 40),
1✔
2144
                TileInformation {
1✔
2145
                    global_tile_position: [-1, 0].into(),
1✔
2146
                    tile_size_in_pixels: [3, 2].into(),
1✔
2147
                    global_geo_transform: TestDefault::test_default(),
1✔
2148
                },
1✔
2149
                0,
1✔
2150
                Grid2D::new(
1✔
2151
                    [3, 2].into(),
1✔
2152
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2153
                )
1✔
2154
                .unwrap()
1✔
2155
                .into(),
1✔
2156
                CacheHint::default()
1✔
2157
            ),
1✔
2158
            RasterTile2D::new_with_tile_info(
1✔
2159
                TimeInterval::new_unchecked(20, 40),
1✔
2160
                TileInformation {
1✔
2161
                    global_tile_position: [-1, 1].into(),
1✔
2162
                    tile_size_in_pixels: [3, 2].into(),
1✔
2163
                    global_geo_transform: TestDefault::test_default(),
1✔
2164
                },
1✔
2165
                0,
1✔
2166
                Grid2D::new(
1✔
2167
                    [3, 2].into(),
1✔
2168
                    vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2169
                )
1✔
2170
                .unwrap()
1✔
2171
                .into(),
1✔
2172
                CacheHint::default()
1✔
2173
            )
1✔
2174
        ]),);
1✔
2175
    }
1✔
2176

2177
    #[tokio::test]
2178
    #[allow(clippy::too_many_lines)]
2179
    async fn test_count_without_nodata() {
1✔
2180
        let operator = TemporalRasterAggregation {
1✔
2181
            params: TemporalRasterAggregationParameters {
1✔
2182
                aggregation: Aggregation::Count {
1✔
2183
                    ignore_no_data: false,
1✔
2184
                },
1✔
2185
                window: TimeStep {
1✔
2186
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2187
                    step: 20,
1✔
2188
                },
1✔
2189
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
2190
                output_type: None,
1✔
2191
            },
1✔
2192
            sources: SingleRasterSource {
1✔
2193
                raster: MockRasterSource {
1✔
2194
                    params: MockRasterSourceParams {
1✔
2195
                        data: make_raster(),
1✔
2196
                        result_descriptor: RasterResultDescriptor {
1✔
2197
                            data_type: RasterDataType::U8,
1✔
2198
                            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2199
                            time: None,
1✔
2200
                            bbox: None,
1✔
2201
                            resolution: None,
1✔
2202
                            bands: RasterBandDescriptors::new_single_band(),
1✔
2203
                        },
1✔
2204
                    },
1✔
2205
                }
1✔
2206
                .boxed(),
1✔
2207
            },
1✔
2208
        }
1✔
2209
        .boxed();
1✔
2210

1✔
2211
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2212
            (0., 0.).into(),
1✔
2213
            [3, 2].into(),
1✔
2214
        ));
1✔
2215
        let query_rect = RasterQueryRectangle {
1✔
2216
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2217
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2218
            spatial_resolution: SpatialResolution::one(),
1✔
2219
            attributes: BandSelection::first(),
1✔
2220
        };
1✔
2221
        let query_ctx = MockQueryContext::test_default();
1✔
2222

1✔
2223
        let query_processor = operator
1✔
2224
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2225
            .await
1✔
2226
            .unwrap()
1✔
2227
            .query_processor()
1✔
2228
            .unwrap()
1✔
2229
            .get_u8()
1✔
2230
            .unwrap();
1✔
2231

1✔
2232
        let result = query_processor
1✔
2233
            .raster_query(query_rect, &query_ctx)
1✔
2234
            .await
1✔
2235
            .unwrap()
1✔
2236
            .map(Result::unwrap)
1✔
2237
            .collect::<Vec<_>>()
1✔
2238
            .await;
1✔
2239

1✔
2240
        assert!(result.tiles_equal_ignoring_cache_hint(&[
1✔
2241
            RasterTile2D::new_with_tile_info(
1✔
2242
                TimeInterval::new_unchecked(0, 20),
1✔
2243
                TileInformation {
1✔
2244
                    global_tile_position: [-1, 0].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(0, 20),
1✔
2256
                TileInformation {
1✔
2257
                    global_tile_position: [-1, 1].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, 0].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
            RasterTile2D::new_with_tile_info(
1✔
2281
                TimeInterval::new_unchecked(20, 40),
1✔
2282
                TileInformation {
1✔
2283
                    global_tile_position: [-1, 1].into(),
1✔
2284
                    tile_size_in_pixels: [3, 2].into(),
1✔
2285
                    global_geo_transform: TestDefault::test_default(),
1✔
2286
                },
1✔
2287
                0,
1✔
2288
                Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2289
                    .unwrap()
1✔
2290
                    .into(),
1✔
2291
                CacheHint::default()
1✔
2292
            )
1✔
2293
        ]));
1✔
2294
    }
1✔
2295

2296
    #[tokio::test]
2297
    async fn test_count_nodata() {
1✔
2298
        let raster_tiles = make_raster_with_no_data();
1✔
2299

1✔
2300
        let mrs = MockRasterSource {
1✔
2301
            params: MockRasterSourceParams {
1✔
2302
                data: raster_tiles,
1✔
2303
                result_descriptor: RasterResultDescriptor {
1✔
2304
                    data_type: RasterDataType::U8,
1✔
2305
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2306
                    time: None,
1✔
2307
                    bbox: None,
1✔
2308
                    resolution: None,
1✔
2309
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2310
                },
1✔
2311
            },
1✔
2312
        }
1✔
2313
        .boxed();
1✔
2314

1✔
2315
        let agg = TemporalRasterAggregation {
1✔
2316
            params: TemporalRasterAggregationParameters {
1✔
2317
                aggregation: Aggregation::Count {
1✔
2318
                    ignore_no_data: false,
1✔
2319
                },
1✔
2320
                window: TimeStep {
1✔
2321
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2322
                    step: 30,
1✔
2323
                },
1✔
2324
                window_reference: None,
1✔
2325
                output_type: None,
1✔
2326
            },
1✔
2327
            sources: SingleRasterSource { raster: mrs },
1✔
2328
        }
1✔
2329
        .boxed();
1✔
2330

1✔
2331
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2332
            (0., 0.).into(),
1✔
2333
            [3, 2].into(),
1✔
2334
        ));
1✔
2335
        let query_rect = RasterQueryRectangle {
1✔
2336
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2337
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2338
            spatial_resolution: SpatialResolution::one(),
1✔
2339
            attributes: BandSelection::first(),
1✔
2340
        };
1✔
2341
        let query_ctx = MockQueryContext::test_default();
1✔
2342

1✔
2343
        let qp = agg
1✔
2344
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2345
            .await
1✔
2346
            .unwrap()
1✔
2347
            .query_processor()
1✔
2348
            .unwrap()
1✔
2349
            .get_u8()
1✔
2350
            .unwrap();
1✔
2351

1✔
2352
        let result = qp
1✔
2353
            .raster_query(query_rect, &query_ctx)
1✔
2354
            .await
1✔
2355
            .unwrap()
1✔
2356
            .collect::<Vec<_>>()
1✔
2357
            .await;
1✔
2358

1✔
2359
        assert_eq!(result.len(), 2);
1✔
2360

1✔
2361
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2362
            &RasterTile2D::new_with_tile_info(
1✔
2363
                TimeInterval::new_unchecked(0, 30),
1✔
2364
                TileInformation {
1✔
2365
                    global_tile_position: [-1, 0].into(),
1✔
2366
                    tile_size_in_pixels: [3, 2].into(),
1✔
2367
                    global_geo_transform: TestDefault::test_default(),
1✔
2368
                },
1✔
2369
                0,
1✔
2370
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
2371
                CacheHint::default()
1✔
2372
            )
1✔
2373
        ));
1✔
2374

1✔
2375
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2376
            &RasterTile2D::new_with_tile_info(
1✔
2377
                TimeInterval::new_unchecked(0, 30),
1✔
2378
                TileInformation {
1✔
2379
                    global_tile_position: [-1, 1].into(),
1✔
2380
                    tile_size_in_pixels: [3, 2].into(),
1✔
2381
                    global_geo_transform: TestDefault::test_default(),
1✔
2382
                },
1✔
2383
                0,
1✔
2384
                GridOrEmpty::from(
1✔
2385
                    MaskedGrid2D::new(
1✔
2386
                        Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
1✔
2387
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2388
                            .unwrap()
1✔
2389
                    )
1✔
2390
                    .unwrap()
1✔
2391
                ),
1✔
2392
                CacheHint::default()
1✔
2393
            )
1✔
2394
        ));
1✔
2395
    }
1✔
2396

2397
    #[tokio::test]
2398
    async fn test_count_ignore_no_data() {
1✔
2399
        let raster_tiles = make_raster_with_no_data();
1✔
2400

1✔
2401
        let mrs = MockRasterSource {
1✔
2402
            params: MockRasterSourceParams {
1✔
2403
                data: raster_tiles,
1✔
2404
                result_descriptor: RasterResultDescriptor {
1✔
2405
                    data_type: RasterDataType::U8,
1✔
2406
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2407
                    time: None,
1✔
2408
                    bbox: None,
1✔
2409
                    resolution: None,
1✔
2410
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2411
                },
1✔
2412
            },
1✔
2413
        }
1✔
2414
        .boxed();
1✔
2415

1✔
2416
        let agg = TemporalRasterAggregation {
1✔
2417
            params: TemporalRasterAggregationParameters {
1✔
2418
                aggregation: Aggregation::Count {
1✔
2419
                    ignore_no_data: true,
1✔
2420
                },
1✔
2421
                window: TimeStep {
1✔
2422
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2423
                    step: 30,
1✔
2424
                },
1✔
2425
                window_reference: None,
1✔
2426
                output_type: None,
1✔
2427
            },
1✔
2428
            sources: SingleRasterSource { raster: mrs },
1✔
2429
        }
1✔
2430
        .boxed();
1✔
2431

1✔
2432
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2433
            (0., 0.).into(),
1✔
2434
            [3, 2].into(),
1✔
2435
        ));
1✔
2436
        let query_rect = RasterQueryRectangle {
1✔
2437
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2438
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2439
            spatial_resolution: SpatialResolution::one(),
1✔
2440
            attributes: BandSelection::first(),
1✔
2441
        };
1✔
2442
        let query_ctx = MockQueryContext::test_default();
1✔
2443

1✔
2444
        let qp = agg
1✔
2445
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2446
            .await
1✔
2447
            .unwrap()
1✔
2448
            .query_processor()
1✔
2449
            .unwrap()
1✔
2450
            .get_u8()
1✔
2451
            .unwrap();
1✔
2452

1✔
2453
        let result = qp
1✔
2454
            .raster_query(query_rect, &query_ctx)
1✔
2455
            .await
1✔
2456
            .unwrap()
1✔
2457
            .collect::<Vec<_>>()
1✔
2458
            .await;
1✔
2459

1✔
2460
        assert_eq!(result.len(), 2);
1✔
2461

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

1✔
2476
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2477
            &RasterTile2D::new_with_tile_info(
1✔
2478
                TimeInterval::new_unchecked(0, 30),
1✔
2479
                TileInformation {
1✔
2480
                    global_tile_position: [-1, 1].into(),
1✔
2481
                    tile_size_in_pixels: [3, 2].into(),
1✔
2482
                    global_geo_transform: TestDefault::test_default(),
1✔
2483
                },
1✔
2484
                0,
1✔
2485
                GridOrEmpty::from(
1✔
2486
                    MaskedGrid2D::new(
1✔
2487
                        Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
1✔
2488
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2489
                            .unwrap()
1✔
2490
                    )
1✔
2491
                    .unwrap()
1✔
2492
                ),
1✔
2493
                CacheHint::default()
1✔
2494
            )
1✔
2495
        ));
1✔
2496
    }
1✔
2497

2498
    #[tokio::test]
2499
    async fn test_query_not_aligned_with_window_reference() {
1✔
2500
        let raster_tiles = make_raster();
1✔
2501

1✔
2502
        let mrs = MockRasterSource {
1✔
2503
            params: MockRasterSourceParams {
1✔
2504
                data: raster_tiles,
1✔
2505
                result_descriptor: RasterResultDescriptor {
1✔
2506
                    data_type: RasterDataType::U8,
1✔
2507
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2508
                    time: None,
1✔
2509
                    bbox: None,
1✔
2510
                    resolution: None,
1✔
2511
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2512
                },
1✔
2513
            },
1✔
2514
        }
1✔
2515
        .boxed();
1✔
2516

1✔
2517
        let agg = TemporalRasterAggregation {
1✔
2518
            params: TemporalRasterAggregationParameters {
1✔
2519
                aggregation: Aggregation::Last {
1✔
2520
                    ignore_no_data: false,
1✔
2521
                },
1✔
2522
                window: TimeStep {
1✔
2523
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2524
                    step: 30,
1✔
2525
                },
1✔
2526
                window_reference: Some(TimeInstance::EPOCH_START),
1✔
2527
                output_type: None,
1✔
2528
            },
1✔
2529
            sources: SingleRasterSource { raster: mrs },
1✔
2530
        }
1✔
2531
        .boxed();
1✔
2532

1✔
2533
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2534
            (0., 0.).into(),
1✔
2535
            [3, 2].into(),
1✔
2536
        ));
1✔
2537
        let query_rect = RasterQueryRectangle {
1✔
2538
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2539
            time_interval: TimeInterval::new_unchecked(5, 5),
1✔
2540
            spatial_resolution: SpatialResolution::one(),
1✔
2541
            attributes: BandSelection::first(),
1✔
2542
        };
1✔
2543
        let query_ctx = MockQueryContext::test_default();
1✔
2544

1✔
2545
        let qp = agg
1✔
2546
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2547
            .await
1✔
2548
            .unwrap()
1✔
2549
            .query_processor()
1✔
2550
            .unwrap()
1✔
2551
            .get_u8()
1✔
2552
            .unwrap();
1✔
2553

1✔
2554
        let result = qp
1✔
2555
            .query(query_rect, &query_ctx)
1✔
2556
            .await
1✔
2557
            .unwrap()
1✔
2558
            .collect::<Vec<_>>()
1✔
2559
            .await;
1✔
2560

1✔
2561
        assert_eq!(result.len(), 2);
1✔
2562
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2563
            &RasterTile2D::new_with_tile_info(
1✔
2564
                TimeInterval::new_unchecked(0, 30),
1✔
2565
                TileInformation {
1✔
2566
                    global_tile_position: [-1, 0].into(),
1✔
2567
                    tile_size_in_pixels: [3, 2].into(),
1✔
2568
                    global_geo_transform: TestDefault::test_default(),
1✔
2569
                },
1✔
2570
                0,
1✔
2571
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
2572
                CacheHint::default()
1✔
2573
            )
1✔
2574
        ));
1✔
2575

1✔
2576
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2577
            &RasterTile2D::new_with_tile_info(
1✔
2578
                TimeInterval::new_unchecked(0, 30),
1✔
2579
                TileInformation {
1✔
2580
                    global_tile_position: [-1, 1].into(),
1✔
2581
                    tile_size_in_pixels: [3, 2].into(),
1✔
2582
                    global_geo_transform: TestDefault::test_default(),
1✔
2583
                },
1✔
2584
                0,
1✔
2585
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
2586
                CacheHint::default()
1✔
2587
            )
1✔
2588
        ));
1✔
2589
    }
1✔
2590

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

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

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

1✔
2836
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2837
            (0., 0.).into(),
1✔
2838
            [3, 2].into(),
1✔
2839
        ));
1✔
2840
        let query_rect = RasterQueryRectangle {
1✔
2841
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
2842
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2843
            spatial_resolution: SpatialResolution::one(),
1✔
2844
            attributes: [0, 1].try_into().unwrap(),
1✔
2845
        };
1✔
2846
        let query_ctx = MockQueryContext::test_default();
1✔
2847

1✔
2848
        let query_processor = operator
1✔
2849
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2850
            .await
1✔
2851
            .unwrap()
1✔
2852
            .query_processor()
1✔
2853
            .unwrap()
1✔
2854
            .get_u8()
1✔
2855
            .unwrap();
1✔
2856

1✔
2857
        let result = query_processor
1✔
2858
            .raster_query(query_rect, &query_ctx)
1✔
2859
            .await
1✔
2860
            .unwrap()
1✔
2861
            .map(Result::unwrap)
1✔
2862
            .collect::<Vec<_>>()
1✔
2863
            .await;
1✔
2864

1✔
2865
        assert!(result.tiles_equal_ignoring_cache_hint(&[
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
                0,
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, 0].into(),
1✔
2883
                    tile_size_in_pixels: [3, 2].into(),
1✔
2884
                    global_geo_transform: TestDefault::test_default(),
1✔
2885
                },
1✔
2886
                1,
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
                0,
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(0, 20),
1✔
2907
                TileInformation {
1✔
2908
                    global_tile_position: [-1, 1].into(),
1✔
2909
                    tile_size_in_pixels: [3, 2].into(),
1✔
2910
                    global_geo_transform: TestDefault::test_default(),
1✔
2911
                },
1✔
2912
                1,
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
                0,
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, 0].into(),
1✔
2935
                    tile_size_in_pixels: [3, 2].into(),
1✔
2936
                    global_geo_transform: TestDefault::test_default(),
1✔
2937
                },
1✔
2938
                1,
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
                0,
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
            RasterTile2D::new_with_tile_info(
1✔
2958
                TimeInterval::new_unchecked(20, 40),
1✔
2959
                TileInformation {
1✔
2960
                    global_tile_position: [-1, 1].into(),
1✔
2961
                    tile_size_in_pixels: [3, 2].into(),
1✔
2962
                    global_geo_transform: TestDefault::test_default(),
1✔
2963
                },
1✔
2964
                1,
1✔
2965
                Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
2966
                    .unwrap()
1✔
2967
                    .into(),
1✔
2968
                CacheHint::default()
1✔
2969
            )
1✔
2970
        ]));
1✔
2971
    }
1✔
2972

2973
    #[tokio::test]
2974
    async fn it_estimates_a_median() {
1✔
2975
        let raster_tiles = make_raster();
1✔
2976

1✔
2977
        let mrs = MockRasterSource {
1✔
2978
            params: MockRasterSourceParams {
1✔
2979
                data: raster_tiles,
1✔
2980
                result_descriptor: RasterResultDescriptor {
1✔
2981
                    data_type: RasterDataType::U8,
1✔
2982
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2983
                    time: None,
1✔
2984
                    bbox: None,
1✔
2985
                    resolution: None,
1✔
2986
                    bands: RasterBandDescriptors::new_single_band(),
1✔
2987
                },
1✔
2988
            },
1✔
2989
        }
1✔
2990
        .boxed();
1✔
2991

1✔
2992
        let agg = TemporalRasterAggregation {
1✔
2993
            params: TemporalRasterAggregationParameters {
1✔
2994
                aggregation: Aggregation::PercentileEstimate {
1✔
2995
                    percentile: 0.5,
1✔
2996
                    ignore_no_data: false,
1✔
2997
                },
1✔
2998
                window: TimeStep {
1✔
2999
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
3000
                    step: 40,
1✔
3001
                },
1✔
3002
                window_reference: None,
1✔
3003
                output_type: None,
1✔
3004
            },
1✔
3005
            sources: SingleRasterSource { raster: mrs },
1✔
3006
        }
1✔
3007
        .boxed();
1✔
3008

1✔
3009
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
3010
            (0., 0.).into(),
1✔
3011
            [3, 2].into(),
1✔
3012
        ));
1✔
3013
        let query_rect = RasterQueryRectangle {
1✔
3014
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
1✔
3015
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
3016
            spatial_resolution: SpatialResolution::one(),
1✔
3017
            attributes: BandSelection::first(),
1✔
3018
        };
1✔
3019
        let query_ctx = MockQueryContext::test_default();
1✔
3020

1✔
3021
        let qp = agg
1✔
3022
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
3023
            .await
1✔
3024
            .unwrap()
1✔
3025
            .query_processor()
1✔
3026
            .unwrap()
1✔
3027
            .get_u8()
1✔
3028
            .unwrap();
1✔
3029

1✔
3030
        let result = qp
1✔
3031
            .query(query_rect, &query_ctx)
1✔
3032
            .await
1✔
3033
            .unwrap()
1✔
3034
            .map(Result::unwrap)
1✔
3035
            .collect::<Vec<_>>()
1✔
3036
            .await;
1✔
3037

1✔
3038
        assert_eq!(result.len(), 2);
1✔
3039

1✔
3040
        assert_eq!(
1✔
3041
            result[0].grid_array,
1✔
3042
            GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 6, 6, 6, 6, 6]).unwrap())
1✔
3043
        );
1✔
3044
    }
1✔
3045
}
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