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

geo-engine / geoengine / 3929938005

pending completion
3929938005

push

github

GitHub
Merge #713

84930 of 96741 relevant lines covered (87.79%)

79640.1 hits per line

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

96.01
/operators/src/processing/time_shift.rs
1
use crate::engine::{
2
    CreateSpan, ExecutionContext, InitializedRasterOperator, InitializedVectorOperator, Operator,
3
    OperatorName, QueryContext, RasterOperator, RasterQueryProcessor, RasterResultDescriptor,
4
    ResultDescriptor, SingleRasterOrVectorSource, TypedRasterQueryProcessor,
5
    TypedVectorQueryProcessor, VectorOperator, VectorQueryProcessor, VectorResultDescriptor,
6
};
7
use crate::util::input::RasterOrVectorOperator;
8
use crate::util::Result;
9
use async_trait::async_trait;
10
use futures::stream::BoxStream;
11
use futures::StreamExt;
12
use geoengine_datatypes::collections::{
13
    FeatureCollection, FeatureCollectionInfos, FeatureCollectionModifications,
14
};
15
use geoengine_datatypes::error::{BoxedResultExt, ErrorSource};
16
use geoengine_datatypes::primitives::{
17
    Duration, Geometry, RasterQueryRectangle, TimeGranularity, TimeInstance, TimeInterval,
18
};
19
use geoengine_datatypes::primitives::{TimeStep, VectorQueryRectangle};
20
use geoengine_datatypes::raster::{Pixel, RasterTile2D};
21
use geoengine_datatypes::util::arrow::ArrowTyped;
22
use serde::{Deserialize, Serialize};
23
use snafu::Snafu;
24
use tracing::{span, Level};
25

26
/// Project the query rectangle to a new time interval.
27
pub type TimeShift = Operator<TimeShiftParams, SingleRasterOrVectorSource>;
28

29
impl OperatorName for TimeShift {
30
    const TYPE_NAME: &'static str = "TimeShift";
31
}
32

33
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12✔
34
#[serde(tag = "type", rename_all = "camelCase")]
35
pub enum TimeShiftParams {
36
    /// Shift the query rectangle relative with a time step
37
    #[serde(rename_all = "camelCase")]
38
    Relative {
39
        granularity: TimeGranularity,
40
        value: i32,
41
    },
42
    /// Set the time interval to a fixed value
43
    #[serde(rename_all = "camelCase")]
44
    Absolute { time_interval: TimeInterval },
45
}
46

47
pub trait TimeShiftOperation: Send + Sync + Copy {
48
    type State: Send + Sync + Copy;
49

50
    fn shift(
51
        &self,
52
        time_interval: TimeInterval,
53
    ) -> Result<(TimeInterval, Self::State), TimeShiftError>;
54

55
    fn reverse_shift(
56
        &self,
57
        time_interval: TimeInterval,
58
        state: Self::State,
59
    ) -> Result<TimeInterval, TimeShiftError>;
60
}
61

62
#[derive(Debug, Clone, Copy)]
×
63
pub struct RelativeForwardShift {
64
    step: TimeStep,
65
}
66

67
impl TimeShiftOperation for RelativeForwardShift {
68
    type State = ();
69

70
    fn shift(
4✔
71
        &self,
4✔
72
        time_interval: TimeInterval,
4✔
73
    ) -> Result<(TimeInterval, Self::State), TimeShiftError> {
4✔
74
        let time_interval = time_interval + self.step;
4✔
75
        let time_interval = time_interval.boxed_context(error::TimeOverflow)?;
4✔
76
        Ok((time_interval, ()))
4✔
77
    }
4✔
78

79
    fn reverse_shift(
5✔
80
        &self,
5✔
81
        time_interval: TimeInterval,
5✔
82
        _state: Self::State,
5✔
83
    ) -> Result<TimeInterval, TimeShiftError> {
5✔
84
        let reversed_time_interval = time_interval - self.step;
5✔
85
        reversed_time_interval.boxed_context(error::TimeOverflow)
5✔
86
    }
5✔
87
}
88

89
#[derive(Debug, Clone, Copy)]
×
90
pub struct RelativeBackwardShift {
91
    step: TimeStep,
92
}
93

94
impl TimeShiftOperation for RelativeBackwardShift {
95
    type State = ();
96

97
    fn shift(
6✔
98
        &self,
6✔
99
        time_interval: TimeInterval,
6✔
100
    ) -> Result<(TimeInterval, Self::State), TimeShiftError> {
6✔
101
        let time_interval = time_interval - self.step;
6✔
102
        let time_interval = time_interval.boxed_context(error::TimeOverflow)?;
6✔
103
        Ok((time_interval, ()))
6✔
104
    }
6✔
105

106
    fn reverse_shift(
9✔
107
        &self,
9✔
108
        time_interval: TimeInterval,
9✔
109
        _state: Self::State,
9✔
110
    ) -> Result<TimeInterval, TimeShiftError> {
9✔
111
        let reversed_time_interval = time_interval + self.step;
9✔
112
        reversed_time_interval.boxed_context(error::TimeOverflow)
9✔
113
    }
9✔
114
}
115

116
#[derive(Debug, Clone, Copy)]
×
117
pub struct AbsoluteShift {
118
    time_interval: TimeInterval,
119
}
120

121
impl TimeShiftOperation for AbsoluteShift {
122
    type State = (Duration, Duration);
123

124
    fn shift(
4✔
125
        &self,
4✔
126
        time_interval: TimeInterval,
4✔
127
    ) -> Result<(TimeInterval, Self::State), TimeShiftError> {
4✔
128
        let time_start_difference = time_interval.start() - self.time_interval.start();
4✔
129
        let time_end_difference = time_interval.end() - self.time_interval.end();
4✔
130

4✔
131
        Ok((
4✔
132
            self.time_interval,
4✔
133
            (time_start_difference, time_end_difference),
4✔
134
        ))
4✔
135
    }
4✔
136

137
    fn reverse_shift(
7✔
138
        &self,
7✔
139
        time_interval: TimeInterval,
7✔
140
        (time_start_difference, time_end_difference): Self::State,
7✔
141
    ) -> Result<TimeInterval, TimeShiftError> {
7✔
142
        let t1 = time_interval.start() + time_start_difference.num_milliseconds();
7✔
143
        let t2 = time_interval.end() + time_end_difference.num_milliseconds();
7✔
144
        TimeInterval::new(t1, t2).boxed_context(error::FaultyTimeInterval { t1, t2 })
7✔
145
    }
7✔
146
}
147

148
#[derive(Debug, Snafu)]
×
149
#[snafu(visibility(pub(crate)), context(suffix(false)), module(error))]
150
pub enum TimeShiftError {
151
    #[snafu(display("Output type must match the type of the source"))]
152
    UnmatchedOutput,
153
    #[snafu(display("Shifting the time led to an overflowing time interval"))]
154
    TimeOverflow { source: Box<dyn ErrorSource> },
155
    #[snafu(display("Shifting the time to a faulty time interval: {t1} / {t2}"))]
156
    FaultyTimeInterval {
157
        source: Box<dyn ErrorSource>,
158
        t1: TimeInstance,
159
        t2: TimeInstance,
160
    },
161
    #[snafu(display("Modifying the timestamps of the feature collection failed"))]
162
    FeatureCollectionTimeModification { source: Box<dyn ErrorSource> },
163
}
164

165
#[typetag::serde]
×
166
#[async_trait]
167
impl VectorOperator for TimeShift {
168
    async fn _initialize(
2✔
169
        self: Box<Self>,
2✔
170
        context: &dyn ExecutionContext,
2✔
171
    ) -> Result<Box<dyn InitializedVectorOperator>> {
2✔
172
        match (self.sources.source, self.params) {
2✔
173
            (
174
                RasterOrVectorOperator::Vector(source),
×
175
                TimeShiftParams::Relative { granularity, value },
×
176
            ) if value.is_positive() => {
1✔
177
                let source = source.initialize(context).await?;
×
178

179
                let shift = RelativeForwardShift {
×
180
                    step: TimeStep {
×
181
                        granularity,
×
182
                        step: value.unsigned_abs(),
×
183
                    },
×
184
                };
×
185

×
186
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
×
187

×
188
                Ok(Box::new(InitializedVectorTimeShift {
×
189
                    source,
×
190
                    result_descriptor,
×
191
                    shift,
×
192
                }))
×
193
            }
194
            (
195
                RasterOrVectorOperator::Vector(source),
1✔
196
                TimeShiftParams::Relative { granularity, value },
1✔
197
            ) => {
198
                let source = source.initialize(context).await?;
1✔
199

200
                let shift = RelativeBackwardShift {
1✔
201
                    step: TimeStep {
1✔
202
                        granularity,
1✔
203
                        step: value.unsigned_abs(),
1✔
204
                    },
1✔
205
                };
1✔
206

1✔
207
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
1✔
208

1✔
209
                Ok(Box::new(InitializedVectorTimeShift {
1✔
210
                    source,
1✔
211
                    result_descriptor,
1✔
212
                    shift,
1✔
213
                }))
1✔
214
            }
215
            (
216
                RasterOrVectorOperator::Vector(source),
1✔
217
                TimeShiftParams::Absolute { time_interval },
1✔
218
            ) => {
219
                let source = source.initialize(context).await?;
1✔
220

221
                let shift = AbsoluteShift { time_interval };
1✔
222

1✔
223
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
1✔
224

1✔
225
                Ok(Box::new(InitializedVectorTimeShift {
1✔
226
                    source,
1✔
227
                    result_descriptor,
1✔
228
                    shift,
1✔
229
                }))
1✔
230
            }
231
            (RasterOrVectorOperator::Raster(_), _) => Err(TimeShiftError::UnmatchedOutput.into()),
×
232
        }
233
    }
4✔
234

235
    span_fn!(TimeShift);
×
236
}
237

238
#[typetag::serde]
×
239
#[async_trait]
240
impl RasterOperator for TimeShift {
241
    async fn _initialize(
4✔
242
        self: Box<Self>,
4✔
243
        context: &dyn ExecutionContext,
4✔
244
    ) -> Result<Box<dyn InitializedRasterOperator>> {
4✔
245
        match (self.sources.source, self.params) {
4✔
246
            (
247
                RasterOrVectorOperator::Raster(source),
1✔
248
                TimeShiftParams::Relative { granularity, value },
1✔
249
            ) if value.is_positive() => {
2✔
250
                let source = source.initialize(context).await?;
1✔
251

252
                let shift = RelativeForwardShift {
1✔
253
                    step: TimeStep {
1✔
254
                        granularity,
1✔
255
                        step: value.unsigned_abs(),
1✔
256
                    },
1✔
257
                };
1✔
258

1✔
259
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
1✔
260

1✔
261
                Ok(Box::new(InitializedRasterTimeShift {
1✔
262
                    source,
1✔
263
                    result_descriptor,
1✔
264
                    shift,
1✔
265
                }))
1✔
266
            }
267
            (
268
                RasterOrVectorOperator::Raster(source),
1✔
269
                TimeShiftParams::Relative { granularity, value },
1✔
270
            ) => {
271
                let source = source.initialize(context).await?;
1✔
272

273
                let shift = RelativeBackwardShift {
1✔
274
                    step: TimeStep {
1✔
275
                        granularity,
1✔
276
                        step: value.unsigned_abs(),
1✔
277
                    },
1✔
278
                };
1✔
279

1✔
280
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
1✔
281

1✔
282
                Ok(Box::new(InitializedRasterTimeShift {
1✔
283
                    source,
1✔
284
                    result_descriptor,
1✔
285
                    shift,
1✔
286
                }))
1✔
287
            }
288
            (
289
                RasterOrVectorOperator::Raster(source),
2✔
290
                TimeShiftParams::Absolute { time_interval },
2✔
291
            ) => {
292
                let source = source.initialize(context).await?;
2✔
293

294
                let shift = AbsoluteShift { time_interval };
2✔
295

2✔
296
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
2✔
297

2✔
298
                Ok(Box::new(InitializedRasterTimeShift {
2✔
299
                    source,
2✔
300
                    result_descriptor,
2✔
301
                    shift,
2✔
302
                }))
2✔
303
            }
304
            (RasterOrVectorOperator::Vector(_), _) => Err(TimeShiftError::UnmatchedOutput.into()),
×
305
        }
306
    }
8✔
307

308
    span_fn!(TimeShift);
×
309
}
310

311
fn shift_result_descriptor<R: ResultDescriptor, S: TimeShiftOperation>(
6✔
312
    result_descriptor: &R,
6✔
313
    shift: S,
6✔
314
) -> R {
6✔
315
    result_descriptor.map_time(|time| {
6✔
316
        if let Some(time) = time {
6✔
317
            shift.shift(*time).map(|r| r.0).ok()
2✔
318
        } else {
319
            None
4✔
320
        }
321
    })
6✔
322
}
6✔
323

324
pub struct InitializedVectorTimeShift<Shift: TimeShiftOperation> {
325
    source: Box<dyn InitializedVectorOperator>,
326
    result_descriptor: VectorResultDescriptor,
327
    shift: Shift,
328
}
329

330
pub struct InitializedRasterTimeShift<Shift: TimeShiftOperation> {
331
    source: Box<dyn InitializedRasterOperator>,
332
    result_descriptor: RasterResultDescriptor,
333
    shift: Shift,
334
}
335

336
impl<Shift: TimeShiftOperation + 'static> InitializedVectorOperator
337
    for InitializedVectorTimeShift<Shift>
338
{
339
    fn result_descriptor(&self) -> &VectorResultDescriptor {
×
340
        &self.result_descriptor
×
341
    }
×
342

343
    fn query_processor(&self) -> Result<TypedVectorQueryProcessor> {
2✔
344
        let source_processor = self.source.query_processor()?;
2✔
345

346
        Ok(
347
            call_on_generic_vector_processor!(source_processor, processor => VectorTimeShiftProcessor {
2✔
348
                processor,
2✔
349
                shift: self.shift,
2✔
350
            }.boxed().into()),
2✔
351
        )
352
    }
2✔
353
}
354

355
impl<Shift: TimeShiftOperation + 'static> InitializedRasterOperator
356
    for InitializedRasterTimeShift<Shift>
357
{
358
    fn result_descriptor(&self) -> &RasterResultDescriptor {
1✔
359
        &self.result_descriptor
1✔
360
    }
1✔
361

362
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor> {
4✔
363
        let source_processor = self.source.query_processor()?;
4✔
364

365
        Ok(
366
            call_on_generic_raster_processor!(source_processor, processor => RasterTimeShiftProcessor {
4✔
367
                processor,
4✔
368
                shift: self.shift,
4✔
369
            }.boxed().into()),
4✔
370
        )
371
    }
4✔
372
}
373

374
pub struct RasterTimeShiftProcessor<Q, P, Shift: TimeShiftOperation>
375
where
376
    Q: RasterQueryProcessor<RasterType = P>,
377
{
378
    processor: Q,
379
    shift: Shift,
380
}
381

382
pub struct VectorTimeShiftProcessor<Q, G, Shift: TimeShiftOperation>
383
where
384
    G: Geometry,
385
    Q: VectorQueryProcessor<VectorType = FeatureCollection<G>>,
386
{
387
    processor: Q,
388
    shift: Shift,
389
}
390

391
#[async_trait]
392
impl<Q, G, Shift> VectorQueryProcessor for VectorTimeShiftProcessor<Q, G, Shift>
393
where
394
    G: Geometry + ArrowTyped + 'static,
395
    Q: VectorQueryProcessor<VectorType = FeatureCollection<G>>,
396
    Shift: TimeShiftOperation + 'static,
397
{
398
    type VectorType = FeatureCollection<G>;
399

400
    async fn vector_query<'a>(
2✔
401
        &'a self,
2✔
402
        query: VectorQueryRectangle,
2✔
403
        ctx: &'a dyn QueryContext,
2✔
404
    ) -> Result<BoxStream<'a, Result<Self::VectorType>>> {
2✔
405
        let (time_interval, state) = self.shift.shift(query.time_interval)?;
2✔
406

407
        let query = VectorQueryRectangle {
2✔
408
            spatial_bounds: query.spatial_bounds,
2✔
409
            time_interval,
2✔
410
            spatial_resolution: query.spatial_resolution,
2✔
411
        };
2✔
412
        let stream = self.processor.vector_query(query, ctx).await?;
2✔
413

414
        let stream = stream.then(move |collection| async move {
2✔
415
            let collection = collection?;
2✔
416
            let shift = self.shift;
2✔
417

2✔
418
            crate::util::spawn_blocking(move || {
2✔
419
                let time_intervals = collection
2✔
420
                    .time_intervals()
2✔
421
                    .iter()
2✔
422
                    .map(move |time| shift.reverse_shift(*time, state))
3✔
423
                    .collect::<Result<Vec<TimeInterval>, TimeShiftError>>()?;
2✔
424

425
                collection
2✔
426
                    .replace_time(&time_intervals)
2✔
427
                    .boxed_context(error::FeatureCollectionTimeModification)
2✔
428
                    .map_err(Into::into)
2✔
429
            })
2✔
430
            .await?
2✔
431
        });
2✔
432

2✔
433
        Ok(stream.boxed())
2✔
434
    }
4✔
435
}
436

437
#[async_trait]
438
impl<Q, P, Shift> RasterQueryProcessor for RasterTimeShiftProcessor<Q, P, Shift>
439
where
440
    Q: RasterQueryProcessor<RasterType = P>,
441
    P: Pixel,
442
    Shift: TimeShiftOperation,
443
{
444
    type RasterType = P;
445

446
    async fn raster_query<'a>(
4✔
447
        &'a self,
4✔
448
        query: RasterQueryRectangle,
4✔
449
        ctx: &'a dyn QueryContext,
4✔
450
    ) -> Result<BoxStream<'a, Result<RasterTile2D<Self::RasterType>>>> {
4✔
451
        let (time_interval, state) = self.shift.shift(query.time_interval)?;
4✔
452
        let query = RasterQueryRectangle {
4✔
453
            spatial_bounds: query.spatial_bounds,
4✔
454
            time_interval,
4✔
455
            spatial_resolution: query.spatial_resolution,
4✔
456
        };
4✔
457
        let stream = self.processor.raster_query(query, ctx).await?;
4✔
458

459
        let stream = stream.map(move |raster| {
12✔
460
            // reverse time shift for results
461
            let mut raster = raster?;
12✔
462

463
            raster.time = self.shift.reverse_shift(raster.time, state)?;
12✔
464

465
            Ok(raster)
12✔
466
        });
12✔
467

4✔
468
        Ok(Box::pin(stream))
4✔
469
    }
8✔
470
}
471

472
#[cfg(test)]
473
mod tests {
474
    use super::*;
475

476
    use crate::{
477
        engine::{MockExecutionContext, MockQueryContext},
478
        mock::{MockFeatureCollectionSource, MockRasterSource, MockRasterSourceParams},
479
        processing::{Expression, ExpressionParams, ExpressionSources},
480
        source::{GdalSource, GdalSourceParameters},
481
        util::gdal::add_ndvi_dataset,
482
    };
483
    use futures::StreamExt;
484
    use geoengine_datatypes::{
485
        collections::MultiPointCollection,
486
        dataset::DatasetId,
487
        primitives::{
488
            BoundingBox2D, DateTime, Measurement, MultiPoint, SpatialPartition2D,
489
            SpatialResolution, TimeGranularity,
490
        },
491
        raster::{EmptyGrid2D, GridOrEmpty, RasterDataType, TileInformation, TilingSpecification},
492
        spatial_reference::SpatialReference,
493
        util::test::TestDefault,
494
    };
495

496
    #[test]
1✔
497
    fn test_ser_de_absolute() {
1✔
498
        let time_shift = TimeShift {
1✔
499
            sources: SingleRasterOrVectorSource {
1✔
500
                source: RasterOrVectorOperator::Raster(
1✔
501
                    GdalSource {
1✔
502
                        params: GdalSourceParameters {
1✔
503
                            data: DatasetId::from_u128(1337).into(),
1✔
504
                        },
1✔
505
                    }
1✔
506
                    .boxed(),
1✔
507
                ),
1✔
508
            },
1✔
509
            params: TimeShiftParams::Absolute {
1✔
510
                time_interval: TimeInterval::new_unchecked(
1✔
511
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
512
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
513
                ),
1✔
514
            },
1✔
515
        };
1✔
516

1✔
517
        let serialized = serde_json::to_value(&time_shift).unwrap();
1✔
518

1✔
519
        assert_eq!(
1✔
520
            serialized,
1✔
521
            serde_json::json!({
1✔
522
                "params": {
1✔
523
                    "type": "absolute",
1✔
524
                    "timeInterval": {
1✔
525
                        "start": 1_293_840_000_000_i64,
1✔
526
                        "end": 1_325_376_000_000_i64
1✔
527
                    }
1✔
528
                },
1✔
529
                "sources": {
1✔
530
                    "source": {
1✔
531
                        "type": "GdalSource",
1✔
532
                        "params": {
1✔
533
                            "data": {
1✔
534
                                "type": "internal",
1✔
535
                                "datasetId": "00000000-0000-0000-0000-000000000539"
1✔
536
                            }
1✔
537
                        }
1✔
538
                    }
1✔
539
                }
1✔
540
            })
1✔
541
        );
1✔
542

543
        let deserialized: TimeShift = serde_json::from_value(serialized).unwrap();
1✔
544

1✔
545
        assert_eq!(time_shift.params, deserialized.params);
1✔
546
    }
1✔
547

548
    #[test]
1✔
549
    fn test_ser_de_relative() {
1✔
550
        let time_shift = TimeShift {
1✔
551
            sources: SingleRasterOrVectorSource {
1✔
552
                source: RasterOrVectorOperator::Raster(
1✔
553
                    GdalSource {
1✔
554
                        params: GdalSourceParameters {
1✔
555
                            data: DatasetId::from_u128(1337).into(),
1✔
556
                        },
1✔
557
                    }
1✔
558
                    .boxed(),
1✔
559
                ),
1✔
560
            },
1✔
561
            params: TimeShiftParams::Relative {
1✔
562
                granularity: TimeGranularity::Years,
1✔
563
                value: 1,
1✔
564
            },
1✔
565
        };
1✔
566

1✔
567
        let serialized = serde_json::to_value(&time_shift).unwrap();
1✔
568

1✔
569
        assert_eq!(
1✔
570
            serialized,
1✔
571
            serde_json::json!({
1✔
572
                "params": {
1✔
573
                    "type": "relative",
1✔
574
                    "granularity": "years",
1✔
575
                    "value": 1
1✔
576
                },
1✔
577
                "sources": {
1✔
578
                    "source": {
1✔
579
                        "type": "GdalSource",
1✔
580
                        "params": {
1✔
581
                            "data": {
1✔
582
                                "type": "internal",
1✔
583
                                "datasetId": "00000000-0000-0000-0000-000000000539"
1✔
584
                            }
1✔
585
                        }
1✔
586
                    }
1✔
587
                }
1✔
588
            })
1✔
589
        );
1✔
590

591
        let deserialized: TimeShift = serde_json::from_value(serialized).unwrap();
1✔
592

1✔
593
        assert_eq!(time_shift.params, deserialized.params);
1✔
594
    }
1✔
595

596
    #[tokio::test]
1✔
597
    async fn test_absolute_vector_shift() {
1✔
598
        let execution_context = MockExecutionContext::test_default();
1✔
599
        let query_context = MockQueryContext::test_default();
1✔
600

1✔
601
        let source = MockFeatureCollectionSource::single(
1✔
602
            MultiPointCollection::from_data(
1✔
603
                MultiPoint::many(vec![(0., 0.), (1., 1.), (2., 2.)]).unwrap(),
1✔
604
                vec![
1✔
605
                    TimeInterval::new(
1✔
606
                        DateTime::new_utc(2009, 1, 1, 0, 0, 0),
1✔
607
                        DateTime::new_utc_with_millis(2010, 12, 31, 23, 59, 59, 999),
1✔
608
                    )
1✔
609
                    .unwrap(),
1✔
610
                    TimeInterval::new(
1✔
611
                        DateTime::new_utc(2009, 6, 3, 0, 0, 0),
1✔
612
                        DateTime::new_utc(2010, 7, 14, 0, 0, 0),
1✔
613
                    )
1✔
614
                    .unwrap(),
1✔
615
                    TimeInterval::new(
1✔
616
                        DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
617
                        DateTime::new_utc_with_millis(2011, 3, 31, 23, 59, 59, 999),
1✔
618
                    )
1✔
619
                    .unwrap(),
1✔
620
                ],
1✔
621
                Default::default(),
1✔
622
            )
1✔
623
            .unwrap(),
1✔
624
        );
1✔
625

1✔
626
        let time_shift = TimeShift {
1✔
627
            sources: SingleRasterOrVectorSource {
1✔
628
                source: RasterOrVectorOperator::Vector(source.boxed()),
1✔
629
            },
1✔
630
            params: TimeShiftParams::Absolute {
1✔
631
                time_interval: TimeInterval::new(
1✔
632
                    DateTime::new_utc(2009, 1, 1, 0, 0, 0),
1✔
633
                    DateTime::new_utc(2009, 6, 1, 0, 0, 0),
1✔
634
                )
1✔
635
                .unwrap(),
1✔
636
            },
1✔
637
        };
1✔
638

639
        let query_processor = VectorOperator::boxed(time_shift)
1✔
640
            .initialize(&execution_context)
1✔
641
            .await
×
642
            .unwrap()
1✔
643
            .query_processor()
1✔
644
            .unwrap()
1✔
645
            .multi_point()
1✔
646
            .unwrap();
1✔
647

648
        let mut stream = query_processor
1✔
649
            .vector_query(
1✔
650
                VectorQueryRectangle {
1✔
651
                    spatial_bounds: BoundingBox2D::new((0., 0.).into(), (2., 2.).into()).unwrap(),
1✔
652
                    time_interval: TimeInterval::new(
1✔
653
                        DateTime::new_utc(2009, 1, 1, 0, 0, 0),
1✔
654
                        DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
655
                    )
1✔
656
                    .unwrap(),
1✔
657
                    spatial_resolution: SpatialResolution::one(),
1✔
658
                },
1✔
659
                &query_context,
1✔
660
            )
1✔
661
            .await
×
662
            .unwrap();
1✔
663

1✔
664
        let mut result = Vec::new();
1✔
665
        while let Some(collection) = stream.next().await {
2✔
666
            result.push(collection.unwrap());
1✔
667
        }
1✔
668

669
        assert_eq!(result.len(), 1);
1✔
670

671
        let expected = MultiPointCollection::from_data(
1✔
672
            MultiPoint::many(vec![(0., 0.)]).unwrap(),
1✔
673
            vec![TimeInterval::new(
1✔
674
                DateTime::new_utc(2009, 1, 1, 0, 0, 0),
1✔
675
                DateTime::new_utc_with_millis(2013, 8, 1, 23, 59, 59, 999),
1✔
676
            )
1✔
677
            .unwrap()],
1✔
678
            Default::default(),
1✔
679
        )
1✔
680
        .unwrap();
1✔
681

1✔
682
        assert_eq!(result[0], expected);
1✔
683
    }
684

685
    #[tokio::test]
1✔
686
    async fn test_relative_vector_shift() {
1✔
687
        let execution_context = MockExecutionContext::test_default();
1✔
688
        let query_context = MockQueryContext::test_default();
1✔
689

1✔
690
        let source = MockFeatureCollectionSource::single(
1✔
691
            MultiPointCollection::from_data(
1✔
692
                MultiPoint::many(vec![(0., 0.), (1., 1.), (2., 2.)]).unwrap(),
1✔
693
                vec![
1✔
694
                    TimeInterval::new(
1✔
695
                        DateTime::new_utc(2009, 1, 1, 0, 0, 0),
1✔
696
                        DateTime::new_utc_with_millis(2010, 12, 31, 23, 59, 59, 999),
1✔
697
                    )
1✔
698
                    .unwrap(),
1✔
699
                    TimeInterval::new(
1✔
700
                        DateTime::new_utc(2009, 6, 3, 0, 0, 0),
1✔
701
                        DateTime::new_utc(2010, 7, 14, 0, 0, 0),
1✔
702
                    )
1✔
703
                    .unwrap(),
1✔
704
                    TimeInterval::new(
1✔
705
                        DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
706
                        DateTime::new_utc_with_millis(2011, 3, 31, 23, 59, 59, 999),
1✔
707
                    )
1✔
708
                    .unwrap(),
1✔
709
                ],
1✔
710
                Default::default(),
1✔
711
            )
1✔
712
            .unwrap(),
1✔
713
        );
1✔
714

1✔
715
        let time_shift = TimeShift {
1✔
716
            sources: SingleRasterOrVectorSource {
1✔
717
                source: RasterOrVectorOperator::Vector(source.boxed()),
1✔
718
            },
1✔
719
            params: TimeShiftParams::Relative {
1✔
720
                granularity: TimeGranularity::Years,
1✔
721
                value: -1,
1✔
722
            },
1✔
723
        };
1✔
724

725
        let query_processor = VectorOperator::boxed(time_shift)
1✔
726
            .initialize(&execution_context)
1✔
727
            .await
×
728
            .unwrap()
1✔
729
            .query_processor()
1✔
730
            .unwrap()
1✔
731
            .multi_point()
1✔
732
            .unwrap();
1✔
733

734
        let mut stream = query_processor
1✔
735
            .vector_query(
1✔
736
                VectorQueryRectangle {
1✔
737
                    spatial_bounds: BoundingBox2D::new((0., 0.).into(), (2., 2.).into()).unwrap(),
1✔
738
                    time_interval: TimeInterval::new(
1✔
739
                        DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
740
                        DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
741
                    )
1✔
742
                    .unwrap(),
1✔
743
                    spatial_resolution: SpatialResolution::one(),
1✔
744
                },
1✔
745
                &query_context,
1✔
746
            )
1✔
747
            .await
×
748
            .unwrap();
1✔
749

1✔
750
        let mut result = Vec::new();
1✔
751
        while let Some(collection) = stream.next().await {
2✔
752
            result.push(collection.unwrap());
1✔
753
        }
1✔
754

755
        assert_eq!(result.len(), 1);
1✔
756

757
        let expected = MultiPointCollection::from_data(
1✔
758
            MultiPoint::many(vec![(0., 0.), (1., 1.)]).unwrap(),
1✔
759
            vec![
1✔
760
                TimeInterval::new(
1✔
761
                    DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
762
                    DateTime::new_utc_with_millis(2011, 12, 31, 23, 59, 59, 999),
1✔
763
                )
1✔
764
                .unwrap(),
1✔
765
                TimeInterval::new(
1✔
766
                    DateTime::new_utc(2010, 6, 3, 0, 0, 0),
1✔
767
                    DateTime::new_utc(2011, 7, 14, 0, 0, 0),
1✔
768
                )
1✔
769
                .unwrap(),
1✔
770
            ],
1✔
771
            Default::default(),
1✔
772
        )
1✔
773
        .unwrap();
1✔
774

1✔
775
        assert_eq!(result[0], expected);
1✔
776
    }
777

778
    #[tokio::test]
1✔
779
    #[allow(clippy::too_many_lines)]
780
    async fn test_absolute_raster_shift() {
1✔
781
        let empty_grid = GridOrEmpty::Empty(EmptyGrid2D::<u8>::new([3, 2].into()));
1✔
782
        let raster_tiles = vec![
1✔
783
            RasterTile2D::new_with_tile_info(
1✔
784
                TimeInterval::new_unchecked(
1✔
785
                    DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
786
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
787
                ),
1✔
788
                TileInformation {
1✔
789
                    global_tile_position: [-1, 0].into(),
1✔
790
                    tile_size_in_pixels: [3, 2].into(),
1✔
791
                    global_geo_transform: TestDefault::test_default(),
1✔
792
                },
1✔
793
                empty_grid.clone(),
1✔
794
            ),
1✔
795
            RasterTile2D::new_with_tile_info(
1✔
796
                TimeInterval::new_unchecked(
1✔
797
                    DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
798
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
799
                ),
1✔
800
                TileInformation {
1✔
801
                    global_tile_position: [-1, 1].into(),
1✔
802
                    tile_size_in_pixels: [3, 2].into(),
1✔
803
                    global_geo_transform: TestDefault::test_default(),
1✔
804
                },
1✔
805
                empty_grid.clone(),
1✔
806
            ),
1✔
807
            RasterTile2D::new_with_tile_info(
1✔
808
                TimeInterval::new_unchecked(
1✔
809
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
810
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
811
                ),
1✔
812
                TileInformation {
1✔
813
                    global_tile_position: [-1, 0].into(),
1✔
814
                    tile_size_in_pixels: [3, 2].into(),
1✔
815
                    global_geo_transform: TestDefault::test_default(),
1✔
816
                },
1✔
817
                empty_grid.clone(),
1✔
818
            ),
1✔
819
            RasterTile2D::new_with_tile_info(
1✔
820
                TimeInterval::new_unchecked(
1✔
821
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
822
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
823
                ),
1✔
824
                TileInformation {
1✔
825
                    global_tile_position: [-1, 1].into(),
1✔
826
                    tile_size_in_pixels: [3, 2].into(),
1✔
827
                    global_geo_transform: TestDefault::test_default(),
1✔
828
                },
1✔
829
                empty_grid.clone(),
1✔
830
            ),
1✔
831
            RasterTile2D::new_with_tile_info(
1✔
832
                TimeInterval::new_unchecked(
1✔
833
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
834
                    DateTime::new_utc(2013, 1, 1, 0, 0, 0),
1✔
835
                ),
1✔
836
                TileInformation {
1✔
837
                    global_tile_position: [-1, 0].into(),
1✔
838
                    tile_size_in_pixels: [3, 2].into(),
1✔
839
                    global_geo_transform: TestDefault::test_default(),
1✔
840
                },
1✔
841
                empty_grid.clone(),
1✔
842
            ),
1✔
843
            RasterTile2D::new_with_tile_info(
1✔
844
                TimeInterval::new_unchecked(
1✔
845
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
846
                    DateTime::new_utc(2013, 1, 1, 0, 0, 0),
1✔
847
                ),
1✔
848
                TileInformation {
1✔
849
                    global_tile_position: [-1, 1].into(),
1✔
850
                    tile_size_in_pixels: [3, 2].into(),
1✔
851
                    global_geo_transform: TestDefault::test_default(),
1✔
852
                },
1✔
853
                empty_grid.clone(),
1✔
854
            ),
1✔
855
        ];
1✔
856

1✔
857
        let mrs = MockRasterSource {
1✔
858
            params: MockRasterSourceParams {
1✔
859
                data: raster_tiles,
1✔
860
                result_descriptor: RasterResultDescriptor {
1✔
861
                    data_type: RasterDataType::U8,
1✔
862
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
863
                    measurement: Measurement::Unitless,
1✔
864
                    time: None,
1✔
865
                    bbox: None,
1✔
866
                    resolution: None,
1✔
867
                },
1✔
868
            },
1✔
869
        }
1✔
870
        .boxed();
1✔
871

1✔
872
        let time_shift = TimeShift {
1✔
873
            sources: SingleRasterOrVectorSource {
1✔
874
                source: RasterOrVectorOperator::Raster(mrs),
1✔
875
            },
1✔
876
            params: TimeShiftParams::Absolute {
1✔
877
                time_interval: TimeInterval::new_unchecked(
1✔
878
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
879
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
880
                ),
1✔
881
            },
1✔
882
        };
1✔
883

1✔
884
        let execution_context = MockExecutionContext::new_with_tiling_spec(
1✔
885
            TilingSpecification::new((0., 0.).into(), [3, 2].into()),
1✔
886
        );
1✔
887
        let query_context = MockQueryContext::test_default();
1✔
888

889
        let query_processor = RasterOperator::boxed(time_shift)
1✔
890
            .initialize(&execution_context)
1✔
891
            .await
×
892
            .unwrap()
1✔
893
            .query_processor()
1✔
894
            .unwrap()
1✔
895
            .get_u8()
1✔
896
            .unwrap();
1✔
897

898
        let mut stream = query_processor
1✔
899
            .raster_query(
1✔
900
                RasterQueryRectangle {
1✔
901
                    spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
902
                        (0., 3.).into(),
1✔
903
                        (4., 0.).into(),
1✔
904
                    ),
1✔
905
                    time_interval: TimeInterval::new(
1✔
906
                        DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
907
                        DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
908
                    )
1✔
909
                    .unwrap(),
1✔
910
                    spatial_resolution: SpatialResolution::one(),
1✔
911
                },
1✔
912
                &query_context,
1✔
913
            )
1✔
914
            .await
×
915
            .unwrap();
1✔
916

1✔
917
        let mut result = Vec::new();
1✔
918
        while let Some(tile) = stream.next().await {
3✔
919
            result.push(tile.unwrap());
2✔
920
        }
2✔
921

922
        assert_eq!(result.len(), 2);
1✔
923

924
        assert_eq!(
1✔
925
            result[0].time,
1✔
926
            TimeInterval::new_unchecked(
1✔
927
                DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
928
                DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
929
            ),
1✔
930
        );
1✔
931
        assert_eq!(
1✔
932
            result[1].time,
1✔
933
            TimeInterval::new_unchecked(
1✔
934
                DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
935
                DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
936
            ),
1✔
937
        );
1✔
938
    }
939

940
    #[tokio::test]
1✔
941
    #[allow(clippy::too_many_lines)]
942
    async fn test_relative_raster_shift() {
1✔
943
        let empty_grid = GridOrEmpty::Empty(EmptyGrid2D::<u8>::new([3, 2].into()));
1✔
944
        let raster_tiles = vec![
1✔
945
            RasterTile2D::new_with_tile_info(
1✔
946
                TimeInterval::new_unchecked(
1✔
947
                    DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
948
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
949
                ),
1✔
950
                TileInformation {
1✔
951
                    global_tile_position: [-1, 0].into(),
1✔
952
                    tile_size_in_pixels: [3, 2].into(),
1✔
953
                    global_geo_transform: TestDefault::test_default(),
1✔
954
                },
1✔
955
                empty_grid.clone(),
1✔
956
            ),
1✔
957
            RasterTile2D::new_with_tile_info(
1✔
958
                TimeInterval::new_unchecked(
1✔
959
                    DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
960
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
961
                ),
1✔
962
                TileInformation {
1✔
963
                    global_tile_position: [-1, 1].into(),
1✔
964
                    tile_size_in_pixels: [3, 2].into(),
1✔
965
                    global_geo_transform: TestDefault::test_default(),
1✔
966
                },
1✔
967
                empty_grid.clone(),
1✔
968
            ),
1✔
969
            RasterTile2D::new_with_tile_info(
1✔
970
                TimeInterval::new_unchecked(
1✔
971
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
972
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
973
                ),
1✔
974
                TileInformation {
1✔
975
                    global_tile_position: [-1, 0].into(),
1✔
976
                    tile_size_in_pixels: [3, 2].into(),
1✔
977
                    global_geo_transform: TestDefault::test_default(),
1✔
978
                },
1✔
979
                empty_grid.clone(),
1✔
980
            ),
1✔
981
            RasterTile2D::new_with_tile_info(
1✔
982
                TimeInterval::new_unchecked(
1✔
983
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
984
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
985
                ),
1✔
986
                TileInformation {
1✔
987
                    global_tile_position: [-1, 1].into(),
1✔
988
                    tile_size_in_pixels: [3, 2].into(),
1✔
989
                    global_geo_transform: TestDefault::test_default(),
1✔
990
                },
1✔
991
                empty_grid.clone(),
1✔
992
            ),
1✔
993
            RasterTile2D::new_with_tile_info(
1✔
994
                TimeInterval::new_unchecked(
1✔
995
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
996
                    DateTime::new_utc(2013, 1, 1, 0, 0, 0),
1✔
997
                ),
1✔
998
                TileInformation {
1✔
999
                    global_tile_position: [-1, 0].into(),
1✔
1000
                    tile_size_in_pixels: [3, 2].into(),
1✔
1001
                    global_geo_transform: TestDefault::test_default(),
1✔
1002
                },
1✔
1003
                empty_grid.clone(),
1✔
1004
            ),
1✔
1005
            RasterTile2D::new_with_tile_info(
1✔
1006
                TimeInterval::new_unchecked(
1✔
1007
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
1008
                    DateTime::new_utc(2013, 1, 1, 0, 0, 0),
1✔
1009
                ),
1✔
1010
                TileInformation {
1✔
1011
                    global_tile_position: [-1, 1].into(),
1✔
1012
                    tile_size_in_pixels: [3, 2].into(),
1✔
1013
                    global_geo_transform: TestDefault::test_default(),
1✔
1014
                },
1✔
1015
                empty_grid.clone(),
1✔
1016
            ),
1✔
1017
        ];
1✔
1018

1✔
1019
        let mrs = MockRasterSource {
1✔
1020
            params: MockRasterSourceParams {
1✔
1021
                data: raster_tiles,
1✔
1022
                result_descriptor: RasterResultDescriptor {
1✔
1023
                    data_type: RasterDataType::U8,
1✔
1024
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1025
                    measurement: Measurement::Unitless,
1✔
1026
                    time: None,
1✔
1027
                    bbox: None,
1✔
1028
                    resolution: None,
1✔
1029
                },
1✔
1030
            },
1✔
1031
        }
1✔
1032
        .boxed();
1✔
1033

1✔
1034
        let time_shift = TimeShift {
1✔
1035
            sources: SingleRasterOrVectorSource {
1✔
1036
                source: RasterOrVectorOperator::Raster(mrs),
1✔
1037
            },
1✔
1038
            params: TimeShiftParams::Relative {
1✔
1039
                granularity: TimeGranularity::Years,
1✔
1040
                value: 1,
1✔
1041
            },
1✔
1042
        };
1✔
1043

1✔
1044
        let execution_context = MockExecutionContext::new_with_tiling_spec(
1✔
1045
            TilingSpecification::new((0., 0.).into(), [3, 2].into()),
1✔
1046
        );
1✔
1047
        let query_context = MockQueryContext::test_default();
1✔
1048

1049
        let query_processor = RasterOperator::boxed(time_shift)
1✔
1050
            .initialize(&execution_context)
1✔
1051
            .await
×
1052
            .unwrap()
1✔
1053
            .query_processor()
1✔
1054
            .unwrap()
1✔
1055
            .get_u8()
1✔
1056
            .unwrap();
1✔
1057

1058
        let mut stream = query_processor
1✔
1059
            .raster_query(
1✔
1060
                RasterQueryRectangle {
1✔
1061
                    spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1062
                        (0., 3.).into(),
1✔
1063
                        (4., 0.).into(),
1✔
1064
                    ),
1✔
1065
                    time_interval: TimeInterval::new(
1✔
1066
                        DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
1067
                        DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
1068
                    )
1✔
1069
                    .unwrap(),
1✔
1070
                    spatial_resolution: SpatialResolution::one(),
1✔
1071
                },
1✔
1072
                &query_context,
1✔
1073
            )
1✔
1074
            .await
×
1075
            .unwrap();
1✔
1076

1✔
1077
        let mut result = Vec::new();
1✔
1078
        while let Some(tile) = stream.next().await {
3✔
1079
            result.push(tile.unwrap());
2✔
1080
        }
2✔
1081

1082
        assert_eq!(result.len(), 2);
1✔
1083

1084
        assert_eq!(
1✔
1085
            result[0].time,
1✔
1086
            TimeInterval::new_unchecked(
1✔
1087
                DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
1088
                DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
1089
            ),
1✔
1090
        );
1✔
1091
        assert_eq!(
1✔
1092
            result[1].time,
1✔
1093
            TimeInterval::new_unchecked(
1✔
1094
                DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
1095
                DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
1096
            ),
1✔
1097
        );
1✔
1098
    }
1099

1100
    #[tokio::test]
1✔
1101
    async fn test_expression_on_shifted_raster() {
1✔
1102
        let mut execution_context = MockExecutionContext::test_default();
1✔
1103

1✔
1104
        let ndvi_source = GdalSource {
1✔
1105
            params: GdalSourceParameters {
1✔
1106
                data: add_ndvi_dataset(&mut execution_context),
1✔
1107
            },
1✔
1108
        }
1✔
1109
        .boxed();
1✔
1110

1✔
1111
        let shifted_ndvi_source = RasterOperator::boxed(TimeShift {
1✔
1112
            params: TimeShiftParams::Relative {
1✔
1113
                granularity: TimeGranularity::Months,
1✔
1114
                value: -1,
1✔
1115
            },
1✔
1116
            sources: SingleRasterOrVectorSource {
1✔
1117
                source: RasterOrVectorOperator::Raster(ndvi_source.clone()),
1✔
1118
            },
1✔
1119
        });
1✔
1120

1✔
1121
        let expression = Expression {
1✔
1122
            params: ExpressionParams {
1✔
1123
                expression: "A - B".to_string(),
1✔
1124
                output_type: RasterDataType::F64,
1✔
1125
                output_measurement: None,
1✔
1126
                map_no_data: false,
1✔
1127
            },
1✔
1128
            sources: ExpressionSources::new_a_b(ndvi_source, shifted_ndvi_source),
1✔
1129
        }
1✔
1130
        .boxed();
1✔
1131

1132
        let query_processor = expression
1✔
1133
            .initialize(&execution_context)
1✔
1134
            .await
×
1135
            .unwrap()
1✔
1136
            .query_processor()
1✔
1137
            .unwrap()
1✔
1138
            .get_f64()
1✔
1139
            .unwrap();
1✔
1140

1✔
1141
        let query_context = MockQueryContext::test_default();
1✔
1142

1143
        let mut stream = query_processor
1✔
1144
            .raster_query(
1✔
1145
                RasterQueryRectangle {
1✔
1146
                    spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1147
                        (-180., 90.).into(),
1✔
1148
                        (180., -90.).into(),
1✔
1149
                    ),
1✔
1150
                    time_interval: TimeInterval::new_instant(DateTime::new_utc(
1✔
1151
                        2014, 3, 1, 0, 0, 0,
1✔
1152
                    ))
1✔
1153
                    .unwrap(),
1✔
1154
                    spatial_resolution: SpatialResolution::one(),
1✔
1155
                },
1✔
1156
                &query_context,
1✔
1157
            )
1✔
1158
            .await
×
1159
            .unwrap();
1✔
1160

1✔
1161
        let mut result = Vec::new();
1✔
1162
        while let Some(tile) = stream.next().await {
21✔
1163
            result.push(tile.unwrap());
4✔
1164
        }
4✔
1165

1166
        assert_eq!(result.len(), 4);
1✔
1167
        assert_eq!(
1✔
1168
            result[0].time,
1✔
1169
            TimeInterval::new(
1✔
1170
                DateTime::new_utc(2014, 3, 1, 0, 0, 0),
1✔
1171
                DateTime::new_utc(2014, 4, 1, 0, 0, 0)
1✔
1172
            )
1✔
1173
            .unwrap()
1✔
1174
        );
1✔
1175
    }
1176

1177
    #[tokio::test]
1✔
1178
    async fn test_expression_on_absolute_shifted_raster() {
1✔
1179
        let mut execution_context = MockExecutionContext::test_default();
1✔
1180

1✔
1181
        let ndvi_source = GdalSource {
1✔
1182
            params: GdalSourceParameters {
1✔
1183
                data: add_ndvi_dataset(&mut execution_context),
1✔
1184
            },
1✔
1185
        }
1✔
1186
        .boxed();
1✔
1187

1✔
1188
        let shifted_ndvi_source = RasterOperator::boxed(TimeShift {
1✔
1189
            params: TimeShiftParams::Absolute {
1✔
1190
                time_interval: TimeInterval::new_instant(DateTime::new_utc(2014, 5, 1, 0, 0, 0))
1✔
1191
                    .unwrap(),
1✔
1192
            },
1✔
1193
            sources: SingleRasterOrVectorSource {
1✔
1194
                source: RasterOrVectorOperator::Raster(ndvi_source),
1✔
1195
            },
1✔
1196
        });
1✔
1197

1198
        let query_processor = shifted_ndvi_source
1✔
1199
            .initialize(&execution_context)
1✔
1200
            .await
×
1201
            .unwrap()
1✔
1202
            .query_processor()
1✔
1203
            .unwrap()
1✔
1204
            .get_u8()
1✔
1205
            .unwrap();
1✔
1206

1✔
1207
        let query_context = MockQueryContext::test_default();
1✔
1208

1209
        let mut stream = query_processor
1✔
1210
            .raster_query(
1✔
1211
                RasterQueryRectangle {
1✔
1212
                    spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1213
                        (-180., 90.).into(),
1✔
1214
                        (180., -90.).into(),
1✔
1215
                    ),
1✔
1216
                    time_interval: TimeInterval::new_instant(DateTime::new_utc(
1✔
1217
                        2014, 3, 1, 0, 0, 0,
1✔
1218
                    ))
1✔
1219
                    .unwrap(),
1✔
1220
                    spatial_resolution: SpatialResolution::one(),
1✔
1221
                },
1✔
1222
                &query_context,
1✔
1223
            )
1✔
1224
            .await
×
1225
            .unwrap();
1✔
1226

1✔
1227
        let mut result = Vec::new();
1✔
1228
        while let Some(tile) = stream.next().await {
5✔
1229
            result.push(tile.unwrap());
4✔
1230
        }
4✔
1231

1232
        assert_eq!(result.len(), 4);
1✔
1233
        assert_eq!(
1✔
1234
            result[0].time,
1✔
1235
            TimeInterval::new(
1✔
1236
                DateTime::new_utc(2014, 3, 1, 0, 0, 0),
1✔
1237
                DateTime::new_utc(2014, 4, 1, 0, 0, 0)
1✔
1238
            )
1✔
1239
            .unwrap()
1✔
1240
        );
1✔
1241
    }
1242

1243
    #[test]
1✔
1244
    fn shift_eternal() {
1✔
1245
        let eternal = TimeInterval::default();
1✔
1246

1✔
1247
        let f_shift = RelativeForwardShift {
1✔
1248
            step: TimeStep {
1✔
1249
                granularity: TimeGranularity::Seconds,
1✔
1250
                step: 1,
1✔
1251
            },
1✔
1252
        };
1✔
1253

1✔
1254
        let (shifted, state) = f_shift.shift(eternal).unwrap();
1✔
1255
        assert_eq!(shifted, eternal);
1✔
1256
        let reverse_shifted = f_shift.reverse_shift(eternal, state).unwrap();
1✔
1257
        assert_eq!(reverse_shifted, eternal);
1✔
1258

1259
        let b_shift = RelativeBackwardShift {
1✔
1260
            step: TimeStep {
1✔
1261
                granularity: TimeGranularity::Seconds,
1✔
1262
                step: 1,
1✔
1263
            },
1✔
1264
        };
1✔
1265

1✔
1266
        let (shifted, state) = b_shift.shift(eternal).unwrap();
1✔
1267
        assert_eq!(shifted, eternal);
1✔
1268

1269
        let reverse_shifted = b_shift.reverse_shift(eternal, state).unwrap();
1✔
1270
        assert_eq!(reverse_shifted, eternal);
1✔
1271
    }
1✔
1272

1273
    #[test]
1✔
1274
    fn shift_begin_of_time() {
1✔
1275
        let time = TimeInterval::new_unchecked(TimeInstance::MIN, TimeInstance::EPOCH_START);
1✔
1276

1✔
1277
        let f_shift = RelativeForwardShift {
1✔
1278
            step: TimeStep {
1✔
1279
                granularity: TimeGranularity::Seconds,
1✔
1280
                step: 1,
1✔
1281
            },
1✔
1282
        };
1✔
1283

1✔
1284
        let expected_shift =
1✔
1285
            TimeInterval::new_unchecked(TimeInstance::MIN, TimeInstance::EPOCH_START + 1_000);
1✔
1286

1✔
1287
        let (shifted, state) = f_shift.shift(time).unwrap();
1✔
1288
        assert_eq!(shifted, expected_shift);
1✔
1289

1290
        let reverse_shifted = f_shift.reverse_shift(expected_shift, state).unwrap();
1✔
1291
        assert_eq!(reverse_shifted, time);
1✔
1292

1293
        let f_shift = RelativeBackwardShift {
1✔
1294
            step: TimeStep {
1✔
1295
                granularity: TimeGranularity::Seconds,
1✔
1296
                step: 1,
1✔
1297
            },
1✔
1298
        };
1✔
1299

1✔
1300
        let expected_shift =
1✔
1301
            TimeInterval::new_unchecked(TimeInstance::MIN, TimeInstance::EPOCH_START - 1_000);
1✔
1302

1✔
1303
        let (shifted, state) = f_shift.shift(time).unwrap();
1✔
1304
        assert_eq!(shifted, expected_shift);
1✔
1305

1306
        let reverse_shifted = f_shift.reverse_shift(expected_shift, state).unwrap();
1✔
1307
        assert_eq!(reverse_shifted, time);
1✔
1308
    }
1✔
1309

1310
    #[test]
1✔
1311
    fn shift_end_of_time() {
1✔
1312
        let time = TimeInterval::new_unchecked(TimeInstance::EPOCH_START, TimeInstance::MAX);
1✔
1313

1✔
1314
        let f_shift = RelativeForwardShift {
1✔
1315
            step: TimeStep {
1✔
1316
                granularity: TimeGranularity::Seconds,
1✔
1317
                step: 1,
1✔
1318
            },
1✔
1319
        };
1✔
1320

1✔
1321
        let expected_shift =
1✔
1322
            TimeInterval::new_unchecked(TimeInstance::EPOCH_START + 1_000, TimeInstance::MAX);
1✔
1323

1✔
1324
        let (shifted, state) = f_shift.shift(time).unwrap();
1✔
1325
        assert_eq!(shifted, expected_shift);
1✔
1326

1327
        let reverse_shifted = f_shift.reverse_shift(expected_shift, state).unwrap();
1✔
1328
        assert_eq!(reverse_shifted, time);
1✔
1329

1330
        let f_shift = RelativeBackwardShift {
1✔
1331
            step: TimeStep {
1✔
1332
                granularity: TimeGranularity::Seconds,
1✔
1333
                step: 1,
1✔
1334
            },
1✔
1335
        };
1✔
1336

1✔
1337
        let expected_shift =
1✔
1338
            TimeInterval::new_unchecked(TimeInstance::EPOCH_START - 1_000, TimeInstance::MAX);
1✔
1339

1✔
1340
        let (shifted, state) = f_shift.shift(time).unwrap();
1✔
1341
        assert_eq!(shifted, expected_shift);
1✔
1342

1343
        let reverse_shifted = f_shift.reverse_shift(expected_shift, state).unwrap();
1✔
1344
        assert_eq!(reverse_shifted, time);
1✔
1345
    }
1✔
1346
}
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