• 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

54.92
/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 {
95✔
20
        Self(value.0)
95✔
21
    }
95✔
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 {
50✔
29
        Self(value.0)
50✔
30
    }
50✔
31
}
32

33
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
14✔
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,
29✔
49
        } = self
29✔
50
        {
51
            return Some(*dataset);
29✔
52
        }
×
53
        None
×
54
    }
29✔
55

56
    pub fn external(&self) -> Option<ExternalDataId> {
57
        if let Self::External(id) = self {
17✔
58
            return Some(id.clone());
17✔
59
        }
×
60
        None
×
61
    }
17✔
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 {
6✔
84
        Self::Internal {
6✔
85
            dataset_id: value.into(),
6✔
86
        }
6✔
87
    }
6✔
88
}
89

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

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

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

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

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

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

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

144
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
31✔
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)]
15✔
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 {
66✔
186
        match value {
66✔
187
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Epsg => Self::Epsg,
66✔
188
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::SrOrg => Self::SrOrg,
×
189
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Iau2000 => {
190
                Self::Iau2000
×
191
            }
192
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Esri => Self::Esri,
×
193
        }
194
    }
66✔
195
}
196

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

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

213
    fn from_str(s: &str) -> Result<Self, Self::Err> {
22✔
214
        Ok(match s {
22✔
215
            "EPSG" => SpatialReferenceAuthority::Epsg,
22✔
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
    }
22✔
226
}
227

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

243
/// A spatial reference consists of an authority and a code
244
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
15✔
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))
10✔
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
    }
10✔
264

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

272
impl ToSchema for SpatialReference {
273
    fn schema() -> utoipa::openapi::schema::Schema {
×
274
        use utoipa::openapi::*;
×
275
        ObjectBuilder::new().schema_type(SchemaType::String).into()
×
276
    }
×
277
}
278

279
impl From<geoengine_datatypes::spatial_reference::SpatialReference> for SpatialReference {
280
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReference) -> Self {
66✔
281
        Self {
66✔
282
            authority: (*value.authority()).into(),
66✔
283
            code: value.code(),
66✔
284
        }
66✔
285
    }
66✔
286
}
287

288
impl From<SpatialReference> for geoengine_datatypes::spatial_reference::SpatialReference {
289
    fn from(value: SpatialReference) -> Self {
8✔
290
        geoengine_datatypes::spatial_reference::SpatialReference::new(
8✔
291
            value.authority.into(),
8✔
292
            value.code,
8✔
293
        )
8✔
294
    }
8✔
295
}
296

297
impl SpatialReference {
298
    pub fn new(authority: SpatialReferenceAuthority, code: u32) -> Self {
26✔
299
        Self { authority, code }
26✔
300
    }
26✔
301

302
    pub fn authority(&self) -> &SpatialReferenceAuthority {
1✔
303
        &self.authority
1✔
304
    }
1✔
305

306
    pub fn code(self) -> u32 {
1✔
307
        self.code
1✔
308
    }
1✔
309
}
310

311
impl FromStr for SpatialReference {
312
    type Err = error::Error;
313

314
    fn from_str(s: &str) -> Result<Self, Self::Err> {
22✔
315
        let mut split = s.split(':');
22✔
316

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

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

335
impl Serialize for SpatialReference {
336
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
×
337
    where
×
338
        S: Serializer,
×
339
    {
×
340
        serializer.serialize_str(&self.to_string())
×
341
    }
×
342
}
343

344
/// Helper struct for deserializing a `SpatialReferencce`
345
struct SpatialReferenceDeserializeVisitor;
346

347
impl<'de> Visitor<'de> for SpatialReferenceDeserializeVisitor {
348
    type Value = SpatialReference;
349

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

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

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

371
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ToSchema)]
12✔
372
pub enum SpatialReferenceOption {
373
    SpatialReference(SpatialReference),
374
    Unreferenced,
375
}
376

377
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceOption>
378
    for SpatialReferenceOption
379
{
380
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceOption) -> Self {
76✔
381
        match value {
76✔
382
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::SpatialReference(s) => {
64✔
383
                Self::SpatialReference(s.into())
64✔
384
            }
385
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::Unreferenced => {
386
                Self::Unreferenced
12✔
387
            }
388
        }
389
    }
76✔
390
}
391

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

403
impl From<SpatialReference> for SpatialReferenceOption {
404
    fn from(spatial_reference: SpatialReference) -> Self {
5✔
405
        Self::SpatialReference(spatial_reference)
5✔
406
    }
5✔
407
}
408

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

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

427
/// Helper struct for deserializing a `SpatialReferenceOption`
428
struct SpatialReferenceOptionDeserializeVisitor;
429

430
impl<'de> Visitor<'de> for SpatialReferenceOptionDeserializeVisitor {
431
    type Value = SpatialReferenceOption;
432

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

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

445
        let spatial_reference: SpatialReference = v.parse().map_err(serde::de::Error::custom)?;
5✔
446

447
        Ok(spatial_reference.into())
5✔
448
    }
10✔
449
}
450

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

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

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

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

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

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

513
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize, Default, ToSchema)]
30✔
514
pub struct Coordinate2D {
515
    pub x: f64,
516
    pub y: f64,
517
}
518

519
impl From<geoengine_datatypes::primitives::Coordinate2D> for Coordinate2D {
520
    fn from(coordinate: geoengine_datatypes::primitives::Coordinate2D) -> Self {
104✔
521
        Self {
104✔
522
            x: coordinate.x,
104✔
523
            y: coordinate.y,
104✔
524
        }
104✔
525
    }
104✔
526
}
527

528
impl From<Coordinate2D> for geoengine_datatypes::primitives::Coordinate2D {
529
    fn from(coordinate: Coordinate2D) -> Self {
9✔
530
        Self {
9✔
531
            x: coordinate.x,
9✔
532
            y: coordinate.y,
9✔
533
        }
9✔
534
    }
9✔
535
}
536

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

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

557
impl From<BoundingBox2D> for geoengine_datatypes::primitives::BoundingBox2D {
558
    fn from(bbox: BoundingBox2D) -> Self {
×
559
        Self::new_unchecked(
×
560
            bbox.lower_left_coordinate.into(),
×
561
            bbox.upper_right_coordinate.into(),
×
562
        )
×
563
    }
×
564
}
565

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

572
impl FromStr for DateTime {
573
    type Err = geoengine_datatypes::primitives::DateTimeError;
574

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

582
        Ok(date_time.into())
×
583
    }
×
584
}
585

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

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

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

618
impl From<FeatureDataType> for geoengine_datatypes::primitives::FeatureDataType {
619
    fn from(value: FeatureDataType) -> Self {
10✔
620
        match value {
10✔
621
            FeatureDataType::Category => Self::Category,
×
622
            FeatureDataType::Int => Self::Int,
2✔
623
            FeatureDataType::Float => Self::Float,
2✔
624
            FeatureDataType::Text => Self::Text,
6✔
625
            FeatureDataType::Bool => Self::Bool,
×
626
            FeatureDataType::DateTime => Self::DateTime,
×
627
        }
628
    }
10✔
629
}
630

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

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

653
impl From<Measurement> for geoengine_datatypes::primitives::Measurement {
654
    fn from(value: Measurement) -> Self {
13✔
655
        match value {
13✔
656
            Measurement::Unitless => Self::Unitless,
12✔
657
            Measurement::Continuous(cm) => Self::Continuous(cm.into()),
×
658
            Measurement::Classification(cm) => Self::Classification(cm.into()),
1✔
659
        }
660
    }
13✔
661
}
662

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

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

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

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

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

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

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

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

741
impl TryFrom<SerializableClassificationMeasurement> for ClassificationMeasurement {
742
    type Error = <u8 as FromStr>::Err;
743

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

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

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

773
impl From<SpatialPartition2D> for geoengine_datatypes::primitives::SpatialPartition2D {
774
    fn from(value: SpatialPartition2D) -> Self {
3✔
775
        Self::new_unchecked(
3✔
776
            value.upper_left_coordinate.into(),
3✔
777
            value.lower_right_coordinate.into(),
3✔
778
        )
3✔
779
    }
3✔
780
}
781

782
/// A spatio-temporal rectangle with a specified resolution
783
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
×
784
#[serde(rename_all = "camelCase")]
785
pub struct QueryRectangle<SpatialBounds> {
786
    pub spatial_bounds: SpatialBounds,
787
    pub time_interval: TimeInterval,
788
    pub spatial_resolution: SpatialResolution,
789
}
790

791
pub type VectorQueryRectangle = QueryRectangle<BoundingBox2D>;
792
pub type RasterQueryRectangle = QueryRectangle<SpatialPartition2D>;
793
pub type PlotQueryRectangle = QueryRectangle<BoundingBox2D>;
794

795
// manual implementation, because derivation can't handle the `SpatialBounds` generic (yet)
796
impl ToSchema for QueryRectangle<SpatialPartition2D> {
797
    fn schema() -> utoipa::openapi::Schema {
×
798
        use utoipa::openapi::*;
×
799
        ObjectBuilder::new()
×
800
            .property("spatialBounds", Ref::from_schema_name("SpatialPartition2D"))
×
801
            .required("spatialBounds")
×
802
            .property("timeInterval", Ref::from_schema_name("TimeInterval"))
×
803
            .required("timeInterval")
×
804
            .property(
×
805
                "spatialResolution",
×
806
                Ref::from_schema_name("SpatialResolution"),
×
807
            )
×
808
            .required("spatialResolution")
×
809
            .description(Some(
×
810
                "A spatio-temporal rectangle with a specified resolution",
×
811
            ))
×
812
            .into()
×
813
    }
×
814
}
815

816
/// manual implementation, because derivation can't handle the `SpatialBounds` generic (yet)
817
impl ToSchema for QueryRectangle<BoundingBox2D> {
818
    fn schema() -> utoipa::openapi::Schema {
×
819
        use utoipa::openapi::*;
×
820
        ObjectBuilder::new()
×
821
            .property("spatialBounds", Ref::from_schema_name("BoundingBox2D"))
×
822
            .required("spatialBounds")
×
823
            .property("timeInterval", Ref::from_schema_name("TimeInterval"))
×
824
            .required("timeInterval")
×
825
            .property(
×
826
                "spatialResolution",
×
827
                Ref::from_schema_name("SpatialResolution"),
×
828
            )
×
829
            .required("spatialResolution")
×
830
            .description(Some(
×
831
                "A spatio-temporal rectangle with a specified resolution",
×
832
            ))
×
833
            .into()
×
834
    }
×
835
}
836

837
/// The spatial resolution in SRS units
838
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema)]
10✔
839
pub struct SpatialResolution {
840
    pub x: f64,
841
    pub y: f64,
842
}
843

844
impl From<geoengine_datatypes::primitives::SpatialResolution> for SpatialResolution {
845
    fn from(value: geoengine_datatypes::primitives::SpatialResolution) -> Self {
49✔
846
        Self {
49✔
847
            x: value.x,
49✔
848
            y: value.y,
49✔
849
        }
49✔
850
    }
49✔
851
}
852

853
impl From<SpatialResolution> for geoengine_datatypes::primitives::SpatialResolution {
854
    fn from(value: SpatialResolution) -> Self {
3✔
855
        Self {
3✔
856
            x: value.x,
3✔
857
            y: value.y,
3✔
858
        }
3✔
859
    }
3✔
860
}
861

862
#[derive(Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, ToSchema)]
32✔
863
#[repr(C)]
864
pub struct TimeInstance(i64);
865

866
impl FromStr for TimeInstance {
867
    type Err = geoengine_datatypes::primitives::DateTimeError;
868

869
    fn from_str(s: &str) -> Result<Self, Self::Err> {
×
870
        let date_time = DateTime::from_str(s)?;
×
871
        Ok(date_time.into())
×
872
    }
×
873
}
874

875
impl From<geoengine_datatypes::primitives::TimeInstance> for TimeInstance {
876
    fn from(value: geoengine_datatypes::primitives::TimeInstance) -> Self {
208✔
877
        Self(value.inner())
208✔
878
    }
208✔
879
}
880

881
impl From<TimeInstance> for geoengine_datatypes::primitives::TimeInstance {
882
    fn from(value: TimeInstance) -> Self {
44✔
883
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(value.inner())
44✔
884
    }
44✔
885
}
886

887
impl From<DateTime> for TimeInstance {
888
    fn from(datetime: DateTime) -> Self {
×
889
        Self::from(&datetime)
×
890
    }
×
891
}
892

893
impl From<&DateTime> for TimeInstance {
894
    fn from(datetime: &DateTime) -> Self {
×
895
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(
×
896
            datetime.datetime.timestamp_millis(),
×
897
        )
×
898
        .into()
×
899
    }
×
900
}
901

902
impl TimeInstance {
903
    pub const fn inner(self) -> i64 {
44✔
904
        self.0
44✔
905
    }
44✔
906
}
907

908
impl<'de> Deserialize<'de> for TimeInstance {
909
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
8✔
910
    where
8✔
911
        D: serde::Deserializer<'de>,
8✔
912
    {
8✔
913
        struct IsoStringOrUnixTimestamp;
8✔
914

8✔
915
        impl<'de> serde::de::Visitor<'de> for IsoStringOrUnixTimestamp {
8✔
916
            type Value = TimeInstance;
8✔
917

8✔
918
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
8✔
919
                formatter.write_str("RFC 3339 timestamp string or Unix timestamp integer")
×
920
            }
×
921

8✔
922
            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
8✔
923
            where
×
924
                E: serde::de::Error,
×
925
            {
×
926
                TimeInstance::from_str(value).map_err(E::custom)
×
927
            }
×
928

8✔
929
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
8✔
930
            where
8✔
931
                E: serde::de::Error,
8✔
932
            {
8✔
933
                geoengine_datatypes::primitives::TimeInstance::from_millis(v)
8✔
934
                    .map(Into::into)
8✔
935
                    .map_err(E::custom)
8✔
936
            }
8✔
937

8✔
938
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
8✔
939
            where
8✔
940
                E: serde::de::Error,
8✔
941
            {
8✔
942
                Self::visit_i64(self, v as i64)
8✔
943
            }
8✔
944
        }
8✔
945

8✔
946
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
8✔
947
    }
8✔
948
}
949

950
/// A time granularity.
951
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
4✔
952
#[serde(rename_all = "camelCase")]
953
pub enum TimeGranularity {
954
    Millis,
955
    Seconds,
956
    Minutes,
957
    Hours,
958
    Days,
959
    Months,
960
    Years,
961
}
962

963
impl From<geoengine_datatypes::primitives::TimeGranularity> for TimeGranularity {
964
    fn from(value: geoengine_datatypes::primitives::TimeGranularity) -> Self {
2✔
965
        match value {
2✔
966
            geoengine_datatypes::primitives::TimeGranularity::Millis => Self::Millis,
×
967
            geoengine_datatypes::primitives::TimeGranularity::Seconds => Self::Seconds,
×
968
            geoengine_datatypes::primitives::TimeGranularity::Minutes => Self::Minutes,
×
969
            geoengine_datatypes::primitives::TimeGranularity::Hours => Self::Hours,
×
970
            geoengine_datatypes::primitives::TimeGranularity::Days => Self::Days,
×
971
            geoengine_datatypes::primitives::TimeGranularity::Months => Self::Months,
2✔
972
            geoengine_datatypes::primitives::TimeGranularity::Years => Self::Years,
×
973
        }
974
    }
2✔
975
}
976

977
impl From<TimeGranularity> for geoengine_datatypes::primitives::TimeGranularity {
978
    fn from(value: TimeGranularity) -> Self {
2✔
979
        match value {
2✔
980
            TimeGranularity::Millis => Self::Millis,
×
981
            TimeGranularity::Seconds => Self::Seconds,
×
982
            TimeGranularity::Minutes => Self::Minutes,
×
983
            TimeGranularity::Hours => Self::Hours,
×
984
            TimeGranularity::Days => Self::Days,
×
985
            TimeGranularity::Months => Self::Months,
2✔
986
            TimeGranularity::Years => Self::Years,
×
987
        }
988
    }
2✔
989
}
990

991
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
10✔
992
pub struct TimeStep {
993
    pub granularity: TimeGranularity,
994
    pub step: u32, // TODO: ensure on deserialization it is > 0
995
}
996

997
impl From<geoengine_datatypes::primitives::TimeStep> for TimeStep {
998
    fn from(value: geoengine_datatypes::primitives::TimeStep) -> Self {
2✔
999
        Self {
2✔
1000
            granularity: value.granularity.into(),
2✔
1001
            step: value.step,
2✔
1002
        }
2✔
1003
    }
2✔
1004
}
1005

1006
impl From<TimeStep> for geoengine_datatypes::primitives::TimeStep {
1007
    fn from(value: TimeStep) -> Self {
2✔
1008
        Self {
2✔
1009
            granularity: value.granularity.into(),
2✔
1010
            step: value.step,
2✔
1011
        }
2✔
1012
    }
2✔
1013
}
1014

1015
/// Stores time intervals in ms in close-open semantic [start, end)
1016
#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
20✔
1017
pub struct TimeInterval {
1018
    start: TimeInstance,
1019
    end: TimeInstance,
1020
}
1021

1022
impl ToSchema for TimeInterval {
1023
    fn schema() -> utoipa::openapi::schema::Schema {
×
1024
        use utoipa::openapi::*;
×
1025
        ObjectBuilder::new().schema_type(SchemaType::String).into()
×
1026
    }
×
1027
}
1028

1029
impl From<TimeInterval> for geoengine_datatypes::primitives::TimeInterval {
1030
    fn from(value: TimeInterval) -> Self {
22✔
1031
        geoengine_datatypes::primitives::TimeInterval::new_unchecked::<
22✔
1032
            geoengine_datatypes::primitives::TimeInstance,
22✔
1033
            geoengine_datatypes::primitives::TimeInstance,
22✔
1034
        >(value.start.into(), value.end.into())
22✔
1035
    }
22✔
1036
}
1037

1038
impl From<geoengine_datatypes::primitives::TimeInterval> for TimeInterval {
1039
    fn from(value: geoengine_datatypes::primitives::TimeInterval) -> Self {
100✔
1040
        Self {
100✔
1041
            start: value.start().into(),
100✔
1042
            end: value.end().into(),
100✔
1043
        }
100✔
1044
    }
100✔
1045
}
1046

1047
impl core::fmt::Debug for TimeInterval {
1048
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
×
1049
        write!(
×
1050
            f,
×
1051
            "TimeInterval [{}, {})",
×
1052
            self.start.inner(),
×
1053
            &self.end.inner()
×
1054
        )
×
1055
    }
×
1056
}
1057

1058
#[derive(
1059
    Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Deserialize, Serialize, Copy, Clone, ToSchema,
5✔
1060
)]
1061
pub enum RasterDataType {
1062
    U8,
1063
    U16,
1064
    U32,
1065
    U64,
1066
    I8,
1067
    I16,
1068
    I32,
1069
    I64,
1070
    F32,
1071
    F64,
1072
}
1073

1074
impl From<geoengine_datatypes::raster::RasterDataType> for RasterDataType {
1075
    fn from(value: geoengine_datatypes::raster::RasterDataType) -> Self {
50✔
1076
        match value {
50✔
1077
            geoengine_datatypes::raster::RasterDataType::U8 => Self::U8,
50✔
1078
            geoengine_datatypes::raster::RasterDataType::U16 => Self::U16,
×
1079
            geoengine_datatypes::raster::RasterDataType::U32 => Self::U32,
×
1080
            geoengine_datatypes::raster::RasterDataType::U64 => Self::U64,
×
1081
            geoengine_datatypes::raster::RasterDataType::I8 => Self::I8,
×
1082
            geoengine_datatypes::raster::RasterDataType::I16 => Self::I16,
×
1083
            geoengine_datatypes::raster::RasterDataType::I32 => Self::I32,
×
1084
            geoengine_datatypes::raster::RasterDataType::I64 => Self::I64,
×
1085
            geoengine_datatypes::raster::RasterDataType::F32 => Self::F32,
×
1086
            geoengine_datatypes::raster::RasterDataType::F64 => Self::F64,
×
1087
        }
1088
    }
50✔
1089
}
1090

1091
impl From<RasterDataType> for geoengine_datatypes::raster::RasterDataType {
1092
    fn from(value: RasterDataType) -> Self {
3✔
1093
        match value {
3✔
1094
            RasterDataType::U8 => Self::U8,
3✔
1095
            RasterDataType::U16 => Self::U16,
×
1096
            RasterDataType::U32 => Self::U32,
×
1097
            RasterDataType::U64 => Self::U64,
×
1098
            RasterDataType::I8 => Self::I8,
×
1099
            RasterDataType::I16 => Self::I16,
×
1100
            RasterDataType::I32 => Self::I32,
×
1101
            RasterDataType::I64 => Self::I64,
×
1102
            RasterDataType::F32 => Self::F32,
×
1103
            RasterDataType::F64 => Self::F64,
×
1104
        }
1105
    }
3✔
1106
}
1107

1108
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
×
1109
#[serde(rename_all = "UPPERCASE")]
1110
pub enum ResamplingMethod {
1111
    Nearest,
1112
    Average,
1113
    Bilinear,
1114
    Cubic,
1115
    CubicSpline,
1116
    Lanczos,
1117
}
1118

1119
impl std::fmt::Display for ResamplingMethod {
1120
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19✔
1121
        match self {
19✔
1122
            ResamplingMethod::Nearest => write!(f, "NEAREST"),
19✔
1123
            ResamplingMethod::Average => write!(f, "AVERAGE"),
×
1124
            ResamplingMethod::Bilinear => write!(f, "BILINEAR"),
×
1125
            ResamplingMethod::Cubic => write!(f, "CUBIC"),
×
1126
            ResamplingMethod::CubicSpline => write!(f, "CUBICSPLINE"),
×
1127
            ResamplingMethod::Lanczos => write!(f, "LANCZOS"),
×
1128
        }
1129
    }
19✔
1130
}
1131

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

1136
// manual implementation utoipa generates an integer field
1137
impl ToSchema for RgbaColor {
1138
    fn schema() -> utoipa::openapi::schema::Schema {
×
1139
        use utoipa::openapi::*;
×
1140
        ArrayBuilder::new()
×
1141
            .items(ObjectBuilder::new().schema_type(SchemaType::Integer))
×
1142
            .min_items(Some(4))
×
1143
            .max_items(Some(4))
×
1144
            .into()
×
1145
    }
×
1146
}
1147

1148
impl From<geoengine_datatypes::operations::image::RgbaColor> for RgbaColor {
1149
    fn from(color: geoengine_datatypes::operations::image::RgbaColor) -> Self {
263✔
1150
        Self(color.into_inner())
263✔
1151
    }
263✔
1152
}
1153

1154
/// A container type for breakpoints that specify a value to color mapping
1155
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
×
1156
pub struct Breakpoint {
1157
    pub value: NotNan<f64>,
1158
    pub color: RgbaColor,
1159
}
1160

1161
// manual implementation because of NotNan
1162
impl ToSchema for Breakpoint {
1163
    fn schema() -> utoipa::openapi::schema::Schema {
×
1164
        use utoipa::openapi::*;
×
1165
        ObjectBuilder::new()
×
1166
            .property(
×
1167
                "value",
×
1168
                ObjectBuilder::new().schema_type(SchemaType::Number),
×
1169
            )
×
1170
            .property("color", Ref::from_schema_name("RgbaColor"))
×
1171
            .into()
×
1172
    }
×
1173
}
1174

1175
impl From<geoengine_datatypes::operations::image::Breakpoint> for Breakpoint {
1176
    fn from(breakpoint: geoengine_datatypes::operations::image::Breakpoint) -> Self {
122✔
1177
        Self {
122✔
1178
            value: breakpoint.value,
122✔
1179
            color: breakpoint.color.into(),
122✔
1180
        }
122✔
1181
    }
122✔
1182
}
1183

1184
/// A colorizer specifies a mapping between raster values and an output image
1185
/// There are different variants that perform different kinds of mapping.
1186
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
29✔
1187
#[serde(rename_all = "camelCase", tag = "type")]
1188
pub enum Colorizer {
1189
    #[serde(rename_all = "camelCase")]
1190
    LinearGradient {
1191
        breakpoints: Vec<Breakpoint>,
1192
        no_data_color: RgbaColor,
1193
        default_color: RgbaColor,
1194
    },
1195
    #[serde(rename_all = "camelCase")]
1196
    LogarithmicGradient {
1197
        breakpoints: Vec<Breakpoint>,
1198
        no_data_color: RgbaColor,
1199
        default_color: RgbaColor,
1200
    },
1201
    #[serde(rename_all = "camelCase")]
1202
    Palette {
1203
        colors: Palette,
1204
        no_data_color: RgbaColor,
1205
        default_color: RgbaColor,
1206
    },
1207
    Rgba,
1208
}
1209

1210
impl From<geoengine_datatypes::operations::image::Colorizer> for Colorizer {
1211
    fn from(v: geoengine_datatypes::operations::image::Colorizer) -> Self {
74✔
1212
        match v {
74✔
1213
            geoengine_datatypes::operations::image::Colorizer::LinearGradient {
1214
                breakpoints,
61✔
1215
                no_data_color,
61✔
1216
                default_color,
61✔
1217
            } => Self::LinearGradient {
61✔
1218
                breakpoints: breakpoints
61✔
1219
                    .into_iter()
61✔
1220
                    .map(Into::into)
61✔
1221
                    .collect::<Vec<Breakpoint>>(),
61✔
1222
                no_data_color: no_data_color.into(),
61✔
1223
                default_color: default_color.into(),
61✔
1224
            },
61✔
1225
            geoengine_datatypes::operations::image::Colorizer::LogarithmicGradient {
1226
                breakpoints,
×
1227
                no_data_color,
×
1228
                default_color,
×
1229
            } => Self::LogarithmicGradient {
×
1230
                breakpoints: breakpoints
×
1231
                    .into_iter()
×
1232
                    .map(Into::into)
×
1233
                    .collect::<Vec<Breakpoint>>(),
×
1234
                no_data_color: no_data_color.into(),
×
1235
                default_color: default_color.into(),
×
1236
            },
×
1237
            geoengine_datatypes::operations::image::Colorizer::Palette {
1238
                colors,
1✔
1239
                no_data_color,
1✔
1240
                default_color,
1✔
1241
            } => Self::Palette {
1✔
1242
                colors: colors.into(),
1✔
1243
                no_data_color: no_data_color.into(),
1✔
1244
                default_color: default_color.into(),
1✔
1245
            },
1✔
1246
            geoengine_datatypes::operations::image::Colorizer::Rgba => Self::Rgba,
12✔
1247
        }
1248
    }
74✔
1249
}
1250

1251
/// A map from value to color
1252
///
1253
/// It is assumed that is has at least one and at most 256 entries.
1254
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
1✔
1255
#[serde(try_from = "SerializablePalette", into = "SerializablePalette")]
1256
pub struct Palette(HashMap<NotNan<f64>, RgbaColor>);
1257

1258
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1259
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
1✔
1260
        Self(
1✔
1261
            palette
1✔
1262
                .into_inner()
1✔
1263
                .into_iter()
1✔
1264
                .map(|(value, color)| (value, color.into()))
17✔
1265
                .collect(),
1✔
1266
        )
1✔
1267
    }
1✔
1268
}
1269

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

1275
impl From<Palette> for SerializablePalette {
1276
    fn from(palette: Palette) -> Self {
×
1277
        Self(
×
1278
            palette
×
1279
                .0
×
1280
                .into_iter()
×
1281
                .map(|(k, v)| (k.to_string(), v))
×
1282
                .collect(),
×
1283
        )
×
1284
    }
×
1285
}
1286

1287
impl TryFrom<SerializablePalette> for Palette {
1288
    type Error = <NotNan<f64> as FromStr>::Err;
1289

1290
    fn try_from(palette: SerializablePalette) -> Result<Self, Self::Error> {
1✔
1291
        let mut inner = HashMap::<NotNan<f64>, RgbaColor>::with_capacity(palette.0.len());
1✔
1292
        for (k, v) in palette.0 {
257✔
1293
            inner.insert(k.parse()?, v);
256✔
1294
        }
1295
        Ok(Self(inner))
1✔
1296
    }
1✔
1297
}
1298

1299
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
×
1300
pub struct RasterPropertiesKey {
1301
    pub domain: Option<String>,
1302
    pub key: String,
1303
}
1304

1305
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1306
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1307
        Self {
×
1308
            domain: value.domain,
×
1309
            key: value.key,
×
1310
        }
×
1311
    }
×
1312
}
1313

1314
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1315
    fn from(value: RasterPropertiesKey) -> Self {
×
1316
        Self {
×
1317
            domain: value.domain,
×
1318
            key: value.key,
×
1319
        }
×
1320
    }
×
1321
}
1322

1323
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
×
1324
pub enum RasterPropertiesEntryType {
1325
    Number,
1326
    String,
1327
}
1328

1329
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1330
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1331
        match value {
×
1332
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1333
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1334
        }
1335
    }
×
1336
}
1337

1338
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1339
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1340
        match value {
×
1341
            RasterPropertiesEntryType::Number => Self::Number,
×
1342
            RasterPropertiesEntryType::String => Self::String,
×
1343
        }
1344
    }
×
1345
}
1346

1347
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, ToSchema)]
14✔
1348
pub struct DateTimeParseFormat {
1349
    fmt: String,
1350
    has_tz: bool,
1351
    has_time: bool,
1352
}
1353

1354
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1355
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
2✔
1356
        Self {
2✔
1357
            fmt: value._to_parse_format().to_string(),
2✔
1358
            has_tz: value.has_tz(),
2✔
1359
            has_time: value.has_time(),
2✔
1360
        }
2✔
1361
    }
2✔
1362
}
1363

1364
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1365
    fn from(value: DateTimeParseFormat) -> Self {
2✔
1366
        Self::custom(value.fmt)
2✔
1367
    }
2✔
1368
}
1369

1370
impl DateTimeParseFormat {
1371
    // this is used as default value
1372
    pub fn unix() -> Self {
×
1373
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1374
    }
×
1375
}
1376

1377
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
×
1378
pub struct NoGeometry;
1379

1380
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
1381
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
1382
        Self {}
×
1383
    }
×
1384
}
1385

1386
impl From<NoGeometry> for geoengine_datatypes::primitives::NoGeometry {
1387
    fn from(_: NoGeometry) -> Self {
×
1388
        Self {}
×
1389
    }
×
1390
}
1391

1392
impl ToSchema for NoGeometry {
1393
    fn schema() -> utoipa::openapi::Schema {
×
1394
        use utoipa::openapi::*;
×
1395
        ObjectBuilder::new()
×
1396
            .default(Some(serde_json::Value::Null))
×
1397
            .example(Some(serde_json::Value::Null))
×
1398
            .into()
×
1399
    }
×
1400
}
1401

1402
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
×
1403
pub struct MultiPoint {
1404
    coordinates: Vec<Coordinate2D>,
1405
}
1406

1407
impl From<geoengine_datatypes::primitives::MultiPoint> for MultiPoint {
1408
    fn from(value: geoengine_datatypes::primitives::MultiPoint) -> Self {
×
1409
        Self {
×
1410
            coordinates: value.points().iter().map(|x| (*x).into()).collect(),
×
1411
        }
×
1412
    }
×
1413
}
1414

1415
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
1416
    fn from(value: MultiPoint) -> Self {
×
1417
        Self::new(value.coordinates.into_iter().map(Into::into).collect()).unwrap()
×
1418
    }
×
1419
}
1420

1421
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
×
1422
pub struct MultiLineString {
1423
    coordinates: Vec<Vec<Coordinate2D>>,
1424
}
1425

1426
impl From<geoengine_datatypes::primitives::MultiLineString> for MultiLineString {
1427
    fn from(value: geoengine_datatypes::primitives::MultiLineString) -> Self {
×
1428
        Self {
×
1429
            coordinates: value
×
1430
                .lines()
×
1431
                .iter()
×
1432
                .map(|x| x.iter().map(|x| (*x).into()).collect())
×
1433
                .collect(),
×
1434
        }
×
1435
    }
×
1436
}
1437

1438
impl From<MultiLineString> for geoengine_datatypes::primitives::MultiLineString {
1439
    fn from(value: MultiLineString) -> Self {
×
1440
        Self::new(
×
1441
            value
×
1442
                .coordinates
×
1443
                .into_iter()
×
1444
                .map(|x| x.into_iter().map(Into::into).collect())
×
1445
                .collect(),
×
1446
        )
×
1447
        .unwrap()
×
1448
    }
×
1449
}
1450

1451
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
×
1452
pub struct MultiPolygon {
1453
    polygons: Vec<Vec<Vec<Coordinate2D>>>,
1454
}
1455

1456
impl From<geoengine_datatypes::primitives::MultiPolygon> for MultiPolygon {
1457
    fn from(value: geoengine_datatypes::primitives::MultiPolygon) -> Self {
×
1458
        Self {
×
1459
            polygons: value
×
1460
                .polygons()
×
1461
                .iter()
×
1462
                .map(|x| {
×
1463
                    x.iter()
×
1464
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1465
                        .collect()
×
1466
                })
×
1467
                .collect(),
×
1468
        }
×
1469
    }
×
1470
}
1471

1472
impl From<MultiPolygon> for geoengine_datatypes::primitives::MultiPolygon {
1473
    fn from(value: MultiPolygon) -> Self {
×
1474
        Self::new(
×
1475
            value
×
1476
                .polygons
×
1477
                .iter()
×
1478
                .map(|x| {
×
1479
                    x.iter()
×
1480
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1481
                        .collect()
×
1482
                })
×
1483
                .collect(),
×
1484
        )
×
1485
        .unwrap()
×
1486
    }
×
1487
}
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