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

geo-engine / geoengine / 5750561412

03 Aug 2023 11:02AM UTC coverage: 88.974% (+0.02%) from 88.958%
5750561412

push

github

web-flow
Merge pull request #845 from geo-engine/pg-result-descriptor-mapping

Pg-result-descriptor-mapping

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

106836 of 120075 relevant lines covered (88.97%)

60295.69 hits per line

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

67.94
/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 postgres_types::{FromSql, ToSql};
8
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
9
use snafu::ResultExt;
10
use std::{
11
    collections::{BTreeMap, HashMap},
12
    fmt::{Debug, Formatter},
13
    str::FromStr,
14
};
15
use utoipa::{IntoParams, ToSchema};
16

17
identifier!(DataProviderId);
×
18

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

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

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

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

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

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

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

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

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

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

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

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

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

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

130
/// The user-facing identifier for loadable data.
131
/// It can be resolved into a [`DataId`].
132
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)]
65✔
133
// TODO: Have separate type once `geoengine_datatypes::dataset::NamedData` is not part of the API anymore.
134
#[serde(
135
    from = "geoengine_datatypes::dataset::NamedData",
136
    into = "geoengine_datatypes::dataset::NamedData"
137
)]
138
pub struct NamedData {
139
    pub namespace: Option<String>,
140
    pub provider: Option<String>,
141
    pub name: String,
142
}
143

144
impl From<geoengine_datatypes::dataset::NamedData> for NamedData {
145
    fn from(
×
146
        geoengine_datatypes::dataset::NamedData {
×
147
            namespace,
×
148
            provider,
×
149
            name,
×
150
        }: geoengine_datatypes::dataset::NamedData,
×
151
    ) -> Self {
×
152
        Self {
×
153
            namespace,
×
154
            provider,
×
155
            name,
×
156
        }
×
157
    }
×
158
}
159

160
impl From<&geoengine_datatypes::dataset::NamedData> for NamedData {
161
    fn from(named_data: &geoengine_datatypes::dataset::NamedData) -> Self {
×
162
        Self::from(named_data.clone())
×
163
    }
×
164
}
165

166
impl From<NamedData> for geoengine_datatypes::dataset::NamedData {
167
    fn from(
118✔
168
        NamedData {
118✔
169
            namespace,
118✔
170
            provider,
118✔
171
            name,
118✔
172
        }: NamedData,
118✔
173
    ) -> Self {
118✔
174
        Self {
118✔
175
            namespace,
118✔
176
            provider,
118✔
177
            name,
118✔
178
        }
118✔
179
    }
118✔
180
}
181

182
impl From<&NamedData> for geoengine_datatypes::dataset::NamedData {
183
    fn from(named_data: &NamedData) -> Self {
61✔
184
        Self::from(named_data.clone())
61✔
185
    }
61✔
186
}
187

188
impl<'a> ToSchema<'a> for NamedData {
189
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
190
        use utoipa::openapi::*;
2✔
191
        (
2✔
192
            "NamedData",
2✔
193
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
194
        )
2✔
195
    }
2✔
196
}
197

198
/// A (optionally namespaced) name for a `Dataset`.
199
/// It can be resolved into a [`DataId`] if you know the data provider.
200
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, IntoParams, ToSql, FromSql)]
293✔
201
pub struct DatasetName {
202
    pub namespace: Option<String>,
203
    pub name: String,
204
}
205

206
impl DatasetName {
207
    /// Canonicalize a name that reflects the system namespace and provider.
208
    fn canonicalize<S: Into<String> + PartialEq<&'static str>>(
8✔
209
        name: S,
8✔
210
        system_name: &'static str,
8✔
211
    ) -> Option<String> {
8✔
212
        if name == system_name {
8✔
213
            None
×
214
        } else {
215
            Some(name.into())
8✔
216
        }
217
    }
8✔
218

219
    pub fn new<S: Into<String>>(namespace: Option<String>, name: S) -> Self {
24✔
220
        Self {
24✔
221
            namespace,
24✔
222
            name: name.into(),
24✔
223
        }
24✔
224
    }
24✔
225
}
226

227
impl std::fmt::Display for DatasetName {
228
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6✔
229
        let d = geoengine_datatypes::dataset::NAME_DELIMITER;
6✔
230
        match (&self.namespace, &self.name) {
6✔
231
            (None, name) => write!(f, "{name}"),
3✔
232
            (Some(namespace), name) => {
3✔
233
                write!(f, "{namespace}{d}{name}")
3✔
234
            }
235
        }
236
    }
6✔
237
}
238

239
impl Serialize for DatasetName {
240
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
15✔
241
    where
15✔
242
        S: serde::Serializer,
15✔
243
    {
15✔
244
        let d = geoengine_datatypes::dataset::NAME_DELIMITER;
15✔
245
        let serialized = match (&self.namespace, &self.name) {
15✔
246
            (None, name) => name.to_string(),
9✔
247
            (Some(namespace), name) => {
6✔
248
                format!("{namespace}{d}{name}")
6✔
249
            }
250
        };
251

252
        serializer.serialize_str(&serialized)
15✔
253
    }
15✔
254
}
255

256
impl<'de> Deserialize<'de> for DatasetName {
257
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
20✔
258
    where
20✔
259
        D: serde::Deserializer<'de>,
20✔
260
    {
20✔
261
        deserializer.deserialize_str(DatasetNameDeserializeVisitor)
20✔
262
    }
20✔
263
}
264

265
struct DatasetNameDeserializeVisitor;
266

267
impl<'de> Visitor<'de> for DatasetNameDeserializeVisitor {
268
    type Value = DatasetName;
269

270
    /// always keep in sync with [`is_allowed_name_char`]
271
    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
×
272
        write!(
×
273
            formatter,
×
274
            "a string consisting of a namespace and name name, separated by a colon, only using alphanumeric characters, underscores & dashes"
×
275
        )
×
276
    }
×
277

278
    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
20✔
279
    where
20✔
280
        E: serde::de::Error,
20✔
281
    {
20✔
282
        let mut strings = [None, None];
20✔
283
        let mut split = s.split(geoengine_datatypes::dataset::NAME_DELIMITER);
20✔
284

285
        for (buffer, part) in strings.iter_mut().zip(&mut split) {
28✔
286
            if part.is_empty() {
28✔
287
                return Err(E::custom("empty part in named data"));
×
288
            }
28✔
289

290
            if let Some(c) = part
28✔
291
                .matches(geoengine_datatypes::dataset::is_invalid_name_char)
28✔
292
                .next()
28✔
293
            {
294
                return Err(E::custom(format!("invalid character '{c}' in named data")));
×
295
            }
28✔
296

28✔
297
            *buffer = Some(part.to_string());
28✔
298
        }
299

300
        if split.next().is_some() {
20✔
301
            return Err(E::custom("named data must consist of at most two parts"));
×
302
        }
20✔
303

304
        match strings {
20✔
305
            [Some(namespace), Some(name)] => Ok(DatasetName {
8✔
306
                namespace: DatasetName::canonicalize(
8✔
307
                    namespace,
8✔
308
                    geoengine_datatypes::dataset::SYSTEM_NAMESPACE,
8✔
309
                ),
8✔
310
                name,
8✔
311
            }),
8✔
312
            [Some(name), None] => Ok(DatasetName {
12✔
313
                namespace: None,
12✔
314
                name,
12✔
315
            }),
12✔
316
            _ => Err(E::custom("empty named data")),
×
317
        }
318
    }
20✔
319
}
320

321
impl From<NamedData> for DatasetName {
322
    fn from(
×
323
        NamedData {
×
324
            namespace,
×
325
            provider: _,
×
326
            name,
×
327
        }: NamedData,
×
328
    ) -> Self {
×
329
        Self { namespace, name }
×
330
    }
×
331
}
332

333
impl From<&NamedData> for DatasetName {
334
    fn from(named_data: &NamedData) -> Self {
×
335
        Self {
×
336
            namespace: named_data.namespace.clone(),
×
337
            name: named_data.name.clone(),
×
338
        }
×
339
    }
×
340
}
341

342
impl From<&geoengine_datatypes::dataset::NamedData> for DatasetName {
343
    fn from(named_data: &geoengine_datatypes::dataset::NamedData) -> Self {
33✔
344
        Self {
33✔
345
            namespace: named_data.namespace.clone(),
33✔
346
            name: named_data.name.clone(),
33✔
347
        }
33✔
348
    }
33✔
349
}
350

351
impl From<DatasetName> for NamedData {
352
    fn from(DatasetName { namespace, name }: DatasetName) -> Self {
5✔
353
        NamedData {
5✔
354
            namespace,
5✔
355
            provider: None,
5✔
356
            name,
5✔
357
        }
5✔
358
    }
5✔
359
}
360

361
impl From<DatasetName> for geoengine_datatypes::dataset::NamedData {
362
    fn from(DatasetName { namespace, name }: DatasetName) -> Self {
3✔
363
        geoengine_datatypes::dataset::NamedData {
3✔
364
            namespace,
3✔
365
            provider: None,
3✔
366
            name,
3✔
367
        }
3✔
368
    }
3✔
369
}
370

371
impl<'a> ToSchema<'a> for DatasetName {
372
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
373
        use utoipa::openapi::*;
2✔
374
        (
2✔
375
            "DatasetName",
2✔
376
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
377
        )
2✔
378
    }
2✔
379
}
380

381
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, ToSchema, ToSql, FromSql)]
207✔
382
pub struct LayerId(pub String); // TODO: differentiate between internal layer ids (UUID) and external layer ids (String)
383

384
impl From<LayerId> for geoengine_datatypes::dataset::LayerId {
385
    fn from(value: LayerId) -> Self {
24✔
386
        Self(value.0)
24✔
387
    }
24✔
388
}
389

390
impl std::fmt::Display for LayerId {
391
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
79✔
392
        write!(f, "{}", self.0)
79✔
393
    }
79✔
394
}
395

396
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
33✔
397
#[serde(rename_all = "camelCase")]
398
pub struct ExternalDataId {
399
    pub provider_id: DataProviderId,
400
    pub layer_id: LayerId,
401
}
402

403
impl From<geoengine_datatypes::dataset::ExternalDataId> for ExternalDataId {
404
    fn from(id: geoengine_datatypes::dataset::ExternalDataId) -> Self {
16✔
405
        Self {
16✔
406
            provider_id: id.provider_id.into(),
16✔
407
            layer_id: id.layer_id.into(),
16✔
408
        }
16✔
409
    }
16✔
410
}
411

412
impl From<geoengine_datatypes::dataset::DataProviderId> for DataProviderId {
413
    fn from(id: geoengine_datatypes::dataset::DataProviderId) -> Self {
17✔
414
        Self(id.0)
17✔
415
    }
17✔
416
}
417

418
impl From<geoengine_datatypes::dataset::LayerId> for LayerId {
419
    fn from(id: geoengine_datatypes::dataset::LayerId) -> Self {
16✔
420
        Self(id.0)
16✔
421
    }
16✔
422
}
423

424
/// A spatial reference authority that is part of a spatial reference definition
425
#[derive(
426
    Debug,
×
427
    Copy,
428
    Clone,
×
429
    Eq,
430
    PartialEq,
31✔
431
    Ord,
×
432
    PartialOrd,
×
433
    Serialize,
×
434
    Deserialize,
×
435
    ToSchema,
2✔
436
    FromSql,
675✔
437
    ToSql,
31✔
438
)]
439
#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
440
pub enum SpatialReferenceAuthority {
441
    Epsg,
442
    SrOrg,
443
    Iau2000,
444
    Esri,
445
}
446

447
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceAuthority>
448
    for SpatialReferenceAuthority
449
{
450
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceAuthority) -> Self {
128✔
451
        match value {
128✔
452
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Epsg => Self::Epsg,
127✔
453
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::SrOrg => Self::SrOrg,
1✔
454
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Iau2000 => {
455
                Self::Iau2000
×
456
            }
457
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Esri => Self::Esri,
×
458
        }
459
    }
128✔
460
}
461

462
impl From<SpatialReferenceAuthority>
463
    for geoengine_datatypes::spatial_reference::SpatialReferenceAuthority
464
{
465
    fn from(value: SpatialReferenceAuthority) -> Self {
13✔
466
        match value {
13✔
467
            SpatialReferenceAuthority::Epsg => Self::Epsg,
13✔
468
            SpatialReferenceAuthority::SrOrg => Self::SrOrg,
×
469
            SpatialReferenceAuthority::Iau2000 => Self::Iau2000,
×
470
            SpatialReferenceAuthority::Esri => Self::Esri,
×
471
        }
472
    }
13✔
473
}
474

475
impl FromStr for SpatialReferenceAuthority {
476
    type Err = error::Error;
477

478
    fn from_str(s: &str) -> Result<Self, Self::Err> {
32✔
479
        Ok(match s {
32✔
480
            "EPSG" => SpatialReferenceAuthority::Epsg,
32✔
481
            "SR-ORG" => SpatialReferenceAuthority::SrOrg,
×
482
            "IAU2000" => SpatialReferenceAuthority::Iau2000,
×
483
            "ESRI" => SpatialReferenceAuthority::Esri,
×
484
            _ => {
485
                return Err(error::Error::InvalidSpatialReferenceString {
×
486
                    spatial_reference_string: s.into(),
×
487
                })
×
488
            }
489
        })
490
    }
32✔
491
}
492

493
impl std::fmt::Display for SpatialReferenceAuthority {
494
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44✔
495
        write!(
44✔
496
            f,
44✔
497
            "{}",
44✔
498
            match self {
44✔
499
                SpatialReferenceAuthority::Epsg => "EPSG",
44✔
500
                SpatialReferenceAuthority::SrOrg => "SR-ORG",
×
501
                SpatialReferenceAuthority::Iau2000 => "IAU2000",
×
502
                SpatialReferenceAuthority::Esri => "ESRI",
×
503
            }
504
        )
505
    }
44✔
506
}
507

508
/// A spatial reference consists of an authority and a code
509
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, FromSql, ToSql)]
449✔
510
pub struct SpatialReference {
511
    authority: SpatialReferenceAuthority,
512
    code: u32,
513
}
514

515
impl SpatialReference {
516
    pub fn proj_string(self) -> Result<String> {
517
        match self.authority {
×
518
            SpatialReferenceAuthority::Epsg | SpatialReferenceAuthority::Iau2000 => {
519
                Ok(format!("{}:{}", self.authority, self.code))
17✔
520
            }
521
            // poor-mans integration of Meteosat Second Generation
522
            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()),
×
523
            SpatialReferenceAuthority::SrOrg | SpatialReferenceAuthority::Esri => {
524
                Err(error::Error::ProjStringUnresolvable { spatial_ref: self })
×
525
                //TODO: we might need to look them up somehow! Best solution would be a registry where we can store user definexd srs strings.
526
            }
527
        }
528
    }
17✔
529

530
    /// Return the srs-string "authority:code"
531
    #[allow(clippy::trivially_copy_pass_by_ref)]
532
    pub fn srs_string(&self) -> String {
17✔
533
        format!("{}:{}", self.authority, self.code)
17✔
534
    }
17✔
535
}
536

537
impl<'a> ToSchema<'a> for SpatialReference {
538
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
539
        use utoipa::openapi::*;
2✔
540
        (
2✔
541
            "SpatialReference",
2✔
542
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
543
        )
2✔
544
    }
2✔
545
}
546

547
impl From<geoengine_datatypes::spatial_reference::SpatialReference> for SpatialReference {
548
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReference) -> Self {
128✔
549
        Self {
128✔
550
            authority: (*value.authority()).into(),
128✔
551
            code: value.code(),
128✔
552
        }
128✔
553
    }
128✔
554
}
555

556
impl From<SpatialReference> for geoengine_datatypes::spatial_reference::SpatialReference {
557
    fn from(value: SpatialReference) -> Self {
13✔
558
        geoengine_datatypes::spatial_reference::SpatialReference::new(
13✔
559
            value.authority.into(),
13✔
560
            value.code,
13✔
561
        )
13✔
562
    }
13✔
563
}
564

565
impl SpatialReference {
566
    pub fn new(authority: SpatialReferenceAuthority, code: u32) -> Self {
37✔
567
        Self { authority, code }
37✔
568
    }
37✔
569

570
    pub fn authority(&self) -> &SpatialReferenceAuthority {
1✔
571
        &self.authority
1✔
572
    }
1✔
573

574
    pub fn code(self) -> u32 {
1✔
575
        self.code
1✔
576
    }
1✔
577
}
578

579
impl FromStr for SpatialReference {
580
    type Err = error::Error;
581

582
    fn from_str(s: &str) -> Result<Self, Self::Err> {
32✔
583
        let mut split = s.split(':');
32✔
584

32✔
585
        match (split.next(), split.next(), split.next()) {
32✔
586
            (Some(authority), Some(code), None) => Ok(Self::new(
32✔
587
                authority.parse()?,
32✔
588
                code.parse::<u32>().context(error::ParseU32)?,
32✔
589
            )),
590
            _ => Err(error::Error::InvalidSpatialReferenceString {
×
591
                spatial_reference_string: s.into(),
×
592
            }),
×
593
        }
594
    }
32✔
595
}
596

597
impl std::fmt::Display for SpatialReference {
598
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9✔
599
        write!(f, "{}:{}", self.authority, self.code)
9✔
600
    }
9✔
601
}
602

603
impl Serialize for SpatialReference {
604
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
1✔
605
    where
1✔
606
        S: Serializer,
1✔
607
    {
1✔
608
        serializer.serialize_str(&self.to_string())
1✔
609
    }
1✔
610
}
611

612
/// Helper struct for deserializing a `SpatialReferencce`
613
struct SpatialReferenceDeserializeVisitor;
614

615
impl<'de> Visitor<'de> for SpatialReferenceDeserializeVisitor {
616
    type Value = SpatialReference;
617

618
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
619
        formatter.write_str("a spatial reference in the form authority:code")
×
620
    }
×
621

622
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
16✔
623
    where
16✔
624
        E: serde::de::Error,
16✔
625
    {
16✔
626
        v.parse().map_err(serde::de::Error::custom)
16✔
627
    }
16✔
628
}
629

630
impl<'de> Deserialize<'de> for SpatialReference {
631
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
16✔
632
    where
16✔
633
        D: Deserializer<'de>,
16✔
634
    {
16✔
635
        deserializer.deserialize_str(SpatialReferenceDeserializeVisitor)
16✔
636
    }
16✔
637
}
638

639
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ToSchema)]
118✔
640
pub enum SpatialReferenceOption {
641
    SpatialReference(SpatialReference),
642
    Unreferenced,
643
}
644

645
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceOption>
646
    for SpatialReferenceOption
647
{
648
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceOption) -> Self {
128✔
649
        match value {
128✔
650
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::SpatialReference(s) => {
98✔
651
                Self::SpatialReference(s.into())
98✔
652
            }
653
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::Unreferenced => {
654
                Self::Unreferenced
30✔
655
            }
656
        }
657
    }
128✔
658
}
659

660
impl From<SpatialReferenceOption>
661
    for geoengine_datatypes::spatial_reference::SpatialReferenceOption
662
{
663
    fn from(value: SpatialReferenceOption) -> Self {
9✔
664
        match value {
9✔
665
            SpatialReferenceOption::SpatialReference(sr) => Self::SpatialReference(sr.into()),
9✔
666
            SpatialReferenceOption::Unreferenced => Self::Unreferenced,
×
667
        }
668
    }
9✔
669
}
670

671
impl From<SpatialReference> for SpatialReferenceOption {
672
    fn from(spatial_reference: SpatialReference) -> Self {
8✔
673
        Self::SpatialReference(spatial_reference)
8✔
674
    }
8✔
675
}
676

677
impl std::fmt::Display for SpatialReferenceOption {
678
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11✔
679
        match self {
11✔
680
            SpatialReferenceOption::SpatialReference(p) => write!(f, "{p}"),
8✔
681
            SpatialReferenceOption::Unreferenced => Ok(()),
3✔
682
        }
683
    }
11✔
684
}
685

686
impl Serialize for SpatialReferenceOption {
687
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
11✔
688
    where
11✔
689
        S: Serializer,
11✔
690
    {
11✔
691
        serializer.serialize_str(&self.to_string())
11✔
692
    }
11✔
693
}
694

695
/// Helper struct for deserializing a `SpatialReferenceOption`
696
struct SpatialReferenceOptionDeserializeVisitor;
697

698
impl<'de> Visitor<'de> for SpatialReferenceOptionDeserializeVisitor {
699
    type Value = SpatialReferenceOption;
700

701
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
702
        formatter.write_str("a spatial reference in the form authority:code")
×
703
    }
×
704

705
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
8✔
706
    where
8✔
707
        E: serde::de::Error,
8✔
708
    {
8✔
709
        if v.is_empty() {
8✔
710
            return Ok(SpatialReferenceOption::Unreferenced);
×
711
        }
8✔
712

713
        let spatial_reference: SpatialReference = v.parse().map_err(serde::de::Error::custom)?;
8✔
714

715
        Ok(spatial_reference.into())
8✔
716
    }
8✔
717
}
718

719
impl<'de> Deserialize<'de> for SpatialReferenceOption {
720
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
8✔
721
    where
8✔
722
        D: Deserializer<'de>,
8✔
723
    {
8✔
724
        deserializer.deserialize_str(SpatialReferenceOptionDeserializeVisitor)
8✔
725
    }
8✔
726
}
727

728
impl From<Option<SpatialReference>> for SpatialReferenceOption {
729
    fn from(option: Option<SpatialReference>) -> Self {
×
730
        match option {
×
731
            Some(p) => SpatialReferenceOption::SpatialReference(p),
×
732
            None => SpatialReferenceOption::Unreferenced,
×
733
        }
734
    }
×
735
}
736

737
impl From<SpatialReferenceOption> for Option<SpatialReference> {
738
    fn from(option: SpatialReferenceOption) -> Self {
12✔
739
        match option {
12✔
740
            SpatialReferenceOption::SpatialReference(p) => Some(p),
12✔
741
            SpatialReferenceOption::Unreferenced => None,
×
742
        }
743
    }
12✔
744
}
745

746
impl ToSql for SpatialReferenceOption {
747
    fn to_sql(
32✔
748
        &self,
32✔
749
        ty: &postgres_types::Type,
32✔
750
        out: &mut bytes::BytesMut,
32✔
751
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>>
32✔
752
    where
32✔
753
        Self: Sized,
32✔
754
    {
32✔
755
        match self {
32✔
756
            SpatialReferenceOption::SpatialReference(sref) => sref.to_sql(ty, out),
15✔
757
            SpatialReferenceOption::Unreferenced => Ok(postgres_types::IsNull::Yes),
17✔
758
        }
759
    }
32✔
760

761
    fn accepts(ty: &postgres_types::Type) -> bool
2✔
762
    where
2✔
763
        Self: Sized,
2✔
764
    {
2✔
765
        <SpatialReference as ToSql>::accepts(ty)
2✔
766
    }
2✔
767

768
    fn to_sql_checked(
2✔
769
        &self,
2✔
770
        ty: &postgres_types::Type,
2✔
771
        out: &mut bytes::BytesMut,
2✔
772
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
2✔
773
        match self {
2✔
774
            SpatialReferenceOption::SpatialReference(sref) => sref.to_sql_checked(ty, out),
1✔
775
            SpatialReferenceOption::Unreferenced => Ok(postgres_types::IsNull::Yes),
1✔
776
        }
777
    }
2✔
778
}
779

780
impl<'a> FromSql<'a> for SpatialReferenceOption {
781
    fn from_sql(
11✔
782
        ty: &postgres_types::Type,
11✔
783
        raw: &'a [u8],
11✔
784
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
11✔
785
        Ok(SpatialReferenceOption::SpatialReference(
11✔
786
            SpatialReference::from_sql(ty, raw)?,
11✔
787
        ))
788
    }
11✔
789

790
    fn from_sql_null(
8✔
791
        _: &postgres_types::Type,
8✔
792
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
8✔
793
        Ok(SpatialReferenceOption::Unreferenced)
8✔
794
    }
8✔
795

796
    fn accepts(ty: &postgres_types::Type) -> bool {
135✔
797
        <SpatialReference as FromSql>::accepts(ty)
135✔
798
    }
135✔
799
}
800

801
/// An enum that contains all possible vector data types
802
#[derive(
803
    Debug,
×
804
    Ord,
×
805
    PartialOrd,
×
806
    Eq,
807
    PartialEq,
10✔
808
    Hash,
×
809
    Deserialize,
16✔
810
    Serialize,
5✔
811
    Copy,
812
    Clone,
46✔
813
    ToSchema,
2✔
814
    FromSql,
245✔
815
    ToSql,
42✔
816
)]
817
pub enum VectorDataType {
818
    Data,
819
    MultiPoint,
820
    MultiLineString,
821
    MultiPolygon,
822
}
823

824
impl From<geoengine_datatypes::collections::VectorDataType> for VectorDataType {
825
    fn from(value: geoengine_datatypes::collections::VectorDataType) -> Self {
41✔
826
        match value {
41✔
827
            geoengine_datatypes::collections::VectorDataType::Data => Self::Data,
18✔
828
            geoengine_datatypes::collections::VectorDataType::MultiPoint => Self::MultiPoint,
23✔
829
            geoengine_datatypes::collections::VectorDataType::MultiLineString => {
830
                Self::MultiLineString
×
831
            }
832
            geoengine_datatypes::collections::VectorDataType::MultiPolygon => Self::MultiPolygon,
×
833
        }
834
    }
41✔
835
}
836

837
impl From<VectorDataType> for geoengine_datatypes::collections::VectorDataType {
838
    fn from(value: VectorDataType) -> Self {
8✔
839
        match value {
8✔
840
            VectorDataType::Data => Self::Data,
×
841
            VectorDataType::MultiPoint => Self::MultiPoint,
8✔
842
            VectorDataType::MultiLineString => Self::MultiLineString,
×
843
            VectorDataType::MultiPolygon => Self::MultiPolygon,
×
844
        }
845
    }
8✔
846
}
847

848
#[derive(
849
    Clone,
×
850
    Copy,
851
    Debug,
×
852
    Deserialize,
70✔
853
    PartialEq,
21✔
854
    PartialOrd,
×
855
    Serialize,
21✔
856
    Default,
×
857
    ToSchema,
2✔
858
    ToSql,
78✔
859
    FromSql,
865✔
860
)]
861
pub struct Coordinate2D {
862
    pub x: f64,
863
    pub y: f64,
864
}
865

866
impl From<geoengine_datatypes::primitives::Coordinate2D> for Coordinate2D {
867
    fn from(coordinate: geoengine_datatypes::primitives::Coordinate2D) -> Self {
199✔
868
        Self {
199✔
869
            x: coordinate.x,
199✔
870
            y: coordinate.y,
199✔
871
        }
199✔
872
    }
199✔
873
}
874

875
impl From<Coordinate2D> for geoengine_datatypes::primitives::Coordinate2D {
876
    fn from(coordinate: Coordinate2D) -> Self {
17✔
877
        Self {
17✔
878
            x: coordinate.x,
17✔
879
            y: coordinate.y,
17✔
880
        }
17✔
881
    }
17✔
882
}
883

884
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema, ToSql, FromSql)]
282✔
885
#[serde(rename_all = "camelCase")]
886
/// A bounding box that includes all border points.
887
/// Note: may degenerate to a point!
888
pub struct BoundingBox2D {
889
    pub lower_left_coordinate: Coordinate2D,
890
    pub upper_right_coordinate: Coordinate2D,
891
}
892

893
impl From<geoengine_datatypes::primitives::BoundingBox2D> for BoundingBox2D {
894
    fn from(bbox: geoengine_datatypes::primitives::BoundingBox2D) -> Self {
30✔
895
        Self {
30✔
896
            lower_left_coordinate:
30✔
897
                geoengine_datatypes::primitives::AxisAlignedRectangle::lower_left(&bbox).into(),
30✔
898
            upper_right_coordinate:
30✔
899
                geoengine_datatypes::primitives::AxisAlignedRectangle::upper_right(&bbox).into(),
30✔
900
        }
30✔
901
    }
30✔
902
}
903

904
impl From<BoundingBox2D> for geoengine_datatypes::primitives::BoundingBox2D {
905
    fn from(bbox: BoundingBox2D) -> Self {
1✔
906
        Self::new_unchecked(
1✔
907
            bbox.lower_left_coordinate.into(),
1✔
908
            bbox.upper_right_coordinate.into(),
1✔
909
        )
1✔
910
    }
1✔
911
}
912

913
/// An object that composes the date and a timestamp with time zone.
914
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToSchema)]
2✔
915
pub struct DateTime {
916
    datetime: chrono::DateTime<chrono::Utc>,
917
}
918

919
impl FromStr for DateTime {
920
    type Err = geoengine_datatypes::primitives::DateTimeError;
921

922
    fn from_str(input: &str) -> Result<Self, Self::Err> {
×
923
        let date_time = chrono::DateTime::<chrono::FixedOffset>::from_str(input).map_err(|e| {
×
924
            Self::Err::DateParse {
×
925
                source: Box::new(e),
×
926
            }
×
927
        })?;
×
928

929
        Ok(date_time.into())
×
930
    }
×
931
}
932

933
impl From<chrono::DateTime<chrono::FixedOffset>> for DateTime {
934
    fn from(datetime: chrono::DateTime<chrono::FixedOffset>) -> Self {
×
935
        Self {
×
936
            datetime: datetime.into(),
×
937
        }
×
938
    }
×
939
}
940

941
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, ToSchema, FromSql, ToSql)]
357✔
942
#[serde(rename_all = "camelCase")]
943
pub enum FeatureDataType {
944
    Category,
945
    Int,
946
    Float,
947
    Text,
948
    Bool,
949
    DateTime,
950
}
951

952
impl From<geoengine_datatypes::primitives::FeatureDataType> for FeatureDataType {
953
    fn from(value: geoengine_datatypes::primitives::FeatureDataType) -> Self {
40✔
954
        match value {
40✔
955
            geoengine_datatypes::primitives::FeatureDataType::Category => Self::Category,
×
956
            geoengine_datatypes::primitives::FeatureDataType::Int => Self::Int,
12✔
957
            geoengine_datatypes::primitives::FeatureDataType::Float => Self::Float,
15✔
958
            geoengine_datatypes::primitives::FeatureDataType::Text => Self::Text,
13✔
959
            geoengine_datatypes::primitives::FeatureDataType::Bool => Self::Bool,
×
960
            geoengine_datatypes::primitives::FeatureDataType::DateTime => Self::DateTime,
×
961
        }
962
    }
40✔
963
}
964

965
impl From<FeatureDataType> for geoengine_datatypes::primitives::FeatureDataType {
966
    fn from(value: FeatureDataType) -> Self {
20✔
967
        match value {
20✔
968
            FeatureDataType::Category => Self::Category,
×
969
            FeatureDataType::Int => Self::Int,
4✔
970
            FeatureDataType::Float => Self::Float,
4✔
971
            FeatureDataType::Text => Self::Text,
12✔
972
            FeatureDataType::Bool => Self::Bool,
×
973
            FeatureDataType::DateTime => Self::DateTime,
×
974
        }
975
    }
20✔
976
}
977

978
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
128✔
979
#[serde(rename_all = "camelCase", tag = "type")]
980
pub enum Measurement {
981
    Unitless,
982
    Continuous(ContinuousMeasurement),
983
    Classification(ClassificationMeasurement),
984
}
985

986
impl From<geoengine_datatypes::primitives::Measurement> for Measurement {
987
    fn from(value: geoengine_datatypes::primitives::Measurement) -> Self {
112✔
988
        match value {
112✔
989
            geoengine_datatypes::primitives::Measurement::Unitless => Self::Unitless,
109✔
990
            geoengine_datatypes::primitives::Measurement::Continuous(cm) => {
1✔
991
                Self::Continuous(cm.into())
1✔
992
            }
993
            geoengine_datatypes::primitives::Measurement::Classification(cm) => {
2✔
994
                Self::Classification(cm.into())
2✔
995
            }
996
        }
997
    }
112✔
998
}
999

1000
impl From<Measurement> for geoengine_datatypes::primitives::Measurement {
1001
    fn from(value: Measurement) -> Self {
33✔
1002
        match value {
33✔
1003
            Measurement::Unitless => Self::Unitless,
32✔
1004
            Measurement::Continuous(cm) => Self::Continuous(cm.into()),
×
1005
            Measurement::Classification(cm) => Self::Classification(cm.into()),
1✔
1006
        }
1007
    }
33✔
1008
}
1009

1010
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema, FromSql, ToSql)]
292✔
1011
pub struct ContinuousMeasurement {
1012
    pub measurement: String,
1013
    pub unit: Option<String>,
1014
}
1015

1016
impl From<geoengine_datatypes::primitives::ContinuousMeasurement> for ContinuousMeasurement {
1017
    fn from(value: geoengine_datatypes::primitives::ContinuousMeasurement) -> Self {
1✔
1018
        Self {
1✔
1019
            measurement: value.measurement,
1✔
1020
            unit: value.unit,
1✔
1021
        }
1✔
1022
    }
1✔
1023
}
1024

1025
impl From<ContinuousMeasurement> for geoengine_datatypes::primitives::ContinuousMeasurement {
1026
    fn from(value: ContinuousMeasurement) -> Self {
×
1027
        Self {
×
1028
            measurement: value.measurement,
×
1029
            unit: value.unit,
×
1030
        }
×
1031
    }
×
1032
}
1033

1034
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
2✔
1035
#[serde(
1036
    try_from = "SerializableClassificationMeasurement",
1037
    into = "SerializableClassificationMeasurement"
1038
)]
1039
pub struct ClassificationMeasurement {
1040
    pub measurement: String,
1041
    pub classes: HashMap<u8, String>,
1042
}
1043

1044
impl From<geoengine_datatypes::primitives::ClassificationMeasurement>
1045
    for ClassificationMeasurement
1046
{
1047
    fn from(value: geoengine_datatypes::primitives::ClassificationMeasurement) -> Self {
2✔
1048
        Self {
2✔
1049
            measurement: value.measurement,
2✔
1050
            classes: value.classes,
2✔
1051
        }
2✔
1052
    }
2✔
1053
}
1054

1055
impl From<ClassificationMeasurement>
1056
    for geoengine_datatypes::primitives::ClassificationMeasurement
1057
{
1058
    fn from(value: ClassificationMeasurement) -> Self {
1✔
1059
        Self {
1✔
1060
            measurement: value.measurement,
1✔
1061
            classes: value.classes,
1✔
1062
        }
1✔
1063
    }
1✔
1064
}
1065

1066
/// A type that is solely for serde's serializability.
1067
/// You cannot serialize floats as JSON map keys.
1068
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
×
1069
pub struct SerializableClassificationMeasurement {
1070
    pub measurement: String,
1071
    // use a BTreeMap to preserve the order of the keys
1072
    pub classes: BTreeMap<String, String>,
1073
}
1074

1075
impl From<ClassificationMeasurement> for SerializableClassificationMeasurement {
1076
    fn from(measurement: ClassificationMeasurement) -> Self {
×
1077
        let mut classes = BTreeMap::new();
×
1078
        for (k, v) in measurement.classes {
×
1079
            classes.insert(k.to_string(), v);
×
1080
        }
×
1081
        Self {
×
1082
            measurement: measurement.measurement,
×
1083
            classes,
×
1084
        }
×
1085
    }
×
1086
}
1087

1088
impl TryFrom<SerializableClassificationMeasurement> for ClassificationMeasurement {
1089
    type Error = <u8 as FromStr>::Err;
1090

1091
    fn try_from(measurement: SerializableClassificationMeasurement) -> Result<Self, Self::Error> {
×
1092
        let mut classes = HashMap::with_capacity(measurement.classes.len());
×
1093
        for (k, v) in measurement.classes {
×
1094
            classes.insert(k.parse::<u8>()?, v);
×
1095
        }
1096
        Ok(Self {
×
1097
            measurement: measurement.measurement,
×
1098
            classes,
×
1099
        })
×
1100
    }
×
1101
}
1102

1103
/// A partition of space that include the upper left but excludes the lower right coordinate
1104
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema, FromSql, ToSql)]
147✔
1105
#[serde(rename_all = "camelCase")]
1106
pub struct SpatialPartition2D {
1107
    pub upper_left_coordinate: Coordinate2D,
1108
    pub lower_right_coordinate: Coordinate2D,
1109
}
1110

1111
impl From<geoengine_datatypes::primitives::SpatialPartition2D> for SpatialPartition2D {
1112
    fn from(value: geoengine_datatypes::primitives::SpatialPartition2D) -> Self {
63✔
1113
        Self {
63✔
1114
            upper_left_coordinate: value.upper_left().into(),
63✔
1115
            lower_right_coordinate: value.lower_right().into(),
63✔
1116
        }
63✔
1117
    }
63✔
1118
}
1119

1120
impl From<SpatialPartition2D> for geoengine_datatypes::primitives::SpatialPartition2D {
1121
    fn from(value: SpatialPartition2D) -> Self {
5✔
1122
        Self::new_unchecked(
5✔
1123
            value.upper_left_coordinate.into(),
5✔
1124
            value.lower_right_coordinate.into(),
5✔
1125
        )
5✔
1126
    }
5✔
1127
}
1128

1129
/// A spatio-temporal rectangle with a specified resolution
1130
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
6✔
1131
#[serde(rename_all = "camelCase")]
1132
#[aliases(
1133
    VectorQueryRectangle = QueryRectangle<BoundingBox2D>,
1134
    RasterQueryRectangle = QueryRectangle<SpatialPartition2D>,
1135
    PlotQueryRectangle = QueryRectangle<BoundingBox2D>)
1136
]
1137
pub struct QueryRectangle<SpatialBounds> {
1138
    pub spatial_bounds: SpatialBounds,
1139
    pub time_interval: TimeInterval,
1140
    pub spatial_resolution: SpatialResolution,
1141
}
1142

1143
/// The spatial resolution in SRS units
1144
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema, ToSql, FromSql)]
147✔
1145
pub struct SpatialResolution {
1146
    pub x: f64,
1147
    pub y: f64,
1148
}
1149

1150
impl From<geoengine_datatypes::primitives::SpatialResolution> for SpatialResolution {
1151
    fn from(value: geoengine_datatypes::primitives::SpatialResolution) -> Self {
64✔
1152
        Self {
64✔
1153
            x: value.x,
64✔
1154
            y: value.y,
64✔
1155
        }
64✔
1156
    }
64✔
1157
}
1158

1159
impl From<SpatialResolution> for geoengine_datatypes::primitives::SpatialResolution {
1160
    fn from(value: SpatialResolution) -> Self {
5✔
1161
        Self {
5✔
1162
            x: value.x,
5✔
1163
            y: value.y,
5✔
1164
        }
5✔
1165
    }
5✔
1166
}
1167

1168
#[derive(
1169
    Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, ToSchema, FromSql, ToSql,
278✔
1170
)]
1171
#[repr(C)]
1172
#[postgres(transparent)]
1173
pub struct TimeInstance(i64);
1174

1175
impl FromStr for TimeInstance {
1176
    type Err = geoengine_datatypes::primitives::DateTimeError;
1177

1178
    fn from_str(s: &str) -> Result<Self, Self::Err> {
×
1179
        let date_time = DateTime::from_str(s)?;
×
1180
        Ok(date_time.into())
×
1181
    }
×
1182
}
1183

1184
impl From<geoengine_datatypes::primitives::TimeInstance> for TimeInstance {
1185
    fn from(value: geoengine_datatypes::primitives::TimeInstance) -> Self {
270✔
1186
        Self(value.inner())
270✔
1187
    }
270✔
1188
}
1189

1190
impl From<TimeInstance> for geoengine_datatypes::primitives::TimeInstance {
1191
    fn from(value: TimeInstance) -> Self {
72✔
1192
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(value.inner())
72✔
1193
    }
72✔
1194
}
1195

1196
impl From<DateTime> for TimeInstance {
1197
    fn from(datetime: DateTime) -> Self {
×
1198
        Self::from(&datetime)
×
1199
    }
×
1200
}
1201

1202
impl From<&DateTime> for TimeInstance {
1203
    fn from(datetime: &DateTime) -> Self {
×
1204
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(
×
1205
            datetime.datetime.timestamp_millis(),
×
1206
        )
×
1207
        .into()
×
1208
    }
×
1209
}
1210

1211
impl TimeInstance {
1212
    pub const fn inner(self) -> i64 {
72✔
1213
        self.0
72✔
1214
    }
72✔
1215
}
1216

1217
impl<'de> Deserialize<'de> for TimeInstance {
1218
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
16✔
1219
    where
16✔
1220
        D: serde::Deserializer<'de>,
16✔
1221
    {
16✔
1222
        struct IsoStringOrUnixTimestamp;
16✔
1223

16✔
1224
        impl<'de> serde::de::Visitor<'de> for IsoStringOrUnixTimestamp {
16✔
1225
            type Value = TimeInstance;
16✔
1226

16✔
1227
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
16✔
1228
                formatter.write_str("RFC 3339 timestamp string or Unix timestamp integer")
×
1229
            }
×
1230

16✔
1231
            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
16✔
1232
            where
×
1233
                E: serde::de::Error,
×
1234
            {
×
1235
                TimeInstance::from_str(value).map_err(E::custom)
×
1236
            }
×
1237

16✔
1238
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
16✔
1239
            where
16✔
1240
                E: serde::de::Error,
16✔
1241
            {
16✔
1242
                geoengine_datatypes::primitives::TimeInstance::from_millis(v)
16✔
1243
                    .map(Into::into)
16✔
1244
                    .map_err(E::custom)
16✔
1245
            }
16✔
1246

16✔
1247
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
16✔
1248
            where
16✔
1249
                E: serde::de::Error,
16✔
1250
            {
16✔
1251
                Self::visit_i64(self, v as i64)
16✔
1252
            }
16✔
1253
        }
16✔
1254

16✔
1255
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
16✔
1256
    }
16✔
1257
}
1258

1259
/// A time granularity.
1260
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
8✔
1261
#[serde(rename_all = "camelCase")]
1262
pub enum TimeGranularity {
1263
    Millis,
1264
    Seconds,
1265
    Minutes,
1266
    Hours,
1267
    Days,
1268
    Months,
1269
    Years,
1270
}
1271

1272
impl From<geoengine_datatypes::primitives::TimeGranularity> for TimeGranularity {
1273
    fn from(value: geoengine_datatypes::primitives::TimeGranularity) -> Self {
4✔
1274
        match value {
4✔
1275
            geoengine_datatypes::primitives::TimeGranularity::Millis => Self::Millis,
×
1276
            geoengine_datatypes::primitives::TimeGranularity::Seconds => Self::Seconds,
×
1277
            geoengine_datatypes::primitives::TimeGranularity::Minutes => Self::Minutes,
×
1278
            geoengine_datatypes::primitives::TimeGranularity::Hours => Self::Hours,
×
1279
            geoengine_datatypes::primitives::TimeGranularity::Days => Self::Days,
×
1280
            geoengine_datatypes::primitives::TimeGranularity::Months => Self::Months,
4✔
1281
            geoengine_datatypes::primitives::TimeGranularity::Years => Self::Years,
×
1282
        }
1283
    }
4✔
1284
}
1285

1286
impl From<TimeGranularity> for geoengine_datatypes::primitives::TimeGranularity {
1287
    fn from(value: TimeGranularity) -> Self {
4✔
1288
        match value {
4✔
1289
            TimeGranularity::Millis => Self::Millis,
×
1290
            TimeGranularity::Seconds => Self::Seconds,
×
1291
            TimeGranularity::Minutes => Self::Minutes,
×
1292
            TimeGranularity::Hours => Self::Hours,
×
1293
            TimeGranularity::Days => Self::Days,
×
1294
            TimeGranularity::Months => Self::Months,
4✔
1295
            TimeGranularity::Years => Self::Years,
×
1296
        }
1297
    }
4✔
1298
}
1299

1300
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
20✔
1301
pub struct TimeStep {
1302
    pub granularity: TimeGranularity,
1303
    pub step: u32, // TODO: ensure on deserialization it is > 0
1304
}
1305

1306
impl From<geoengine_datatypes::primitives::TimeStep> for TimeStep {
1307
    fn from(value: geoengine_datatypes::primitives::TimeStep) -> Self {
4✔
1308
        Self {
4✔
1309
            granularity: value.granularity.into(),
4✔
1310
            step: value.step,
4✔
1311
        }
4✔
1312
    }
4✔
1313
}
1314

1315
impl From<TimeStep> for geoengine_datatypes::primitives::TimeStep {
1316
    fn from(value: TimeStep) -> Self {
4✔
1317
        Self {
4✔
1318
            granularity: value.granularity.into(),
4✔
1319
            step: value.step,
4✔
1320
        }
4✔
1321
    }
4✔
1322
}
1323

1324
/// Stores time intervals in ms in close-open semantic [start, end)
1325
#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ToSql, FromSql)]
422✔
1326
pub struct TimeInterval {
1327
    start: TimeInstance,
1328
    end: TimeInstance,
1329
}
1330

1331
impl<'a> ToSchema<'a> for TimeInterval {
1332
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1333
        use utoipa::openapi::*;
2✔
1334
        (
2✔
1335
            "TimeInterval",
2✔
1336
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
1337
        )
2✔
1338
    }
2✔
1339
}
1340

1341
impl From<TimeInterval> for geoengine_datatypes::primitives::TimeInterval {
1342
    fn from(value: TimeInterval) -> Self {
36✔
1343
        geoengine_datatypes::primitives::TimeInterval::new_unchecked::<
36✔
1344
            geoengine_datatypes::primitives::TimeInstance,
36✔
1345
            geoengine_datatypes::primitives::TimeInstance,
36✔
1346
        >(value.start.into(), value.end.into())
36✔
1347
    }
36✔
1348
}
1349

1350
impl From<geoengine_datatypes::primitives::TimeInterval> for TimeInterval {
1351
    fn from(value: geoengine_datatypes::primitives::TimeInterval) -> Self {
127✔
1352
        Self {
127✔
1353
            start: value.start().into(),
127✔
1354
            end: value.end().into(),
127✔
1355
        }
127✔
1356
    }
127✔
1357
}
1358

1359
impl core::fmt::Debug for TimeInterval {
1360
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
×
1361
        write!(
×
1362
            f,
×
1363
            "TimeInterval [{}, {})",
×
1364
            self.start.inner(),
×
1365
            &self.end.inner()
×
1366
        )
×
1367
    }
×
1368
}
1369

1370
#[derive(
1371
    Debug,
×
1372
    Ord,
×
1373
    PartialOrd,
×
1374
    Eq,
1375
    PartialEq,
12✔
1376
    Hash,
×
1377
    Deserialize,
8✔
1378
    Serialize,
7✔
1379
    Copy,
1380
    Clone,
72✔
1381
    ToSchema,
2✔
1382
    FromSql,
594✔
1383
    ToSql,
143✔
1384
)]
1385
pub enum RasterDataType {
1386
    U8,
1387
    U16,
1388
    U32,
1389
    U64,
1390
    I8,
1391
    I16,
1392
    I32,
1393
    I64,
1394
    F32,
1395
    F64,
1396
}
1397

1398
impl From<geoengine_datatypes::raster::RasterDataType> for RasterDataType {
1399
    fn from(value: geoengine_datatypes::raster::RasterDataType) -> Self {
73✔
1400
        match value {
73✔
1401
            geoengine_datatypes::raster::RasterDataType::U8 => Self::U8,
73✔
1402
            geoengine_datatypes::raster::RasterDataType::U16 => Self::U16,
×
1403
            geoengine_datatypes::raster::RasterDataType::U32 => Self::U32,
×
1404
            geoengine_datatypes::raster::RasterDataType::U64 => Self::U64,
×
1405
            geoengine_datatypes::raster::RasterDataType::I8 => Self::I8,
×
1406
            geoengine_datatypes::raster::RasterDataType::I16 => Self::I16,
×
1407
            geoengine_datatypes::raster::RasterDataType::I32 => Self::I32,
×
1408
            geoengine_datatypes::raster::RasterDataType::I64 => Self::I64,
×
1409
            geoengine_datatypes::raster::RasterDataType::F32 => Self::F32,
×
1410
            geoengine_datatypes::raster::RasterDataType::F64 => Self::F64,
×
1411
        }
1412
    }
73✔
1413
}
1414

1415
impl From<RasterDataType> for geoengine_datatypes::raster::RasterDataType {
1416
    fn from(value: RasterDataType) -> Self {
5✔
1417
        match value {
5✔
1418
            RasterDataType::U8 => Self::U8,
5✔
1419
            RasterDataType::U16 => Self::U16,
×
1420
            RasterDataType::U32 => Self::U32,
×
1421
            RasterDataType::U64 => Self::U64,
×
1422
            RasterDataType::I8 => Self::I8,
×
1423
            RasterDataType::I16 => Self::I16,
×
1424
            RasterDataType::I32 => Self::I32,
×
1425
            RasterDataType::I64 => Self::I64,
×
1426
            RasterDataType::F32 => Self::F32,
×
1427
            RasterDataType::F64 => Self::F64,
×
1428
        }
1429
    }
5✔
1430
}
1431

1432
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
×
1433
#[serde(rename_all = "UPPERCASE")]
1434
pub enum ResamplingMethod {
1435
    Nearest,
1436
    Average,
1437
    Bilinear,
1438
    Cubic,
1439
    CubicSpline,
1440
    Lanczos,
1441
}
1442

1443
impl std::fmt::Display for ResamplingMethod {
1444
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19✔
1445
        match self {
19✔
1446
            ResamplingMethod::Nearest => write!(f, "NEAREST"),
19✔
1447
            ResamplingMethod::Average => write!(f, "AVERAGE"),
×
1448
            ResamplingMethod::Bilinear => write!(f, "BILINEAR"),
×
1449
            ResamplingMethod::Cubic => write!(f, "CUBIC"),
×
1450
            ResamplingMethod::CubicSpline => write!(f, "CUBICSPLINE"),
×
1451
            ResamplingMethod::Lanczos => write!(f, "LANCZOS"),
×
1452
        }
1453
    }
19✔
1454
}
1455

1456
/// `RgbaColor` defines a 32 bit RGB color with alpha value
1457
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
429✔
1458
pub struct RgbaColor(pub [u8; 4]);
1459

1460
impl ToSql for RgbaColor {
1461
    fn to_sql(
220✔
1462
        &self,
220✔
1463
        ty: &postgres_types::Type,
220✔
1464
        w: &mut bytes::BytesMut,
220✔
1465
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
220✔
1466
        let tuple = self.0.map(i16::from);
220✔
1467

1468
        let postgres_types::Kind::Domain(inner_type) = ty.kind() else {
220✔
1469
            return Err(Box::new(crate::error::Error::UnexpectedInvalidDbTypeConversion));
×
1470
        };
1471

1472
        <[i16; 4] as ToSql>::to_sql(&tuple, inner_type, w)
220✔
1473
    }
220✔
1474

1475
    fn accepts(ty: &postgres_types::Type) -> bool {
2✔
1476
        if ty.name() != "RgbaColor" {
2✔
1477
            return false;
×
1478
        }
2✔
1479
        let postgres_types::Kind::Domain(inner_type) = ty.kind() else {
2✔
1480
            return false;
×
1481
        };
1482

1483
        <[i16; 4] as ToSql>::accepts(inner_type)
2✔
1484
    }
2✔
1485

1486
    postgres_types::to_sql_checked!();
1487
}
1488

1489
impl<'a> FromSql<'a> for RgbaColor {
1490
    fn from_sql(
176✔
1491
        ty: &postgres_types::Type,
176✔
1492
        raw: &'a [u8],
176✔
1493
    ) -> Result<RgbaColor, Box<dyn std::error::Error + Sync + Send>> {
176✔
1494
        let array_ty = match ty.kind() {
176✔
1495
            postgres_types::Kind::Domain(inner_type) => inner_type,
175✔
1496
            _ => ty,
1✔
1497
        };
1498

1499
        let tuple = <[i16; 4] as FromSql>::from_sql(array_ty, raw)?;
176✔
1500

1501
        Ok(RgbaColor(tuple.map(|v| v as u8)))
704✔
1502
    }
176✔
1503

1504
    fn accepts(ty: &postgres_types::Type) -> bool {
8,266✔
1505
        type Target = [i16; 4];
8,266✔
1506

8,266✔
1507
        if <Target as FromSql>::accepts(ty) {
8,266✔
1508
            return true;
1✔
1509
        }
8,265✔
1510

8,265✔
1511
        if ty.name() != "RgbaColor" {
8,265✔
1512
            return false;
×
1513
        }
8,265✔
1514
        let postgres_types::Kind::Domain(inner_type) = ty.kind() else {
8,265✔
1515
            return false;
×
1516
        };
1517

1518
        <Target as FromSql>::accepts(inner_type)
8,265✔
1519
    }
8,266✔
1520
}
1521

1522
// manual implementation utoipa generates an integer field
1523
impl<'a> ToSchema<'a> for RgbaColor {
1524
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1525
        use utoipa::openapi::*;
2✔
1526
        (
2✔
1527
            "RgbaColor",
2✔
1528
            ArrayBuilder::new()
2✔
1529
                .items(ObjectBuilder::new().schema_type(SchemaType::Integer))
2✔
1530
                .min_items(Some(4))
2✔
1531
                .max_items(Some(4))
2✔
1532
                .into(),
2✔
1533
        )
2✔
1534
    }
2✔
1535
}
1536

1537
impl From<geoengine_datatypes::operations::image::RgbaColor> for RgbaColor {
1538
    fn from(color: geoengine_datatypes::operations::image::RgbaColor) -> Self {
357✔
1539
        Self(color.into_inner())
357✔
1540
    }
357✔
1541
}
1542

1543
impl From<RgbaColor> for geoengine_datatypes::operations::image::RgbaColor {
1544
    fn from(color: RgbaColor) -> Self {
42✔
1545
        Self::new(color.0[0], color.0[1], color.0[2], color.0[3])
42✔
1546
    }
42✔
1547
}
1548

1549
/// A container type for breakpoints that specify a value to color mapping
1550
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSql, FromSql)]
4,025✔
1551
pub struct Breakpoint {
1552
    pub value: NotNanF64,
1553
    pub color: RgbaColor,
1554
}
1555

1556
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
404✔
1557
pub struct NotNanF64(NotNan<f64>);
1558

1559
impl From<NotNan<f64>> for NotNanF64 {
1560
    fn from(value: NotNan<f64>) -> Self {
133✔
1561
        Self(value)
133✔
1562
    }
133✔
1563
}
1564

1565
impl From<NotNanF64> for NotNan<f64> {
1566
    fn from(value: NotNanF64) -> Self {
3✔
1567
        value.0
3✔
1568
    }
3✔
1569
}
1570

1571
impl ToSql for NotNanF64 {
1572
    fn to_sql(
143✔
1573
        &self,
143✔
1574
        ty: &postgres_types::Type,
143✔
1575
        w: &mut bytes::BytesMut,
143✔
1576
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
143✔
1577
        <f64 as ToSql>::to_sql(&self.0.into_inner(), ty, w)
143✔
1578
    }
143✔
1579

1580
    fn accepts(ty: &postgres_types::Type) -> bool {
2✔
1581
        <f64 as ToSql>::accepts(ty)
2✔
1582
    }
2✔
1583

1584
    postgres_types::to_sql_checked!();
1585
}
1586

1587
impl<'a> FromSql<'a> for NotNanF64 {
1588
    fn from_sql(
111✔
1589
        ty: &postgres_types::Type,
111✔
1590
        raw: &'a [u8],
111✔
1591
    ) -> Result<NotNanF64, Box<dyn std::error::Error + Sync + Send>> {
111✔
1592
        let value = <f64 as FromSql>::from_sql(ty, raw)?;
111✔
1593

1594
        Ok(NotNanF64(value.try_into()?))
111✔
1595
    }
111✔
1596

1597
    fn accepts(ty: &postgres_types::Type) -> bool {
1,196✔
1598
        <f64 as FromSql>::accepts(ty)
1,196✔
1599
    }
1,196✔
1600
}
1601

1602
// manual implementation because of NotNan
1603
impl<'a> ToSchema<'a> for Breakpoint {
1604
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1605
        use utoipa::openapi::*;
2✔
1606
        (
2✔
1607
            "Breakpoint",
2✔
1608
            ObjectBuilder::new()
2✔
1609
                .property("value", Object::with_type(SchemaType::Number))
2✔
1610
                .property("color", Ref::from_schema_name("RgbaColor"))
2✔
1611
                .into(),
2✔
1612
        )
2✔
1613
    }
2✔
1614
}
1615

1616
impl From<geoengine_datatypes::operations::image::Breakpoint> for Breakpoint {
1617
    fn from(breakpoint: geoengine_datatypes::operations::image::Breakpoint) -> Self {
122✔
1618
        Self {
122✔
1619
            value: breakpoint.value.into(),
122✔
1620
            color: breakpoint.color.into(),
122✔
1621
        }
122✔
1622
    }
122✔
1623
}
1624

1625
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
40✔
1626
#[serde(untagged, rename_all = "camelCase", into = "OverUnderColors")]
1627
pub enum DefaultColors {
1628
    #[serde(rename_all = "camelCase")]
1629
    DefaultColor { default_color: RgbaColor },
1630
    #[serde(rename_all = "camelCase")]
1631
    OverUnder(OverUnderColors),
1632
}
1633

1634
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
40✔
1635
#[serde(rename_all = "camelCase")]
1636
pub struct OverUnderColors {
1637
    pub over_color: RgbaColor,
1638
    pub under_color: RgbaColor,
1639
}
1640

1641
impl From<DefaultColors> for OverUnderColors {
1642
    fn from(value: DefaultColors) -> Self {
×
1643
        match value {
×
1644
            DefaultColors::DefaultColor { default_color } => Self {
×
1645
                over_color: default_color,
×
1646
                under_color: default_color,
×
1647
            },
×
1648
            DefaultColors::OverUnder(over_under) => over_under,
×
1649
        }
1650
    }
×
1651
}
1652

1653
impl From<DefaultColors> for geoengine_datatypes::operations::image::DefaultColors {
1654
    fn from(value: DefaultColors) -> Self {
×
1655
        match value {
×
1656
            DefaultColors::DefaultColor { default_color } => Self::DefaultColor {
×
1657
                default_color: default_color.into(),
×
1658
            },
×
1659
            DefaultColors::OverUnder(OverUnderColors {
1660
                over_color,
×
1661
                under_color,
×
1662
            }) => Self::OverUnder {
×
1663
                over_color: over_color.into(),
×
1664
                under_color: under_color.into(),
×
1665
            },
×
1666
        }
1667
    }
×
1668
}
1669

1670
impl From<geoengine_datatypes::operations::image::DefaultColors> for DefaultColors {
1671
    fn from(value: geoengine_datatypes::operations::image::DefaultColors) -> Self {
61✔
1672
        match value {
61✔
1673
            geoengine_datatypes::operations::image::DefaultColors::DefaultColor {
1674
                default_color,
×
1675
            } => Self::DefaultColor {
×
1676
                default_color: default_color.into(),
×
1677
            },
×
1678
            geoengine_datatypes::operations::image::DefaultColors::OverUnder {
1679
                over_color,
61✔
1680
                under_color,
61✔
1681
            } => Self::OverUnder(OverUnderColors {
61✔
1682
                over_color: over_color.into(),
61✔
1683
                under_color: under_color.into(),
61✔
1684
            }),
61✔
1685
        }
1686
    }
61✔
1687
}
1688

1689
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
56✔
1690
#[serde(rename_all = "camelCase")]
1691
pub struct LinearGradient {
1692
    pub breakpoints: Vec<Breakpoint>,
1693
    pub no_data_color: RgbaColor,
1694
    #[serde(flatten)]
1695
    pub color_fields: DefaultColors,
1696
}
1697

1698
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1699
#[serde(rename_all = "camelCase")]
1700
pub struct LogarithmicGradient {
1701
    pub breakpoints: Vec<Breakpoint>,
1702
    pub no_data_color: RgbaColor,
1703
    #[serde(flatten)]
1704
    pub color_fields: DefaultColors,
1705
}
1706

1707
/// A colorizer specifies a mapping between raster values and an output image
1708
/// There are different variants that perform different kinds of mapping.
1709
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
47✔
1710
#[serde(rename_all = "camelCase", tag = "type")]
1711
pub enum Colorizer {
1712
    #[serde(rename_all = "camelCase")]
1713
    LinearGradient(LinearGradient),
1714
    #[serde(rename_all = "camelCase")]
1715
    LogarithmicGradient(LogarithmicGradient),
1716
    #[serde(rename_all = "camelCase")]
1717
    Palette {
1718
        colors: Palette,
1719
        no_data_color: RgbaColor,
1720
        default_color: RgbaColor,
1721
    },
1722
    Rgba,
1723
}
1724

1725
impl From<geoengine_datatypes::operations::image::Colorizer> for Colorizer {
1726
    fn from(v: geoengine_datatypes::operations::image::Colorizer) -> Self {
65✔
1727
        match v {
65✔
1728
            geoengine_datatypes::operations::image::Colorizer::LinearGradient {
1729
                breakpoints,
61✔
1730
                no_data_color,
61✔
1731
                default_colors: color_fields,
61✔
1732
            } => Self::LinearGradient(LinearGradient {
61✔
1733
                breakpoints: breakpoints
61✔
1734
                    .into_iter()
61✔
1735
                    .map(Into::into)
61✔
1736
                    .collect::<Vec<Breakpoint>>(),
61✔
1737
                no_data_color: no_data_color.into(),
61✔
1738
                color_fields: color_fields.into(),
61✔
1739
            }),
61✔
1740
            geoengine_datatypes::operations::image::Colorizer::LogarithmicGradient {
1741
                breakpoints,
×
1742
                no_data_color,
×
1743
                default_colors: color_fields,
×
1744
            } => Self::LogarithmicGradient(LogarithmicGradient {
×
1745
                breakpoints: breakpoints
×
1746
                    .into_iter()
×
1747
                    .map(Into::into)
×
1748
                    .collect::<Vec<Breakpoint>>(),
×
1749
                no_data_color: no_data_color.into(),
×
1750
                color_fields: color_fields.into(),
×
1751
            }),
×
1752
            geoengine_datatypes::operations::image::Colorizer::Palette {
1753
                colors,
1✔
1754
                no_data_color,
1✔
1755
                default_color,
1✔
1756
            } => Self::Palette {
1✔
1757
                colors: colors.into(),
1✔
1758
                no_data_color: no_data_color.into(),
1✔
1759
                default_color: default_color.into(),
1✔
1760
            },
1✔
1761
            geoengine_datatypes::operations::image::Colorizer::Rgba => Self::Rgba,
3✔
1762
        }
1763
    }
65✔
1764
}
1765

1766
/// A map from value to color
1767
///
1768
/// It is assumed that is has at least one and at most 256 entries.
1769
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1770
#[serde(try_from = "SerializablePalette", into = "SerializablePalette")]
1771
pub struct Palette(pub HashMap<NotNan<f64>, RgbaColor>);
1772

1773
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1774
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
1✔
1775
        Self(
1✔
1776
            palette
1✔
1777
                .into_inner()
1✔
1778
                .into_iter()
1✔
1779
                .map(|(value, color)| (value, color.into()))
17✔
1780
                .collect(),
1✔
1781
        )
1✔
1782
    }
1✔
1783
}
1784

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

1790
impl From<Palette> for SerializablePalette {
1791
    fn from(palette: Palette) -> Self {
×
1792
        Self(
×
1793
            palette
×
1794
                .0
×
1795
                .into_iter()
×
1796
                .map(|(k, v)| (k.to_string(), v))
×
1797
                .collect(),
×
1798
        )
×
1799
    }
×
1800
}
1801

1802
impl TryFrom<SerializablePalette> for Palette {
1803
    type Error = <NotNan<f64> as FromStr>::Err;
1804

1805
    fn try_from(palette: SerializablePalette) -> Result<Self, Self::Error> {
1✔
1806
        let mut inner = HashMap::<NotNan<f64>, RgbaColor>::with_capacity(palette.0.len());
1✔
1807
        for (k, v) in palette.0 {
257✔
1808
            inner.insert(k.parse()?, v);
256✔
1809
        }
1810
        Ok(Self(inner))
1✔
1811
    }
1✔
1812
}
1813

1814
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
2✔
1815
pub struct RasterPropertiesKey {
1816
    pub domain: Option<String>,
1817
    pub key: String,
1818
}
1819

1820
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1821
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1822
        Self {
×
1823
            domain: value.domain,
×
1824
            key: value.key,
×
1825
        }
×
1826
    }
×
1827
}
1828

1829
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1830
    fn from(value: RasterPropertiesKey) -> Self {
×
1831
        Self {
×
1832
            domain: value.domain,
×
1833
            key: value.key,
×
1834
        }
×
1835
    }
×
1836
}
1837

1838
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
2✔
1839
pub enum RasterPropertiesEntryType {
1840
    Number,
1841
    String,
1842
}
1843

1844
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1845
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1846
        match value {
×
1847
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1848
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1849
        }
1850
    }
×
1851
}
1852

1853
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1854
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1855
        match value {
×
1856
            RasterPropertiesEntryType::Number => Self::Number,
×
1857
            RasterPropertiesEntryType::String => Self::String,
×
1858
        }
1859
    }
×
1860
}
1861

1862
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, ToSchema)]
28✔
1863
pub struct DateTimeParseFormat {
1864
    fmt: String,
1865
    has_tz: bool,
1866
    has_time: bool,
1867
}
1868

1869
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1870
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
4✔
1871
        Self {
4✔
1872
            fmt: value._to_parse_format().to_string(),
4✔
1873
            has_tz: value.has_tz(),
4✔
1874
            has_time: value.has_time(),
4✔
1875
        }
4✔
1876
    }
4✔
1877
}
1878

1879
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1880
    fn from(value: DateTimeParseFormat) -> Self {
4✔
1881
        Self::custom(value.fmt)
4✔
1882
    }
4✔
1883
}
1884

1885
impl DateTimeParseFormat {
1886
    // this is used as default value
1887
    pub fn unix() -> Self {
×
1888
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1889
    }
×
1890
}
1891

1892
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1893
pub struct NoGeometry;
1894

1895
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
1896
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
1897
        Self {}
×
1898
    }
×
1899
}
1900

1901
impl From<NoGeometry> for geoengine_datatypes::primitives::NoGeometry {
1902
    fn from(_: NoGeometry) -> Self {
×
1903
        Self {}
×
1904
    }
×
1905
}
1906

1907
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1908
pub struct MultiPoint {
1909
    coordinates: Vec<Coordinate2D>,
1910
}
1911

1912
impl From<geoengine_datatypes::primitives::MultiPoint> for MultiPoint {
1913
    fn from(value: geoengine_datatypes::primitives::MultiPoint) -> Self {
×
1914
        Self {
×
1915
            coordinates: value.points().iter().map(|x| (*x).into()).collect(),
×
1916
        }
×
1917
    }
×
1918
}
1919

1920
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
1921
    fn from(value: MultiPoint) -> Self {
×
1922
        Self::new(value.coordinates.into_iter().map(Into::into).collect()).unwrap()
×
1923
    }
×
1924
}
1925

1926
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1927
pub struct MultiLineString {
1928
    coordinates: Vec<Vec<Coordinate2D>>,
1929
}
1930

1931
impl From<geoengine_datatypes::primitives::MultiLineString> for MultiLineString {
1932
    fn from(value: geoengine_datatypes::primitives::MultiLineString) -> Self {
×
1933
        Self {
×
1934
            coordinates: value
×
1935
                .lines()
×
1936
                .iter()
×
1937
                .map(|x| x.iter().map(|x| (*x).into()).collect())
×
1938
                .collect(),
×
1939
        }
×
1940
    }
×
1941
}
1942

1943
impl From<MultiLineString> for geoengine_datatypes::primitives::MultiLineString {
1944
    fn from(value: MultiLineString) -> Self {
×
1945
        Self::new(
×
1946
            value
×
1947
                .coordinates
×
1948
                .into_iter()
×
1949
                .map(|x| x.into_iter().map(Into::into).collect())
×
1950
                .collect(),
×
1951
        )
×
1952
        .unwrap()
×
1953
    }
×
1954
}
1955

1956
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1957
pub struct MultiPolygon {
1958
    polygons: Vec<Vec<Vec<Coordinate2D>>>,
1959
}
1960

1961
impl From<geoengine_datatypes::primitives::MultiPolygon> for MultiPolygon {
1962
    fn from(value: geoengine_datatypes::primitives::MultiPolygon) -> Self {
×
1963
        Self {
×
1964
            polygons: value
×
1965
                .polygons()
×
1966
                .iter()
×
1967
                .map(|x| {
×
1968
                    x.iter()
×
1969
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1970
                        .collect()
×
1971
                })
×
1972
                .collect(),
×
1973
        }
×
1974
    }
×
1975
}
1976

1977
impl From<MultiPolygon> for geoengine_datatypes::primitives::MultiPolygon {
1978
    fn from(value: MultiPolygon) -> Self {
×
1979
        Self::new(
×
1980
            value
×
1981
                .polygons
×
1982
                .iter()
×
1983
                .map(|x| {
×
1984
                    x.iter()
×
1985
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1986
                        .collect()
×
1987
                })
×
1988
                .collect(),
×
1989
        )
×
1990
        .unwrap()
×
1991
    }
×
1992
}
1993

1994
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
4✔
1995
pub struct StringPair((String, String));
1996

1997
impl<'a> ToSchema<'a> for StringPair {
1998
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
6✔
1999
        use utoipa::openapi::*;
6✔
2000
        (
6✔
2001
            "StringPair",
6✔
2002
            ArrayBuilder::new()
6✔
2003
                .items(Object::with_type(SchemaType::String))
6✔
2004
                .min_items(Some(2))
6✔
2005
                .max_items(Some(2))
6✔
2006
                .into(),
6✔
2007
        )
6✔
2008
    }
6✔
2009
}
2010

2011
impl From<(String, String)> for StringPair {
2012
    fn from(value: (String, String)) -> Self {
26✔
2013
        Self(value)
26✔
2014
    }
26✔
2015
}
2016

2017
impl From<StringPair> for (String, String) {
2018
    fn from(value: StringPair) -> Self {
×
2019
        value.0
×
2020
    }
×
2021
}
2022

2023
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
2✔
2024
pub enum PlotOutputFormat {
2025
    JsonPlain,
2026
    JsonVega,
2027
    ImagePng,
2028
}
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