• 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

94.19
/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

UNCOV
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]
×
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>> {
19✔
46
        let name = CanonicOperatorName::from(&self);
19✔
47

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

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

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

19✔
64
        ensure!(
19✔
65
            in_descriptors.iter().all(|d| d.spatial_reference
45✔
66
                == in_descriptors[0].spatial_reference
45✔
67
                && d.data_type == in_descriptors[0].data_type),
45✔
68
            RasterInputsMustHaveSameSpatialReferenceAndDatatype {
19✔
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
        );
19✔
79

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

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

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

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

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

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

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

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

134
    span_fn!(RasterStacker);
135
}
136

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

144
impl InitializedRasterOperator for InitializedRasterStacker {
145
    fn result_descriptor(&self) -> &RasterResultDescriptor {
24✔
146
        &self.result_descriptor
24✔
147
    }
24✔
148

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

156
        // unpack all processors
157
        let datatype = typed_raster_processors[0].raster_data_type();
19✔
158

19✔
159
        let bands_per_source = self.bands_per_source.clone();
19✔
160

19✔
161
        // TODO: use a macro to unpack all the input processor to the same datatype?
19✔
162
        Ok(match datatype {
19✔
163
            geoengine_datatypes::raster::RasterDataType::U8 => {
164
                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();
16✔
165
                let p = RasterStackerProcessor::new(
8✔
166
                    inputs,
8✔
167
                    self.result_descriptor.clone(),
8✔
168
                    bands_per_source,
8✔
169
                );
8✔
170
                TypedRasterQueryProcessor::U8(Box::new(p))
8✔
171
            }
172
            geoengine_datatypes::raster::RasterDataType::U16 => {
173
                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();
×
174
                let p = RasterStackerProcessor::new(
×
175
                    inputs,
×
176
                    self.result_descriptor.clone(),
×
177
                    bands_per_source,
×
178
                );
×
179
                TypedRasterQueryProcessor::U16(Box::new(p))
×
180
            }
181
            geoengine_datatypes::raster::RasterDataType::U32 => {
182
                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();
×
183
                let p = RasterStackerProcessor::new(
×
184
                    inputs,
×
185
                    self.result_descriptor.clone(),
×
186
                    bands_per_source,
×
187
                );
×
188
                TypedRasterQueryProcessor::U32(Box::new(p))
×
189
            }
190
            geoengine_datatypes::raster::RasterDataType::U64 => {
191
                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();
×
192
                let p = RasterStackerProcessor::new(
×
193
                    inputs,
×
194
                    self.result_descriptor.clone(),
×
195
                    bands_per_source,
×
196
                );
×
197
                TypedRasterQueryProcessor::U64(Box::new(p))
×
198
            }
199
            geoengine_datatypes::raster::RasterDataType::I8 => {
200
                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();
27✔
201
                let p = RasterStackerProcessor::new(
10✔
202
                    inputs,
10✔
203
                    self.result_descriptor.clone(),
10✔
204
                    bands_per_source,
10✔
205
                );
10✔
206
                TypedRasterQueryProcessor::I8(Box::new(p))
10✔
207
            }
208
            geoengine_datatypes::raster::RasterDataType::I16 => {
209
                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✔
210
                let p = RasterStackerProcessor::new(
1✔
211
                    inputs,
1✔
212
                    self.result_descriptor.clone(),
1✔
213
                    bands_per_source,
1✔
214
                );
1✔
215
                TypedRasterQueryProcessor::I16(Box::new(p))
1✔
216
            }
217
            geoengine_datatypes::raster::RasterDataType::I32 => {
218
                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();
×
219
                let p = RasterStackerProcessor::new(
×
220
                    inputs,
×
221
                    self.result_descriptor.clone(),
×
222
                    bands_per_source,
×
223
                );
×
224
                TypedRasterQueryProcessor::I32(Box::new(p))
×
225
            }
226
            geoengine_datatypes::raster::RasterDataType::I64 => {
227
                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();
×
228
                let p = RasterStackerProcessor::new(
×
229
                    inputs,
×
230
                    self.result_descriptor.clone(),
×
231
                    bands_per_source,
×
232
                );
×
233
                TypedRasterQueryProcessor::I64(Box::new(p))
×
234
            }
235
            geoengine_datatypes::raster::RasterDataType::F32 => {
236
                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();
×
237
                let p = RasterStackerProcessor::new(
×
238
                    inputs,
×
239
                    self.result_descriptor.clone(),
×
240
                    bands_per_source,
×
241
                );
×
242
                TypedRasterQueryProcessor::F32(Box::new(p))
×
243
            }
244
            geoengine_datatypes::raster::RasterDataType::F64 => {
245
                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();
×
246
                let p = RasterStackerProcessor::new(
×
247
                    inputs,
×
248
                    self.result_descriptor.clone(),
×
249
                    bands_per_source,
×
250
                );
×
251
                TypedRasterQueryProcessor::F64(Box::new(p))
×
252
            }
253
        })
254
    }
19✔
255

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

261
pub(crate) struct RasterStackerProcessor<T> {
262
    sources: Vec<Box<dyn RasterQueryProcessor<RasterType = T>>>,
263
    result_descriptor: RasterResultDescriptor,
264
    bands_per_source: Vec<u32>,
265
}
266

267
impl<T> RasterStackerProcessor<T> {
268
    pub fn new(
19✔
269
        sources: Vec<Box<dyn RasterQueryProcessor<RasterType = T>>>,
19✔
270
        result_descriptor: RasterResultDescriptor,
19✔
271
        bands_per_source: Vec<u32>,
19✔
272
    ) -> Self {
19✔
273
        Self {
19✔
274
            sources,
19✔
275
            result_descriptor,
19✔
276
            bands_per_source,
19✔
277
        }
19✔
278
    }
19✔
279
}
280

281
/// compute the bands in the input source from the bands in a query that uses multiple sources
282
fn map_query_bands_to_source_bands(
153✔
283
    query_bands: &BandSelection,
153✔
284
    bands_per_source: &[u32],
153✔
285
    source_index: usize,
153✔
286
) -> Option<BandSelection> {
153✔
287
    let source_start: u32 = bands_per_source.iter().take(source_index).sum();
153✔
288
    let source_bands = bands_per_source[source_index];
153✔
289
    let source_end = source_start + source_bands;
153✔
290

153✔
291
    let bands = query_bands
153✔
292
        .as_slice()
153✔
293
        .iter()
153✔
294
        .filter(|output_band| **output_band >= source_start && **output_band < source_end)
248✔
295
        .map(|output_band| output_band - source_start)
153✔
296
        .collect::<Vec<_>>();
153✔
297

153✔
298
    if bands.is_empty() {
153✔
299
        return None;
61✔
300
    }
92✔
301

92✔
302
    Some(BandSelection::new_unchecked(bands))
92✔
303
}
153✔
304

305
#[async_trait]
306
impl<T> RasterQueryProcessor for RasterStackerProcessor<T>
307
where
308
    T: Pixel,
309
{
310
    type RasterType = T;
311
    async fn raster_query<'a>(
312
        &'a self,
313
        query: RasterQueryRectangle,
314
        ctx: &'a dyn QueryContext,
315
    ) -> Result<BoxStream<'a, Result<RasterTile2D<T>>>> {
67✔
316
        let mut sources = vec![];
67✔
317

67✔
318
        for (idx, source) in self.sources.iter().enumerate() {
148✔
319
            let Some(bands) =
88✔
320
                map_query_bands_to_source_bands(&query.attributes, &self.bands_per_source, idx)
148✔
321
            else {
67✔
322
                continue;
67✔
323
            };
67✔
324

67✔
325
            let mut source_query = query.clone();
88✔
326
            source_query.attributes = bands.clone();
88✔
327
            sources.push(RasterStackerSource {
88✔
328
                queryable: QueryWrapper { p: source, ctx },
88✔
329
                band_idxs: bands.as_vec(),
88✔
330
            });
88✔
331
        }
67✔
332

67✔
333
        let output = RasterStackerAdapter::new(sources, query.into());
67✔
334

67✔
335
        Ok(Box::pin(output))
67✔
336
    }
67✔
337

338
    fn raster_result_descriptor(&self) -> &RasterResultDescriptor {
95✔
339
        &self.result_descriptor
95✔
340
    }
95✔
341
}
342

343
#[cfg(test)]
344
mod tests {
345
    use std::str::FromStr;
346

347
    use futures::StreamExt;
348
    use geoengine_datatypes::{
349
        primitives::{CacheHint, SpatialPartition2D, TimeInstance, TimeInterval},
350
        raster::{Grid, GridShape, RasterDataType, TilesEqualIgnoringCacheHint},
351
        spatial_reference::SpatialReference,
352
        util::test::TestDefault,
353
    };
354

355
    use crate::{
356
        engine::{
357
            MockExecutionContext, MockQueryContext, RasterBandDescriptor, RasterBandDescriptors,
358
            SingleRasterSource,
359
        },
360
        mock::{MockRasterSource, MockRasterSourceParams},
361
        processing::{Expression, ExpressionParams},
362
        source::{GdalSource, GdalSourceParameters},
363
        util::gdal::add_ndvi_dataset,
364
    };
365

366
    use super::*;
367

368
    #[test]
369
    fn it_maps_query_bands_to_source_bands() {
1✔
370
        assert_eq!(
1✔
371
            map_query_bands_to_source_bands(&0.into(), &[2, 1], 0),
1✔
372
            Some(0.into())
1✔
373
        );
1✔
374
        assert_eq!(map_query_bands_to_source_bands(&0.into(), &[2, 1], 1), None);
1✔
375
        assert_eq!(
1✔
376
            map_query_bands_to_source_bands(&2.into(), &[2, 1], 1),
1✔
377
            Some(0.into())
1✔
378
        );
1✔
379

380
        assert_eq!(
1✔
381
            map_query_bands_to_source_bands(&[1, 2].try_into().unwrap(), &[2, 2], 0),
1✔
382
            Some(1.into())
1✔
383
        );
1✔
384
        assert_eq!(
1✔
385
            map_query_bands_to_source_bands(&[1, 2, 3].try_into().unwrap(), &[2, 2], 1),
1✔
386
            Some([0, 1].try_into().unwrap())
1✔
387
        );
1✔
388
    }
1✔
389

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

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

1✔
481
        let mrs1 = MockRasterSource {
1✔
482
            params: MockRasterSourceParams {
1✔
483
                data: data.clone(),
1✔
484
                result_descriptor: RasterResultDescriptor {
1✔
485
                    data_type: RasterDataType::U8,
1✔
486
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
487
                    time: None,
1✔
488
                    bbox: None,
1✔
489
                    resolution: None,
1✔
490
                    bands: RasterBandDescriptors::new_single_band(),
1✔
491
                },
1✔
492
            },
1✔
493
        }
1✔
494
        .boxed();
1✔
495

1✔
496
        let mrs2 = MockRasterSource {
1✔
497
            params: MockRasterSourceParams {
1✔
498
                data: data2.clone(),
1✔
499
                result_descriptor: RasterResultDescriptor {
1✔
500
                    data_type: RasterDataType::U8,
1✔
501
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
502
                    time: None,
1✔
503
                    bbox: None,
1✔
504
                    resolution: None,
1✔
505
                    bands: RasterBandDescriptors::new_single_band(),
1✔
506
                },
1✔
507
            },
1✔
508
        }
1✔
509
        .boxed();
1✔
510

1✔
511
        let stacker = RasterStacker {
1✔
512
            params: RasterStackerParams {
1✔
513
                rename_bands: RenameBands::Default,
1✔
514
            },
1✔
515
            sources: MultipleRasterSources {
1✔
516
                rasters: vec![mrs1, mrs2],
1✔
517
            },
1✔
518
        }
1✔
519
        .boxed();
1✔
520

1✔
521
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
522
        exe_ctx.tiling_specification.tile_size_in_pixels = GridShape {
1✔
523
            shape_array: [2, 2],
1✔
524
        };
1✔
525

1✔
526
        let query_rect = RasterQueryRectangle {
1✔
527
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 1.).into(), (3., 0.).into()),
1✔
528
            time_interval: TimeInterval::new_unchecked(0, 10),
1✔
529
            spatial_resolution: SpatialResolution::one(),
1✔
530
            attributes: [0, 1].try_into().unwrap(),
1✔
531
        };
1✔
532

1✔
533
        let query_ctx = MockQueryContext::test_default();
1✔
534

1✔
535
        let op = stacker
1✔
536
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
537
            .await
1✔
538
            .unwrap();
1✔
539

1✔
540
        let qp = op.query_processor().unwrap().get_u8().unwrap();
1✔
541

1✔
542
        let result = qp
1✔
543
            .raster_query(query_rect, &query_ctx)
1✔
544
            .await
1✔
545
            .unwrap()
1✔
546
            .collect::<Vec<_>>()
1✔
547
            .await;
1✔
548
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
549

1✔
550
        let expected: Vec<_> = data
1✔
551
            .into_iter()
1✔
552
            .zip(data2.into_iter().map(|mut tile| {
4✔
553
                tile.band = 1;
4✔
554
                tile
4✔
555
            }))
4✔
556
            .flat_map(|(a, b)| vec![a.clone(), b.clone()])
4✔
557
            .collect();
1✔
558

1✔
559
        assert!(expected.tiles_equal_ignoring_cache_hint(&result));
1✔
560
    }
1✔
561

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

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

1✔
735
        let mrs1 = MockRasterSource {
1✔
736
            params: MockRasterSourceParams {
1✔
737
                data: data.clone(),
1✔
738
                result_descriptor: RasterResultDescriptor {
1✔
739
                    data_type: RasterDataType::U8,
1✔
740
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
741
                    time: None,
1✔
742
                    bbox: None,
1✔
743
                    resolution: None,
1✔
744
                    bands: RasterBandDescriptors::new(vec![
1✔
745
                        RasterBandDescriptor::new_unitless("band_0".into()),
1✔
746
                        RasterBandDescriptor::new_unitless("band_1".into()),
1✔
747
                    ])
1✔
748
                    .unwrap(),
1✔
749
                },
1✔
750
            },
1✔
751
        }
1✔
752
        .boxed();
1✔
753

1✔
754
        let mrs2 = MockRasterSource {
1✔
755
            params: MockRasterSourceParams {
1✔
756
                data: data2.clone(),
1✔
757
                result_descriptor: RasterResultDescriptor {
1✔
758
                    data_type: RasterDataType::U8,
1✔
759
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
760
                    time: None,
1✔
761
                    bbox: None,
1✔
762
                    resolution: None,
1✔
763
                    bands: RasterBandDescriptors::new(vec![
1✔
764
                        RasterBandDescriptor::new_unitless("band_0".into()),
1✔
765
                        RasterBandDescriptor::new_unitless("band_1".into()),
1✔
766
                    ])
1✔
767
                    .unwrap(),
1✔
768
                },
1✔
769
            },
1✔
770
        }
1✔
771
        .boxed();
1✔
772

1✔
773
        let stacker = RasterStacker {
1✔
774
            params: RasterStackerParams {
1✔
775
                rename_bands: RenameBands::Default,
1✔
776
            },
1✔
777
            sources: MultipleRasterSources {
1✔
778
                rasters: vec![mrs1, mrs2],
1✔
779
            },
1✔
780
        }
1✔
781
        .boxed();
1✔
782

1✔
783
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
784
        exe_ctx.tiling_specification.tile_size_in_pixels = GridShape {
1✔
785
            shape_array: [2, 2],
1✔
786
        };
1✔
787

1✔
788
        let query_rect = RasterQueryRectangle {
1✔
789
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 1.).into(), (3., 0.).into()),
1✔
790
            time_interval: TimeInterval::new_unchecked(0, 10),
1✔
791
            spatial_resolution: SpatialResolution::one(),
1✔
792
            attributes: [0, 1, 2, 3].try_into().unwrap(),
1✔
793
        };
1✔
794

1✔
795
        let query_ctx = MockQueryContext::test_default();
1✔
796

1✔
797
        let op = stacker
1✔
798
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
799
            .await
1✔
800
            .unwrap();
1✔
801

1✔
802
        let qp = op.query_processor().unwrap().get_u8().unwrap();
1✔
803

1✔
804
        let result = qp
1✔
805
            .raster_query(query_rect, &query_ctx)
1✔
806
            .await
1✔
807
            .unwrap()
1✔
808
            .collect::<Vec<_>>()
1✔
809
            .await;
1✔
810
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
811

1✔
812
        let expected: Vec<_> = data
1✔
813
            .chunks(2)
1✔
814
            .zip(
1✔
815
                data2
1✔
816
                    .into_iter()
1✔
817
                    .map(|mut tile| {
8✔
818
                        tile.band += 2;
8✔
819
                        tile
8✔
820
                    })
8✔
821
                    .collect::<Vec<_>>()
1✔
822
                    .chunks(2),
1✔
823
            )
1✔
824
            .flat_map(|(chunk1, chunk2)| chunk1.iter().chain(chunk2.iter()))
4✔
825
            .cloned()
1✔
826
            .collect();
1✔
827

1✔
828
        assert!(expected.tiles_equal_ignoring_cache_hint(&result));
1✔
829
    }
1✔
830

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

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

1✔
922
        let mrs1 = MockRasterSource {
1✔
923
            params: MockRasterSourceParams {
1✔
924
                data: data.clone(),
1✔
925
                result_descriptor: RasterResultDescriptor {
1✔
926
                    data_type: RasterDataType::U8,
1✔
927
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
928
                    time: None,
1✔
929
                    bbox: None,
1✔
930
                    resolution: None,
1✔
931
                    bands: RasterBandDescriptors::new_single_band(),
1✔
932
                },
1✔
933
            },
1✔
934
        }
1✔
935
        .boxed();
1✔
936

1✔
937
        let mrs2 = MockRasterSource {
1✔
938
            params: MockRasterSourceParams {
1✔
939
                data: data2.clone(),
1✔
940
                result_descriptor: RasterResultDescriptor {
1✔
941
                    data_type: RasterDataType::U8,
1✔
942
                    spatial_reference: SpatialReference::epsg_4326().into(),
1✔
943
                    time: None,
1✔
944
                    bbox: None,
1✔
945
                    resolution: None,
1✔
946
                    bands: RasterBandDescriptors::new_single_band(),
1✔
947
                },
1✔
948
            },
1✔
949
        }
1✔
950
        .boxed();
1✔
951

1✔
952
        let stacker = RasterStacker {
1✔
953
            params: RasterStackerParams {
1✔
954
                rename_bands: RenameBands::Default,
1✔
955
            },
1✔
956
            sources: MultipleRasterSources {
1✔
957
                rasters: vec![mrs1, mrs2],
1✔
958
            },
1✔
959
        }
1✔
960
        .boxed();
1✔
961

1✔
962
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
963
        exe_ctx.tiling_specification.tile_size_in_pixels = GridShape {
1✔
964
            shape_array: [2, 2],
1✔
965
        };
1✔
966

1✔
967
        let query_rect = RasterQueryRectangle {
1✔
968
            spatial_bounds: SpatialPartition2D::new_unchecked((0., 1.).into(), (3., 0.).into()),
1✔
969
            time_interval: TimeInterval::new_unchecked(0, 10),
1✔
970
            spatial_resolution: SpatialResolution::one(),
1✔
971
            attributes: 1.into(),
1✔
972
        };
1✔
973

1✔
974
        let query_ctx = MockQueryContext::test_default();
1✔
975

1✔
976
        let op = stacker
1✔
977
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
978
            .await
1✔
979
            .unwrap();
1✔
980

1✔
981
        let qp = op.query_processor().unwrap().get_u8().unwrap();
1✔
982

1✔
983
        let result = qp
1✔
984
            .raster_query(query_rect, &query_ctx)
1✔
985
            .await
1✔
986
            .unwrap()
1✔
987
            .collect::<Vec<_>>()
1✔
988
            .await;
1✔
989
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
990

1✔
991
        assert!(data2.tiles_equal_ignoring_cache_hint(&result));
1✔
992
    }
1✔
993

994
    #[tokio::test]
995
    #[allow(clippy::too_many_lines)]
996
    async fn it_stacks_ndvi() {
1✔
997
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
998

1✔
999
        let ndvi_id = add_ndvi_dataset(&mut exe_ctx);
1✔
1000

1✔
1001
        let expression = Expression {
1✔
1002
            params: ExpressionParams {
1✔
1003
                expression: "if A > 100 { A } else { 0 }".into(),
1✔
1004
                output_type: RasterDataType::U8,
1✔
1005
                output_band: None,
1✔
1006
                map_no_data: false,
1✔
1007
            },
1✔
1008
            sources: SingleRasterSource {
1✔
1009
                raster: GdalSource {
1✔
1010
                    params: GdalSourceParameters {
1✔
1011
                        data: ndvi_id.clone(),
1✔
1012
                    },
1✔
1013
                }
1✔
1014
                .boxed(),
1✔
1015
            },
1✔
1016
        }
1✔
1017
        .boxed();
1✔
1018

1✔
1019
        let operator = RasterStacker {
1✔
1020
            params: RasterStackerParams {
1✔
1021
                rename_bands: RenameBands::Default,
1✔
1022
            },
1✔
1023
            sources: MultipleRasterSources {
1✔
1024
                rasters: vec![
1✔
1025
                    GdalSource {
1✔
1026
                        params: GdalSourceParameters { data: ndvi_id },
1✔
1027
                    }
1✔
1028
                    .boxed(),
1✔
1029
                    expression,
1✔
1030
                ],
1✔
1031
            },
1✔
1032
        }
1✔
1033
        .boxed();
1✔
1034

1✔
1035
        let operator = operator
1✔
1036
            .initialize(WorkflowOperatorPath::initialize_root(), &exe_ctx)
1✔
1037
            .await
1✔
1038
            .unwrap();
1✔
1039

1✔
1040
        let processor = operator.query_processor().unwrap().get_u8().unwrap();
1✔
1041

1✔
1042
        let mut exe_ctx = MockExecutionContext::test_default();
1✔
1043
        exe_ctx.tiling_specification.tile_size_in_pixels = GridShape {
1✔
1044
            shape_array: [2, 2],
1✔
1045
        };
1✔
1046

1✔
1047
        let query_ctx = MockQueryContext::test_default();
1✔
1048

1✔
1049
        // query both bands
1✔
1050
        let query_rect = RasterQueryRectangle {
1✔
1051
            spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1052
                (-180., 90.).into(),
1✔
1053
                (180., -90.).into(),
1✔
1054
            ),
1✔
1055
            time_interval: TimeInterval::new_unchecked(
1✔
1056
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1057
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1058
            ),
1✔
1059
            spatial_resolution: SpatialResolution::one(),
1✔
1060
            attributes: [0, 1].try_into().unwrap(),
1✔
1061
        };
1✔
1062

1✔
1063
        let result = processor
1✔
1064
            .raster_query(query_rect, &query_ctx)
1✔
1065
            .await
1✔
1066
            .unwrap()
1✔
1067
            .collect::<Vec<_>>()
1✔
1068
            .await;
16✔
1069
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
1070

1✔
1071
        assert!(!result.is_empty());
1✔
1072

1✔
1073
        // query only first band
1✔
1074
        let query_rect = RasterQueryRectangle {
1✔
1075
            spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1076
                (-180., 90.).into(),
1✔
1077
                (180., -90.).into(),
1✔
1078
            ),
1✔
1079
            time_interval: TimeInterval::new_unchecked(
1✔
1080
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1081
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1082
            ),
1✔
1083
            spatial_resolution: SpatialResolution::one(),
1✔
1084
            attributes: [0].try_into().unwrap(),
1✔
1085
        };
1✔
1086

1✔
1087
        let result = processor
1✔
1088
            .raster_query(query_rect, &query_ctx)
1✔
1089
            .await
1✔
1090
            .unwrap()
1✔
1091
            .collect::<Vec<_>>()
1✔
1092
            .await;
5✔
1093
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
1094

1✔
1095
        assert!(!result.is_empty());
1✔
1096

1✔
1097
        // query only second band
1✔
1098
        let query_rect = RasterQueryRectangle {
1✔
1099
            spatial_bounds: SpatialPartition2D::new_unchecked(
1✔
1100
                (-180., 90.).into(),
1✔
1101
                (180., -90.).into(),
1✔
1102
            ),
1✔
1103
            time_interval: TimeInterval::new_unchecked(
1✔
1104
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1105
                TimeInstance::from_str("2014-01-01T00:00:00.000Z").unwrap(),
1✔
1106
            ),
1✔
1107
            spatial_resolution: SpatialResolution::one(),
1✔
1108
            attributes: [1].try_into().unwrap(),
1✔
1109
        };
1✔
1110

1✔
1111
        let result = processor
1✔
1112
            .raster_query(query_rect, &query_ctx)
1✔
1113
            .await
1✔
1114
            .unwrap()
1✔
1115
            .collect::<Vec<_>>()
1✔
1116
            .await;
11✔
1117
        let result = result.into_iter().collect::<Result<Vec<_>>>().unwrap();
1✔
1118

1✔
1119
        assert!(!result.is_empty());
1✔
1120
    }
1✔
1121

1122
    #[test]
1123
    fn it_renames() {
1✔
1124
        let names = vec![
1✔
1125
            vec!["foo".to_string(), "bar".to_string()],
1✔
1126
            vec!["foo".to_string(), "bla".to_string()],
1✔
1127
            vec!["foo".to_string(), "baz".to_string()],
1✔
1128
        ];
1✔
1129

1✔
1130
        assert_eq!(
1✔
1131
            RenameBands::Default.apply(names.clone()).unwrap(),
1✔
1132
            vec![
1✔
1133
                "foo".to_string(),
1✔
1134
                "bar".to_string(),
1✔
1135
                "foo (1)".to_string(),
1✔
1136
                "bla".to_string(),
1✔
1137
                "foo (2)".to_string(),
1✔
1138
                "baz".to_string()
1✔
1139
            ]
1✔
1140
        );
1✔
1141

1142
        assert_eq!(
1✔
1143
            RenameBands::Suffix(vec![
1✔
1144
                String::new(),
1✔
1145
                " second".to_string(),
1✔
1146
                " third".to_string()
1✔
1147
            ])
1✔
1148
            .apply(names.clone())
1✔
1149
            .unwrap(),
1✔
1150
            vec![
1✔
1151
                "foo".to_string(),
1✔
1152
                "bar".to_string(),
1✔
1153
                "foo second".to_string(),
1✔
1154
                "bla second".to_string(),
1✔
1155
                "foo third".to_string(),
1✔
1156
                "baz third".to_string()
1✔
1157
            ]
1✔
1158
        );
1✔
1159

1160
        assert_eq!(
1✔
1161
            RenameBands::Rename(vec![
1✔
1162
                "A".to_string(),
1✔
1163
                "B".to_string(),
1✔
1164
                "C".to_string(),
1✔
1165
                "D".to_string(),
1✔
1166
                "E".to_string(),
1✔
1167
                "F".to_string()
1✔
1168
            ])
1✔
1169
            .apply(names.clone())
1✔
1170
            .unwrap(),
1✔
1171
            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
        );
1✔
1180
    }
1✔
1181
}
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