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

geo-engine / geoengine / 19567513936

21 Nov 2025 10:25AM UTC coverage: 88.459%. First build
19567513936

Pull #1083

github

web-flow
Merge 5dc8f9b67 into ba34db99d
Pull Request #1083: feat: new gdal source workflow optimization

6332 of 7653 new or added lines in 79 files covered. (82.74%)

116534 of 131738 relevant lines covered (88.46%)

492817.72 hits per line

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

97.95
/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
    TemporalRasterAggregationSubQueryNoDataOnly, first_tile_fold_future, last_tile_fold_future,
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, TimeDescriptor, WorkflowOperatorPath,
16
};
17
use crate::optimization::OptimizationError;
18
use crate::processing::temporal_raster_aggregation::aggregators::PercentileEstimateAggregator;
19
use crate::{
20
    adapters::SubQueryTileAggregator,
21
    engine::{
22
        InitializedRasterOperator, OperatorName, RasterQueryProcessor, RasterResultDescriptor,
23
        TypedRasterQueryProcessor,
24
    },
25
    error,
26
    util::Result,
27
};
28
use async_trait::async_trait;
29
use futures::{Stream, StreamExt};
30
use geoengine_datatypes::primitives::{
31
    BandSelection, RasterQueryRectangle, RegularTimeDimension, SpatialResolution, TimeFilledItem,
32
    TimeInstance,
33
};
34
use geoengine_datatypes::raster::{GridBoundingBox2D, Pixel, RasterDataType, RasterTile2D};
35
use geoengine_datatypes::{primitives::TimeStep, raster::TilingSpecification};
36
use serde::{Deserialize, Serialize};
37
use snafu::ensure;
38
use std::marker::PhantomData;
39
use std::sync::Arc;
40
use tracing::debug;
41

42
use typetag;
43

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

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

83
pub type TemporalRasterAggregation =
84
    Operator<TemporalRasterAggregationParameters, SingleRasterSource>;
85

86
impl OperatorName for TemporalRasterAggregation {
87
    const TYPE_NAME: &'static str = "TemporalRasterAggregation";
88
}
89

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

100
        let name = CanonicOperatorName::from(&self);
101

102
        let initialized_source = self
103
            .sources
104
            .initialize_sources(path.clone(), context)
105
            .await?;
106
        let source = initialized_source.raster;
107

108
        debug!(
109
            "Initializing TemporalRasterAggregation with {:?}.",
110
            &self.params
111
        );
112

113
        let mut out_result_descriptor = source.result_descriptor().clone();
114

115
        out_result_descriptor.time = TimeDescriptor::new(
116
            source.result_descriptor().time.bounds,
117
            geoengine_datatypes::primitives::TimeDimension::Regular(RegularTimeDimension {
118
                step: self.params.window,
119
                origin: self
120
                    .params
121
                    .window_reference
122
                    .unwrap_or(TimeInstance::from_millis_unchecked(0)),
123
            }),
124
        );
125

126
        if let Some(output_type) = self.params.output_type {
127
            out_result_descriptor.data_type = output_type;
128
        }
129

130
        let initialized_operator = InitializedTemporalRasterAggregation {
131
            name,
132
            path,
133
            aggregation_type: self.params.aggregation,
134
            window: self.params.window,
135
            window_reference: self
136
                .params
137
                .window_reference
138
                .unwrap_or(TimeInstance::EPOCH_START),
139
            result_descriptor: out_result_descriptor,
140
            source,
141
            tiling_specification: context.tiling_specification(),
142
            output_type: self.params.output_type,
143
        };
144

145
        Ok(initialized_operator.boxed())
146
    }
21✔
147

148
    span_fn!(TemporalRasterAggregation);
149
}
150

151
pub struct InitializedTemporalRasterAggregation {
152
    name: CanonicOperatorName,
153
    path: WorkflowOperatorPath,
154
    aggregation_type: Aggregation,
155
    window: TimeStep,
156
    window_reference: TimeInstance,
157
    source: Box<dyn InitializedRasterOperator>,
158
    result_descriptor: RasterResultDescriptor,
159
    tiling_specification: TilingSpecification,
160
    output_type: Option<RasterDataType>,
161
}
162

163
impl InitializedRasterOperator for InitializedTemporalRasterAggregation {
164
    fn result_descriptor(&self) -> &RasterResultDescriptor {
×
165
        &self.result_descriptor
×
166
    }
×
167

168
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor> {
21✔
169
        let source_processor = self.source.query_processor()?;
21✔
170

171
        let source_processor: TypedRasterQueryProcessor = match self.output_type {
21✔
172
            Some(RasterDataType::U8) => source_processor.into_u8().into(),
×
173
            Some(RasterDataType::U16) => source_processor.into_u16().into(),
1✔
174
            Some(RasterDataType::U32) => source_processor.into_u32().into(),
×
175
            Some(RasterDataType::U64) => source_processor.into_u64().into(),
×
176
            Some(RasterDataType::I8) => source_processor.into_i8().into(),
×
177
            Some(RasterDataType::I16) => source_processor.into_i16().into(),
×
178
            Some(RasterDataType::I32) => source_processor.into_i32().into(),
×
179
            Some(RasterDataType::I64) => source_processor.into_i64().into(),
×
180
            Some(RasterDataType::F32) => source_processor.into_f32().into(),
×
181
            Some(RasterDataType::F64) => source_processor.into_f64().into(),
×
182
            // use the same output type as the input type
183
            None => source_processor,
20✔
184
        };
185

186
        let res = call_on_generic_raster_processor!(
21✔
187
            source_processor, p =>
21✔
188
            TemporalRasterAggregationProcessor::new(
20✔
189
                self.result_descriptor.clone(),
20✔
190
                self.aggregation_type,
20✔
191
                p,
20✔
192
                self.tiling_specification,
20✔
193
            ).boxed()
20✔
194
            .into()
20✔
195
        );
196

197
        Ok(res)
21✔
198
    }
21✔
199

200
    fn canonic_name(&self) -> CanonicOperatorName {
×
201
        self.name.clone()
×
202
    }
×
203

204
    fn name(&self) -> &'static str {
×
205
        TemporalRasterAggregation::TYPE_NAME
×
206
    }
×
207

208
    fn path(&self) -> WorkflowOperatorPath {
×
209
        self.path.clone()
×
210
    }
×
211

NEW
212
    fn optimize(
×
NEW
213
        &self,
×
NEW
214
        target_resolution: SpatialResolution,
×
NEW
215
    ) -> Result<Box<dyn RasterOperator>, OptimizationError> {
×
216
        Ok(TemporalRasterAggregation {
NEW
217
            params: TemporalRasterAggregationParameters {
×
NEW
218
                aggregation: self.aggregation_type,
×
NEW
219
                window: self.window,
×
NEW
220
                window_reference: Some(self.window_reference),
×
NEW
221
                output_type: self.output_type,
×
NEW
222
            },
×
223
            sources: SingleRasterSource {
NEW
224
                raster: self.source.optimize(target_resolution)?,
×
225
            },
226
        }
NEW
227
        .boxed())
×
NEW
228
    }
×
229
}
230

231
pub struct TemporalRasterAggregationProcessor<Q, P>
232
where
233
    Q: RasterQueryProcessor<RasterType = P>,
234
    P: Pixel,
235
{
236
    result_descriptor: RasterResultDescriptor,
237
    aggregation_type: Aggregation,
238
    source: Q,
239
    tiling_specification: TilingSpecification,
240
}
241

242
impl<Q, P> TemporalRasterAggregationProcessor<Q, P>
243
where
244
    Q: RasterQueryProcessor<RasterType = P>
245
        + QueryProcessor<
246
            Output = RasterTile2D<P>,
247
            SpatialBounds = GridBoundingBox2D,
248
            Selection = BandSelection,
249
            ResultDescription = RasterResultDescriptor,
250
        >,
251
    P: Pixel,
252
{
253
    fn new(
21✔
254
        result_descriptor: RasterResultDescriptor,
21✔
255
        aggregation_type: Aggregation,
21✔
256
        source: Q,
21✔
257
        tiling_specification: TilingSpecification,
21✔
258
    ) -> Self {
21✔
259
        Self {
21✔
260
            result_descriptor,
21✔
261
            aggregation_type,
21✔
262
            source,
21✔
263
            tiling_specification,
21✔
264
        }
21✔
265
    }
21✔
266

267
    fn create_subquery<F: TemporalRasterPixelAggregator<P> + 'static, FoldFn>(
18✔
268
        fold_fn: FoldFn,
18✔
269
    ) -> super::subquery::TemporalRasterAggregationSubQuery<FoldFn, P, F> {
18✔
270
        super::subquery::TemporalRasterAggregationSubQuery {
18✔
271
            fold_fn,
18✔
272
            _phantom_pixel_type: PhantomData,
18✔
273
        }
18✔
274
    }
18✔
275

276
    fn create_global_state_subquery<
1✔
277
        F: GlobalStateTemporalRasterPixelAggregator<P> + 'static,
1✔
278
        FoldFn,
1✔
279
    >(
1✔
280
        aggregator: F,
1✔
281
        fold_fn: FoldFn,
1✔
282
    ) -> GlobalStateTemporalRasterAggregationSubQuery<FoldFn, P, F> {
1✔
283
        GlobalStateTemporalRasterAggregationSubQuery {
1✔
284
            aggregator: Arc::new(aggregator),
1✔
285
            fold_fn,
1✔
286

1✔
287
            _phantom_pixel_type: PhantomData,
1✔
288
        }
1✔
289
    }
1✔
290

291
    fn create_subquery_first<F>(fold_fn: F) -> TemporalRasterAggregationSubQueryNoDataOnly<F, P> {
1✔
292
        TemporalRasterAggregationSubQueryNoDataOnly {
1✔
293
            fold_fn,
1✔
294

1✔
295
            _phantom_pixel_type: PhantomData,
1✔
296
        }
1✔
297
    }
1✔
298

299
    fn create_subquery_last<F>(fold_fn: F) -> TemporalRasterAggregationSubQueryNoDataOnly<F, P> {
2✔
300
        TemporalRasterAggregationSubQueryNoDataOnly {
2✔
301
            fold_fn,
2✔
302

2✔
303
            _phantom_pixel_type: PhantomData,
2✔
304
        }
2✔
305
    }
2✔
306

307
    #[allow(clippy::too_many_lines)]
308
    async fn create_subquery_adapter_stream_for_single_band<'a>(
22✔
309
        &'a self,
22✔
310
        query: RasterQueryRectangle,
22✔
311
        ctx: &'a dyn crate::engine::QueryContext,
22✔
312
    ) -> Result<futures::stream::BoxStream<'a, Result<RasterTile2D<P>>>> {
22✔
313
        ensure!(
22✔
314
            query.attributes().count() == 1,
22✔
315
            error::InvalidBandCount {
×
316
                expected: 1u32,
×
317
                found: query.attributes().count()
×
318
            }
×
319
        );
320

321
        let grid_desc = self.result_descriptor.spatial_grid_descriptor();
22✔
322
        let tiling_strategy = grid_desc
22✔
323
            .tiling_grid_definition(self.tiling_specification)
22✔
324
            .generate_data_tiling_strategy();
22✔
325

326
        let time_stream: std::pin::Pin<
22✔
327
            Box<
22✔
328
                dyn Stream<
22✔
329
                        Item = std::result::Result<
22✔
330
                            geoengine_datatypes::primitives::TimeInterval,
22✔
331
                            error::Error,
22✔
332
                        >,
22✔
333
                    > + Send,
22✔
334
            >,
22✔
335
        > = self.time_query(query.time_interval(), ctx).await?;
22✔
336

337
        Ok(match self.aggregation_type {
22✔
338
            Aggregation::Min {
339
                ignore_no_data: true,
340
            } => Self::create_subquery(
×
341
                super::subquery::subquery_all_tiles_fold_fn::<P, MinPixelAggregatorIngoringNoData>,
×
342
            )
×
343
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
×
344
            .box_pin(),
×
345
            Aggregation::Min {
346
                ignore_no_data: false,
347
            } => Self::create_subquery(
1✔
348
                super::subquery::subquery_all_tiles_fold_fn::<P, MinPixelAggregator>,
1✔
349
            )
1✔
350
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
1✔
351
            .box_pin(),
1✔
352
            Aggregation::Max {
353
                ignore_no_data: true,
354
            } => Self::create_subquery(
1✔
355
                super::subquery::subquery_all_tiles_fold_fn::<P, MaxPixelAggregatorIngoringNoData>,
1✔
356
            )
1✔
357
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
1✔
358
            .box_pin(),
1✔
359
            Aggregation::Max {
360
                ignore_no_data: false,
361
            } => Self::create_subquery(
3✔
362
                super::subquery::subquery_all_tiles_fold_fn::<P, MaxPixelAggregator>,
3✔
363
            )
3✔
364
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
3✔
365
            .box_pin(),
3✔
366
            Aggregation::First {
367
                ignore_no_data: true,
368
            } => {
369
                Self::create_subquery(
1✔
370
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
371
                        P,
1✔
372
                        FirstPixelAggregatorIngoringNoData,
1✔
373
                    >,
1✔
374
                )
1✔
375
                .into_raster_subquery_adapter(
1✔
376
                    &self.source,
1✔
377
                    query,
1✔
378
                    ctx,
1✔
379
                    tiling_strategy,
1✔
380
                    time_stream,
1✔
381
                )
1✔
382
                .box_pin()
1✔
383
            }
384
            Aggregation::First {
385
                ignore_no_data: false,
386
            } => Self::create_subquery_first(first_tile_fold_future::<P>)
1✔
387
                .into_raster_subquery_adapter(
1✔
388
                    &self.source,
1✔
389
                    query,
1✔
390
                    ctx,
1✔
391
                    tiling_strategy,
1✔
392
                    time_stream,
1✔
393
                )
1✔
394
                .box_pin(),
1✔
395
            Aggregation::Last {
396
                ignore_no_data: true,
397
            } => Self::create_subquery(
1✔
398
                super::subquery::subquery_all_tiles_fold_fn::<P, LastPixelAggregatorIngoringNoData>,
1✔
399
            )
1✔
400
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
1✔
401
            .box_pin(),
1✔
402
            Aggregation::Last {
403
                ignore_no_data: false,
404
            } => Self::create_subquery_last(last_tile_fold_future::<P>)
2✔
405
                .into_raster_subquery_adapter(
2✔
406
                    &self.source,
2✔
407
                    query,
2✔
408
                    ctx,
2✔
409
                    tiling_strategy,
2✔
410
                    time_stream,
2✔
411
                )
2✔
412
                .box_pin(),
2✔
413
            Aggregation::Mean {
414
                ignore_no_data: true,
415
            } => Self::create_subquery(
1✔
416
                super::subquery::subquery_all_tiles_fold_fn::<P, MeanPixelAggregator<true>>,
1✔
417
            )
1✔
418
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
1✔
419
            .box_pin(),
1✔
420
            Aggregation::Mean {
421
                ignore_no_data: false,
422
            } => Self::create_subquery(
1✔
423
                super::subquery::subquery_all_tiles_fold_fn::<P, MeanPixelAggregator<false>>,
1✔
424
            )
1✔
425
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
1✔
426
            .box_pin(),
1✔
427

428
            Aggregation::Sum {
429
                ignore_no_data: true,
430
            } => Self::create_subquery(
1✔
431
                super::subquery::subquery_all_tiles_fold_fn::<P, SumPixelAggregatorIngoringNoData>,
1✔
432
            )
1✔
433
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
1✔
434
            .box_pin(),
1✔
435
            Aggregation::Sum {
436
                ignore_no_data: false,
437
            } => Self::create_subquery(
5✔
438
                super::subquery::subquery_all_tiles_fold_fn::<P, SumPixelAggregator>,
5✔
439
            )
5✔
440
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
5✔
441
            .box_pin(),
5✔
442

443
            Aggregation::Count {
444
                ignore_no_data: true,
445
            } => {
446
                Self::create_subquery(
1✔
447
                    super::subquery::subquery_all_tiles_fold_fn::<
1✔
448
                        P,
1✔
449
                        CountPixelAggregatorIngoringNoData,
1✔
450
                    >,
1✔
451
                )
1✔
452
                .into_raster_subquery_adapter(
1✔
453
                    &self.source,
1✔
454
                    query,
1✔
455
                    ctx,
1✔
456
                    tiling_strategy,
1✔
457
                    time_stream,
1✔
458
                )
1✔
459
                .box_pin()
1✔
460
            }
461
            Aggregation::Count {
462
                ignore_no_data: false,
463
            } => Self::create_subquery(
2✔
464
                super::subquery::subquery_all_tiles_fold_fn::<P, CountPixelAggregator>,
2✔
465
            )
2✔
466
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
2✔
467
            .box_pin(),
2✔
468
            Aggregation::PercentileEstimate {
469
                ignore_no_data: true,
470
                percentile,
×
471
            } => Self::create_global_state_subquery(
×
472
                PercentileEstimateAggregator::<true>::new(percentile),
×
473
                super::subquery::subquery_all_tiles_global_state_fold_fn,
×
474
            )
×
475
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
×
476
            .box_pin(),
×
477
            Aggregation::PercentileEstimate {
478
                ignore_no_data: false,
479
                percentile,
1✔
480
            } => Self::create_global_state_subquery(
1✔
481
                PercentileEstimateAggregator::<false>::new(percentile),
1✔
482
                super::subquery::subquery_all_tiles_global_state_fold_fn,
1✔
483
            )
1✔
484
            .into_raster_subquery_adapter(&self.source, query, ctx, tiling_strategy, time_stream)
1✔
485
            .box_pin(),
1✔
486
        })
487
    }
22✔
488
}
489

490
#[async_trait]
491
impl<Q, P> QueryProcessor for TemporalRasterAggregationProcessor<Q, P>
492
where
493
    Q: RasterQueryProcessor<RasterType = P> + Send + Sync,
494
    P: Pixel,
495
{
496
    type Output = RasterTile2D<P>;
497
    type SpatialBounds = GridBoundingBox2D;
498
    type Selection = BandSelection;
499
    type ResultDescription = RasterResultDescriptor;
500

501
    async fn _query<'a>(
502
        &'a self,
503
        query: RasterQueryRectangle,
504
        ctx: &'a dyn crate::engine::QueryContext,
505
    ) -> Result<futures::stream::BoxStream<'a, Result<Self::Output>>> {
21✔
506
        stack_individual_aligned_raster_bands(&query, ctx, |query, ctx| async {
22✔
507
            self.create_subquery_adapter_stream_for_single_band(query, ctx)
22✔
508
                .await
22✔
509
        })
44✔
510
        .await
511
    }
21✔
512

513
    fn result_descriptor(&self) -> &Self::ResultDescription {
64✔
514
        &self.result_descriptor
64✔
515
    }
64✔
516
}
517

518
#[async_trait]
519
impl<Q, P> RasterQueryProcessor for TemporalRasterAggregationProcessor<Q, P>
520
where
521
    Q: RasterQueryProcessor<RasterType = P>,
522
    P: Pixel,
523
{
524
    type RasterType = P;
525

526
    async fn _time_query<'a>(
527
        &'a self,
528
        query: geoengine_datatypes::primitives::TimeInterval,
529
        _ctx: &'a dyn crate::engine::QueryContext,
530
    ) -> Result<futures::stream::BoxStream<'a, Result<geoengine_datatypes::primitives::TimeInterval>>>
531
    {
×
532
        let ti_iter = self
533
            .result_descriptor
534
            .time
535
            .dimension
536
            .unwrap_regular()
537
            .expect("must be regular as this operator only creates regular time dimensions")
538
            .intersecting_intervals(query.time())?
539
            .map(Ok);
540
        Ok(futures::stream::iter(ti_iter).boxed())
541
    }
×
542
}
543

544
#[cfg(test)]
545
mod tests {
546
    use super::*;
547
    use crate::{
548
        engine::{
549
            MockExecutionContext, MultipleRasterSources, RasterBandDescriptors,
550
            SpatialGridDescriptor, TimeDescriptor,
551
        },
552
        mock::{MockRasterSource, MockRasterSourceParams},
553
        processing::{
554
            Expression, ExpressionParams,
555
            raster_stacker::{RasterStacker, RasterStackerParams},
556
        },
557
    };
558
    use futures::stream::StreamExt;
559
    use geoengine_datatypes::{
560
        primitives::{CacheHint, Coordinate2D, TimeInterval},
561
        raster::{
562
            EmptyGrid, EmptyGrid2D, GeoTransform, Grid2D, GridBoundingBox2D, GridOrEmpty,
563
            GridShape2D, MaskedGrid2D, RasterDataType, RenameBands, TileInformation,
564
            TilesEqualIgnoringCacheHint,
565
        },
566
        spatial_reference::SpatialReference,
567
        util::test::TestDefault,
568
    };
569

570
    #[tokio::test]
571
    #[allow(clippy::too_many_lines)]
572
    async fn test_min() {
1✔
573
        let raster_tiles = make_raster();
1✔
574

575
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
576
        let result_descriptor = RasterResultDescriptor {
1✔
577
            data_type: RasterDataType::U8,
1✔
578
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
579
            time: TimeDescriptor::new_regular_with_epoch(
1✔
580
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
581
                TimeStep::millis(10).unwrap(),
1✔
582
            ),
1✔
583
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
584
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
585
                GridBoundingBox2D::new([-3, 0], [-1, 2]).unwrap(),
1✔
586
            ),
1✔
587
            bands: RasterBandDescriptors::new_single_band(),
1✔
588
        };
1✔
589
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
590

591
        let mrs = MockRasterSource {
1✔
592
            params: MockRasterSourceParams {
1✔
593
                data: raster_tiles,
1✔
594
                result_descriptor,
1✔
595
            },
1✔
596
        }
1✔
597
        .boxed();
1✔
598

599
        let agg = TemporalRasterAggregation {
1✔
600
            params: TemporalRasterAggregationParameters {
1✔
601
                aggregation: Aggregation::Min {
1✔
602
                    ignore_no_data: false,
1✔
603
                },
1✔
604
                window: TimeStep {
1✔
605
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
606
                    step: 20,
1✔
607
                },
1✔
608
                window_reference: None,
1✔
609
                output_type: None,
1✔
610
            },
1✔
611
            sources: SingleRasterSource { raster: mrs },
1✔
612
        }
1✔
613
        .boxed();
1✔
614

615
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
616
        let query_rect = RasterQueryRectangle::new(
1✔
617
            GridBoundingBox2D::new([-3, 0], [-1, 3]).unwrap(),
1✔
618
            TimeInterval::new_unchecked(0, 40),
1✔
619
            BandSelection::first(),
1✔
620
        );
621
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
622

623
        let qp = agg
1✔
624
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
625
            .await
1✔
626
            .unwrap()
1✔
627
            .query_processor()
1✔
628
            .unwrap()
1✔
629
            .get_u8()
1✔
630
            .unwrap();
1✔
631

632
        let result = qp
1✔
633
            .query(query_rect, &query_ctx)
1✔
634
            .await
1✔
635
            .unwrap()
1✔
636
            .collect::<Vec<_>>()
1✔
637
            .await;
1✔
638

639
        assert_eq!(result.len(), 4);
1✔
640

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

655
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
656
            &RasterTile2D::new_with_tile_info(
1✔
657
                TimeInterval::new_unchecked(0, 20),
1✔
658
                TileInformation {
1✔
659
                    global_tile_position: [-1, 1].into(),
1✔
660
                    tile_size_in_pixels: [3, 2].into(),
1✔
661
                    global_geo_transform: TestDefault::test_default(),
1✔
662
                },
1✔
663
                0,
1✔
664
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
1✔
665
                CacheHint::default()
1✔
666
            )
1✔
667
        ));
668

669
        assert!(result[2].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
670
            &RasterTile2D::new_with_tile_info(
1✔
671
                TimeInterval::new_unchecked(20, 40),
1✔
672
                TileInformation {
1✔
673
                    global_tile_position: [-1, 0].into(),
1✔
674
                    tile_size_in_pixels: [3, 2].into(),
1✔
675
                    global_geo_transform: TestDefault::test_default(),
1✔
676
                },
1✔
677
                0,
1✔
678
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
679
                CacheHint::default()
1✔
680
            )
1✔
681
        ));
682

683
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
684
            &RasterTile2D::new_with_tile_info(
1✔
685
                TimeInterval::new_unchecked(20, 40),
1✔
686
                TileInformation {
1✔
687
                    global_tile_position: [-1, 1].into(),
1✔
688
                    tile_size_in_pixels: [3, 2].into(),
1✔
689
                    global_geo_transform: TestDefault::test_default(),
1✔
690
                },
1✔
691
                0,
1✔
692
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
1✔
693
                CacheHint::default()
1✔
694
            )
1✔
695
        ));
1✔
696
    }
1✔
697

698
    #[tokio::test]
699
    #[allow(clippy::too_many_lines)]
700
    async fn test_max() {
1✔
701
        let raster_tiles = make_raster();
1✔
702

703
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
704
        let result_descriptor = RasterResultDescriptor {
1✔
705
            data_type: RasterDataType::U8,
1✔
706
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
707
            time: TimeDescriptor::new_regular_with_epoch(
1✔
708
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
709
                TimeStep::millis(10).unwrap(),
1✔
710
            ),
1✔
711
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
712
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
713
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
714
            ),
1✔
715
            bands: RasterBandDescriptors::new_single_band(),
1✔
716
        };
1✔
717
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
718

719
        let mrs = MockRasterSource {
1✔
720
            params: MockRasterSourceParams {
1✔
721
                data: raster_tiles,
1✔
722
                result_descriptor,
1✔
723
            },
1✔
724
        }
1✔
725
        .boxed();
1✔
726

727
        let agg = TemporalRasterAggregation {
1✔
728
            params: TemporalRasterAggregationParameters {
1✔
729
                aggregation: Aggregation::Max {
1✔
730
                    ignore_no_data: false,
1✔
731
                },
1✔
732
                window: TimeStep {
1✔
733
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
734
                    step: 20,
1✔
735
                },
1✔
736
                window_reference: None,
1✔
737
                output_type: None,
1✔
738
            },
1✔
739
            sources: SingleRasterSource { raster: mrs },
1✔
740
        }
1✔
741
        .boxed();
1✔
742

743
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
744
        let query_rect = RasterQueryRectangle::new(
1✔
745
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
746
            TimeInterval::new_unchecked(0, 40),
1✔
747
            BandSelection::first(),
1✔
748
        );
749
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
750

751
        let qp = agg
1✔
752
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
753
            .await
1✔
754
            .unwrap()
1✔
755
            .query_processor()
1✔
756
            .unwrap()
1✔
757
            .get_u8()
1✔
758
            .unwrap();
1✔
759

760
        let result = qp
1✔
761
            .query(query_rect, &query_ctx)
1✔
762
            .await
1✔
763
            .unwrap()
1✔
764
            .collect::<Vec<_>>()
1✔
765
            .await;
1✔
766

767
        assert_eq!(result.len(), 4);
1✔
768

769
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
770
            &RasterTile2D::new_with_tile_info(
1✔
771
                TimeInterval::new_unchecked(0, 20),
1✔
772
                TileInformation {
1✔
773
                    global_tile_position: [-1, 0].into(),
1✔
774
                    tile_size_in_pixels: [3, 2].into(),
1✔
775
                    global_geo_transform: TestDefault::test_default(),
1✔
776
                },
1✔
777
                0,
1✔
778
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
779
                CacheHint::default()
1✔
780
            )
1✔
781
        ));
782

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

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

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

826
    #[tokio::test]
827
    #[allow(clippy::too_many_lines)]
828
    async fn test_max_with_no_data() {
1✔
829
        let raster_tiles = make_raster(); // TODO: switch to make_raster_with_no_data?
1✔
830

831
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
832
        let result_descriptor = RasterResultDescriptor {
1✔
833
            data_type: RasterDataType::U8,
1✔
834
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
835
            time: TimeDescriptor::new_regular_with_epoch(
1✔
836
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
837
                TimeStep::millis(10).unwrap(),
1✔
838
            ),
1✔
839
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
840
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
841
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
842
            ),
1✔
843
            bands: RasterBandDescriptors::new_single_band(),
1✔
844
        };
1✔
845
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
846

847
        let mrs = MockRasterSource {
1✔
848
            params: MockRasterSourceParams {
1✔
849
                data: raster_tiles,
1✔
850
                result_descriptor,
1✔
851
            },
1✔
852
        }
1✔
853
        .boxed();
1✔
854

855
        let agg = TemporalRasterAggregation {
1✔
856
            params: TemporalRasterAggregationParameters {
1✔
857
                aggregation: Aggregation::Max {
1✔
858
                    ignore_no_data: false,
1✔
859
                },
1✔
860
                window: TimeStep {
1✔
861
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
862
                    step: 20,
1✔
863
                },
1✔
864
                window_reference: None,
1✔
865
                output_type: None,
1✔
866
            },
1✔
867
            sources: SingleRasterSource { raster: mrs },
1✔
868
        }
1✔
869
        .boxed();
1✔
870

871
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
872
        let query_rect = RasterQueryRectangle::new(
1✔
873
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
874
            TimeInterval::new_unchecked(0, 40),
1✔
875
            BandSelection::first(),
1✔
876
        );
877
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
878

879
        let qp = agg
1✔
880
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
881
            .await
1✔
882
            .unwrap()
1✔
883
            .query_processor()
1✔
884
            .unwrap()
1✔
885
            .get_u8()
1✔
886
            .unwrap();
1✔
887

888
        let result = qp
1✔
889
            .query(query_rect, &query_ctx)
1✔
890
            .await
1✔
891
            .unwrap()
1✔
892
            .collect::<Vec<_>>()
1✔
893
            .await;
1✔
894

895
        assert_eq!(result.len(), 4);
1✔
896

897
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
898
            &RasterTile2D::new_with_tile_info(
1✔
899
                TimeInterval::new_unchecked(0, 20),
1✔
900
                TileInformation {
1✔
901
                    global_tile_position: [-1, 0].into(),
1✔
902
                    tile_size_in_pixels: [3, 2].into(),
1✔
903
                    global_geo_transform: TestDefault::test_default(),
1✔
904
                },
1✔
905
                0,
1✔
906
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7],).unwrap()),
1✔
907
                CacheHint::default()
1✔
908
            )
1✔
909
        ));
910

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

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

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

954
    #[tokio::test]
955
    #[allow(clippy::too_many_lines)]
956
    async fn test_max_with_no_data_but_ignoring_it() {
1✔
957
        let raster_tiles = make_raster(); // TODO: switch to make_raster_with_no_data?
1✔
958

959
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
960
        let result_descriptor = RasterResultDescriptor {
1✔
961
            data_type: RasterDataType::U8,
1✔
962
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
963
            time: TimeDescriptor::new_regular_with_epoch(
1✔
964
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
965
                TimeStep::millis(10).unwrap(),
1✔
966
            ),
1✔
967
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
968
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
969
                GridBoundingBox2D::new([-3, 0], [-1, 3]).unwrap(),
1✔
970
            ),
1✔
971
            bands: RasterBandDescriptors::new_single_band(),
1✔
972
        };
1✔
973
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
974

975
        let mrs = MockRasterSource {
1✔
976
            params: MockRasterSourceParams {
1✔
977
                data: raster_tiles,
1✔
978
                result_descriptor,
1✔
979
            },
1✔
980
        }
1✔
981
        .boxed();
1✔
982

983
        let agg = TemporalRasterAggregation {
1✔
984
            params: TemporalRasterAggregationParameters {
1✔
985
                aggregation: Aggregation::Max {
1✔
986
                    ignore_no_data: true,
1✔
987
                },
1✔
988
                window: TimeStep {
1✔
989
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
990
                    step: 20,
1✔
991
                },
1✔
992
                window_reference: None,
1✔
993
                output_type: None,
1✔
994
            },
1✔
995
            sources: SingleRasterSource { raster: mrs },
1✔
996
        }
1✔
997
        .boxed();
1✔
998

999
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
1000
        let query_rect = RasterQueryRectangle::new(
1✔
1001
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1002
            TimeInterval::new_unchecked(0, 40),
1✔
1003
            BandSelection::first(),
1✔
1004
        );
1005
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
1006

1007
        let qp = agg
1✔
1008
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1009
            .await
1✔
1010
            .unwrap()
1✔
1011
            .query_processor()
1✔
1012
            .unwrap()
1✔
1013
            .get_u8()
1✔
1014
            .unwrap();
1✔
1015

1016
        let result = qp
1✔
1017
            .query(query_rect, &query_ctx)
1✔
1018
            .await
1✔
1019
            .unwrap()
1✔
1020
            .collect::<Vec<_>>()
1✔
1021
            .await;
1✔
1022

1023
        assert_eq!(result.len(), 4);
1✔
1024

1025
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1026
            &RasterTile2D::new_with_tile_info(
1✔
1027
                TimeInterval::new_unchecked(0, 20),
1✔
1028
                TileInformation {
1✔
1029
                    global_tile_position: [-1, 0].into(),
1✔
1030
                    tile_size_in_pixels: [3, 2].into(),
1✔
1031
                    global_geo_transform: TestDefault::test_default(),
1✔
1032
                },
1✔
1033
                0,
1✔
1034
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
1035
                CacheHint::default()
1✔
1036
            )
1✔
1037
        ));
1038

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

1053
        assert!(result[2].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1054
            &RasterTile2D::new_with_tile_info(
1✔
1055
                TimeInterval::new_unchecked(20, 40),
1✔
1056
                TileInformation {
1✔
1057
                    global_tile_position: [-1, 0].into(),
1✔
1058
                    tile_size_in_pixels: [3, 2].into(),
1✔
1059
                    global_geo_transform: TestDefault::test_default(),
1✔
1060
                },
1✔
1061
                0,
1✔
1062
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
1063
                CacheHint::default()
1✔
1064
            )
1✔
1065
        ));
1066

1067
        assert!(result[3].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1068
            &RasterTile2D::new_with_tile_info(
1✔
1069
                TimeInterval::new_unchecked(20, 40),
1✔
1070
                TileInformation {
1✔
1071
                    global_tile_position: [-1, 1].into(),
1✔
1072
                    tile_size_in_pixels: [3, 2].into(),
1✔
1073
                    global_geo_transform: TestDefault::test_default(),
1✔
1074
                },
1✔
1075
                0,
1✔
1076
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
1077
                CacheHint::default()
1✔
1078
            )
1✔
1079
        ));
1✔
1080
    }
1✔
1081

1082
    #[tokio::test]
1083
    #[allow(clippy::too_many_lines)]
1084
    async fn test_only_no_data() {
1✔
1085
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
1086
        let result_descriptor = RasterResultDescriptor {
1✔
1087
            data_type: RasterDataType::U8,
1✔
1088
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1089
            time: TimeDescriptor::new_regular_with_epoch(
1✔
1090
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
1091
                TimeStep::millis(10).unwrap(),
1✔
1092
            ),
1✔
1093
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
1094
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
1095
                GridBoundingBox2D::new_min_max(-3, -1, 0, 2).unwrap(),
1✔
1096
            ),
1✔
1097
            bands: RasterBandDescriptors::new_single_band(),
1✔
1098
        };
1✔
1099
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
1100

1101
        let mrs = MockRasterSource {
1✔
1102
            params: MockRasterSourceParams {
1✔
1103
                data: vec![RasterTile2D::new_with_tile_info(
1✔
1104
                    TimeInterval::new_unchecked(0, 20),
1✔
1105
                    TileInformation {
1✔
1106
                        global_tile_position: [-1, 0].into(),
1✔
1107
                        tile_size_in_pixels: [3, 2].into(),
1✔
1108
                        global_geo_transform: TestDefault::test_default(),
1✔
1109
                    },
1✔
1110
                    0,
1✔
1111
                    GridOrEmpty::from(EmptyGrid2D::<u8>::new([3, 2].into())),
1✔
1112
                    CacheHint::default(),
1✔
1113
                )],
1✔
1114
                result_descriptor,
1✔
1115
            },
1✔
1116
        }
1✔
1117
        .boxed();
1✔
1118

1119
        let agg = TemporalRasterAggregation {
1✔
1120
            params: TemporalRasterAggregationParameters {
1✔
1121
                aggregation: Aggregation::Max {
1✔
1122
                    ignore_no_data: false,
1✔
1123
                },
1✔
1124
                window: TimeStep {
1✔
1125
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1126
                    step: 20,
1✔
1127
                },
1✔
1128
                window_reference: None,
1✔
1129
                output_type: None,
1✔
1130
            },
1✔
1131
            sources: SingleRasterSource { raster: mrs },
1✔
1132
        }
1✔
1133
        .boxed();
1✔
1134

1135
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
1136
        let query_rect = RasterQueryRectangle::new(
1✔
1137
            GridBoundingBox2D::new_min_max(-3, -1, 0, 1).unwrap(),
1✔
1138
            TimeInterval::new_unchecked(0, 20),
1✔
1139
            BandSelection::first(),
1✔
1140
        );
1141
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
1142

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

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

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

1161
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1162
            &RasterTile2D::new_with_tile_info(
1✔
1163
                TimeInterval::new_unchecked(0, 20),
1✔
1164
                TileInformation {
1✔
1165
                    global_tile_position: [-1, 0].into(),
1✔
1166
                    tile_size_in_pixels: [3, 2].into(),
1✔
1167
                    global_geo_transform: TestDefault::test_default(),
1✔
1168
                },
1✔
1169
                0,
1✔
1170
                GridOrEmpty::Empty(EmptyGrid::new([3, 2].into())),
1✔
1171
                CacheHint::default()
1✔
1172
            )
1✔
1173
        ));
1✔
1174
    }
1✔
1175

1176
    #[tokio::test]
1177
    async fn test_first_with_no_data() {
1✔
1178
        let raster_tiles = make_raster_with_no_data();
1✔
1179

1180
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
1181
        let result_descriptor = RasterResultDescriptor {
1✔
1182
            data_type: RasterDataType::U8,
1✔
1183
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1184
            time: TimeDescriptor::new_regular_with_epoch(
1✔
1185
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
1186
                TimeStep::millis(10).unwrap(),
1✔
1187
            ),
1✔
1188
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
1189
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
1190
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1191
            ),
1✔
1192
            bands: RasterBandDescriptors::new_single_band(),
1✔
1193
        };
1✔
1194
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
1195

1196
        let mrs = MockRasterSource {
1✔
1197
            params: MockRasterSourceParams {
1✔
1198
                data: raster_tiles,
1✔
1199
                result_descriptor,
1✔
1200
            },
1✔
1201
        }
1✔
1202
        .boxed();
1✔
1203

1204
        let agg = TemporalRasterAggregation {
1✔
1205
            params: TemporalRasterAggregationParameters {
1✔
1206
                aggregation: Aggregation::First {
1✔
1207
                    ignore_no_data: true,
1✔
1208
                },
1✔
1209
                window: TimeStep {
1✔
1210
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1211
                    step: 30,
1✔
1212
                },
1✔
1213
                window_reference: None,
1✔
1214
                output_type: None,
1✔
1215
            },
1✔
1216
            sources: SingleRasterSource { raster: mrs },
1✔
1217
        }
1✔
1218
        .boxed();
1✔
1219

1220
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
1221
        let query_rect = RasterQueryRectangle::new(
1✔
1222
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1223
            TimeInterval::new_unchecked(0, 30),
1✔
1224
            BandSelection::first(),
1✔
1225
        );
1226
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
1227

1228
        let qp = agg
1✔
1229
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1230
            .await
1✔
1231
            .unwrap()
1✔
1232
            .query_processor()
1✔
1233
            .unwrap()
1✔
1234
            .get_u8()
1✔
1235
            .unwrap();
1✔
1236

1237
        let result = qp
1✔
1238
            .query(query_rect, &query_ctx)
1✔
1239
            .await
1✔
1240
            .unwrap()
1✔
1241
            .collect::<Vec<_>>()
1✔
1242
            .await;
1✔
1243

1244
        assert_eq!(result.len(), 2);
1✔
1245

1246
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1247
            &RasterTile2D::new_with_tile_info(
1✔
1248
                TimeInterval::new_unchecked(0, 30),
1✔
1249
                TileInformation {
1✔
1250
                    global_tile_position: [-1, 0].into(),
1✔
1251
                    tile_size_in_pixels: [3, 2].into(),
1✔
1252
                    global_geo_transform: TestDefault::test_default(),
1✔
1253
                },
1✔
1254
                0,
1✔
1255
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 16, 11, 12]).unwrap()),
1✔
1256
                CacheHint::default()
1✔
1257
            )
1✔
1258
        ));
1259

1260
        assert!(
1✔
1261
            result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1262
                &RasterTile2D::new_with_tile_info(
1✔
1263
                    TimeInterval::new_unchecked(0, 30),
1✔
1264
                    TileInformation {
1✔
1265
                        global_tile_position: [-1, 1].into(),
1✔
1266
                        tile_size_in_pixels: [3, 2].into(),
1✔
1267
                        global_geo_transform: TestDefault::test_default(),
1✔
1268
                    },
1✔
1269
                    0,
1✔
1270
                    GridOrEmpty::from(
1✔
1271
                        MaskedGrid2D::new(
1✔
1272
                            Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1273
                            Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1274
                                .unwrap()
1✔
1275
                        )
1✔
1276
                        .unwrap()
1✔
1277
                    ),
1✔
1278
                    CacheHint::default()
1✔
1279
                )
1✔
1280
            )
1✔
1281
        );
1✔
1282
    }
1✔
1283

1284
    #[tokio::test]
1285
    async fn test_last_with_no_data() {
1✔
1286
        let raster_tiles = make_raster_with_no_data();
1✔
1287

1288
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
1289
        let result_descriptor = RasterResultDescriptor {
1✔
1290
            data_type: RasterDataType::U8,
1✔
1291
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1292
            time: TimeDescriptor::new_regular_with_epoch(
1✔
1293
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
1294
                TimeStep::millis(10).unwrap(),
1✔
1295
            ),
1✔
1296
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
1297
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
1298
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1299
            ),
1✔
1300
            bands: RasterBandDescriptors::new_single_band(),
1✔
1301
        };
1✔
1302
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
1303

1304
        let mrs = MockRasterSource {
1✔
1305
            params: MockRasterSourceParams {
1✔
1306
                data: raster_tiles,
1✔
1307
                result_descriptor,
1✔
1308
            },
1✔
1309
        }
1✔
1310
        .boxed();
1✔
1311
        let agg = TemporalRasterAggregation {
1✔
1312
            params: TemporalRasterAggregationParameters {
1✔
1313
                aggregation: Aggregation::Last {
1✔
1314
                    ignore_no_data: true,
1✔
1315
                },
1✔
1316
                window: TimeStep {
1✔
1317
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1318
                    step: 30,
1✔
1319
                },
1✔
1320
                window_reference: None,
1✔
1321
                output_type: None,
1✔
1322
            },
1✔
1323
            sources: SingleRasterSource { raster: mrs },
1✔
1324
        }
1✔
1325
        .boxed();
1✔
1326

1327
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
1328
        let query_rect = RasterQueryRectangle::new(
1✔
1329
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1330
            TimeInterval::new_unchecked(0, 30),
1✔
1331
            BandSelection::first(),
1✔
1332
        );
1333
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
1334

1335
        let qp = agg
1✔
1336
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1337
            .await
1✔
1338
            .unwrap()
1✔
1339
            .query_processor()
1✔
1340
            .unwrap()
1✔
1341
            .get_u8()
1✔
1342
            .unwrap();
1✔
1343

1344
        let result = qp
1✔
1345
            .query(query_rect, &query_ctx)
1✔
1346
            .await
1✔
1347
            .unwrap()
1✔
1348
            .collect::<Vec<_>>()
1✔
1349
            .await;
1✔
1350

1351
        assert_eq!(result.len(), 2);
1✔
1352

1353
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1354
            &RasterTile2D::new_with_tile_info(
1✔
1355
                TimeInterval::new_unchecked(0, 30),
1✔
1356
                TileInformation {
1✔
1357
                    global_tile_position: [-1, 0].into(),
1✔
1358
                    tile_size_in_pixels: [3, 2].into(),
1✔
1359
                    global_geo_transform: TestDefault::test_default(),
1✔
1360
                },
1✔
1361
                0,
1✔
1362
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![13, 8, 15, 16, 17, 18]).unwrap()),
1✔
1363
                CacheHint::default()
1✔
1364
            )
1✔
1365
        ));
1366

1367
        assert!(
1✔
1368
            result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1369
                &RasterTile2D::new_with_tile_info(
1✔
1370
                    TimeInterval::new_unchecked(0, 30),
1✔
1371
                    TileInformation {
1✔
1372
                        global_tile_position: [-1, 1].into(),
1✔
1373
                        tile_size_in_pixels: [3, 2].into(),
1✔
1374
                        global_geo_transform: TestDefault::test_default(),
1✔
1375
                    },
1✔
1376
                    0,
1✔
1377
                    GridOrEmpty::from(
1✔
1378
                        MaskedGrid2D::new(
1✔
1379
                            Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1380
                            Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1381
                                .unwrap()
1✔
1382
                        )
1✔
1383
                        .unwrap()
1✔
1384
                    ),
1✔
1385
                    CacheHint::default()
1✔
1386
                )
1✔
1387
            )
1✔
1388
        );
1✔
1389
    }
1✔
1390

1391
    #[tokio::test]
1392
    async fn test_last() {
1✔
1393
        let raster_tiles = make_raster_with_no_data();
1✔
1394

1395
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
1396
        let result_descriptor = RasterResultDescriptor {
1✔
1397
            data_type: RasterDataType::U8,
1✔
1398
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1399
            time: TimeDescriptor::new_regular_with_epoch(
1✔
1400
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
1401
                TimeStep::millis(10).unwrap(),
1✔
1402
            ),
1✔
1403
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
1404
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
1405
                GridBoundingBox2D::new([-3, 0], [-1, 3]).unwrap(),
1✔
1406
            ),
1✔
1407
            bands: RasterBandDescriptors::new_single_band(),
1✔
1408
        };
1✔
1409
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
1410

1411
        let mrs = MockRasterSource {
1✔
1412
            params: MockRasterSourceParams {
1✔
1413
                data: raster_tiles,
1✔
1414
                result_descriptor,
1✔
1415
            },
1✔
1416
        }
1✔
1417
        .boxed();
1✔
1418

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

1435
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
1436
        let query_rect = RasterQueryRectangle::new(
1✔
1437
            GridBoundingBox2D::new([-3, 0], [-1, 3]).unwrap(),
1✔
1438
            TimeInterval::new_unchecked(0, 30),
1✔
1439
            BandSelection::first(),
1✔
1440
        );
1441
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
1442

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

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

1459
        assert_eq!(result.len(), 2);
1✔
1460

1461
        assert!(
1✔
1462
            result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1463
                &RasterTile2D::new_with_tile_info(
1✔
1464
                    TimeInterval::new_unchecked(0, 30),
1✔
1465
                    TileInformation {
1✔
1466
                        global_tile_position: [-1, 0].into(),
1✔
1467
                        tile_size_in_pixels: [3, 2].into(),
1✔
1468
                        global_geo_transform: TestDefault::test_default(),
1✔
1469
                    },
1✔
1470
                    0,
1✔
1471
                    GridOrEmpty::from(
1✔
1472
                        MaskedGrid2D::new(
1✔
1473
                            Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
1✔
1474
                            Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
1✔
1475
                                .unwrap()
1✔
1476
                        )
1✔
1477
                        .unwrap()
1✔
1478
                    ),
1✔
1479
                    CacheHint::default()
1✔
1480
                )
1✔
1481
            )
1482
        );
1483

1484
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1485
            &RasterTile2D::new_with_tile_info(
1✔
1486
                TimeInterval::new_unchecked(0, 30),
1✔
1487
                TileInformation {
1✔
1488
                    global_tile_position: [-1, 1].into(),
1✔
1489
                    tile_size_in_pixels: [3, 2].into(),
1✔
1490
                    global_geo_transform: TestDefault::test_default(),
1✔
1491
                },
1✔
1492
                0,
1✔
1493
                GridOrEmpty::Empty(EmptyGrid2D::new([3, 2].into())),
1✔
1494
                CacheHint::default()
1✔
1495
            )
1✔
1496
        ));
1✔
1497
    }
1✔
1498

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

1503
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
1504
        let result_descriptor = RasterResultDescriptor {
1✔
1505
            data_type: RasterDataType::U8,
1✔
1506
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1507
            time: TimeDescriptor::new_regular_with_epoch(
1✔
1508
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
1509
                TimeStep::millis(10).unwrap(),
1✔
1510
            ),
1✔
1511
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
1512
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
1513
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1514
            ),
1✔
1515
            bands: RasterBandDescriptors::new_single_band(),
1✔
1516
        };
1✔
1517
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
1518

1519
        let mrs = MockRasterSource {
1✔
1520
            params: MockRasterSourceParams {
1✔
1521
                data: raster_tiles,
1✔
1522
                result_descriptor,
1✔
1523
            },
1✔
1524
        }
1✔
1525
        .boxed();
1✔
1526

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

1543
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
1544
        let query_rect = RasterQueryRectangle::new(
1✔
1545
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1546
            TimeInterval::new_unchecked(0, 30),
1✔
1547
            BandSelection::first(),
1✔
1548
        );
1549
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
1550

1551
        let qp = agg
1✔
1552
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1553
            .await
1✔
1554
            .unwrap()
1✔
1555
            .query_processor()
1✔
1556
            .unwrap()
1✔
1557
            .get_u8()
1✔
1558
            .unwrap();
1✔
1559

1560
        let result = qp
1✔
1561
            .query(query_rect, &query_ctx)
1✔
1562
            .await
1✔
1563
            .unwrap()
1✔
1564
            .collect::<Vec<_>>()
1✔
1565
            .await;
1✔
1566

1567
        assert_eq!(result.len(), 2);
1✔
1568

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

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

1607
    #[tokio::test]
1608
    async fn test_mean_nodata() {
1✔
1609
        let raster_tiles = make_raster_with_no_data();
1✔
1610

1611
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
1612
        let result_descriptor = RasterResultDescriptor {
1✔
1613
            data_type: RasterDataType::U8,
1✔
1614
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1615
            time: TimeDescriptor::new_regular_with_epoch(
1✔
1616
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
1617
                TimeStep::millis(10).unwrap(),
1✔
1618
            ),
1✔
1619
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
1620
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
1621
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1622
            ),
1✔
1623
            bands: RasterBandDescriptors::new_single_band(),
1✔
1624
        };
1✔
1625
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
1626

1627
        let mrs = MockRasterSource {
1✔
1628
            params: MockRasterSourceParams {
1✔
1629
                data: raster_tiles,
1✔
1630
                result_descriptor,
1✔
1631
            },
1✔
1632
        }
1✔
1633
        .boxed();
1✔
1634

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

1651
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
1652
        let query_rect = RasterQueryRectangle::new(
1✔
1653
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1654
            TimeInterval::new_unchecked(0, 30),
1✔
1655
            BandSelection::first(),
1✔
1656
        );
1657
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
1658

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

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

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

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

1691
        assert!(
1✔
1692
            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
    }
1✔
1714

1715
    #[tokio::test]
1716
    async fn test_mean_ignore_no_data() {
1✔
1717
        let raster_tiles = make_raster_with_no_data();
1✔
1718

1719
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
1720
        let result_descriptor = RasterResultDescriptor {
1✔
1721
            data_type: RasterDataType::U8,
1✔
1722
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1723
            time: TimeDescriptor::new_regular_with_epoch(
1✔
1724
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
1725
                TimeStep::millis(10).unwrap(),
1✔
1726
            ),
1✔
1727
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
1728
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
1729
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1730
            ),
1✔
1731
            bands: RasterBandDescriptors::new_single_band(),
1✔
1732
        };
1✔
1733
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
1734

1735
        let mrs = MockRasterSource {
1✔
1736
            params: MockRasterSourceParams {
1✔
1737
                data: raster_tiles,
1✔
1738
                result_descriptor,
1✔
1739
            },
1✔
1740
        }
1✔
1741
        .boxed();
1✔
1742

1743
        let agg = TemporalRasterAggregation {
1✔
1744
            params: TemporalRasterAggregationParameters {
1✔
1745
                aggregation: Aggregation::Mean {
1✔
1746
                    ignore_no_data: true,
1✔
1747
                },
1✔
1748
                window: TimeStep {
1✔
1749
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1750
                    step: 30,
1✔
1751
                },
1✔
1752
                window_reference: None,
1✔
1753
                output_type: None,
1✔
1754
            },
1✔
1755
            sources: SingleRasterSource { raster: mrs },
1✔
1756
        }
1✔
1757
        .boxed();
1✔
1758

1759
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
1760
        let query_rect = RasterQueryRectangle::new(
1✔
1761
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1762
            TimeInterval::new_unchecked(0, 30),
1✔
1763
            BandSelection::first(),
1✔
1764
        );
1765
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
1766

1767
        let qp = agg
1✔
1768
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1769
            .await
1✔
1770
            .unwrap()
1✔
1771
            .query_processor()
1✔
1772
            .unwrap()
1✔
1773
            .get_u8()
1✔
1774
            .unwrap();
1✔
1775

1776
        let result = qp
1✔
1777
            .raster_query(query_rect, &query_ctx)
1✔
1778
            .await
1✔
1779
            .unwrap()
1✔
1780
            .collect::<Vec<_>>()
1✔
1781
            .await;
1✔
1782

1783
        assert_eq!(result.len(), 2);
1✔
1784

1785
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1786
            &RasterTile2D::new_with_tile_info(
1✔
1787
                TimeInterval::new_unchecked(0, 30),
1✔
1788
                TileInformation {
1✔
1789
                    global_tile_position: [-1, 0].into(),
1✔
1790
                    tile_size_in_pixels: [3, 2].into(),
1✔
1791
                    global_geo_transform: TestDefault::test_default(),
1✔
1792
                },
1✔
1793
                0,
1✔
1794
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![10, 8, 12, 16, 14, 15]).unwrap()),
1✔
1795
                CacheHint::default()
1✔
1796
            )
1✔
1797
        ));
1798

1799
        assert!(
1✔
1800
            result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
1801
                &RasterTile2D::new_with_tile_info(
1✔
1802
                    TimeInterval::new_unchecked(0, 30),
1✔
1803
                    TileInformation {
1✔
1804
                        global_tile_position: [-1, 1].into(),
1✔
1805
                        tile_size_in_pixels: [3, 2].into(),
1✔
1806
                        global_geo_transform: TestDefault::test_default(),
1✔
1807
                    },
1✔
1808
                    0,
1✔
1809
                    GridOrEmpty::from(
1✔
1810
                        MaskedGrid2D::new(
1✔
1811
                            Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
1812
                            Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
1813
                                .unwrap()
1✔
1814
                        )
1✔
1815
                        .unwrap()
1✔
1816
                    ),
1✔
1817
                    CacheHint::default()
1✔
1818
                )
1✔
1819
            )
1✔
1820
        );
1✔
1821
    }
1✔
1822

1823
    #[tokio::test]
1824
    #[allow(clippy::too_many_lines)]
1825
    async fn test_sum_without_nodata() {
1✔
1826
        let raster_tiles = make_raster();
1✔
1827

1828
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
1829
        let result_descriptor = RasterResultDescriptor {
1✔
1830
            data_type: RasterDataType::U8,
1✔
1831
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1832
            time: TimeDescriptor::new_regular_with_epoch(
1✔
1833
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
1834
                TimeStep::millis(10).unwrap(),
1✔
1835
            ),
1✔
1836
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
1837
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
1838
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1839
            ),
1✔
1840
            bands: RasterBandDescriptors::new_single_band(),
1✔
1841
        };
1✔
1842
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
1843

1844
        let mrs = MockRasterSource {
1✔
1845
            params: MockRasterSourceParams {
1✔
1846
                data: raster_tiles,
1✔
1847
                result_descriptor,
1✔
1848
            },
1✔
1849
        }
1✔
1850
        .boxed();
1✔
1851

1852
        let operator = 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: 20,
1✔
1860
                },
1✔
1861
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
1862
                output_type: None,
1✔
1863
            },
1✔
1864
            sources: SingleRasterSource { raster: mrs },
1✔
1865
        }
1✔
1866
        .boxed();
1✔
1867

1868
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
1869
        let query_rect = RasterQueryRectangle::new(
1✔
1870
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1871
            TimeInterval::new_unchecked(0, 30),
1✔
1872
            BandSelection::first(),
1✔
1873
        );
1874
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
1875

1876
        let query_processor = operator
1✔
1877
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1878
            .await
1✔
1879
            .unwrap()
1✔
1880
            .query_processor()
1✔
1881
            .unwrap()
1✔
1882
            .get_u8()
1✔
1883
            .unwrap();
1✔
1884

1885
        let result = query_processor
1✔
1886
            .raster_query(query_rect, &query_ctx)
1✔
1887
            .await
1✔
1888
            .unwrap()
1✔
1889
            .map(Result::unwrap)
1✔
1890
            .collect::<Vec<_>>()
1✔
1891
            .await;
1✔
1892

1893
        assert!(
1✔
1894
            result.tiles_equal_ignoring_cache_hint(&[
1✔
1895
                RasterTile2D::new_with_tile_info(
1✔
1896
                    TimeInterval::new_unchecked(0, 20),
1✔
1897
                    TileInformation {
1✔
1898
                        global_tile_position: [-1, 0].into(),
1✔
1899
                        tile_size_in_pixels: [3, 2].into(),
1✔
1900
                        global_geo_transform: TestDefault::test_default(),
1✔
1901
                    },
1✔
1902
                    0,
1✔
1903
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1904
                        .unwrap()
1✔
1905
                        .into(),
1✔
1906
                    CacheHint::default()
1✔
1907
                ),
1✔
1908
                RasterTile2D::new_with_tile_info(
1✔
1909
                    TimeInterval::new_unchecked(0, 20),
1✔
1910
                    TileInformation {
1✔
1911
                        global_tile_position: [-1, 1].into(),
1✔
1912
                        tile_size_in_pixels: [3, 2].into(),
1✔
1913
                        global_geo_transform: TestDefault::test_default(),
1✔
1914
                    },
1✔
1915
                    0,
1✔
1916
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1917
                        .unwrap()
1✔
1918
                        .into(),
1✔
1919
                    CacheHint::default()
1✔
1920
                ),
1✔
1921
                RasterTile2D::new_with_tile_info(
1✔
1922
                    TimeInterval::new_unchecked(20, 40),
1✔
1923
                    TileInformation {
1✔
1924
                        global_tile_position: [-1, 0].into(),
1✔
1925
                        tile_size_in_pixels: [3, 2].into(),
1✔
1926
                        global_geo_transform: TestDefault::test_default(),
1✔
1927
                    },
1✔
1928
                    0,
1✔
1929
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1930
                        .unwrap()
1✔
1931
                        .into(),
1✔
1932
                    CacheHint::default()
1✔
1933
                ),
1✔
1934
                RasterTile2D::new_with_tile_info(
1✔
1935
                    TimeInterval::new_unchecked(20, 40),
1✔
1936
                    TileInformation {
1✔
1937
                        global_tile_position: [-1, 1].into(),
1✔
1938
                        tile_size_in_pixels: [3, 2].into(),
1✔
1939
                        global_geo_transform: TestDefault::test_default(),
1✔
1940
                    },
1✔
1941
                    0,
1✔
1942
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1943
                        .unwrap()
1✔
1944
                        .into(),
1✔
1945
                    CacheHint::default()
1✔
1946
                )
1✔
1947
            ])
1✔
1948
        );
1✔
1949
    }
1✔
1950

1951
    #[tokio::test]
1952
    async fn test_sum_nodata() {
1✔
1953
        let raster_tiles = make_raster_with_no_data();
1✔
1954

1955
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
1956
        let result_descriptor = RasterResultDescriptor {
1✔
1957
            data_type: RasterDataType::U8,
1✔
1958
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1959
            time: TimeDescriptor::new_regular_with_epoch(
1✔
1960
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
1961
                TimeStep::millis(10).unwrap(),
1✔
1962
            ),
1✔
1963
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
1964
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
1965
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1966
            ),
1✔
1967
            bands: RasterBandDescriptors::new_single_band(),
1✔
1968
        };
1✔
1969
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
1970

1971
        let mrs = MockRasterSource {
1✔
1972
            params: MockRasterSourceParams {
1✔
1973
                data: raster_tiles,
1✔
1974
                result_descriptor,
1✔
1975
            },
1✔
1976
        }
1✔
1977
        .boxed();
1✔
1978

1979
        let agg = TemporalRasterAggregation {
1✔
1980
            params: TemporalRasterAggregationParameters {
1✔
1981
                aggregation: Aggregation::Sum {
1✔
1982
                    ignore_no_data: false,
1✔
1983
                },
1✔
1984
                window: TimeStep {
1✔
1985
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
1986
                    step: 30,
1✔
1987
                },
1✔
1988
                window_reference: None,
1✔
1989
                output_type: None,
1✔
1990
            },
1✔
1991
            sources: SingleRasterSource { raster: mrs },
1✔
1992
        }
1✔
1993
        .boxed();
1✔
1994

1995
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
1996
        let query_rect = RasterQueryRectangle::new(
1✔
1997
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
1998
            TimeInterval::new_unchecked(0, 30),
1✔
1999
            BandSelection::first(),
1✔
2000
        );
2001
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
2002

2003
        let qp = agg
1✔
2004
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2005
            .await
1✔
2006
            .unwrap()
1✔
2007
            .query_processor()
1✔
2008
            .unwrap()
1✔
2009
            .get_u8()
1✔
2010
            .unwrap();
1✔
2011

2012
        let result = qp
1✔
2013
            .raster_query(query_rect, &query_ctx)
1✔
2014
            .await
1✔
2015
            .unwrap()
1✔
2016
            .collect::<Vec<_>>()
1✔
2017
            .await;
1✔
2018

2019
        assert_eq!(result.len(), 2);
1✔
2020

2021
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2022
            &RasterTile2D::new_with_tile_info(
1✔
2023
                TimeInterval::new_unchecked(0, 30),
1✔
2024
                TileInformation {
1✔
2025
                    global_tile_position: [-1, 0].into(),
1✔
2026
                    tile_size_in_pixels: [3, 2].into(),
1✔
2027
                    global_geo_transform: TestDefault::test_default(),
1✔
2028
                },
1✔
2029
                0,
1✔
2030
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
2031
                CacheHint::default()
1✔
2032
            )
1✔
2033
        ));
2034

2035
        assert!(
1✔
2036
            result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2037
                &RasterTile2D::new_with_tile_info(
1✔
2038
                    TimeInterval::new_unchecked(0, 30),
1✔
2039
                    TileInformation {
1✔
2040
                        global_tile_position: [-1, 1].into(),
1✔
2041
                        tile_size_in_pixels: [3, 2].into(),
1✔
2042
                        global_geo_transform: TestDefault::test_default(),
1✔
2043
                    },
1✔
2044
                    0,
1✔
2045
                    GridOrEmpty::from(
1✔
2046
                        MaskedGrid2D::new(
1✔
2047
                            Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
2048
                            Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2049
                                .unwrap()
1✔
2050
                        )
1✔
2051
                        .unwrap(),
1✔
2052
                    ),
1✔
2053
                    CacheHint::default()
1✔
2054
                )
1✔
2055
            )
1✔
2056
        );
1✔
2057
    }
1✔
2058

2059
    #[tokio::test]
2060
    async fn test_sum_ignore_no_data() {
1✔
2061
        let raster_tiles = make_raster_with_no_data();
1✔
2062

2063
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
2064
        let result_descriptor = RasterResultDescriptor {
1✔
2065
            data_type: RasterDataType::U8,
1✔
2066
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2067
            time: TimeDescriptor::new_regular_with_epoch(
1✔
2068
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
2069
                TimeStep::millis(10).unwrap(),
1✔
2070
            ),
1✔
2071
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
2072
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
2073
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2074
            ),
1✔
2075
            bands: RasterBandDescriptors::new_single_band(),
1✔
2076
        };
1✔
2077
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
2078

2079
        let mrs = MockRasterSource {
1✔
2080
            params: MockRasterSourceParams {
1✔
2081
                data: raster_tiles,
1✔
2082
                result_descriptor,
1✔
2083
            },
1✔
2084
        }
1✔
2085
        .boxed();
1✔
2086

2087
        let agg = TemporalRasterAggregation {
1✔
2088
            params: TemporalRasterAggregationParameters {
1✔
2089
                aggregation: Aggregation::Sum {
1✔
2090
                    ignore_no_data: true,
1✔
2091
                },
1✔
2092
                window: TimeStep {
1✔
2093
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2094
                    step: 30,
1✔
2095
                },
1✔
2096
                window_reference: None,
1✔
2097
                output_type: None,
1✔
2098
            },
1✔
2099
            sources: SingleRasterSource { raster: mrs },
1✔
2100
        }
1✔
2101
        .boxed();
1✔
2102

2103
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
2104
        let query_rect = RasterQueryRectangle::new(
1✔
2105
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2106
            TimeInterval::new_unchecked(0, 30),
1✔
2107
            BandSelection::first(),
1✔
2108
        );
2109
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
2110

2111
        let qp = agg
1✔
2112
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2113
            .await
1✔
2114
            .unwrap()
1✔
2115
            .query_processor()
1✔
2116
            .unwrap()
1✔
2117
            .get_u8()
1✔
2118
            .unwrap();
1✔
2119

2120
        let result = qp
1✔
2121
            .raster_query(query_rect, &query_ctx)
1✔
2122
            .await
1✔
2123
            .unwrap()
1✔
2124
            .collect::<Vec<_>>()
1✔
2125
            .await;
1✔
2126

2127
        assert_eq!(result.len(), 2);
1✔
2128

2129
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2130
            &RasterTile2D::new_with_tile_info(
1✔
2131
                TimeInterval::new_unchecked(0, 30),
1✔
2132
                TileInformation {
1✔
2133
                    global_tile_position: [-1, 0].into(),
1✔
2134
                    tile_size_in_pixels: [3, 2].into(),
1✔
2135
                    global_geo_transform: TestDefault::test_default(),
1✔
2136
                },
1✔
2137
                0,
1✔
2138
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![20, 8, 24, 16, 28, 30]).unwrap()),
1✔
2139
                CacheHint::default()
1✔
2140
            )
1✔
2141
        ));
2142

2143
        assert!(
1✔
2144
            result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2145
                &RasterTile2D::new_with_tile_info(
1✔
2146
                    TimeInterval::new_unchecked(0, 30),
1✔
2147
                    TileInformation {
1✔
2148
                        global_tile_position: [-1, 1].into(),
1✔
2149
                        tile_size_in_pixels: [3, 2].into(),
1✔
2150
                        global_geo_transform: TestDefault::test_default(),
1✔
2151
                    },
1✔
2152
                    0,
1✔
2153
                    GridOrEmpty::from(
1✔
2154
                        MaskedGrid2D::new(
1✔
2155
                            Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
1✔
2156
                            Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2157
                                .unwrap()
1✔
2158
                        )
1✔
2159
                        .unwrap()
1✔
2160
                    ),
1✔
2161
                    CacheHint::default()
1✔
2162
                )
1✔
2163
            )
1✔
2164
        );
1✔
2165
    }
1✔
2166

2167
    #[tokio::test]
2168
    #[allow(clippy::too_many_lines)]
2169
    async fn test_sum_with_larger_data_type() {
1✔
2170
        let raster_tiles = make_raster();
1✔
2171

2172
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
2173
        let result_descriptor = RasterResultDescriptor {
1✔
2174
            data_type: RasterDataType::U8,
1✔
2175
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2176
            time: TimeDescriptor::new_regular_with_epoch(
1✔
2177
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
2178
                TimeStep::millis(10).unwrap(),
1✔
2179
            ),
1✔
2180
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
2181
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
2182
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2183
            ),
1✔
2184
            bands: RasterBandDescriptors::new_single_band(),
1✔
2185
        };
1✔
2186
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
2187

2188
        let mrs = MockRasterSource {
1✔
2189
            params: MockRasterSourceParams {
1✔
2190
                data: raster_tiles,
1✔
2191
                result_descriptor: result_descriptor.clone(),
1✔
2192
            },
1✔
2193
        }
1✔
2194
        .boxed();
1✔
2195

2196
        let operator = TemporalRasterAggregation {
1✔
2197
            params: TemporalRasterAggregationParameters {
1✔
2198
                aggregation: Aggregation::Sum {
1✔
2199
                    ignore_no_data: false,
1✔
2200
                },
1✔
2201
                window: TimeStep {
1✔
2202
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2203
                    step: 20,
1✔
2204
                },
1✔
2205
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
2206
                output_type: Some(RasterDataType::U16),
1✔
2207
            },
1✔
2208
            sources: SingleRasterSource {
1✔
2209
                raster: Expression {
1✔
2210
                    params: ExpressionParams {
1✔
2211
                        expression: "20 * A".to_string(),
1✔
2212
                        output_type: RasterDataType::U8,
1✔
2213
                        output_band: None,
1✔
2214
                        map_no_data: true,
1✔
2215
                    },
1✔
2216
                    sources: SingleRasterSource { raster: mrs },
1✔
2217
                }
1✔
2218
                .boxed(),
1✔
2219
            },
1✔
2220
        }
1✔
2221
        .boxed();
1✔
2222

2223
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
2224
        let query_rect = RasterQueryRectangle::new(
1✔
2225
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2226
            TimeInterval::new_unchecked(0, 30),
1✔
2227
            BandSelection::first(),
1✔
2228
        );
2229
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
2230

2231
        let query_processor = operator
1✔
2232
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2233
            .await
1✔
2234
            .unwrap()
1✔
2235
            .query_processor()
1✔
2236
            .unwrap()
1✔
2237
            .get_u16()
1✔
2238
            .unwrap();
1✔
2239

2240
        let result = query_processor
1✔
2241
            .raster_query(query_rect, &query_ctx)
1✔
2242
            .await
1✔
2243
            .unwrap()
1✔
2244
            .map(Result::unwrap)
1✔
2245
            .collect::<Vec<_>>()
1✔
2246
            .await;
1✔
2247

2248
        assert!(
1✔
2249
            result.tiles_equal_ignoring_cache_hint(&[
1✔
2250
                RasterTile2D::new_with_tile_info(
1✔
2251
                    TimeInterval::new_unchecked(0, 20),
1✔
2252
                    TileInformation {
1✔
2253
                        global_tile_position: [-1, 0].into(),
1✔
2254
                        tile_size_in_pixels: [3, 2].into(),
1✔
2255
                        global_geo_transform: TestDefault::test_default(),
1✔
2256
                    },
1✔
2257
                    0,
1✔
2258
                    Grid2D::new(
1✔
2259
                        [3, 2].into(),
1✔
2260
                        vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2261
                    )
1✔
2262
                    .unwrap()
1✔
2263
                    .into(),
1✔
2264
                    CacheHint::default()
1✔
2265
                ),
1✔
2266
                RasterTile2D::new_with_tile_info(
1✔
2267
                    TimeInterval::new_unchecked(0, 20),
1✔
2268
                    TileInformation {
1✔
2269
                        global_tile_position: [-1, 1].into(),
1✔
2270
                        tile_size_in_pixels: [3, 2].into(),
1✔
2271
                        global_geo_transform: TestDefault::test_default(),
1✔
2272
                    },
1✔
2273
                    0,
1✔
2274
                    Grid2D::new(
1✔
2275
                        [3, 2].into(),
1✔
2276
                        vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2277
                    )
1✔
2278
                    .unwrap()
1✔
2279
                    .into(),
1✔
2280
                    CacheHint::default()
1✔
2281
                ),
1✔
2282
                RasterTile2D::new_with_tile_info(
1✔
2283
                    TimeInterval::new_unchecked(20, 40),
1✔
2284
                    TileInformation {
1✔
2285
                        global_tile_position: [-1, 0].into(),
1✔
2286
                        tile_size_in_pixels: [3, 2].into(),
1✔
2287
                        global_geo_transform: TestDefault::test_default(),
1✔
2288
                    },
1✔
2289
                    0,
1✔
2290
                    Grid2D::new(
1✔
2291
                        [3, 2].into(),
1✔
2292
                        vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2293
                    )
1✔
2294
                    .unwrap()
1✔
2295
                    .into(),
1✔
2296
                    CacheHint::default()
1✔
2297
                ),
1✔
2298
                RasterTile2D::new_with_tile_info(
1✔
2299
                    TimeInterval::new_unchecked(20, 40),
1✔
2300
                    TileInformation {
1✔
2301
                        global_tile_position: [-1, 1].into(),
1✔
2302
                        tile_size_in_pixels: [3, 2].into(),
1✔
2303
                        global_geo_transform: TestDefault::test_default(),
1✔
2304
                    },
1✔
2305
                    0,
1✔
2306
                    Grid2D::new(
1✔
2307
                        [3, 2].into(),
1✔
2308
                        vec![13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20, 13 * 20]
1✔
2309
                    )
1✔
2310
                    .unwrap()
1✔
2311
                    .into(),
1✔
2312
                    CacheHint::default()
1✔
2313
                )
1✔
2314
            ]),
1✔
2315
        );
1✔
2316
    }
1✔
2317

2318
    #[tokio::test]
2319
    #[allow(clippy::too_many_lines)]
2320
    async fn test_count_without_nodata() {
1✔
2321
        let raster_tiles = make_raster();
1✔
2322

2323
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
2324
        let result_descriptor = RasterResultDescriptor {
1✔
2325
            data_type: RasterDataType::U8,
1✔
2326
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2327
            time: TimeDescriptor::new_regular_with_epoch(
1✔
2328
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
2329
                TimeStep::millis(10).unwrap(),
1✔
2330
            ),
1✔
2331
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
2332
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
2333
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2334
            ),
1✔
2335
            bands: RasterBandDescriptors::new_single_band(),
1✔
2336
        };
1✔
2337
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
2338

2339
        let mrs = MockRasterSource {
1✔
2340
            params: MockRasterSourceParams {
1✔
2341
                data: raster_tiles,
1✔
2342
                result_descriptor,
1✔
2343
            },
1✔
2344
        }
1✔
2345
        .boxed();
1✔
2346

2347
        let operator = TemporalRasterAggregation {
1✔
2348
            params: TemporalRasterAggregationParameters {
1✔
2349
                aggregation: Aggregation::Count {
1✔
2350
                    ignore_no_data: false,
1✔
2351
                },
1✔
2352
                window: TimeStep {
1✔
2353
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2354
                    step: 20,
1✔
2355
                },
1✔
2356
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
2357
                output_type: None,
1✔
2358
            },
1✔
2359
            sources: SingleRasterSource { raster: mrs },
1✔
2360
        }
1✔
2361
        .boxed();
1✔
2362

2363
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
2364
        let query_rect = RasterQueryRectangle::new(
1✔
2365
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2366
            TimeInterval::new_unchecked(0, 30),
1✔
2367
            BandSelection::first(),
1✔
2368
        );
2369
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
2370

2371
        let query_processor = operator
1✔
2372
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2373
            .await
1✔
2374
            .unwrap()
1✔
2375
            .query_processor()
1✔
2376
            .unwrap()
1✔
2377
            .get_u8()
1✔
2378
            .unwrap();
1✔
2379

2380
        let result = query_processor
1✔
2381
            .raster_query(query_rect, &query_ctx)
1✔
2382
            .await
1✔
2383
            .unwrap()
1✔
2384
            .map(Result::unwrap)
1✔
2385
            .collect::<Vec<_>>()
1✔
2386
            .await;
1✔
2387

2388
        assert!(
1✔
2389
            result.tiles_equal_ignoring_cache_hint(&[
1✔
2390
                RasterTile2D::new_with_tile_info(
1✔
2391
                    TimeInterval::new_unchecked(0, 20),
1✔
2392
                    TileInformation {
1✔
2393
                        global_tile_position: [-1, 0].into(),
1✔
2394
                        tile_size_in_pixels: [3, 2].into(),
1✔
2395
                        global_geo_transform: TestDefault::test_default(),
1✔
2396
                    },
1✔
2397
                    0,
1✔
2398
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2399
                        .unwrap()
1✔
2400
                        .into(),
1✔
2401
                    CacheHint::default()
1✔
2402
                ),
1✔
2403
                RasterTile2D::new_with_tile_info(
1✔
2404
                    TimeInterval::new_unchecked(0, 20),
1✔
2405
                    TileInformation {
1✔
2406
                        global_tile_position: [-1, 1].into(),
1✔
2407
                        tile_size_in_pixels: [3, 2].into(),
1✔
2408
                        global_geo_transform: TestDefault::test_default(),
1✔
2409
                    },
1✔
2410
                    0,
1✔
2411
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2412
                        .unwrap()
1✔
2413
                        .into(),
1✔
2414
                    CacheHint::default()
1✔
2415
                ),
1✔
2416
                RasterTile2D::new_with_tile_info(
1✔
2417
                    TimeInterval::new_unchecked(20, 40),
1✔
2418
                    TileInformation {
1✔
2419
                        global_tile_position: [-1, 0].into(),
1✔
2420
                        tile_size_in_pixels: [3, 2].into(),
1✔
2421
                        global_geo_transform: TestDefault::test_default(),
1✔
2422
                    },
1✔
2423
                    0,
1✔
2424
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2425
                        .unwrap()
1✔
2426
                        .into(),
1✔
2427
                    CacheHint::default()
1✔
2428
                ),
1✔
2429
                RasterTile2D::new_with_tile_info(
1✔
2430
                    TimeInterval::new_unchecked(20, 40),
1✔
2431
                    TileInformation {
1✔
2432
                        global_tile_position: [-1, 1].into(),
1✔
2433
                        tile_size_in_pixels: [3, 2].into(),
1✔
2434
                        global_geo_transform: TestDefault::test_default(),
1✔
2435
                    },
1✔
2436
                    0,
1✔
2437
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2438
                        .unwrap()
1✔
2439
                        .into(),
1✔
2440
                    CacheHint::default()
1✔
2441
                )
1✔
2442
            ])
1✔
2443
        );
1✔
2444
    }
1✔
2445

2446
    #[tokio::test]
2447
    async fn test_count_nodata() {
1✔
2448
        let raster_tiles = make_raster_with_no_data();
1✔
2449

2450
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
2451
        let result_descriptor = RasterResultDescriptor {
1✔
2452
            data_type: RasterDataType::U8,
1✔
2453
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2454
            time: TimeDescriptor::new_regular_with_epoch(
1✔
2455
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
2456
                TimeStep::millis(10).unwrap(),
1✔
2457
            ),
1✔
2458
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
2459
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
2460
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2461
            ),
1✔
2462
            bands: RasterBandDescriptors::new_single_band(),
1✔
2463
        };
1✔
2464
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
2465

2466
        let mrs = MockRasterSource {
1✔
2467
            params: MockRasterSourceParams {
1✔
2468
                data: raster_tiles,
1✔
2469
                result_descriptor,
1✔
2470
            },
1✔
2471
        }
1✔
2472
        .boxed();
1✔
2473

2474
        let agg = TemporalRasterAggregation {
1✔
2475
            params: TemporalRasterAggregationParameters {
1✔
2476
                aggregation: Aggregation::Count {
1✔
2477
                    ignore_no_data: false,
1✔
2478
                },
1✔
2479
                window: TimeStep {
1✔
2480
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2481
                    step: 30,
1✔
2482
                },
1✔
2483
                window_reference: None,
1✔
2484
                output_type: None,
1✔
2485
            },
1✔
2486
            sources: SingleRasterSource { raster: mrs },
1✔
2487
        }
1✔
2488
        .boxed();
1✔
2489

2490
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
2491
        let query_rect = RasterQueryRectangle::new(
1✔
2492
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2493
            TimeInterval::new_unchecked(0, 30),
1✔
2494
            BandSelection::first(),
1✔
2495
        );
2496
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
2497

2498
        let qp = agg
1✔
2499
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2500
            .await
1✔
2501
            .unwrap()
1✔
2502
            .query_processor()
1✔
2503
            .unwrap()
1✔
2504
            .get_u8()
1✔
2505
            .unwrap();
1✔
2506

2507
        let result = qp
1✔
2508
            .raster_query(query_rect, &query_ctx)
1✔
2509
            .await
1✔
2510
            .unwrap()
1✔
2511
            .collect::<Vec<_>>()
1✔
2512
            .await;
1✔
2513

2514
        assert_eq!(result.len(), 2);
1✔
2515

2516
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2517
            &RasterTile2D::new_with_tile_info(
1✔
2518
                TimeInterval::new_unchecked(0, 30),
1✔
2519
                TileInformation {
1✔
2520
                    global_tile_position: [-1, 0].into(),
1✔
2521
                    tile_size_in_pixels: [3, 2].into(),
1✔
2522
                    global_geo_transform: TestDefault::test_default(),
1✔
2523
                },
1✔
2524
                0,
1✔
2525
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
2526
                CacheHint::default()
1✔
2527
            )
1✔
2528
        ));
2529

2530
        assert!(
1✔
2531
            result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2532
                &RasterTile2D::new_with_tile_info(
1✔
2533
                    TimeInterval::new_unchecked(0, 30),
1✔
2534
                    TileInformation {
1✔
2535
                        global_tile_position: [-1, 1].into(),
1✔
2536
                        tile_size_in_pixels: [3, 2].into(),
1✔
2537
                        global_geo_transform: TestDefault::test_default(),
1✔
2538
                    },
1✔
2539
                    0,
1✔
2540
                    GridOrEmpty::from(
1✔
2541
                        MaskedGrid2D::new(
1✔
2542
                            Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
1✔
2543
                            Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2544
                                .unwrap()
1✔
2545
                        )
1✔
2546
                        .unwrap()
1✔
2547
                    ),
1✔
2548
                    CacheHint::default()
1✔
2549
                )
1✔
2550
            )
1✔
2551
        );
1✔
2552
    }
1✔
2553

2554
    #[tokio::test]
2555
    async fn test_count_ignore_no_data() {
1✔
2556
        let raster_tiles = make_raster_with_no_data();
1✔
2557

2558
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
2559
        let result_descriptor = RasterResultDescriptor {
1✔
2560
            data_type: RasterDataType::U8,
1✔
2561
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2562
            time: TimeDescriptor::new_regular_with_epoch(
1✔
2563
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
2564
                TimeStep::millis(10).unwrap(),
1✔
2565
            ),
1✔
2566
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
2567
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
2568
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2569
            ),
1✔
2570
            bands: RasterBandDescriptors::new_single_band(),
1✔
2571
        };
1✔
2572
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
2573

2574
        let mrs = MockRasterSource {
1✔
2575
            params: MockRasterSourceParams {
1✔
2576
                data: raster_tiles,
1✔
2577
                result_descriptor,
1✔
2578
            },
1✔
2579
        }
1✔
2580
        .boxed();
1✔
2581

2582
        let agg = TemporalRasterAggregation {
1✔
2583
            params: TemporalRasterAggregationParameters {
1✔
2584
                aggregation: Aggregation::Count {
1✔
2585
                    ignore_no_data: true,
1✔
2586
                },
1✔
2587
                window: TimeStep {
1✔
2588
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2589
                    step: 30,
1✔
2590
                },
1✔
2591
                window_reference: None,
1✔
2592
                output_type: None,
1✔
2593
            },
1✔
2594
            sources: SingleRasterSource { raster: mrs },
1✔
2595
        }
1✔
2596
        .boxed();
1✔
2597

2598
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
2599
        let query_rect = RasterQueryRectangle::new(
1✔
2600
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2601
            TimeInterval::new_unchecked(0, 30),
1✔
2602
            BandSelection::first(),
1✔
2603
        );
2604
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
2605

2606
        let qp = agg
1✔
2607
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2608
            .await
1✔
2609
            .unwrap()
1✔
2610
            .query_processor()
1✔
2611
            .unwrap()
1✔
2612
            .get_u8()
1✔
2613
            .unwrap();
1✔
2614

2615
        let result = qp
1✔
2616
            .raster_query(query_rect, &query_ctx)
1✔
2617
            .await
1✔
2618
            .unwrap()
1✔
2619
            .collect::<Vec<_>>()
1✔
2620
            .await;
1✔
2621

2622
        assert_eq!(result.len(), 2);
1✔
2623

2624
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2625
            &RasterTile2D::new_with_tile_info(
1✔
2626
                TimeInterval::new_unchecked(0, 30),
1✔
2627
                TileInformation {
1✔
2628
                    global_tile_position: [-1, 0].into(),
1✔
2629
                    tile_size_in_pixels: [3, 2].into(),
1✔
2630
                    global_geo_transform: TestDefault::test_default(),
1✔
2631
                },
1✔
2632
                0,
1✔
2633
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![2, 1, 2, 1, 2, 2]).unwrap()),
1✔
2634
                CacheHint::default()
1✔
2635
            )
1✔
2636
        ));
2637

2638
        assert!(
1✔
2639
            result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2640
                &RasterTile2D::new_with_tile_info(
1✔
2641
                    TimeInterval::new_unchecked(0, 30),
1✔
2642
                    TileInformation {
1✔
2643
                        global_tile_position: [-1, 1].into(),
1✔
2644
                        tile_size_in_pixels: [3, 2].into(),
1✔
2645
                        global_geo_transform: TestDefault::test_default(),
1✔
2646
                    },
1✔
2647
                    0,
1✔
2648
                    GridOrEmpty::from(
1✔
2649
                        MaskedGrid2D::new(
1✔
2650
                            Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
1✔
2651
                            Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
1✔
2652
                                .unwrap()
1✔
2653
                        )
1✔
2654
                        .unwrap()
1✔
2655
                    ),
1✔
2656
                    CacheHint::default()
1✔
2657
                )
1✔
2658
            )
1✔
2659
        );
1✔
2660
    }
1✔
2661

2662
    #[tokio::test]
2663
    async fn test_query_not_aligned_with_window_reference() {
1✔
2664
        let raster_tiles = make_raster();
1✔
2665

2666
        let tile_size_in_pixels = GridShape2D::new_2d(3, 2);
1✔
2667
        let result_descriptor = RasterResultDescriptor {
1✔
2668
            data_type: RasterDataType::U8,
1✔
2669
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2670
            time: TimeDescriptor::new_regular_with_epoch(
1✔
2671
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
2672
                TimeStep::millis(10).unwrap(),
1✔
2673
            ),
1✔
2674
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
2675
                GeoTransform::new(Coordinate2D::new(0., 0.), 1., -1.),
1✔
2676
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2677
            ),
1✔
2678
            bands: RasterBandDescriptors::new_single_band(),
1✔
2679
        };
1✔
2680
        let tiling_specification = TilingSpecification::new(tile_size_in_pixels);
1✔
2681

2682
        let mrs = MockRasterSource {
1✔
2683
            params: MockRasterSourceParams {
1✔
2684
                data: raster_tiles,
1✔
2685
                result_descriptor,
1✔
2686
            },
1✔
2687
        }
1✔
2688
        .boxed();
1✔
2689

2690
        let agg = TemporalRasterAggregation {
1✔
2691
            params: TemporalRasterAggregationParameters {
1✔
2692
                aggregation: Aggregation::Last {
1✔
2693
                    ignore_no_data: false,
1✔
2694
                },
1✔
2695
                window: TimeStep {
1✔
2696
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2697
                    step: 30,
1✔
2698
                },
1✔
2699
                window_reference: Some(TimeInstance::EPOCH_START),
1✔
2700
                output_type: None,
1✔
2701
            },
1✔
2702
            sources: SingleRasterSource { raster: mrs },
1✔
2703
        }
1✔
2704
        .boxed();
1✔
2705

2706
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
2707
        let query_rect = RasterQueryRectangle::new(
1✔
2708
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2709
            TimeInterval::new_unchecked(5, 5),
1✔
2710
            BandSelection::first(),
1✔
2711
        );
2712
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
2713

2714
        let qp = agg
1✔
2715
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
2716
            .await
1✔
2717
            .unwrap()
1✔
2718
            .query_processor()
1✔
2719
            .unwrap()
1✔
2720
            .get_u8()
1✔
2721
            .unwrap();
1✔
2722

2723
        let result = qp
1✔
2724
            .query(query_rect, &query_ctx)
1✔
2725
            .await
1✔
2726
            .unwrap()
1✔
2727
            .collect::<Vec<_>>()
1✔
2728
            .await;
1✔
2729

2730
        assert_eq!(result.len(), 2);
1✔
2731
        assert!(result[0].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2732
            &RasterTile2D::new_with_tile_info(
1✔
2733
                TimeInterval::new_unchecked(0, 30),
1✔
2734
                TileInformation {
1✔
2735
                    global_tile_position: [-1, 0].into(),
1✔
2736
                    tile_size_in_pixels: [3, 2].into(),
1✔
2737
                    global_geo_transform: TestDefault::test_default(),
1✔
2738
                },
1✔
2739
                0,
1✔
2740
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
2741
                CacheHint::default()
1✔
2742
            )
1✔
2743
        ));
2744

2745
        assert!(result[1].as_ref().unwrap().tiles_equal_ignoring_cache_hint(
1✔
2746
            &RasterTile2D::new_with_tile_info(
1✔
2747
                TimeInterval::new_unchecked(0, 30),
1✔
2748
                TileInformation {
1✔
2749
                    global_tile_position: [-1, 1].into(),
1✔
2750
                    tile_size_in_pixels: [3, 2].into(),
1✔
2751
                    global_geo_transform: TestDefault::test_default(),
1✔
2752
                },
1✔
2753
                0,
1✔
2754
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
2755
                CacheHint::default()
1✔
2756
            )
1✔
2757
        ));
1✔
2758
    }
1✔
2759

2760
    fn make_raster() -> Vec<geoengine_datatypes::raster::RasterTile2D<u8>> {
10✔
2761
        let raster_tiles = vec![
10✔
2762
            RasterTile2D::new_with_tile_info(
10✔
2763
                TimeInterval::new_unchecked(0, 10),
10✔
2764
                TileInformation {
10✔
2765
                    global_tile_position: [-1, 0].into(),
10✔
2766
                    tile_size_in_pixels: [3, 2].into(),
10✔
2767
                    global_geo_transform: TestDefault::test_default(),
10✔
2768
                },
10✔
2769
                0,
2770
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
10✔
2771
                CacheHint::default(),
10✔
2772
            ),
2773
            RasterTile2D::new_with_tile_info(
10✔
2774
                TimeInterval::new_unchecked(0, 10),
10✔
2775
                TileInformation {
10✔
2776
                    global_tile_position: [-1, 1].into(),
10✔
2777
                    tile_size_in_pixels: [3, 2].into(),
10✔
2778
                    global_geo_transform: TestDefault::test_default(),
10✔
2779
                },
10✔
2780
                0,
2781
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
10✔
2782
                CacheHint::default(),
10✔
2783
            ),
2784
            RasterTile2D::new_with_tile_info(
10✔
2785
                TimeInterval::new_unchecked(10, 20),
10✔
2786
                TileInformation {
10✔
2787
                    global_tile_position: [-1, 0].into(),
10✔
2788
                    tile_size_in_pixels: [3, 2].into(),
10✔
2789
                    global_geo_transform: TestDefault::test_default(),
10✔
2790
                },
10✔
2791
                0,
2792
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
10✔
2793
                CacheHint::default(),
10✔
2794
            ),
2795
            RasterTile2D::new_with_tile_info(
10✔
2796
                TimeInterval::new_unchecked(10, 20),
10✔
2797
                TileInformation {
10✔
2798
                    global_tile_position: [-1, 1].into(),
10✔
2799
                    tile_size_in_pixels: [3, 2].into(),
10✔
2800
                    global_geo_transform: TestDefault::test_default(),
10✔
2801
                },
10✔
2802
                0,
2803
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
10✔
2804
                CacheHint::default(),
10✔
2805
            ),
2806
            RasterTile2D::new_with_tile_info(
10✔
2807
                TimeInterval::new_unchecked(20, 30),
10✔
2808
                TileInformation {
10✔
2809
                    global_tile_position: [-1, 0].into(),
10✔
2810
                    tile_size_in_pixels: [3, 2].into(),
10✔
2811
                    global_geo_transform: TestDefault::test_default(),
10✔
2812
                },
10✔
2813
                0,
2814
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
10✔
2815
                CacheHint::default(),
10✔
2816
            ),
2817
            RasterTile2D::new_with_tile_info(
10✔
2818
                TimeInterval::new_unchecked(20, 30),
10✔
2819
                TileInformation {
10✔
2820
                    global_tile_position: [-1, 1].into(),
10✔
2821
                    tile_size_in_pixels: [3, 2].into(),
10✔
2822
                    global_geo_transform: TestDefault::test_default(),
10✔
2823
                },
10✔
2824
                0,
2825
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
10✔
2826
                CacheHint::default(),
10✔
2827
            ),
2828
            RasterTile2D::new_with_tile_info(
10✔
2829
                TimeInterval::new_unchecked(30, 40),
10✔
2830
                TileInformation {
10✔
2831
                    global_tile_position: [-1, 0].into(),
10✔
2832
                    tile_size_in_pixels: [3, 2].into(),
10✔
2833
                    global_geo_transform: TestDefault::test_default(),
10✔
2834
                },
10✔
2835
                0,
2836
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
10✔
2837
                CacheHint::default(),
10✔
2838
            ),
2839
            RasterTile2D::new_with_tile_info(
10✔
2840
                TimeInterval::new_unchecked(30, 40),
10✔
2841
                TileInformation {
10✔
2842
                    global_tile_position: [-1, 1].into(),
10✔
2843
                    tile_size_in_pixels: [3, 2].into(),
10✔
2844
                    global_geo_transform: TestDefault::test_default(),
10✔
2845
                },
10✔
2846
                0,
2847
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 5, 4, 3, 2, 1]).unwrap()),
10✔
2848
                CacheHint::default(),
10✔
2849
            ),
2850
        ];
2851
        raster_tiles
10✔
2852
    }
10✔
2853

2854
    fn make_raster_with_no_data() -> Vec<geoengine_datatypes::raster::RasterTile2D<u8>> {
10✔
2855
        let raster_tiles = vec![
10✔
2856
            RasterTile2D::new_with_tile_info(
10✔
2857
                TimeInterval::new_unchecked(0, 10),
10✔
2858
                TileInformation {
10✔
2859
                    global_tile_position: [-1, 0].into(),
10✔
2860
                    tile_size_in_pixels: [3, 2].into(),
10✔
2861
                    global_geo_transform: TestDefault::test_default(),
10✔
2862
                },
10✔
2863
                0,
2864
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2865
                CacheHint::default(),
10✔
2866
            ),
2867
            RasterTile2D::new_with_tile_info(
10✔
2868
                TimeInterval::new_unchecked(0, 10),
10✔
2869
                TileInformation {
10✔
2870
                    global_tile_position: [-1, 1].into(),
10✔
2871
                    tile_size_in_pixels: [3, 2].into(),
10✔
2872
                    global_geo_transform: TestDefault::test_default(),
10✔
2873
                },
10✔
2874
                0,
2875
                GridOrEmpty::from(
10✔
2876
                    MaskedGrid2D::new(
10✔
2877
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 42, 5, 6]).unwrap(),
10✔
2878
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
10✔
2879
                            .unwrap(),
10✔
2880
                    )
2881
                    .unwrap(),
10✔
2882
                ),
2883
                CacheHint::default(),
10✔
2884
            ),
2885
            RasterTile2D::new_with_tile_info(
10✔
2886
                TimeInterval::new_unchecked(10, 20),
10✔
2887
                TileInformation {
10✔
2888
                    global_tile_position: [-1, 0].into(),
10✔
2889
                    tile_size_in_pixels: [3, 2].into(),
10✔
2890
                    global_geo_transform: TestDefault::test_default(),
10✔
2891
                },
10✔
2892
                0,
2893
                GridOrEmpty::from(
10✔
2894
                    MaskedGrid2D::new(
10✔
2895
                        Grid2D::new([3, 2].into(), vec![7, 8, 9, 42, 11, 12]).unwrap(),
10✔
2896
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
10✔
2897
                            .unwrap(),
10✔
2898
                    )
2899
                    .unwrap(),
10✔
2900
                ),
2901
                CacheHint::default(),
10✔
2902
            ),
2903
            RasterTile2D::new_with_tile_info(
10✔
2904
                TimeInterval::new_unchecked(10, 20),
10✔
2905
                TileInformation {
10✔
2906
                    global_tile_position: [-1, 1].into(),
10✔
2907
                    tile_size_in_pixels: [3, 2].into(),
10✔
2908
                    global_geo_transform: TestDefault::test_default(),
10✔
2909
                },
10✔
2910
                0,
2911
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2912
                CacheHint::default(),
10✔
2913
            ),
2914
            RasterTile2D::new_with_tile_info(
10✔
2915
                TimeInterval::new_unchecked(20, 30),
10✔
2916
                TileInformation {
10✔
2917
                    global_tile_position: [-1, 0].into(),
10✔
2918
                    tile_size_in_pixels: [3, 2].into(),
10✔
2919
                    global_geo_transform: TestDefault::test_default(),
10✔
2920
                },
10✔
2921
                0,
2922
                GridOrEmpty::from(
10✔
2923
                    MaskedGrid2D::new(
10✔
2924
                        Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
10✔
2925
                        Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
10✔
2926
                            .unwrap(),
10✔
2927
                    )
2928
                    .unwrap(),
10✔
2929
                ),
2930
                CacheHint::default(),
10✔
2931
            ),
2932
            RasterTile2D::new_with_tile_info(
10✔
2933
                TimeInterval::new_unchecked(20, 30),
10✔
2934
                TileInformation {
10✔
2935
                    global_tile_position: [-1, 1].into(),
10✔
2936
                    tile_size_in_pixels: [3, 2].into(),
10✔
2937
                    global_geo_transform: TestDefault::test_default(),
10✔
2938
                },
10✔
2939
                0,
2940
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
10✔
2941
                CacheHint::default(),
10✔
2942
            ),
2943
        ];
2944
        raster_tiles
10✔
2945
    }
10✔
2946

2947
    #[tokio::test]
2948
    #[allow(clippy::too_many_lines)]
2949
    async fn it_sums_multiple_bands() {
1✔
2950
        let data = make_raster();
1✔
2951
        let result_descriptor = RasterResultDescriptor {
1✔
2952
            data_type: RasterDataType::U8,
1✔
2953
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
2954
            time: TimeDescriptor::new_regular_with_epoch(
1✔
2955
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
2956
                TimeStep::millis(10).unwrap(),
1✔
2957
            ),
1✔
2958
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
2959
                GeoTransform::test_default(),
1✔
2960
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
2961
            ),
1✔
2962
            bands: RasterBandDescriptors::new_single_band(),
1✔
2963
        };
1✔
2964

2965
        let operator = TemporalRasterAggregation {
1✔
2966
            params: TemporalRasterAggregationParameters {
1✔
2967
                aggregation: Aggregation::Sum {
1✔
2968
                    ignore_no_data: false,
1✔
2969
                },
1✔
2970
                window: TimeStep {
1✔
2971
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
2972
                    step: 20,
1✔
2973
                },
1✔
2974
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1✔
2975
                output_type: None,
1✔
2976
            },
1✔
2977
            sources: SingleRasterSource {
1✔
2978
                raster: RasterStacker {
1✔
2979
                    params: RasterStackerParams {
1✔
2980
                        rename_bands: RenameBands::Default,
1✔
2981
                    },
1✔
2982
                    sources: MultipleRasterSources {
1✔
2983
                        rasters: vec![
1✔
2984
                            MockRasterSource {
1✔
2985
                                params: MockRasterSourceParams {
1✔
2986
                                    data: data.clone(),
1✔
2987
                                    result_descriptor: result_descriptor.clone(),
1✔
2988
                                },
1✔
2989
                            }
1✔
2990
                            .boxed(),
1✔
2991
                            MockRasterSource {
1✔
2992
                                params: MockRasterSourceParams {
1✔
2993
                                    data: data.clone(),
1✔
2994
                                    result_descriptor,
1✔
2995
                                },
1✔
2996
                            }
1✔
2997
                            .boxed(),
1✔
2998
                        ],
1✔
2999
                    },
1✔
3000
                }
1✔
3001
                .boxed(),
1✔
3002
            },
1✔
3003
        }
1✔
3004
        .boxed();
1✔
3005

3006
        let exe_ctx =
1✔
3007
            MockExecutionContext::new_with_tiling_spec(TilingSpecification::new([3, 2].into()));
1✔
3008
        let query_rect = RasterQueryRectangle::new(
1✔
3009
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
3010
            TimeInterval::new_unchecked(0, 30),
1✔
3011
            [0, 1].try_into().unwrap(),
1✔
3012
        );
3013
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
3014

3015
        let query_processor = operator
1✔
3016
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
3017
            .await
1✔
3018
            .unwrap()
1✔
3019
            .query_processor()
1✔
3020
            .unwrap()
1✔
3021
            .get_u8()
1✔
3022
            .unwrap();
1✔
3023

3024
        let result = query_processor
1✔
3025
            .raster_query(query_rect, &query_ctx)
1✔
3026
            .await
1✔
3027
            .unwrap()
1✔
3028
            .map(Result::unwrap)
1✔
3029
            .collect::<Vec<_>>()
1✔
3030
            .await;
1✔
3031

3032
        assert!(
1✔
3033
            result.tiles_equal_ignoring_cache_hint(&[
1✔
3034
                RasterTile2D::new_with_tile_info(
1✔
3035
                    TimeInterval::new_unchecked(0, 20),
1✔
3036
                    TileInformation {
1✔
3037
                        global_tile_position: [-1, 0].into(),
1✔
3038
                        tile_size_in_pixels: [3, 2].into(),
1✔
3039
                        global_geo_transform: TestDefault::test_default(),
1✔
3040
                    },
1✔
3041
                    0,
1✔
3042
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
3043
                        .unwrap()
1✔
3044
                        .into(),
1✔
3045
                    CacheHint::default()
1✔
3046
                ),
1✔
3047
                RasterTile2D::new_with_tile_info(
1✔
3048
                    TimeInterval::new_unchecked(0, 20),
1✔
3049
                    TileInformation {
1✔
3050
                        global_tile_position: [-1, 0].into(),
1✔
3051
                        tile_size_in_pixels: [3, 2].into(),
1✔
3052
                        global_geo_transform: TestDefault::test_default(),
1✔
3053
                    },
1✔
3054
                    1,
1✔
3055
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
3056
                        .unwrap()
1✔
3057
                        .into(),
1✔
3058
                    CacheHint::default()
1✔
3059
                ),
1✔
3060
                RasterTile2D::new_with_tile_info(
1✔
3061
                    TimeInterval::new_unchecked(0, 20),
1✔
3062
                    TileInformation {
1✔
3063
                        global_tile_position: [-1, 1].into(),
1✔
3064
                        tile_size_in_pixels: [3, 2].into(),
1✔
3065
                        global_geo_transform: TestDefault::test_default(),
1✔
3066
                    },
1✔
3067
                    0,
1✔
3068
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
3069
                        .unwrap()
1✔
3070
                        .into(),
1✔
3071
                    CacheHint::default()
1✔
3072
                ),
1✔
3073
                RasterTile2D::new_with_tile_info(
1✔
3074
                    TimeInterval::new_unchecked(0, 20),
1✔
3075
                    TileInformation {
1✔
3076
                        global_tile_position: [-1, 1].into(),
1✔
3077
                        tile_size_in_pixels: [3, 2].into(),
1✔
3078
                        global_geo_transform: TestDefault::test_default(),
1✔
3079
                    },
1✔
3080
                    1,
1✔
3081
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
3082
                        .unwrap()
1✔
3083
                        .into(),
1✔
3084
                    CacheHint::default()
1✔
3085
                ),
1✔
3086
                RasterTile2D::new_with_tile_info(
1✔
3087
                    TimeInterval::new_unchecked(20, 40),
1✔
3088
                    TileInformation {
1✔
3089
                        global_tile_position: [-1, 0].into(),
1✔
3090
                        tile_size_in_pixels: [3, 2].into(),
1✔
3091
                        global_geo_transform: TestDefault::test_default(),
1✔
3092
                    },
1✔
3093
                    0,
1✔
3094
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
3095
                        .unwrap()
1✔
3096
                        .into(),
1✔
3097
                    CacheHint::default()
1✔
3098
                ),
1✔
3099
                RasterTile2D::new_with_tile_info(
1✔
3100
                    TimeInterval::new_unchecked(20, 40),
1✔
3101
                    TileInformation {
1✔
3102
                        global_tile_position: [-1, 0].into(),
1✔
3103
                        tile_size_in_pixels: [3, 2].into(),
1✔
3104
                        global_geo_transform: TestDefault::test_default(),
1✔
3105
                    },
1✔
3106
                    1,
1✔
3107
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
3108
                        .unwrap()
1✔
3109
                        .into(),
1✔
3110
                    CacheHint::default()
1✔
3111
                ),
1✔
3112
                RasterTile2D::new_with_tile_info(
1✔
3113
                    TimeInterval::new_unchecked(20, 40),
1✔
3114
                    TileInformation {
1✔
3115
                        global_tile_position: [-1, 1].into(),
1✔
3116
                        tile_size_in_pixels: [3, 2].into(),
1✔
3117
                        global_geo_transform: TestDefault::test_default(),
1✔
3118
                    },
1✔
3119
                    0,
1✔
3120
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
3121
                        .unwrap()
1✔
3122
                        .into(),
1✔
3123
                    CacheHint::default()
1✔
3124
                ),
1✔
3125
                RasterTile2D::new_with_tile_info(
1✔
3126
                    TimeInterval::new_unchecked(20, 40),
1✔
3127
                    TileInformation {
1✔
3128
                        global_tile_position: [-1, 1].into(),
1✔
3129
                        tile_size_in_pixels: [3, 2].into(),
1✔
3130
                        global_geo_transform: TestDefault::test_default(),
1✔
3131
                    },
1✔
3132
                    1,
1✔
3133
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
3134
                        .unwrap()
1✔
3135
                        .into(),
1✔
3136
                    CacheHint::default()
1✔
3137
                )
1✔
3138
            ])
1✔
3139
        );
1✔
3140
    }
1✔
3141

3142
    #[tokio::test]
3143
    async fn it_estimates_a_median() {
1✔
3144
        let raster_tiles = make_raster();
1✔
3145

3146
        let result_descriptor = RasterResultDescriptor {
1✔
3147
            data_type: RasterDataType::U8,
1✔
3148
            spatial_reference: SpatialReference::epsg_4326().into(),
1✔
3149
            time: TimeDescriptor::new_regular_with_epoch(
1✔
3150
                Some(TimeInterval::new_unchecked(0, 40)),
1✔
3151
                TimeStep::millis(10).unwrap(),
1✔
3152
            ),
1✔
3153
            spatial_grid: SpatialGridDescriptor::source_from_parts(
1✔
3154
                GeoTransform::test_default(),
1✔
3155
                GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
3156
            ),
1✔
3157
            bands: RasterBandDescriptors::new_single_band(),
1✔
3158
        };
1✔
3159

3160
        let mrs = MockRasterSource {
1✔
3161
            params: MockRasterSourceParams {
1✔
3162
                data: raster_tiles,
1✔
3163
                result_descriptor,
1✔
3164
            },
1✔
3165
        }
1✔
3166
        .boxed();
1✔
3167

3168
        let agg = TemporalRasterAggregation {
1✔
3169
            params: TemporalRasterAggregationParameters {
1✔
3170
                aggregation: Aggregation::PercentileEstimate {
1✔
3171
                    percentile: 0.5,
1✔
3172
                    ignore_no_data: false,
1✔
3173
                },
1✔
3174
                window: TimeStep {
1✔
3175
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1✔
3176
                    step: 40,
1✔
3177
                },
1✔
3178
                window_reference: None,
1✔
3179
                output_type: None,
1✔
3180
            },
1✔
3181
            sources: SingleRasterSource { raster: mrs },
1✔
3182
        }
1✔
3183
        .boxed();
1✔
3184

3185
        let exe_ctx =
1✔
3186
            MockExecutionContext::new_with_tiling_spec(TilingSpecification::new([3, 2].into()));
1✔
3187
        let query_rect = RasterQueryRectangle::new(
1✔
3188
            GridBoundingBox2D::new([-3, -0], [-1, 3]).unwrap(),
1✔
3189
            TimeInterval::new_unchecked(0, 40),
1✔
3190
            BandSelection::first(),
1✔
3191
        );
3192

3193
        let query_ctx = exe_ctx.mock_query_context_test_default();
1✔
3194

3195
        let qp = agg
1✔
3196
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
3197
            .await
1✔
3198
            .unwrap()
1✔
3199
            .query_processor()
1✔
3200
            .unwrap()
1✔
3201
            .get_u8()
1✔
3202
            .unwrap();
1✔
3203

3204
        let result = qp
1✔
3205
            .query(query_rect, &query_ctx)
1✔
3206
            .await
1✔
3207
            .unwrap()
1✔
3208
            .map(Result::unwrap)
1✔
3209
            .collect::<Vec<_>>()
1✔
3210
            .await;
1✔
3211

3212
        assert_eq!(result.len(), 2);
1✔
3213

3214
        assert_eq!(
1✔
3215
            result[0].grid_array,
1✔
3216
            GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![6, 6, 6, 6, 6, 6]).unwrap())
1✔
3217
        );
1✔
3218
    }
1✔
3219
}
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

© 2026 Coveralls, Inc