• 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.47
/operators/src/processing/meteosat/temperature.rs
1
use std::sync::Arc;
2

3
use crate::engine::{
4
    CanonicOperatorName, ExecutionContext, InitializedRasterOperator, InitializedSources, Operator,
5
    OperatorName, QueryContext, QueryProcessor, RasterBandDescriptor, RasterBandDescriptors,
6
    RasterOperator, RasterQueryProcessor, RasterResultDescriptor, SingleRasterSource,
7
    TypedRasterQueryProcessor, WorkflowOperatorPath,
8
};
9
use crate::util::Result;
10
use async_trait::async_trait;
11
use rayon::ThreadPool;
12

13
use TypedRasterQueryProcessor::F32 as QueryProcessorOut;
14

15
use crate::error::Error;
16
use futures::stream::BoxStream;
17
use futures::{StreamExt, TryStreamExt};
18
use geoengine_datatypes::primitives::{
19
    BandSelection, ClassificationMeasurement, ContinuousMeasurement, Measurement,
20
    RasterQueryRectangle, SpatialPartition2D,
21
};
22
use geoengine_datatypes::raster::{
23
    MapElementsParallel, Pixel, RasterDataType, RasterPropertiesKey, RasterTile2D,
24
};
25
use serde::{Deserialize, Serialize};
26

27
// Output type is always f32
28
type PixelOut = f32;
29
use crate::processing::meteosat::satellite::{Channel, Satellite};
30
use crate::processing::meteosat::{
31
    new_channel_key, new_offset_key, new_satellite_key, new_slope_key,
32
};
33
use RasterDataType::F32 as RasterOut;
34

35
/// Parameters for the `Temperature` operator.
36
/// * `force_satellite` forces the use of the satellite with the given name.
UNCOV
37
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)]
×
38
#[serde(rename_all = "camelCase")]
39
pub struct TemperatureParams {
40
    force_satellite: Option<u8>,
41
}
42

43
/// The temperature operator approximates BT from
44
/// the raw MSG rasters.
45
pub type Temperature = Operator<TemperatureParams, SingleRasterSource>;
46

47
impl OperatorName for Temperature {
48
    const TYPE_NAME: &'static str = "Temperature";
49
}
50

51
pub struct InitializedTemperature {
52
    name: CanonicOperatorName,
53
    result_descriptor: RasterResultDescriptor,
54
    source: Box<dyn InitializedRasterOperator>,
55
    params: TemperatureParams,
56
}
57

58
#[typetag::serde]
×
59
#[async_trait]
60
impl RasterOperator for Temperature {
61
    async fn _initialize(
62
        self: Box<Self>,
63
        path: WorkflowOperatorPath,
64
        context: &dyn ExecutionContext,
65
    ) -> Result<Box<dyn InitializedRasterOperator>> {
14✔
66
        let name = CanonicOperatorName::from(&self);
14✔
67

14✔
68
        let initialized_sources = self.sources.initialize_sources(path, context).await?;
14✔
69
        let input = initialized_sources.raster;
14✔
70

14✔
71
        let in_desc = input.result_descriptor();
14✔
72

14✔
73
        for band in in_desc.bands.iter() {
14✔
74
            match &band.measurement {
14✔
75
                Measurement::Continuous(ContinuousMeasurement {
12✔
76
                    measurement: m,
12✔
77
                    unit: _,
12✔
78
                }) if m != "raw" => {
12✔
79
                    return Err(Error::InvalidMeasurement {
1✔
80
                        expected: "raw".into(),
1✔
81
                        found: m.clone(),
1✔
82
                    })
1✔
83
                }
14✔
84
                Measurement::Classification(ClassificationMeasurement {
14✔
85
                    measurement: m,
14✔
86
                    classes: _,
1✔
87
                }) => {
1✔
88
                    return Err(Error::InvalidMeasurement {
1✔
89
                        expected: "raw".into(),
1✔
90
                        found: m.clone(),
1✔
91
                    })
1✔
92
                }
14✔
93
                Measurement::Unitless => {
14✔
94
                    return Err(Error::InvalidMeasurement {
14✔
95
                        expected: "raw".into(),
1✔
96
                        found: "unitless".into(),
1✔
97
                    })
1✔
98
                }
14✔
99
                // OK Case
14✔
100
                Measurement::Continuous(ContinuousMeasurement {
14✔
101
                    measurement: _,
14✔
102
                    unit: _,
14✔
103
                }) => {}
14✔
104
            }
14✔
105
        }
14✔
106

14✔
107
        let out_desc = RasterResultDescriptor {
14✔
108
            spatial_reference: in_desc.spatial_reference,
14✔
109
            data_type: RasterOut,
11✔
110
            time: in_desc.time,
11✔
111
            bbox: in_desc.bbox,
11✔
112
            resolution: in_desc.resolution,
11✔
113
            bands: RasterBandDescriptors::new(
11✔
114
                in_desc
11✔
115
                    .bands
11✔
116
                    .iter()
11✔
117
                    .map(|b| RasterBandDescriptor {
11✔
118
                        name: b.name.clone(),
11✔
119
                        measurement: Measurement::Continuous(ContinuousMeasurement {
11✔
120
                            measurement: "temperature".into(),
11✔
121
                            unit: Some("k".into()),
11✔
122
                        }),
11✔
123
                    })
11✔
124
                    .collect::<Vec<_>>(),
11✔
125
            )?,
11✔
126
        };
14✔
127

14✔
128
        let initialized_operator = InitializedTemperature {
14✔
129
            name,
11✔
130
            result_descriptor: out_desc,
11✔
131
            source: input,
11✔
132
            params: self.params,
11✔
133
        };
11✔
134

11✔
135
        Ok(initialized_operator.boxed())
11✔
136
    }
14✔
137

138
    span_fn!(Temperature);
139
}
140

141
impl InitializedRasterOperator for InitializedTemperature {
142
    fn result_descriptor(&self) -> &RasterResultDescriptor {
×
143
        &self.result_descriptor
×
144
    }
×
145

146
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor, Error> {
11✔
147
        let q = self.source.query_processor()?;
11✔
148

149
        Ok(match q {
11✔
150
            TypedRasterQueryProcessor::U8(p) => QueryProcessorOut(Box::new(
10✔
151
                TemperatureProcessor::new(p, self.result_descriptor.clone(), self.params.clone()),
10✔
152
            )),
10✔
153
            TypedRasterQueryProcessor::U16(p) => QueryProcessorOut(Box::new(
1✔
154
                TemperatureProcessor::new(p, self.result_descriptor.clone(), self.params.clone()),
1✔
155
            )),
1✔
156
            TypedRasterQueryProcessor::U32(p) => QueryProcessorOut(Box::new(
×
157
                TemperatureProcessor::new(p, self.result_descriptor.clone(), self.params.clone()),
×
158
            )),
×
159
            TypedRasterQueryProcessor::U64(p) => QueryProcessorOut(Box::new(
×
160
                TemperatureProcessor::new(p, self.result_descriptor.clone(), self.params.clone()),
×
161
            )),
×
162
            TypedRasterQueryProcessor::I8(p) => QueryProcessorOut(Box::new(
×
163
                TemperatureProcessor::new(p, self.result_descriptor.clone(), self.params.clone()),
×
164
            )),
×
165
            TypedRasterQueryProcessor::I16(p) => QueryProcessorOut(Box::new(
×
166
                TemperatureProcessor::new(p, self.result_descriptor.clone(), self.params.clone()),
×
167
            )),
×
168
            TypedRasterQueryProcessor::I32(p) => QueryProcessorOut(Box::new(
×
169
                TemperatureProcessor::new(p, self.result_descriptor.clone(), self.params.clone()),
×
170
            )),
×
171
            TypedRasterQueryProcessor::I64(p) => QueryProcessorOut(Box::new(
×
172
                TemperatureProcessor::new(p, self.result_descriptor.clone(), self.params.clone()),
×
173
            )),
×
174
            TypedRasterQueryProcessor::F32(p) => QueryProcessorOut(Box::new(
×
175
                TemperatureProcessor::new(p, self.result_descriptor.clone(), self.params.clone()),
×
176
            )),
×
177
            TypedRasterQueryProcessor::F64(p) => QueryProcessorOut(Box::new(
×
178
                TemperatureProcessor::new(p, self.result_descriptor.clone(), self.params.clone()),
×
179
            )),
×
180
        })
181
    }
11✔
182

183
    fn canonic_name(&self) -> CanonicOperatorName {
×
184
        self.name.clone()
×
185
    }
×
186
}
187

188
struct TemperatureProcessor<Q, P>
189
where
190
    Q: RasterQueryProcessor<RasterType = P>,
191
{
192
    source: Q,
193
    result_descriptor: RasterResultDescriptor,
194
    params: TemperatureParams,
195
    satellite_key: RasterPropertiesKey,
196
    channel_key: RasterPropertiesKey,
197
    offset_key: RasterPropertiesKey,
198
    slope_key: RasterPropertiesKey,
199
}
200

201
impl<Q, P> TemperatureProcessor<Q, P>
202
where
203
    Q: RasterQueryProcessor<RasterType = P>,
204
    P: Pixel,
205
{
206
    pub fn new(
11✔
207
        source: Q,
11✔
208
        result_descriptor: RasterResultDescriptor,
11✔
209
        params: TemperatureParams,
11✔
210
    ) -> Self {
11✔
211
        Self {
11✔
212
            source,
11✔
213
            result_descriptor,
11✔
214
            params,
11✔
215
            satellite_key: new_satellite_key(),
11✔
216
            channel_key: new_channel_key(),
11✔
217
            offset_key: new_offset_key(),
11✔
218
            slope_key: new_slope_key(),
11✔
219
        }
11✔
220
    }
11✔
221

222
    fn satellite(&self, tile: &RasterTile2D<P>) -> Result<&'static Satellite> {
11✔
223
        let id = match self.params.force_satellite {
11✔
224
            Some(id) => id,
2✔
225
            _ => tile.properties.number_property(&self.satellite_key)?,
9✔
226
        };
227
        Satellite::satellite_by_msg_id(id)
10✔
228
    }
11✔
229

230
    fn channel<'a>(&self, tile: &RasterTile2D<P>, satellite: &'a Satellite) -> Result<&'a Channel> {
8✔
231
        let channel_id = tile
8✔
232
            .properties
8✔
233
            .number_property::<usize>(&self.channel_key)?
8✔
234
            - 1;
235
        if (3..=10).contains(&channel_id) {
7✔
236
            satellite.channel(channel_id)
6✔
237
        } else {
238
            Err(Error::InvalidChannel {
1✔
239
                channel: channel_id,
1✔
240
            })
1✔
241
        }
242
    }
8✔
243

244
    async fn process_tile_async(
11✔
245
        &self,
11✔
246
        tile: RasterTile2D<P>,
11✔
247
        pool: Arc<ThreadPool>,
11✔
248
    ) -> Result<RasterTile2D<PixelOut>> {
11✔
249
        let satellite = self.satellite(&tile)?;
11✔
250
        let channel = self.channel(&tile, satellite)?;
8✔
251
        let offset = tile.properties.number_property::<f64>(&self.offset_key)?;
6✔
252
        let slope = tile.properties.number_property::<f64>(&self.slope_key)?;
5✔
253

254
        let temp_tile = crate::util::spawn_blocking_with_thread_pool(pool.clone(), move || {
4✔
255
            let lut = create_lookup_table(channel, offset, slope, &pool);
4✔
256

4✔
257
            let map_fn = move |pixel_option: Option<P>| {
19✔
258
                pixel_option.and_then(|p| {
19✔
259
                    let lut_idx: u64 = p.as_();
15✔
260
                    lut.get(lut_idx as usize).copied()
15✔
261
                })
19✔
262
            };
19✔
263

264
            tile.map_elements_parallel(map_fn)
4✔
265
        })
4✔
266
        .await?;
4✔
267

268
        Ok(temp_tile)
4✔
269
    }
11✔
270
}
271

272
fn create_lookup_table(channel: &Channel, offset: f64, slope: f64, _pool: &ThreadPool) -> Vec<f32> {
4✔
273
    // this should propably be done with SIMD not a threadpool
4✔
274
    (0..1024)
4✔
275
        .map(|i| {
4,096✔
276
            let radiance = offset + f64::from(i) * slope;
4,096✔
277
            channel.calculate_temperature_from_radiance(radiance) as f32
4,096✔
278
        })
4,096✔
279
        .collect::<Vec<f32>>()
4✔
280
}
4✔
281

282
#[async_trait]
283
impl<Q, P> QueryProcessor for TemperatureProcessor<Q, P>
284
where
285
    Q: QueryProcessor<
286
        Output = RasterTile2D<P>,
287
        SpatialBounds = SpatialPartition2D,
288
        Selection = BandSelection,
289
        ResultDescription = RasterResultDescriptor,
290
    >,
291
    P: Pixel,
292
{
293
    type Output = RasterTile2D<PixelOut>;
294
    type SpatialBounds = SpatialPartition2D;
295
    type Selection = BandSelection;
296
    type ResultDescription = RasterResultDescriptor;
297

298
    async fn _query<'a>(
299
        &'a self,
300
        query: RasterQueryRectangle,
301
        ctx: &'a dyn QueryContext,
302
    ) -> Result<BoxStream<'a, Result<Self::Output>>> {
11✔
303
        let src = self.source.query(query, ctx).await?;
11✔
304
        let rs = src.and_then(move |tile| self.process_tile_async(tile, ctx.thread_pool().clone()));
11✔
305
        Ok(rs.boxed())
11✔
306
    }
11✔
307

308
    fn result_descriptor(&self) -> &Self::ResultDescription {
22✔
309
        &self.result_descriptor
22✔
310
    }
22✔
311
}
312

313
#[cfg(test)]
314
mod tests {
315
    use crate::engine::{MockExecutionContext, RasterOperator, SingleRasterSource};
316
    use crate::processing::meteosat::temperature::{Temperature, TemperatureParams};
317
    use crate::processing::meteosat::test_util;
318
    use geoengine_datatypes::primitives::{
319
        ClassificationMeasurement, ContinuousMeasurement, Measurement,
320
    };
321
    use geoengine_datatypes::raster::{EmptyGrid2D, Grid2D, MaskedGrid2D, TilingSpecification};
322
    use std::collections::HashMap;
323

324
    // #[tokio::test]
325
    // async fn test_msg_raster() {
326
    //     let mut ctx = MockExecutionContext::test_default();
327
    //     let src = test_util::_create_gdal_src(&mut ctx);
328
    //
329
    //     let result = test_util::process(
330
    //         move || {
331
    //             RasterOperator::boxed(Temperature {
332
    //                 params: TemperatureParams::default(),
333
    //                 sources: SingleRasterSource {
334
    //                     raster: src.boxed(),
335
    //                 },
336
    //             })
337
    //         },
338
    //         test_util::_create_gdal_query(),
339
    //         &ctx,
340
    //     )
341
    //     .await;
342
    //     assert!(result.as_ref().is_ok());
343
    // }
344

345
    #[tokio::test]
346
    async fn test_empty_ok() {
1✔
347
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
348
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
349

1✔
350
        let res = test_util::process(
1✔
351
            || {
1✔
352
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
353
                let src = test_util::create_mock_source::<u8>(
1✔
354
                    props,
1✔
355
                    Some(EmptyGrid2D::new([3, 2].into()).into()),
1✔
356
                    None,
1✔
357
                );
1✔
358

1✔
359
                RasterOperator::boxed(Temperature {
1✔
360
                    params: TemperatureParams::default(),
1✔
361
                    sources: SingleRasterSource {
1✔
362
                        raster: src.boxed(),
1✔
363
                    },
1✔
364
                })
1✔
365
            },
1✔
366
            test_util::create_mock_query(),
1✔
367
            &ctx,
1✔
368
        )
1✔
369
        .await
1✔
370
        .unwrap();
1✔
371

1✔
372
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
373
            &res.grid_array,
1✔
374
            &EmptyGrid2D::new([3, 2].into()).into()
1✔
375
        ));
1✔
376
    }
1✔
377

378
    #[tokio::test]
379
    async fn test_ok() {
1✔
380
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
381
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
382

1✔
383
        let res = test_util::process(
1✔
384
            || {
1✔
385
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
386
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
387

1✔
388
                RasterOperator::boxed(Temperature {
1✔
389
                    params: TemperatureParams::default(),
1✔
390
                    sources: SingleRasterSource {
1✔
391
                        raster: src.boxed(),
1✔
392
                    },
1✔
393
                })
1✔
394
            },
1✔
395
            test_util::create_mock_query(),
1✔
396
            &ctx,
1✔
397
        )
1✔
398
        .await
1✔
399
        .unwrap();
1✔
400

1✔
401
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
402
            &res.grid_array,
1✔
403
            &MaskedGrid2D::new(
1✔
404
                Grid2D::new(
1✔
405
                    [3, 2].into(),
1✔
406
                    vec![300.341_43, 318.617_65, 330.365_14, 339.233_64, 346.443_94, 0.,],
1✔
407
                )
1✔
408
                .unwrap(),
1✔
409
                Grid2D::new([3, 2].into(), vec![true, true, true, true, true, false,],).unwrap(),
1✔
410
            )
1✔
411
            .unwrap()
1✔
412
            .into()
1✔
413
        ));
1✔
414

1✔
415
        // TODO: add assert to check mask
1✔
416
    }
1✔
417

418
    #[tokio::test]
419
    async fn test_ok_force_satellite() {
1✔
420
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
421
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
422

1✔
423
        let res = test_util::process(
1✔
424
            || {
1✔
425
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
426
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
427

1✔
428
                RasterOperator::boxed(Temperature {
1✔
429
                    params: TemperatureParams {
1✔
430
                        force_satellite: Some(4),
1✔
431
                    },
1✔
432
                    sources: SingleRasterSource {
1✔
433
                        raster: src.boxed(),
1✔
434
                    },
1✔
435
                })
1✔
436
            },
1✔
437
            test_util::create_mock_query(),
1✔
438
            &ctx,
1✔
439
        )
1✔
440
        .await
1✔
441
        .unwrap();
1✔
442

1✔
443
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
444
            &res.grid_array,
1✔
445
            &MaskedGrid2D::new(
1✔
446
                Grid2D::new(
1✔
447
                    [3, 2].into(),
1✔
448
                    vec![300.9428, 319.250_15, 331.019_04, 339.9044, 347.128_78, 0.],
1✔
449
                )
1✔
450
                .unwrap(),
1✔
451
                Grid2D::new([3, 2].into(), vec![true, true, true, true, true, false,],).unwrap(),
1✔
452
            )
1✔
453
            .unwrap()
1✔
454
            .into()
1✔
455
        ));
1✔
456
    }
1✔
457

458
    #[tokio::test]
459
    async fn test_ok_illegal_input_to_masked() {
1✔
460
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
461
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
462

1✔
463
        let res = test_util::process(
1✔
464
            || {
1✔
465
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
466
                let src = test_util::create_mock_source::<u16>(
1✔
467
                    props,
1✔
468
                    Some(
1✔
469
                        MaskedGrid2D::new(
1✔
470
                            Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 1024, 0]).unwrap(),
1✔
471
                            Grid2D::new([3, 2].into(), vec![true, true, true, true, true, false])
1✔
472
                                .unwrap(),
1✔
473
                        )
1✔
474
                        .unwrap()
1✔
475
                        .into(),
1✔
476
                    ),
1✔
477
                    None,
1✔
478
                );
1✔
479

1✔
480
                RasterOperator::boxed(Temperature {
1✔
481
                    params: TemperatureParams::default(),
1✔
482
                    sources: SingleRasterSource {
1✔
483
                        raster: src.boxed(),
1✔
484
                    },
1✔
485
                })
1✔
486
            },
1✔
487
            test_util::create_mock_query(),
1✔
488
            &ctx,
1✔
489
        )
1✔
490
        .await;
1✔
491
        assert!(res.is_ok());
1✔
492
        let res = res.unwrap();
1✔
493
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
494
            &res.grid_array,
1✔
495
            &MaskedGrid2D::new(
1✔
496
                Grid2D::new(
1✔
497
                    [3, 2].into(),
1✔
498
                    vec![300.341_43, 318.617_65, 330.365_14, 339.233_64, 0., 0.],
1✔
499
                )
1✔
500
                .unwrap(),
1✔
501
                Grid2D::new([3, 2].into(), vec![true, true, true, true, false, false,],).unwrap(),
1✔
502
            )
1✔
503
            .unwrap()
1✔
504
            .into()
1✔
505
        ));
1✔
506
    }
1✔
507

508
    #[tokio::test]
509
    async fn test_invalid_force_satellite() {
1✔
510
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
511
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
512

1✔
513
        let res = test_util::process(
1✔
514
            || {
1✔
515
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
516
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
517

1✔
518
                RasterOperator::boxed(Temperature {
1✔
519
                    params: TemperatureParams {
1✔
520
                        force_satellite: Some(13),
1✔
521
                    },
1✔
522
                    sources: SingleRasterSource {
1✔
523
                        raster: src.boxed(),
1✔
524
                    },
1✔
525
                })
1✔
526
            },
1✔
527
            test_util::create_mock_query(),
1✔
528
            &ctx,
1✔
529
        )
1✔
530
        .await;
1✔
531
        assert!(res.is_err());
1✔
532
    }
1✔
533

534
    #[tokio::test]
535
    async fn test_missing_satellite() {
1✔
536
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
537
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
538

1✔
539
        let res = test_util::process(
1✔
540
            || {
1✔
541
                let props = test_util::create_properties(Some(4), None, Some(0.0), Some(1.0));
1✔
542
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
543

1✔
544
                RasterOperator::boxed(Temperature {
1✔
545
                    params: TemperatureParams::default(),
1✔
546
                    sources: SingleRasterSource {
1✔
547
                        raster: src.boxed(),
1✔
548
                    },
1✔
549
                })
1✔
550
            },
1✔
551
            test_util::create_mock_query(),
1✔
552
            &ctx,
1✔
553
        )
1✔
554
        .await;
1✔
555
        assert!(res.is_err());
1✔
556
    }
1✔
557

558
    #[tokio::test]
559
    async fn test_invalid_satellite() {
1✔
560
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
561
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
562

1✔
563
        let res = test_util::process(
1✔
564
            || {
1✔
565
                let props = test_util::create_properties(Some(4), Some(42), Some(0.0), Some(1.0));
1✔
566
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
567

1✔
568
                RasterOperator::boxed(Temperature {
1✔
569
                    params: TemperatureParams::default(),
1✔
570
                    sources: SingleRasterSource {
1✔
571
                        raster: src.boxed(),
1✔
572
                    },
1✔
573
                })
1✔
574
            },
1✔
575
            test_util::create_mock_query(),
1✔
576
            &ctx,
1✔
577
        )
1✔
578
        .await;
1✔
579
        assert!(res.is_err());
1✔
580
    }
1✔
581

582
    #[tokio::test]
583
    async fn test_missing_channel() {
1✔
584
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
585
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
586

1✔
587
        let res = test_util::process(
1✔
588
            || {
1✔
589
                let props = test_util::create_properties(None, Some(1), Some(0.0), Some(1.0));
1✔
590
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
591

1✔
592
                RasterOperator::boxed(Temperature {
1✔
593
                    params: TemperatureParams::default(),
1✔
594
                    sources: SingleRasterSource {
1✔
595
                        raster: src.boxed(),
1✔
596
                    },
1✔
597
                })
1✔
598
            },
1✔
599
            test_util::create_mock_query(),
1✔
600
            &ctx,
1✔
601
        )
1✔
602
        .await;
1✔
603
        assert!(res.is_err());
1✔
604
    }
1✔
605

606
    #[tokio::test]
607
    async fn test_invalid_channel() {
1✔
608
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
609
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
610

1✔
611
        let res = test_util::process(
1✔
612
            || {
1✔
613
                let props = test_util::create_properties(Some(1), Some(1), Some(0.0), Some(1.0));
1✔
614
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
615

1✔
616
                RasterOperator::boxed(Temperature {
1✔
617
                    params: TemperatureParams::default(),
1✔
618
                    sources: SingleRasterSource {
1✔
619
                        raster: src.boxed(),
1✔
620
                    },
1✔
621
                })
1✔
622
            },
1✔
623
            test_util::create_mock_query(),
1✔
624
            &ctx,
1✔
625
        )
1✔
626
        .await;
1✔
627
        assert!(res.is_err());
1✔
628
    }
1✔
629

630
    #[tokio::test]
631
    async fn test_missing_slope() {
1✔
632
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
633
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
634

1✔
635
        let res = test_util::process(
1✔
636
            || {
1✔
637
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), None);
1✔
638
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
639

1✔
640
                RasterOperator::boxed(Temperature {
1✔
641
                    params: TemperatureParams::default(),
1✔
642
                    sources: SingleRasterSource {
1✔
643
                        raster: src.boxed(),
1✔
644
                    },
1✔
645
                })
1✔
646
            },
1✔
647
            test_util::create_mock_query(),
1✔
648
            &ctx,
1✔
649
        )
1✔
650
        .await;
1✔
651
        assert!(res.is_err());
1✔
652
    }
1✔
653

654
    #[tokio::test]
655
    async fn test_missing_offset() {
1✔
656
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
657
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
658

1✔
659
        let res = test_util::process(
1✔
660
            || {
1✔
661
                let props = test_util::create_properties(Some(4), Some(1), None, Some(1.0));
1✔
662
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
663

1✔
664
                RasterOperator::boxed(Temperature {
1✔
665
                    params: TemperatureParams::default(),
1✔
666
                    sources: SingleRasterSource {
1✔
667
                        raster: src.boxed(),
1✔
668
                    },
1✔
669
                })
1✔
670
            },
1✔
671
            test_util::create_mock_query(),
1✔
672
            &ctx,
1✔
673
        )
1✔
674
        .await;
1✔
675
        assert!(res.is_err());
1✔
676
    }
1✔
677

678
    #[tokio::test]
679
    async fn test_invalid_measurement_unitless() {
1✔
680
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
681
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
682

1✔
683
        let res = test_util::process(
1✔
684
            || {
1✔
685
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
686
                let src =
1✔
687
                    test_util::create_mock_source::<u8>(props, None, Some(Measurement::Unitless));
1✔
688

1✔
689
                RasterOperator::boxed(Temperature {
1✔
690
                    params: TemperatureParams::default(),
1✔
691
                    sources: SingleRasterSource {
1✔
692
                        raster: src.boxed(),
1✔
693
                    },
1✔
694
                })
1✔
695
            },
1✔
696
            test_util::create_mock_query(),
1✔
697
            &ctx,
1✔
698
        )
1✔
699
        .await;
1✔
700
        assert!(res.is_err());
1✔
701
    }
1✔
702

703
    #[tokio::test]
704
    async fn test_invalid_measurement_continuous() {
1✔
705
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
706
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
707

1✔
708
        let res = test_util::process(
1✔
709
            || {
1✔
710
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
711
                let src = test_util::create_mock_source::<u8>(
1✔
712
                    props,
1✔
713
                    None,
1✔
714
                    Some(Measurement::Continuous(ContinuousMeasurement {
1✔
715
                        measurement: "invalid".into(),
1✔
716
                        unit: None,
1✔
717
                    })),
1✔
718
                );
1✔
719

1✔
720
                RasterOperator::boxed(Temperature {
1✔
721
                    params: TemperatureParams::default(),
1✔
722
                    sources: SingleRasterSource {
1✔
723
                        raster: src.boxed(),
1✔
724
                    },
1✔
725
                })
1✔
726
            },
1✔
727
            test_util::create_mock_query(),
1✔
728
            &ctx,
1✔
729
        )
1✔
730
        .await;
1✔
731

1✔
732
        assert!(res.is_err());
1✔
733
    }
1✔
734

735
    #[tokio::test]
736
    async fn test_invalid_measurement_classification() {
1✔
737
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
738

1✔
739
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
740

1✔
741
        let res = test_util::process(
1✔
742
            || {
1✔
743
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
744
                let src = test_util::create_mock_source::<u8>(
1✔
745
                    props,
1✔
746
                    None,
1✔
747
                    Some(Measurement::Classification(ClassificationMeasurement {
1✔
748
                        measurement: "invalid".into(),
1✔
749
                        classes: HashMap::new(),
1✔
750
                    })),
1✔
751
                );
1✔
752

1✔
753
                RasterOperator::boxed(Temperature {
1✔
754
                    params: TemperatureParams::default(),
1✔
755
                    sources: SingleRasterSource {
1✔
756
                        raster: src.boxed(),
1✔
757
                    },
1✔
758
                })
1✔
759
            },
1✔
760
            test_util::create_mock_query(),
1✔
761
            &ctx,
1✔
762
        )
1✔
763
        .await;
1✔
764
        assert!(res.is_err());
1✔
765
    }
1✔
766
}
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

© 2026 Coveralls, Inc