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

geo-engine / geoengine / 19240886216

10 Nov 2025 05:48PM UTC coverage: 88.94%. First build
19240886216

Pull #1083

github

web-flow
Merge eef5160d4 into 113de40ca
Pull Request #1083: feat: new gdal source workflow optimization

6215 of 7057 new or added lines in 71 files covered. (88.07%)

116335 of 130802 relevant lines covered (88.94%)

496353.73 hits per line

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

73.51
/operators/src/engine/operator.rs
1
use serde::{Deserialize, Serialize};
2
use snafu::ResultExt;
3
use tracing::debug;
4

5
use crate::util::Result;
6
use crate::{error, optimization::OptimizationError};
7
use async_trait::async_trait;
8
use geoengine_datatypes::{dataset::NamedData, primitives::SpatialResolution, util::ByteSize};
9

10
use super::{
11
    CloneablePlotOperator, CloneableRasterOperator, CloneableVectorOperator, CreateSpan,
12
    ExecutionContext, PlotResultDescriptor, RasterResultDescriptor, TypedPlotQueryProcessor,
13
    VectorResultDescriptor, WorkflowOperatorPath,
14
    query_processor::{TypedRasterQueryProcessor, TypedVectorQueryProcessor},
15
};
16

17
pub trait OperatorData {
18
    /// Get the ids of all the data involoved in this operator and its sources
19
    fn data_names(&self) -> Vec<NamedData> {
2✔
20
        let mut datasets = vec![];
2✔
21
        self.data_names_collect(&mut datasets);
2✔
22
        datasets
2✔
23
    }
2✔
24

25
    fn data_names_collect(&self, data_names: &mut Vec<NamedData>);
26
}
27

28
/// Common methods for `RasterOperator`s
29
#[typetag::serde(tag = "type")]
6✔
30
#[async_trait]
31
pub trait RasterOperator:
32
    CloneableRasterOperator + OperatorData + Send + Sync + std::fmt::Debug
33
{
34
    /// Internal initialization logic of the operator
35
    async fn _initialize(
36
        self: Box<Self>,
37
        path: WorkflowOperatorPath,
38
        context: &dyn ExecutionContext,
39
    ) -> Result<Box<dyn InitializedRasterOperator>>;
40

41
    /// Initialize the operator
42
    ///
43
    /// This method should not be overriden because it handles wrapping the operator using the
44
    /// execution context. Instead, `_initialize` should be implemented.
45
    async fn initialize(
46
        self: Box<Self>,
47
        path: WorkflowOperatorPath,
48
        context: &dyn ExecutionContext,
49
    ) -> Result<Box<dyn InitializedRasterOperator>> {
482✔
50
        let span = self.span();
51
        debug!("Initialize {}, path: {}", self.typetag_name(), &path);
52
        #[allow(clippy::used_underscore_items)] // TODO: maybe rename?
53
        let op = self._initialize(path.clone(), context).await?;
54

55
        Ok(context.wrap_initialized_raster_operator(op, span))
56
    }
482✔
57

58
    /// Wrap a box around a `RasterOperator`
59
    fn boxed(self) -> Box<dyn RasterOperator>
8,211✔
60
    where
8,211✔
61
        Self: Sized + 'static,
8,211✔
62
    {
63
        Box::new(self)
8,211✔
64
    }
8,211✔
65

66
    fn span(&self) -> CreateSpan;
67
}
68

69
/// Common methods for `VectorOperator`s
70
#[typetag::serde(tag = "type")]
×
71
#[async_trait]
72
pub trait VectorOperator:
73
    CloneableVectorOperator + OperatorData + Send + Sync + std::fmt::Debug
74
{
75
    async fn _initialize(
76
        self: Box<Self>,
77
        path: WorkflowOperatorPath,
78
        context: &dyn ExecutionContext,
79
    ) -> Result<Box<dyn InitializedVectorOperator>>;
80

81
    #[allow(clippy::used_underscore_items)]
82
    async fn initialize(
83
        self: Box<Self>,
84
        path: WorkflowOperatorPath,
85
        context: &dyn ExecutionContext,
86
    ) -> Result<Box<dyn InitializedVectorOperator>> {
206✔
87
        let span = self.span();
88
        debug!("Initialize {}, path: {}", self.typetag_name(), &path);
89
        let op = self._initialize(path.clone(), context).await?;
90
        Ok(context.wrap_initialized_vector_operator(op, span))
91
    }
206✔
92

93
    /// Wrap a box around a `VectorOperator`
94
    fn boxed(self) -> Box<dyn VectorOperator>
259✔
95
    where
259✔
96
        Self: Sized + 'static,
259✔
97
    {
98
        Box::new(self)
259✔
99
    }
259✔
100

101
    fn span(&self) -> CreateSpan;
102
}
103

104
/// Common methods for `PlotOperator`s
105
#[typetag::serde(tag = "type")]
×
106
#[async_trait]
107
pub trait PlotOperator:
108
    CloneablePlotOperator + OperatorData + Send + Sync + std::fmt::Debug
109
{
110
    async fn _initialize(
111
        self: Box<Self>,
112
        path: WorkflowOperatorPath,
113
        context: &dyn ExecutionContext,
114
    ) -> Result<Box<dyn InitializedPlotOperator>>;
115

116
    async fn initialize(
117
        self: Box<Self>,
118
        path: WorkflowOperatorPath,
119
        context: &dyn ExecutionContext,
120
    ) -> Result<Box<dyn InitializedPlotOperator>> {
71✔
121
        let span = self.span();
122
        debug!("Initialize {}, path: {}", self.typetag_name(), &path);
123
        #[allow(clippy::used_underscore_items)] // TODO: maybe rename?
124
        let op = self._initialize(path.clone(), context).await?;
125
        Ok(context.wrap_initialized_plot_operator(op, span))
126
    }
71✔
127

128
    /// Wrap a box around a `PlotOperator`
129
    fn boxed(self) -> Box<dyn PlotOperator>
80✔
130
    where
80✔
131
        Self: Sized + 'static,
80✔
132
    {
133
        Box::new(self)
80✔
134
    }
80✔
135

136
    fn span(&self) -> CreateSpan;
137
}
138

139
// TODO: implement a derive macro for common fields of operators: name, path, data, result_descriptor and automatically implement common trait functions
140
#[async_trait]
141
pub trait InitializedRasterOperator: Send + Sync {
142
    /// Get the result descriptor of the `Operator`
143
    fn result_descriptor(&self) -> &RasterResultDescriptor;
144

145
    /// Instantiate a `TypedVectorQueryProcessor` from a `RasterOperator`
146
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor>;
147

148
    /// Wrap a box around a `RasterOperator`
149
    fn boxed(self) -> Box<dyn InitializedRasterOperator>
428✔
150
    where
428✔
151
        Self: Sized + 'static,
428✔
152
    {
153
        Box::new(self)
428✔
154
    }
428✔
155

156
    /// Get a canonic representation of the operator and its sources
157
    fn canonic_name(&self) -> CanonicOperatorName;
158

159
    /// Get the unique name of the operator
160
    fn name(&self) -> &'static str;
161

162
    // Get the path of the operator in the workflow
163
    fn path(&self) -> WorkflowOperatorPath;
164

165
    /// Return the name of the data loaded by the operator (if any)
166
    fn data(&self) -> Option<String> {
20✔
167
        None
20✔
168
    }
20✔
169

170
    /// Optimize the operator graph for a given resolution
171
    fn optimize(
172
        &self,
173
        resolution: SpatialResolution,
174
    ) -> Result<Box<dyn RasterOperator>, OptimizationError>;
175

176
    /// optimize the operator graph and reinitialize it
177
    async fn optimize_and_reinitialize(
178
        &self,
179
        resolution: SpatialResolution,
180
        exe_ctx: &dyn ExecutionContext,
NEW
181
    ) -> Result<Box<dyn InitializedRasterOperator>> {
×
182
        let optimized = self
183
            .optimize(resolution)
184
            .context(crate::error::Optimization)?;
185
        optimized
186
            .initialize(WorkflowOperatorPath::initialize_root(), exe_ctx)
187
            .await
NEW
188
    }
×
189
}
190

191
pub trait InitializedVectorOperator: Send + Sync {
192
    /// Get the result descriptor of the `Operator`
193
    fn result_descriptor(&self) -> &VectorResultDescriptor;
194

195
    /// Instantiate a `TypedVectorQueryProcessor` from a `RasterOperator`
196
    fn query_processor(&self) -> Result<TypedVectorQueryProcessor>;
197

198
    /// Wrap a box around a `RasterOperator`
199
    fn boxed(self) -> Box<dyn InitializedVectorOperator>
190✔
200
    where
190✔
201
        Self: Sized + 'static,
190✔
202
    {
203
        Box::new(self)
190✔
204
    }
190✔
205

206
    /// Get a canonic representation of the operator and its sources.
207
    /// This only includes *logical* operators, not wrappers
208
    fn canonic_name(&self) -> CanonicOperatorName;
209

210
    /// Get the unique name of the operator
211
    fn name(&self) -> &'static str;
212

213
    // Get the path of the operator in the workflow
214
    fn path(&self) -> WorkflowOperatorPath;
215

216
    /// Return the name of the data loaded by the operator (if any)
217
    fn data(&self) -> Option<String> {
4✔
218
        None
4✔
219
    }
4✔
220

221
    /// Optimize the operator graph for a given resolution
222
    fn optimize(
223
        &self,
224
        resolution: SpatialResolution,
225
    ) -> Result<Box<dyn VectorOperator>, OptimizationError>;
226
}
227

228
/// A canonic name for an operator and its sources
229
/// We use a byte representation of the operator json
230
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
231
pub struct CanonicOperatorName(Vec<u8>);
232

233
impl CanonicOperatorName {
234
    pub fn new<T: Serialize>(value: &T) -> Result<Self> {
×
235
        Ok(CanonicOperatorName(serde_json::to_vec(&value)?))
×
236
    }
×
237

238
    ///
239
    /// # Panics
240
    ///
241
    /// if the value cannot be serialized as json
242
    ///
243
    pub fn new_unchecked<T: Serialize>(value: &T) -> Self {
766✔
244
        CanonicOperatorName(serde_json::to_vec(&value).expect("it should be checked by the caller"))
766✔
245
    }
766✔
246
}
247

248
impl ByteSize for CanonicOperatorName {
249
    fn heap_byte_size(&self) -> usize {
2✔
250
        self.0.heap_byte_size()
2✔
251
    }
2✔
252
}
253

254
impl<T> From<&T> for CanonicOperatorName
255
where
256
    T: Serialize,
257
{
258
    fn from(value: &T) -> Self {
752✔
259
        CanonicOperatorName::new_unchecked(value)
752✔
260
    }
752✔
261
}
262

263
impl std::fmt::Display for CanonicOperatorName {
264
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
265
        let s = String::from_utf8_lossy(&self.0);
×
266
        write!(f, "{s}")
×
267
    }
×
268
}
269

270
pub trait InitializedPlotOperator: Send + Sync {
271
    /// Get the result descriptor of the `Operator`
272
    fn result_descriptor(&self) -> &PlotResultDescriptor;
273

274
    /// Instantiate a `TypedVectorQueryProcessor` from a `RasterOperator`
275
    fn query_processor(&self) -> Result<TypedPlotQueryProcessor>;
276

277
    /// Wrap a box around a `RasterOperator`
278
    fn boxed(self) -> Box<dyn InitializedPlotOperator>
59✔
279
    where
59✔
280
        Self: Sized + 'static,
59✔
281
    {
282
        Box::new(self)
59✔
283
    }
59✔
284

285
    /// Get a canonic representation of the operator and its sources
286
    fn canonic_name(&self) -> CanonicOperatorName;
287

288
    /// Optimize the operator graph for a given resolution
289
    fn optimize(
290
        &self,
291
        resolution: SpatialResolution,
292
    ) -> Result<Box<dyn PlotOperator>, OptimizationError>;
293
}
294

295
impl InitializedRasterOperator for Box<dyn InitializedRasterOperator> {
296
    fn result_descriptor(&self) -> &RasterResultDescriptor {
456✔
297
        self.as_ref().result_descriptor()
456✔
298
    }
456✔
299

300
    fn query_processor(&self) -> Result<TypedRasterQueryProcessor> {
335✔
301
        self.as_ref().query_processor()
335✔
302
    }
335✔
303

304
    fn canonic_name(&self) -> CanonicOperatorName {
2✔
305
        self.as_ref().canonic_name()
2✔
306
    }
2✔
307

308
    fn name(&self) -> &'static str {
56✔
309
        self.as_ref().name()
56✔
310
    }
56✔
311

312
    fn path(&self) -> WorkflowOperatorPath {
56✔
313
        self.as_ref().path()
56✔
314
    }
56✔
315

316
    fn data(&self) -> Option<String> {
56✔
317
        self.as_ref().data()
56✔
318
    }
56✔
319

320
    fn optimize(
54✔
321
        &self,
54✔
322
        resolution: SpatialResolution,
54✔
323
    ) -> Result<Box<dyn RasterOperator>, OptimizationError> {
54✔
324
        self.as_ref().optimize(resolution)
54✔
325
    }
54✔
326
}
327

328
impl InitializedVectorOperator for Box<dyn InitializedVectorOperator> {
329
    fn result_descriptor(&self) -> &VectorResultDescriptor {
203✔
330
        self.as_ref().result_descriptor()
203✔
331
    }
203✔
332

333
    fn query_processor(&self) -> Result<TypedVectorQueryProcessor> {
136✔
334
        self.as_ref().query_processor()
136✔
335
    }
136✔
336

337
    fn canonic_name(&self) -> CanonicOperatorName {
×
338
        self.as_ref().canonic_name()
×
339
    }
×
340

341
    fn name(&self) -> &'static str {
8✔
342
        self.as_ref().name()
8✔
343
    }
8✔
344

345
    fn path(&self) -> WorkflowOperatorPath {
8✔
346
        self.as_ref().path()
8✔
347
    }
8✔
348

349
    fn data(&self) -> Option<String> {
8✔
350
        self.as_ref().data()
8✔
351
    }
8✔
352

353
    fn optimize(
10✔
354
        &self,
10✔
355
        resolution: SpatialResolution,
10✔
356
    ) -> Result<Box<dyn VectorOperator>, OptimizationError> {
10✔
357
        self.as_ref().optimize(resolution)
10✔
358
    }
10✔
359
}
360

361
impl InitializedPlotOperator for Box<dyn InitializedPlotOperator> {
362
    fn result_descriptor(&self) -> &PlotResultDescriptor {
×
363
        self.as_ref().result_descriptor()
×
364
    }
×
365

366
    fn query_processor(&self) -> Result<TypedPlotQueryProcessor> {
53✔
367
        self.as_ref().query_processor()
53✔
368
    }
53✔
369

370
    fn canonic_name(&self) -> CanonicOperatorName {
×
371
        self.as_ref().canonic_name()
×
372
    }
×
373

NEW
374
    fn optimize(
×
NEW
375
        &self,
×
NEW
376
        resolution: SpatialResolution,
×
NEW
377
    ) -> Result<Box<dyn PlotOperator>, OptimizationError> {
×
NEW
378
        self.as_ref().optimize(resolution)
×
NEW
379
    }
×
380
}
381

382
/// An enum to differentiate between `Operator` variants
383
#[derive(Clone, Debug, Serialize, Deserialize)]
384
#[serde(tag = "type", content = "operator")]
385
pub enum TypedOperator {
386
    Vector(Box<dyn VectorOperator>),
387
    Raster(Box<dyn RasterOperator>),
388
    Plot(Box<dyn PlotOperator>),
389
}
390

391
impl TypedOperator {
392
    pub fn get_vector(self) -> Result<Box<dyn VectorOperator>> {
6✔
393
        if let TypedOperator::Vector(o) = self {
6✔
394
            return Ok(o);
6✔
395
        }
×
396
        Err(error::Error::InvalidOperatorType {
×
397
            expected: "Vector".to_owned(),
×
398
            found: self.type_name().to_owned(),
×
399
        })
×
400
    }
6✔
401

402
    pub fn get_raster(self) -> Result<Box<dyn RasterOperator>> {
25✔
403
        if let TypedOperator::Raster(o) = self {
25✔
404
            return Ok(o);
25✔
405
        }
×
406
        Err(error::Error::InvalidOperatorType {
×
407
            expected: "Raster".to_owned(),
×
408
            found: self.type_name().to_owned(),
×
409
        })
×
410
    }
25✔
411

412
    pub fn get_plot(self) -> Result<Box<dyn PlotOperator>> {
2✔
413
        if let TypedOperator::Plot(o) = self {
2✔
414
            return Ok(o);
2✔
415
        }
×
416
        Err(error::Error::InvalidOperatorType {
×
417
            expected: "Plot".to_owned(),
×
418
            found: self.type_name().to_owned(),
×
419
        })
×
420
    }
2✔
421

422
    fn type_name(&self) -> &str {
×
423
        match self {
×
424
            TypedOperator::Vector(_) => "Vector",
×
425
            TypedOperator::Raster(_) => "Raster",
×
426
            TypedOperator::Plot(_) => "Plot",
×
427
        }
428
    }
×
429
}
430

431
impl From<Box<dyn VectorOperator>> for TypedOperator {
432
    fn from(operator: Box<dyn VectorOperator>) -> Self {
26✔
433
        Self::Vector(operator)
26✔
434
    }
26✔
435
}
436

437
impl From<Box<dyn RasterOperator>> for TypedOperator {
438
    fn from(operator: Box<dyn RasterOperator>) -> Self {
5✔
439
        Self::Raster(operator)
5✔
440
    }
5✔
441
}
442

443
impl From<Box<dyn PlotOperator>> for TypedOperator {
444
    fn from(operator: Box<dyn PlotOperator>) -> Self {
13✔
445
        Self::Plot(operator)
13✔
446
    }
13✔
447
}
448

449
#[macro_export]
450
macro_rules! call_on_typed_operator {
451
    ($typed_operator:expr, $operator_var:ident => $function_call:expr) => {
452
        match $typed_operator {
453
            $crate::engine::TypedOperator::Vector($operator_var) => $function_call,
454
            $crate::engine::TypedOperator::Raster($operator_var) => $function_call,
455
            $crate::engine::TypedOperator::Plot($operator_var) => $function_call,
456
        }
457
    };
458
}
459

460
impl OperatorData for TypedOperator {
461
    fn data_names_collect(&self, data_ids: &mut Vec<NamedData>) {
2✔
462
        match self {
2✔
463
            TypedOperator::Vector(v) => v.data_names_collect(data_ids),
×
464
            TypedOperator::Raster(r) => r.data_names_collect(data_ids),
2✔
465
            TypedOperator::Plot(p) => p.data_names_collect(data_ids),
×
466
        }
467
    }
2✔
468
}
469

470
pub trait OperatorName {
471
    const TYPE_NAME: &'static str;
472
}
473

474
#[cfg(test)]
475
mod tests {
476
    use serde_json::json;
477

478
    use super::*;
479

480
    #[test]
481
    fn op_name_byte_size() {
1✔
482
        let op = CanonicOperatorName::new_unchecked(&json!({"foo": "bar"}));
1✔
483
        assert_eq!(op.byte_size(), 37);
1✔
484

485
        let op = CanonicOperatorName::new_unchecked(&json!({"foo": {"bar": [1,2,3]}}));
1✔
486
        assert_eq!(op.byte_size(), 47);
1✔
487
    }
1✔
488
}
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