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

geo-engine / geoengine / 12469296660

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

push

github

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

Quota and Data usage Logging

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

3 existing lines in 2 files now uncovered.

133923 of 147883 relevant lines covered (90.56%)

54439.32 hits per line

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

94.23
/operators/src/processing/raster_stacker.rs
1
use crate::adapters::{QueryWrapper, RasterStackerAdapter, RasterStackerSource};
2
use crate::engine::{
3
    CanonicOperatorName, ExecutionContext, InitializedRasterOperator, InitializedSources,
4
    MultipleRasterSources, Operator, OperatorName, QueryContext, RasterBandDescriptor,
5
    RasterOperator, RasterQueryProcessor, RasterResultDescriptor, TypedRasterQueryProcessor,
6
    WorkflowOperatorPath,
7
};
8
use crate::error::{
9
    InvalidNumberOfRasterStackerInputs, RasterInputsMustHaveSameSpatialReferenceAndDatatype,
10
};
11
use crate::util::Result;
12
use async_trait::async_trait;
13
use futures::stream::BoxStream;
14
use geoengine_datatypes::primitives::{
15
    partitions_extent, time_interval_extent, BandSelection, RasterQueryRectangle, SpatialResolution,
16
};
17
use geoengine_datatypes::raster::{DynamicRasterDataType, Pixel, RasterTile2D, RenameBands};
18
use serde::{Deserialize, Serialize};
19
use snafu::ensure;
20

21
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22
#[serde(rename_all = "camelCase")]
23
pub struct RasterStackerParams {
24
    pub rename_bands: RenameBands,
25
}
26

27
/// This `QueryProcessor` stacks all of it's inputs into a single raster time-series.
28
/// It does so by querying all of it's inputs outputting them by band, space and then time.
29
/// The tiles are automatically temporally aligned.
30
///
31
/// All inputs must have the same data type and spatial reference.
32
pub type RasterStacker = Operator<RasterStackerParams, MultipleRasterSources>;
33

34
impl OperatorName for RasterStacker {
35
    const TYPE_NAME: &'static str = "RasterStacker";
36
}
37

38
#[typetag::serde]
2✔
39
#[async_trait]
40
impl RasterOperator for RasterStacker {
41
    async fn _initialize(
42
        self: Box<Self>,
43
        path: WorkflowOperatorPath,
44
        context: &dyn ExecutionContext,
45
    ) -> Result<Box<dyn InitializedRasterOperator>> {
24✔
46
        let name = CanonicOperatorName::from(&self);
24✔
47

24✔
48
        ensure!(
24✔
49
            !self.sources.rasters.is_empty() && self.sources.rasters.len() <= 8,
24✔
50
            InvalidNumberOfRasterStackerInputs
×
51
        );
52

53
        let raster_sources = self
24✔
54
            .sources
24✔
55
            .initialize_sources(path.clone(), context)
24✔
56
            .await?
24✔
57
            .rasters;
58

59
        let in_descriptors = raster_sources
24✔
60
            .iter()
24✔
61
            .map(InitializedRasterOperator::result_descriptor)
24✔
62
            .collect::<Vec<_>>();
24✔
63

24✔
64
        ensure!(
24✔
65
            in_descriptors.iter().all(|d| d.spatial_reference
57✔
66
                == in_descriptors[0].spatial_reference
57✔
67
                && d.data_type == in_descriptors[0].data_type),
57✔
68
            RasterInputsMustHaveSameSpatialReferenceAndDatatype {
×
69
                datatypes: in_descriptors
×
70
                    .iter()
×
71
                    .map(|d| d.data_type)
×
72
                    .collect::<Vec<_>>(),
×
73
                spatial_references: in_descriptors
×
74
                    .iter()
×
75
                    .map(|d| d.spatial_reference)
×
76
                    .collect::<Vec<_>>(),
×
77
            }
×
78
        );
79

80
        let time = time_interval_extent(in_descriptors.iter().map(|d| d.time));
31✔
81
        let bbox = partitions_extent(in_descriptors.iter().map(|d| d.bbox));
31✔
82

24✔
83
        let resolution = in_descriptors
24✔
84
            .iter()
24✔
85
            .map(|d| d.resolution)
57✔
86
            .reduce(|a, b| match (a, b) {
33✔
87
                (Some(a), Some(b)) => {
7✔
88
                    Some(SpatialResolution::new_unchecked(a.x.min(b.x), a.y.min(b.y)))
7✔
89
                }
90
                _ => None,
26✔
91
            })
33✔
92
            .flatten();
24✔
93

24✔
94
        let data_type = in_descriptors[0].data_type;
24✔
95
        let spatial_reference = in_descriptors[0].spatial_reference;
24✔
96

24✔
97
        let bands_per_source = in_descriptors
24✔
98
            .iter()
24✔
99
            .map(|d| d.bands.count())
57✔
100
            .collect::<Vec<_>>();
24✔
101

102
        let band_names = self.params.rename_bands.apply(
24✔
103
            in_descriptors
24✔
104
                .iter()
24✔
105
                .map(|d| d.bands.iter().map(|b| b.name.clone()).collect())
59✔
106
                .collect(),
24✔
107
        )?;
24✔
108

109
        let output_band_descriptors = in_descriptors
24✔
110
            .into_iter()
24✔
111
            .flat_map(|d| d.bands.iter().cloned())
57✔
112
            .zip(band_names)
24✔
113
            .map(|(descriptor, name)| RasterBandDescriptor { name, ..descriptor })
59✔
114
            .collect::<Vec<_>>()
24✔
115
            .try_into()?;
24✔
116

117
        let result_descriptor = RasterResultDescriptor {
24✔
118
            data_type,
24✔
119
            spatial_reference,
24✔
120
            time,
24✔
121
            bbox,
24✔
122
            resolution,
24✔
123
            bands: output_band_descriptors,
24✔
124
        };
24✔
125

24✔
126
        Ok(Box::new(InitializedRasterStacker {
24✔
127
            name,
24✔
128
            path,
24✔
129
            result_descriptor,
24✔
130
            raster_sources,
24✔
131
            bands_per_source,
24✔
132
        }))
24✔
133
    }
48✔
134

135
    span_fn!(RasterStacker);
136
}
137

138
pub struct InitializedRasterStacker {
139
    name: CanonicOperatorName,
140
    path: WorkflowOperatorPath,
141
    result_descriptor: RasterResultDescriptor,
142
    raster_sources: Vec<Box<dyn InitializedRasterOperator>>,
143
    bands_per_source: Vec<u32>,
144
}
145

146
impl InitializedRasterOperator for InitializedRasterStacker {
147
    fn result_descriptor(&self) -> &RasterResultDescriptor {
30✔
148
        &self.result_descriptor
30✔
149
    }
30✔
150

151
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor> {
24✔
152
        let typed_raster_processors = self
24✔
153
            .raster_sources
24✔
154
            .iter()
24✔
155
            .map(InitializedRasterOperator::query_processor)
24✔
156
            .collect::<Result<Vec<_>>>()?;
24✔
157

158
        // unpack all processors
159
        let datatype = typed_raster_processors[0].raster_data_type();
24✔
160

24✔
161
        let bands_per_source = self.bands_per_source.clone();
24✔
162

24✔
163
        // TODO: use a macro to unpack all the input processor to the same datatype?
24✔
164
        Ok(match datatype {
24✔
165
            geoengine_datatypes::raster::RasterDataType::U8 => {
166
                let inputs = typed_raster_processors.into_iter().map(|p| p.get_u8().expect("all inputs should have the same datatype because it was checked in the initialization of the operator")).collect();
22✔
167
                let p = RasterStackerProcessor::new(
10✔
168
                    inputs,
10✔
169
                    self.result_descriptor.clone(),
10✔
170
                    bands_per_source,
10✔
171
                );
10✔
172
                TypedRasterQueryProcessor::U8(Box::new(p))
10✔
173
            }
174
            geoengine_datatypes::raster::RasterDataType::U16 => {
175
                let inputs = typed_raster_processors.into_iter().map(|p| p.get_u16().expect("all inputs should have the same datatype because it was checked in the initialization of the operator")).collect();
×
176
                let p = RasterStackerProcessor::new(
×
177
                    inputs,
×
178
                    self.result_descriptor.clone(),
×
179
                    bands_per_source,
×
180
                );
×
181
                TypedRasterQueryProcessor::U16(Box::new(p))
×
182
            }
183
            geoengine_datatypes::raster::RasterDataType::U32 => {
184
                let inputs = typed_raster_processors.into_iter().map(|p| p.get_u32().expect("all inputs should have the same datatype because it was checked in the initialization of the operator")).collect();
×
185
                let p = RasterStackerProcessor::new(
×
186
                    inputs,
×
187
                    self.result_descriptor.clone(),
×
188
                    bands_per_source,
×
189
                );
×
190
                TypedRasterQueryProcessor::U32(Box::new(p))
×
191
            }
192
            geoengine_datatypes::raster::RasterDataType::U64 => {
193
                let inputs = typed_raster_processors.into_iter().map(|p| p.get_u64().expect("all inputs should have the same datatype because it was checked in the initialization of the operator")).collect();
×
194
                let p = RasterStackerProcessor::new(
×
195
                    inputs,
×
196
                    self.result_descriptor.clone(),
×
197
                    bands_per_source,
×
198
                );
×
199
                TypedRasterQueryProcessor::U64(Box::new(p))
×
200
            }
201
            geoengine_datatypes::raster::RasterDataType::I8 => {
202
                let inputs = typed_raster_processors.into_iter().map(|p| p.get_i8().expect("all inputs should have the same datatype because it was checked in the initialization of the operator")).collect();
28✔
203
                let p = RasterStackerProcessor::new(
11✔
204
                    inputs,
11✔
205
                    self.result_descriptor.clone(),
11✔
206
                    bands_per_source,
11✔
207
                );
11✔
208
                TypedRasterQueryProcessor::I8(Box::new(p))
11✔
209
            }
210
            geoengine_datatypes::raster::RasterDataType::I16 => {
211
                let inputs = typed_raster_processors.into_iter().map(|p| p.get_i16().expect("all inputs should have the same datatype because it was checked in the initialization of the operator")).collect();
2✔
212
                let p = RasterStackerProcessor::new(
1✔
213
                    inputs,
1✔
214
                    self.result_descriptor.clone(),
1✔
215
                    bands_per_source,
1✔
216
                );
1✔
217
                TypedRasterQueryProcessor::I16(Box::new(p))
1✔
218
            }
219
            geoengine_datatypes::raster::RasterDataType::I32 => {
220
                let inputs = typed_raster_processors.into_iter().map(|p| p.get_i32().expect("all inputs should have the same datatype because it was checked in the initialization of the operator")).collect();
×
221
                let p = RasterStackerProcessor::new(
×
222
                    inputs,
×
223
                    self.result_descriptor.clone(),
×
224
                    bands_per_source,
×
225
                );
×
226
                TypedRasterQueryProcessor::I32(Box::new(p))
×
227
            }
228
            geoengine_datatypes::raster::RasterDataType::I64 => {
229
                let inputs = typed_raster_processors.into_iter().map(|p| p.get_i64().expect("all inputs should have the same datatype because it was checked in the initialization of the operator")).collect();
×
230
                let p = RasterStackerProcessor::new(
×
231
                    inputs,
×
232
                    self.result_descriptor.clone(),
×
233
                    bands_per_source,
×
234
                );
×
235
                TypedRasterQueryProcessor::I64(Box::new(p))
×
236
            }
237
            geoengine_datatypes::raster::RasterDataType::F32 => {
238
                let inputs = typed_raster_processors.into_iter().map(|p| p.get_f32().expect("all inputs should have the same datatype because it was checked in the initialization of the operator")).collect();
5✔
239
                let p = RasterStackerProcessor::new(
2✔
240
                    inputs,
2✔
241
                    self.result_descriptor.clone(),
2✔
242
                    bands_per_source,
2✔
243
                );
2✔
244
                TypedRasterQueryProcessor::F32(Box::new(p))
2✔
245
            }
246
            geoengine_datatypes::raster::RasterDataType::F64 => {
247
                let inputs = typed_raster_processors.into_iter().map(|p| p.get_f64().expect("all inputs should have the same datatype because it was checked in the initialization of the operator")).collect();
×
248
                let p = RasterStackerProcessor::new(
×
249
                    inputs,
×
250
                    self.result_descriptor.clone(),
×
251
                    bands_per_source,
×
252
                );
×
253
                TypedRasterQueryProcessor::F64(Box::new(p))
×
254
            }
255
        })
256
    }
24✔
257

258
    fn canonic_name(&self) -> CanonicOperatorName {
1✔
259
        self.name.clone()
1✔
260
    }
1✔
261

NEW
262
    fn name(&self) -> &'static str {
×
NEW
263
        RasterStacker::TYPE_NAME
×
NEW
264
    }
×
265

NEW
266
    fn path(&self) -> WorkflowOperatorPath {
×
NEW
267
        self.path.clone()
×
NEW
268
    }
×
269
}
270

271
pub(crate) struct RasterStackerProcessor<T> {
272
    sources: Vec<Box<dyn RasterQueryProcessor<RasterType = T>>>,
273
    result_descriptor: RasterResultDescriptor,
274
    bands_per_source: Vec<u32>,
275
}
276

277
impl<T> RasterStackerProcessor<T> {
278
    pub fn new(
24✔
279
        sources: Vec<Box<dyn RasterQueryProcessor<RasterType = T>>>,
24✔
280
        result_descriptor: RasterResultDescriptor,
24✔
281
        bands_per_source: Vec<u32>,
24✔
282
    ) -> Self {
24✔
283
        Self {
24✔
284
            sources,
24✔
285
            result_descriptor,
24✔
286
            bands_per_source,
24✔
287
        }
24✔
288
    }
24✔
289
}
290

291
/// compute the bands in the input source from the bands in a query that uses multiple sources
292
fn map_query_bands_to_source_bands(
165✔
293
    query_bands: &BandSelection,
165✔
294
    bands_per_source: &[u32],
165✔
295
    source_index: usize,
165✔
296
) -> Option<BandSelection> {
165✔
297
    let source_start: u32 = bands_per_source.iter().take(source_index).sum();
165✔
298
    let source_bands = bands_per_source[source_index];
165✔
299
    let source_end = source_start + source_bands;
165✔
300

165✔
301
    let bands = query_bands
165✔
302
        .as_slice()
165✔
303
        .iter()
165✔
304
        .filter(|output_band| **output_band >= source_start && **output_band < source_end)
274✔
305
        .map(|output_band| output_band - source_start)
165✔
306
        .collect::<Vec<_>>();
165✔
307

165✔
308
    if bands.is_empty() {
165✔
309
        return None;
63✔
310
    }
102✔
311

102✔
312
    Some(BandSelection::new_unchecked(bands))
102✔
313
}
165✔
314

315
#[async_trait]
316
impl<T> RasterQueryProcessor for RasterStackerProcessor<T>
317
where
318
    T: Pixel,
319
{
320
    type RasterType = T;
321
    async fn raster_query<'a>(
322
        &'a self,
323
        query: RasterQueryRectangle,
324
        ctx: &'a dyn QueryContext,
325
    ) -> Result<BoxStream<'a, Result<RasterTile2D<T>>>> {
72✔
326
        let mut sources = vec![];
72✔
327

328
        for (idx, source) in self.sources.iter().enumerate() {
160✔
329
            let Some(bands) =
98✔
330
                map_query_bands_to_source_bands(&query.attributes, &self.bands_per_source, idx)
160✔
331
            else {
332
                continue;
62✔
333
            };
334

335
            let mut source_query = query.clone();
98✔
336
            source_query.attributes = bands.clone();
98✔
337
            sources.push(RasterStackerSource {
98✔
338
                queryable: QueryWrapper { p: source, ctx },
98✔
339
                band_idxs: bands.as_vec(),
98✔
340
            });
98✔
341
        }
342

343
        let output = RasterStackerAdapter::new(sources, query.into());
72✔
344

72✔
345
        Ok(Box::pin(output))
72✔
346
    }
144✔
347

348
    fn raster_result_descriptor(&self) -> &RasterResultDescriptor {
104✔
349
        &self.result_descriptor
104✔
350
    }
104✔
351
}
352

353
#[cfg(test)]
354
mod tests {
355
    use std::str::FromStr;
356

357
    use futures::StreamExt;
358
    use geoengine_datatypes::{
359
        primitives::{CacheHint, SpatialPartition2D, TimeInstance, TimeInterval},
360
        raster::{Grid, GridShape, RasterDataType, TilesEqualIgnoringCacheHint},
361
        spatial_reference::SpatialReference,
362
        util::test::TestDefault,
363
    };
364

365
    use crate::{
366
        engine::{
367
            MockExecutionContext, MockQueryContext, RasterBandDescriptor, RasterBandDescriptors,
368
            SingleRasterSource,
369
        },
370
        mock::{MockRasterSource, MockRasterSourceParams},
371
        processing::{Expression, ExpressionParams},
372
        source::{GdalSource, GdalSourceParameters},
373
        util::gdal::add_ndvi_dataset,
374
    };
375

376
    use super::*;
377

378
    #[test]
379
    fn it_maps_query_bands_to_source_bands() {
1✔
380
        assert_eq!(
1✔
381
            map_query_bands_to_source_bands(&0.into(), &[2, 1], 0),
1✔
382
            Some(0.into())
1✔
383
        );
1✔
384
        assert_eq!(map_query_bands_to_source_bands(&0.into(), &[2, 1], 1), None);
1✔
385
        assert_eq!(
1✔
386
            map_query_bands_to_source_bands(&2.into(), &[2, 1], 1),
1✔
387
            Some(0.into())
1✔
388
        );
1✔
389

390
        assert_eq!(
1✔
391
            map_query_bands_to_source_bands(&[1, 2].try_into().unwrap(), &[2, 2], 0),
1✔
392
            Some(1.into())
1✔
393
        );
1✔
394
        assert_eq!(
1✔
395
            map_query_bands_to_source_bands(&[1, 2, 3].try_into().unwrap(), &[2, 2], 1),
1✔
396
            Some([0, 1].try_into().unwrap())
1✔
397
        );
1✔
398
    }
1✔
399

400
    #[tokio::test]
401
    #[allow(clippy::too_many_lines)]
402
    async fn it_stacks() {
1✔
403
        let data: Vec<RasterTile2D<u8>> = vec![
1✔
404
            RasterTile2D {
1✔
405
                time: TimeInterval::new_unchecked(0, 5),
1✔
406
                tile_position: [-1, 0].into(),
1✔
407
                band: 0,
1✔
408
                global_geo_transform: TestDefault::test_default(),
1✔
409
                grid_array: Grid::new([2, 2].into(), vec![0, 1, 2, 3]).unwrap().into(),
1✔
410
                properties: Default::default(),
1✔
411
                cache_hint: CacheHint::default(),
1✔
412
            },
1✔
413
            RasterTile2D {
1✔
414
                time: TimeInterval::new_unchecked(0, 5),
1✔
415
                tile_position: [-1, 1].into(),
1✔
416
                band: 0,
1✔
417
                global_geo_transform: TestDefault::test_default(),
1✔
418
                grid_array: Grid::new([2, 2].into(), vec![4, 5, 6, 7]).unwrap().into(),
1✔
419
                properties: Default::default(),
1✔
420
                cache_hint: CacheHint::default(),
1✔
421
            },
1✔
422
            RasterTile2D {
1✔
423
                time: TimeInterval::new_unchecked(5, 10),
1✔
424
                tile_position: [-1, 0].into(),
1✔
425
                band: 0,
1✔
426
                global_geo_transform: TestDefault::test_default(),
1✔
427
                grid_array: Grid::new([2, 2].into(), vec![8, 9, 10, 11]).unwrap().into(),
1✔
428
                properties: Default::default(),
1✔
429
                cache_hint: CacheHint::default(),
1✔
430
            },
1✔
431
            RasterTile2D {
1✔
432
                time: TimeInterval::new_unchecked(5, 10),
1✔
433
                tile_position: [-1, 1].into(),
1✔
434
                band: 0,
1✔
435
                global_geo_transform: TestDefault::test_default(),
1✔
436
                grid_array: Grid::new([2, 2].into(), vec![12, 13, 14, 15])
1✔
437
                    .unwrap()
1✔
438
                    .into(),
1✔
439
                properties: Default::default(),
1✔
440
                cache_hint: CacheHint::default(),
1✔
441
            },
1✔
442
        ];
1✔
443

1✔
444
        let data2: Vec<RasterTile2D<u8>> = vec![
1✔
445
            RasterTile2D {
1✔
446
                time: TimeInterval::new_unchecked(0, 5),
1✔
447
                tile_position: [-1, 0].into(),
1✔
448
                band: 0,
1✔
449
                global_geo_transform: TestDefault::test_default(),
1✔
450
                grid_array: Grid::new([2, 2].into(), vec![16, 17, 18, 19])
1✔
451
                    .unwrap()
1✔
452
                    .into(),
1✔
453
                properties: Default::default(),
1✔
454
                cache_hint: CacheHint::default(),
1✔
455
            },
1✔
456
            RasterTile2D {
1✔
457
                time: TimeInterval::new_unchecked(0, 5),
1✔
458
                tile_position: [-1, 1].into(),
1✔
459
                band: 0,
1✔
460
                global_geo_transform: TestDefault::test_default(),
1✔
461
                grid_array: Grid::new([2, 2].into(), vec![20, 21, 22, 23])
1✔
462
                    .unwrap()
1✔
463
                    .into(),
1✔
464
                properties: Default::default(),
1✔
465
                cache_hint: CacheHint::default(),
1✔
466
            },
1✔
467
            RasterTile2D {
1✔
468
                time: TimeInterval::new_unchecked(5, 10),
1✔
469
                tile_position: [-1, 0].into(),
1✔
470
                band: 0,
1✔
471
                global_geo_transform: TestDefault::test_default(),
1✔
472
                grid_array: Grid::new([2, 2].into(), vec![24, 25, 26, 27])
1✔
473
                    .unwrap()
1✔
474
                    .into(),
1✔
475
                properties: Default::default(),
1✔
476
                cache_hint: CacheHint::default(),
1✔
477
            },
1✔
478
            RasterTile2D {
1✔
479
                time: TimeInterval::new_unchecked(5, 10),
1✔
480
                tile_position: [-1, 1].into(),
1✔
481
                band: 0,
1✔
482
                global_geo_transform: TestDefault::test_default(),
1✔
483
                grid_array: Grid::new([2, 2].into(), vec![28, 29, 30, 31])
1✔
484
                    .unwrap()
1✔
485
                    .into(),
1✔
486
                properties: Default::default(),
1✔
487
                cache_hint: CacheHint::default(),
1✔
488
            },
1✔
489
        ];
1✔
490

1✔
491
        let mrs1 = MockRasterSource {
1✔
492
            params: MockRasterSourceParams {
1✔
493
                data: data.clone(),
1✔
494
                result_descriptor: RasterResultDescriptor {
1✔
495
                    data_type: RasterDataType::U8,
1✔
496
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
497
                    time: None,
1✔
498
                    bbox: None,
1✔
499
                    resolution: None,
1✔
500
                    bands: RasterBandDescriptors::new_single_band(),
1✔
501
                },
1✔
502
            },
1✔
503
        }
1✔
504
        .boxed();
1✔
505

1✔
506
        let mrs2 = MockRasterSource {
1✔
507
            params: MockRasterSourceParams {
1✔
508
                data: data2.clone(),
1✔
509
                result_descriptor: RasterResultDescriptor {
1✔
510
                    data_type: RasterDataType::U8,
1✔
511
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
512
                    time: None,
1✔
513
                    bbox: None,
1✔
514
                    resolution: None,
1✔
515
                    bands: RasterBandDescriptors::new_single_band(),
1✔
516
                },
1✔
517
            },
1✔
518
        }
1✔
519
        .boxed();
1✔
520

1✔
521
        let stacker = RasterStacker {
1✔
522
            params: RasterStackerParams {
1✔
523
                rename_bands: RenameBands::Default,
1✔
524
            },
1✔
525
            sources: MultipleRasterSources {
1✔
526
                rasters: vec![mrs1, mrs2],
1✔
527
            },
1✔
528
        }
1✔
529
        .boxed();
1✔
530

1✔
531
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
532
        exe_ctx.tiling_specification.tile_size_in_pixels = GridShape {
1✔
533
            shape_array: [2, 2],
1✔
534
        };
1✔
535

1✔
536
        let query_rect = RasterQueryRectangle {
1✔
537
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 1.).into(), (3., 0.).into()),
1✔
538
            time_interval: TimeInterval::new_unchecked(0, 10),
1✔
539
            spatial_resolution: SpatialResolution::one(),
1✔
540
            attributes: [0, 1].try_into().unwrap(),
1✔
541
        };
1✔
542

1✔
543
        let query_ctx = MockQueryContext::test_default();
1✔
544

1✔
545
        let op = stacker
1✔
546
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
547
            .await
1✔
548
            .unwrap();
1✔
549

1✔
550
        let qp = op.query_processor().unwrap().get_u8().unwrap();
1✔
551

1✔
552
        let result = qp
1✔
553
            .raster_query(query_rect, &query_ctx)
1✔
554
            .await
1✔
555
            .unwrap()
1✔
556
            .collect::<Vec<_>>()
1✔
557
            .await;
1✔
558
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
559

1✔
560
        let expected: Vec<_> = data
1✔
561
            .into_iter()
1✔
562
            .zip(data2.into_iter().map(|mut tile| {
4✔
563
                tile.band = 1;
4✔
564
                tile
4✔
565
            }))
4✔
566
            .flat_map(|(a, b)| vec![a.clone(), b.clone()])
4✔
567
            .collect();
1✔
568

1✔
569
        assert!(expected.tiles_equal_ignoring_cache_hint(&result));
1✔
570
    }
1✔
571

572
    #[tokio::test]
573
    #[allow(clippy::too_many_lines)]
574
    async fn it_stacks_stacks() {
1✔
575
        let data: Vec<RasterTile2D<u8>> = vec![
1✔
576
            RasterTile2D {
1✔
577
                time: TimeInterval::new_unchecked(0, 5),
1✔
578
                tile_position: [-1, 0].into(),
1✔
579
                band: 0,
1✔
580
                global_geo_transform: TestDefault::test_default(),
1✔
581
                grid_array: Grid::new([2, 2].into(), vec![0, 1, 2, 3]).unwrap().into(),
1✔
582
                properties: Default::default(),
1✔
583
                cache_hint: CacheHint::default(),
1✔
584
            },
1✔
585
            RasterTile2D {
1✔
586
                time: TimeInterval::new_unchecked(0, 5),
1✔
587
                tile_position: [-1, 0].into(),
1✔
588
                band: 1,
1✔
589
                global_geo_transform: TestDefault::test_default(),
1✔
590
                grid_array: Grid::new([2, 2].into(), vec![3, 2, 1, 0]).unwrap().into(),
1✔
591
                properties: Default::default(),
1✔
592
                cache_hint: CacheHint::default(),
1✔
593
            },
1✔
594
            RasterTile2D {
1✔
595
                time: TimeInterval::new_unchecked(0, 5),
1✔
596
                tile_position: [-1, 1].into(),
1✔
597
                band: 0,
1✔
598
                global_geo_transform: TestDefault::test_default(),
1✔
599
                grid_array: Grid::new([2, 2].into(), vec![4, 5, 6, 7]).unwrap().into(),
1✔
600
                properties: Default::default(),
1✔
601
                cache_hint: CacheHint::default(),
1✔
602
            },
1✔
603
            RasterTile2D {
1✔
604
                time: TimeInterval::new_unchecked(0, 5),
1✔
605
                tile_position: [-1, 1].into(),
1✔
606
                band: 1,
1✔
607
                global_geo_transform: TestDefault::test_default(),
1✔
608
                grid_array: Grid::new([2, 2].into(), vec![7, 6, 5, 4]).unwrap().into(),
1✔
609
                properties: Default::default(),
1✔
610
                cache_hint: CacheHint::default(),
1✔
611
            },
1✔
612
            RasterTile2D {
1✔
613
                time: TimeInterval::new_unchecked(5, 10),
1✔
614
                tile_position: [-1, 0].into(),
1✔
615
                band: 0,
1✔
616
                global_geo_transform: TestDefault::test_default(),
1✔
617
                grid_array: Grid::new([2, 2].into(), vec![8, 9, 10, 11]).unwrap().into(),
1✔
618
                properties: Default::default(),
1✔
619
                cache_hint: CacheHint::default(),
1✔
620
            },
1✔
621
            RasterTile2D {
1✔
622
                time: TimeInterval::new_unchecked(5, 10),
1✔
623
                tile_position: [-1, 0].into(),
1✔
624
                band: 1,
1✔
625
                global_geo_transform: TestDefault::test_default(),
1✔
626
                grid_array: Grid::new([2, 2].into(), vec![11, 10, 9, 8]).unwrap().into(),
1✔
627
                properties: Default::default(),
1✔
628
                cache_hint: CacheHint::default(),
1✔
629
            },
1✔
630
            RasterTile2D {
1✔
631
                time: TimeInterval::new_unchecked(5, 10),
1✔
632
                tile_position: [-1, 1].into(),
1✔
633
                band: 0,
1✔
634
                global_geo_transform: TestDefault::test_default(),
1✔
635
                grid_array: Grid::new([2, 2].into(), vec![12, 13, 14, 15])
1✔
636
                    .unwrap()
1✔
637
                    .into(),
1✔
638
                properties: Default::default(),
1✔
639
                cache_hint: CacheHint::default(),
1✔
640
            },
1✔
641
            RasterTile2D {
1✔
642
                time: TimeInterval::new_unchecked(5, 10),
1✔
643
                tile_position: [-1, 1].into(),
1✔
644
                band: 1,
1✔
645
                global_geo_transform: TestDefault::test_default(),
1✔
646
                grid_array: Grid::new([2, 2].into(), vec![15, 14, 13, 12])
1✔
647
                    .unwrap()
1✔
648
                    .into(),
1✔
649
                properties: Default::default(),
1✔
650
                cache_hint: CacheHint::default(),
1✔
651
            },
1✔
652
        ];
1✔
653

1✔
654
        let data2: Vec<RasterTile2D<u8>> = vec![
1✔
655
            RasterTile2D {
1✔
656
                time: TimeInterval::new_unchecked(0, 5),
1✔
657
                tile_position: [-1, 0].into(),
1✔
658
                band: 0,
1✔
659
                global_geo_transform: TestDefault::test_default(),
1✔
660
                grid_array: Grid::new([2, 2].into(), vec![16, 17, 18, 19])
1✔
661
                    .unwrap()
1✔
662
                    .into(),
1✔
663
                properties: Default::default(),
1✔
664
                cache_hint: CacheHint::default(),
1✔
665
            },
1✔
666
            RasterTile2D {
1✔
667
                time: TimeInterval::new_unchecked(0, 5),
1✔
668
                tile_position: [-1, 0].into(),
1✔
669
                band: 1,
1✔
670
                global_geo_transform: TestDefault::test_default(),
1✔
671
                grid_array: Grid::new([2, 2].into(), vec![19, 18, 17, 16])
1✔
672
                    .unwrap()
1✔
673
                    .into(),
1✔
674
                properties: Default::default(),
1✔
675
                cache_hint: CacheHint::default(),
1✔
676
            },
1✔
677
            RasterTile2D {
1✔
678
                time: TimeInterval::new_unchecked(0, 5),
1✔
679
                tile_position: [-1, 1].into(),
1✔
680
                band: 0,
1✔
681
                global_geo_transform: TestDefault::test_default(),
1✔
682
                grid_array: Grid::new([2, 2].into(), vec![20, 21, 22, 23])
1✔
683
                    .unwrap()
1✔
684
                    .into(),
1✔
685
                properties: Default::default(),
1✔
686
                cache_hint: CacheHint::default(),
1✔
687
            },
1✔
688
            RasterTile2D {
1✔
689
                time: TimeInterval::new_unchecked(0, 5),
1✔
690
                tile_position: [-1, 1].into(),
1✔
691
                band: 1,
1✔
692
                global_geo_transform: TestDefault::test_default(),
1✔
693
                grid_array: Grid::new([2, 2].into(), vec![32, 22, 21, 20])
1✔
694
                    .unwrap()
1✔
695
                    .into(),
1✔
696
                properties: Default::default(),
1✔
697
                cache_hint: CacheHint::default(),
1✔
698
            },
1✔
699
            RasterTile2D {
1✔
700
                time: TimeInterval::new_unchecked(5, 10),
1✔
701
                tile_position: [-1, 0].into(),
1✔
702
                band: 0,
1✔
703
                global_geo_transform: TestDefault::test_default(),
1✔
704
                grid_array: Grid::new([2, 2].into(), vec![24, 25, 26, 27])
1✔
705
                    .unwrap()
1✔
706
                    .into(),
1✔
707
                properties: Default::default(),
1✔
708
                cache_hint: CacheHint::default(),
1✔
709
            },
1✔
710
            RasterTile2D {
1✔
711
                time: TimeInterval::new_unchecked(5, 10),
1✔
712
                tile_position: [-1, 0].into(),
1✔
713
                band: 1,
1✔
714
                global_geo_transform: TestDefault::test_default(),
1✔
715
                grid_array: Grid::new([2, 2].into(), vec![27, 26, 25, 24])
1✔
716
                    .unwrap()
1✔
717
                    .into(),
1✔
718
                properties: Default::default(),
1✔
719
                cache_hint: CacheHint::default(),
1✔
720
            },
1✔
721
            RasterTile2D {
1✔
722
                time: TimeInterval::new_unchecked(5, 10),
1✔
723
                tile_position: [-1, 1].into(),
1✔
724
                band: 0,
1✔
725
                global_geo_transform: TestDefault::test_default(),
1✔
726
                grid_array: Grid::new([2, 2].into(), vec![28, 29, 30, 31])
1✔
727
                    .unwrap()
1✔
728
                    .into(),
1✔
729
                properties: Default::default(),
1✔
730
                cache_hint: CacheHint::default(),
1✔
731
            },
1✔
732
            RasterTile2D {
1✔
733
                time: TimeInterval::new_unchecked(5, 10),
1✔
734
                tile_position: [-1, 1].into(),
1✔
735
                band: 1,
1✔
736
                global_geo_transform: TestDefault::test_default(),
1✔
737
                grid_array: Grid::new([2, 2].into(), vec![31, 30, 39, 28])
1✔
738
                    .unwrap()
1✔
739
                    .into(),
1✔
740
                properties: Default::default(),
1✔
741
                cache_hint: CacheHint::default(),
1✔
742
            },
1✔
743
        ];
1✔
744

1✔
745
        let mrs1 = MockRasterSource {
1✔
746
            params: MockRasterSourceParams {
1✔
747
                data: data.clone(),
1✔
748
                result_descriptor: RasterResultDescriptor {
1✔
749
                    data_type: RasterDataType::U8,
1✔
750
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
751
                    time: None,
1✔
752
                    bbox: None,
1✔
753
                    resolution: None,
1✔
754
                    bands: RasterBandDescriptors::new(vec![
1✔
755
                        RasterBandDescriptor::new_unitless("band_0".into()),
1✔
756
                        RasterBandDescriptor::new_unitless("band_1".into()),
1✔
757
                    ])
1✔
758
                    .unwrap(),
1✔
759
                },
1✔
760
            },
1✔
761
        }
1✔
762
        .boxed();
1✔
763

1✔
764
        let mrs2 = MockRasterSource {
1✔
765
            params: MockRasterSourceParams {
1✔
766
                data: data2.clone(),
1✔
767
                result_descriptor: RasterResultDescriptor {
1✔
768
                    data_type: RasterDataType::U8,
1✔
769
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
770
                    time: None,
1✔
771
                    bbox: None,
1✔
772
                    resolution: None,
1✔
773
                    bands: RasterBandDescriptors::new(vec![
1✔
774
                        RasterBandDescriptor::new_unitless("band_0".into()),
1✔
775
                        RasterBandDescriptor::new_unitless("band_1".into()),
1✔
776
                    ])
1✔
777
                    .unwrap(),
1✔
778
                },
1✔
779
            },
1✔
780
        }
1✔
781
        .boxed();
1✔
782

1✔
783
        let stacker = RasterStacker {
1✔
784
            params: RasterStackerParams {
1✔
785
                rename_bands: RenameBands::Default,
1✔
786
            },
1✔
787
            sources: MultipleRasterSources {
1✔
788
                rasters: vec![mrs1, mrs2],
1✔
789
            },
1✔
790
        }
1✔
791
        .boxed();
1✔
792

1✔
793
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
794
        exe_ctx.tiling_specification.tile_size_in_pixels = GridShape {
1✔
795
            shape_array: [2, 2],
1✔
796
        };
1✔
797

1✔
798
        let query_rect = RasterQueryRectangle {
1✔
799
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 1.).into(), (3., 0.).into()),
1✔
800
            time_interval: TimeInterval::new_unchecked(0, 10),
1✔
801
            spatial_resolution: SpatialResolution::one(),
1✔
802
            attributes: [0, 1, 2, 3].try_into().unwrap(),
1✔
803
        };
1✔
804

1✔
805
        let query_ctx = MockQueryContext::test_default();
1✔
806

1✔
807
        let op = stacker
1✔
808
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
809
            .await
1✔
810
            .unwrap();
1✔
811

1✔
812
        let qp = op.query_processor().unwrap().get_u8().unwrap();
1✔
813

1✔
814
        let result = qp
1✔
815
            .raster_query(query_rect, &query_ctx)
1✔
816
            .await
1✔
817
            .unwrap()
1✔
818
            .collect::<Vec<_>>()
1✔
819
            .await;
1✔
820
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
821

1✔
822
        let expected: Vec<_> = data
1✔
823
            .chunks(2)
1✔
824
            .zip(
1✔
825
                data2
1✔
826
                    .into_iter()
1✔
827
                    .map(|mut tile| {
8✔
828
                        tile.band += 2;
8✔
829
                        tile
8✔
830
                    })
8✔
831
                    .collect::<Vec<_>>()
1✔
832
                    .chunks(2),
1✔
833
            )
1✔
834
            .flat_map(|(chunk1, chunk2)| chunk1.iter().chain(chunk2.iter()))
4✔
835
            .cloned()
1✔
836
            .collect();
1✔
837

1✔
838
        assert!(expected.tiles_equal_ignoring_cache_hint(&result));
1✔
839
    }
1✔
840

841
    #[tokio::test]
842
    #[allow(clippy::too_many_lines)]
843
    async fn it_selects_band_from_stack() {
1✔
844
        let data: Vec<RasterTile2D<u8>> = vec![
1✔
845
            RasterTile2D {
1✔
846
                time: TimeInterval::new_unchecked(0, 5),
1✔
847
                tile_position: [-1, 0].into(),
1✔
848
                band: 0,
1✔
849
                global_geo_transform: TestDefault::test_default(),
1✔
850
                grid_array: Grid::new([2, 2].into(), vec![0, 1, 2, 3]).unwrap().into(),
1✔
851
                properties: Default::default(),
1✔
852
                cache_hint: CacheHint::default(),
1✔
853
            },
1✔
854
            RasterTile2D {
1✔
855
                time: TimeInterval::new_unchecked(0, 5),
1✔
856
                tile_position: [-1, 1].into(),
1✔
857
                band: 0,
1✔
858
                global_geo_transform: TestDefault::test_default(),
1✔
859
                grid_array: Grid::new([2, 2].into(), vec![4, 5, 6, 7]).unwrap().into(),
1✔
860
                properties: Default::default(),
1✔
861
                cache_hint: CacheHint::default(),
1✔
862
            },
1✔
863
            RasterTile2D {
1✔
864
                time: TimeInterval::new_unchecked(5, 10),
1✔
865
                tile_position: [-1, 0].into(),
1✔
866
                band: 0,
1✔
867
                global_geo_transform: TestDefault::test_default(),
1✔
868
                grid_array: Grid::new([2, 2].into(), vec![8, 9, 10, 11]).unwrap().into(),
1✔
869
                properties: Default::default(),
1✔
870
                cache_hint: CacheHint::default(),
1✔
871
            },
1✔
872
            RasterTile2D {
1✔
873
                time: TimeInterval::new_unchecked(5, 10),
1✔
874
                tile_position: [-1, 1].into(),
1✔
875
                band: 0,
1✔
876
                global_geo_transform: TestDefault::test_default(),
1✔
877
                grid_array: Grid::new([2, 2].into(), vec![12, 13, 14, 15])
1✔
878
                    .unwrap()
1✔
879
                    .into(),
1✔
880
                properties: Default::default(),
1✔
881
                cache_hint: CacheHint::default(),
1✔
882
            },
1✔
883
        ];
1✔
884

1✔
885
        let data2: Vec<RasterTile2D<u8>> = vec![
1✔
886
            RasterTile2D {
1✔
887
                time: TimeInterval::new_unchecked(0, 5),
1✔
888
                tile_position: [-1, 0].into(),
1✔
889
                band: 0,
1✔
890
                global_geo_transform: TestDefault::test_default(),
1✔
891
                grid_array: Grid::new([2, 2].into(), vec![16, 17, 18, 19])
1✔
892
                    .unwrap()
1✔
893
                    .into(),
1✔
894
                properties: Default::default(),
1✔
895
                cache_hint: CacheHint::default(),
1✔
896
            },
1✔
897
            RasterTile2D {
1✔
898
                time: TimeInterval::new_unchecked(0, 5),
1✔
899
                tile_position: [-1, 1].into(),
1✔
900
                band: 0,
1✔
901
                global_geo_transform: TestDefault::test_default(),
1✔
902
                grid_array: Grid::new([2, 2].into(), vec![20, 21, 22, 23])
1✔
903
                    .unwrap()
1✔
904
                    .into(),
1✔
905
                properties: Default::default(),
1✔
906
                cache_hint: CacheHint::default(),
1✔
907
            },
1✔
908
            RasterTile2D {
1✔
909
                time: TimeInterval::new_unchecked(5, 10),
1✔
910
                tile_position: [-1, 0].into(),
1✔
911
                band: 0,
1✔
912
                global_geo_transform: TestDefault::test_default(),
1✔
913
                grid_array: Grid::new([2, 2].into(), vec![24, 25, 26, 27])
1✔
914
                    .unwrap()
1✔
915
                    .into(),
1✔
916
                properties: Default::default(),
1✔
917
                cache_hint: CacheHint::default(),
1✔
918
            },
1✔
919
            RasterTile2D {
1✔
920
                time: TimeInterval::new_unchecked(5, 10),
1✔
921
                tile_position: [-1, 1].into(),
1✔
922
                band: 0,
1✔
923
                global_geo_transform: TestDefault::test_default(),
1✔
924
                grid_array: Grid::new([2, 2].into(), vec![28, 29, 30, 31])
1✔
925
                    .unwrap()
1✔
926
                    .into(),
1✔
927
                properties: Default::default(),
1✔
928
                cache_hint: CacheHint::default(),
1✔
929
            },
1✔
930
        ];
1✔
931

1✔
932
        let mrs1 = MockRasterSource {
1✔
933
            params: MockRasterSourceParams {
1✔
934
                data: data.clone(),
1✔
935
                result_descriptor: RasterResultDescriptor {
1✔
936
                    data_type: RasterDataType::U8,
1✔
937
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
938
                    time: None,
1✔
939
                    bbox: None,
1✔
940
                    resolution: None,
1✔
941
                    bands: RasterBandDescriptors::new_single_band(),
1✔
942
                },
1✔
943
            },
1✔
944
        }
1✔
945
        .boxed();
1✔
946

1✔
947
        let mrs2 = MockRasterSource {
1✔
948
            params: MockRasterSourceParams {
1✔
949
                data: data2.clone(),
1✔
950
                result_descriptor: RasterResultDescriptor {
1✔
951
                    data_type: RasterDataType::U8,
1✔
952
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
953
                    time: None,
1✔
954
                    bbox: None,
1✔
955
                    resolution: None,
1✔
956
                    bands: RasterBandDescriptors::new_single_band(),
1✔
957
                },
1✔
958
            },
1✔
959
        }
1✔
960
        .boxed();
1✔
961

1✔
962
        let stacker = RasterStacker {
1✔
963
            params: RasterStackerParams {
1✔
964
                rename_bands: RenameBands::Default,
1✔
965
            },
1✔
966
            sources: MultipleRasterSources {
1✔
967
                rasters: vec![mrs1, mrs2],
1✔
968
            },
1✔
969
        }
1✔
970
        .boxed();
1✔
971

1✔
972
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
973
        exe_ctx.tiling_specification.tile_size_in_pixels = GridShape {
1✔
974
            shape_array: [2, 2],
1✔
975
        };
1✔
976

1✔
977
        let query_rect = RasterQueryRectangle {
1✔
978
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 1.).into(), (3., 0.).into()),
1✔
979
            time_interval: TimeInterval::new_unchecked(0, 10),
1✔
980
            spatial_resolution: SpatialResolution::one(),
1✔
981
            attributes: 1.into(),
1✔
982
        };
1✔
983

1✔
984
        let query_ctx = MockQueryContext::test_default();
1✔
985

1✔
986
        let op = stacker
1✔
987
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
988
            .await
1✔
989
            .unwrap();
1✔
990

1✔
991
        let qp = op.query_processor().unwrap().get_u8().unwrap();
1✔
992

1✔
993
        let result = qp
1✔
994
            .raster_query(query_rect, &query_ctx)
1✔
995
            .await
1✔
996
            .unwrap()
1✔
997
            .collect::<Vec<_>>()
1✔
998
            .await;
1✔
999
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
1000

1✔
1001
        assert!(data2.tiles_equal_ignoring_cache_hint(&result));
1✔
1002
    }
1✔
1003

1004
    #[tokio::test]
1005
    #[allow(clippy::too_many_lines)]
1006
    async fn it_stacks_ndvi() {
1✔
1007
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
1008

1✔
1009
        let ndvi_id = add_ndvi_dataset(&mut exe_ctx);
1✔
1010

1✔
1011
        let expression = Expression {
1✔
1012
            params: ExpressionParams {
1✔
1013
                expression: "if A > 100 { A } else { 0 }".into(),
1✔
1014
                output_type: RasterDataType::U8,
1✔
1015
                output_band: None,
1✔
1016
                map_no_data: false,
1✔
1017
            },
1✔
1018
            sources: SingleRasterSource {
1✔
1019
                raster: GdalSource {
1✔
1020
                    params: GdalSourceParameters {
1✔
1021
                        data: ndvi_id.clone(),
1✔
1022
                    },
1✔
1023
                }
1✔
1024
                .boxed(),
1✔
1025
            },
1✔
1026
        }
1✔
1027
        .boxed();
1✔
1028

1✔
1029
        let operator = RasterStacker {
1✔
1030
            params: RasterStackerParams {
1✔
1031
                rename_bands: RenameBands::Default,
1✔
1032
            },
1✔
1033
            sources: MultipleRasterSources {
1✔
1034
                rasters: vec![
1✔
1035
                    GdalSource {
1✔
1036
                        params: GdalSourceParameters { data: ndvi_id },
1✔
1037
                    }
1✔
1038
                    .boxed(),
1✔
1039
                    expression,
1✔
1040
                ],
1✔
1041
            },
1✔
1042
        }
1✔
1043
        .boxed();
1✔
1044

1✔
1045
        let operator = operator
1✔
1046
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1047
            .await
1✔
1048
            .unwrap();
1✔
1049

1✔
1050
        let processor = operator.query_processor().unwrap().get_u8().unwrap();
1✔
1051

1✔
1052
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
1053
        exe_ctx.tiling_specification.tile_size_in_pixels = GridShape {
1✔
1054
            shape_array: [2, 2],
1✔
1055
        };
1✔
1056

1✔
1057
        let query_ctx = MockQueryContext::test_default();
1✔
1058

1✔
1059
        // query both bands
1✔
1060
        let query_rect = RasterQueryRectangle {
1✔
1061
            spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1062
                (-180., 90.).into(),
1✔
1063
                (180., -90.).into(),
1✔
1064
            ),
1✔
1065
            time_interval: TimeInterval::new_unchecked(
1✔
1066
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1067
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1068
            ),
1✔
1069
            spatial_resolution: SpatialResolution::one(),
1✔
1070
            attributes: [0, 1].try_into().unwrap(),
1✔
1071
        };
1✔
1072

1✔
1073
        let result = processor
1✔
1074
            .raster_query(query_rect, &query_ctx)
1✔
1075
            .await
1✔
1076
            .unwrap()
1✔
1077
            .collect::<Vec<_>>()
1✔
1078
            .await;
1✔
1079
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
1080

1✔
1081
        assert!(!result.is_empty());
1✔
1082

1✔
1083
        // query only first band
1✔
1084
        let query_rect = RasterQueryRectangle {
1✔
1085
            spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1086
                (-180., 90.).into(),
1✔
1087
                (180., -90.).into(),
1✔
1088
            ),
1✔
1089
            time_interval: TimeInterval::new_unchecked(
1✔
1090
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1091
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1092
            ),
1✔
1093
            spatial_resolution: SpatialResolution::one(),
1✔
1094
            attributes: [0].try_into().unwrap(),
1✔
1095
        };
1✔
1096

1✔
1097
        let result = processor
1✔
1098
            .raster_query(query_rect, &query_ctx)
1✔
1099
            .await
1✔
1100
            .unwrap()
1✔
1101
            .collect::<Vec<_>>()
1✔
1102
            .await;
1✔
1103
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
1104

1✔
1105
        assert!(!result.is_empty());
1✔
1106

1✔
1107
        // query only second band
1✔
1108
        let query_rect = RasterQueryRectangle {
1✔
1109
            spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1110
                (-180., 90.).into(),
1✔
1111
                (180., -90.).into(),
1✔
1112
            ),
1✔
1113
            time_interval: TimeInterval::new_unchecked(
1✔
1114
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1115
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1116
            ),
1✔
1117
            spatial_resolution: SpatialResolution::one(),
1✔
1118
            attributes: [1].try_into().unwrap(),
1✔
1119
        };
1✔
1120

1✔
1121
        let result = processor
1✔
1122
            .raster_query(query_rect, &query_ctx)
1✔
1123
            .await
1✔
1124
            .unwrap()
1✔
1125
            .collect::<Vec<_>>()
1✔
1126
            .await;
1✔
1127
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
1128

1✔
1129
        assert!(!result.is_empty());
1✔
1130
    }
1✔
1131

1132
    #[test]
1133
    fn it_renames() {
1✔
1134
        let names = vec![
1✔
1135
            vec!["foo".to_string(), "bar".to_string()],
1✔
1136
            vec!["foo".to_string(), "bla".to_string()],
1✔
1137
            vec!["foo".to_string(), "baz".to_string()],
1✔
1138
        ];
1✔
1139

1✔
1140
        assert_eq!(
1✔
1141
            RenameBands::Default.apply(names.clone()).unwrap(),
1✔
1142
            vec![
1✔
1143
                "foo".to_string(),
1✔
1144
                "bar".to_string(),
1✔
1145
                "foo (1)".to_string(),
1✔
1146
                "bla".to_string(),
1✔
1147
                "foo (2)".to_string(),
1✔
1148
                "baz".to_string()
1✔
1149
            ]
1✔
1150
        );
1✔
1151

1152
        assert_eq!(
1✔
1153
            RenameBands::Suffix(vec![
1✔
1154
                String::new(),
1✔
1155
                " second".to_string(),
1✔
1156
                " third".to_string()
1✔
1157
            ])
1✔
1158
            .apply(names.clone())
1✔
1159
            .unwrap(),
1✔
1160
            vec![
1✔
1161
                "foo".to_string(),
1✔
1162
                "bar".to_string(),
1✔
1163
                "foo second".to_string(),
1✔
1164
                "bla second".to_string(),
1✔
1165
                "foo third".to_string(),
1✔
1166
                "baz third".to_string()
1✔
1167
            ]
1✔
1168
        );
1✔
1169

1170
        assert_eq!(
1✔
1171
            RenameBands::Rename(vec![
1✔
1172
                "A".to_string(),
1✔
1173
                "B".to_string(),
1✔
1174
                "C".to_string(),
1✔
1175
                "D".to_string(),
1✔
1176
                "E".to_string(),
1✔
1177
                "F".to_string()
1✔
1178
            ])
1✔
1179
            .apply(names.clone())
1✔
1180
            .unwrap(),
1✔
1181
            vec![
1✔
1182
                "A".to_string(),
1✔
1183
                "B".to_string(),
1✔
1184
                "C".to_string(),
1✔
1185
                "D".to_string(),
1✔
1186
                "E".to_string(),
1✔
1187
                "F".to_string()
1✔
1188
            ]
1✔
1189
        );
1✔
1190
    }
1✔
1191
}
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