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

geo-engine / geoengine / 3601061012

pending completion
3601061012

push

github

GitHub
Merge #662

593 of 593 new or added lines in 7 files covered. (100.0%)

41177 of 48949 relevant lines covered (84.12%)

1.97 hits per line

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

41.93
/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);
3✔
17

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

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

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

33
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
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> {
1✔
47
        if let Self::Internal {
1✔
48
            dataset_id: dataset,
1✔
49
        } = self
50
        {
51
            return Some(*dataset);
1✔
52
        }
53
        None
×
54
    }
55

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

64
impl From<DatasetId> for DataId {
65
    fn from(value: DatasetId) -> Self {
1✔
66
        Self::Internal { dataset_id: value }
67
    }
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 {
1✔
78
        Self::External(value.into())
1✔
79
    }
80
}
81

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

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

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

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

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

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

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

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

144
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
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 {
1✔
153
        Self {
154
            provider_id: id.provider_id.into(),
1✔
155
            layer_id: id.layer_id.into(),
1✔
156
        }
157
    }
158
}
159

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

166
impl From<geoengine_datatypes::dataset::LayerId> for LayerId {
167
    fn from(id: geoengine_datatypes::dataset::LayerId) -> Self {
1✔
168
        Self(id.0)
1✔
169
    }
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)]
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 {
1✔
186
        match value {
1✔
187
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Epsg => Self::Epsg,
1✔
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
    }
195
}
196

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

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

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

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

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

250
impl SpatialReference {
251
    pub fn proj_string(self) -> Result<String> {
1✔
252
        match self.authority {
1✔
253
            SpatialReferenceAuthority::Epsg | SpatialReferenceAuthority::Iau2000 => {
254
                Ok(format!("{}:{}", self.authority, self.code))
2✔
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
    }
264

265
    /// Return the srs-string "authority:code"
266
    #[allow(clippy::trivially_copy_pass_by_ref)]
267
    pub fn srs_string(&self) -> String {
1✔
268
        format!("{}:{}", self.authority, self.code)
1✔
269
    }
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 {
1✔
281
        Self {
282
            authority: (*value.authority()).into(),
1✔
283
            code: value.code(),
1✔
284
        }
285
    }
286
}
287

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

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

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

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

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

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

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

329
impl std::fmt::Display for SpatialReference {
330
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1✔
331
        write!(f, "{}:{}", self.authority, self.code)
1✔
332
    }
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>
1✔
355
    where
356
        E: serde::de::Error,
357
    {
358
        v.parse().map_err(serde::de::Error::custom)
1✔
359
    }
360
}
361

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

371
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ToSchema)]
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 {
1✔
381
        match value {
1✔
382
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::SpatialReference(s) => {
1✔
383
                Self::SpatialReference(s.into())
2✔
384
            }
385
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::Unreferenced => {
386
                Self::Unreferenced
1✔
387
            }
388
        }
389
    }
390
}
391

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

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

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

418
impl Serialize for SpatialReferenceOption {
419
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
1✔
420
    where
421
        S: Serializer,
422
    {
423
        serializer.serialize_str(&self.to_string())
1✔
424
    }
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>
1✔
438
    where
439
        E: serde::de::Error,
440
    {
441
        if v.is_empty() {
1✔
442
            return Ok(SpatialReferenceOption::Unreferenced);
1✔
443
        }
444

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

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

451
impl<'de> Deserialize<'de> for SpatialReferenceOption {
452
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
1✔
453
    where
454
        D: Deserializer<'de>,
455
    {
456
        deserializer.deserialize_str(SpatialReferenceOptionDeserializeVisitor)
1✔
457
    }
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 {
1✔
471
        match option {
1✔
472
            SpatialReferenceOption::SpatialReference(p) => Some(p),
1✔
473
            SpatialReferenceOption::Unreferenced => None,
×
474
        }
475
    }
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,
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 {
1✔
491
        match value {
1✔
492
            geoengine_datatypes::collections::VectorDataType::Data => Self::Data,
1✔
493
            geoengine_datatypes::collections::VectorDataType::MultiPoint => Self::MultiPoint,
1✔
494
            geoengine_datatypes::collections::VectorDataType::MultiLineString => {
495
                Self::MultiLineString
×
496
            }
497
            geoengine_datatypes::collections::VectorDataType::MultiPolygon => Self::MultiPolygon,
×
498
        }
499
    }
500
}
501

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

513
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize, Default, ToSchema)]
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 {
1✔
521
        Self {
522
            x: coordinate.x,
523
            y: coordinate.y,
524
        }
525
    }
526
}
527

528
impl From<Coordinate2D> for geoengine_datatypes::primitives::Coordinate2D {
529
    fn from(coordinate: Coordinate2D) -> Self {
×
530
        Self {
531
            x: coordinate.x,
532
            y: coordinate.y,
533
        }
534
    }
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 {
549
            lower_left_coordinate:
550
                geoengine_datatypes::primitives::AxisAlignedRectangle::lower_left(&bbox).into(),
551
            upper_right_coordinate:
552
                geoengine_datatypes::primitives::AxisAlignedRectangle::upper_right(&bbox).into(),
553
        }
554
    }
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)]
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 {
1✔
607
        match value {
1✔
608
            geoengine_datatypes::primitives::FeatureDataType::Category => Self::Category,
×
609
            geoengine_datatypes::primitives::FeatureDataType::Int => Self::Int,
1✔
610
            geoengine_datatypes::primitives::FeatureDataType::Float => Self::Float,
1✔
611
            geoengine_datatypes::primitives::FeatureDataType::Text => Self::Text,
1✔
612
            geoengine_datatypes::primitives::FeatureDataType::Bool => Self::Bool,
×
613
            geoengine_datatypes::primitives::FeatureDataType::DateTime => Self::DateTime,
×
614
        }
615
    }
616
}
617

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

631
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
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 {
1✔
641
        match value {
1✔
642
            geoengine_datatypes::primitives::Measurement::Unitless => Self::Unitless,
1✔
643
            geoengine_datatypes::primitives::Measurement::Continuous(cm) => {
1✔
644
                Self::Continuous(cm.into())
2✔
645
            }
646
            geoengine_datatypes::primitives::Measurement::Classification(cm) => {
×
647
                Self::Classification(cm.into())
×
648
            }
649
        }
650
    }
651
}
652

653
impl From<Measurement> for geoengine_datatypes::primitives::Measurement {
654
    fn from(value: Measurement) -> Self {
1✔
655
        match value {
1✔
656
            Measurement::Unitless => Self::Unitless,
1✔
657
            Measurement::Continuous(cm) => Self::Continuous(cm.into()),
×
658
            Measurement::Classification(cm) => Self::Classification(cm.into()),
×
659
        }
660
    }
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 {
672
            measurement: value.measurement,
1✔
673
            unit: value.unit,
1✔
674
        }
675
    }
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 {
×
701
        Self {
702
            measurement: value.measurement,
×
703
            classes: value.classes,
×
704
        }
705
    }
706
}
707

708
impl From<ClassificationMeasurement>
709
    for geoengine_datatypes::primitives::ClassificationMeasurement
710
{
711
    fn from(value: ClassificationMeasurement) -> Self {
×
712
        Self {
713
            measurement: value.measurement,
×
714
            classes: value.classes,
×
715
        }
716
    }
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)]
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 {
1✔
766
        Self {
767
            upper_left_coordinate: value.upper_left().into(),
1✔
768
            lower_right_coordinate: value.lower_right().into(),
1✔
769
        }
770
    }
771
}
772

773
impl From<SpatialPartition2D> for geoengine_datatypes::primitives::SpatialPartition2D {
774
    fn from(value: SpatialPartition2D) -> Self {
×
775
        Self::new_unchecked(
776
            value.upper_left_coordinate.into(),
×
777
            value.lower_right_coordinate.into(),
×
778
        )
779
    }
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)]
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 {
1✔
846
        Self {
847
            x: value.x,
848
            y: value.y,
849
        }
850
    }
851
}
852

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

862
#[derive(Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, ToSchema)]
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 {
1✔
877
        Self(value.inner())
1✔
878
    }
879
}
880

881
impl From<TimeInstance> for geoengine_datatypes::primitives::TimeInstance {
882
    fn from(value: TimeInstance) -> Self {
1✔
883
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(value.inner())
1✔
884
    }
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 {
1✔
904
        self.0
1✔
905
    }
906
}
907

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

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

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

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

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

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

946
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
×
947
    }
948
}
949

950
/// A time granularity.
951
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
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 {
×
965
        match value {
×
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,
×
972
            geoengine_datatypes::primitives::TimeGranularity::Years => Self::Years,
×
973
        }
974
    }
975
}
976

977
impl From<TimeGranularity> for geoengine_datatypes::primitives::TimeGranularity {
978
    fn from(value: TimeGranularity) -> Self {
×
979
        match value {
×
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,
×
986
            TimeGranularity::Years => Self::Years,
×
987
        }
988
    }
989
}
990

991
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
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 {
×
999
        Self {
1000
            granularity: value.granularity.into(),
×
1001
            step: value.step,
1002
        }
1003
    }
1004
}
1005

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

1015
/// Stores time intervals in ms in close-open semantic [start, end)
1016
#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
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 {
1✔
1031
        geoengine_datatypes::primitives::TimeInterval::new_unchecked::<
1032
            geoengine_datatypes::primitives::TimeInstance,
1033
            geoengine_datatypes::primitives::TimeInstance,
1034
        >(value.start.into(), value.end.into())
1✔
1035
    }
1036
}
1037

1038
impl From<geoengine_datatypes::primitives::TimeInterval> for TimeInterval {
1039
    fn from(value: geoengine_datatypes::primitives::TimeInterval) -> Self {
1✔
1040
        Self {
1041
            start: value.start().into(),
1✔
1042
            end: value.end().into(),
1✔
1043
        }
1044
    }
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,
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 {
1✔
1076
        match value {
1✔
1077
            geoengine_datatypes::raster::RasterDataType::U8 => Self::U8,
1✔
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
    }
1089
}
1090

1091
impl From<RasterDataType> for geoengine_datatypes::raster::RasterDataType {
1092
    fn from(value: RasterDataType) -> Self {
×
1093
        match value {
×
1094
            RasterDataType::U8 => Self::U8,
×
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
    }
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 {
1✔
1121
        match self {
1✔
1122
            ResamplingMethod::Nearest => write!(f, "NEAREST"),
2✔
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
    }
1130
}
1131

1132
/// `RgbaColor` defines a 32 bit RGB color with alpha value
1133
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
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 {
1✔
1150
        Self(color.into_inner())
1✔
1151
    }
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 {
1✔
1177
        Self {
1178
            value: breakpoint.value,
1✔
1179
            color: breakpoint.color.into(),
1✔
1180
        }
1181
    }
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)]
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 {
1✔
1212
        match v {
1✔
1213
            geoengine_datatypes::operations::image::Colorizer::LinearGradient {
1✔
1214
                breakpoints,
1215
                no_data_color,
1216
                default_color,
1217
            } => Self::LinearGradient {
1218
                breakpoints: breakpoints
2✔
1219
                    .into_iter()
1220
                    .map(Into::into)
1221
                    .collect::<Vec<Breakpoint>>(),
1222
                no_data_color: no_data_color.into(),
2✔
1223
                default_color: default_color.into(),
1✔
1224
            },
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,
1239
                no_data_color,
1240
                default_color,
1241
            } => Self::Palette {
1242
                colors: colors.into(),
×
1243
                no_data_color: no_data_color.into(),
×
1244
                default_color: default_color.into(),
×
1245
            },
1246
            geoengine_datatypes::operations::image::Colorizer::Rgba => Self::Rgba,
×
1247
        }
1248
    }
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)]
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 {
×
1260
        Self(
1261
            palette
×
1262
                .into_inner()
1263
                .into_iter()
1264
                .map(|(value, color)| (value, color.into()))
×
1265
                .collect(),
1266
        )
1267
    }
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)]
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());
2✔
1292
        for (k, v) in palette.0 {
3✔
1293
            inner.insert(k.parse()?, v);
3✔
1294
        }
1295
        Ok(Self(inner))
1✔
1296
    }
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)]
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 {
×
1356
        Self {
1357
            fmt: value._to_parse_format().to_string(),
×
1358
            has_tz: value.has_tz(),
×
1359
            has_time: value.has_time(),
×
1360
        }
1361
    }
1362
}
1363

1364
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1365
    fn from(value: DateTimeParseFormat) -> Self {
×
1366
        Self::custom(value.fmt)
×
1367
    }
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