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

geo-engine / geoengine / 3929938005

pending completion
3929938005

push

github

GitHub
Merge #713

84930 of 96741 relevant lines covered (87.79%)

79640.1 hits per line

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

94.08
/operators/src/processing/meteosat/temperature.rs
1
use std::sync::Arc;
2

3
use crate::engine::{
4
    CreateSpan, ExecutionContext, InitializedRasterOperator, Operator, OperatorName, QueryContext,
5
    QueryProcessor, RasterOperator, RasterQueryProcessor, RasterResultDescriptor,
6
    SingleRasterSource, TypedRasterQueryProcessor,
7
};
8
use crate::util::Result;
9
use async_trait::async_trait;
10
use rayon::ThreadPool;
11
use tracing::{span, Level};
12
use TypedRasterQueryProcessor::F32 as QueryProcessorOut;
13

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

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

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

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

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

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

56
#[typetag::serde]
×
57
#[async_trait]
58
impl RasterOperator for Temperature {
59
    async fn _initialize(
14✔
60
        self: Box<Self>,
14✔
61
        context: &dyn ExecutionContext,
14✔
62
    ) -> Result<Box<dyn InitializedRasterOperator>> {
14✔
63
        let input = self.sources.raster.initialize(context).await?;
14✔
64

65
        let in_desc = input.result_descriptor();
14✔
66

67
        match &in_desc.measurement {
12✔
68
            Measurement::Continuous(ContinuousMeasurement {
12✔
69
                measurement: m,
12✔
70
                unit: _,
12✔
71
            }) if m != "raw" => {
12✔
72
                return Err(Error::InvalidMeasurement {
1✔
73
                    expected: "raw".into(),
1✔
74
                    found: m.clone(),
1✔
75
                })
1✔
76
            }
77
            Measurement::Classification(ClassificationMeasurement {
78
                measurement: m,
1✔
79
                classes: _,
1✔
80
            }) => {
1✔
81
                return Err(Error::InvalidMeasurement {
1✔
82
                    expected: "raw".into(),
1✔
83
                    found: m.clone(),
1✔
84
                })
1✔
85
            }
86
            Measurement::Unitless => {
87
                return Err(Error::InvalidMeasurement {
1✔
88
                    expected: "raw".into(),
1✔
89
                    found: "unitless".into(),
1✔
90
                })
1✔
91
            }
92
            // OK Case
93
            Measurement::Continuous(ContinuousMeasurement {
94
                measurement: _,
95
                unit: _,
96
            }) => {}
11✔
97
        }
11✔
98

11✔
99
        let out_desc = RasterResultDescriptor {
11✔
100
            spatial_reference: in_desc.spatial_reference,
11✔
101
            data_type: RasterOut,
11✔
102
            measurement: Measurement::Continuous(ContinuousMeasurement {
11✔
103
                measurement: "temperature".into(),
11✔
104
                unit: Some("k".into()),
11✔
105
            }),
11✔
106
            time: in_desc.time,
11✔
107
            bbox: in_desc.bbox,
11✔
108
            resolution: in_desc.resolution,
11✔
109
        };
11✔
110

11✔
111
        let initialized_operator = InitializedTemperature {
11✔
112
            result_descriptor: out_desc,
11✔
113
            source: input,
11✔
114
            params: self.params,
11✔
115
        };
11✔
116

11✔
117
        Ok(initialized_operator.boxed())
11✔
118
    }
28✔
119

120
    span_fn!(Temperature);
×
121
}
122

123
impl InitializedRasterOperator for InitializedTemperature {
124
    fn result_descriptor(&self) -> &RasterResultDescriptor {
×
125
        &self.result_descriptor
×
126
    }
×
127

128
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor, Error> {
11✔
129
        let q = self.source.query_processor()?;
11✔
130

131
        Ok(match q {
11✔
132
            TypedRasterQueryProcessor::U8(p) => {
10✔
133
                QueryProcessorOut(Box::new(TemperatureProcessor::new(p, self.params.clone())))
10✔
134
            }
135
            TypedRasterQueryProcessor::U16(p) => {
1✔
136
                QueryProcessorOut(Box::new(TemperatureProcessor::new(p, self.params.clone())))
1✔
137
            }
138
            TypedRasterQueryProcessor::U32(p) => {
×
139
                QueryProcessorOut(Box::new(TemperatureProcessor::new(p, self.params.clone())))
×
140
            }
141
            TypedRasterQueryProcessor::U64(p) => {
×
142
                QueryProcessorOut(Box::new(TemperatureProcessor::new(p, self.params.clone())))
×
143
            }
144
            TypedRasterQueryProcessor::I8(p) => {
×
145
                QueryProcessorOut(Box::new(TemperatureProcessor::new(p, self.params.clone())))
×
146
            }
147
            TypedRasterQueryProcessor::I16(p) => {
×
148
                QueryProcessorOut(Box::new(TemperatureProcessor::new(p, self.params.clone())))
×
149
            }
150
            TypedRasterQueryProcessor::I32(p) => {
×
151
                QueryProcessorOut(Box::new(TemperatureProcessor::new(p, self.params.clone())))
×
152
            }
153
            TypedRasterQueryProcessor::I64(p) => {
×
154
                QueryProcessorOut(Box::new(TemperatureProcessor::new(p, self.params.clone())))
×
155
            }
156
            TypedRasterQueryProcessor::F32(p) => {
×
157
                QueryProcessorOut(Box::new(TemperatureProcessor::new(p, self.params.clone())))
×
158
            }
159
            TypedRasterQueryProcessor::F64(p) => {
×
160
                QueryProcessorOut(Box::new(TemperatureProcessor::new(p, self.params.clone())))
×
161
            }
162
        })
163
    }
11✔
164
}
165

166
struct TemperatureProcessor<Q, P>
167
where
168
    Q: RasterQueryProcessor<RasterType = P>,
169
{
170
    source: Q,
171
    params: TemperatureParams,
172
    satellite_key: RasterPropertiesKey,
173
    channel_key: RasterPropertiesKey,
174
    offset_key: RasterPropertiesKey,
175
    slope_key: RasterPropertiesKey,
176
}
177

178
impl<Q, P> TemperatureProcessor<Q, P>
179
where
180
    Q: RasterQueryProcessor<RasterType = P>,
181
    P: Pixel,
182
{
183
    pub fn new(source: Q, params: TemperatureParams) -> Self {
11✔
184
        Self {
11✔
185
            source,
11✔
186
            params,
11✔
187
            satellite_key: new_satellite_key(),
11✔
188
            channel_key: new_channel_key(),
11✔
189
            offset_key: new_offset_key(),
11✔
190
            slope_key: new_slope_key(),
11✔
191
        }
11✔
192
    }
11✔
193

194
    fn satellite(&self, tile: &RasterTile2D<P>) -> Result<&'static Satellite> {
11✔
195
        let id = match self.params.force_satellite {
11✔
196
            Some(id) => id,
2✔
197
            _ => tile.properties.number_property(&self.satellite_key)?,
9✔
198
        };
199
        Satellite::satellite_by_msg_id(id)
10✔
200
    }
11✔
201

202
    fn channel<'a>(&self, tile: &RasterTile2D<P>, satellite: &'a Satellite) -> Result<&'a Channel> {
8✔
203
        let channel_id = tile
8✔
204
            .properties
8✔
205
            .number_property::<usize>(&self.channel_key)?
8✔
206
            - 1;
207
        if (3..=10).contains(&channel_id) {
7✔
208
            satellite.channel(channel_id)
6✔
209
        } else {
210
            Err(Error::InvalidChannel {
1✔
211
                channel: channel_id,
1✔
212
            })
1✔
213
        }
214
    }
8✔
215

216
    async fn process_tile_async(
11✔
217
        &self,
11✔
218
        tile: RasterTile2D<P>,
11✔
219
        pool: Arc<ThreadPool>,
11✔
220
    ) -> Result<RasterTile2D<PixelOut>> {
11✔
221
        let satellite = self.satellite(&tile)?;
11✔
222
        let channel = self.channel(&tile, satellite)?;
8✔
223
        let offset = tile.properties.number_property::<f64>(&self.offset_key)?;
6✔
224
        let slope = tile.properties.number_property::<f64>(&self.slope_key)?;
5✔
225

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

4✔
229
            let map_fn = move |pixel_option: Option<P>| {
19✔
230
                pixel_option.and_then(|p| {
19✔
231
                    let lut_idx: u64 = p.as_();
15✔
232
                    lut.get(lut_idx as usize).copied()
15✔
233
                })
19✔
234
            };
19✔
235

236
            tile.map_elements_parallel(map_fn)
4✔
237
        })
4✔
238
        .await?;
4✔
239

240
        Ok(temp_tile)
4✔
241
    }
11✔
242
}
243

244
fn create_lookup_table(channel: &Channel, offset: f64, slope: f64, _pool: &ThreadPool) -> Vec<f32> {
4✔
245
    // this should propably be done with SIMD not a threadpool
4✔
246
    (0..1024)
4✔
247
        .into_iter()
4✔
248
        .map(|i| {
4,096✔
249
            let radiance = offset + f64::from(i) * slope;
4,096✔
250
            channel.calculate_temperature_from_radiance(radiance) as f32
4,096✔
251
        })
4,096✔
252
        .collect::<Vec<f32>>()
4✔
253
}
4✔
254

255
#[async_trait]
256
impl<Q, P> QueryProcessor for TemperatureProcessor<Q, P>
257
where
258
    Q: QueryProcessor<Output = RasterTile2D<P>, SpatialBounds = SpatialPartition2D>,
259
    P: Pixel,
260
{
261
    type Output = RasterTile2D<PixelOut>;
262
    type SpatialBounds = SpatialPartition2D;
263

264
    async fn _query<'a>(
11✔
265
        &'a self,
11✔
266
        query: RasterQueryRectangle,
11✔
267
        ctx: &'a dyn QueryContext,
11✔
268
    ) -> Result<BoxStream<'a, Result<Self::Output>>> {
11✔
269
        let src = self.source.query(query, ctx).await?;
11✔
270
        let rs = src.and_then(move |tile| self.process_tile_async(tile, ctx.thread_pool().clone()));
11✔
271
        Ok(rs.boxed())
11✔
272
    }
22✔
273
}
274

275
#[cfg(test)]
276
mod tests {
277
    use crate::engine::{MockExecutionContext, RasterOperator, SingleRasterSource};
278
    use crate::processing::meteosat::temperature::{Temperature, TemperatureParams};
279
    use crate::processing::meteosat::test_util;
280
    use geoengine_datatypes::primitives::{
281
        ClassificationMeasurement, ContinuousMeasurement, Measurement,
282
    };
283
    use geoengine_datatypes::raster::{EmptyGrid2D, Grid2D, MaskedGrid2D, TilingSpecification};
284
    use std::collections::HashMap;
285

286
    // #[tokio::test]
287
    // async fn test_msg_raster() {
288
    //     let mut ctx = MockExecutionContext::test_default();
289
    //     let src = test_util::_create_gdal_src(&mut ctx);
290
    //
291
    //     let result = test_util::process(
292
    //         move || {
293
    //             RasterOperator::boxed(Temperature {
294
    //                 params: TemperatureParams::default(),
295
    //                 sources: SingleRasterSource {
296
    //                     raster: src.boxed(),
297
    //                 },
298
    //             })
299
    //         },
300
    //         test_util::_create_gdal_query(),
301
    //         &ctx,
302
    //     )
303
    //     .await;
304
    //     assert!(result.as_ref().is_ok());
305
    // }
306

307
    #[tokio::test]
1✔
308
    async fn test_empty_ok() {
1✔
309
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
310
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
311

312
        let res = test_util::process(
1✔
313
            || {
1✔
314
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
315
                let src = test_util::create_mock_source::<u8>(
1✔
316
                    props,
1✔
317
                    Some(EmptyGrid2D::new([3, 2].into()).into()),
1✔
318
                    None,
1✔
319
                );
1✔
320

1✔
321
                RasterOperator::boxed(Temperature {
1✔
322
                    params: TemperatureParams::default(),
1✔
323
                    sources: SingleRasterSource {
1✔
324
                        raster: src.boxed(),
1✔
325
                    },
1✔
326
                })
1✔
327
            },
1✔
328
            test_util::create_mock_query(),
1✔
329
            &ctx,
1✔
330
        )
1✔
331
        .await
1✔
332
        .unwrap();
1✔
333

1✔
334
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
335
            &res.grid_array,
1✔
336
            &EmptyGrid2D::new([3, 2].into()).into()
1✔
337
        ));
1✔
338
    }
339

340
    #[tokio::test]
1✔
341
    async fn test_ok() {
1✔
342
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
343
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
344

345
        let res = test_util::process(
1✔
346
            || {
1✔
347
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
348
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
349

1✔
350
                RasterOperator::boxed(Temperature {
1✔
351
                    params: TemperatureParams::default(),
1✔
352
                    sources: SingleRasterSource {
1✔
353
                        raster: src.boxed(),
1✔
354
                    },
1✔
355
                })
1✔
356
            },
1✔
357
            test_util::create_mock_query(),
1✔
358
            &ctx,
1✔
359
        )
1✔
360
        .await
1✔
361
        .unwrap();
1✔
362

1✔
363
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
364
            &res.grid_array,
1✔
365
            &MaskedGrid2D::new(
1✔
366
                Grid2D::new(
1✔
367
                    [3, 2].into(),
1✔
368
                    vec![300.341_43, 318.617_65, 330.365_14, 339.233_64, 346.443_94, 0.,],
1✔
369
                )
1✔
370
                .unwrap(),
1✔
371
                Grid2D::new([3, 2].into(), vec![true, true, true, true, true, false,],).unwrap(),
1✔
372
            )
1✔
373
            .unwrap()
1✔
374
            .into()
1✔
375
        ));
1✔
376

377
        // TODO: add assert to check mask
378
    }
379

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

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

1✔
390
                RasterOperator::boxed(Temperature {
1✔
391
                    params: TemperatureParams {
1✔
392
                        force_satellite: Some(4),
1✔
393
                    },
1✔
394
                    sources: SingleRasterSource {
1✔
395
                        raster: src.boxed(),
1✔
396
                    },
1✔
397
                })
1✔
398
            },
1✔
399
            test_util::create_mock_query(),
1✔
400
            &ctx,
1✔
401
        )
1✔
402
        .await
1✔
403
        .unwrap();
1✔
404

1✔
405
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
406
            &res.grid_array,
1✔
407
            &MaskedGrid2D::new(
1✔
408
                Grid2D::new(
1✔
409
                    [3, 2].into(),
1✔
410
                    vec![300.9428, 319.250_15, 331.019_04, 339.9044, 347.128_78, 0.],
1✔
411
                )
1✔
412
                .unwrap(),
1✔
413
                Grid2D::new([3, 2].into(), vec![true, true, true, true, true, false,],).unwrap(),
1✔
414
            )
1✔
415
            .unwrap()
1✔
416
            .into()
1✔
417
        ));
1✔
418
    }
419

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

425
        let res = test_util::process(
1✔
426
            || {
1✔
427
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
428
                let src = test_util::create_mock_source::<u16>(
1✔
429
                    props,
1✔
430
                    Some(
1✔
431
                        MaskedGrid2D::new(
1✔
432
                            Grid2D::new([3, 2].into(), vec![1, 2, 3, 4, 1024, 0]).unwrap(),
1✔
433
                            Grid2D::new([3, 2].into(), vec![true, true, true, true, true, false])
1✔
434
                                .unwrap(),
1✔
435
                        )
1✔
436
                        .unwrap()
1✔
437
                        .into(),
1✔
438
                    ),
1✔
439
                    None,
1✔
440
                );
1✔
441

1✔
442
                RasterOperator::boxed(Temperature {
1✔
443
                    params: TemperatureParams::default(),
1✔
444
                    sources: SingleRasterSource {
1✔
445
                        raster: src.boxed(),
1✔
446
                    },
1✔
447
                })
1✔
448
            },
1✔
449
            test_util::create_mock_query(),
1✔
450
            &ctx,
1✔
451
        )
1✔
452
        .await;
1✔
453
        assert!(res.is_ok());
1✔
454
        let res = res.unwrap();
1✔
455
        assert!(geoengine_datatypes::util::test::grid_or_empty_grid_eq(
1✔
456
            &res.grid_array,
1✔
457
            &MaskedGrid2D::new(
1✔
458
                Grid2D::new(
1✔
459
                    [3, 2].into(),
1✔
460
                    vec![300.341_43, 318.617_65, 330.365_14, 339.233_64, 0., 0.],
1✔
461
                )
1✔
462
                .unwrap(),
1✔
463
                Grid2D::new([3, 2].into(), vec![true, true, true, true, false, false,],).unwrap(),
1✔
464
            )
1✔
465
            .unwrap()
1✔
466
            .into()
1✔
467
        ));
1✔
468
    }
469

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

475
        let res = test_util::process(
1✔
476
            || {
1✔
477
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
478
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
479

1✔
480
                RasterOperator::boxed(Temperature {
1✔
481
                    params: TemperatureParams {
1✔
482
                        force_satellite: Some(13),
1✔
483
                    },
1✔
484
                    sources: SingleRasterSource {
1✔
485
                        raster: src.boxed(),
1✔
486
                    },
1✔
487
                })
1✔
488
            },
1✔
489
            test_util::create_mock_query(),
1✔
490
            &ctx,
1✔
491
        )
1✔
492
        .await;
×
493
        assert!(res.is_err());
1✔
494
    }
495

496
    #[tokio::test]
1✔
497
    async fn test_missing_satellite() {
1✔
498
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
499
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
500

501
        let res = test_util::process(
1✔
502
            || {
1✔
503
                let props = test_util::create_properties(Some(4), None, Some(0.0), Some(1.0));
1✔
504
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
505

1✔
506
                RasterOperator::boxed(Temperature {
1✔
507
                    params: TemperatureParams::default(),
1✔
508
                    sources: SingleRasterSource {
1✔
509
                        raster: src.boxed(),
1✔
510
                    },
1✔
511
                })
1✔
512
            },
1✔
513
            test_util::create_mock_query(),
1✔
514
            &ctx,
1✔
515
        )
1✔
516
        .await;
×
517
        assert!(res.is_err());
1✔
518
    }
519

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

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

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

544
    #[tokio::test]
1✔
545
    async fn test_missing_channel() {
1✔
546
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
547
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
548

549
        let res = test_util::process(
1✔
550
            || {
1✔
551
                let props = test_util::create_properties(None, Some(1), Some(0.0), Some(1.0));
1✔
552
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
553

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

568
    #[tokio::test]
1✔
569
    async fn test_invalid_channel() {
1✔
570
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
571
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
572

573
        let res = test_util::process(
1✔
574
            || {
1✔
575
                let props = test_util::create_properties(Some(1), Some(1), Some(0.0), Some(1.0));
1✔
576
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
577

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

592
    #[tokio::test]
1✔
593
    async fn test_missing_slope() {
1✔
594
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
595
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
596

597
        let res = test_util::process(
1✔
598
            || {
1✔
599
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), None);
1✔
600
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
601

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

616
    #[tokio::test]
1✔
617
    async fn test_missing_offset() {
1✔
618
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
619
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
620

621
        let res = test_util::process(
1✔
622
            || {
1✔
623
                let props = test_util::create_properties(Some(4), Some(1), None, Some(1.0));
1✔
624
                let src = test_util::create_mock_source::<u8>(props, None, None);
1✔
625

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

640
    #[tokio::test]
1✔
641
    async fn test_invalid_measurement_unitless() {
1✔
642
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
643
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
644

645
        let res = test_util::process(
1✔
646
            || {
1✔
647
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
648
                let src =
1✔
649
                    test_util::create_mock_source::<u8>(props, None, Some(Measurement::Unitless));
1✔
650

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

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

670
        let res = test_util::process(
1✔
671
            || {
1✔
672
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
673
                let src = test_util::create_mock_source::<u8>(
1✔
674
                    props,
1✔
675
                    None,
1✔
676
                    Some(Measurement::Continuous(ContinuousMeasurement {
1✔
677
                        measurement: "invalid".into(),
1✔
678
                        unit: None,
1✔
679
                    })),
1✔
680
                );
1✔
681

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

694
        assert!(res.is_err());
1✔
695
    }
696

697
    #[tokio::test]
1✔
698
    async fn test_invalid_measurement_classification() {
1✔
699
        let tiling_specification = TilingSpecification::new([0.0, 0.0].into(), [3, 2].into());
1✔
700

1✔
701
        let ctx = MockExecutionContext::new_with_tiling_spec(tiling_specification);
1✔
702

703
        let res = test_util::process(
1✔
704
            || {
1✔
705
                let props = test_util::create_properties(Some(4), Some(1), Some(0.0), Some(1.0));
1✔
706
                let src = test_util::create_mock_source::<u8>(
1✔
707
                    props,
1✔
708
                    None,
1✔
709
                    Some(Measurement::Classification(ClassificationMeasurement {
1✔
710
                        measurement: "invalid".into(),
1✔
711
                        classes: HashMap::new(),
1✔
712
                    })),
1✔
713
                );
1✔
714

1✔
715
                RasterOperator::boxed(Temperature {
1✔
716
                    params: TemperatureParams::default(),
1✔
717
                    sources: SingleRasterSource {
1✔
718
                        raster: src.boxed(),
1✔
719
                    },
1✔
720
                })
1✔
721
            },
1✔
722
            test_util::create_mock_query(),
1✔
723
            &ctx,
1✔
724
        )
1✔
725
        .await;
×
726
        assert!(res.is_err());
1✔
727
    }
728
}
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