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

geo-engine / geoengine / 16167706152

09 Jul 2025 11:08AM UTC coverage: 88.738% (-1.0%) from 89.762%
16167706152

push

github

web-flow
refactor: Updates-2025-07-02 (#1062)

* rust 1.88

* clippy auto-fixes

* manual clippy fixes

* update deps

* cargo update

* update onnx

* cargo fmt

* update sqlfluff

121 of 142 new or added lines in 29 files covered. (85.21%)

300 existing lines in 88 files now uncovered.

111259 of 125379 relevant lines covered (88.74%)

77910.92 hits per line

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

92.59
/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>> {
28✔
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

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 {
80
                    measurement: m,
12✔
81
                    unit: _,
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: _,
91
                }) => {
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✔
UNCOV
129
            )?,
×
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

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

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

196
    fn path(&self) -> WorkflowOperatorPath {
×
197
        self.path.clone()
×
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

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
                })
15✔
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
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>>> {
22✔
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

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
                );
371

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
        )
382
        .await
1✔
383
        .unwrap();
1✔
384

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

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

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
        )
411
        .await
1✔
412
        .unwrap();
1✔
413

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![
1✔
420
                        300.341_43, 318.617_65, 330.365_14, 339.233_64, 346.443_94, 0.,
1✔
421
                    ],
1✔
422
                )
1✔
423
                .unwrap(),
1✔
424
                Grid2D::new([3, 2].into(), vec![true, true, true, true, true, false,],).unwrap(),
1✔
425
            )
1✔
426
            .unwrap()
1✔
427
            .into()
1✔
428
        ));
1✔
429

430
        // TODO: add assert to check mask
431
    }
1✔
432

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

747
        assert!(res.is_err());
1✔
748
    }
1✔
749

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

754
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
755

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

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