• 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

93.53
/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.
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
    path: WorkflowOperatorPath,
54
    result_descriptor: RasterResultDescriptor,
55
    source: Box<dyn InitializedRasterOperator>,
56
    params: TemperatureParams,
57
}
58

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

69
        let initialized_sources = self
14✔
70
            .sources
14✔
71
            .initialize_sources(path.clone(), context)
14✔
72
            .await?;
14✔
73
        let input = initialized_sources.raster;
14✔
74

14✔
75
        let in_desc = input.result_descriptor();
14✔
76

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

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

132
        let initialized_operator = InitializedTemperature {
11✔
133
            name,
11✔
134
            path,
11✔
135
            result_descriptor: out_desc,
11✔
136
            source: input,
11✔
137
            params: self.params,
11✔
138
        };
11✔
139

11✔
140
        Ok(initialized_operator.boxed())
11✔
141
    }
28✔
142

143
    span_fn!(Temperature);
144
}
145

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

151
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor, Error> {
11✔
152
        let q = self.source.query_processor()?;
11✔
153

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

188
    fn canonic_name(&self) -> CanonicOperatorName {
×
189
        self.name.clone()
×
190
    }
×
191

NEW
192
    fn name(&self) -> &'static str {
×
NEW
193
        Temperature::TYPE_NAME
×
NEW
194
    }
×
195

NEW
196
    fn path(&self) -> WorkflowOperatorPath {
×
NEW
197
        self.path.clone()
×
NEW
198
    }
×
199
}
200

201
struct TemperatureProcessor<Q, P>
202
where
203
    Q: RasterQueryProcessor<RasterType = P>,
204
{
205
    source: Q,
206
    result_descriptor: RasterResultDescriptor,
207
    params: TemperatureParams,
208
    satellite_key: RasterPropertiesKey,
209
    channel_key: RasterPropertiesKey,
210
    offset_key: RasterPropertiesKey,
211
    slope_key: RasterPropertiesKey,
212
}
213

214
impl<Q, P> TemperatureProcessor<Q, P>
215
where
216
    Q: RasterQueryProcessor<RasterType = P>,
217
    P: Pixel,
218
{
219
    pub fn new(
11✔
220
        source: Q,
11✔
221
        result_descriptor: RasterResultDescriptor,
11✔
222
        params: TemperatureParams,
11✔
223
    ) -> Self {
11✔
224
        Self {
11✔
225
            source,
11✔
226
            result_descriptor,
11✔
227
            params,
11✔
228
            satellite_key: new_satellite_key(),
11✔
229
            channel_key: new_channel_key(),
11✔
230
            offset_key: new_offset_key(),
11✔
231
            slope_key: new_slope_key(),
11✔
232
        }
11✔
233
    }
11✔
234

235
    fn satellite(&self, tile: &RasterTile2D<P>) -> Result<&'static Satellite> {
11✔
236
        let id = match self.params.force_satellite {
11✔
237
            Some(id) => id,
2✔
238
            _ => tile.properties.number_property(&self.satellite_key)?,
9✔
239
        };
240
        Satellite::satellite_by_msg_id(id)
10✔
241
    }
11✔
242

243
    fn channel<'a>(&self, tile: &RasterTile2D<P>, satellite: &'a Satellite) -> Result<&'a Channel> {
8✔
244
        let channel_id = tile
8✔
245
            .properties
8✔
246
            .number_property::<usize>(&self.channel_key)?
8✔
247
            - 1;
248
        if (3..=10).contains(&channel_id) {
7✔
249
            satellite.channel(channel_id)
6✔
250
        } else {
251
            Err(Error::InvalidChannel {
1✔
252
                channel: channel_id,
1✔
253
            })
1✔
254
        }
255
    }
8✔
256

257
    async fn process_tile_async(
11✔
258
        &self,
11✔
259
        tile: RasterTile2D<P>,
11✔
260
        pool: Arc<ThreadPool>,
11✔
261
    ) -> Result<RasterTile2D<PixelOut>> {
11✔
262
        let satellite = self.satellite(&tile)?;
11✔
263
        let channel = self.channel(&tile, satellite)?;
8✔
264
        let offset = tile.properties.number_property::<f64>(&self.offset_key)?;
6✔
265
        let slope = tile.properties.number_property::<f64>(&self.slope_key)?;
5✔
266

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

4✔
270
            let map_fn = move |pixel_option: Option<P>| {
19✔
271
                pixel_option.and_then(|p| {
19✔
272
                    let lut_idx: u64 = p.as_();
15✔
273
                    lut.get(lut_idx as usize).copied()
15✔
274
                })
19✔
275
            };
19✔
276

277
            tile.map_elements_parallel(map_fn)
4✔
278
        })
4✔
279
        .await?;
4✔
280

281
        Ok(temp_tile)
4✔
282
    }
11✔
283
}
284

285
fn create_lookup_table(channel: &Channel, offset: f64, slope: f64, _pool: &ThreadPool) -> Vec<f32> {
4✔
286
    // this should propably be done with SIMD not a threadpool
4✔
287
    (0..1024)
4✔
288
        .map(|i| {
4,096✔
289
            let radiance = offset + f64::from(i) * slope;
4,096✔
290
            channel.calculate_temperature_from_radiance(radiance) as f32
4,096✔
291
        })
4,096✔
292
        .collect::<Vec<f32>>()
4✔
293
}
4✔
294

295
#[async_trait]
296
impl<Q, P> QueryProcessor for TemperatureProcessor<Q, P>
297
where
298
    Q: QueryProcessor<
299
        Output = RasterTile2D<P>,
300
        SpatialBounds = SpatialPartition2D,
301
        Selection = BandSelection,
302
        ResultDescription = RasterResultDescriptor,
303
    >,
304
    P: Pixel,
305
{
306
    type Output = RasterTile2D<PixelOut>;
307
    type SpatialBounds = SpatialPartition2D;
308
    type Selection = BandSelection;
309
    type ResultDescription = RasterResultDescriptor;
310

311
    async fn _query<'a>(
312
        &'a self,
313
        query: RasterQueryRectangle,
314
        ctx: &'a dyn QueryContext,
315
    ) -> Result<BoxStream<'a, Result<Self::Output>>> {
11✔
316
        let src = self.source.query(query, ctx).await?;
11✔
317
        let rs = src.and_then(move |tile| self.process_tile_async(tile, ctx.thread_pool().clone()));
11✔
318
        Ok(rs.boxed())
11✔
319
    }
22✔
320

321
    fn result_descriptor(&self) -> &Self::ResultDescription {
22✔
322
        &self.result_descriptor
22✔
323
    }
22✔
324
}
325

326
#[cfg(test)]
327
mod tests {
328
    use crate::engine::{MockExecutionContext, RasterOperator, SingleRasterSource};
329
    use crate::processing::meteosat::temperature::{Temperature, TemperatureParams};
330
    use crate::processing::meteosat::test_util;
331
    use geoengine_datatypes::primitives::{
332
        ClassificationMeasurement, ContinuousMeasurement, Measurement,
333
    };
334
    use geoengine_datatypes::raster::{EmptyGrid2D, Grid2D, MaskedGrid2D, TilingSpecification};
335
    use std::collections::HashMap;
336

337
    // #[tokio::test]
338
    // async fn test_msg_raster() {
339
    //     let mut ctx = MockExecutionContext::test_default();
340
    //     let src = test_util::_create_gdal_src(&mut ctx);
341
    //
342
    //     let result = test_util::process(
343
    //         move || {
344
    //             RasterOperator::boxed(Temperature {
345
    //                 params: TemperatureParams::default(),
346
    //                 sources: SingleRasterSource {
347
    //                     raster: src.boxed(),
348
    //                 },
349
    //             })
350
    //         },
351
    //         test_util::_create_gdal_query(),
352
    //         &ctx,
353
    //     )
354
    //     .await;
355
    //     assert!(result.as_ref().is_ok());
356
    // }
357

358
    #[tokio::test]
359
    async fn test_empty_ok() {
1✔
360
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
361
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
362

1✔
363
        let res = test_util::process(
1✔
364
            || {
1✔
365
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
366
                let src = test_util::create_mock_source::<u8>(
1✔
367
                    props,
1✔
368
                    Some(EmptyGrid2D::new([3, 2].into()).into()),
1✔
369
                    None,
1✔
370
                );
1✔
371

1✔
372
                RasterOperator::boxed(Temperature {
1✔
373
                    params: TemperatureParams::default(),
1✔
374
                    sources: SingleRasterSource {
1✔
375
                        raster: src.boxed(),
1✔
376
                    },
1✔
377
                })
1✔
378
            },
1✔
379
            test_util::create_mock_query(),
1✔
380
            &ctx,
1✔
381
        )
1✔
382
        .await
1✔
383
        .unwrap();
1✔
384

1✔
385
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
386
            &res.grid_array,
1✔
387
            &EmptyGrid2D::new([3, 2].into()).into()
1✔
388
        ));
1✔
389
    }
1✔
390

391
    #[tokio::test]
392
    async fn test_ok() {
1✔
393
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
394
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
395

1✔
396
        let res = test_util::process(
1✔
397
            || {
1✔
398
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
399
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
400

1✔
401
                RasterOperator::boxed(Temperature {
1✔
402
                    params: TemperatureParams::default(),
1✔
403
                    sources: SingleRasterSource {
1✔
404
                        raster: src.boxed(),
1✔
405
                    },
1✔
406
                })
1✔
407
            },
1✔
408
            test_util::create_mock_query(),
1✔
409
            &ctx,
1✔
410
        )
1✔
411
        .await
1✔
412
        .unwrap();
1✔
413

1✔
414
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
415
            &res.grid_array,
1✔
416
            &MaskedGrid2D::new(
1✔
417
                Grid2D::new(
1✔
418
                    [3, 2].into(),
1✔
419
                    vec![300.341_43, 318.617_65, 330.365_14, 339.233_64, 346.443_94, 0.,],
1✔
420
                )
1✔
421
                .unwrap(),
1✔
422
                Grid2D::new([3, 2].into(), vec![true, true, true, true, true, false,],).unwrap(),
1✔
423
            )
1✔
424
            .unwrap()
1✔
425
            .into()
1✔
426
        ));
1✔
427

1✔
428
        // TODO: add assert to check mask
1✔
429
    }
1✔
430

431
    #[tokio::test]
432
    async fn test_ok_force_satellite() {
1✔
433
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
434
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
435

1✔
436
        let res = test_util::process(
1✔
437
            || {
1✔
438
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
439
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
440

1✔
441
                RasterOperator::boxed(Temperature {
1✔
442
                    params: TemperatureParams {
1✔
443
                        force_satellite: Some(4),
1✔
444
                    },
1✔
445
                    sources: SingleRasterSource {
1✔
446
                        raster: src.boxed(),
1✔
447
                    },
1✔
448
                })
1✔
449
            },
1✔
450
            test_util::create_mock_query(),
1✔
451
            &ctx,
1✔
452
        )
1✔
453
        .await
1✔
454
        .unwrap();
1✔
455

1✔
456
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
457
            &res.grid_array,
1✔
458
            &MaskedGrid2D::new(
1✔
459
                Grid2D::new(
1✔
460
                    [3, 2].into(),
1✔
461
                    vec![300.9428, 319.250_15, 331.019_04, 339.9044, 347.128_78, 0.],
1✔
462
                )
1✔
463
                .unwrap(),
1✔
464
                Grid2D::new([3, 2].into(), vec![true, true, true, true, true, false,],).unwrap(),
1✔
465
            )
1✔
466
            .unwrap()
1✔
467
            .into()
1✔
468
        ));
1✔
469
    }
1✔
470

471
    #[tokio::test]
472
    async fn test_ok_illegal_input_to_masked() {
1✔
473
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
474
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
475

1✔
476
        let res = test_util::process(
1✔
477
            || {
1✔
478
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
479
                let src = test_util::create_mock_source::<u16>(
1✔
480
                    props,
1✔
481
                    Some(
1✔
482
                        MaskedGrid2D::new(
1✔
483
                            Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 1024, 0]).unwrap(),
1✔
484
                            Grid2D::new([3, 2].into(), vec![true, true, true, true, true, false])
1✔
485
                                .unwrap(),
1✔
486
                        )
1✔
487
                        .unwrap()
1✔
488
                        .into(),
1✔
489
                    ),
1✔
490
                    None,
1✔
491
                );
1✔
492

1✔
493
                RasterOperator::boxed(Temperature {
1✔
494
                    params: TemperatureParams::default(),
1✔
495
                    sources: SingleRasterSource {
1✔
496
                        raster: src.boxed(),
1✔
497
                    },
1✔
498
                })
1✔
499
            },
1✔
500
            test_util::create_mock_query(),
1✔
501
            &ctx,
1✔
502
        )
1✔
503
        .await;
1✔
504
        assert!(res.is_ok());
1✔
505
        let res = res.unwrap();
1✔
506
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
507
            &res.grid_array,
1✔
508
            &MaskedGrid2D::new(
1✔
509
                Grid2D::new(
1✔
510
                    [3, 2].into(),
1✔
511
                    vec![300.341_43, 318.617_65, 330.365_14, 339.233_64, 0., 0.],
1✔
512
                )
1✔
513
                .unwrap(),
1✔
514
                Grid2D::new([3, 2].into(), vec![true, true, true, true, false, false,],).unwrap(),
1✔
515
            )
1✔
516
            .unwrap()
1✔
517
            .into()
1✔
518
        ));
1✔
519
    }
1✔
520

521
    #[tokio::test]
522
    async fn test_invalid_force_satellite() {
1✔
523
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
524
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
525

1✔
526
        let res = test_util::process(
1✔
527
            || {
1✔
528
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
529
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
530

1✔
531
                RasterOperator::boxed(Temperature {
1✔
532
                    params: TemperatureParams {
1✔
533
                        force_satellite: Some(13),
1✔
534
                    },
1✔
535
                    sources: SingleRasterSource {
1✔
536
                        raster: src.boxed(),
1✔
537
                    },
1✔
538
                })
1✔
539
            },
1✔
540
            test_util::create_mock_query(),
1✔
541
            &ctx,
1✔
542
        )
1✔
543
        .await;
1✔
544
        assert!(res.is_err());
1✔
545
    }
1✔
546

547
    #[tokio::test]
548
    async fn test_missing_satellite() {
1✔
549
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
550
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
551

1✔
552
        let res = test_util::process(
1✔
553
            || {
1✔
554
                let props = test_util::create_properties(Some(4), None, Some(0.0), Some(1.0));
1✔
555
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
556

1✔
557
                RasterOperator::boxed(Temperature {
1✔
558
                    params: TemperatureParams::default(),
1✔
559
                    sources: SingleRasterSource {
1✔
560
                        raster: src.boxed(),
1✔
561
                    },
1✔
562
                })
1✔
563
            },
1✔
564
            test_util::create_mock_query(),
1✔
565
            &ctx,
1✔
566
        )
1✔
567
        .await;
1✔
568
        assert!(res.is_err());
1✔
569
    }
1✔
570

571
    #[tokio::test]
572
    async fn test_invalid_satellite() {
1✔
573
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
574
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
575

1✔
576
        let res = test_util::process(
1✔
577
            || {
1✔
578
                let props = test_util::create_properties(Some(4), Some(42), Some(0.0), Some(1.0));
1✔
579
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
580

1✔
581
                RasterOperator::boxed(Temperature {
1✔
582
                    params: TemperatureParams::default(),
1✔
583
                    sources: SingleRasterSource {
1✔
584
                        raster: src.boxed(),
1✔
585
                    },
1✔
586
                })
1✔
587
            },
1✔
588
            test_util::create_mock_query(),
1✔
589
            &ctx,
1✔
590
        )
1✔
591
        .await;
1✔
592
        assert!(res.is_err());
1✔
593
    }
1✔
594

595
    #[tokio::test]
596
    async fn test_missing_channel() {
1✔
597
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
598
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
599

1✔
600
        let res = test_util::process(
1✔
601
            || {
1✔
602
                let props = test_util::create_properties(None, Some(1), Some(0.0), Some(1.0));
1✔
603
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
604

1✔
605
                RasterOperator::boxed(Temperature {
1✔
606
                    params: TemperatureParams::default(),
1✔
607
                    sources: SingleRasterSource {
1✔
608
                        raster: src.boxed(),
1✔
609
                    },
1✔
610
                })
1✔
611
            },
1✔
612
            test_util::create_mock_query(),
1✔
613
            &ctx,
1✔
614
        )
1✔
615
        .await;
1✔
616
        assert!(res.is_err());
1✔
617
    }
1✔
618

619
    #[tokio::test]
620
    async fn test_invalid_channel() {
1✔
621
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
622
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
623

1✔
624
        let res = test_util::process(
1✔
625
            || {
1✔
626
                let props = test_util::create_properties(Some(1), Some(1), Some(0.0), Some(1.0));
1✔
627
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
628

1✔
629
                RasterOperator::boxed(Temperature {
1✔
630
                    params: TemperatureParams::default(),
1✔
631
                    sources: SingleRasterSource {
1✔
632
                        raster: src.boxed(),
1✔
633
                    },
1✔
634
                })
1✔
635
            },
1✔
636
            test_util::create_mock_query(),
1✔
637
            &ctx,
1✔
638
        )
1✔
639
        .await;
1✔
640
        assert!(res.is_err());
1✔
641
    }
1✔
642

643
    #[tokio::test]
644
    async fn test_missing_slope() {
1✔
645
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
646
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
647

1✔
648
        let res = test_util::process(
1✔
649
            || {
1✔
650
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), None);
1✔
651
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
652

1✔
653
                RasterOperator::boxed(Temperature {
1✔
654
                    params: TemperatureParams::default(),
1✔
655
                    sources: SingleRasterSource {
1✔
656
                        raster: src.boxed(),
1✔
657
                    },
1✔
658
                })
1✔
659
            },
1✔
660
            test_util::create_mock_query(),
1✔
661
            &ctx,
1✔
662
        )
1✔
663
        .await;
1✔
664
        assert!(res.is_err());
1✔
665
    }
1✔
666

667
    #[tokio::test]
668
    async fn test_missing_offset() {
1✔
669
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
670
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
671

1✔
672
        let res = test_util::process(
1✔
673
            || {
1✔
674
                let props = test_util::create_properties(Some(4), Some(1), None, Some(1.0));
1✔
675
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
676

1✔
677
                RasterOperator::boxed(Temperature {
1✔
678
                    params: TemperatureParams::default(),
1✔
679
                    sources: SingleRasterSource {
1✔
680
                        raster: src.boxed(),
1✔
681
                    },
1✔
682
                })
1✔
683
            },
1✔
684
            test_util::create_mock_query(),
1✔
685
            &ctx,
1✔
686
        )
1✔
687
        .await;
1✔
688
        assert!(res.is_err());
1✔
689
    }
1✔
690

691
    #[tokio::test]
692
    async fn test_invalid_measurement_unitless() {
1✔
693
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
694
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
695

1✔
696
        let res = test_util::process(
1✔
697
            || {
1✔
698
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
699
                let src =
1✔
700
                    test_util::create_mock_source::<u8>(props, None, Some(Measurement::Unitless));
1✔
701

1✔
702
                RasterOperator::boxed(Temperature {
1✔
703
                    params: TemperatureParams::default(),
1✔
704
                    sources: SingleRasterSource {
1✔
705
                        raster: src.boxed(),
1✔
706
                    },
1✔
707
                })
1✔
708
            },
1✔
709
            test_util::create_mock_query(),
1✔
710
            &ctx,
1✔
711
        )
1✔
712
        .await;
1✔
713
        assert!(res.is_err());
1✔
714
    }
1✔
715

716
    #[tokio::test]
717
    async fn test_invalid_measurement_continuous() {
1✔
718
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
719
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
720

1✔
721
        let res = test_util::process(
1✔
722
            || {
1✔
723
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
724
                let src = test_util::create_mock_source::<u8>(
1✔
725
                    props,
1✔
726
                    None,
1✔
727
                    Some(Measurement::Continuous(ContinuousMeasurement {
1✔
728
                        measurement: "invalid".into(),
1✔
729
                        unit: None,
1✔
730
                    })),
1✔
731
                );
1✔
732

1✔
733
                RasterOperator::boxed(Temperature {
1✔
734
                    params: TemperatureParams::default(),
1✔
735
                    sources: SingleRasterSource {
1✔
736
                        raster: src.boxed(),
1✔
737
                    },
1✔
738
                })
1✔
739
            },
1✔
740
            test_util::create_mock_query(),
1✔
741
            &ctx,
1✔
742
        )
1✔
743
        .await;
1✔
744

1✔
745
        assert!(res.is_err());
1✔
746
    }
1✔
747

748
    #[tokio::test]
749
    async fn test_invalid_measurement_classification() {
1✔
750
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
751

1✔
752
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
753

1✔
754
        let res = test_util::process(
1✔
755
            || {
1✔
756
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
757
                let src = test_util::create_mock_source::<u8>(
1✔
758
                    props,
1✔
759
                    None,
1✔
760
                    Some(Measurement::Classification(ClassificationMeasurement {
1✔
761
                        measurement: "invalid".into(),
1✔
762
                        classes: HashMap::new(),
1✔
763
                    })),
1✔
764
                );
1✔
765

1✔
766
                RasterOperator::boxed(Temperature {
1✔
767
                    params: TemperatureParams::default(),
1✔
768
                    sources: SingleRasterSource {
1✔
769
                        raster: src.boxed(),
1✔
770
                    },
1✔
771
                })
1✔
772
            },
1✔
773
            test_util::create_mock_query(),
1✔
774
            &ctx,
1✔
775
        )
1✔
776
        .await;
1✔
777
        assert!(res.is_err());
1✔
778
    }
1✔
779
}
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