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

geo-engine / geoengine / 5741261921

02 Aug 2023 03:32PM UTC coverage: 88.958% (+0.05%) from 88.913%
5741261921

push

github

web-flow
Merge pull request #844 from geo-engine/pg-symbology-mapping

Pg symbology mapping

610 of 610 new or added lines in 10 files covered. (100.0%)

106476 of 119693 relevant lines covered (88.96%)

60487.29 hits per line

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

67.3
/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(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, ToSchema)]
26✔
426
#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
427
pub enum SpatialReferenceAuthority {
428
    Epsg,
429
    SrOrg,
430
    Iau2000,
431
    Esri,
432
}
433

434
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceAuthority>
435
    for SpatialReferenceAuthority
436
{
437
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceAuthority) -> Self {
112✔
438
        match value {
112✔
439
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Epsg => Self::Epsg,
111✔
440
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::SrOrg => Self::SrOrg,
1✔
441
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Iau2000 => {
442
                Self::Iau2000
×
443
            }
444
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Esri => Self::Esri,
×
445
        }
446
    }
112✔
447
}
448

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

462
impl FromStr for SpatialReferenceAuthority {
463
    type Err = error::Error;
464

465
    fn from_str(s: &str) -> Result<Self, Self::Err> {
38✔
466
        Ok(match s {
38✔
467
            "EPSG" => SpatialReferenceAuthority::Epsg,
38✔
468
            "SR-ORG" => SpatialReferenceAuthority::SrOrg,
×
469
            "IAU2000" => SpatialReferenceAuthority::Iau2000,
×
470
            "ESRI" => SpatialReferenceAuthority::Esri,
×
471
            _ => {
472
                return Err(error::Error::InvalidSpatialReferenceString {
×
473
                    spatial_reference_string: s.into(),
×
474
                })
×
475
            }
476
        })
477
    }
38✔
478
}
479

480
impl std::fmt::Display for SpatialReferenceAuthority {
481
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44✔
482
        write!(
44✔
483
            f,
44✔
484
            "{}",
44✔
485
            match self {
44✔
486
                SpatialReferenceAuthority::Epsg => "EPSG",
44✔
487
                SpatialReferenceAuthority::SrOrg => "SR-ORG",
×
488
                SpatialReferenceAuthority::Iau2000 => "IAU2000",
×
489
                SpatialReferenceAuthority::Esri => "ESRI",
×
490
            }
491
        )
492
    }
44✔
493
}
494

495
/// A spatial reference consists of an authority and a code
496
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
26✔
497
pub struct SpatialReference {
498
    authority: SpatialReferenceAuthority,
499
    code: u32,
500
}
501

502
impl SpatialReference {
503
    pub fn proj_string(self) -> Result<String> {
504
        match self.authority {
×
505
            SpatialReferenceAuthority::Epsg | SpatialReferenceAuthority::Iau2000 => {
506
                Ok(format!("{}:{}", self.authority, self.code))
17✔
507
            }
508
            // poor-mans integration of Meteosat Second Generation
509
            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()),
×
510
            SpatialReferenceAuthority::SrOrg | SpatialReferenceAuthority::Esri => {
511
                Err(error::Error::ProjStringUnresolvable { spatial_ref: self })
×
512
                //TODO: we might need to look them up somehow! Best solution would be a registry where we can store user definexd srs strings.
513
            }
514
        }
515
    }
17✔
516

517
    /// Return the srs-string "authority:code"
518
    #[allow(clippy::trivially_copy_pass_by_ref)]
519
    pub fn srs_string(&self) -> String {
17✔
520
        format!("{}:{}", self.authority, self.code)
17✔
521
    }
17✔
522
}
523

524
impl<'a> ToSchema<'a> for SpatialReference {
525
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
526
        use utoipa::openapi::*;
2✔
527
        (
2✔
528
            "SpatialReference",
2✔
529
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
530
        )
2✔
531
    }
2✔
532
}
533

534
impl From<geoengine_datatypes::spatial_reference::SpatialReference> for SpatialReference {
535
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReference) -> Self {
112✔
536
        Self {
112✔
537
            authority: (*value.authority()).into(),
112✔
538
            code: value.code(),
112✔
539
        }
112✔
540
    }
112✔
541
}
542

543
impl From<SpatialReference> for geoengine_datatypes::spatial_reference::SpatialReference {
544
    fn from(value: SpatialReference) -> Self {
13✔
545
        geoengine_datatypes::spatial_reference::SpatialReference::new(
13✔
546
            value.authority.into(),
13✔
547
            value.code,
13✔
548
        )
13✔
549
    }
13✔
550
}
551

552
impl SpatialReference {
553
    pub fn new(authority: SpatialReferenceAuthority, code: u32) -> Self {
43✔
554
        Self { authority, code }
43✔
555
    }
43✔
556

557
    pub fn authority(&self) -> &SpatialReferenceAuthority {
1✔
558
        &self.authority
1✔
559
    }
1✔
560

561
    pub fn code(self) -> u32 {
1✔
562
        self.code
1✔
563
    }
1✔
564
}
565

566
impl FromStr for SpatialReference {
567
    type Err = error::Error;
568

569
    fn from_str(s: &str) -> Result<Self, Self::Err> {
38✔
570
        let mut split = s.split(':');
38✔
571

38✔
572
        match (split.next(), split.next(), split.next()) {
38✔
573
            (Some(authority), Some(code), None) => Ok(Self::new(
38✔
574
                authority.parse()?,
38✔
575
                code.parse::<u32>().context(error::ParseU32)?,
38✔
576
            )),
577
            _ => Err(error::Error::InvalidSpatialReferenceString {
×
578
                spatial_reference_string: s.into(),
×
579
            }),
×
580
        }
581
    }
38✔
582
}
583

584
impl std::fmt::Display for SpatialReference {
585
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9✔
586
        write!(f, "{}:{}", self.authority, self.code)
9✔
587
    }
9✔
588
}
589

590
impl Serialize for SpatialReference {
591
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
1✔
592
    where
1✔
593
        S: Serializer,
1✔
594
    {
1✔
595
        serializer.serialize_str(&self.to_string())
1✔
596
    }
1✔
597
}
598

599
/// Helper struct for deserializing a `SpatialReferencce`
600
struct SpatialReferenceDeserializeVisitor;
601

602
impl<'de> Visitor<'de> for SpatialReferenceDeserializeVisitor {
603
    type Value = SpatialReference;
604

605
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
606
        formatter.write_str("a spatial reference in the form authority:code")
×
607
    }
×
608

609
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
16✔
610
    where
16✔
611
        E: serde::de::Error,
16✔
612
    {
16✔
613
        v.parse().map_err(serde::de::Error::custom)
16✔
614
    }
16✔
615
}
616

617
impl<'de> Deserialize<'de> for SpatialReference {
618
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
16✔
619
    where
16✔
620
        D: Deserializer<'de>,
16✔
621
    {
16✔
622
        deserializer.deserialize_str(SpatialReferenceDeserializeVisitor)
16✔
623
    }
16✔
624
}
625

626
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, ToSchema)]
90✔
627
pub enum SpatialReferenceOption {
628
    SpatialReference(SpatialReference),
629
    Unreferenced,
630
}
631

632
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceOption>
633
    for SpatialReferenceOption
634
{
635
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceOption) -> Self {
96✔
636
        match value {
96✔
637
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::SpatialReference(s) => {
83✔
638
                Self::SpatialReference(s.into())
83✔
639
            }
640
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::Unreferenced => {
641
                Self::Unreferenced
13✔
642
            }
643
        }
644
    }
96✔
645
}
646

647
impl From<SpatialReferenceOption>
648
    for geoengine_datatypes::spatial_reference::SpatialReferenceOption
649
{
650
    fn from(value: SpatialReferenceOption) -> Self {
9✔
651
        match value {
9✔
652
            SpatialReferenceOption::SpatialReference(sr) => Self::SpatialReference(sr.into()),
9✔
653
            SpatialReferenceOption::Unreferenced => Self::Unreferenced,
×
654
        }
655
    }
9✔
656
}
657

658
impl From<SpatialReference> for SpatialReferenceOption {
659
    fn from(spatial_reference: SpatialReference) -> Self {
14✔
660
        Self::SpatialReference(spatial_reference)
14✔
661
    }
14✔
662
}
663

664
impl std::fmt::Display for SpatialReferenceOption {
665
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11✔
666
        match self {
11✔
667
            SpatialReferenceOption::SpatialReference(p) => write!(f, "{p}"),
8✔
668
            SpatialReferenceOption::Unreferenced => Ok(()),
3✔
669
        }
670
    }
11✔
671
}
672

673
impl Serialize for SpatialReferenceOption {
674
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
11✔
675
    where
11✔
676
        S: Serializer,
11✔
677
    {
11✔
678
        serializer.serialize_str(&self.to_string())
11✔
679
    }
11✔
680
}
681

682
/// Helper struct for deserializing a `SpatialReferenceOption`
683
struct SpatialReferenceOptionDeserializeVisitor;
684

685
impl<'de> Visitor<'de> for SpatialReferenceOptionDeserializeVisitor {
686
    type Value = SpatialReferenceOption;
687

688
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
689
        formatter.write_str("a spatial reference in the form authority:code")
×
690
    }
×
691

692
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
19✔
693
    where
19✔
694
        E: serde::de::Error,
19✔
695
    {
19✔
696
        if v.is_empty() {
19✔
697
            return Ok(SpatialReferenceOption::Unreferenced);
5✔
698
        }
14✔
699

700
        let spatial_reference: SpatialReference = v.parse().map_err(serde::de::Error::custom)?;
14✔
701

702
        Ok(spatial_reference.into())
14✔
703
    }
19✔
704
}
705

706
impl<'de> Deserialize<'de> for SpatialReferenceOption {
707
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
19✔
708
    where
19✔
709
        D: Deserializer<'de>,
19✔
710
    {
19✔
711
        deserializer.deserialize_str(SpatialReferenceOptionDeserializeVisitor)
19✔
712
    }
19✔
713
}
714

715
impl From<Option<SpatialReference>> for SpatialReferenceOption {
716
    fn from(option: Option<SpatialReference>) -> Self {
×
717
        match option {
×
718
            Some(p) => SpatialReferenceOption::SpatialReference(p),
×
719
            None => SpatialReferenceOption::Unreferenced,
×
720
        }
721
    }
×
722
}
723

724
impl From<SpatialReferenceOption> for Option<SpatialReference> {
725
    fn from(option: SpatialReferenceOption) -> Self {
12✔
726
        match option {
12✔
727
            SpatialReferenceOption::SpatialReference(p) => Some(p),
12✔
728
            SpatialReferenceOption::Unreferenced => None,
×
729
        }
730
    }
12✔
731
}
732

733
/// An enum that contains all possible vector data types
734
#[derive(
735
    Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Deserialize, Serialize, Copy, Clone, ToSchema,
38✔
736
)]
737
pub enum VectorDataType {
738
    Data,
739
    MultiPoint,
740
    MultiLineString,
741
    MultiPolygon,
742
}
743

744
impl From<geoengine_datatypes::collections::VectorDataType> for VectorDataType {
745
    fn from(value: geoengine_datatypes::collections::VectorDataType) -> Self {
23✔
746
        match value {
23✔
747
            geoengine_datatypes::collections::VectorDataType::Data => Self::Data,
11✔
748
            geoengine_datatypes::collections::VectorDataType::MultiPoint => Self::MultiPoint,
12✔
749
            geoengine_datatypes::collections::VectorDataType::MultiLineString => {
750
                Self::MultiLineString
×
751
            }
752
            geoengine_datatypes::collections::VectorDataType::MultiPolygon => Self::MultiPolygon,
×
753
        }
754
    }
23✔
755
}
756

757
impl From<VectorDataType> for geoengine_datatypes::collections::VectorDataType {
758
    fn from(value: VectorDataType) -> Self {
8✔
759
        match value {
8✔
760
            VectorDataType::Data => Self::Data,
×
761
            VectorDataType::MultiPoint => Self::MultiPoint,
8✔
762
            VectorDataType::MultiLineString => Self::MultiLineString,
×
763
            VectorDataType::MultiPolygon => Self::MultiPolygon,
×
764
        }
765
    }
8✔
766
}
767

768
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, PartialOrd, Serialize, Default, ToSchema)]
70✔
769
pub struct Coordinate2D {
770
    pub x: f64,
771
    pub y: f64,
772
}
773

774
impl From<geoengine_datatypes::primitives::Coordinate2D> for Coordinate2D {
775
    fn from(coordinate: geoengine_datatypes::primitives::Coordinate2D) -> Self {
182✔
776
        Self {
182✔
777
            x: coordinate.x,
182✔
778
            y: coordinate.y,
182✔
779
        }
182✔
780
    }
182✔
781
}
782

783
impl From<Coordinate2D> for geoengine_datatypes::primitives::Coordinate2D {
784
    fn from(coordinate: Coordinate2D) -> Self {
17✔
785
        Self {
17✔
786
            x: coordinate.x,
17✔
787
            y: coordinate.y,
17✔
788
        }
17✔
789
    }
17✔
790
}
791

792
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema)]
5✔
793
#[serde(rename_all = "camelCase")]
794
/// A bounding box that includes all border points.
795
/// Note: may degenerate to a point!
796
pub struct BoundingBox2D {
797
    pub lower_left_coordinate: Coordinate2D,
798
    pub upper_right_coordinate: Coordinate2D,
799
}
800

801
impl From<geoengine_datatypes::primitives::BoundingBox2D> for BoundingBox2D {
802
    fn from(bbox: geoengine_datatypes::primitives::BoundingBox2D) -> Self {
28✔
803
        Self {
28✔
804
            lower_left_coordinate:
28✔
805
                geoengine_datatypes::primitives::AxisAlignedRectangle::lower_left(&bbox).into(),
28✔
806
            upper_right_coordinate:
28✔
807
                geoengine_datatypes::primitives::AxisAlignedRectangle::upper_right(&bbox).into(),
28✔
808
        }
28✔
809
    }
28✔
810
}
811

812
impl From<BoundingBox2D> for geoengine_datatypes::primitives::BoundingBox2D {
813
    fn from(bbox: BoundingBox2D) -> Self {
1✔
814
        Self::new_unchecked(
1✔
815
            bbox.lower_left_coordinate.into(),
1✔
816
            bbox.upper_right_coordinate.into(),
1✔
817
        )
1✔
818
    }
1✔
819
}
820

821
/// An object that composes the date and a timestamp with time zone.
822
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToSchema)]
2✔
823
pub struct DateTime {
824
    datetime: chrono::DateTime<chrono::Utc>,
825
}
826

827
impl FromStr for DateTime {
828
    type Err = geoengine_datatypes::primitives::DateTimeError;
829

830
    fn from_str(input: &str) -> Result<Self, Self::Err> {
×
831
        let date_time = chrono::DateTime::<chrono::FixedOffset>::from_str(input).map_err(|e| {
×
832
            Self::Err::DateParse {
×
833
                source: Box::new(e),
×
834
            }
×
835
        })?;
×
836

837
        Ok(date_time.into())
×
838
    }
×
839
}
840

841
impl From<chrono::DateTime<chrono::FixedOffset>> for DateTime {
842
    fn from(datetime: chrono::DateTime<chrono::FixedOffset>) -> Self {
×
843
        Self {
×
844
            datetime: datetime.into(),
×
845
        }
×
846
    }
×
847
}
848

849
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
52✔
850
#[serde(rename_all = "camelCase")]
851
pub enum FeatureDataType {
852
    Category,
853
    Int,
854
    Float,
855
    Text,
856
    Bool,
857
    DateTime,
858
}
859

860
impl From<geoengine_datatypes::primitives::FeatureDataType> for FeatureDataType {
861
    fn from(value: geoengine_datatypes::primitives::FeatureDataType) -> Self {
29✔
862
        match value {
29✔
863
            geoengine_datatypes::primitives::FeatureDataType::Category => Self::Category,
×
864
            geoengine_datatypes::primitives::FeatureDataType::Int => Self::Int,
10✔
865
            geoengine_datatypes::primitives::FeatureDataType::Float => Self::Float,
6✔
866
            geoengine_datatypes::primitives::FeatureDataType::Text => Self::Text,
13✔
867
            geoengine_datatypes::primitives::FeatureDataType::Bool => Self::Bool,
×
868
            geoengine_datatypes::primitives::FeatureDataType::DateTime => Self::DateTime,
×
869
        }
870
    }
29✔
871
}
872

873
impl From<FeatureDataType> for geoengine_datatypes::primitives::FeatureDataType {
874
    fn from(value: FeatureDataType) -> Self {
20✔
875
        match value {
20✔
876
            FeatureDataType::Category => Self::Category,
×
877
            FeatureDataType::Int => Self::Int,
4✔
878
            FeatureDataType::Float => Self::Float,
4✔
879
            FeatureDataType::Text => Self::Text,
12✔
880
            FeatureDataType::Bool => Self::Bool,
×
881
            FeatureDataType::DateTime => Self::DateTime,
×
882
        }
883
    }
20✔
884
}
885

886
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
96✔
887
#[serde(rename_all = "camelCase", tag = "type")]
888
pub enum Measurement {
889
    Unitless,
890
    Continuous(ContinuousMeasurement),
891
    Classification(ClassificationMeasurement),
892
}
893

894
impl From<geoengine_datatypes::primitives::Measurement> for Measurement {
895
    fn from(value: geoengine_datatypes::primitives::Measurement) -> Self {
91✔
896
        match value {
91✔
897
            geoengine_datatypes::primitives::Measurement::Unitless => Self::Unitless,
88✔
898
            geoengine_datatypes::primitives::Measurement::Continuous(cm) => {
1✔
899
                Self::Continuous(cm.into())
1✔
900
            }
901
            geoengine_datatypes::primitives::Measurement::Classification(cm) => {
2✔
902
                Self::Classification(cm.into())
2✔
903
            }
904
        }
905
    }
91✔
906
}
907

908
impl From<Measurement> for geoengine_datatypes::primitives::Measurement {
909
    fn from(value: Measurement) -> Self {
25✔
910
        match value {
25✔
911
            Measurement::Unitless => Self::Unitless,
24✔
912
            Measurement::Continuous(cm) => Self::Continuous(cm.into()),
×
913
            Measurement::Classification(cm) => Self::Classification(cm.into()),
1✔
914
        }
915
    }
25✔
916
}
917

918
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
2✔
919
pub struct ContinuousMeasurement {
920
    pub measurement: String,
921
    pub unit: Option<String>,
922
}
923

924
impl From<geoengine_datatypes::primitives::ContinuousMeasurement> for ContinuousMeasurement {
925
    fn from(value: geoengine_datatypes::primitives::ContinuousMeasurement) -> Self {
1✔
926
        Self {
1✔
927
            measurement: value.measurement,
1✔
928
            unit: value.unit,
1✔
929
        }
1✔
930
    }
1✔
931
}
932

933
impl From<ContinuousMeasurement> for geoengine_datatypes::primitives::ContinuousMeasurement {
934
    fn from(value: ContinuousMeasurement) -> Self {
×
935
        Self {
×
936
            measurement: value.measurement,
×
937
            unit: value.unit,
×
938
        }
×
939
    }
×
940
}
941

942
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
2✔
943
#[serde(
944
    try_from = "SerializableClassificationMeasurement",
945
    into = "SerializableClassificationMeasurement"
946
)]
947
pub struct ClassificationMeasurement {
948
    pub measurement: String,
949
    pub classes: HashMap<u8, String>,
950
}
951

952
impl From<geoengine_datatypes::primitives::ClassificationMeasurement>
953
    for ClassificationMeasurement
954
{
955
    fn from(value: geoengine_datatypes::primitives::ClassificationMeasurement) -> Self {
2✔
956
        Self {
2✔
957
            measurement: value.measurement,
2✔
958
            classes: value.classes,
2✔
959
        }
2✔
960
    }
2✔
961
}
962

963
impl From<ClassificationMeasurement>
964
    for geoengine_datatypes::primitives::ClassificationMeasurement
965
{
966
    fn from(value: ClassificationMeasurement) -> Self {
1✔
967
        Self {
1✔
968
            measurement: value.measurement,
1✔
969
            classes: value.classes,
1✔
970
        }
1✔
971
    }
1✔
972
}
973

974
/// A type that is solely for serde's serializability.
975
/// You cannot serialize floats as JSON map keys.
976
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
×
977
pub struct SerializableClassificationMeasurement {
978
    pub measurement: String,
979
    // use a BTreeMap to preserve the order of the keys
980
    pub classes: BTreeMap<String, String>,
981
}
982

983
impl From<ClassificationMeasurement> for SerializableClassificationMeasurement {
984
    fn from(measurement: ClassificationMeasurement) -> Self {
×
985
        let mut classes = BTreeMap::new();
×
986
        for (k, v) in measurement.classes {
×
987
            classes.insert(k.to_string(), v);
×
988
        }
×
989
        Self {
×
990
            measurement: measurement.measurement,
×
991
            classes,
×
992
        }
×
993
    }
×
994
}
995

996
impl TryFrom<SerializableClassificationMeasurement> for ClassificationMeasurement {
997
    type Error = <u8 as FromStr>::Err;
998

999
    fn try_from(measurement: SerializableClassificationMeasurement) -> Result<Self, Self::Error> {
×
1000
        let mut classes = HashMap::with_capacity(measurement.classes.len());
×
1001
        for (k, v) in measurement.classes {
×
1002
            classes.insert(k.parse::<u8>()?, v);
×
1003
        }
1004
        Ok(Self {
×
1005
            measurement: measurement.measurement,
×
1006
            classes,
×
1007
        })
×
1008
    }
×
1009
}
1010

1011
/// A partition of space that include the upper left but excludes the lower right coordinate
1012
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema)]
60✔
1013
#[serde(rename_all = "camelCase")]
1014
pub struct SpatialPartition2D {
1015
    upper_left_coordinate: Coordinate2D,
1016
    lower_right_coordinate: Coordinate2D,
1017
}
1018

1019
impl From<geoengine_datatypes::primitives::SpatialPartition2D> for SpatialPartition2D {
1020
    fn from(value: geoengine_datatypes::primitives::SpatialPartition2D) -> Self {
61✔
1021
        Self {
61✔
1022
            upper_left_coordinate: value.upper_left().into(),
61✔
1023
            lower_right_coordinate: value.lower_right().into(),
61✔
1024
        }
61✔
1025
    }
61✔
1026
}
1027

1028
impl From<SpatialPartition2D> for geoengine_datatypes::primitives::SpatialPartition2D {
1029
    fn from(value: SpatialPartition2D) -> Self {
5✔
1030
        Self::new_unchecked(
5✔
1031
            value.upper_left_coordinate.into(),
5✔
1032
            value.lower_right_coordinate.into(),
5✔
1033
        )
5✔
1034
    }
5✔
1035
}
1036

1037
/// A spatio-temporal rectangle with a specified resolution
1038
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
6✔
1039
#[serde(rename_all = "camelCase")]
1040
#[aliases(
1041
    VectorQueryRectangle = QueryRectangle<BoundingBox2D>,
1042
    RasterQueryRectangle = QueryRectangle<SpatialPartition2D>,
1043
    PlotQueryRectangle = QueryRectangle<BoundingBox2D>)
1044
]
1045
pub struct QueryRectangle<SpatialBounds> {
1046
    pub spatial_bounds: SpatialBounds,
1047
    pub time_interval: TimeInterval,
1048
    pub spatial_resolution: SpatialResolution,
1049
}
1050

1051
/// The spatial resolution in SRS units
1052
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema)]
60✔
1053
pub struct SpatialResolution {
1054
    pub x: f64,
1055
    pub y: f64,
1056
}
1057

1058
impl From<geoengine_datatypes::primitives::SpatialResolution> for SpatialResolution {
1059
    fn from(value: geoengine_datatypes::primitives::SpatialResolution) -> Self {
60✔
1060
        Self {
60✔
1061
            x: value.x,
60✔
1062
            y: value.y,
60✔
1063
        }
60✔
1064
    }
60✔
1065
}
1066

1067
impl From<SpatialResolution> for geoengine_datatypes::primitives::SpatialResolution {
1068
    fn from(value: SpatialResolution) -> Self {
5✔
1069
        Self {
5✔
1070
            x: value.x,
5✔
1071
            y: value.y,
5✔
1072
        }
5✔
1073
    }
5✔
1074
}
1075

1076
#[derive(Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, ToSchema)]
32✔
1077
#[repr(C)]
1078
pub struct TimeInstance(i64);
1079

1080
impl FromStr for TimeInstance {
1081
    type Err = geoengine_datatypes::primitives::DateTimeError;
1082

1083
    fn from_str(s: &str) -> Result<Self, Self::Err> {
×
1084
        let date_time = DateTime::from_str(s)?;
×
1085
        Ok(date_time.into())
×
1086
    }
×
1087
}
1088

1089
impl From<geoengine_datatypes::primitives::TimeInstance> for TimeInstance {
1090
    fn from(value: geoengine_datatypes::primitives::TimeInstance) -> Self {
256✔
1091
        Self(value.inner())
256✔
1092
    }
256✔
1093
}
1094

1095
impl From<TimeInstance> for geoengine_datatypes::primitives::TimeInstance {
1096
    fn from(value: TimeInstance) -> Self {
72✔
1097
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(value.inner())
72✔
1098
    }
72✔
1099
}
1100

1101
impl From<DateTime> for TimeInstance {
1102
    fn from(datetime: DateTime) -> Self {
×
1103
        Self::from(&datetime)
×
1104
    }
×
1105
}
1106

1107
impl From<&DateTime> for TimeInstance {
1108
    fn from(datetime: &DateTime) -> Self {
×
1109
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(
×
1110
            datetime.datetime.timestamp_millis(),
×
1111
        )
×
1112
        .into()
×
1113
    }
×
1114
}
1115

1116
impl TimeInstance {
1117
    pub const fn inner(self) -> i64 {
72✔
1118
        self.0
72✔
1119
    }
72✔
1120
}
1121

1122
impl<'de> Deserialize<'de> for TimeInstance {
1123
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
16✔
1124
    where
16✔
1125
        D: serde::Deserializer<'de>,
16✔
1126
    {
16✔
1127
        struct IsoStringOrUnixTimestamp;
16✔
1128

16✔
1129
        impl<'de> serde::de::Visitor<'de> for IsoStringOrUnixTimestamp {
16✔
1130
            type Value = TimeInstance;
16✔
1131

16✔
1132
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
16✔
1133
                formatter.write_str("RFC 3339 timestamp string or Unix timestamp integer")
×
1134
            }
×
1135

16✔
1136
            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
16✔
1137
            where
×
1138
                E: serde::de::Error,
×
1139
            {
×
1140
                TimeInstance::from_str(value).map_err(E::custom)
×
1141
            }
×
1142

16✔
1143
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
16✔
1144
            where
16✔
1145
                E: serde::de::Error,
16✔
1146
            {
16✔
1147
                geoengine_datatypes::primitives::TimeInstance::from_millis(v)
16✔
1148
                    .map(Into::into)
16✔
1149
                    .map_err(E::custom)
16✔
1150
            }
16✔
1151

16✔
1152
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
16✔
1153
            where
16✔
1154
                E: serde::de::Error,
16✔
1155
            {
16✔
1156
                Self::visit_i64(self, v as i64)
16✔
1157
            }
16✔
1158
        }
16✔
1159

16✔
1160
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
16✔
1161
    }
16✔
1162
}
1163

1164
/// A time granularity.
1165
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
8✔
1166
#[serde(rename_all = "camelCase")]
1167
pub enum TimeGranularity {
1168
    Millis,
1169
    Seconds,
1170
    Minutes,
1171
    Hours,
1172
    Days,
1173
    Months,
1174
    Years,
1175
}
1176

1177
impl From<geoengine_datatypes::primitives::TimeGranularity> for TimeGranularity {
1178
    fn from(value: geoengine_datatypes::primitives::TimeGranularity) -> Self {
4✔
1179
        match value {
4✔
1180
            geoengine_datatypes::primitives::TimeGranularity::Millis => Self::Millis,
×
1181
            geoengine_datatypes::primitives::TimeGranularity::Seconds => Self::Seconds,
×
1182
            geoengine_datatypes::primitives::TimeGranularity::Minutes => Self::Minutes,
×
1183
            geoengine_datatypes::primitives::TimeGranularity::Hours => Self::Hours,
×
1184
            geoengine_datatypes::primitives::TimeGranularity::Days => Self::Days,
×
1185
            geoengine_datatypes::primitives::TimeGranularity::Months => Self::Months,
4✔
1186
            geoengine_datatypes::primitives::TimeGranularity::Years => Self::Years,
×
1187
        }
1188
    }
4✔
1189
}
1190

1191
impl From<TimeGranularity> for geoengine_datatypes::primitives::TimeGranularity {
1192
    fn from(value: TimeGranularity) -> Self {
4✔
1193
        match value {
4✔
1194
            TimeGranularity::Millis => Self::Millis,
×
1195
            TimeGranularity::Seconds => Self::Seconds,
×
1196
            TimeGranularity::Minutes => Self::Minutes,
×
1197
            TimeGranularity::Hours => Self::Hours,
×
1198
            TimeGranularity::Days => Self::Days,
×
1199
            TimeGranularity::Months => Self::Months,
4✔
1200
            TimeGranularity::Years => Self::Years,
×
1201
        }
1202
    }
4✔
1203
}
1204

1205
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
20✔
1206
pub struct TimeStep {
1207
    pub granularity: TimeGranularity,
1208
    pub step: u32, // TODO: ensure on deserialization it is > 0
1209
}
1210

1211
impl From<geoengine_datatypes::primitives::TimeStep> for TimeStep {
1212
    fn from(value: geoengine_datatypes::primitives::TimeStep) -> Self {
4✔
1213
        Self {
4✔
1214
            granularity: value.granularity.into(),
4✔
1215
            step: value.step,
4✔
1216
        }
4✔
1217
    }
4✔
1218
}
1219

1220
impl From<TimeStep> for geoengine_datatypes::primitives::TimeStep {
1221
    fn from(value: TimeStep) -> Self {
4✔
1222
        Self {
4✔
1223
            granularity: value.granularity.into(),
4✔
1224
            step: value.step,
4✔
1225
        }
4✔
1226
    }
4✔
1227
}
1228

1229
/// Stores time intervals in ms in close-open semantic [start, end)
1230
#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
62✔
1231
pub struct TimeInterval {
1232
    start: TimeInstance,
1233
    end: TimeInstance,
1234
}
1235

1236
impl<'a> ToSchema<'a> for TimeInterval {
1237
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1238
        use utoipa::openapi::*;
2✔
1239
        (
2✔
1240
            "TimeInterval",
2✔
1241
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
1242
        )
2✔
1243
    }
2✔
1244
}
1245

1246
impl From<TimeInterval> for geoengine_datatypes::primitives::TimeInterval {
1247
    fn from(value: TimeInterval) -> Self {
36✔
1248
        geoengine_datatypes::primitives::TimeInterval::new_unchecked::<
36✔
1249
            geoengine_datatypes::primitives::TimeInstance,
36✔
1250
            geoengine_datatypes::primitives::TimeInstance,
36✔
1251
        >(value.start.into(), value.end.into())
36✔
1252
    }
36✔
1253
}
1254

1255
impl From<geoengine_datatypes::primitives::TimeInterval> for TimeInterval {
1256
    fn from(value: geoengine_datatypes::primitives::TimeInterval) -> Self {
120✔
1257
        Self {
120✔
1258
            start: value.start().into(),
120✔
1259
            end: value.end().into(),
120✔
1260
        }
120✔
1261
    }
120✔
1262
}
1263

1264
impl core::fmt::Debug for TimeInterval {
1265
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
×
1266
        write!(
×
1267
            f,
×
1268
            "TimeInterval [{}, {})",
×
1269
            self.start.inner(),
×
1270
            &self.end.inner()
×
1271
        )
×
1272
    }
×
1273
}
1274

1275
#[derive(
1276
    Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Deserialize, Serialize, Copy, Clone, ToSchema,
61✔
1277
)]
1278
pub enum RasterDataType {
1279
    U8,
1280
    U16,
1281
    U32,
1282
    U64,
1283
    I8,
1284
    I16,
1285
    I32,
1286
    I64,
1287
    F32,
1288
    F64,
1289
}
1290

1291
impl From<geoengine_datatypes::raster::RasterDataType> for RasterDataType {
1292
    fn from(value: geoengine_datatypes::raster::RasterDataType) -> Self {
61✔
1293
        match value {
61✔
1294
            geoengine_datatypes::raster::RasterDataType::U8 => Self::U8,
61✔
1295
            geoengine_datatypes::raster::RasterDataType::U16 => Self::U16,
×
1296
            geoengine_datatypes::raster::RasterDataType::U32 => Self::U32,
×
1297
            geoengine_datatypes::raster::RasterDataType::U64 => Self::U64,
×
1298
            geoengine_datatypes::raster::RasterDataType::I8 => Self::I8,
×
1299
            geoengine_datatypes::raster::RasterDataType::I16 => Self::I16,
×
1300
            geoengine_datatypes::raster::RasterDataType::I32 => Self::I32,
×
1301
            geoengine_datatypes::raster::RasterDataType::I64 => Self::I64,
×
1302
            geoengine_datatypes::raster::RasterDataType::F32 => Self::F32,
×
1303
            geoengine_datatypes::raster::RasterDataType::F64 => Self::F64,
×
1304
        }
1305
    }
61✔
1306
}
1307

1308
impl From<RasterDataType> for geoengine_datatypes::raster::RasterDataType {
1309
    fn from(value: RasterDataType) -> Self {
5✔
1310
        match value {
5✔
1311
            RasterDataType::U8 => Self::U8,
5✔
1312
            RasterDataType::U16 => Self::U16,
×
1313
            RasterDataType::U32 => Self::U32,
×
1314
            RasterDataType::U64 => Self::U64,
×
1315
            RasterDataType::I8 => Self::I8,
×
1316
            RasterDataType::I16 => Self::I16,
×
1317
            RasterDataType::I32 => Self::I32,
×
1318
            RasterDataType::I64 => Self::I64,
×
1319
            RasterDataType::F32 => Self::F32,
×
1320
            RasterDataType::F64 => Self::F64,
×
1321
        }
1322
    }
5✔
1323
}
1324

1325
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
×
1326
#[serde(rename_all = "UPPERCASE")]
1327
pub enum ResamplingMethod {
1328
    Nearest,
1329
    Average,
1330
    Bilinear,
1331
    Cubic,
1332
    CubicSpline,
1333
    Lanczos,
1334
}
1335

1336
impl std::fmt::Display for ResamplingMethod {
1337
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19✔
1338
        match self {
19✔
1339
            ResamplingMethod::Nearest => write!(f, "NEAREST"),
19✔
1340
            ResamplingMethod::Average => write!(f, "AVERAGE"),
×
1341
            ResamplingMethod::Bilinear => write!(f, "BILINEAR"),
×
1342
            ResamplingMethod::Cubic => write!(f, "CUBIC"),
×
1343
            ResamplingMethod::CubicSpline => write!(f, "CUBICSPLINE"),
×
1344
            ResamplingMethod::Lanczos => write!(f, "LANCZOS"),
×
1345
        }
1346
    }
19✔
1347
}
1348

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

1353
impl ToSql for RgbaColor {
1354
    fn to_sql(
220✔
1355
        &self,
220✔
1356
        ty: &postgres_types::Type,
220✔
1357
        w: &mut bytes::BytesMut,
220✔
1358
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
220✔
1359
        let tuple = self.0.map(i16::from);
220✔
1360

1361
        let postgres_types::Kind::Domain(inner_type) = ty.kind() else {
220✔
1362
            return Err(Box::new(crate::error::Error::UnexpectedInvalidDbTypeConversion));
×
1363
        };
1364

1365
        <[i16; 4] as ToSql>::to_sql(&tuple, inner_type, w)
220✔
1366
    }
220✔
1367

1368
    fn accepts(ty: &postgres_types::Type) -> bool {
2✔
1369
        if ty.name() != "RgbaColor" {
2✔
1370
            return false;
×
1371
        }
2✔
1372
        let postgres_types::Kind::Domain(inner_type) = ty.kind() else {
2✔
1373
            return false;
×
1374
        };
1375

1376
        <[i16; 4] as ToSql>::accepts(inner_type)
2✔
1377
    }
2✔
1378

1379
    postgres_types::to_sql_checked!();
1380
}
1381

1382
impl<'a> FromSql<'a> for RgbaColor {
1383
    fn from_sql(
176✔
1384
        ty: &postgres_types::Type,
176✔
1385
        raw: &'a [u8],
176✔
1386
    ) -> Result<RgbaColor, Box<dyn std::error::Error + Sync + Send>> {
176✔
1387
        let array_ty = match ty.kind() {
176✔
1388
            postgres_types::Kind::Domain(inner_type) => inner_type,
175✔
1389
            _ => ty,
1✔
1390
        };
1391

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

1394
        Ok(RgbaColor(tuple.map(|v| v as u8)))
704✔
1395
    }
176✔
1396

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

8,266✔
1400
        if <Target as FromSql>::accepts(ty) {
8,266✔
1401
            return true;
1✔
1402
        }
8,265✔
1403

8,265✔
1404
        if ty.name() != "RgbaColor" {
8,265✔
1405
            return false;
×
1406
        }
8,265✔
1407
        let postgres_types::Kind::Domain(inner_type) = ty.kind() else {
8,265✔
1408
            return false;
×
1409
        };
1410

1411
        <Target as FromSql>::accepts(inner_type)
8,265✔
1412
    }
8,266✔
1413
}
1414

1415
// manual implementation utoipa generates an integer field
1416
impl<'a> ToSchema<'a> for RgbaColor {
1417
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1418
        use utoipa::openapi::*;
2✔
1419
        (
2✔
1420
            "RgbaColor",
2✔
1421
            ArrayBuilder::new()
2✔
1422
                .items(ObjectBuilder::new().schema_type(SchemaType::Integer))
2✔
1423
                .min_items(Some(4))
2✔
1424
                .max_items(Some(4))
2✔
1425
                .into(),
2✔
1426
        )
2✔
1427
    }
2✔
1428
}
1429

1430
impl From<geoengine_datatypes::operations::image::RgbaColor> for RgbaColor {
1431
    fn from(color: geoengine_datatypes::operations::image::RgbaColor) -> Self {
357✔
1432
        Self(color.into_inner())
357✔
1433
    }
357✔
1434
}
1435

1436
impl From<RgbaColor> for geoengine_datatypes::operations::image::RgbaColor {
1437
    fn from(color: RgbaColor) -> Self {
42✔
1438
        Self::new(color.0[0], color.0[1], color.0[2], color.0[3])
42✔
1439
    }
42✔
1440
}
1441

1442
/// A container type for breakpoints that specify a value to color mapping
1443
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSql, FromSql)]
4,025✔
1444
pub struct Breakpoint {
1445
    pub value: NotNanF64,
1446
    pub color: RgbaColor,
1447
}
1448

1449
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
404✔
1450
pub struct NotNanF64(NotNan<f64>);
1451

1452
impl From<NotNan<f64>> for NotNanF64 {
1453
    fn from(value: NotNan<f64>) -> Self {
133✔
1454
        Self(value)
133✔
1455
    }
133✔
1456
}
1457

1458
impl From<NotNanF64> for NotNan<f64> {
1459
    fn from(value: NotNanF64) -> Self {
3✔
1460
        value.0
3✔
1461
    }
3✔
1462
}
1463

1464
impl ToSql for NotNanF64 {
1465
    fn to_sql(
143✔
1466
        &self,
143✔
1467
        ty: &postgres_types::Type,
143✔
1468
        w: &mut bytes::BytesMut,
143✔
1469
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
143✔
1470
        <f64 as ToSql>::to_sql(&self.0.into_inner(), ty, w)
143✔
1471
    }
143✔
1472

1473
    fn accepts(ty: &postgres_types::Type) -> bool {
2✔
1474
        <f64 as ToSql>::accepts(ty)
2✔
1475
    }
2✔
1476

1477
    postgres_types::to_sql_checked!();
1478
}
1479

1480
impl<'a> FromSql<'a> for NotNanF64 {
1481
    fn from_sql(
111✔
1482
        ty: &postgres_types::Type,
111✔
1483
        raw: &'a [u8],
111✔
1484
    ) -> Result<NotNanF64, Box<dyn std::error::Error + Sync + Send>> {
111✔
1485
        let value = <f64 as FromSql>::from_sql(ty, raw)?;
111✔
1486

1487
        Ok(NotNanF64(value.try_into()?))
111✔
1488
    }
111✔
1489

1490
    fn accepts(ty: &postgres_types::Type) -> bool {
1,196✔
1491
        <f64 as FromSql>::accepts(ty)
1,196✔
1492
    }
1,196✔
1493
}
1494

1495
// manual implementation because of NotNan
1496
impl<'a> ToSchema<'a> for Breakpoint {
1497
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1498
        use utoipa::openapi::*;
2✔
1499
        (
2✔
1500
            "Breakpoint",
2✔
1501
            ObjectBuilder::new()
2✔
1502
                .property("value", Object::with_type(SchemaType::Number))
2✔
1503
                .property("color", Ref::from_schema_name("RgbaColor"))
2✔
1504
                .into(),
2✔
1505
        )
2✔
1506
    }
2✔
1507
}
1508

1509
impl From<geoengine_datatypes::operations::image::Breakpoint> for Breakpoint {
1510
    fn from(breakpoint: geoengine_datatypes::operations::image::Breakpoint) -> Self {
122✔
1511
        Self {
122✔
1512
            value: breakpoint.value.into(),
122✔
1513
            color: breakpoint.color.into(),
122✔
1514
        }
122✔
1515
    }
122✔
1516
}
1517

1518
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
40✔
1519
#[serde(untagged, rename_all = "camelCase", into = "OverUnderColors")]
1520
pub enum DefaultColors {
1521
    #[serde(rename_all = "camelCase")]
1522
    DefaultColor { default_color: RgbaColor },
1523
    #[serde(rename_all = "camelCase")]
1524
    OverUnder(OverUnderColors),
1525
}
1526

1527
#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
40✔
1528
#[serde(rename_all = "camelCase")]
1529
pub struct OverUnderColors {
1530
    pub over_color: RgbaColor,
1531
    pub under_color: RgbaColor,
1532
}
1533

1534
impl From<DefaultColors> for OverUnderColors {
1535
    fn from(value: DefaultColors) -> Self {
×
1536
        match value {
×
1537
            DefaultColors::DefaultColor { default_color } => Self {
×
1538
                over_color: default_color,
×
1539
                under_color: default_color,
×
1540
            },
×
1541
            DefaultColors::OverUnder(over_under) => over_under,
×
1542
        }
1543
    }
×
1544
}
1545

1546
impl From<DefaultColors> for geoengine_datatypes::operations::image::DefaultColors {
1547
    fn from(value: DefaultColors) -> Self {
×
1548
        match value {
×
1549
            DefaultColors::DefaultColor { default_color } => Self::DefaultColor {
×
1550
                default_color: default_color.into(),
×
1551
            },
×
1552
            DefaultColors::OverUnder(OverUnderColors {
1553
                over_color,
×
1554
                under_color,
×
1555
            }) => Self::OverUnder {
×
1556
                over_color: over_color.into(),
×
1557
                under_color: under_color.into(),
×
1558
            },
×
1559
        }
1560
    }
×
1561
}
1562

1563
impl From<geoengine_datatypes::operations::image::DefaultColors> for DefaultColors {
1564
    fn from(value: geoengine_datatypes::operations::image::DefaultColors) -> Self {
61✔
1565
        match value {
61✔
1566
            geoengine_datatypes::operations::image::DefaultColors::DefaultColor {
1567
                default_color,
×
1568
            } => Self::DefaultColor {
×
1569
                default_color: default_color.into(),
×
1570
            },
×
1571
            geoengine_datatypes::operations::image::DefaultColors::OverUnder {
1572
                over_color,
61✔
1573
                under_color,
61✔
1574
            } => Self::OverUnder(OverUnderColors {
61✔
1575
                over_color: over_color.into(),
61✔
1576
                under_color: under_color.into(),
61✔
1577
            }),
61✔
1578
        }
1579
    }
61✔
1580
}
1581

1582
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
56✔
1583
#[serde(rename_all = "camelCase")]
1584
pub struct LinearGradient {
1585
    pub breakpoints: Vec<Breakpoint>,
1586
    pub no_data_color: RgbaColor,
1587
    #[serde(flatten)]
1588
    pub color_fields: DefaultColors,
1589
}
1590

1591
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1592
#[serde(rename_all = "camelCase")]
1593
pub struct LogarithmicGradient {
1594
    pub breakpoints: Vec<Breakpoint>,
1595
    pub no_data_color: RgbaColor,
1596
    #[serde(flatten)]
1597
    pub color_fields: DefaultColors,
1598
}
1599

1600
/// A colorizer specifies a mapping between raster values and an output image
1601
/// There are different variants that perform different kinds of mapping.
1602
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
47✔
1603
#[serde(rename_all = "camelCase", tag = "type")]
1604
pub enum Colorizer {
1605
    #[serde(rename_all = "camelCase")]
1606
    LinearGradient(LinearGradient),
1607
    #[serde(rename_all = "camelCase")]
1608
    LogarithmicGradient(LogarithmicGradient),
1609
    #[serde(rename_all = "camelCase")]
1610
    Palette {
1611
        colors: Palette,
1612
        no_data_color: RgbaColor,
1613
        default_color: RgbaColor,
1614
    },
1615
    Rgba,
1616
}
1617

1618
impl From<geoengine_datatypes::operations::image::Colorizer> for Colorizer {
1619
    fn from(v: geoengine_datatypes::operations::image::Colorizer) -> Self {
65✔
1620
        match v {
65✔
1621
            geoengine_datatypes::operations::image::Colorizer::LinearGradient {
1622
                breakpoints,
61✔
1623
                no_data_color,
61✔
1624
                default_colors: color_fields,
61✔
1625
            } => Self::LinearGradient(LinearGradient {
61✔
1626
                breakpoints: breakpoints
61✔
1627
                    .into_iter()
61✔
1628
                    .map(Into::into)
61✔
1629
                    .collect::<Vec<Breakpoint>>(),
61✔
1630
                no_data_color: no_data_color.into(),
61✔
1631
                color_fields: color_fields.into(),
61✔
1632
            }),
61✔
1633
            geoengine_datatypes::operations::image::Colorizer::LogarithmicGradient {
1634
                breakpoints,
×
1635
                no_data_color,
×
1636
                default_colors: color_fields,
×
1637
            } => Self::LogarithmicGradient(LogarithmicGradient {
×
1638
                breakpoints: breakpoints
×
1639
                    .into_iter()
×
1640
                    .map(Into::into)
×
1641
                    .collect::<Vec<Breakpoint>>(),
×
1642
                no_data_color: no_data_color.into(),
×
1643
                color_fields: color_fields.into(),
×
1644
            }),
×
1645
            geoengine_datatypes::operations::image::Colorizer::Palette {
1646
                colors,
1✔
1647
                no_data_color,
1✔
1648
                default_color,
1✔
1649
            } => Self::Palette {
1✔
1650
                colors: colors.into(),
1✔
1651
                no_data_color: no_data_color.into(),
1✔
1652
                default_color: default_color.into(),
1✔
1653
            },
1✔
1654
            geoengine_datatypes::operations::image::Colorizer::Rgba => Self::Rgba,
3✔
1655
        }
1656
    }
65✔
1657
}
1658

1659
/// A map from value to color
1660
///
1661
/// It is assumed that is has at least one and at most 256 entries.
1662
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1663
#[serde(try_from = "SerializablePalette", into = "SerializablePalette")]
1664
pub struct Palette(pub HashMap<NotNan<f64>, RgbaColor>);
1665

1666
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1667
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
1✔
1668
        Self(
1✔
1669
            palette
1✔
1670
                .into_inner()
1✔
1671
                .into_iter()
1✔
1672
                .map(|(value, color)| (value, color.into()))
17✔
1673
                .collect(),
1✔
1674
        )
1✔
1675
    }
1✔
1676
}
1677

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

1683
impl From<Palette> for SerializablePalette {
1684
    fn from(palette: Palette) -> Self {
×
1685
        Self(
×
1686
            palette
×
1687
                .0
×
1688
                .into_iter()
×
1689
                .map(|(k, v)| (k.to_string(), v))
×
1690
                .collect(),
×
1691
        )
×
1692
    }
×
1693
}
1694

1695
impl TryFrom<SerializablePalette> for Palette {
1696
    type Error = <NotNan<f64> as FromStr>::Err;
1697

1698
    fn try_from(palette: SerializablePalette) -> Result<Self, Self::Error> {
1✔
1699
        let mut inner = HashMap::<NotNan<f64>, RgbaColor>::with_capacity(palette.0.len());
1✔
1700
        for (k, v) in palette.0 {
257✔
1701
            inner.insert(k.parse()?, v);
256✔
1702
        }
1703
        Ok(Self(inner))
1✔
1704
    }
1✔
1705
}
1706

1707
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
2✔
1708
pub struct RasterPropertiesKey {
1709
    pub domain: Option<String>,
1710
    pub key: String,
1711
}
1712

1713
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1714
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1715
        Self {
×
1716
            domain: value.domain,
×
1717
            key: value.key,
×
1718
        }
×
1719
    }
×
1720
}
1721

1722
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1723
    fn from(value: RasterPropertiesKey) -> Self {
×
1724
        Self {
×
1725
            domain: value.domain,
×
1726
            key: value.key,
×
1727
        }
×
1728
    }
×
1729
}
1730

1731
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
2✔
1732
pub enum RasterPropertiesEntryType {
1733
    Number,
1734
    String,
1735
}
1736

1737
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1738
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1739
        match value {
×
1740
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1741
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1742
        }
1743
    }
×
1744
}
1745

1746
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1747
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1748
        match value {
×
1749
            RasterPropertiesEntryType::Number => Self::Number,
×
1750
            RasterPropertiesEntryType::String => Self::String,
×
1751
        }
1752
    }
×
1753
}
1754

1755
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, ToSchema)]
28✔
1756
pub struct DateTimeParseFormat {
1757
    fmt: String,
1758
    has_tz: bool,
1759
    has_time: bool,
1760
}
1761

1762
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1763
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
4✔
1764
        Self {
4✔
1765
            fmt: value._to_parse_format().to_string(),
4✔
1766
            has_tz: value.has_tz(),
4✔
1767
            has_time: value.has_time(),
4✔
1768
        }
4✔
1769
    }
4✔
1770
}
1771

1772
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1773
    fn from(value: DateTimeParseFormat) -> Self {
4✔
1774
        Self::custom(value.fmt)
4✔
1775
    }
4✔
1776
}
1777

1778
impl DateTimeParseFormat {
1779
    // this is used as default value
1780
    pub fn unix() -> Self {
×
1781
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1782
    }
×
1783
}
1784

1785
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1786
pub struct NoGeometry;
1787

1788
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
1789
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
1790
        Self {}
×
1791
    }
×
1792
}
1793

1794
impl From<NoGeometry> for geoengine_datatypes::primitives::NoGeometry {
1795
    fn from(_: NoGeometry) -> Self {
×
1796
        Self {}
×
1797
    }
×
1798
}
1799

1800
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1801
pub struct MultiPoint {
1802
    coordinates: Vec<Coordinate2D>,
1803
}
1804

1805
impl From<geoengine_datatypes::primitives::MultiPoint> for MultiPoint {
1806
    fn from(value: geoengine_datatypes::primitives::MultiPoint) -> Self {
×
1807
        Self {
×
1808
            coordinates: value.points().iter().map(|x| (*x).into()).collect(),
×
1809
        }
×
1810
    }
×
1811
}
1812

1813
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
1814
    fn from(value: MultiPoint) -> Self {
×
1815
        Self::new(value.coordinates.into_iter().map(Into::into).collect()).unwrap()
×
1816
    }
×
1817
}
1818

1819
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1820
pub struct MultiLineString {
1821
    coordinates: Vec<Vec<Coordinate2D>>,
1822
}
1823

1824
impl From<geoengine_datatypes::primitives::MultiLineString> for MultiLineString {
1825
    fn from(value: geoengine_datatypes::primitives::MultiLineString) -> Self {
×
1826
        Self {
×
1827
            coordinates: value
×
1828
                .lines()
×
1829
                .iter()
×
1830
                .map(|x| x.iter().map(|x| (*x).into()).collect())
×
1831
                .collect(),
×
1832
        }
×
1833
    }
×
1834
}
1835

1836
impl From<MultiLineString> for geoengine_datatypes::primitives::MultiLineString {
1837
    fn from(value: MultiLineString) -> Self {
×
1838
        Self::new(
×
1839
            value
×
1840
                .coordinates
×
1841
                .into_iter()
×
1842
                .map(|x| x.into_iter().map(Into::into).collect())
×
1843
                .collect(),
×
1844
        )
×
1845
        .unwrap()
×
1846
    }
×
1847
}
1848

1849
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1850
pub struct MultiPolygon {
1851
    polygons: Vec<Vec<Vec<Coordinate2D>>>,
1852
}
1853

1854
impl From<geoengine_datatypes::primitives::MultiPolygon> for MultiPolygon {
1855
    fn from(value: geoengine_datatypes::primitives::MultiPolygon) -> Self {
×
1856
        Self {
×
1857
            polygons: value
×
1858
                .polygons()
×
1859
                .iter()
×
1860
                .map(|x| {
×
1861
                    x.iter()
×
1862
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1863
                        .collect()
×
1864
                })
×
1865
                .collect(),
×
1866
        }
×
1867
    }
×
1868
}
1869

1870
impl From<MultiPolygon> for geoengine_datatypes::primitives::MultiPolygon {
1871
    fn from(value: MultiPolygon) -> Self {
×
1872
        Self::new(
×
1873
            value
×
1874
                .polygons
×
1875
                .iter()
×
1876
                .map(|x| {
×
1877
                    x.iter()
×
1878
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1879
                        .collect()
×
1880
                })
×
1881
                .collect(),
×
1882
        )
×
1883
        .unwrap()
×
1884
    }
×
1885
}
1886

1887
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
4✔
1888
pub struct StringPair((String, String));
1889

1890
impl<'a> ToSchema<'a> for StringPair {
1891
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
6✔
1892
        use utoipa::openapi::*;
6✔
1893
        (
6✔
1894
            "StringPair",
6✔
1895
            ArrayBuilder::new()
6✔
1896
                .items(Object::with_type(SchemaType::String))
6✔
1897
                .min_items(Some(2))
6✔
1898
                .max_items(Some(2))
6✔
1899
                .into(),
6✔
1900
        )
6✔
1901
    }
6✔
1902
}
1903

1904
impl From<(String, String)> for StringPair {
1905
    fn from(value: (String, String)) -> Self {
26✔
1906
        Self(value)
26✔
1907
    }
26✔
1908
}
1909

1910
impl From<StringPair> for (String, String) {
1911
    fn from(value: StringPair) -> Self {
×
1912
        value.0
×
1913
    }
×
1914
}
1915

1916
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
2✔
1917
pub enum PlotOutputFormat {
1918
    JsonPlain,
1919
    JsonVega,
1920
    ImagePng,
1921
}
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