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

geo-engine / geoengine / 3676601107

pending completion
3676601107

push

github

GitHub
Merge #695

42001 of 50014 relevant lines covered (83.98%)

2.01 hits per line

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

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

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

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

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

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

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

83
        let source = self.sources.raster.initialize(context).await?;
2✔
84

85
        debug!(
3✔
86
            "Initializing TemporalRasterAggregation with {:?}.",
87
            &self.params
×
88
        );
89

90
        let initialized_operator = InitializedTemporalRasterAggregation {
91
            aggregation_type: self.params.aggregation,
1✔
92
            window: self.params.window,
1✔
93
            window_reference: self
1✔
94
                .params
95
                .window_reference
96
                .unwrap_or(TimeInstance::EPOCH_START),
97
            result_descriptor: source.result_descriptor().clone(),
1✔
98
            source,
99
            tiling_specification: context.tiling_specification(),
1✔
100
            output_type: self.params.output_type,
1✔
101
        };
102

103
        Ok(initialized_operator.boxed())
2✔
104
    }
105

106
    span_fn!(TemporalRasterAggregation);
107
}
108

109
pub struct InitializedTemporalRasterAggregation {
110
    aggregation_type: Aggregation,
111
    window: TimeStep,
112
    window_reference: TimeInstance,
113
    source: Box<dyn InitializedRasterOperator>,
114
    result_descriptor: RasterResultDescriptor,
115
    tiling_specification: TilingSpecification,
116
    output_type: Option<RasterDataType>,
117
}
118

119
impl InitializedRasterOperator for InitializedTemporalRasterAggregation {
120
    fn result_descriptor(&self) -> &RasterResultDescriptor {
×
121
        &self.result_descriptor
×
122
    }
123

124
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor> {
1✔
125
        let source_processor = self.source.query_processor()?;
1✔
126

127
        let source_processor: TypedRasterQueryProcessor = match self.output_type {
2✔
128
            Some(RasterDataType::U8) => source_processor.into_u8().into(),
×
129
            Some(RasterDataType::U16) => source_processor.into_u16().into(),
2✔
130
            Some(RasterDataType::U32) => source_processor.into_u32().into(),
×
131
            Some(RasterDataType::U64) => source_processor.into_u64().into(),
×
132
            Some(RasterDataType::I8) => source_processor.into_i8().into(),
×
133
            Some(RasterDataType::I16) => source_processor.into_i16().into(),
×
134
            Some(RasterDataType::I32) => source_processor.into_i32().into(),
×
135
            Some(RasterDataType::I64) => source_processor.into_i64().into(),
×
136
            Some(RasterDataType::F32) => source_processor.into_f32().into(),
×
137
            Some(RasterDataType::F64) => source_processor.into_f64().into(),
×
138
            // use the same output type as the input type
139
            None => source_processor,
1✔
140
        };
141

142
        let res = call_on_generic_raster_processor!(
2✔
143
            source_processor, p =>
1✔
144
            TemporalRasterAggregationProcessor::new(
4✔
145
                self.aggregation_type,
146
                self.window,
2✔
147
                self.window_reference,
2✔
148
                p,
149
                self.tiling_specification,
2✔
150
            ).boxed()
151
            .into()
152
        );
153

154
        Ok(res)
1✔
155
    }
156
}
157

158
pub struct TemporalRasterAggregationProcessor<Q, P>
159
where
160
    Q: RasterQueryProcessor<RasterType = P>,
161
    P: Pixel,
162
{
163
    aggregation_type: Aggregation,
164
    window: TimeStep,
165
    window_reference: TimeInstance,
166
    source: Q,
167
    tiling_specification: TilingSpecification,
168
}
169

170
impl<Q, P> TemporalRasterAggregationProcessor<Q, P>
171
where
172
    Q: RasterQueryProcessor<RasterType = P>,
173
    P: Pixel,
174
{
175
    fn new(
2✔
176
        aggregation_type: Aggregation,
177
        window: TimeStep,
178
        window_reference: TimeInstance,
179
        source: Q,
180
        tiling_specification: TilingSpecification,
181
    ) -> Self {
182
        Self {
183
            aggregation_type,
184
            window,
185
            window_reference,
186
            source,
187
            tiling_specification,
188
        }
189
    }
190

191
    fn create_subquery<F: TemporalRasterPixelAggregator<P> + 'static, FoldFn>(
12✔
192
        &self,
193
        fold_fn: FoldFn,
194
    ) -> super::subquery::TemporalRasterAggregationSubQuery<FoldFn, P, F> {
195
        super::subquery::TemporalRasterAggregationSubQuery {
196
            fold_fn,
197
            step: self.window,
12✔
198
            step_reference: self.window_reference,
12✔
199
            _phantom_pixel_type: PhantomData,
200
        }
201
    }
202

203
    fn create_subquery_first<F>(
1✔
204
        &self,
205
        fold_fn: F,
206
    ) -> TemporalRasterAggregationSubQueryNoDataOnly<F, P> {
207
        TemporalRasterAggregationSubQueryNoDataOnly {
208
            fold_fn,
209
            step: self.window,
1✔
210
            step_reference: self.window_reference,
1✔
211
            _phantom_pixel_type: PhantomData,
212
        }
213
    }
214

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

228
#[async_trait]
229
impl<Q, P> QueryProcessor for TemporalRasterAggregationProcessor<Q, P>
230
where
231
    Q: QueryProcessor<Output = RasterTile2D<P>, SpatialBounds = SpatialPartition2D>,
232
    P: Pixel,
233
{
234
    type Output = RasterTile2D<P>;
235
    type SpatialBounds = SpatialPartition2D;
236

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

283
            Aggregation::First {
2✔
284
                ignore_no_data: true,
×
285
            } => Ok(self
×
286
                .create_subquery(
×
287
                    super::subquery::subquery_all_tiles_fold_fn::<
×
288
                        P,
×
289
                        FirstPixelAggregatorIngoringNoData,
×
290
                    >,
×
291
                )
292
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
293
                .expect("no tiles must be skipped in Aggregation::First")),
1✔
294
            Aggregation::First {
2✔
295
                ignore_no_data: false,
×
296
            } => Ok(self
×
297
                .create_subquery_first(first_tile_fold_future::<P>)
×
298
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
299
                .expect("no tiles must be skipped in Aggregation::First")),
1✔
300
            Aggregation::Last {
2✔
301
                ignore_no_data: true,
×
302
            } => Ok(self
×
303
                .create_subquery(
×
304
                    super::subquery::subquery_all_tiles_fold_fn::<
×
305
                        P,
×
306
                        LastPixelAggregatorIngoringNoData,
×
307
                    >,
×
308
                )
309
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
310
                .expect("no tiles must be skipped in Aggregation::Last")),
1✔
311

312
            Aggregation::Last {
2✔
313
                ignore_no_data: false,
×
314
            } => Ok(self
×
315
                .create_subquery_last(last_tile_fold_future::<P>)
×
316
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
317
                .expect("no tiles must be skipped in Aggregation::Last")),
1✔
318

319
            Aggregation::Mean {
2✔
320
                ignore_no_data: true,
×
321
            } => Ok(self
×
322
                .create_subquery(
×
323
                    super::subquery::subquery_all_tiles_fold_fn::<
×
324
                        P,
×
325
                        MeanPixelAggregatorIngoringNoData,
×
326
                    >,
×
327
                )
328
                .into_raster_subquery_adapter(&self.source, query, ctx, self.tiling_specification)
1✔
329
                .expect("no tiles must be skipped in Aggregation::Mean")),
1✔
330

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

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

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

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

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

385
#[cfg(test)]
386
mod tests {
387
    use futures::stream::StreamExt;
388
    use geoengine_datatypes::{
389
        primitives::{Measurement, SpatialResolution, TimeInterval},
390
        raster::{
391
            EmptyGrid, EmptyGrid2D, Grid2D, GridOrEmpty, MaskedGrid2D, RasterDataType,
392
            TileInformation,
393
        },
394
        spatial_reference::SpatialReference,
395
        util::test::TestDefault,
396
    };
397

398
    use crate::{
399
        engine::{MockExecutionContext, MockQueryContext},
400
        mock::{MockRasterSource, MockRasterSourceParams},
401
        processing::{Expression, ExpressionParams, ExpressionSources},
402
    };
403

404
    use super::*;
405

406
    #[tokio::test]
2✔
407
    #[allow(clippy::too_many_lines)]
408
    async fn test_min() {
6✔
409
        let raster_tiles = make_raster();
1✔
410

411
        let mrs = MockRasterSource {
412
            params: MockRasterSourceParams {
1✔
413
                data: raster_tiles,
414
                result_descriptor: RasterResultDescriptor {
415
                    data_type: RasterDataType::U8,
416
                    spatial_reference: SpatialReference::epsg_4326().into(),
417
                    measurement: Measurement::Unitless,
418
                    time: None,
419
                    bbox: None,
420
                    resolution: None,
421
                },
422
            },
423
        }
424
        .boxed();
425

426
        let agg = TemporalRasterAggregation {
427
            params: TemporalRasterAggregationParameters {
1✔
428
                aggregation: Aggregation::Min {
429
                    ignore_no_data: false,
430
                },
431
                window: TimeStep {
432
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
433
                    step: 20,
434
                },
435
                window_reference: None,
436
                output_type: None,
437
            },
438
            sources: SingleRasterSource { raster: mrs },
1✔
439
        }
440
        .boxed();
441

442
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
443
            (0., 0.).into(),
1✔
444
            [3, 2].into(),
1✔
445
        ));
446
        let query_rect = RasterQueryRectangle {
447
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
448
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
449
            spatial_resolution: SpatialResolution::one(),
1✔
450
        };
451
        let query_ctx = MockQueryContext::test_default();
1✔
452

453
        let qp = agg
6✔
454
            .initialize(&exe_ctx)
1✔
455
            .await
4✔
456
            .unwrap()
457
            .query_processor()
458
            .unwrap()
459
            .get_u8()
460
            .unwrap();
461

462
        let result = qp
7✔
463
            .query(query_rect, &query_ctx)
1✔
464
            .await
4✔
465
            .unwrap()
466
            .collect::<Vec<_>>()
467
            .await;
4✔
468

469
        assert_eq!(result.len(), 4);
2✔
470

471
        assert_eq!(
1✔
472
            result[0].as_ref().unwrap(),
2✔
473
            &RasterTile2D::new_with_tile_info(
1✔
474
                TimeInterval::new_unchecked(0, 20),
1✔
475
                TileInformation {
1✔
476
                    global_tile_position: [-1, 0].into(),
1✔
477
                    tile_size_in_pixels: [3, 2].into(),
1✔
478
                    global_geo_transform: TestDefault::test_default(),
1✔
479
                },
480
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
481
            )
482
        );
483

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

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

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

524
    #[tokio::test]
2✔
525
    #[allow(clippy::too_many_lines)]
526
    async fn test_max() {
6✔
527
        let raster_tiles = make_raster();
1✔
528

529
        let mrs = MockRasterSource {
530
            params: MockRasterSourceParams {
1✔
531
                data: raster_tiles,
532
                result_descriptor: RasterResultDescriptor {
533
                    data_type: RasterDataType::U8,
534
                    spatial_reference: SpatialReference::epsg_4326().into(),
535
                    measurement: Measurement::Unitless,
536
                    time: None,
537
                    bbox: None,
538
                    resolution: None,
539
                },
540
            },
541
        }
542
        .boxed();
543

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

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

571
        let qp = agg
6✔
572
            .initialize(&exe_ctx)
1✔
573
            .await
4✔
574
            .unwrap()
575
            .query_processor()
576
            .unwrap()
577
            .get_u8()
578
            .unwrap();
579

580
        let result = qp
7✔
581
            .query(query_rect, &query_ctx)
1✔
582
            .await
4✔
583
            .unwrap()
584
            .collect::<Vec<_>>()
585
            .await;
4✔
586

587
        assert_eq!(result.len(), 4);
2✔
588

589
        assert_eq!(
1✔
590
            result[0].as_ref().unwrap(),
2✔
591
            &RasterTile2D::new_with_tile_info(
1✔
592
                TimeInterval::new_unchecked(0, 20),
1✔
593
                TileInformation {
1✔
594
                    global_tile_position: [-1, 0].into(),
1✔
595
                    tile_size_in_pixels: [3, 2].into(),
1✔
596
                    global_geo_transform: TestDefault::test_default(),
1✔
597
                },
598
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![12, 11, 10, 9, 8, 7]).unwrap()),
1✔
599
            )
600
        );
601

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

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

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

642
    #[tokio::test]
2✔
643
    #[allow(clippy::too_many_lines)]
644
    async fn test_max_with_no_data() {
6✔
645
        let raster_tiles = make_raster(); // TODO: switch to make_raster_with_no_data?
1✔
646

647
        let mrs = MockRasterSource {
648
            params: MockRasterSourceParams {
1✔
649
                data: raster_tiles,
650
                result_descriptor: RasterResultDescriptor {
651
                    data_type: RasterDataType::U8,
652
                    spatial_reference: SpatialReference::epsg_4326().into(),
653
                    measurement: Measurement::Unitless,
654
                    time: None,
655
                    bbox: None,
656
                    resolution: None,
657
                },
658
            },
659
        }
660
        .boxed();
661

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

678
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
679
            (0., 0.).into(),
1✔
680
            [3, 2].into(),
1✔
681
        ));
682
        let query_rect = RasterQueryRectangle {
683
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
684
            time_interval: TimeInterval::new_unchecked(0, 40),
1✔
685
            spatial_resolution: SpatialResolution::one(),
1✔
686
        };
687
        let query_ctx = MockQueryContext::test_default();
1✔
688

689
        let qp = agg
6✔
690
            .initialize(&exe_ctx)
1✔
691
            .await
4✔
692
            .unwrap()
693
            .query_processor()
694
            .unwrap()
695
            .get_u8()
696
            .unwrap();
697

698
        let result = qp
7✔
699
            .query(query_rect, &query_ctx)
1✔
700
            .await
4✔
701
            .unwrap()
702
            .collect::<Vec<_>>()
703
            .await;
4✔
704

705
        assert_eq!(result.len(), 4);
2✔
706

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

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

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

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

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

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

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

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

807
        let qp = agg
6✔
808
            .initialize(&exe_ctx)
1✔
809
            .await
4✔
810
            .unwrap()
811
            .query_processor()
812
            .unwrap()
813
            .get_u8()
814
            .unwrap();
815

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

823
        assert_eq!(result.len(), 4);
2✔
824

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

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

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

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

878
    #[tokio::test]
2✔
879
    #[allow(clippy::too_many_lines)]
880
    async fn test_only_no_data() {
6✔
881
        let mrs = MockRasterSource {
882
            params: MockRasterSourceParams {
1✔
883
                data: vec![RasterTile2D::new_with_tile_info(
884
                    TimeInterval::new_unchecked(0, 20),
885
                    TileInformation {
886
                        global_tile_position: [-1, 0].into(),
887
                        tile_size_in_pixels: [3, 2].into(),
888
                        global_geo_transform: TestDefault::test_default(),
889
                    },
890
                    GridOrEmpty::from(EmptyGrid2D::<u8>::new([3, 2].into())),
891
                )],
892
                result_descriptor: RasterResultDescriptor {
893
                    data_type: RasterDataType::U8,
894
                    spatial_reference: SpatialReference::epsg_4326().into(),
895
                    measurement: Measurement::Unitless,
896
                    time: None,
897
                    bbox: None,
898
                    resolution: None,
899
                },
900
            },
901
        }
902
        .boxed();
903

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

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

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

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

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

949
        assert_eq!(
3✔
950
            result[0].as_ref().unwrap(),
2✔
951
            &RasterTile2D::new_with_tile_info(
1✔
952
                TimeInterval::new_unchecked(0, 20),
1✔
953
                TileInformation {
1✔
954
                    global_tile_position: [-1, 0].into(),
1✔
955
                    tile_size_in_pixels: [3, 2].into(),
1✔
956
                    global_geo_transform: TestDefault::test_default(),
1✔
957
                },
958
                GridOrEmpty::Empty(EmptyGrid::new([3, 2].into())),
1✔
959
            )
960
        );
961
    }
962

963
    #[tokio::test]
2✔
964
    async fn test_first_with_no_data() {
6✔
965
        let raster_tiles = make_raster_with_no_data();
1✔
966

967
        let mrs = MockRasterSource {
968
            params: MockRasterSourceParams {
1✔
969
                data: raster_tiles,
970
                result_descriptor: RasterResultDescriptor {
971
                    data_type: RasterDataType::U8,
972
                    spatial_reference: SpatialReference::epsg_4326().into(),
973
                    measurement: Measurement::Unitless,
974
                    time: None,
975
                    bbox: None,
976
                    resolution: None,
977
                },
978
            },
979
        }
980
        .boxed();
981

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

998
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
999
            (0., 0.).into(),
1✔
1000
            [3, 2].into(),
1✔
1001
        ));
1002
        let query_rect = RasterQueryRectangle {
1003
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
1004
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1005
            spatial_resolution: SpatialResolution::one(),
1✔
1006
        };
1007
        let query_ctx = MockQueryContext::test_default();
1✔
1008

1009
        let qp = agg
6✔
1010
            .initialize(&exe_ctx)
1✔
1011
            .await
4✔
1012
            .unwrap()
1013
            .query_processor()
1014
            .unwrap()
1015
            .get_u8()
1016
            .unwrap();
1017

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

1025
        assert_eq!(result.len(), 2);
2✔
1026

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

1040
        assert_eq!(
3✔
1041
            result[1].as_ref().unwrap(),
1✔
1042
            &RasterTile2D::new_with_tile_info(
1✔
1043
                TimeInterval::new_unchecked(0, 30),
1✔
1044
                TileInformation {
1✔
1045
                    global_tile_position: [-1, 1].into(),
1✔
1046
                    tile_size_in_pixels: [3, 2].into(),
1✔
1047
                    global_geo_transform: TestDefault::test_default(),
1✔
1048
                },
1049
                GridOrEmpty::from(
1✔
1050
                    MaskedGrid2D::new(
2✔
1051
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
2✔
1052
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
2✔
1053
                            .unwrap()
1054
                    )
1055
                    .unwrap()
1056
                ),
1057
            )
1058
        );
1059
    }
1060

1061
    #[tokio::test]
2✔
1062
    async fn test_last_with_no_data() {
6✔
1063
        let raster_tiles = make_raster_with_no_data();
1✔
1064

1065
        let mrs = MockRasterSource {
1066
            params: MockRasterSourceParams {
1✔
1067
                data: raster_tiles,
1068
                result_descriptor: RasterResultDescriptor {
1069
                    data_type: RasterDataType::U8,
1070
                    spatial_reference: SpatialReference::epsg_4326().into(),
1071
                    measurement: Measurement::Unitless,
1072
                    time: None,
1073
                    bbox: None,
1074
                    resolution: None,
1075
                },
1076
            },
1077
        }
1078
        .boxed();
1079

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

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

1107
        let qp = agg
6✔
1108
            .initialize(&exe_ctx)
1✔
1109
            .await
4✔
1110
            .unwrap()
1111
            .query_processor()
1112
            .unwrap()
1113
            .get_u8()
1114
            .unwrap();
1115

1116
        let result = qp
7✔
1117
            .query(query_rect, &query_ctx)
1✔
1118
            .await
4✔
1119
            .unwrap()
1120
            .collect::<Vec<_>>()
1121
            .await;
4✔
1122

1123
        assert_eq!(result.len(), 2);
2✔
1124

1125
        assert_eq!(
1✔
1126
            result[0].as_ref().unwrap(),
2✔
1127
            &RasterTile2D::new_with_tile_info(
1✔
1128
                TimeInterval::new_unchecked(0, 30),
1✔
1129
                TileInformation {
1✔
1130
                    global_tile_position: [-1, 0].into(),
1✔
1131
                    tile_size_in_pixels: [3, 2].into(),
1✔
1132
                    global_geo_transform: TestDefault::test_default(),
1✔
1133
                },
1134
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![13, 8, 15, 16, 17, 18]).unwrap()),
1✔
1135
            )
1136
        );
1137

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

1159
    #[tokio::test]
2✔
1160
    async fn test_last() {
6✔
1161
        let raster_tiles = make_raster_with_no_data();
1✔
1162

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

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

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

1205
        let qp = agg
6✔
1206
            .initialize(&exe_ctx)
1✔
1207
            .await
4✔
1208
            .unwrap()
1209
            .query_processor()
1210
            .unwrap()
1211
            .get_u8()
1212
            .unwrap();
1213

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

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

1223
        assert_eq!(
1✔
1224
            result[0].as_ref().unwrap(),
2✔
1225
            &RasterTile2D::new_with_tile_info(
1✔
1226
                TimeInterval::new_unchecked(0, 30),
1✔
1227
                TileInformation {
1✔
1228
                    global_tile_position: [-1, 0].into(),
1✔
1229
                    tile_size_in_pixels: [3, 2].into(),
1✔
1230
                    global_geo_transform: TestDefault::test_default(),
1✔
1231
                },
1232
                GridOrEmpty::from(
1✔
1233
                    MaskedGrid2D::new(
2✔
1234
                        Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
2✔
1235
                        Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
2✔
1236
                            .unwrap()
1237
                    )
1238
                    .unwrap()
1239
                )
1240
            )
1241
        );
1242

1243
        assert_eq!(
3✔
1244
            result[1].as_ref().unwrap(),
1✔
1245
            &RasterTile2D::new_with_tile_info(
1✔
1246
                TimeInterval::new_unchecked(0, 30),
1✔
1247
                TileInformation {
1✔
1248
                    global_tile_position: [-1, 1].into(),
1✔
1249
                    tile_size_in_pixels: [3, 2].into(),
1✔
1250
                    global_geo_transform: TestDefault::test_default(),
1✔
1251
                },
1252
                GridOrEmpty::Empty(EmptyGrid2D::new([3, 2].into()))
1✔
1253
            )
1254
        );
1255
    }
1256

1257
    #[tokio::test]
2✔
1258
    async fn test_first() {
6✔
1259
        let raster_tiles = make_raster_with_no_data();
1✔
1260

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

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

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

1303
        let qp = agg
6✔
1304
            .initialize(&exe_ctx)
1✔
1305
            .await
4✔
1306
            .unwrap()
1307
            .query_processor()
1308
            .unwrap()
1309
            .get_u8()
1310
            .unwrap();
1311

1312
        let result = qp
7✔
1313
            .query(query_rect, &query_ctx)
1✔
1314
            .await
4✔
1315
            .unwrap()
1316
            .collect::<Vec<_>>()
1317
            .await;
4✔
1318

1319
        assert_eq!(result.len(), 2);
2✔
1320

1321
        assert_eq!(
1✔
1322
            result[0].as_ref().unwrap(),
2✔
1323
            &RasterTile2D::new_with_tile_info(
1✔
1324
                TimeInterval::new_unchecked(0, 30),
1✔
1325
                TileInformation {
1✔
1326
                    global_tile_position: [-1, 0].into(),
1✔
1327
                    tile_size_in_pixels: [3, 2].into(),
1✔
1328
                    global_geo_transform: TestDefault::test_default(),
1✔
1329
                },
1330
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into()))
1✔
1331
            )
1332
        );
1333

1334
        assert_eq!(
3✔
1335
            result[1].as_ref().unwrap(),
1✔
1336
            &RasterTile2D::new_with_tile_info(
1✔
1337
                TimeInterval::new_unchecked(0, 30),
1✔
1338
                TileInformation {
1✔
1339
                    global_tile_position: [-1, 1].into(),
1✔
1340
                    tile_size_in_pixels: [3, 2].into(),
1✔
1341
                    global_geo_transform: TestDefault::test_default(),
1✔
1342
                },
1343
                GridOrEmpty::from(
1✔
1344
                    MaskedGrid2D::new(
2✔
1345
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 42, 5, 6]).unwrap(),
2✔
1346
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
2✔
1347
                            .unwrap()
1348
                    )
1349
                    .unwrap()
1350
                ),
1351
            )
1352
        );
1353
    }
1354

1355
    #[tokio::test]
2✔
1356
    async fn test_mean_nodata() {
6✔
1357
        let raster_tiles = make_raster_with_no_data();
1✔
1358

1359
        let mrs = MockRasterSource {
1360
            params: MockRasterSourceParams {
1✔
1361
                data: raster_tiles,
1362
                result_descriptor: RasterResultDescriptor {
1363
                    data_type: RasterDataType::U8,
1364
                    spatial_reference: SpatialReference::epsg_4326().into(),
1365
                    measurement: Measurement::Unitless,
1366
                    time: None,
1367
                    bbox: None,
1368
                    resolution: None,
1369
                },
1370
            },
1371
        }
1372
        .boxed();
1373

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

1390
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1391
            (0., 0.).into(),
1✔
1392
            [3, 2].into(),
1✔
1393
        ));
1394
        let query_rect = RasterQueryRectangle {
1395
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
1396
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1397
            spatial_resolution: SpatialResolution::one(),
1✔
1398
        };
1399
        let query_ctx = MockQueryContext::test_default();
1✔
1400

1401
        let qp = agg
6✔
1402
            .initialize(&exe_ctx)
1✔
1403
            .await
4✔
1404
            .unwrap()
1405
            .query_processor()
1406
            .unwrap()
1407
            .get_u8()
1408
            .unwrap();
1409

1410
        let result = qp
7✔
1411
            .raster_query(query_rect, &query_ctx)
1✔
1412
            .await
4✔
1413
            .unwrap()
1414
            .collect::<Vec<_>>()
1415
            .await;
4✔
1416

1417
        assert_eq!(result.len(), 2);
2✔
1418

1419
        assert_eq!(
1✔
1420
            result[0].as_ref().unwrap(),
2✔
1421
            &RasterTile2D::new_with_tile_info(
1✔
1422
                TimeInterval::new_unchecked(0, 30),
1✔
1423
                TileInformation {
1✔
1424
                    global_tile_position: [-1, 0].into(),
1✔
1425
                    tile_size_in_pixels: [3, 2].into(),
1✔
1426
                    global_geo_transform: TestDefault::test_default(),
1✔
1427
                },
1428
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into()))
1✔
1429
            )
1430
        );
1431

1432
        assert_eq!(
3✔
1433
            result[1].as_ref().unwrap(),
1✔
1434
            &RasterTile2D::new_with_tile_info(
1✔
1435
                TimeInterval::new_unchecked(0, 30),
1✔
1436
                TileInformation {
1✔
1437
                    global_tile_position: [-1, 1].into(),
1✔
1438
                    tile_size_in_pixels: [3, 2].into(),
1✔
1439
                    global_geo_transform: TestDefault::test_default(),
1✔
1440
                },
1441
                GridOrEmpty::from(
1✔
1442
                    MaskedGrid2D::new(
2✔
1443
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
2✔
1444
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
2✔
1445
                            .unwrap()
1446
                    )
1447
                    .unwrap()
1448
                ),
1449
            )
1450
        );
1451
    }
1452

1453
    #[tokio::test]
2✔
1454
    async fn test_mean_ignore_nodata() {
6✔
1455
        let raster_tiles = make_raster_with_no_data();
1✔
1456

1457
        let mrs = MockRasterSource {
1458
            params: MockRasterSourceParams {
1✔
1459
                data: raster_tiles,
1460
                result_descriptor: RasterResultDescriptor {
1461
                    data_type: RasterDataType::U8,
1462
                    spatial_reference: SpatialReference::epsg_4326().into(),
1463
                    measurement: Measurement::Unitless,
1464
                    time: None,
1465
                    bbox: None,
1466
                    resolution: None,
1467
                },
1468
            },
1469
        }
1470
        .boxed();
1471

1472
        let agg = TemporalRasterAggregation {
1473
            params: TemporalRasterAggregationParameters {
1✔
1474
                aggregation: Aggregation::Mean {
1475
                    ignore_no_data: true,
1476
                },
1477
                window: TimeStep {
1478
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1479
                    step: 30,
1480
                },
1481
                window_reference: None,
1482
                output_type: None,
1483
            },
1484
            sources: SingleRasterSource { raster: mrs },
1✔
1485
        }
1486
        .boxed();
1487

1488
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1489
            (0., 0.).into(),
1✔
1490
            [3, 2].into(),
1✔
1491
        ));
1492
        let query_rect = RasterQueryRectangle {
1493
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
1494
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1495
            spatial_resolution: SpatialResolution::one(),
1✔
1496
        };
1497
        let query_ctx = MockQueryContext::test_default();
1✔
1498

1499
        let qp = agg
6✔
1500
            .initialize(&exe_ctx)
1✔
1501
            .await
4✔
1502
            .unwrap()
1503
            .query_processor()
1504
            .unwrap()
1505
            .get_u8()
1506
            .unwrap();
1507

1508
        let result = qp
7✔
1509
            .raster_query(query_rect, &query_ctx)
1✔
1510
            .await
4✔
1511
            .unwrap()
1512
            .collect::<Vec<_>>()
1513
            .await;
4✔
1514

1515
        assert_eq!(result.len(), 2);
2✔
1516

1517
        assert_eq!(
1✔
1518
            result[0].as_ref().unwrap(),
2✔
1519
            &RasterTile2D::new_with_tile_info(
1✔
1520
                TimeInterval::new_unchecked(0, 30),
1✔
1521
                TileInformation {
1✔
1522
                    global_tile_position: [-1, 0].into(),
1✔
1523
                    tile_size_in_pixels: [3, 2].into(),
1✔
1524
                    global_geo_transform: TestDefault::test_default(),
1✔
1525
                },
1526
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![10, 8, 12, 16, 14, 15]).unwrap())
1✔
1527
            )
1528
        );
1529

1530
        assert_eq!(
3✔
1531
            result[1].as_ref().unwrap(),
1✔
1532
            &RasterTile2D::new_with_tile_info(
1✔
1533
                TimeInterval::new_unchecked(0, 30),
1✔
1534
                TileInformation {
1✔
1535
                    global_tile_position: [-1, 1].into(),
1✔
1536
                    tile_size_in_pixels: [3, 2].into(),
1✔
1537
                    global_geo_transform: TestDefault::test_default(),
1✔
1538
                },
1539
                GridOrEmpty::from(
1✔
1540
                    MaskedGrid2D::new(
2✔
1541
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
2✔
1542
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
2✔
1543
                            .unwrap()
1544
                    )
1545
                    .unwrap()
1546
                ),
1547
            )
1548
        );
1549
    }
1550

1551
    #[tokio::test]
2✔
1552
    #[allow(clippy::too_many_lines)]
1553
    async fn test_sum_without_nodata() {
6✔
1554
        let operator = TemporalRasterAggregation {
1555
            params: TemporalRasterAggregationParameters {
1✔
1556
                aggregation: Aggregation::Sum {
1557
                    ignore_no_data: false,
1558
                },
1559
                window: TimeStep {
1560
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1561
                    step: 20,
1562
                },
1563
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
1564
                output_type: None,
1565
            },
1566
            sources: SingleRasterSource {
1✔
1567
                raster: MockRasterSource {
1568
                    params: MockRasterSourceParams {
1569
                        data: make_raster(),
1570
                        result_descriptor: RasterResultDescriptor {
1571
                            data_type: RasterDataType::U8,
1572
                            spatial_reference: SpatialReference::epsg_4326().into(),
1573
                            measurement: Measurement::Unitless,
1574
                            time: None,
1575
                            bbox: None,
1576
                            resolution: None,
1577
                        },
1578
                    },
1579
                }
1580
                .boxed(),
1581
            },
1582
        }
1583
        .boxed();
1584

1585
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1586
            (0., 0.).into(),
1✔
1587
            [3, 2].into(),
1✔
1588
        ));
1589
        let query_rect = RasterQueryRectangle {
1590
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
1591
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1592
            spatial_resolution: SpatialResolution::one(),
1✔
1593
        };
1594
        let query_ctx = MockQueryContext::test_default();
1✔
1595

1596
        let query_processor = operator
6✔
1597
            .initialize(&exe_ctx)
1✔
1598
            .await
4✔
1599
            .unwrap()
1600
            .query_processor()
1601
            .unwrap()
1602
            .get_u8()
1603
            .unwrap();
1604

1605
        let result = query_processor
7✔
1606
            .raster_query(query_rect, &query_ctx)
1✔
1607
            .await
4✔
1608
            .unwrap()
1609
            .map(Result::unwrap)
1610
            .collect::<Vec<_>>()
1611
            .await;
4✔
1612

1613
        assert_eq!(
3✔
1614
            result,
1615
            [
1616
                RasterTile2D::new_with_tile_info(
1✔
1617
                    TimeInterval::new_unchecked(0, 20),
1✔
1618
                    TileInformation {
1✔
1619
                        global_tile_position: [-1, 0].into(),
1✔
1620
                        tile_size_in_pixels: [3, 2].into(),
1✔
1621
                        global_geo_transform: TestDefault::test_default(),
1✔
1622
                    },
1623
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1624
                        .unwrap()
1625
                        .into()
1626
                ),
1627
                RasterTile2D::new_with_tile_info(
1✔
1628
                    TimeInterval::new_unchecked(0, 20),
1✔
1629
                    TileInformation {
1✔
1630
                        global_tile_position: [-1, 1].into(),
1✔
1631
                        tile_size_in_pixels: [3, 2].into(),
1✔
1632
                        global_geo_transform: TestDefault::test_default(),
1✔
1633
                    },
1634
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1635
                        .unwrap()
1636
                        .into(),
1637
                ),
1638
                RasterTile2D::new_with_tile_info(
1✔
1639
                    TimeInterval::new_unchecked(20, 40),
1✔
1640
                    TileInformation {
1✔
1641
                        global_tile_position: [-1, 0].into(),
1✔
1642
                        tile_size_in_pixels: [3, 2].into(),
1✔
1643
                        global_geo_transform: TestDefault::test_default(),
1✔
1644
                    },
1645
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1646
                        .unwrap()
1647
                        .into(),
1648
                ),
1649
                RasterTile2D::new_with_tile_info(
1✔
1650
                    TimeInterval::new_unchecked(20, 40),
1✔
1651
                    TileInformation {
1✔
1652
                        global_tile_position: [-1, 1].into(),
1✔
1653
                        tile_size_in_pixels: [3, 2].into(),
1✔
1654
                        global_geo_transform: TestDefault::test_default(),
1✔
1655
                    },
1656
                    Grid2D::new([3, 2].into(), vec![13, 13, 13, 13, 13, 13])
1✔
1657
                        .unwrap()
1658
                        .into(),
1659
                )
1660
            ]
1661
        );
1662
    }
1663

1664
    #[tokio::test]
2✔
1665
    async fn test_sum_nodata() {
6✔
1666
        let raster_tiles = make_raster_with_no_data();
1✔
1667

1668
        let mrs = MockRasterSource {
1669
            params: MockRasterSourceParams {
1✔
1670
                data: raster_tiles,
1671
                result_descriptor: RasterResultDescriptor {
1672
                    data_type: RasterDataType::U8,
1673
                    spatial_reference: SpatialReference::epsg_4326().into(),
1674
                    measurement: Measurement::Unitless,
1675
                    time: None,
1676
                    bbox: None,
1677
                    resolution: None,
1678
                },
1679
            },
1680
        }
1681
        .boxed();
1682

1683
        let agg = TemporalRasterAggregation {
1684
            params: TemporalRasterAggregationParameters {
1✔
1685
                aggregation: Aggregation::Sum {
1686
                    ignore_no_data: false,
1687
                },
1688
                window: TimeStep {
1689
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1690
                    step: 30,
1691
                },
1692
                window_reference: None,
1693
                output_type: None,
1694
            },
1695
            sources: SingleRasterSource { raster: mrs },
1✔
1696
        }
1697
        .boxed();
1698

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

1710
        let qp = agg
6✔
1711
            .initialize(&exe_ctx)
1✔
1712
            .await
4✔
1713
            .unwrap()
1714
            .query_processor()
1715
            .unwrap()
1716
            .get_u8()
1717
            .unwrap();
1718

1719
        let result = qp
7✔
1720
            .raster_query(query_rect, &query_ctx)
1✔
1721
            .await
4✔
1722
            .unwrap()
1723
            .collect::<Vec<_>>()
1724
            .await;
4✔
1725

1726
        assert_eq!(result.len(), 2);
2✔
1727

1728
        assert_eq!(
1✔
1729
            result[0].as_ref().unwrap(),
2✔
1730
            &RasterTile2D::new_with_tile_info(
1✔
1731
                TimeInterval::new_unchecked(0, 30),
1✔
1732
                TileInformation {
1✔
1733
                    global_tile_position: [-1, 0].into(),
1✔
1734
                    tile_size_in_pixels: [3, 2].into(),
1✔
1735
                    global_geo_transform: TestDefault::test_default(),
1✔
1736
                },
1737
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into()))
1✔
1738
            )
1739
        );
1740

1741
        assert_eq!(
3✔
1742
            result[1].as_ref().unwrap(),
1✔
1743
            &RasterTile2D::new_with_tile_info(
1✔
1744
                TimeInterval::new_unchecked(0, 30),
1✔
1745
                TileInformation {
1✔
1746
                    global_tile_position: [-1, 1].into(),
1✔
1747
                    tile_size_in_pixels: [3, 2].into(),
1✔
1748
                    global_geo_transform: TestDefault::test_default(),
1✔
1749
                },
1750
                GridOrEmpty::from(
1✔
1751
                    MaskedGrid2D::new(
2✔
1752
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
2✔
1753
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
2✔
1754
                            .unwrap()
1755
                    )
1756
                    .unwrap()
1757
                ),
1758
            )
1759
        );
1760
    }
1761

1762
    #[tokio::test]
2✔
1763
    async fn test_sum_ignore_nodata() {
6✔
1764
        let raster_tiles = make_raster_with_no_data();
1✔
1765

1766
        let mrs = MockRasterSource {
1767
            params: MockRasterSourceParams {
1✔
1768
                data: raster_tiles,
1769
                result_descriptor: RasterResultDescriptor {
1770
                    data_type: RasterDataType::U8,
1771
                    spatial_reference: SpatialReference::epsg_4326().into(),
1772
                    measurement: Measurement::Unitless,
1773
                    time: None,
1774
                    bbox: None,
1775
                    resolution: None,
1776
                },
1777
            },
1778
        }
1779
        .boxed();
1780

1781
        let agg = TemporalRasterAggregation {
1782
            params: TemporalRasterAggregationParameters {
1✔
1783
                aggregation: Aggregation::Sum {
1784
                    ignore_no_data: true,
1785
                },
1786
                window: TimeStep {
1787
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
1788
                    step: 30,
1789
                },
1790
                window_reference: None,
1791
                output_type: None,
1792
            },
1793
            sources: SingleRasterSource { raster: mrs },
1✔
1794
        }
1795
        .boxed();
1796

1797
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1798
            (0., 0.).into(),
1✔
1799
            [3, 2].into(),
1✔
1800
        ));
1801
        let query_rect = RasterQueryRectangle {
1802
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
1803
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1804
            spatial_resolution: SpatialResolution::one(),
1✔
1805
        };
1806
        let query_ctx = MockQueryContext::test_default();
1✔
1807

1808
        let qp = agg
6✔
1809
            .initialize(&exe_ctx)
1✔
1810
            .await
4✔
1811
            .unwrap()
1812
            .query_processor()
1813
            .unwrap()
1814
            .get_u8()
1815
            .unwrap();
1816

1817
        let result = qp
7✔
1818
            .raster_query(query_rect, &query_ctx)
1✔
1819
            .await
4✔
1820
            .unwrap()
1821
            .collect::<Vec<_>>()
1822
            .await;
4✔
1823

1824
        assert_eq!(result.len(), 2);
2✔
1825

1826
        assert_eq!(
1✔
1827
            result[0].as_ref().unwrap(),
2✔
1828
            &RasterTile2D::new_with_tile_info(
1✔
1829
                TimeInterval::new_unchecked(0, 30),
1✔
1830
                TileInformation {
1✔
1831
                    global_tile_position: [-1, 0].into(),
1✔
1832
                    tile_size_in_pixels: [3, 2].into(),
1✔
1833
                    global_geo_transform: TestDefault::test_default(),
1✔
1834
                },
1835
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![20, 8, 24, 16, 28, 30]).unwrap())
1✔
1836
            )
1837
        );
1838

1839
        assert_eq!(
3✔
1840
            result[1].as_ref().unwrap(),
1✔
1841
            &RasterTile2D::new_with_tile_info(
1✔
1842
                TimeInterval::new_unchecked(0, 30),
1✔
1843
                TileInformation {
1✔
1844
                    global_tile_position: [-1, 1].into(),
1✔
1845
                    tile_size_in_pixels: [3, 2].into(),
1✔
1846
                    global_geo_transform: TestDefault::test_default(),
1✔
1847
                },
1848
                GridOrEmpty::from(
1✔
1849
                    MaskedGrid2D::new(
2✔
1850
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 0, 5, 6]).unwrap(),
2✔
1851
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
2✔
1852
                            .unwrap()
1853
                    )
1854
                    .unwrap()
1855
                ),
1856
            )
1857
        );
1858
    }
1859

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

1905
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
1906
            (0., 0.).into(),
1✔
1907
            [3, 2].into(),
1✔
1908
        ));
1909
        let query_rect = RasterQueryRectangle {
1910
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
1911
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
1912
            spatial_resolution: SpatialResolution::one(),
1✔
1913
        };
1914
        let query_ctx = MockQueryContext::test_default();
1✔
1915

1916
        let query_processor = operator
6✔
1917
            .initialize(&exe_ctx)
1✔
1918
            .await
4✔
1919
            .unwrap()
1920
            .query_processor()
1921
            .unwrap()
1922
            .get_u16()
1923
            .unwrap();
1924

1925
        let result = query_processor
7✔
1926
            .raster_query(query_rect, &query_ctx)
1✔
1927
            .await
4✔
1928
            .unwrap()
1929
            .map(Result::unwrap)
1930
            .collect::<Vec<_>>()
1931
            .await;
4✔
1932

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

1996
    #[tokio::test]
2✔
1997
    #[allow(clippy::too_many_lines)]
1998
    async fn test_count_without_nodata() {
6✔
1999
        let operator = TemporalRasterAggregation {
2000
            params: TemporalRasterAggregationParameters {
1✔
2001
                aggregation: Aggregation::Count {
2002
                    ignore_no_data: false,
2003
                },
2004
                window: TimeStep {
2005
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
2006
                    step: 20,
2007
                },
2008
                window_reference: Some(TimeInstance::from_millis(0).unwrap()),
2009
                output_type: None,
2010
            },
2011
            sources: SingleRasterSource {
1✔
2012
                raster: MockRasterSource {
2013
                    params: MockRasterSourceParams {
2014
                        data: make_raster(),
2015
                        result_descriptor: RasterResultDescriptor {
2016
                            data_type: RasterDataType::U8,
2017
                            spatial_reference: SpatialReference::epsg_4326().into(),
2018
                            measurement: Measurement::Unitless,
2019
                            time: None,
2020
                            bbox: None,
2021
                            resolution: None,
2022
                        },
2023
                    },
2024
                }
2025
                .boxed(),
2026
            },
2027
        }
2028
        .boxed();
2029

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

2041
        let query_processor = operator
6✔
2042
            .initialize(&exe_ctx)
1✔
2043
            .await
4✔
2044
            .unwrap()
2045
            .query_processor()
2046
            .unwrap()
2047
            .get_u8()
2048
            .unwrap();
2049

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

2058
        assert_eq!(
3✔
2059
            result,
2060
            [
2061
                RasterTile2D::new_with_tile_info(
1✔
2062
                    TimeInterval::new_unchecked(0, 20),
1✔
2063
                    TileInformation {
1✔
2064
                        global_tile_position: [-1, 0].into(),
1✔
2065
                        tile_size_in_pixels: [3, 2].into(),
1✔
2066
                        global_geo_transform: TestDefault::test_default(),
1✔
2067
                    },
2068
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2069
                        .unwrap()
2070
                        .into()
2071
                ),
2072
                RasterTile2D::new_with_tile_info(
1✔
2073
                    TimeInterval::new_unchecked(0, 20),
1✔
2074
                    TileInformation {
1✔
2075
                        global_tile_position: [-1, 1].into(),
1✔
2076
                        tile_size_in_pixels: [3, 2].into(),
1✔
2077
                        global_geo_transform: TestDefault::test_default(),
1✔
2078
                    },
2079
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2080
                        .unwrap()
2081
                        .into(),
2082
                ),
2083
                RasterTile2D::new_with_tile_info(
1✔
2084
                    TimeInterval::new_unchecked(20, 40),
1✔
2085
                    TileInformation {
1✔
2086
                        global_tile_position: [-1, 0].into(),
1✔
2087
                        tile_size_in_pixels: [3, 2].into(),
1✔
2088
                        global_geo_transform: TestDefault::test_default(),
1✔
2089
                    },
2090
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2091
                        .unwrap()
2092
                        .into(),
2093
                ),
2094
                RasterTile2D::new_with_tile_info(
1✔
2095
                    TimeInterval::new_unchecked(20, 40),
1✔
2096
                    TileInformation {
1✔
2097
                        global_tile_position: [-1, 1].into(),
1✔
2098
                        tile_size_in_pixels: [3, 2].into(),
1✔
2099
                        global_geo_transform: TestDefault::test_default(),
1✔
2100
                    },
2101
                    Grid2D::new([3, 2].into(), vec![2, 2, 2, 2, 2, 2])
1✔
2102
                        .unwrap()
2103
                        .into(),
2104
                )
2105
            ]
2106
        );
2107
    }
2108

2109
    #[tokio::test]
2✔
2110
    async fn test_count_nodata() {
6✔
2111
        let raster_tiles = make_raster_with_no_data();
1✔
2112

2113
        let mrs = MockRasterSource {
2114
            params: MockRasterSourceParams {
1✔
2115
                data: raster_tiles,
2116
                result_descriptor: RasterResultDescriptor {
2117
                    data_type: RasterDataType::U8,
2118
                    spatial_reference: SpatialReference::epsg_4326().into(),
2119
                    measurement: Measurement::Unitless,
2120
                    time: None,
2121
                    bbox: None,
2122
                    resolution: None,
2123
                },
2124
            },
2125
        }
2126
        .boxed();
2127

2128
        let agg = TemporalRasterAggregation {
2129
            params: TemporalRasterAggregationParameters {
1✔
2130
                aggregation: Aggregation::Count {
2131
                    ignore_no_data: false,
2132
                },
2133
                window: TimeStep {
2134
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
2135
                    step: 30,
2136
                },
2137
                window_reference: None,
2138
                output_type: None,
2139
            },
2140
            sources: SingleRasterSource { raster: mrs },
1✔
2141
        }
2142
        .boxed();
2143

2144
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2145
            (0., 0.).into(),
1✔
2146
            [3, 2].into(),
1✔
2147
        ));
2148
        let query_rect = RasterQueryRectangle {
2149
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
2150
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2151
            spatial_resolution: SpatialResolution::one(),
1✔
2152
        };
2153
        let query_ctx = MockQueryContext::test_default();
1✔
2154

2155
        let qp = agg
6✔
2156
            .initialize(&exe_ctx)
1✔
2157
            .await
4✔
2158
            .unwrap()
2159
            .query_processor()
2160
            .unwrap()
2161
            .get_u8()
2162
            .unwrap();
2163

2164
        let result = qp
7✔
2165
            .raster_query(query_rect, &query_ctx)
1✔
2166
            .await
4✔
2167
            .unwrap()
2168
            .collect::<Vec<_>>()
2169
            .await;
4✔
2170

2171
        assert_eq!(result.len(), 2);
2✔
2172

2173
        assert_eq!(
1✔
2174
            result[0].as_ref().unwrap(),
2✔
2175
            &RasterTile2D::new_with_tile_info(
1✔
2176
                TimeInterval::new_unchecked(0, 30),
1✔
2177
                TileInformation {
1✔
2178
                    global_tile_position: [-1, 0].into(),
1✔
2179
                    tile_size_in_pixels: [3, 2].into(),
1✔
2180
                    global_geo_transform: TestDefault::test_default(),
1✔
2181
                },
2182
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into()))
1✔
2183
            )
2184
        );
2185

2186
        assert_eq!(
3✔
2187
            result[1].as_ref().unwrap(),
1✔
2188
            &RasterTile2D::new_with_tile_info(
1✔
2189
                TimeInterval::new_unchecked(0, 30),
1✔
2190
                TileInformation {
1✔
2191
                    global_tile_position: [-1, 1].into(),
1✔
2192
                    tile_size_in_pixels: [3, 2].into(),
1✔
2193
                    global_geo_transform: TestDefault::test_default(),
1✔
2194
                },
2195
                GridOrEmpty::from(
1✔
2196
                    MaskedGrid2D::new(
2✔
2197
                        Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
2✔
2198
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
2✔
2199
                            .unwrap()
2200
                    )
2201
                    .unwrap()
2202
                ),
2203
            )
2204
        );
2205
    }
2206

2207
    #[tokio::test]
2✔
2208
    async fn test_count_ignore_nodata() {
6✔
2209
        let raster_tiles = make_raster_with_no_data();
1✔
2210

2211
        let mrs = MockRasterSource {
2212
            params: MockRasterSourceParams {
1✔
2213
                data: raster_tiles,
2214
                result_descriptor: RasterResultDescriptor {
2215
                    data_type: RasterDataType::U8,
2216
                    spatial_reference: SpatialReference::epsg_4326().into(),
2217
                    measurement: Measurement::Unitless,
2218
                    time: None,
2219
                    bbox: None,
2220
                    resolution: None,
2221
                },
2222
            },
2223
        }
2224
        .boxed();
2225

2226
        let agg = TemporalRasterAggregation {
2227
            params: TemporalRasterAggregationParameters {
1✔
2228
                aggregation: Aggregation::Count {
2229
                    ignore_no_data: true,
2230
                },
2231
                window: TimeStep {
2232
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
2233
                    step: 30,
2234
                },
2235
                window_reference: None,
2236
                output_type: None,
2237
            },
2238
            sources: SingleRasterSource { raster: mrs },
1✔
2239
        }
2240
        .boxed();
2241

2242
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2243
            (0., 0.).into(),
1✔
2244
            [3, 2].into(),
1✔
2245
        ));
2246
        let query_rect = RasterQueryRectangle {
2247
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
2248
            time_interval: TimeInterval::new_unchecked(0, 30),
1✔
2249
            spatial_resolution: SpatialResolution::one(),
1✔
2250
        };
2251
        let query_ctx = MockQueryContext::test_default();
1✔
2252

2253
        let qp = agg
6✔
2254
            .initialize(&exe_ctx)
1✔
2255
            .await
4✔
2256
            .unwrap()
2257
            .query_processor()
2258
            .unwrap()
2259
            .get_u8()
2260
            .unwrap();
2261

2262
        let result = qp
7✔
2263
            .raster_query(query_rect, &query_ctx)
1✔
2264
            .await
4✔
2265
            .unwrap()
2266
            .collect::<Vec<_>>()
2267
            .await;
4✔
2268

2269
        assert_eq!(result.len(), 2);
2✔
2270

2271
        assert_eq!(
1✔
2272
            result[0].as_ref().unwrap(),
2✔
2273
            &RasterTile2D::new_with_tile_info(
1✔
2274
                TimeInterval::new_unchecked(0, 30),
1✔
2275
                TileInformation {
1✔
2276
                    global_tile_position: [-1, 0].into(),
1✔
2277
                    tile_size_in_pixels: [3, 2].into(),
1✔
2278
                    global_geo_transform: TestDefault::test_default(),
1✔
2279
                },
2280
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![2, 1, 2, 1, 2, 2]).unwrap())
1✔
2281
            )
2282
        );
2283

2284
        assert_eq!(
3✔
2285
            result[1].as_ref().unwrap(),
1✔
2286
            &RasterTile2D::new_with_tile_info(
1✔
2287
                TimeInterval::new_unchecked(0, 30),
1✔
2288
                TileInformation {
1✔
2289
                    global_tile_position: [-1, 1].into(),
1✔
2290
                    tile_size_in_pixels: [3, 2].into(),
1✔
2291
                    global_geo_transform: TestDefault::test_default(),
1✔
2292
                },
2293
                GridOrEmpty::from(
1✔
2294
                    MaskedGrid2D::new(
2✔
2295
                        Grid2D::new([3, 2].into(), vec![1, 1, 1, 0, 1, 1]).unwrap(),
2✔
2296
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
2✔
2297
                            .unwrap()
2298
                    )
2299
                    .unwrap()
2300
                ),
2301
            )
2302
        );
2303
    }
2304

2305
    #[tokio::test]
2✔
2306
    async fn test_query_not_aligned_with_window_reference() {
6✔
2307
        let raster_tiles = make_raster();
1✔
2308

2309
        let mrs = MockRasterSource {
2310
            params: MockRasterSourceParams {
1✔
2311
                data: raster_tiles,
2312
                result_descriptor: RasterResultDescriptor {
2313
                    data_type: RasterDataType::U8,
2314
                    spatial_reference: SpatialReference::epsg_4326().into(),
2315
                    measurement: Measurement::Unitless,
2316
                    time: None,
2317
                    bbox: None,
2318
                    resolution: None,
2319
                },
2320
            },
2321
        }
2322
        .boxed();
2323

2324
        let agg = TemporalRasterAggregation {
2325
            params: TemporalRasterAggregationParameters {
1✔
2326
                aggregation: Aggregation::Last {
2327
                    ignore_no_data: false,
2328
                },
2329
                window: TimeStep {
2330
                    granularity: geoengine_datatypes::primitives::TimeGranularity::Millis,
2331
                    step: 30,
2332
                },
2333
                window_reference: Some(TimeInstance::EPOCH_START),
2334
                output_type: None,
2335
            },
2336
            sources: SingleRasterSource { raster: mrs },
1✔
2337
        }
2338
        .boxed();
2339

2340
        let exe_ctx = MockExecutionContext::new_with_tiling_spec(TilingSpecification::new(
1✔
2341
            (0., 0.).into(),
1✔
2342
            [3, 2].into(),
1✔
2343
        ));
2344
        let query_rect = RasterQueryRectangle {
2345
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 3.).into(), (4., 0.).into()),
2✔
2346
            time_interval: TimeInterval::new_unchecked(5, 5),
1✔
2347
            spatial_resolution: SpatialResolution::one(),
1✔
2348
        };
2349
        let query_ctx = MockQueryContext::test_default();
1✔
2350

2351
        let qp = agg
6✔
2352
            .initialize(&exe_ctx)
1✔
2353
            .await
4✔
2354
            .unwrap()
2355
            .query_processor()
2356
            .unwrap()
2357
            .get_u8()
2358
            .unwrap();
2359

2360
        let result = qp
7✔
2361
            .query(query_rect, &query_ctx)
1✔
2362
            .await
4✔
2363
            .unwrap()
2364
            .collect::<Vec<_>>()
2365
            .await;
4✔
2366

2367
        assert_eq!(result.len(), 2);
2✔
2368
        assert_eq!(
1✔
2369
            result[0].as_ref().unwrap(),
2✔
2370
            &RasterTile2D::new_with_tile_info(
1✔
2371
                TimeInterval::new_unchecked(0, 30),
1✔
2372
                TileInformation {
1✔
2373
                    global_tile_position: [-1, 0].into(),
1✔
2374
                    tile_size_in_pixels: [3, 2].into(),
1✔
2375
                    global_geo_transform: TestDefault::test_default(),
1✔
2376
                },
2377
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 5, 6]).unwrap()),
1✔
2378
            )
2379
        );
2380

2381
        assert_eq!(
3✔
2382
            result[1].as_ref().unwrap(),
1✔
2383
            &RasterTile2D::new_with_tile_info(
1✔
2384
                TimeInterval::new_unchecked(0, 30),
1✔
2385
                TileInformation {
1✔
2386
                    global_tile_position: [-1, 1].into(),
1✔
2387
                    tile_size_in_pixels: [3, 2].into(),
1✔
2388
                    global_geo_transform: TestDefault::test_default(),
1✔
2389
                },
2390
                GridOrEmpty::from(Grid2D::new([3, 2].into(), vec![7, 8, 9, 10, 11, 12]).unwrap()),
1✔
2391
            )
2392
        );
2393
    }
2394

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

2473
    fn make_raster_with_no_data() -> Vec<geoengine_datatypes::raster::RasterTile2D<u8>> {
1✔
2474
        let raster_tiles = vec![
2✔
2475
            RasterTile2D::new_with_tile_info(
1✔
2476
                TimeInterval::new_unchecked(0, 10),
1✔
2477
                TileInformation {
1✔
2478
                    global_tile_position: [-1, 0].into(),
1✔
2479
                    tile_size_in_pixels: [3, 2].into(),
1✔
2480
                    global_geo_transform: TestDefault::test_default(),
1✔
2481
                },
2482
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
2483
            ),
2484
            RasterTile2D::new_with_tile_info(
1✔
2485
                TimeInterval::new_unchecked(0, 10),
1✔
2486
                TileInformation {
1✔
2487
                    global_tile_position: [-1, 1].into(),
1✔
2488
                    tile_size_in_pixels: [3, 2].into(),
1✔
2489
                    global_geo_transform: TestDefault::test_default(),
1✔
2490
                },
2491
                GridOrEmpty::from(
1✔
2492
                    MaskedGrid2D::new(
2✔
2493
                        Grid2D::new([3, 2].into(), vec![1, 2, 3, 42, 5, 6]).unwrap(),
1✔
2494
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
2✔
2495
                            .unwrap(),
2496
                    )
2497
                    .unwrap(),
2498
                ),
2499
            ),
2500
            RasterTile2D::new_with_tile_info(
1✔
2501
                TimeInterval::new_unchecked(10, 20),
1✔
2502
                TileInformation {
1✔
2503
                    global_tile_position: [-1, 0].into(),
1✔
2504
                    tile_size_in_pixels: [3, 2].into(),
1✔
2505
                    global_geo_transform: TestDefault::test_default(),
1✔
2506
                },
2507
                GridOrEmpty::from(
1✔
2508
                    MaskedGrid2D::new(
2✔
2509
                        Grid2D::new([3, 2].into(), vec![7, 8, 9, 42, 11, 12]).unwrap(),
1✔
2510
                        Grid2D::new([3, 2].into(), vec![true, true, true, false, true, true])
2✔
2511
                            .unwrap(),
2512
                    )
2513
                    .unwrap(),
2514
                ),
2515
            ),
2516
            RasterTile2D::new_with_tile_info(
1✔
2517
                TimeInterval::new_unchecked(10, 20),
1✔
2518
                TileInformation {
1✔
2519
                    global_tile_position: [-1, 1].into(),
1✔
2520
                    tile_size_in_pixels: [3, 2].into(),
1✔
2521
                    global_geo_transform: TestDefault::test_default(),
1✔
2522
                },
2523
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
2524
            ),
2525
            RasterTile2D::new_with_tile_info(
1✔
2526
                TimeInterval::new_unchecked(20, 30),
1✔
2527
                TileInformation {
1✔
2528
                    global_tile_position: [-1, 0].into(),
1✔
2529
                    tile_size_in_pixels: [3, 2].into(),
1✔
2530
                    global_geo_transform: TestDefault::test_default(),
1✔
2531
                },
2532
                GridOrEmpty::from(
1✔
2533
                    MaskedGrid2D::new(
2✔
2534
                        Grid2D::new([3, 2].into(), vec![13, 42, 15, 16, 17, 18]).unwrap(),
1✔
2535
                        Grid2D::new([3, 2].into(), vec![true, false, true, true, true, true])
2✔
2536
                            .unwrap(),
2537
                    )
2538
                    .unwrap(),
2539
                ),
2540
            ),
2541
            RasterTile2D::new_with_tile_info(
1✔
2542
                TimeInterval::new_unchecked(20, 30),
1✔
2543
                TileInformation {
1✔
2544
                    global_tile_position: [-1, 1].into(),
1✔
2545
                    tile_size_in_pixels: [3, 2].into(),
1✔
2546
                    global_geo_transform: TestDefault::test_default(),
1✔
2547
                },
2548
                GridOrEmpty::from(EmptyGrid2D::new([3, 2].into())),
1✔
2549
            ),
2550
        ];
2551
        raster_tiles
2552
    }
2553
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc