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

geo-engine / geoengine / 10178074589

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

push

github

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

Remove-XGB-update-toolchain

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

456 existing lines in 119 files now uncovered.

131088 of 143945 relevant lines covered (91.07%)

53581.03 hits per line

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

166
#[typetag::serde]
×
167
#[async_trait]
168
impl VectorOperator for TimeShift {
169
    async fn _initialize(
170
        self: Box<Self>,
171
        path: WorkflowOperatorPath,
172
        context: &dyn ExecutionContext,
173
    ) -> Result<Box<dyn InitializedVectorOperator>> {
2✔
174
        let name = CanonicOperatorName::from(&self);
2✔
175

2✔
176
        let init_sources = self.sources.initialize_sources(path, context).await?;
2✔
177

2✔
178
        match (init_sources.source, self.params) {
2✔
179
            (
2✔
180
                InitializedSingleRasterOrVectorOperator::Vector(source),
2✔
181
                TimeShiftParams::Relative { granularity, value },
×
182
            ) if value.is_positive() => {
2✔
183
                let shift = RelativeForwardShift {
×
184
                    step: TimeStep {
×
185
                        granularity,
×
186
                        step: value.unsigned_abs(),
×
187
                    },
×
188
                };
×
189

×
190
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
×
191

×
192
                Ok(Box::new(InitializedVectorTimeShift {
×
193
                    name,
×
194
                    source,
×
195
                    result_descriptor,
×
196
                    shift,
×
197
                }))
×
198
            }
2✔
199
            (
2✔
200
                InitializedSingleRasterOrVectorOperator::Vector(source),
2✔
201
                TimeShiftParams::Relative { granularity, value },
1✔
202
            ) => {
1✔
203
                let shift = RelativeBackwardShift {
1✔
204
                    step: TimeStep {
1✔
205
                        granularity,
1✔
206
                        step: value.unsigned_abs(),
1✔
207
                    },
1✔
208
                };
1✔
209

1✔
210
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
1✔
211

1✔
212
                Ok(Box::new(InitializedVectorTimeShift {
1✔
213
                    name,
1✔
214
                    source,
1✔
215
                    result_descriptor,
1✔
216
                    shift,
1✔
217
                }))
1✔
218
            }
2✔
219
            (
2✔
220
                InitializedSingleRasterOrVectorOperator::Vector(source),
2✔
221
                TimeShiftParams::Absolute { time_interval },
1✔
222
            ) => {
1✔
223
                let shift = AbsoluteShift { time_interval };
1✔
224

1✔
225
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
1✔
226

1✔
227
                Ok(Box::new(InitializedVectorTimeShift {
1✔
228
                    name,
1✔
229
                    source,
1✔
230
                    result_descriptor,
1✔
231
                    shift,
1✔
232
                }))
1✔
233
            }
2✔
234
            (InitializedSingleRasterOrVectorOperator::Raster(_), _) => {
2✔
235
                Err(TimeShiftError::UnmatchedOutput.into())
2✔
236
            }
2✔
237
        }
2✔
238
    }
2✔
239

240
    span_fn!(TimeShift);
241
}
242

243
#[typetag::serde]
2✔
244
#[async_trait]
245
impl RasterOperator for TimeShift {
246
    async fn _initialize(
247
        self: Box<Self>,
248
        path: WorkflowOperatorPath,
249
        context: &dyn ExecutionContext,
250
    ) -> Result<Box<dyn InitializedRasterOperator>> {
7✔
251
        let name = CanonicOperatorName::from(&self);
7✔
252

7✔
253
        let init_sources = self.sources.initialize_sources(path, context).await?;
7✔
254

7✔
255
        match (init_sources.source, self.params) {
7✔
256
            (
7✔
257
                InitializedSingleRasterOrVectorOperator::Raster(source),
7✔
258
                TimeShiftParams::Relative { granularity, value },
4✔
259
            ) if value.is_positive() => {
7✔
260
                let shift = RelativeForwardShift {
4✔
261
                    step: TimeStep {
4✔
262
                        granularity,
4✔
263
                        step: value.unsigned_abs(),
4✔
264
                    },
4✔
265
                };
4✔
266

4✔
267
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
4✔
268

4✔
269
                Ok(Box::new(InitializedRasterTimeShift {
4✔
270
                    name,
4✔
271
                    source,
4✔
272
                    result_descriptor,
4✔
273
                    shift,
4✔
274
                }))
4✔
275
            }
7✔
276
            (
7✔
277
                InitializedSingleRasterOrVectorOperator::Raster(source),
7✔
278
                TimeShiftParams::Relative { granularity, value },
1✔
279
            ) => {
1✔
280
                let shift = RelativeBackwardShift {
1✔
281
                    step: TimeStep {
1✔
282
                        granularity,
1✔
283
                        step: value.unsigned_abs(),
1✔
284
                    },
1✔
285
                };
1✔
286

1✔
287
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
1✔
288

1✔
289
                Ok(Box::new(InitializedRasterTimeShift {
1✔
290
                    name,
1✔
291
                    source,
1✔
292
                    result_descriptor,
1✔
293
                    shift,
1✔
294
                }))
1✔
295
            }
7✔
296
            (
7✔
297
                InitializedSingleRasterOrVectorOperator::Raster(source),
7✔
298
                TimeShiftParams::Absolute { time_interval },
2✔
299
            ) => {
2✔
300
                let shift = AbsoluteShift { time_interval };
2✔
301

2✔
302
                let result_descriptor = shift_result_descriptor(source.result_descriptor(), shift);
2✔
303

2✔
304
                Ok(Box::new(InitializedRasterTimeShift {
2✔
305
                    name,
2✔
306
                    source,
2✔
307
                    result_descriptor,
2✔
308
                    shift,
2✔
309
                }))
2✔
310
            }
7✔
311
            (InitializedSingleRasterOrVectorOperator::Vector(_), _) => {
7✔
312
                Err(TimeShiftError::UnmatchedOutput.into())
7✔
313
            }
7✔
314
        }
7✔
315
    }
7✔
316

317
    span_fn!(TimeShift);
318
}
319

320
fn shift_result_descriptor<R: ResultDescriptor, S: TimeShiftOperation>(
9✔
321
    result_descriptor: &R,
9✔
322
    shift: S,
9✔
323
) -> R {
9✔
324
    result_descriptor.map_time(|time| {
9✔
325
        if let Some(time) = time {
9✔
326
            shift.shift(*time).map(|r| r.0).ok()
5✔
327
        } else {
328
            None
4✔
329
        }
330
    })
9✔
331
}
9✔
332

333
pub struct InitializedVectorTimeShift<Shift: TimeShiftOperation> {
334
    name: CanonicOperatorName,
335
    source: Box<dyn InitializedVectorOperator>,
336
    result_descriptor: VectorResultDescriptor,
337
    shift: Shift,
338
}
339

340
pub struct InitializedRasterTimeShift<Shift: TimeShiftOperation> {
341
    name: CanonicOperatorName,
342
    source: Box<dyn InitializedRasterOperator>,
343
    result_descriptor: RasterResultDescriptor,
344
    shift: Shift,
345
}
346

347
impl<Shift: TimeShiftOperation + 'static> InitializedVectorOperator
348
    for InitializedVectorTimeShift<Shift>
349
{
350
    fn result_descriptor(&self) -> &VectorResultDescriptor {
×
351
        &self.result_descriptor
×
352
    }
×
353

354
    fn query_processor(&self) -> Result<TypedVectorQueryProcessor> {
2✔
355
        let source_processor = self.source.query_processor()?;
2✔
356

357
        Ok(
358
            call_on_generic_vector_processor!(source_processor, processor => VectorTimeShiftProcessor {
2✔
UNCOV
359
                processor,
×
UNCOV
360
                result_descriptor: self.result_descriptor.clone(),
×
UNCOV
361
                shift: self.shift,
×
UNCOV
362
            }.boxed().into()),
×
363
        )
364
    }
2✔
365

366
    fn canonic_name(&self) -> CanonicOperatorName {
×
367
        self.name.clone()
×
368
    }
×
369
}
370

371
impl<Shift: TimeShiftOperation + 'static> InitializedRasterOperator
372
    for InitializedRasterTimeShift<Shift>
373
{
374
    fn result_descriptor(&self) -> &RasterResultDescriptor {
3✔
375
        &self.result_descriptor
3✔
376
    }
3✔
377

378
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor> {
6✔
379
        let source_processor = self.source.query_processor()?;
6✔
380

381
        Ok(
382
            call_on_generic_raster_processor!(source_processor, processor => RasterTimeShiftProcessor {
6✔
383
                processor,
6✔
384
                result_descriptor: self.result_descriptor.clone(),
6✔
385
                shift: self.shift,
6✔
386
            }.boxed().into()),
6✔
387
        )
388
    }
6✔
389

390
    fn canonic_name(&self) -> CanonicOperatorName {
×
391
        self.name.clone()
×
392
    }
×
393
}
394

395
pub struct RasterTimeShiftProcessor<Q, P, Shift: TimeShiftOperation>
396
where
397
    Q: RasterQueryProcessor<RasterType = P>,
398
{
399
    processor: Q,
400
    result_descriptor: RasterResultDescriptor,
401
    shift: Shift,
402
}
403

404
pub struct VectorTimeShiftProcessor<Q, G, Shift: TimeShiftOperation>
405
where
406
    G: Geometry,
407
    Q: VectorQueryProcessor<VectorType = FeatureCollection<G>>,
408
{
409
    processor: Q,
410
    result_descriptor: VectorResultDescriptor,
411
    shift: Shift,
412
}
413

414
#[async_trait]
415
impl<Q, G, Shift> VectorQueryProcessor for VectorTimeShiftProcessor<Q, G, Shift>
416
where
417
    G: Geometry + ArrowTyped + 'static,
418
    Q: VectorQueryProcessor<VectorType = FeatureCollection<G>>,
419
    Shift: TimeShiftOperation + 'static,
420
{
421
    type VectorType = FeatureCollection<G>;
422

423
    async fn vector_query<'a>(
424
        &'a self,
425
        query: VectorQueryRectangle,
426
        ctx: &'a dyn QueryContext,
427
    ) -> Result<BoxStream<'a, Result<Self::VectorType>>> {
2✔
428
        let (time_interval, state) = self.shift.shift(query.time_interval)?;
2✔
429

2✔
430
        let query = VectorQueryRectangle {
2✔
431
            spatial_bounds: query.spatial_bounds,
2✔
432
            time_interval,
2✔
433
            spatial_resolution: query.spatial_resolution,
2✔
434
            attributes: ColumnSelection::all(),
2✔
435
        };
2✔
436
        let stream = self.processor.vector_query(query, ctx).await?;
2✔
437

2✔
438
        let stream = stream.then(move |collection| async move {
2✔
439
            let collection = collection?;
2✔
440
            let shift = self.shift;
2✔
441

2✔
442
            crate::util::spawn_blocking(move || {
2✔
443
                let time_intervals = collection
2✔
444
                    .time_intervals()
2✔
445
                    .iter()
2✔
446
                    .map(move |time| shift.reverse_shift(*time, state))
3✔
447
                    .collect::<Result<Vec<TimeInterval>, TimeShiftError>>()?;
2✔
448

2✔
449
                collection
2✔
450
                    .replace_time(&time_intervals)
2✔
451
                    .boxed_context(error::FeatureCollectionTimeModification)
2✔
452
                    .map_err(Into::into)
2✔
453
            })
2✔
454
            .await?
2✔
455
        });
2✔
456

2✔
457
        Ok(stream.boxed())
2✔
458
    }
2✔
459

460
    fn vector_result_descriptor(&self) -> &VectorResultDescriptor {
2✔
461
        &self.result_descriptor
2✔
462
    }
2✔
463
}
464

465
#[async_trait]
466
impl<Q, P, Shift> RasterQueryProcessor for RasterTimeShiftProcessor<Q, P, Shift>
467
where
468
    Q: RasterQueryProcessor<RasterType = P>,
469
    P: Pixel,
470
    Shift: TimeShiftOperation,
471
{
472
    type RasterType = P;
473

474
    async fn raster_query<'a>(
475
        &'a self,
476
        query: RasterQueryRectangle,
477
        ctx: &'a dyn QueryContext,
478
    ) -> Result<BoxStream<'a, Result<RasterTile2D<Self::RasterType>>>> {
6✔
479
        let (time_interval, state) = self.shift.shift(query.time_interval)?;
6✔
480
        let query = RasterQueryRectangle {
6✔
481
            spatial_bounds: query.spatial_bounds,
6✔
482
            time_interval,
6✔
483
            spatial_resolution: query.spatial_resolution,
6✔
484
            attributes: query.attributes,
6✔
485
        };
6✔
486
        let stream = self.processor.raster_query(query, ctx).await?;
6✔
487

6✔
488
        let stream = stream.map(move |raster| {
20✔
489
            // reverse time shift for results
6✔
490
            let mut raster = raster?;
20✔
491

6✔
492
            raster.time = self.shift.reverse_shift(raster.time, state)?;
20✔
493

6✔
494
            Ok(raster)
20✔
495
        });
20✔
496

6✔
497
        Ok(Box::pin(stream))
6✔
498
    }
6✔
499

500
    fn raster_result_descriptor(&self) -> &RasterResultDescriptor {
6✔
501
        &self.result_descriptor
6✔
502
    }
6✔
503
}
504

505
#[cfg(test)]
506
mod tests {
507
    use super::*;
508

509
    use crate::{
510
        engine::{
511
            MockExecutionContext, MockQueryContext, MultipleRasterSources, RasterBandDescriptors,
512
            SingleRasterSource,
513
        },
514
        mock::{MockFeatureCollectionSource, MockRasterSource, MockRasterSourceParams},
515
        processing::{Expression, ExpressionParams, RasterStacker, RasterStackerParams},
516
        source::{GdalSource, GdalSourceParameters},
517
        util::{gdal::add_ndvi_dataset, input::RasterOrVectorOperator},
518
    };
519
    use futures::StreamExt;
520
    use geoengine_datatypes::{
521
        collections::{ChunksEqualIgnoringCacheHint, MultiPointCollection},
522
        dataset::NamedData,
523
        primitives::{
524
            BandSelection, BoundingBox2D, CacheHint, DateTime, MultiPoint, SpatialPartition2D,
525
            SpatialResolution, TimeGranularity,
526
        },
527
        raster::{
528
            EmptyGrid2D, GridOrEmpty, RasterDataType, RenameBands, TileInformation,
529
            TilingSpecification,
530
        },
531
        spatial_reference::SpatialReference,
532
        util::test::TestDefault,
533
    };
534

535
    #[test]
536
    fn test_ser_de_absolute() {
1✔
537
        let time_shift = TimeShift {
1✔
538
            sources: SingleRasterOrVectorSource {
1✔
539
                source: RasterOrVectorOperator::Raster(
1✔
540
                    GdalSource {
1✔
541
                        params: GdalSourceParameters {
1✔
542
                            data: NamedData::with_system_name("test-raster"),
1✔
543
                        },
1✔
544
                    }
1✔
545
                    .boxed(),
1✔
546
                ),
1✔
547
            },
1✔
548
            params: TimeShiftParams::Absolute {
1✔
549
                time_interval: TimeInterval::new_unchecked(
1✔
550
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
551
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
552
                ),
1✔
553
            },
1✔
554
        };
1✔
555

1✔
556
        let serialized = serde_json::to_value(&time_shift).unwrap();
1✔
557

1✔
558
        assert_eq!(
1✔
559
            serialized,
1✔
560
            serde_json::json!({
1✔
561
                "params": {
1✔
562
                    "type": "absolute",
1✔
563
                    "timeInterval": {
1✔
564
                        "start": 1_293_840_000_000_i64,
1✔
565
                        "end": 1_325_376_000_000_i64
1✔
566
                    }
1✔
567
                },
1✔
568
                "sources": {
1✔
569
                    "source": {
1✔
570
                        "type": "GdalSource",
1✔
571
                        "params": {
1✔
572
                            "data": "test-raster"
1✔
573
                        }
1✔
574
                    }
1✔
575
                }
1✔
576
            })
1✔
577
        );
1✔
578

579
        let deserialized: TimeShift = serde_json::from_value(serialized).unwrap();
1✔
580

1✔
581
        assert_eq!(time_shift.params, deserialized.params);
1✔
582
    }
1✔
583

584
    #[test]
585
    fn test_ser_de_relative() {
1✔
586
        let time_shift = TimeShift {
1✔
587
            sources: SingleRasterOrVectorSource {
1✔
588
                source: RasterOrVectorOperator::Raster(
1✔
589
                    GdalSource {
1✔
590
                        params: GdalSourceParameters {
1✔
591
                            data: NamedData::with_system_name("test-raster"),
1✔
592
                        },
1✔
593
                    }
1✔
594
                    .boxed(),
1✔
595
                ),
1✔
596
            },
1✔
597
            params: TimeShiftParams::Relative {
1✔
598
                granularity: TimeGranularity::Years,
1✔
599
                value: 1,
1✔
600
            },
1✔
601
        };
1✔
602

1✔
603
        let serialized = serde_json::to_value(&time_shift).unwrap();
1✔
604

1✔
605
        assert_eq!(
1✔
606
            serialized,
1✔
607
            serde_json::json!({
1✔
608
                "params": {
1✔
609
                    "type": "relative",
1✔
610
                    "granularity": "years",
1✔
611
                    "value": 1
1✔
612
                },
1✔
613
                "sources": {
1✔
614
                    "source": {
1✔
615
                        "type": "GdalSource",
1✔
616
                        "params": {
1✔
617
                            "data": "test-raster"
1✔
618
                        }
1✔
619
                    }
1✔
620
                }
1✔
621
            })
1✔
622
        );
1✔
623

624
        let deserialized: TimeShift = serde_json::from_value(serialized).unwrap();
1✔
625

1✔
626
        assert_eq!(time_shift.params, deserialized.params);
1✔
627
    }
1✔
628

629
    #[tokio::test]
630
    async fn test_absolute_vector_shift() {
1✔
631
        let execution_context = MockExecutionContext::test_default();
1✔
632
        let query_context = MockQueryContext::test_default();
1✔
633

1✔
634
        let source = MockFeatureCollectionSource::single(
1✔
635
            MultiPointCollection::from_data(
1✔
636
                MultiPoint::many(vec![(0., 0.), (1., 1.), (2., 2.)]).unwrap(),
1✔
637
                vec![
1✔
638
                    TimeInterval::new(
1✔
639
                        DateTime::new_utc(2009, 1, 1, 0, 0, 0),
1✔
640
                        DateTime::new_utc_with_millis(2010, 12, 31, 23, 59, 59, 999),
1✔
641
                    )
1✔
642
                    .unwrap(),
1✔
643
                    TimeInterval::new(
1✔
644
                        DateTime::new_utc(2009, 6, 3, 0, 0, 0),
1✔
645
                        DateTime::new_utc(2010, 7, 14, 0, 0, 0),
1✔
646
                    )
1✔
647
                    .unwrap(),
1✔
648
                    TimeInterval::new(
1✔
649
                        DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
650
                        DateTime::new_utc_with_millis(2011, 3, 31, 23, 59, 59, 999),
1✔
651
                    )
1✔
652
                    .unwrap(),
1✔
653
                ],
1✔
654
                Default::default(),
1✔
655
                CacheHint::default(),
1✔
656
            )
1✔
657
            .unwrap(),
1✔
658
        );
1✔
659

1✔
660
        let time_shift = TimeShift {
1✔
661
            sources: SingleRasterOrVectorSource {
1✔
662
                source: RasterOrVectorOperator::Vector(source.boxed()),
1✔
663
            },
1✔
664
            params: TimeShiftParams::Absolute {
1✔
665
                time_interval: TimeInterval::new(
1✔
666
                    DateTime::new_utc(2009, 1, 1, 0, 0, 0),
1✔
667
                    DateTime::new_utc(2009, 6, 1, 0, 0, 0),
1✔
668
                )
1✔
669
                .unwrap(),
1✔
670
            },
1✔
671
        };
1✔
672

1✔
673
        let query_processor = VectorOperator::boxed(time_shift)
1✔
674
            .initialize(WorkflowOperatorPath::initialize_root(), &execution_context)
1✔
675
            .await
1✔
676
            .unwrap()
1✔
677
            .query_processor()
1✔
678
            .unwrap()
1✔
679
            .multi_point()
1✔
680
            .unwrap();
1✔
681

1✔
682
        let mut stream = query_processor
1✔
683
            .vector_query(
1✔
684
                VectorQueryRectangle {
1✔
685
                    spatial_bounds: BoundingBox2D::new((0., 0.).into(), (2., 2.).into()).unwrap(),
1✔
686
                    time_interval: TimeInterval::new(
1✔
687
                        DateTime::new_utc(2009, 1, 1, 0, 0, 0),
1✔
688
                        DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
689
                    )
1✔
690
                    .unwrap(),
1✔
691
                    spatial_resolution: SpatialResolution::one(),
1✔
692
                    attributes: ColumnSelection::all(),
1✔
693
                },
1✔
694
                &query_context,
1✔
695
            )
1✔
696
            .await
1✔
697
            .unwrap();
1✔
698

1✔
699
        let mut result = Vec::new();
1✔
700
        while let Some(collection) = stream.next().await {
2✔
701
            result.push(collection.unwrap());
1✔
702
        }
1✔
703

1✔
704
        assert_eq!(result.len(), 1);
1✔
705

1✔
706
        let expected = MultiPointCollection::from_data(
1✔
707
            MultiPoint::many(vec![(0., 0.)]).unwrap(),
1✔
708
            vec![TimeInterval::new(
1✔
709
                DateTime::new_utc(2009, 1, 1, 0, 0, 0),
1✔
710
                DateTime::new_utc_with_millis(2013, 8, 1, 23, 59, 59, 999),
1✔
711
            )
1✔
712
            .unwrap()],
1✔
713
            Default::default(),
1✔
714
            CacheHint::default(),
1✔
715
        )
1✔
716
        .unwrap();
1✔
717

1✔
718
        assert!(result[0].chunks_equal_ignoring_cache_hint(&expected));
1✔
719
    }
1✔
720

721
    #[tokio::test]
722
    async fn test_relative_vector_shift() {
1✔
723
        let execution_context = MockExecutionContext::test_default();
1✔
724
        let query_context = MockQueryContext::test_default();
1✔
725

1✔
726
        let source = MockFeatureCollectionSource::single(
1✔
727
            MultiPointCollection::from_data(
1✔
728
                MultiPoint::many(vec![(0., 0.), (1., 1.), (2., 2.)]).unwrap(),
1✔
729
                vec![
1✔
730
                    TimeInterval::new(
1✔
731
                        DateTime::new_utc(2009, 1, 1, 0, 0, 0),
1✔
732
                        DateTime::new_utc_with_millis(2010, 12, 31, 23, 59, 59, 999),
1✔
733
                    )
1✔
734
                    .unwrap(),
1✔
735
                    TimeInterval::new(
1✔
736
                        DateTime::new_utc(2009, 6, 3, 0, 0, 0),
1✔
737
                        DateTime::new_utc(2010, 7, 14, 0, 0, 0),
1✔
738
                    )
1✔
739
                    .unwrap(),
1✔
740
                    TimeInterval::new(
1✔
741
                        DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
742
                        DateTime::new_utc_with_millis(2011, 3, 31, 23, 59, 59, 999),
1✔
743
                    )
1✔
744
                    .unwrap(),
1✔
745
                ],
1✔
746
                Default::default(),
1✔
747
                CacheHint::default(),
1✔
748
            )
1✔
749
            .unwrap(),
1✔
750
        );
1✔
751

1✔
752
        let time_shift = TimeShift {
1✔
753
            sources: SingleRasterOrVectorSource {
1✔
754
                source: RasterOrVectorOperator::Vector(source.boxed()),
1✔
755
            },
1✔
756
            params: TimeShiftParams::Relative {
1✔
757
                granularity: TimeGranularity::Years,
1✔
758
                value: -1,
1✔
759
            },
1✔
760
        };
1✔
761

1✔
762
        let query_processor = VectorOperator::boxed(time_shift)
1✔
763
            .initialize(WorkflowOperatorPath::initialize_root(), &execution_context)
1✔
764
            .await
1✔
765
            .unwrap()
1✔
766
            .query_processor()
1✔
767
            .unwrap()
1✔
768
            .multi_point()
1✔
769
            .unwrap();
1✔
770

1✔
771
        let mut stream = query_processor
1✔
772
            .vector_query(
1✔
773
                VectorQueryRectangle {
1✔
774
                    spatial_bounds: BoundingBox2D::new((0., 0.).into(), (2., 2.).into()).unwrap(),
1✔
775
                    time_interval: TimeInterval::new(
1✔
776
                        DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
777
                        DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
778
                    )
1✔
779
                    .unwrap(),
1✔
780
                    spatial_resolution: SpatialResolution::one(),
1✔
781
                    attributes: ColumnSelection::all(),
1✔
782
                },
1✔
783
                &query_context,
1✔
784
            )
1✔
785
            .await
1✔
786
            .unwrap();
1✔
787

1✔
788
        let mut result = Vec::new();
1✔
789
        while let Some(collection) = stream.next().await {
2✔
790
            result.push(collection.unwrap());
1✔
791
        }
1✔
792

1✔
793
        assert_eq!(result.len(), 1);
1✔
794

1✔
795
        let expected = MultiPointCollection::from_data(
1✔
796
            MultiPoint::many(vec![(0., 0.), (1., 1.)]).unwrap(),
1✔
797
            vec![
1✔
798
                TimeInterval::new(
1✔
799
                    DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
800
                    DateTime::new_utc_with_millis(2011, 12, 31, 23, 59, 59, 999),
1✔
801
                )
1✔
802
                .unwrap(),
1✔
803
                TimeInterval::new(
1✔
804
                    DateTime::new_utc(2010, 6, 3, 0, 0, 0),
1✔
805
                    DateTime::new_utc(2011, 7, 14, 0, 0, 0),
1✔
806
                )
1✔
807
                .unwrap(),
1✔
808
            ],
1✔
809
            Default::default(),
1✔
810
            CacheHint::default(),
1✔
811
        )
1✔
812
        .unwrap();
1✔
813

1✔
814
        assert!(result[0].chunks_equal_ignoring_cache_hint(&expected));
1✔
815
    }
1✔
816

817
    #[tokio::test]
818
    #[allow(clippy::too_many_lines)]
819
    async fn test_absolute_raster_shift() {
1✔
820
        let empty_grid = GridOrEmpty::Empty(EmptyGrid2D::<u8>::new([3, 2].into()));
1✔
821
        let raster_tiles = vec![
1✔
822
            RasterTile2D::new_with_tile_info(
1✔
823
                TimeInterval::new_unchecked(
1✔
824
                    DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
825
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
826
                ),
1✔
827
                TileInformation {
1✔
828
                    global_tile_position: [-1, 0].into(),
1✔
829
                    tile_size_in_pixels: [3, 2].into(),
1✔
830
                    global_geo_transform: TestDefault::test_default(),
1✔
831
                },
1✔
832
                0,
1✔
833
                empty_grid.clone(),
1✔
834
                CacheHint::default(),
1✔
835
            ),
1✔
836
            RasterTile2D::new_with_tile_info(
1✔
837
                TimeInterval::new_unchecked(
1✔
838
                    DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
839
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
840
                ),
1✔
841
                TileInformation {
1✔
842
                    global_tile_position: [-1, 1].into(),
1✔
843
                    tile_size_in_pixels: [3, 2].into(),
1✔
844
                    global_geo_transform: TestDefault::test_default(),
1✔
845
                },
1✔
846
                0,
1✔
847
                empty_grid.clone(),
1✔
848
                CacheHint::default(),
1✔
849
            ),
1✔
850
            RasterTile2D::new_with_tile_info(
1✔
851
                TimeInterval::new_unchecked(
1✔
852
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
853
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
854
                ),
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
                },
1✔
860
                0,
1✔
861
                empty_grid.clone(),
1✔
862
                CacheHint::default(),
1✔
863
            ),
1✔
864
            RasterTile2D::new_with_tile_info(
1✔
865
                TimeInterval::new_unchecked(
1✔
866
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
867
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
868
                ),
1✔
869
                TileInformation {
1✔
870
                    global_tile_position: [-1, 1].into(),
1✔
871
                    tile_size_in_pixels: [3, 2].into(),
1✔
872
                    global_geo_transform: TestDefault::test_default(),
1✔
873
                },
1✔
874
                0,
1✔
875
                empty_grid.clone(),
1✔
876
                CacheHint::default(),
1✔
877
            ),
1✔
878
            RasterTile2D::new_with_tile_info(
1✔
879
                TimeInterval::new_unchecked(
1✔
880
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
881
                    DateTime::new_utc(2013, 1, 1, 0, 0, 0),
1✔
882
                ),
1✔
883
                TileInformation {
1✔
884
                    global_tile_position: [-1, 0].into(),
1✔
885
                    tile_size_in_pixels: [3, 2].into(),
1✔
886
                    global_geo_transform: TestDefault::test_default(),
1✔
887
                },
1✔
888
                0,
1✔
889
                empty_grid.clone(),
1✔
890
                CacheHint::default(),
1✔
891
            ),
1✔
892
            RasterTile2D::new_with_tile_info(
1✔
893
                TimeInterval::new_unchecked(
1✔
894
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
895
                    DateTime::new_utc(2013, 1, 1, 0, 0, 0),
1✔
896
                ),
1✔
897
                TileInformation {
1✔
898
                    global_tile_position: [-1, 1].into(),
1✔
899
                    tile_size_in_pixels: [3, 2].into(),
1✔
900
                    global_geo_transform: TestDefault::test_default(),
1✔
901
                },
1✔
902
                0,
1✔
903
                empty_grid.clone(),
1✔
904
                CacheHint::default(),
1✔
905
            ),
1✔
906
        ];
1✔
907

1✔
908
        let mrs = MockRasterSource {
1✔
909
            params: MockRasterSourceParams {
1✔
910
                data: raster_tiles,
1✔
911
                result_descriptor: RasterResultDescriptor {
1✔
912
                    data_type: RasterDataType::U8,
1✔
913
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
914
                    time: None,
1✔
915
                    bbox: None,
1✔
916
                    resolution: None,
1✔
917
                    bands: RasterBandDescriptors::new_single_band(),
1✔
918
                },
1✔
919
            },
1✔
920
        }
1✔
921
        .boxed();
1✔
922

1✔
923
        let time_shift = TimeShift {
1✔
924
            sources: SingleRasterOrVectorSource {
1✔
925
                source: RasterOrVectorOperator::Raster(mrs),
1✔
926
            },
1✔
927
            params: TimeShiftParams::Absolute {
1✔
928
                time_interval: TimeInterval::new_unchecked(
1✔
929
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
930
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
931
                ),
1✔
932
            },
1✔
933
        };
1✔
934

1✔
935
        let execution_context = MockExecutionContext::new_with_tiling_spec(
1✔
936
            TilingSpecification::new((0., 0.).into(), [3, 2].into()),
1✔
937
        );
1✔
938
        let query_context = MockQueryContext::test_default();
1✔
939

1✔
940
        let query_processor = RasterOperator::boxed(time_shift)
1✔
941
            .initialize(WorkflowOperatorPath::initialize_root(), &execution_context)
1✔
942
            .await
1✔
943
            .unwrap()
1✔
944
            .query_processor()
1✔
945
            .unwrap()
1✔
946
            .get_u8()
1✔
947
            .unwrap();
1✔
948

1✔
949
        let mut stream = query_processor
1✔
950
            .raster_query(
1✔
951
                RasterQueryRectangle {
1✔
952
                    spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
953
                        (0., 3.).into(),
1✔
954
                        (4., 0.).into(),
1✔
955
                    ),
1✔
956
                    time_interval: TimeInterval::new(
1✔
957
                        DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
958
                        DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
959
                    )
1✔
960
                    .unwrap(),
1✔
961
                    spatial_resolution: SpatialResolution::one(),
1✔
962
                    attributes: BandSelection::first(),
1✔
963
                },
1✔
964
                &query_context,
1✔
965
            )
1✔
966
            .await
1✔
967
            .unwrap();
1✔
968

1✔
969
        let mut result = Vec::new();
1✔
970
        while let Some(tile) = stream.next().await {
3✔
971
            result.push(tile.unwrap());
2✔
972
        }
2✔
973

1✔
974
        assert_eq!(result.len(), 2);
1✔
975

1✔
976
        assert_eq!(
1✔
977
            result[0].time,
1✔
978
            TimeInterval::new_unchecked(
1✔
979
                DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
980
                DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
981
            ),
1✔
982
        );
1✔
983
        assert_eq!(
1✔
984
            result[1].time,
1✔
985
            TimeInterval::new_unchecked(
1✔
986
                DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
987
                DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
988
            ),
1✔
989
        );
1✔
990
    }
1✔
991

992
    #[tokio::test]
993
    #[allow(clippy::too_many_lines)]
994
    async fn test_relative_raster_shift() {
1✔
995
        let empty_grid = GridOrEmpty::Empty(EmptyGrid2D::<u8>::new([3, 2].into()));
1✔
996
        let raster_tiles = vec![
1✔
997
            RasterTile2D::new_with_tile_info(
1✔
998
                TimeInterval::new_unchecked(
1✔
999
                    DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
1000
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
1001
                ),
1✔
1002
                TileInformation {
1✔
1003
                    global_tile_position: [-1, 0].into(),
1✔
1004
                    tile_size_in_pixels: [3, 2].into(),
1✔
1005
                    global_geo_transform: TestDefault::test_default(),
1✔
1006
                },
1✔
1007
                0,
1✔
1008
                empty_grid.clone(),
1✔
1009
                CacheHint::default(),
1✔
1010
            ),
1✔
1011
            RasterTile2D::new_with_tile_info(
1✔
1012
                TimeInterval::new_unchecked(
1✔
1013
                    DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
1014
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
1015
                ),
1✔
1016
                TileInformation {
1✔
1017
                    global_tile_position: [-1, 1].into(),
1✔
1018
                    tile_size_in_pixels: [3, 2].into(),
1✔
1019
                    global_geo_transform: TestDefault::test_default(),
1✔
1020
                },
1✔
1021
                0,
1✔
1022
                empty_grid.clone(),
1✔
1023
                CacheHint::default(),
1✔
1024
            ),
1✔
1025
            RasterTile2D::new_with_tile_info(
1✔
1026
                TimeInterval::new_unchecked(
1✔
1027
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
1028
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
1029
                ),
1✔
1030
                TileInformation {
1✔
1031
                    global_tile_position: [-1, 0].into(),
1✔
1032
                    tile_size_in_pixels: [3, 2].into(),
1✔
1033
                    global_geo_transform: TestDefault::test_default(),
1✔
1034
                },
1✔
1035
                0,
1✔
1036
                empty_grid.clone(),
1✔
1037
                CacheHint::default(),
1✔
1038
            ),
1✔
1039
            RasterTile2D::new_with_tile_info(
1✔
1040
                TimeInterval::new_unchecked(
1✔
1041
                    DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
1042
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
1043
                ),
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
                },
1✔
1049
                0,
1✔
1050
                empty_grid.clone(),
1✔
1051
                CacheHint::default(),
1✔
1052
            ),
1✔
1053
            RasterTile2D::new_with_tile_info(
1✔
1054
                TimeInterval::new_unchecked(
1✔
1055
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
1056
                    DateTime::new_utc(2013, 1, 1, 0, 0, 0),
1✔
1057
                ),
1✔
1058
                TileInformation {
1✔
1059
                    global_tile_position: [-1, 0].into(),
1✔
1060
                    tile_size_in_pixels: [3, 2].into(),
1✔
1061
                    global_geo_transform: TestDefault::test_default(),
1✔
1062
                },
1✔
1063
                0,
1✔
1064
                empty_grid.clone(),
1✔
1065
                CacheHint::default(),
1✔
1066
            ),
1✔
1067
            RasterTile2D::new_with_tile_info(
1✔
1068
                TimeInterval::new_unchecked(
1✔
1069
                    DateTime::new_utc(2012, 1, 1, 0, 0, 0),
1✔
1070
                    DateTime::new_utc(2013, 1, 1, 0, 0, 0),
1✔
1071
                ),
1✔
1072
                TileInformation {
1✔
1073
                    global_tile_position: [-1, 1].into(),
1✔
1074
                    tile_size_in_pixels: [3, 2].into(),
1✔
1075
                    global_geo_transform: TestDefault::test_default(),
1✔
1076
                },
1✔
1077
                0,
1✔
1078
                empty_grid.clone(),
1✔
1079
                CacheHint::default(),
1✔
1080
            ),
1✔
1081
        ];
1✔
1082

1✔
1083
        let mrs = MockRasterSource {
1✔
1084
            params: MockRasterSourceParams {
1✔
1085
                data: raster_tiles,
1✔
1086
                result_descriptor: RasterResultDescriptor {
1✔
1087
                    data_type: RasterDataType::U8,
1✔
1088
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
1089
                    time: None,
1✔
1090
                    bbox: None,
1✔
1091
                    resolution: None,
1✔
1092
                    bands: RasterBandDescriptors::new_single_band(),
1✔
1093
                },
1✔
1094
            },
1✔
1095
        }
1✔
1096
        .boxed();
1✔
1097

1✔
1098
        let time_shift = TimeShift {
1✔
1099
            sources: SingleRasterOrVectorSource {
1✔
1100
                source: RasterOrVectorOperator::Raster(mrs),
1✔
1101
            },
1✔
1102
            params: TimeShiftParams::Relative {
1✔
1103
                granularity: TimeGranularity::Years,
1✔
1104
                value: 1,
1✔
1105
            },
1✔
1106
        };
1✔
1107

1✔
1108
        let execution_context = MockExecutionContext::new_with_tiling_spec(
1✔
1109
            TilingSpecification::new((0., 0.).into(), [3, 2].into()),
1✔
1110
        );
1✔
1111
        let query_context = MockQueryContext::test_default();
1✔
1112

1✔
1113
        let query_processor = RasterOperator::boxed(time_shift)
1✔
1114
            .initialize(WorkflowOperatorPath::initialize_root(), &execution_context)
1✔
1115
            .await
1✔
1116
            .unwrap()
1✔
1117
            .query_processor()
1✔
1118
            .unwrap()
1✔
1119
            .get_u8()
1✔
1120
            .unwrap();
1✔
1121

1✔
1122
        let mut stream = query_processor
1✔
1123
            .raster_query(
1✔
1124
                RasterQueryRectangle {
1✔
1125
                    spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1126
                        (0., 3.).into(),
1✔
1127
                        (4., 0.).into(),
1✔
1128
                    ),
1✔
1129
                    time_interval: TimeInterval::new(
1✔
1130
                        DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
1131
                        DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
1132
                    )
1✔
1133
                    .unwrap(),
1✔
1134
                    spatial_resolution: SpatialResolution::one(),
1✔
1135
                    attributes: BandSelection::first(),
1✔
1136
                },
1✔
1137
                &query_context,
1✔
1138
            )
1✔
1139
            .await
1✔
1140
            .unwrap();
1✔
1141

1✔
1142
        let mut result = Vec::new();
1✔
1143
        while let Some(tile) = stream.next().await {
3✔
1144
            result.push(tile.unwrap());
2✔
1145
        }
2✔
1146

1✔
1147
        assert_eq!(result.len(), 2);
1✔
1148

1✔
1149
        assert_eq!(
1✔
1150
            result[0].time,
1✔
1151
            TimeInterval::new_unchecked(
1✔
1152
                DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
1153
                DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
1154
            ),
1✔
1155
        );
1✔
1156
        assert_eq!(
1✔
1157
            result[1].time,
1✔
1158
            TimeInterval::new_unchecked(
1✔
1159
                DateTime::new_utc(2010, 1, 1, 0, 0, 0),
1✔
1160
                DateTime::new_utc(2011, 1, 1, 0, 0, 0),
1✔
1161
            ),
1✔
1162
        );
1✔
1163
    }
1✔
1164

1165
    #[tokio::test]
1166
    async fn test_expression_on_shifted_raster() {
1✔
1167
        let mut execution_context = MockExecutionContext::test_default();
1✔
1168

1✔
1169
        let ndvi_source = GdalSource {
1✔
1170
            params: GdalSourceParameters {
1✔
1171
                data: add_ndvi_dataset(&mut execution_context),
1✔
1172
            },
1✔
1173
        }
1✔
1174
        .boxed();
1✔
1175

1✔
1176
        let shifted_ndvi_source = RasterOperator::boxed(TimeShift {
1✔
1177
            params: TimeShiftParams::Relative {
1✔
1178
                granularity: TimeGranularity::Months,
1✔
1179
                value: -1,
1✔
1180
            },
1✔
1181
            sources: SingleRasterOrVectorSource {
1✔
1182
                source: RasterOrVectorOperator::Raster(ndvi_source.clone()),
1✔
1183
            },
1✔
1184
        });
1✔
1185

1✔
1186
        let expression = Expression {
1✔
1187
            params: ExpressionParams {
1✔
1188
                expression: "A - B".to_string(),
1✔
1189
                output_type: RasterDataType::F64,
1✔
1190
                output_band: None,
1✔
1191
                map_no_data: false,
1✔
1192
            },
1✔
1193
            sources: SingleRasterSource {
1✔
1194
                raster: RasterStacker {
1✔
1195
                    params: RasterStackerParams {
1✔
1196
                        rename_bands: RenameBands::Default,
1✔
1197
                    },
1✔
1198
                    sources: MultipleRasterSources {
1✔
1199
                        rasters: vec![ndvi_source, shifted_ndvi_source],
1✔
1200
                    },
1✔
1201
                }
1✔
1202
                .boxed(),
1✔
1203
            },
1✔
1204
        }
1✔
1205
        .boxed();
1✔
1206

1✔
1207
        let query_processor = expression
1✔
1208
            .initialize(WorkflowOperatorPath::initialize_root(), &execution_context)
1✔
1209
            .await
1✔
1210
            .unwrap()
1✔
1211
            .query_processor()
1✔
1212
            .unwrap()
1✔
1213
            .get_f64()
1✔
1214
            .unwrap();
1✔
1215

1✔
1216
        let query_context = MockQueryContext::test_default();
1✔
1217

1✔
1218
        let mut stream = query_processor
1✔
1219
            .raster_query(
1✔
1220
                RasterQueryRectangle {
1✔
1221
                    spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1222
                        (-180., 90.).into(),
1✔
1223
                        (180., -90.).into(),
1✔
1224
                    ),
1✔
1225
                    time_interval: TimeInterval::new_instant(DateTime::new_utc(
1✔
1226
                        2014, 3, 1, 0, 0, 0,
1✔
1227
                    ))
1✔
1228
                    .unwrap(),
1✔
1229
                    spatial_resolution: SpatialResolution::one(),
1✔
1230
                    attributes: BandSelection::first(),
1✔
1231
                },
1✔
1232
                &query_context,
1✔
1233
            )
1✔
1234
            .await
1✔
1235
            .unwrap();
1✔
1236

1✔
1237
        let mut result = Vec::new();
1✔
1238
        while let Some(tile) = stream.next().await {
21✔
1239
            result.push(tile.unwrap());
4✔
1240
        }
4✔
1241

1✔
1242
        assert_eq!(result.len(), 4);
1✔
1243
        assert_eq!(
1✔
1244
            result[0].time,
1✔
1245
            TimeInterval::new(
1✔
1246
                DateTime::new_utc(2014, 3, 1, 0, 0, 0),
1✔
1247
                DateTime::new_utc(2014, 4, 1, 0, 0, 0)
1✔
1248
            )
1✔
1249
            .unwrap()
1✔
1250
        );
1✔
1251
    }
1✔
1252

1253
    #[tokio::test]
1254
    async fn test_expression_on_absolute_shifted_raster() {
1✔
1255
        let mut execution_context = MockExecutionContext::test_default();
1✔
1256

1✔
1257
        let ndvi_source = GdalSource {
1✔
1258
            params: GdalSourceParameters {
1✔
1259
                data: add_ndvi_dataset(&mut execution_context),
1✔
1260
            },
1✔
1261
        }
1✔
1262
        .boxed();
1✔
1263

1✔
1264
        let shifted_ndvi_source = RasterOperator::boxed(TimeShift {
1✔
1265
            params: TimeShiftParams::Absolute {
1✔
1266
                time_interval: TimeInterval::new_instant(DateTime::new_utc(2014, 5, 1, 0, 0, 0))
1✔
1267
                    .unwrap(),
1✔
1268
            },
1✔
1269
            sources: SingleRasterOrVectorSource {
1✔
1270
                source: RasterOrVectorOperator::Raster(ndvi_source),
1✔
1271
            },
1✔
1272
        });
1✔
1273

1✔
1274
        let query_processor = shifted_ndvi_source
1✔
1275
            .initialize(WorkflowOperatorPath::initialize_root(), &execution_context)
1✔
1276
            .await
1✔
1277
            .unwrap()
1✔
1278
            .query_processor()
1✔
1279
            .unwrap()
1✔
1280
            .get_u8()
1✔
1281
            .unwrap();
1✔
1282

1✔
1283
        let query_context = MockQueryContext::test_default();
1✔
1284

1✔
1285
        let mut stream = query_processor
1✔
1286
            .raster_query(
1✔
1287
                RasterQueryRectangle {
1✔
1288
                    spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1289
                        (-180., 90.).into(),
1✔
1290
                        (180., -90.).into(),
1✔
1291
                    ),
1✔
1292
                    time_interval: TimeInterval::new_instant(DateTime::new_utc(
1✔
1293
                        2014, 3, 1, 0, 0, 0,
1✔
1294
                    ))
1✔
1295
                    .unwrap(),
1✔
1296
                    spatial_resolution: SpatialResolution::one(),
1✔
1297
                    attributes: BandSelection::first(),
1✔
1298
                },
1✔
1299
                &query_context,
1✔
1300
            )
1✔
1301
            .await
1✔
1302
            .unwrap();
1✔
1303

1✔
1304
        let mut result = Vec::new();
1✔
1305
        while let Some(tile) = stream.next().await {
5✔
1306
            result.push(tile.unwrap());
4✔
1307
        }
4✔
1308

1✔
1309
        assert_eq!(result.len(), 4);
1✔
1310
        assert_eq!(
1✔
1311
            result[0].time,
1✔
1312
            TimeInterval::new(
1✔
1313
                DateTime::new_utc(2014, 3, 1, 0, 0, 0),
1✔
1314
                DateTime::new_utc(2014, 4, 1, 0, 0, 0)
1✔
1315
            )
1✔
1316
            .unwrap()
1✔
1317
        );
1✔
1318
    }
1✔
1319

1320
    #[test]
1321
    fn shift_eternal() {
1✔
1322
        let eternal = TimeInterval::default();
1✔
1323

1✔
1324
        let f_shift = RelativeForwardShift {
1✔
1325
            step: TimeStep {
1✔
1326
                granularity: TimeGranularity::Seconds,
1✔
1327
                step: 1,
1✔
1328
            },
1✔
1329
        };
1✔
1330

1✔
1331
        let (shifted, state) = f_shift.shift(eternal).unwrap();
1✔
1332
        assert_eq!(shifted, eternal);
1✔
1333
        let reverse_shifted = f_shift.reverse_shift(eternal, state).unwrap();
1✔
1334
        assert_eq!(reverse_shifted, eternal);
1✔
1335

1336
        let b_shift = RelativeBackwardShift {
1✔
1337
            step: TimeStep {
1✔
1338
                granularity: TimeGranularity::Seconds,
1✔
1339
                step: 1,
1✔
1340
            },
1✔
1341
        };
1✔
1342

1✔
1343
        let (shifted, state) = b_shift.shift(eternal).unwrap();
1✔
1344
        assert_eq!(shifted, eternal);
1✔
1345

1346
        let reverse_shifted = b_shift.reverse_shift(eternal, state).unwrap();
1✔
1347
        assert_eq!(reverse_shifted, eternal);
1✔
1348
    }
1✔
1349

1350
    #[test]
1351
    fn shift_begin_of_time() {
1✔
1352
        let time = TimeInterval::new_unchecked(TimeInstance::MIN, TimeInstance::EPOCH_START);
1✔
1353

1✔
1354
        let f_shift = RelativeForwardShift {
1✔
1355
            step: TimeStep {
1✔
1356
                granularity: TimeGranularity::Seconds,
1✔
1357
                step: 1,
1✔
1358
            },
1✔
1359
        };
1✔
1360

1✔
1361
        let expected_shift =
1✔
1362
            TimeInterval::new_unchecked(TimeInstance::MIN, TimeInstance::EPOCH_START + 1_000);
1✔
1363

1✔
1364
        let (shifted, state) = f_shift.shift(time).unwrap();
1✔
1365
        assert_eq!(shifted, expected_shift);
1✔
1366

1367
        let reverse_shifted = f_shift.reverse_shift(expected_shift, state).unwrap();
1✔
1368
        assert_eq!(reverse_shifted, time);
1✔
1369

1370
        let f_shift = RelativeBackwardShift {
1✔
1371
            step: TimeStep {
1✔
1372
                granularity: TimeGranularity::Seconds,
1✔
1373
                step: 1,
1✔
1374
            },
1✔
1375
        };
1✔
1376

1✔
1377
        let expected_shift =
1✔
1378
            TimeInterval::new_unchecked(TimeInstance::MIN, TimeInstance::EPOCH_START - 1_000);
1✔
1379

1✔
1380
        let (shifted, state) = f_shift.shift(time).unwrap();
1✔
1381
        assert_eq!(shifted, expected_shift);
1✔
1382

1383
        let reverse_shifted = f_shift.reverse_shift(expected_shift, state).unwrap();
1✔
1384
        assert_eq!(reverse_shifted, time);
1✔
1385
    }
1✔
1386

1387
    #[test]
1388
    fn shift_end_of_time() {
1✔
1389
        let time = TimeInterval::new_unchecked(TimeInstance::EPOCH_START, TimeInstance::MAX);
1✔
1390

1✔
1391
        let f_shift = RelativeForwardShift {
1✔
1392
            step: TimeStep {
1✔
1393
                granularity: TimeGranularity::Seconds,
1✔
1394
                step: 1,
1✔
1395
            },
1✔
1396
        };
1✔
1397

1✔
1398
        let expected_shift =
1✔
1399
            TimeInterval::new_unchecked(TimeInstance::EPOCH_START + 1_000, TimeInstance::MAX);
1✔
1400

1✔
1401
        let (shifted, state) = f_shift.shift(time).unwrap();
1✔
1402
        assert_eq!(shifted, expected_shift);
1✔
1403

1404
        let reverse_shifted = f_shift.reverse_shift(expected_shift, state).unwrap();
1✔
1405
        assert_eq!(reverse_shifted, time);
1✔
1406

1407
        let f_shift = RelativeBackwardShift {
1✔
1408
            step: TimeStep {
1✔
1409
                granularity: TimeGranularity::Seconds,
1✔
1410
                step: 1,
1✔
1411
            },
1✔
1412
        };
1✔
1413

1✔
1414
        let expected_shift =
1✔
1415
            TimeInterval::new_unchecked(TimeInstance::EPOCH_START - 1_000, TimeInstance::MAX);
1✔
1416

1✔
1417
        let (shifted, state) = f_shift.shift(time).unwrap();
1✔
1418
        assert_eq!(shifted, expected_shift);
1✔
1419

1420
        let reverse_shifted = f_shift.reverse_shift(expected_shift, state).unwrap();
1✔
1421
        assert_eq!(reverse_shifted, time);
1✔
1422
    }
1✔
1423
}
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