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

geo-engine / geoengine / 4126366810

pending completion
4126366810

push

github

GitHub
Merge #733

287 of 287 new or added lines in 11 files covered. (100.0%)

90438 of 102730 relevant lines covered (88.03%)

76338.47 hits per line

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

56.41
/services/src/api/model/datatypes.rs
1
use crate::error::{self, Result};
2
use crate::identifier;
3
use geoengine_datatypes::primitives::{
4
    AxisAlignedRectangle, MultiLineStringAccess, MultiPointAccess, MultiPolygonAccess,
5
};
6
use ordered_float::NotNan;
7
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
8
use snafu::ResultExt;
9
use std::{
10
    collections::{BTreeMap, HashMap},
11
    fmt::{Debug, Formatter},
12
    str::FromStr,
13
};
14
use utoipa::ToSchema;
15

16
identifier!(DataProviderId);
×
17

18
impl From<DataProviderId> for geoengine_datatypes::dataset::DataProviderId {
19
    fn from(value: DataProviderId) -> Self {
99✔
20
        Self(value.0)
99✔
21
    }
99✔
22
}
23

24
// Identifier for datasets managed by Geo Engine
25
identifier!(DatasetId);
×
26

27
impl From<DatasetId> for geoengine_datatypes::dataset::DatasetId {
28
    fn from(value: DatasetId) -> Self {
52✔
29
        Self(value.0)
52✔
30
    }
52✔
31
}
32

33
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
17✔
34
#[serde(rename_all = "camelCase", tag = "type")]
35
/// The identifier for loadable data. It is used in the source operators to get the loading info (aka parametrization)
36
/// for accessing the data. Internal data is loaded from datasets, external from `DataProvider`s.
37
pub enum DataId {
38
    #[serde(rename_all = "camelCase")]
39
    Internal {
40
        dataset_id: DatasetId,
41
    },
42
    External(ExternalDataId),
43
}
44

45
impl DataId {
46
    pub fn internal(&self) -> Option<DatasetId> {
47
        if let Self::Internal {
48
            dataset_id: dataset,
33✔
49
        } = self
33✔
50
        {
51
            return Some(*dataset);
33✔
52
        }
×
53
        None
×
54
    }
33✔
55

56
    pub fn external(&self) -> Option<ExternalDataId> {
57
        if let Self::External(id) = self {
18✔
58
            return Some(id.clone());
18✔
59
        }
×
60
        None
×
61
    }
18✔
62
}
63

64
impl From<DatasetId> for DataId {
65
    fn from(value: DatasetId) -> Self {
15✔
66
        Self::Internal { dataset_id: value }
15✔
67
    }
15✔
68
}
69

70
impl From<ExternalDataId> for DataId {
71
    fn from(value: ExternalDataId) -> Self {
×
72
        Self::External(value)
×
73
    }
×
74
}
75

76
impl From<ExternalDataId> for geoengine_datatypes::dataset::DataId {
77
    fn from(value: ExternalDataId) -> Self {
6✔
78
        Self::External(value.into())
6✔
79
    }
6✔
80
}
81

82
impl From<DatasetId> for geoengine_datatypes::dataset::DataId {
83
    fn from(value: DatasetId) -> Self {
7✔
84
        Self::Internal {
7✔
85
            dataset_id: value.into(),
7✔
86
        }
7✔
87
    }
7✔
88
}
89

90
impl From<ExternalDataId> for geoengine_datatypes::dataset::ExternalDataId {
91
    fn from(value: ExternalDataId) -> Self {
99✔
92
        Self {
99✔
93
            provider_id: value.provider_id.into(),
99✔
94
            layer_id: value.layer_id.into(),
99✔
95
        }
99✔
96
    }
99✔
97
}
98

99
impl From<geoengine_datatypes::dataset::DataId> for DataId {
100
    fn from(id: geoengine_datatypes::dataset::DataId) -> Self {
44✔
101
        match id {
44✔
102
            geoengine_datatypes::dataset::DataId::Internal { dataset_id } => Self::Internal {
28✔
103
                dataset_id: dataset_id.into(),
28✔
104
            },
28✔
105
            geoengine_datatypes::dataset::DataId::External(external_id) => {
16✔
106
                Self::External(external_id.into())
16✔
107
            }
108
        }
109
    }
44✔
110
}
111

112
impl From<DataId> for geoengine_datatypes::dataset::DataId {
113
    fn from(id: DataId) -> Self {
93✔
114
        match id {
93✔
115
            DataId::Internal { dataset_id } => Self::Internal {
×
116
                dataset_id: dataset_id.into(),
×
117
            },
×
118
            DataId::External(external_id) => Self::External(external_id.into()),
93✔
119
        }
120
    }
93✔
121
}
122

123
impl From<geoengine_datatypes::dataset::DatasetId> for DatasetId {
124
    fn from(id: geoengine_datatypes::dataset::DatasetId) -> Self {
28✔
125
        Self(id.0)
28✔
126
    }
28✔
127
}
128

129
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, ToSchema)]
237✔
130
pub struct LayerId(pub String);
131

132
impl From<LayerId> for geoengine_datatypes::dataset::LayerId {
133
    fn from(value: LayerId) -> Self {
99✔
134
        Self(value.0)
99✔
135
    }
99✔
136
}
137

138
impl std::fmt::Display for LayerId {
139
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
15✔
140
        write!(f, "{}", self.0)
15✔
141
    }
15✔
142
}
143

144
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
33✔
145
#[serde(rename_all = "camelCase")]
146
pub struct ExternalDataId {
147
    pub provider_id: DataProviderId,
148
    pub layer_id: LayerId,
149
}
150

151
impl From<geoengine_datatypes::dataset::ExternalDataId> for ExternalDataId {
152
    fn from(id: geoengine_datatypes::dataset::ExternalDataId) -> Self {
16✔
153
        Self {
16✔
154
            provider_id: id.provider_id.into(),
16✔
155
            layer_id: id.layer_id.into(),
16✔
156
        }
16✔
157
    }
16✔
158
}
159

160
impl From<geoengine_datatypes::dataset::DataProviderId> for DataProviderId {
161
    fn from(id: geoengine_datatypes::dataset::DataProviderId) -> Self {
17✔
162
        Self(id.0)
17✔
163
    }
17✔
164
}
165

166
impl From<geoengine_datatypes::dataset::LayerId> for LayerId {
167
    fn from(id: geoengine_datatypes::dataset::LayerId) -> Self {
16✔
168
        Self(id.0)
16✔
169
    }
16✔
170
}
171

172
/// A spatial reference authority that is part of a spatial reference definition
173
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
22✔
174
#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
175
pub enum SpatialReferenceAuthority {
176
    Epsg,
177
    SrOrg,
178
    Iau2000,
179
    Esri,
180
}
181

182
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceAuthority>
183
    for SpatialReferenceAuthority
184
{
185
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceAuthority) -> Self {
98✔
186
        match value {
98✔
187
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Epsg => Self::Epsg,
97✔
188
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::SrOrg => Self::SrOrg,
1✔
189
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Iau2000 => {
190
                Self::Iau2000
×
191
            }
192
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Esri => Self::Esri,
×
193
        }
194
    }
98✔
195
}
196

197
impl From<SpatialReferenceAuthority>
198
    for geoengine_datatypes::spatial_reference::SpatialReferenceAuthority
199
{
200
    fn from(value: SpatialReferenceAuthority) -> Self {
12✔
201
        match value {
12✔
202
            SpatialReferenceAuthority::Epsg => Self::Epsg,
12✔
203
            SpatialReferenceAuthority::SrOrg => Self::SrOrg,
×
204
            SpatialReferenceAuthority::Iau2000 => Self::Iau2000,
×
205
            SpatialReferenceAuthority::Esri => Self::Esri,
×
206
        }
207
    }
12✔
208
}
209

210
impl FromStr for SpatialReferenceAuthority {
211
    type Err = error::Error;
212

213
    fn from_str(s: &str) -> Result<Self, Self::Err> {
31✔
214
        Ok(match s {
31✔
215
            "EPSG" => SpatialReferenceAuthority::Epsg,
31✔
216
            "SR-ORG" => SpatialReferenceAuthority::SrOrg,
×
217
            "IAU2000" => SpatialReferenceAuthority::Iau2000,
×
218
            "ESRI" => SpatialReferenceAuthority::Esri,
×
219
            _ => {
220
                return Err(error::Error::InvalidSpatialReferenceString {
×
221
                    spatial_reference_string: s.into(),
×
222
                })
×
223
            }
224
        })
225
    }
31✔
226
}
227

228
impl std::fmt::Display for SpatialReferenceAuthority {
229
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34✔
230
        write!(
34✔
231
            f,
34✔
232
            "{}",
34✔
233
            match self {
34✔
234
                SpatialReferenceAuthority::Epsg => "EPSG",
34✔
235
                SpatialReferenceAuthority::SrOrg => "SR-ORG",
×
236
                SpatialReferenceAuthority::Iau2000 => "IAU2000",
×
237
                SpatialReferenceAuthority::Esri => "ESRI",
×
238
            }
239
        )
240
    }
34✔
241
}
242

243
/// A spatial reference consists of an authority and a code
244
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
22✔
245
pub struct SpatialReference {
246
    authority: SpatialReferenceAuthority,
247
    code: u32,
248
}
249

250
impl SpatialReference {
251
    pub fn proj_string(self) -> Result<String> {
252
        match self.authority {
×
253
            SpatialReferenceAuthority::Epsg | SpatialReferenceAuthority::Iau2000 => {
254
                Ok(format!("{}:{}", self.authority, self.code))
12✔
255
            }
256
            // poor-mans integration of Meteosat Second Generation
257
            SpatialReferenceAuthority::SrOrg if self.code == 81 => Ok("+proj=geos +lon_0=0 +h=35785831 +x_0=0 +y_0=0 +ellps=WGS84 +units=m +no_defs +type=crs".to_owned()),
×
258
            SpatialReferenceAuthority::SrOrg | SpatialReferenceAuthority::Esri => {
259
                Err(error::Error::ProjStringUnresolvable { spatial_ref: self })
×
260
                //TODO: we might need to look them up somehow! Best solution would be a registry where we can store user definexd srs strings.
261
            }
262
        }
263
    }
12✔
264

265
    /// Return the srs-string "authority:code"
266
    #[allow(clippy::trivially_copy_pass_by_ref)]
267
    pub fn srs_string(&self) -> String {
12✔
268
        format!("{}:{}", self.authority, self.code)
12✔
269
    }
12✔
270
}
271

272
impl<'a> ToSchema<'a> for SpatialReference {
273
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
×
274
        use utoipa::openapi::*;
×
275
        (
×
276
            "SpatialReference",
×
277
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
×
278
        )
×
279
    }
×
280
}
281

282
impl From<geoengine_datatypes::spatial_reference::SpatialReference> for SpatialReference {
283
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReference) -> Self {
98✔
284
        Self {
98✔
285
            authority: (*value.authority()).into(),
98✔
286
            code: value.code(),
98✔
287
        }
98✔
288
    }
98✔
289
}
290

291
impl From<SpatialReference> for geoengine_datatypes::spatial_reference::SpatialReference {
292
    fn from(value: SpatialReference) -> Self {
12✔
293
        geoengine_datatypes::spatial_reference::SpatialReference::new(
12✔
294
            value.authority.into(),
12✔
295
            value.code,
12✔
296
        )
12✔
297
    }
12✔
298
}
299

300
impl SpatialReference {
301
    pub fn new(authority: SpatialReferenceAuthority, code: u32) -> Self {
36✔
302
        Self { authority, code }
36✔
303
    }
36✔
304

305
    pub fn authority(&self) -> &SpatialReferenceAuthority {
1✔
306
        &self.authority
1✔
307
    }
1✔
308

309
    pub fn code(self) -> u32 {
1✔
310
        self.code
1✔
311
    }
1✔
312
}
313

314
impl FromStr for SpatialReference {
315
    type Err = error::Error;
316

317
    fn from_str(s: &str) -> Result<Self, Self::Err> {
31✔
318
        let mut split = s.split(':');
31✔
319

31✔
320
        match (split.next(), split.next(), split.next()) {
31✔
321
            (Some(authority), Some(code), None) => Ok(Self::new(
31✔
322
                authority.parse()?,
31✔
323
                code.parse::<u32>().context(error::ParseU32)?,
31✔
324
            )),
325
            _ => Err(error::Error::InvalidSpatialReferenceString {
×
326
                spatial_reference_string: s.into(),
×
327
            }),
×
328
        }
329
    }
31✔
330
}
331

332
impl std::fmt::Display for SpatialReference {
333
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9✔
334
        write!(f, "{}:{}", self.authority, self.code)
9✔
335
    }
9✔
336
}
337

338
impl Serialize for SpatialReference {
339
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
1✔
340
    where
1✔
341
        S: Serializer,
1✔
342
    {
1✔
343
        serializer.serialize_str(&self.to_string())
1✔
344
    }
1✔
345
}
346

347
/// Helper struct for deserializing a `SpatialReferencce`
348
struct SpatialReferenceDeserializeVisitor;
349

350
impl<'de> Visitor<'de> for SpatialReferenceDeserializeVisitor {
351
    type Value = SpatialReference;
352

353
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
354
        formatter.write_str("a spatial reference in the form authority:code")
×
355
    }
×
356

357
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
14✔
358
    where
14✔
359
        E: serde::de::Error,
14✔
360
    {
14✔
361
        v.parse().map_err(serde::de::Error::custom)
14✔
362
    }
14✔
363
}
364

365
impl<'de> Deserialize<'de> for SpatialReference {
366
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
14✔
367
    where
14✔
368
        D: Deserializer<'de>,
14✔
369
    {
14✔
370
        deserializer.deserialize_str(SpatialReferenceDeserializeVisitor)
14✔
371
    }
14✔
372
}
373

374
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ToSchema)]
16✔
375
pub enum SpatialReferenceOption {
376
    SpatialReference(SpatialReference),
377
    Unreferenced,
378
}
379

380
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceOption>
381
    for SpatialReferenceOption
382
{
383
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceOption) -> Self {
86✔
384
        match value {
86✔
385
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::SpatialReference(s) => {
74✔
386
                Self::SpatialReference(s.into())
74✔
387
            }
388
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::Unreferenced => {
389
                Self::Unreferenced
12✔
390
            }
391
        }
392
    }
86✔
393
}
394

395
impl From<SpatialReferenceOption>
396
    for geoengine_datatypes::spatial_reference::SpatialReferenceOption
397
{
398
    fn from(value: SpatialReferenceOption) -> Self {
9✔
399
        match value {
9✔
400
            SpatialReferenceOption::SpatialReference(sr) => Self::SpatialReference(sr.into()),
9✔
401
            SpatialReferenceOption::Unreferenced => Self::Unreferenced,
×
402
        }
403
    }
9✔
404
}
405

406
impl From<SpatialReference> for SpatialReferenceOption {
407
    fn from(spatial_reference: SpatialReference) -> Self {
11✔
408
        Self::SpatialReference(spatial_reference)
11✔
409
    }
11✔
410
}
411

412
impl std::fmt::Display for SpatialReferenceOption {
413
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11✔
414
        match self {
11✔
415
            SpatialReferenceOption::SpatialReference(p) => write!(f, "{p}"),
8✔
416
            SpatialReferenceOption::Unreferenced => Ok(()),
3✔
417
        }
418
    }
11✔
419
}
420

421
impl Serialize for SpatialReferenceOption {
422
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
11✔
423
    where
11✔
424
        S: Serializer,
11✔
425
    {
11✔
426
        serializer.serialize_str(&self.to_string())
11✔
427
    }
11✔
428
}
429

430
/// Helper struct for deserializing a `SpatialReferenceOption`
431
struct SpatialReferenceOptionDeserializeVisitor;
432

433
impl<'de> Visitor<'de> for SpatialReferenceOptionDeserializeVisitor {
434
    type Value = SpatialReferenceOption;
435

436
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
437
        formatter.write_str("a spatial reference in the form authority:code")
×
438
    }
×
439

440
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
16✔
441
    where
16✔
442
        E: serde::de::Error,
16✔
443
    {
16✔
444
        if v.is_empty() {
16✔
445
            return Ok(SpatialReferenceOption::Unreferenced);
5✔
446
        }
11✔
447

448
        let spatial_reference: SpatialReference = v.parse().map_err(serde::de::Error::custom)?;
11✔
449

450
        Ok(spatial_reference.into())
11✔
451
    }
16✔
452
}
453

454
impl<'de> Deserialize<'de> for SpatialReferenceOption {
455
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
16✔
456
    where
16✔
457
        D: Deserializer<'de>,
16✔
458
    {
16✔
459
        deserializer.deserialize_str(SpatialReferenceOptionDeserializeVisitor)
16✔
460
    }
16✔
461
}
462

463
impl From<Option<SpatialReference>> for SpatialReferenceOption {
464
    fn from(option: Option<SpatialReference>) -> Self {
×
465
        match option {
×
466
            Some(p) => SpatialReferenceOption::SpatialReference(p),
×
467
            None => SpatialReferenceOption::Unreferenced,
×
468
        }
469
    }
×
470
}
471

472
impl From<SpatialReferenceOption> for Option<SpatialReference> {
473
    fn from(option: SpatialReferenceOption) -> Self {
10✔
474
        match option {
10✔
475
            SpatialReferenceOption::SpatialReference(p) => Some(p),
10✔
476
            SpatialReferenceOption::Unreferenced => None,
×
477
        }
478
    }
10✔
479
}
480

481
/// An enum that contains all possible vector data types
482
#[derive(
483
    Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Deserialize, Serialize, Copy, Clone, ToSchema,
32✔
484
)]
485
pub enum VectorDataType {
486
    Data,
487
    MultiPoint,
488
    MultiLineString,
489
    MultiPolygon,
490
}
491

492
impl From<geoengine_datatypes::collections::VectorDataType> for VectorDataType {
493
    fn from(value: geoengine_datatypes::collections::VectorDataType) -> Self {
20✔
494
        match value {
20✔
495
            geoengine_datatypes::collections::VectorDataType::Data => Self::Data,
10✔
496
            geoengine_datatypes::collections::VectorDataType::MultiPoint => Self::MultiPoint,
10✔
497
            geoengine_datatypes::collections::VectorDataType::MultiLineString => {
498
                Self::MultiLineString
×
499
            }
500
            geoengine_datatypes::collections::VectorDataType::MultiPolygon => Self::MultiPolygon,
×
501
        }
502
    }
20✔
503
}
504

505
impl From<VectorDataType> for geoengine_datatypes::collections::VectorDataType {
506
    fn from(value: VectorDataType) -> Self {
8✔
507
        match value {
8✔
508
            VectorDataType::Data => Self::Data,
×
509
            VectorDataType::MultiPoint => Self::MultiPoint,
8✔
510
            VectorDataType::MultiLineString => Self::MultiLineString,
×
511
            VectorDataType::MultiPolygon => Self::MultiPolygon,
×
512
        }
513
    }
8✔
514
}
515

516
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize, Default, ToSchema)]
70✔
517
pub struct Coordinate2D {
518
    pub x: f64,
519
    pub y: f64,
520
}
521

522
impl From<geoengine_datatypes::primitives::Coordinate2D> for Coordinate2D {
523
    fn from(coordinate: geoengine_datatypes::primitives::Coordinate2D) -> Self {
160✔
524
        Self {
160✔
525
            x: coordinate.x,
160✔
526
            y: coordinate.y,
160✔
527
        }
160✔
528
    }
160✔
529
}
530

531
impl From<Coordinate2D> for geoengine_datatypes::primitives::Coordinate2D {
532
    fn from(coordinate: Coordinate2D) -> Self {
17✔
533
        Self {
17✔
534
            x: coordinate.x,
17✔
535
            y: coordinate.y,
17✔
536
        }
17✔
537
    }
17✔
538
}
539

540
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema)]
5✔
541
#[serde(rename_all = "camelCase")]
542
/// A bounding box that includes all border points.
543
/// Note: may degenerate to a point!
544
pub struct BoundingBox2D {
545
    pub lower_left_coordinate: Coordinate2D,
546
    pub upper_right_coordinate: Coordinate2D,
547
}
548

549
impl From<geoengine_datatypes::primitives::BoundingBox2D> for BoundingBox2D {
550
    fn from(bbox: geoengine_datatypes::primitives::BoundingBox2D) -> Self {
22✔
551
        Self {
22✔
552
            lower_left_coordinate:
22✔
553
                geoengine_datatypes::primitives::AxisAlignedRectangle::lower_left(&bbox).into(),
22✔
554
            upper_right_coordinate:
22✔
555
                geoengine_datatypes::primitives::AxisAlignedRectangle::upper_right(&bbox).into(),
22✔
556
        }
22✔
557
    }
22✔
558
}
559

560
impl From<BoundingBox2D> for geoengine_datatypes::primitives::BoundingBox2D {
561
    fn from(bbox: BoundingBox2D) -> Self {
1✔
562
        Self::new_unchecked(
1✔
563
            bbox.lower_left_coordinate.into(),
1✔
564
            bbox.upper_right_coordinate.into(),
1✔
565
        )
1✔
566
    }
1✔
567
}
568

569
/// An object that composes the date and a timestamp with time zone.
570
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToSchema)]
×
571
pub struct DateTime {
572
    datetime: chrono::DateTime<chrono::Utc>,
573
}
574

575
impl FromStr for DateTime {
576
    type Err = geoengine_datatypes::primitives::DateTimeError;
577

578
    fn from_str(input: &str) -> Result<Self, Self::Err> {
×
579
        let date_time = chrono::DateTime::<chrono::FixedOffset>::from_str(input).map_err(|e| {
×
580
            Self::Err::DateParse {
×
581
                source: Box::new(e),
×
582
            }
×
583
        })?;
×
584

585
        Ok(date_time.into())
×
586
    }
×
587
}
588

589
impl From<chrono::DateTime<chrono::FixedOffset>> for DateTime {
590
    fn from(datetime: chrono::DateTime<chrono::FixedOffset>) -> Self {
×
591
        Self {
×
592
            datetime: datetime.into(),
×
593
        }
×
594
    }
×
595
}
596

597
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
46✔
598
#[serde(rename_all = "camelCase")]
599
pub enum FeatureDataType {
600
    Category,
601
    Int,
602
    Float,
603
    Text,
604
    Bool,
605
    DateTime,
606
}
607

608
impl From<geoengine_datatypes::primitives::FeatureDataType> for FeatureDataType {
609
    fn from(value: geoengine_datatypes::primitives::FeatureDataType) -> Self {
23✔
610
        match value {
23✔
611
            geoengine_datatypes::primitives::FeatureDataType::Category => Self::Category,
×
612
            geoengine_datatypes::primitives::FeatureDataType::Int => Self::Int,
5✔
613
            geoengine_datatypes::primitives::FeatureDataType::Float => Self::Float,
5✔
614
            geoengine_datatypes::primitives::FeatureDataType::Text => Self::Text,
13✔
615
            geoengine_datatypes::primitives::FeatureDataType::Bool => Self::Bool,
×
616
            geoengine_datatypes::primitives::FeatureDataType::DateTime => Self::DateTime,
×
617
        }
618
    }
23✔
619
}
620

621
impl From<FeatureDataType> for geoengine_datatypes::primitives::FeatureDataType {
622
    fn from(value: FeatureDataType) -> Self {
20✔
623
        match value {
20✔
624
            FeatureDataType::Category => Self::Category,
×
625
            FeatureDataType::Int => Self::Int,
4✔
626
            FeatureDataType::Float => Self::Float,
4✔
627
            FeatureDataType::Text => Self::Text,
12✔
628
            FeatureDataType::Bool => Self::Bool,
×
629
            FeatureDataType::DateTime => Self::DateTime,
×
630
        }
631
    }
20✔
632
}
633

634
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
54✔
635
#[serde(rename_all = "camelCase", tag = "type")]
636
pub enum Measurement {
637
    Unitless,
638
    Continuous(ContinuousMeasurement),
639
    Classification(ClassificationMeasurement),
640
}
641

642
impl From<geoengine_datatypes::primitives::Measurement> for Measurement {
643
    fn from(value: geoengine_datatypes::primitives::Measurement) -> Self {
80✔
644
        match value {
80✔
645
            geoengine_datatypes::primitives::Measurement::Unitless => Self::Unitless,
77✔
646
            geoengine_datatypes::primitives::Measurement::Continuous(cm) => {
1✔
647
                Self::Continuous(cm.into())
1✔
648
            }
649
            geoengine_datatypes::primitives::Measurement::Classification(cm) => {
2✔
650
                Self::Classification(cm.into())
2✔
651
            }
652
        }
653
    }
80✔
654
}
655

656
impl From<Measurement> for geoengine_datatypes::primitives::Measurement {
657
    fn from(value: Measurement) -> Self {
25✔
658
        match value {
25✔
659
            Measurement::Unitless => Self::Unitless,
24✔
660
            Measurement::Continuous(cm) => Self::Continuous(cm.into()),
×
661
            Measurement::Classification(cm) => Self::Classification(cm.into()),
1✔
662
        }
663
    }
25✔
664
}
665

666
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
×
667
pub struct ContinuousMeasurement {
668
    pub measurement: String,
669
    pub unit: Option<String>,
670
}
671

672
impl From<geoengine_datatypes::primitives::ContinuousMeasurement> for ContinuousMeasurement {
673
    fn from(value: geoengine_datatypes::primitives::ContinuousMeasurement) -> Self {
1✔
674
        Self {
1✔
675
            measurement: value.measurement,
1✔
676
            unit: value.unit,
1✔
677
        }
1✔
678
    }
1✔
679
}
680

681
impl From<ContinuousMeasurement> for geoengine_datatypes::primitives::ContinuousMeasurement {
682
    fn from(value: ContinuousMeasurement) -> Self {
×
683
        Self {
×
684
            measurement: value.measurement,
×
685
            unit: value.unit,
×
686
        }
×
687
    }
×
688
}
689

690
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
×
691
#[serde(
692
    try_from = "SerializableClassificationMeasurement",
693
    into = "SerializableClassificationMeasurement"
694
)]
695
pub struct ClassificationMeasurement {
696
    pub measurement: String,
697
    pub classes: HashMap<u8, String>,
698
}
699

700
impl From<geoengine_datatypes::primitives::ClassificationMeasurement>
701
    for ClassificationMeasurement
702
{
703
    fn from(value: geoengine_datatypes::primitives::ClassificationMeasurement) -> Self {
2✔
704
        Self {
2✔
705
            measurement: value.measurement,
2✔
706
            classes: value.classes,
2✔
707
        }
2✔
708
    }
2✔
709
}
710

711
impl From<ClassificationMeasurement>
712
    for geoengine_datatypes::primitives::ClassificationMeasurement
713
{
714
    fn from(value: ClassificationMeasurement) -> Self {
1✔
715
        Self {
1✔
716
            measurement: value.measurement,
1✔
717
            classes: value.classes,
1✔
718
        }
1✔
719
    }
1✔
720
}
721

722
/// A type that is solely for serde's serializability.
723
/// You cannot serialize floats as JSON map keys.
724
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
×
725
pub struct SerializableClassificationMeasurement {
726
    pub measurement: String,
727
    // use a BTreeMap to preserve the order of the keys
728
    pub classes: BTreeMap<String, String>,
729
}
730

731
impl From<ClassificationMeasurement> for SerializableClassificationMeasurement {
732
    fn from(measurement: ClassificationMeasurement) -> Self {
×
733
        let mut classes = BTreeMap::new();
×
734
        for (k, v) in measurement.classes {
×
735
            classes.insert(k.to_string(), v);
×
736
        }
×
737
        Self {
×
738
            measurement: measurement.measurement,
×
739
            classes,
×
740
        }
×
741
    }
×
742
}
743

744
impl TryFrom<SerializableClassificationMeasurement> for ClassificationMeasurement {
745
    type Error = <u8 as FromStr>::Err;
746

747
    fn try_from(measurement: SerializableClassificationMeasurement) -> Result<Self, Self::Error> {
×
748
        let mut classes = HashMap::with_capacity(measurement.classes.len());
×
749
        for (k, v) in measurement.classes {
×
750
            classes.insert(k.parse::<u8>()?, v);
×
751
        }
752
        Ok(Self {
×
753
            measurement: measurement.measurement,
×
754
            classes,
×
755
        })
×
756
    }
×
757
}
758

759
/// A partition of space that include the upper left but excludes the lower right coordinate
760
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema)]
20✔
761
#[serde(rename_all = "camelCase")]
762
pub struct SpatialPartition2D {
763
    upper_left_coordinate: Coordinate2D,
764
    lower_right_coordinate: Coordinate2D,
765
}
766

767
impl From<geoengine_datatypes::primitives::SpatialPartition2D> for SpatialPartition2D {
768
    fn from(value: geoengine_datatypes::primitives::SpatialPartition2D) -> Self {
56✔
769
        Self {
56✔
770
            upper_left_coordinate: value.upper_left().into(),
56✔
771
            lower_right_coordinate: value.lower_right().into(),
56✔
772
        }
56✔
773
    }
56✔
774
}
775

776
impl From<SpatialPartition2D> for geoengine_datatypes::primitives::SpatialPartition2D {
777
    fn from(value: SpatialPartition2D) -> Self {
5✔
778
        Self::new_unchecked(
5✔
779
            value.upper_left_coordinate.into(),
5✔
780
            value.lower_right_coordinate.into(),
5✔
781
        )
5✔
782
    }
5✔
783
}
784

785
/// A spatio-temporal rectangle with a specified resolution
786
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
×
787
#[serde(rename_all = "camelCase")]
788
#[aliases(
789
    VectorQueryRectangle = QueryRectangle<BoundingBox2D>,
790
    RasterQueryRectangle = QueryRectangle<SpatialPartition2D>,
791
    PlotQueryRectangle = QueryRectangle<BoundingBox2D>)
792
]
793
pub struct QueryRectangle<SpatialBounds> {
794
    pub spatial_bounds: SpatialBounds,
795
    pub time_interval: TimeInterval,
796
    pub spatial_resolution: SpatialResolution,
797
}
798

799
/// The spatial resolution in SRS units
800
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema)]
20✔
801
pub struct SpatialResolution {
802
    pub x: f64,
803
    pub y: f64,
804
}
805

806
impl From<geoengine_datatypes::primitives::SpatialResolution> for SpatialResolution {
807
    fn from(value: geoengine_datatypes::primitives::SpatialResolution) -> Self {
55✔
808
        Self {
55✔
809
            x: value.x,
55✔
810
            y: value.y,
55✔
811
        }
55✔
812
    }
55✔
813
}
814

815
impl From<SpatialResolution> for geoengine_datatypes::primitives::SpatialResolution {
816
    fn from(value: SpatialResolution) -> Self {
5✔
817
        Self {
5✔
818
            x: value.x,
5✔
819
            y: value.y,
5✔
820
        }
5✔
821
    }
5✔
822
}
823

824
#[derive(Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, ToSchema)]
32✔
825
#[repr(C)]
826
pub struct TimeInstance(i64);
827

828
impl FromStr for TimeInstance {
829
    type Err = geoengine_datatypes::primitives::DateTimeError;
830

831
    fn from_str(s: &str) -> Result<Self, Self::Err> {
×
832
        let date_time = DateTime::from_str(s)?;
×
833
        Ok(date_time.into())
×
834
    }
×
835
}
836

837
impl From<geoengine_datatypes::primitives::TimeInstance> for TimeInstance {
838
    fn from(value: geoengine_datatypes::primitives::TimeInstance) -> Self {
236✔
839
        Self(value.inner())
236✔
840
    }
236✔
841
}
842

843
impl From<TimeInstance> for geoengine_datatypes::primitives::TimeInstance {
844
    fn from(value: TimeInstance) -> Self {
60✔
845
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(value.inner())
60✔
846
    }
60✔
847
}
848

849
impl From<DateTime> for TimeInstance {
850
    fn from(datetime: DateTime) -> Self {
×
851
        Self::from(&datetime)
×
852
    }
×
853
}
854

855
impl From<&DateTime> for TimeInstance {
856
    fn from(datetime: &DateTime) -> Self {
×
857
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(
×
858
            datetime.datetime.timestamp_millis(),
×
859
        )
×
860
        .into()
×
861
    }
×
862
}
863

864
impl TimeInstance {
865
    pub const fn inner(self) -> i64 {
60✔
866
        self.0
60✔
867
    }
60✔
868
}
869

870
impl<'de> Deserialize<'de> for TimeInstance {
871
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
16✔
872
    where
16✔
873
        D: serde::Deserializer<'de>,
16✔
874
    {
16✔
875
        struct IsoStringOrUnixTimestamp;
16✔
876

16✔
877
        impl<'de> serde::de::Visitor<'de> for IsoStringOrUnixTimestamp {
16✔
878
            type Value = TimeInstance;
16✔
879

16✔
880
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
16✔
881
                formatter.write_str("RFC 3339 timestamp string or Unix timestamp integer")
×
882
            }
×
883

16✔
884
            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
16✔
885
            where
×
886
                E: serde::de::Error,
×
887
            {
×
888
                TimeInstance::from_str(value).map_err(E::custom)
×
889
            }
×
890

16✔
891
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
16✔
892
            where
16✔
893
                E: serde::de::Error,
16✔
894
            {
16✔
895
                geoengine_datatypes::primitives::TimeInstance::from_millis(v)
16✔
896
                    .map(Into::into)
16✔
897
                    .map_err(E::custom)
16✔
898
            }
16✔
899

16✔
900
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
16✔
901
            where
16✔
902
                E: serde::de::Error,
16✔
903
            {
16✔
904
                Self::visit_i64(self, v as i64)
16✔
905
            }
16✔
906
        }
16✔
907

16✔
908
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
16✔
909
    }
16✔
910
}
911

912
/// A time granularity.
913
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
8✔
914
#[serde(rename_all = "camelCase")]
915
pub enum TimeGranularity {
916
    Millis,
917
    Seconds,
918
    Minutes,
919
    Hours,
920
    Days,
921
    Months,
922
    Years,
923
}
924

925
impl From<geoengine_datatypes::primitives::TimeGranularity> for TimeGranularity {
926
    fn from(value: geoengine_datatypes::primitives::TimeGranularity) -> Self {
4✔
927
        match value {
4✔
928
            geoengine_datatypes::primitives::TimeGranularity::Millis => Self::Millis,
×
929
            geoengine_datatypes::primitives::TimeGranularity::Seconds => Self::Seconds,
×
930
            geoengine_datatypes::primitives::TimeGranularity::Minutes => Self::Minutes,
×
931
            geoengine_datatypes::primitives::TimeGranularity::Hours => Self::Hours,
×
932
            geoengine_datatypes::primitives::TimeGranularity::Days => Self::Days,
×
933
            geoengine_datatypes::primitives::TimeGranularity::Months => Self::Months,
4✔
934
            geoengine_datatypes::primitives::TimeGranularity::Years => Self::Years,
×
935
        }
936
    }
4✔
937
}
938

939
impl From<TimeGranularity> for geoengine_datatypes::primitives::TimeGranularity {
940
    fn from(value: TimeGranularity) -> Self {
4✔
941
        match value {
4✔
942
            TimeGranularity::Millis => Self::Millis,
×
943
            TimeGranularity::Seconds => Self::Seconds,
×
944
            TimeGranularity::Minutes => Self::Minutes,
×
945
            TimeGranularity::Hours => Self::Hours,
×
946
            TimeGranularity::Days => Self::Days,
×
947
            TimeGranularity::Months => Self::Months,
4✔
948
            TimeGranularity::Years => Self::Years,
×
949
        }
950
    }
4✔
951
}
952

953
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
20✔
954
pub struct TimeStep {
955
    pub granularity: TimeGranularity,
956
    pub step: u32, // TODO: ensure on deserialization it is > 0
957
}
958

959
impl From<geoengine_datatypes::primitives::TimeStep> for TimeStep {
960
    fn from(value: geoengine_datatypes::primitives::TimeStep) -> Self {
4✔
961
        Self {
4✔
962
            granularity: value.granularity.into(),
4✔
963
            step: value.step,
4✔
964
        }
4✔
965
    }
4✔
966
}
967

968
impl From<TimeStep> for geoengine_datatypes::primitives::TimeStep {
969
    fn from(value: TimeStep) -> Self {
4✔
970
        Self {
4✔
971
            granularity: value.granularity.into(),
4✔
972
            step: value.step,
4✔
973
        }
4✔
974
    }
4✔
975
}
976

977
/// Stores time intervals in ms in close-open semantic [start, end)
978
#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
40✔
979
pub struct TimeInterval {
980
    start: TimeInstance,
981
    end: TimeInstance,
982
}
983

984
impl<'a> ToSchema<'a> for TimeInterval {
985
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
×
986
        use utoipa::openapi::*;
×
987
        (
×
988
            "TimeInterval",
×
989
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
×
990
        )
×
991
    }
×
992
}
993

994
impl From<TimeInterval> for geoengine_datatypes::primitives::TimeInterval {
995
    fn from(value: TimeInterval) -> Self {
30✔
996
        geoengine_datatypes::primitives::TimeInterval::new_unchecked::<
30✔
997
            geoengine_datatypes::primitives::TimeInstance,
30✔
998
            geoengine_datatypes::primitives::TimeInstance,
30✔
999
        >(value.start.into(), value.end.into())
30✔
1000
    }
30✔
1001
}
1002

1003
impl From<geoengine_datatypes::primitives::TimeInterval> for TimeInterval {
1004
    fn from(value: geoengine_datatypes::primitives::TimeInterval) -> Self {
110✔
1005
        Self {
110✔
1006
            start: value.start().into(),
110✔
1007
            end: value.end().into(),
110✔
1008
        }
110✔
1009
    }
110✔
1010
}
1011

1012
impl core::fmt::Debug for TimeInterval {
1013
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
×
1014
        write!(
×
1015
            f,
×
1016
            "TimeInterval [{}, {})",
×
1017
            self.start.inner(),
×
1018
            &self.end.inner()
×
1019
        )
×
1020
    }
×
1021
}
1022

1023
#[derive(
1024
    Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Deserialize, Serialize, Copy, Clone, ToSchema,
8✔
1025
)]
1026
pub enum RasterDataType {
1027
    U8,
1028
    U16,
1029
    U32,
1030
    U64,
1031
    I8,
1032
    I16,
1033
    I32,
1034
    I64,
1035
    F32,
1036
    F64,
1037
}
1038

1039
impl From<geoengine_datatypes::raster::RasterDataType> for RasterDataType {
1040
    fn from(value: geoengine_datatypes::raster::RasterDataType) -> Self {
56✔
1041
        match value {
56✔
1042
            geoengine_datatypes::raster::RasterDataType::U8 => Self::U8,
56✔
1043
            geoengine_datatypes::raster::RasterDataType::U16 => Self::U16,
×
1044
            geoengine_datatypes::raster::RasterDataType::U32 => Self::U32,
×
1045
            geoengine_datatypes::raster::RasterDataType::U64 => Self::U64,
×
1046
            geoengine_datatypes::raster::RasterDataType::I8 => Self::I8,
×
1047
            geoengine_datatypes::raster::RasterDataType::I16 => Self::I16,
×
1048
            geoengine_datatypes::raster::RasterDataType::I32 => Self::I32,
×
1049
            geoengine_datatypes::raster::RasterDataType::I64 => Self::I64,
×
1050
            geoengine_datatypes::raster::RasterDataType::F32 => Self::F32,
×
1051
            geoengine_datatypes::raster::RasterDataType::F64 => Self::F64,
×
1052
        }
1053
    }
56✔
1054
}
1055

1056
impl From<RasterDataType> for geoengine_datatypes::raster::RasterDataType {
1057
    fn from(value: RasterDataType) -> Self {
5✔
1058
        match value {
5✔
1059
            RasterDataType::U8 => Self::U8,
5✔
1060
            RasterDataType::U16 => Self::U16,
×
1061
            RasterDataType::U32 => Self::U32,
×
1062
            RasterDataType::U64 => Self::U64,
×
1063
            RasterDataType::I8 => Self::I8,
×
1064
            RasterDataType::I16 => Self::I16,
×
1065
            RasterDataType::I32 => Self::I32,
×
1066
            RasterDataType::I64 => Self::I64,
×
1067
            RasterDataType::F32 => Self::F32,
×
1068
            RasterDataType::F64 => Self::F64,
×
1069
        }
1070
    }
5✔
1071
}
1072

1073
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
×
1074
#[serde(rename_all = "UPPERCASE")]
1075
pub enum ResamplingMethod {
1076
    Nearest,
1077
    Average,
1078
    Bilinear,
1079
    Cubic,
1080
    CubicSpline,
1081
    Lanczos,
1082
}
1083

1084
impl std::fmt::Display for ResamplingMethod {
1085
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19✔
1086
        match self {
19✔
1087
            ResamplingMethod::Nearest => write!(f, "NEAREST"),
19✔
1088
            ResamplingMethod::Average => write!(f, "AVERAGE"),
×
1089
            ResamplingMethod::Bilinear => write!(f, "BILINEAR"),
×
1090
            ResamplingMethod::Cubic => write!(f, "CUBIC"),
×
1091
            ResamplingMethod::CubicSpline => write!(f, "CUBICSPLINE"),
×
1092
            ResamplingMethod::Lanczos => write!(f, "LANCZOS"),
×
1093
        }
1094
    }
19✔
1095
}
1096

1097
/// `RgbaColor` defines a 32 bit RGB color with alpha value
1098
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
258✔
1099
pub struct RgbaColor([u8; 4]);
1100

1101
// manual implementation utoipa generates an integer field
1102
impl<'a> ToSchema<'a> for RgbaColor {
1103
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
×
1104
        use utoipa::openapi::*;
×
1105
        (
×
1106
            "RgbaColor",
×
1107
            ArrayBuilder::new()
×
1108
                .items(ObjectBuilder::new().schema_type(SchemaType::Integer))
×
1109
                .min_items(Some(4))
×
1110
                .max_items(Some(4))
×
1111
                .into(),
×
1112
        )
×
1113
    }
×
1114
}
1115

1116
impl From<geoengine_datatypes::operations::image::RgbaColor> for RgbaColor {
1117
    fn from(color: geoengine_datatypes::operations::image::RgbaColor) -> Self {
324✔
1118
        Self(color.into_inner())
324✔
1119
    }
324✔
1120
}
1121

1122
impl From<RgbaColor> for geoengine_datatypes::operations::image::RgbaColor {
1123
    fn from(color: RgbaColor) -> Self {
×
1124
        Self::new(color.0[0], color.0[1], color.0[2], color.0[3])
×
1125
    }
×
1126
}
1127

1128
/// A container type for breakpoints that specify a value to color mapping
1129
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
×
1130
pub struct Breakpoint {
1131
    pub value: NotNan<f64>,
1132
    pub color: RgbaColor,
1133
}
1134

1135
// manual implementation because of NotNan
1136
impl<'a> ToSchema<'a> for Breakpoint {
1137
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
×
1138
        use utoipa::openapi::*;
×
1139
        (
×
1140
            "Breakpoint",
×
1141
            ObjectBuilder::new()
×
1142
                .property("value", Object::with_type(SchemaType::Number))
×
1143
                .property("color", Ref::from_schema_name("RgbaColor"))
×
1144
                .into(),
×
1145
        )
×
1146
    }
×
1147
}
1148

1149
impl From<geoengine_datatypes::operations::image::Breakpoint> for Breakpoint {
1150
    fn from(breakpoint: geoengine_datatypes::operations::image::Breakpoint) -> Self {
122✔
1151
        Self {
122✔
1152
            value: breakpoint.value,
122✔
1153
            color: breakpoint.color.into(),
122✔
1154
        }
122✔
1155
    }
122✔
1156
}
1157

1158
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
×
1159
#[serde(untagged, rename_all = "camelCase", into = "OverUnderColors")]
1160
pub enum DefaultColors {
1161
    #[serde(rename_all = "camelCase")]
1162
    DefaultColor { default_color: RgbaColor },
1163
    #[serde(rename_all = "camelCase")]
1164
    OverUnder(OverUnderColors),
1165
}
1166

1167
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
×
1168
#[serde(rename_all = "camelCase")]
1169
pub struct OverUnderColors {
1170
    over_color: RgbaColor,
1171
    under_color: RgbaColor,
1172
}
1173

1174
impl From<DefaultColors> for OverUnderColors {
1175
    fn from(value: DefaultColors) -> Self {
×
1176
        match value {
×
1177
            DefaultColors::DefaultColor { default_color } => Self {
×
1178
                over_color: default_color,
×
1179
                under_color: default_color,
×
1180
            },
×
1181
            DefaultColors::OverUnder(over_under) => over_under,
×
1182
        }
1183
    }
×
1184
}
1185

1186
impl From<DefaultColors> for geoengine_datatypes::operations::image::DefaultColors {
1187
    fn from(value: DefaultColors) -> Self {
×
1188
        match value {
×
1189
            DefaultColors::DefaultColor { default_color } => Self::DefaultColor {
×
1190
                default_color: default_color.into(),
×
1191
            },
×
1192
            DefaultColors::OverUnder(OverUnderColors {
1193
                over_color,
×
1194
                under_color,
×
1195
            }) => Self::OverUnder {
×
1196
                over_color: over_color.into(),
×
1197
                under_color: under_color.into(),
×
1198
            },
×
1199
        }
1200
    }
×
1201
}
1202

1203
impl From<geoengine_datatypes::operations::image::DefaultColors> for DefaultColors {
1204
    fn from(value: geoengine_datatypes::operations::image::DefaultColors) -> Self {
61✔
1205
        match value {
61✔
1206
            geoengine_datatypes::operations::image::DefaultColors::DefaultColor {
1207
                default_color,
×
1208
            } => Self::DefaultColor {
×
1209
                default_color: default_color.into(),
×
1210
            },
×
1211
            geoengine_datatypes::operations::image::DefaultColors::OverUnder {
1212
                over_color,
61✔
1213
                under_color,
61✔
1214
            } => Self::OverUnder(OverUnderColors {
61✔
1215
                over_color: over_color.into(),
61✔
1216
                under_color: under_color.into(),
61✔
1217
            }),
61✔
1218
        }
1219
    }
61✔
1220
}
1221

1222
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
×
1223
#[serde(rename_all = "camelCase")]
1224
pub struct LinearGradient {
1225
    pub breakpoints: Vec<Breakpoint>,
1226
    pub no_data_color: RgbaColor,
1227
    #[serde(flatten)]
1228
    pub color_fields: DefaultColors,
1229
}
1230

1231
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
×
1232
#[serde(rename_all = "camelCase")]
1233
pub struct LogarithmicGradient {
1234
    pub breakpoints: Vec<Breakpoint>,
1235
    pub no_data_color: RgbaColor,
1236
    #[serde(flatten)]
1237
    pub color_fields: DefaultColors,
1238
}
1239

1240
/// A colorizer specifies a mapping between raster values and an output image
1241
/// There are different variants that perform different kinds of mapping.
1242
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
29✔
1243
#[serde(rename_all = "camelCase", tag = "type")]
1244
pub enum Colorizer {
1245
    #[serde(rename_all = "camelCase")]
1246
    LinearGradient(LinearGradient),
1247
    #[serde(rename_all = "camelCase")]
1248
    LogarithmicGradient(LogarithmicGradient),
1249
    #[serde(rename_all = "camelCase")]
1250
    Palette {
1251
        colors: Palette,
1252
        no_data_color: RgbaColor,
1253
        default_color: RgbaColor,
1254
    },
1255
    Rgba,
1256
}
1257

1258
impl From<geoengine_datatypes::operations::image::Colorizer> for Colorizer {
1259
    fn from(v: geoengine_datatypes::operations::image::Colorizer) -> Self {
74✔
1260
        match v {
74✔
1261
            geoengine_datatypes::operations::image::Colorizer::LinearGradient {
1262
                breakpoints,
61✔
1263
                no_data_color,
61✔
1264
                default_colors: color_fields,
61✔
1265
            } => Self::LinearGradient(LinearGradient {
61✔
1266
                breakpoints: breakpoints
61✔
1267
                    .into_iter()
61✔
1268
                    .map(Into::into)
61✔
1269
                    .collect::<Vec<Breakpoint>>(),
61✔
1270
                no_data_color: no_data_color.into(),
61✔
1271
                color_fields: color_fields.into(),
61✔
1272
            }),
61✔
1273
            geoengine_datatypes::operations::image::Colorizer::LogarithmicGradient {
1274
                breakpoints,
×
1275
                no_data_color,
×
1276
                default_colors: color_fields,
×
1277
            } => Self::LogarithmicGradient(LogarithmicGradient {
×
1278
                breakpoints: breakpoints
×
1279
                    .into_iter()
×
1280
                    .map(Into::into)
×
1281
                    .collect::<Vec<Breakpoint>>(),
×
1282
                no_data_color: no_data_color.into(),
×
1283
                color_fields: color_fields.into(),
×
1284
            }),
×
1285
            geoengine_datatypes::operations::image::Colorizer::Palette {
1286
                colors,
1✔
1287
                no_data_color,
1✔
1288
                default_color,
1✔
1289
            } => Self::Palette {
1✔
1290
                colors: colors.into(),
1✔
1291
                no_data_color: no_data_color.into(),
1✔
1292
                default_color: default_color.into(),
1✔
1293
            },
1✔
1294
            geoengine_datatypes::operations::image::Colorizer::Rgba => Self::Rgba,
12✔
1295
        }
1296
    }
74✔
1297
}
1298

1299
/// A map from value to color
1300
///
1301
/// It is assumed that is has at least one and at most 256 entries.
1302
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
1✔
1303
#[serde(try_from = "SerializablePalette", into = "SerializablePalette")]
1304
pub struct Palette(HashMap<NotNan<f64>, RgbaColor>);
1305

1306
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1307
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
1✔
1308
        Self(
1✔
1309
            palette
1✔
1310
                .into_inner()
1✔
1311
                .into_iter()
1✔
1312
                .map(|(value, color)| (value, color.into()))
17✔
1313
                .collect(),
1✔
1314
        )
1✔
1315
    }
1✔
1316
}
1317

1318
/// A type that is solely for serde's serializability.
1319
/// You cannot serialize floats as JSON map keys.
1320
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1✔
1321
pub struct SerializablePalette(HashMap<String, RgbaColor>);
1322

1323
impl From<Palette> for SerializablePalette {
1324
    fn from(palette: Palette) -> Self {
×
1325
        Self(
×
1326
            palette
×
1327
                .0
×
1328
                .into_iter()
×
1329
                .map(|(k, v)| (k.to_string(), v))
×
1330
                .collect(),
×
1331
        )
×
1332
    }
×
1333
}
1334

1335
impl TryFrom<SerializablePalette> for Palette {
1336
    type Error = <NotNan<f64> as FromStr>::Err;
1337

1338
    fn try_from(palette: SerializablePalette) -> Result<Self, Self::Error> {
1✔
1339
        let mut inner = HashMap::<NotNan<f64>, RgbaColor>::with_capacity(palette.0.len());
1✔
1340
        for (k, v) in palette.0 {
257✔
1341
            inner.insert(k.parse()?, v);
256✔
1342
        }
1343
        Ok(Self(inner))
1✔
1344
    }
1✔
1345
}
1346

1347
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
×
1348
pub struct RasterPropertiesKey {
1349
    pub domain: Option<String>,
1350
    pub key: String,
1351
}
1352

1353
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1354
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1355
        Self {
×
1356
            domain: value.domain,
×
1357
            key: value.key,
×
1358
        }
×
1359
    }
×
1360
}
1361

1362
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1363
    fn from(value: RasterPropertiesKey) -> Self {
×
1364
        Self {
×
1365
            domain: value.domain,
×
1366
            key: value.key,
×
1367
        }
×
1368
    }
×
1369
}
1370

1371
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
×
1372
pub enum RasterPropertiesEntryType {
1373
    Number,
1374
    String,
1375
}
1376

1377
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1378
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1379
        match value {
×
1380
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1381
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1382
        }
1383
    }
×
1384
}
1385

1386
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1387
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1388
        match value {
×
1389
            RasterPropertiesEntryType::Number => Self::Number,
×
1390
            RasterPropertiesEntryType::String => Self::String,
×
1391
        }
1392
    }
×
1393
}
1394

1395
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, ToSchema)]
28✔
1396
pub struct DateTimeParseFormat {
1397
    fmt: String,
1398
    has_tz: bool,
1399
    has_time: bool,
1400
}
1401

1402
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1403
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
4✔
1404
        Self {
4✔
1405
            fmt: value._to_parse_format().to_string(),
4✔
1406
            has_tz: value.has_tz(),
4✔
1407
            has_time: value.has_time(),
4✔
1408
        }
4✔
1409
    }
4✔
1410
}
1411

1412
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1413
    fn from(value: DateTimeParseFormat) -> Self {
4✔
1414
        Self::custom(value.fmt)
4✔
1415
    }
4✔
1416
}
1417

1418
impl DateTimeParseFormat {
1419
    // this is used as default value
1420
    pub fn unix() -> Self {
×
1421
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1422
    }
×
1423
}
1424

1425
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
×
1426
pub struct NoGeometry;
1427

1428
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
1429
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
1430
        Self {}
×
1431
    }
×
1432
}
1433

1434
impl From<NoGeometry> for geoengine_datatypes::primitives::NoGeometry {
1435
    fn from(_: NoGeometry) -> Self {
×
1436
        Self {}
×
1437
    }
×
1438
}
1439

1440
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
×
1441
pub struct MultiPoint {
1442
    coordinates: Vec<Coordinate2D>,
1443
}
1444

1445
impl From<geoengine_datatypes::primitives::MultiPoint> for MultiPoint {
1446
    fn from(value: geoengine_datatypes::primitives::MultiPoint) -> Self {
×
1447
        Self {
×
1448
            coordinates: value.points().iter().map(|x| (*x).into()).collect(),
×
1449
        }
×
1450
    }
×
1451
}
1452

1453
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
1454
    fn from(value: MultiPoint) -> Self {
×
1455
        Self::new(value.coordinates.into_iter().map(Into::into).collect()).unwrap()
×
1456
    }
×
1457
}
1458

1459
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
×
1460
pub struct MultiLineString {
1461
    coordinates: Vec<Vec<Coordinate2D>>,
1462
}
1463

1464
impl From<geoengine_datatypes::primitives::MultiLineString> for MultiLineString {
1465
    fn from(value: geoengine_datatypes::primitives::MultiLineString) -> Self {
×
1466
        Self {
×
1467
            coordinates: value
×
1468
                .lines()
×
1469
                .iter()
×
1470
                .map(|x| x.iter().map(|x| (*x).into()).collect())
×
1471
                .collect(),
×
1472
        }
×
1473
    }
×
1474
}
1475

1476
impl From<MultiLineString> for geoengine_datatypes::primitives::MultiLineString {
1477
    fn from(value: MultiLineString) -> Self {
×
1478
        Self::new(
×
1479
            value
×
1480
                .coordinates
×
1481
                .into_iter()
×
1482
                .map(|x| x.into_iter().map(Into::into).collect())
×
1483
                .collect(),
×
1484
        )
×
1485
        .unwrap()
×
1486
    }
×
1487
}
1488

1489
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
×
1490
pub struct MultiPolygon {
1491
    polygons: Vec<Vec<Vec<Coordinate2D>>>,
1492
}
1493

1494
impl From<geoengine_datatypes::primitives::MultiPolygon> for MultiPolygon {
1495
    fn from(value: geoengine_datatypes::primitives::MultiPolygon) -> Self {
×
1496
        Self {
×
1497
            polygons: value
×
1498
                .polygons()
×
1499
                .iter()
×
1500
                .map(|x| {
×
1501
                    x.iter()
×
1502
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1503
                        .collect()
×
1504
                })
×
1505
                .collect(),
×
1506
        }
×
1507
    }
×
1508
}
1509

1510
impl From<MultiPolygon> for geoengine_datatypes::primitives::MultiPolygon {
1511
    fn from(value: MultiPolygon) -> Self {
×
1512
        Self::new(
×
1513
            value
×
1514
                .polygons
×
1515
                .iter()
×
1516
                .map(|x| {
×
1517
                    x.iter()
×
1518
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1519
                        .collect()
×
1520
                })
×
1521
                .collect(),
×
1522
        )
×
1523
        .unwrap()
×
1524
    }
×
1525
}
1526

1527
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
4✔
1528
pub struct StringPair((String, String));
1529

1530
impl<'a> ToSchema<'a> for StringPair {
1531
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
×
1532
        use utoipa::openapi::*;
×
1533
        (
×
1534
            "StringPair",
×
1535
            ArrayBuilder::new()
×
1536
                .items(Object::with_type(SchemaType::String))
×
1537
                .min_items(Some(2))
×
1538
                .max_items(Some(2))
×
1539
                .into(),
×
1540
        )
×
1541
    }
×
1542
}
1543

1544
impl From<(String, String)> for StringPair {
1545
    fn from(value: (String, String)) -> Self {
21✔
1546
        Self(value)
21✔
1547
    }
21✔
1548
}
1549

1550
impl From<StringPair> for (String, String) {
1551
    fn from(value: StringPair) -> Self {
×
1552
        value.0
×
1553
    }
×
1554
}
1555

1556
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
×
1557
pub enum PlotOutputFormat {
1558
    JsonPlain,
1559
    JsonVega,
1560
    ImagePng,
1561
}
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