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

geo-engine / geoengine / 13769157167

10 Mar 2025 04:00PM UTC coverage: 90.065% (-0.01%) from 90.076%
13769157167

Pull #1013

github

web-flow
Merge 46f938f1e into c96026921
Pull Request #1013: Update-utoipa

383 of 441 new or added lines in 24 files covered. (86.85%)

9 existing lines in 6 files now uncovered.

126092 of 140001 relevant lines covered (90.07%)

57491.5 hits per line

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

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

19
identifier!(DataProviderId);
20

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

27
// Identifier for datasets managed by Geo Engine
28
identifier!(DatasetId);
29

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

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

46
type_tag!(Internal);
47
type_tag!(External);
48

49
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
64✔
50
#[serde(rename_all = "camelCase")]
51
pub struct InternalDataId {
52
    #[schema(inline)]
53
    r#type: InternalTag,
54
    pub dataset_id: DatasetId,
55
}
56

57
impl DataId {
58
    pub fn internal(&self) -> Option<DatasetId> {
×
NEW
59
        if let Self::Internal(internal) = self {
×
NEW
60
            return Some(internal.dataset_id);
×
61
        }
×
62
        None
×
63
    }
×
64

65
    pub fn external(&self) -> Option<ExternalDataId> {
×
66
        if let Self::External(id) = self {
×
67
            return Some(id.clone());
×
68
        }
×
69
        None
×
70
    }
×
71
}
72

73
impl From<DatasetId> for DataId {
74
    fn from(value: DatasetId) -> Self {
×
NEW
75
        Self::Internal(InternalDataId {
×
NEW
76
            r#type: Default::default(),
×
NEW
77
            dataset_id: value,
×
NEW
78
        })
×
UNCOV
79
    }
×
80
}
81

82
impl From<ExternalDataId> for DataId {
83
    fn from(value: ExternalDataId) -> Self {
×
84
        Self::External(value)
×
85
    }
×
86
}
87

88
impl From<ExternalDataId> for geoengine_datatypes::dataset::DataId {
89
    fn from(value: ExternalDataId) -> Self {
×
90
        Self::External(value.into())
×
91
    }
×
92
}
93

94
impl From<DatasetId> for geoengine_datatypes::dataset::DataId {
95
    fn from(value: DatasetId) -> Self {
×
96
        Self::Internal {
×
97
            dataset_id: value.into(),
×
98
        }
×
99
    }
×
100
}
101

102
impl From<ExternalDataId> for geoengine_datatypes::dataset::ExternalDataId {
103
    fn from(value: ExternalDataId) -> Self {
×
104
        Self {
×
105
            provider_id: value.provider_id.into(),
×
106
            layer_id: value.layer_id.into(),
×
107
        }
×
108
    }
×
109
}
110

111
impl From<geoengine_datatypes::dataset::DataId> for DataId {
112
    fn from(id: geoengine_datatypes::dataset::DataId) -> Self {
4✔
113
        match id {
4✔
114
            geoengine_datatypes::dataset::DataId::Internal { dataset_id } => {
4✔
115
                Self::Internal(InternalDataId {
4✔
116
                    r#type: Default::default(),
4✔
117
                    dataset_id: dataset_id.into(),
4✔
118
                })
4✔
119
            }
120
            geoengine_datatypes::dataset::DataId::External(external_id) => {
×
121
                Self::External(external_id.into())
×
122
            }
123
        }
124
    }
4✔
125
}
126

127
impl From<DataId> for geoengine_datatypes::dataset::DataId {
128
    fn from(id: DataId) -> Self {
×
129
        match id {
×
NEW
130
            DataId::Internal(internal) => Self::Internal {
×
NEW
131
                dataset_id: internal.dataset_id.into(),
×
132
            },
×
133
            DataId::External(external_id) => Self::External(external_id.into()),
×
134
        }
135
    }
×
136
}
137

138
impl From<&DataId> for geoengine_datatypes::dataset::DataId {
139
    fn from(id: &DataId) -> Self {
×
140
        match id {
×
NEW
141
            DataId::Internal(internal) => Self::Internal {
×
NEW
142
                dataset_id: internal.dataset_id.into(),
×
143
            },
×
144
            DataId::External(external_id) => Self::External(external_id.into()),
×
145
        }
146
    }
×
147
}
148

149
impl From<geoengine_datatypes::dataset::DatasetId> for DatasetId {
150
    fn from(id: geoengine_datatypes::dataset::DatasetId) -> Self {
4✔
151
        Self(id.0)
4✔
152
    }
4✔
153
}
154

155
impl From<&DatasetId> for geoengine_datatypes::dataset::DatasetId {
UNCOV
156
    fn from(value: &DatasetId) -> Self {
×
UNCOV
157
        Self(value.0)
×
UNCOV
158
    }
×
159
}
160

161
impl From<&ExternalDataId> for geoengine_datatypes::dataset::ExternalDataId {
162
    fn from(value: &ExternalDataId) -> Self {
×
163
        Self {
×
164
            provider_id: value.provider_id.into(),
×
165
            layer_id: value.layer_id.clone().into(),
×
166
        }
×
167
    }
×
168
}
169

170
/// The user-facing identifier for loadable data.
171
/// It can be resolved into a [`DataId`].
172
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)]
173
// TODO: Have separate type once `geoengine_datatypes::dataset::NamedData` is not part of the API anymore.
174
#[serde(
175
    from = "geoengine_datatypes::dataset::NamedData",
176
    into = "geoengine_datatypes::dataset::NamedData"
177
)]
178
pub struct NamedData {
179
    pub namespace: Option<String>,
180
    pub provider: Option<String>,
181
    pub name: String,
182
}
183

184
impl From<geoengine_datatypes::dataset::NamedData> for NamedData {
185
    fn from(
1✔
186
        geoengine_datatypes::dataset::NamedData {
1✔
187
            namespace,
1✔
188
            provider,
1✔
189
            name,
1✔
190
        }: geoengine_datatypes::dataset::NamedData,
1✔
191
    ) -> Self {
1✔
192
        Self {
1✔
193
            namespace,
1✔
194
            provider,
1✔
195
            name,
1✔
196
        }
1✔
197
    }
1✔
198
}
199

200
impl From<&geoengine_datatypes::dataset::NamedData> for NamedData {
201
    fn from(named_data: &geoengine_datatypes::dataset::NamedData) -> Self {
×
202
        Self::from(named_data.clone())
×
203
    }
×
204
}
205

206
impl From<NamedData> for geoengine_datatypes::dataset::NamedData {
207
    fn from(
1✔
208
        NamedData {
1✔
209
            namespace,
1✔
210
            provider,
1✔
211
            name,
1✔
212
        }: NamedData,
1✔
213
    ) -> Self {
1✔
214
        Self {
1✔
215
            namespace,
1✔
216
            provider,
1✔
217
            name,
1✔
218
        }
1✔
219
    }
1✔
220
}
221

222
impl From<&NamedData> for geoengine_datatypes::dataset::NamedData {
223
    fn from(named_data: &NamedData) -> Self {
×
224
        Self::from(named_data.clone())
×
225
    }
×
226
}
227

228
impl PartialSchema for NamedData {
229
    fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::Schema> {
4✔
230
        use utoipa::openapi::schema::{ObjectBuilder, SchemaType, Type};
231
        ObjectBuilder::new()
4✔
232
            .schema_type(SchemaType::Type(Type::String))
4✔
233
            .into()
4✔
234
    }
4✔
235
}
236

237
impl ToSchema for NamedData {}
238

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

242
impl From<LayerId> for geoengine_datatypes::dataset::LayerId {
243
    fn from(value: LayerId) -> Self {
15✔
244
        Self(value.0)
15✔
245
    }
15✔
246
}
247

248
impl std::fmt::Display for LayerId {
249
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
×
250
        write!(f, "{}", self.0)
×
251
    }
×
252
}
253

254
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
76✔
255
#[serde(rename_all = "camelCase")]
256
pub struct ExternalDataId {
257
    #[schema(inline)]
258
    r#type: ExternalTag,
259
    pub provider_id: DataProviderId,
260
    pub layer_id: LayerId,
261
}
262

263
impl From<geoengine_datatypes::dataset::ExternalDataId> for ExternalDataId {
264
    fn from(id: geoengine_datatypes::dataset::ExternalDataId) -> Self {
×
265
        Self {
×
NEW
266
            r#type: Default::default(),
×
267
            provider_id: id.provider_id.into(),
×
268
            layer_id: id.layer_id.into(),
×
269
        }
×
270
    }
×
271
}
272

273
impl From<geoengine_datatypes::dataset::DataProviderId> for DataProviderId {
274
    fn from(id: geoengine_datatypes::dataset::DataProviderId) -> Self {
×
275
        Self(id.0)
×
276
    }
×
277
}
278

279
impl From<geoengine_datatypes::dataset::LayerId> for LayerId {
280
    fn from(id: geoengine_datatypes::dataset::LayerId) -> Self {
1✔
281
        Self(id.0)
1✔
282
    }
1✔
283
}
284

285
/// A spatial reference authority that is part of a spatial reference definition
286
#[derive(
287
    Debug,
288
    Copy,
289
    Clone,
290
    Eq,
291
    PartialEq,
292
    Ord,
293
    PartialOrd,
294
    Serialize,
295
    Deserialize,
296
    ToSchema,
12✔
297
    FromSql,
×
298
    ToSql,
299
)]
300
#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
301
pub enum SpatialReferenceAuthority {
302
    Epsg,
303
    SrOrg,
304
    Iau2000,
305
    Esri,
306
}
307

308
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceAuthority>
309
    for SpatialReferenceAuthority
310
{
311
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceAuthority) -> Self {
50✔
312
        match value {
50✔
313
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Epsg => Self::Epsg,
49✔
314
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::SrOrg => Self::SrOrg,
1✔
315
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Iau2000 => {
316
                Self::Iau2000
×
317
            }
318
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Esri => Self::Esri,
×
319
        }
320
    }
50✔
321
}
322

323
impl From<SpatialReferenceAuthority>
324
    for geoengine_datatypes::spatial_reference::SpatialReferenceAuthority
325
{
326
    fn from(value: SpatialReferenceAuthority) -> Self {
10✔
327
        match value {
10✔
328
            SpatialReferenceAuthority::Epsg => Self::Epsg,
10✔
329
            SpatialReferenceAuthority::SrOrg => Self::SrOrg,
×
330
            SpatialReferenceAuthority::Iau2000 => Self::Iau2000,
×
331
            SpatialReferenceAuthority::Esri => Self::Esri,
×
332
        }
333
    }
10✔
334
}
335

336
impl FromStr for SpatialReferenceAuthority {
337
    type Err = error::Error;
338

339
    fn from_str(s: &str) -> Result<Self, Self::Err> {
32✔
340
        Ok(match s {
32✔
341
            "EPSG" => SpatialReferenceAuthority::Epsg,
32✔
342
            "SR-ORG" => SpatialReferenceAuthority::SrOrg,
×
343
            "IAU2000" => SpatialReferenceAuthority::Iau2000,
×
344
            "ESRI" => SpatialReferenceAuthority::Esri,
×
345
            _ => {
346
                return Err(error::Error::InvalidSpatialReferenceString {
×
347
                    spatial_reference_string: s.into(),
×
348
                })
×
349
            }
350
        })
351
    }
32✔
352
}
353

354
impl std::fmt::Display for SpatialReferenceAuthority {
355
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46✔
356
        write!(
46✔
357
            f,
46✔
358
            "{}",
46✔
359
            match self {
46✔
360
                SpatialReferenceAuthority::Epsg => "EPSG",
46✔
361
                SpatialReferenceAuthority::SrOrg => "SR-ORG",
×
362
                SpatialReferenceAuthority::Iau2000 => "IAU2000",
×
363
                SpatialReferenceAuthority::Esri => "ESRI",
×
364
            }
365
        )
366
    }
46✔
367
}
368

369
/// A spatial reference consists of an authority and a code
370
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, FromSql, ToSql)]
×
371
pub struct SpatialReference {
372
    authority: SpatialReferenceAuthority,
373
    code: u32,
374
}
375

376
impl SpatialReference {
377
    pub fn proj_string(self) -> Result<String> {
19✔
378
        match self.authority {
×
379
            SpatialReferenceAuthority::Epsg | SpatialReferenceAuthority::Iau2000 => {
380
                Ok(format!("{}:{}", self.authority, self.code))
19✔
381
            }
382
            // poor-mans integration of Meteosat Second Generation
383
            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()),
×
384
            SpatialReferenceAuthority::SrOrg | SpatialReferenceAuthority::Esri => {
385
                Err(error::Error::ProjStringUnresolvable { spatial_ref: self })
×
386
                //TODO: we might need to look them up somehow! Best solution would be a registry where we can store user definexd srs strings.
387
            }
388
        }
389
    }
19✔
390

391
    /// Return the srs-string "authority:code"
392
    #[allow(clippy::trivially_copy_pass_by_ref)]
393
    pub fn srs_string(&self) -> String {
19✔
394
        format!("{}:{}", self.authority, self.code)
19✔
395
    }
19✔
396
}
397

398
impl From<geoengine_datatypes::spatial_reference::SpatialReference> for SpatialReference {
399
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReference) -> Self {
50✔
400
        Self {
50✔
401
            authority: (*value.authority()).into(),
50✔
402
            code: value.code(),
50✔
403
        }
50✔
404
    }
50✔
405
}
406

407
impl From<SpatialReference> for geoengine_datatypes::spatial_reference::SpatialReference {
408
    fn from(value: SpatialReference) -> Self {
10✔
409
        geoengine_datatypes::spatial_reference::SpatialReference::new(
10✔
410
            value.authority.into(),
10✔
411
            value.code,
10✔
412
        )
10✔
413
    }
10✔
414
}
415

416
impl SpatialReference {
417
    pub fn new(authority: SpatialReferenceAuthority, code: u32) -> Self {
37✔
418
        Self { authority, code }
37✔
419
    }
37✔
420

421
    pub fn authority(&self) -> &SpatialReferenceAuthority {
1✔
422
        &self.authority
1✔
423
    }
1✔
424

425
    pub fn code(self) -> u32 {
1✔
426
        self.code
1✔
427
    }
1✔
428
}
429

430
impl FromStr for SpatialReference {
431
    type Err = error::Error;
432

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

32✔
436
        match (split.next(), split.next(), split.next()) {
32✔
437
            (Some(authority), Some(code), None) => Ok(Self::new(
32✔
438
                authority.parse()?,
32✔
439
                code.parse::<u32>().context(error::ParseU32)?,
32✔
440
            )),
441
            _ => Err(error::Error::InvalidSpatialReferenceString {
×
442
                spatial_reference_string: s.into(),
×
443
            }),
×
444
        }
445
    }
32✔
446
}
447

448
impl std::fmt::Display for SpatialReference {
449
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7✔
450
        write!(f, "{}:{}", self.authority, self.code)
7✔
451
    }
7✔
452
}
453

454
impl Serialize for SpatialReference {
455
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
1✔
456
    where
1✔
457
        S: Serializer,
1✔
458
    {
1✔
459
        serializer.serialize_str(&self.to_string())
1✔
460
    }
1✔
461
}
462

463
/// Helper struct for deserializing a `SpatialReferencce`
464
struct SpatialReferenceDeserializeVisitor;
465

466
impl Visitor<'_> for SpatialReferenceDeserializeVisitor {
467
    type Value = SpatialReference;
468

469
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
470
        formatter.write_str("a spatial reference in the form authority:code")
×
471
    }
×
472

473
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
18✔
474
    where
18✔
475
        E: serde::de::Error,
18✔
476
    {
18✔
477
        v.parse().map_err(serde::de::Error::custom)
18✔
478
    }
18✔
479
}
480

481
impl<'de> Deserialize<'de> for SpatialReference {
482
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
18✔
483
    where
18✔
484
        D: Deserializer<'de>,
18✔
485
    {
18✔
486
        deserializer.deserialize_str(SpatialReferenceDeserializeVisitor)
18✔
487
    }
18✔
488
}
489

490
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
491
pub enum SpatialReferenceOption {
492
    SpatialReference(SpatialReference),
493
    Unreferenced,
494
}
495

496
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceOption>
497
    for SpatialReferenceOption
498
{
499
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceOption) -> Self {
19✔
500
        match value {
19✔
501
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::SpatialReference(s) => {
19✔
502
                Self::SpatialReference(s.into())
19✔
503
            }
504
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::Unreferenced => {
505
                Self::Unreferenced
×
506
            }
507
        }
508
    }
19✔
509
}
510

511
impl From<SpatialReferenceOption>
512
    for geoengine_datatypes::spatial_reference::SpatialReferenceOption
513
{
514
    fn from(value: SpatialReferenceOption) -> Self {
7✔
515
        match value {
7✔
516
            SpatialReferenceOption::SpatialReference(sr) => Self::SpatialReference(sr.into()),
6✔
517
            SpatialReferenceOption::Unreferenced => Self::Unreferenced,
1✔
518
        }
519
    }
7✔
520
}
521

522
impl From<SpatialReference> for SpatialReferenceOption {
523
    fn from(spatial_reference: SpatialReference) -> Self {
6✔
524
        Self::SpatialReference(spatial_reference)
6✔
525
    }
6✔
526
}
527

528
impl std::fmt::Display for SpatialReferenceOption {
529
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6✔
530
        match self {
6✔
531
            SpatialReferenceOption::SpatialReference(p) => write!(f, "{p}"),
6✔
532
            SpatialReferenceOption::Unreferenced => Ok(()),
×
533
        }
534
    }
6✔
535
}
536

537
impl Serialize for SpatialReferenceOption {
538
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
6✔
539
    where
6✔
540
        S: Serializer,
6✔
541
    {
6✔
542
        serializer.serialize_str(&self.to_string())
6✔
543
    }
6✔
544
}
545

546
/// Helper struct for deserializing a `SpatialReferenceOption`
547
struct SpatialReferenceOptionDeserializeVisitor;
548

549
impl Visitor<'_> for SpatialReferenceOptionDeserializeVisitor {
550
    type Value = SpatialReferenceOption;
551

552
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
553
        formatter.write_str("a spatial reference in the form authority:code")
×
554
    }
×
555

556
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
7✔
557
    where
7✔
558
        E: serde::de::Error,
7✔
559
    {
7✔
560
        if v.is_empty() {
7✔
561
            return Ok(SpatialReferenceOption::Unreferenced);
1✔
562
        }
6✔
563

564
        let spatial_reference: SpatialReference = v.parse().map_err(serde::de::Error::custom)?;
6✔
565

566
        Ok(spatial_reference.into())
6✔
567
    }
7✔
568
}
569

570
impl<'de> Deserialize<'de> for SpatialReferenceOption {
571
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
7✔
572
    where
7✔
573
        D: Deserializer<'de>,
7✔
574
    {
7✔
575
        deserializer.deserialize_str(SpatialReferenceOptionDeserializeVisitor)
7✔
576
    }
7✔
577
}
578

579
impl From<Option<SpatialReference>> for SpatialReferenceOption {
580
    fn from(option: Option<SpatialReference>) -> Self {
×
581
        match option {
×
582
            Some(p) => SpatialReferenceOption::SpatialReference(p),
×
583
            None => SpatialReferenceOption::Unreferenced,
×
584
        }
585
    }
×
586
}
587

588
impl From<SpatialReferenceOption> for Option<SpatialReference> {
589
    fn from(option: SpatialReferenceOption) -> Self {
14✔
590
        match option {
14✔
591
            SpatialReferenceOption::SpatialReference(p) => Some(p),
14✔
592
            SpatialReferenceOption::Unreferenced => None,
×
593
        }
594
    }
14✔
595
}
596

597
impl ToSql for SpatialReferenceOption {
598
    fn to_sql(
×
599
        &self,
×
600
        ty: &postgres_types::Type,
×
601
        out: &mut bytes::BytesMut,
×
602
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>>
×
603
    where
×
604
        Self: Sized,
×
605
    {
×
606
        match self {
×
607
            SpatialReferenceOption::SpatialReference(sref) => sref.to_sql(ty, out),
×
608
            SpatialReferenceOption::Unreferenced => Ok(postgres_types::IsNull::Yes),
×
609
        }
610
    }
×
611

612
    fn accepts(ty: &postgres_types::Type) -> bool
×
613
    where
×
614
        Self: Sized,
×
615
    {
×
616
        <SpatialReference as ToSql>::accepts(ty)
×
617
    }
×
618

619
    fn to_sql_checked(
×
620
        &self,
×
621
        ty: &postgres_types::Type,
×
622
        out: &mut bytes::BytesMut,
×
623
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
624
        match self {
×
625
            SpatialReferenceOption::SpatialReference(sref) => sref.to_sql_checked(ty, out),
×
626
            SpatialReferenceOption::Unreferenced => Ok(postgres_types::IsNull::Yes),
×
627
        }
628
    }
×
629
}
630

631
impl<'a> FromSql<'a> for SpatialReferenceOption {
632
    fn from_sql(
×
633
        ty: &postgres_types::Type,
×
634
        raw: &'a [u8],
×
635
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
636
        Ok(SpatialReferenceOption::SpatialReference(
×
637
            SpatialReference::from_sql(ty, raw)?,
×
638
        ))
639
    }
×
640

641
    fn from_sql_null(
×
642
        _: &postgres_types::Type,
×
643
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
644
        Ok(SpatialReferenceOption::Unreferenced)
×
645
    }
×
646

647
    fn accepts(ty: &postgres_types::Type) -> bool {
×
648
        <SpatialReference as FromSql>::accepts(ty)
×
649
    }
×
650
}
651

652
/// An enum that contains all possible vector data types
653
#[derive(
654
    Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Deserialize, Serialize, Copy, Clone, ToSchema,
464✔
655
)]
656
pub enum VectorDataType {
657
    Data,
658
    MultiPoint,
659
    MultiLineString,
660
    MultiPolygon,
661
}
662

663
impl From<geoengine_datatypes::collections::VectorDataType> for VectorDataType {
664
    fn from(value: geoengine_datatypes::collections::VectorDataType) -> Self {
2✔
665
        match value {
2✔
666
            geoengine_datatypes::collections::VectorDataType::Data => Self::Data,
×
667
            geoengine_datatypes::collections::VectorDataType::MultiPoint => Self::MultiPoint,
2✔
668
            geoengine_datatypes::collections::VectorDataType::MultiLineString => {
669
                Self::MultiLineString
×
670
            }
671
            geoengine_datatypes::collections::VectorDataType::MultiPolygon => Self::MultiPolygon,
×
672
        }
673
    }
2✔
674
}
675

676
impl From<VectorDataType> for geoengine_datatypes::collections::VectorDataType {
677
    fn from(value: VectorDataType) -> Self {
5✔
678
        match value {
5✔
679
            VectorDataType::Data => Self::Data,
1✔
680
            VectorDataType::MultiPoint => Self::MultiPoint,
4✔
681
            VectorDataType::MultiLineString => Self::MultiLineString,
×
682
            VectorDataType::MultiPolygon => Self::MultiPolygon,
×
683
        }
684
    }
5✔
685
}
686

687
#[derive(
688
    Clone,
689
    Copy,
690
    Debug,
691
    Deserialize,
692
    PartialEq,
693
    PartialOrd,
694
    Serialize,
695
    Default,
696
    ToSchema,
4,400✔
697
    ToSql,
×
698
    FromSql,
×
699
)]
700
pub struct Coordinate2D {
701
    pub x: f64,
702
    pub y: f64,
703
}
704

705
impl From<geoengine_datatypes::primitives::Coordinate2D> for Coordinate2D {
706
    fn from(coordinate: geoengine_datatypes::primitives::Coordinate2D) -> Self {
72✔
707
        Self {
72✔
708
            x: coordinate.x,
72✔
709
            y: coordinate.y,
72✔
710
        }
72✔
711
    }
72✔
712
}
713

714
impl From<Coordinate2D> for geoengine_datatypes::primitives::Coordinate2D {
715
    fn from(coordinate: Coordinate2D) -> Self {
26✔
716
        Self {
26✔
717
            x: coordinate.x,
26✔
718
            y: coordinate.y,
26✔
719
        }
26✔
720
    }
26✔
721
}
722

723
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema, ToSql, FromSql)]
708✔
724
#[serde(rename_all = "camelCase")]
725
/// A bounding box that includes all border points.
726
/// Note: may degenerate to a point!
727
pub struct BoundingBox2D {
728
    pub lower_left_coordinate: Coordinate2D,
729
    pub upper_right_coordinate: Coordinate2D,
730
}
731

732
impl From<geoengine_datatypes::primitives::BoundingBox2D> for BoundingBox2D {
733
    fn from(bbox: geoengine_datatypes::primitives::BoundingBox2D) -> Self {
28✔
734
        Self {
28✔
735
            lower_left_coordinate:
28✔
736
                geoengine_datatypes::primitives::AxisAlignedRectangle::lower_left(&bbox).into(),
28✔
737
            upper_right_coordinate:
28✔
738
                geoengine_datatypes::primitives::AxisAlignedRectangle::upper_right(&bbox).into(),
28✔
739
        }
28✔
740
    }
28✔
741
}
742

743
impl From<BoundingBox2D> for geoengine_datatypes::primitives::BoundingBox2D {
744
    fn from(bbox: BoundingBox2D) -> Self {
1✔
745
        Self::new_unchecked(
1✔
746
            bbox.lower_left_coordinate.into(),
1✔
747
            bbox.upper_right_coordinate.into(),
1✔
748
        )
1✔
749
    }
1✔
750
}
751

752
/// An object that composes the date and a timestamp with time zone.
753
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
754
pub struct DateTimeString {
755
    datetime: chrono::DateTime<chrono::Utc>,
756
}
757

758
// TODO: use derive ToSchema when OpenAPI derive does not break
759
impl PartialSchema for DateTimeString {
760
    fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
4✔
761
        use utoipa::openapi::schema::{ObjectBuilder, SchemaType, Type};
762
        ObjectBuilder::new()
4✔
763
            .schema_type(SchemaType::Type(Type::String))
4✔
764
            .into()
4✔
765
    }
4✔
766
}
767

768
impl ToSchema for DateTimeString {
769
    // fn name() -> Cow<'static, str> {
770
    //     "DateTime2".into()
771
    // }
772
}
773

774
impl FromStr for DateTimeString {
775
    type Err = geoengine_datatypes::primitives::DateTimeError;
776

777
    fn from_str(input: &str) -> Result<Self, Self::Err> {
×
778
        let date_time = chrono::DateTime::<chrono::FixedOffset>::from_str(input).map_err(|e| {
×
779
            Self::Err::DateParse {
×
780
                source: Box::new(e),
×
781
            }
×
782
        })?;
×
783

784
        Ok(date_time.into())
×
785
    }
×
786
}
787

788
impl From<chrono::DateTime<chrono::FixedOffset>> for DateTimeString {
789
    fn from(datetime: chrono::DateTime<chrono::FixedOffset>) -> Self {
×
790
        Self {
×
791
            datetime: datetime.into(),
×
792
        }
×
793
    }
×
794
}
795

796
impl From<geoengine_datatypes::primitives::DateTime> for DateTimeString {
797
    fn from(datetime: geoengine_datatypes::primitives::DateTime) -> Self {
×
798
        Self {
×
799
            datetime: datetime.into(),
×
800
        }
×
801
    }
×
802
}
803

804
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
428✔
805
#[serde(rename_all = "camelCase")]
806
pub enum FeatureDataType {
807
    Category,
808
    Int,
809
    Float,
810
    Text,
811
    Bool,
812
    DateTime,
813
}
814

815
impl From<geoengine_datatypes::primitives::FeatureDataType> for FeatureDataType {
816
    fn from(value: geoengine_datatypes::primitives::FeatureDataType) -> Self {
2✔
817
        match value {
2✔
818
            geoengine_datatypes::primitives::FeatureDataType::Category => Self::Category,
×
819
            geoengine_datatypes::primitives::FeatureDataType::Int => Self::Int,
1✔
820
            geoengine_datatypes::primitives::FeatureDataType::Float => Self::Float,
×
821
            geoengine_datatypes::primitives::FeatureDataType::Text => Self::Text,
1✔
822
            geoengine_datatypes::primitives::FeatureDataType::Bool => Self::Bool,
×
823
            geoengine_datatypes::primitives::FeatureDataType::DateTime => Self::DateTime,
×
824
        }
825
    }
2✔
826
}
827

828
impl From<FeatureDataType> for geoengine_datatypes::primitives::FeatureDataType {
829
    fn from(value: FeatureDataType) -> Self {
10✔
830
        match value {
10✔
831
            FeatureDataType::Category => Self::Category,
×
832
            FeatureDataType::Int => Self::Int,
2✔
833
            FeatureDataType::Float => Self::Float,
2✔
834
            FeatureDataType::Text => Self::Text,
6✔
835
            FeatureDataType::Bool => Self::Bool,
×
836
            FeatureDataType::DateTime => Self::DateTime,
×
837
        }
838
    }
10✔
839
}
840

841
type_tag!(Unitless);
842
type_tag!(Continuous);
843
type_tag!(Classification);
844

845
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
1,148✔
846
#[serde(rename_all = "camelCase", untagged)]
847
#[schema(discriminator = "type")]
848
pub enum Measurement {
849
    Unitless(UnitlessMeasurement),
850
    Continuous(ContinuousMeasurement),
851
    Classification(ClassificationMeasurement),
852
}
853

854
impl From<geoengine_datatypes::primitives::Measurement> for Measurement {
855
    fn from(value: geoengine_datatypes::primitives::Measurement) -> Self {
6✔
856
        match value {
6✔
857
            geoengine_datatypes::primitives::Measurement::Unitless => {
858
                Self::Unitless(UnitlessMeasurement {
2✔
859
                    r#type: Default::default(),
2✔
860
                })
2✔
861
            }
862
            geoengine_datatypes::primitives::Measurement::Continuous(cm) => {
4✔
863
                Self::Continuous(cm.into())
4✔
864
            }
865
            geoengine_datatypes::primitives::Measurement::Classification(cm) => {
×
866
                Self::Classification(cm.into())
×
867
            }
868
        }
869
    }
6✔
870
}
871

872
impl From<Measurement> for geoengine_datatypes::primitives::Measurement {
873
    fn from(value: Measurement) -> Self {
14✔
874
        match value {
14✔
875
            Measurement::Unitless { .. } => Self::Unitless,
10✔
876
            Measurement::Continuous(cm) => Self::Continuous(cm.into()),
4✔
877
            Measurement::Classification(cm) => Self::Classification(cm.into()),
×
878
        }
879
    }
14✔
880
}
881

882
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema, Default)]
1,152✔
883
pub struct UnitlessMeasurement {
884
    #[schema(inline)]
885
    r#type: UnitlessTag,
886
}
887

888
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
872✔
889
pub struct ContinuousMeasurement {
890
    #[schema(inline)]
891
    r#type: ContinuousTag,
892
    pub measurement: String,
893
    pub unit: Option<String>,
894
}
895

896
impl From<geoengine_datatypes::primitives::ContinuousMeasurement> for ContinuousMeasurement {
897
    fn from(value: geoengine_datatypes::primitives::ContinuousMeasurement) -> Self {
4✔
898
        Self {
4✔
899
            r#type: Default::default(),
4✔
900
            measurement: value.measurement,
4✔
901
            unit: value.unit,
4✔
902
        }
4✔
903
    }
4✔
904
}
905

906
impl From<ContinuousMeasurement> for geoengine_datatypes::primitives::ContinuousMeasurement {
907
    fn from(value: ContinuousMeasurement) -> Self {
4✔
908
        Self {
4✔
909
            measurement: value.measurement,
4✔
910
            unit: value.unit,
4✔
911
        }
4✔
912
    }
4✔
913
}
914

915
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
1,164✔
916
#[serde(
917
    try_from = "SerializableClassificationMeasurement",
918
    into = "SerializableClassificationMeasurement"
919
)]
920
pub struct ClassificationMeasurement {
921
    #[schema(inline)]
922
    r#type: ClassificationTag,
923
    pub measurement: String,
924
    pub classes: HashMap<u8, String>,
925
}
926

927
impl From<geoengine_datatypes::primitives::ClassificationMeasurement>
928
    for ClassificationMeasurement
929
{
930
    fn from(value: geoengine_datatypes::primitives::ClassificationMeasurement) -> Self {
×
931
        Self {
×
NEW
932
            r#type: Default::default(),
×
933
            measurement: value.measurement,
×
934
            classes: value.classes,
×
935
        }
×
936
    }
×
937
}
938

939
impl From<ClassificationMeasurement>
940
    for geoengine_datatypes::primitives::ClassificationMeasurement
941
{
942
    fn from(value: ClassificationMeasurement) -> Self {
×
943
        Self {
×
944
            measurement: value.measurement,
×
945
            classes: value.classes,
×
946
        }
×
947
    }
×
948
}
949

950
/// A type that is solely for serde's serializability.
951
/// You cannot serialize floats as JSON map keys.
952
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
953
pub struct SerializableClassificationMeasurement {
954
    pub measurement: String,
955
    // use a BTreeMap to preserve the order of the keys
956
    pub classes: BTreeMap<String, String>,
957
}
958

959
impl From<ClassificationMeasurement> for SerializableClassificationMeasurement {
960
    fn from(measurement: ClassificationMeasurement) -> Self {
×
961
        let mut classes = BTreeMap::new();
×
962
        for (k, v) in measurement.classes {
×
963
            classes.insert(k.to_string(), v);
×
964
        }
×
965
        Self {
×
966
            measurement: measurement.measurement,
×
967
            classes,
×
968
        }
×
969
    }
×
970
}
971

972
impl TryFrom<SerializableClassificationMeasurement> for ClassificationMeasurement {
973
    type Error = <u8 as FromStr>::Err;
974

975
    fn try_from(measurement: SerializableClassificationMeasurement) -> Result<Self, Self::Error> {
×
976
        let mut classes = HashMap::with_capacity(measurement.classes.len());
×
977
        for (k, v) in measurement.classes {
×
978
            classes.insert(k.parse::<u8>()?, v);
×
979
        }
980
        Ok(Self {
×
NEW
981
            r#type: Default::default(),
×
982
            measurement: measurement.measurement,
×
983
            classes,
×
984
        })
×
985
    }
×
986
}
987

988
/// A partition of space that include the upper left but excludes the lower right coordinate
989
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema, FromSql, ToSql)]
728✔
990
#[serde(rename_all = "camelCase")]
991
pub struct SpatialPartition2D {
992
    pub upper_left_coordinate: Coordinate2D,
993
    pub lower_right_coordinate: Coordinate2D,
994
}
995

996
impl From<geoengine_datatypes::primitives::SpatialPartition2D> for SpatialPartition2D {
997
    fn from(value: geoengine_datatypes::primitives::SpatialPartition2D) -> Self {
6✔
998
        Self {
6✔
999
            upper_left_coordinate: value.upper_left().into(),
6✔
1000
            lower_right_coordinate: value.lower_right().into(),
6✔
1001
        }
6✔
1002
    }
6✔
1003
}
1004

1005
impl From<SpatialPartition2D> for geoengine_datatypes::primitives::SpatialPartition2D {
1006
    fn from(value: SpatialPartition2D) -> Self {
10✔
1007
        Self::new_unchecked(
10✔
1008
            value.upper_left_coordinate.into(),
10✔
1009
            value.lower_right_coordinate.into(),
10✔
1010
        )
10✔
1011
    }
10✔
1012
}
1013

1014
/// A spatio-temporal rectangle with a specified resolution
1015
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
48✔
1016
#[serde(rename_all = "camelCase")]
1017
// #[aliases( // TODO: find solution?
1018
//     VectorQueryRectangle = QueryRectangle<BoundingBox2D>,
1019
//     RasterQueryRectangle = QueryRectangle<SpatialPartition2D>,
1020
//     PlotQueryRectangle = QueryRectangle<BoundingBox2D>)
1021
// ]
1022
pub struct QueryRectangle<SpatialBounds> {
1023
    pub spatial_bounds: SpatialBounds,
1024
    pub time_interval: TimeInterval,
1025
    pub spatial_resolution: SpatialResolution,
1026
}
1027

1028
pub type RasterQueryRectangle = QueryRectangle<SpatialPartition2D>;
1029
pub type VectorQueryRectangle = QueryRectangle<BoundingBox2D>;
1030
pub type PlotQueryRectangle = QueryRectangle<BoundingBox2D>;
1031

1032
impl
1033
    From<
1034
        geoengine_datatypes::primitives::QueryRectangle<
1035
            geoengine_datatypes::primitives::SpatialPartition2D,
1036
            geoengine_datatypes::primitives::BandSelection,
1037
        >,
1038
    > for RasterQueryRectangle
1039
{
1040
    fn from(
2✔
1041
        value: geoengine_datatypes::primitives::QueryRectangle<
2✔
1042
            geoengine_datatypes::primitives::SpatialPartition2D,
2✔
1043
            geoengine_datatypes::primitives::BandSelection,
2✔
1044
        >,
2✔
1045
    ) -> Self {
2✔
1046
        Self {
2✔
1047
            spatial_bounds: value.spatial_bounds.into(),
2✔
1048
            time_interval: value.time_interval.into(),
2✔
1049
            spatial_resolution: value.spatial_resolution.into(),
2✔
1050
        }
2✔
1051
    }
2✔
1052
}
1053

1054
impl From<QueryRectangle<SpatialPartition2D>>
1055
    for geoengine_datatypes::primitives::RasterQueryRectangle
1056
{
1057
    fn from(value: QueryRectangle<SpatialPartition2D>) -> Self {
3✔
1058
        Self {
3✔
1059
            spatial_bounds: value.spatial_bounds.into(),
3✔
1060
            time_interval: value.time_interval.into(),
3✔
1061
            spatial_resolution: value.spatial_resolution.into(),
3✔
1062
            attributes: geoengine_datatypes::primitives::BandSelection::first(), // TODO: adjust once API supports attribute selection
3✔
1063
        }
3✔
1064
    }
3✔
1065
}
1066

1067
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema)]
12✔
1068
pub struct BandSelection(pub Vec<usize>);
1069

1070
impl From<geoengine_datatypes::primitives::BandSelection> for BandSelection {
1071
    fn from(value: geoengine_datatypes::primitives::BandSelection) -> Self {
×
1072
        Self(value.as_vec().into_iter().map(|b| b as usize).collect())
×
1073
    }
×
1074
}
1075

1076
impl TryFrom<BandSelection> for geoengine_datatypes::primitives::BandSelection {
1077
    type Error = Error;
1078

1079
    fn try_from(value: BandSelection) -> Result<Self> {
6✔
1080
        geoengine_datatypes::primitives::BandSelection::new(
6✔
1081
            value.0.into_iter().map(|b| b as u32).collect(),
8✔
1082
        )
6✔
1083
        .map_err(Into::into)
6✔
1084
    }
6✔
1085
}
1086

1087
/// The spatial resolution in SRS units
1088
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema)]
784✔
1089
pub struct SpatialResolution {
1090
    pub x: f64,
1091
    pub y: f64,
1092
}
1093

1094
impl From<geoengine_datatypes::primitives::SpatialResolution> for SpatialResolution {
1095
    fn from(value: geoengine_datatypes::primitives::SpatialResolution) -> Self {
6✔
1096
        Self {
6✔
1097
            x: value.x,
6✔
1098
            y: value.y,
6✔
1099
        }
6✔
1100
    }
6✔
1101
}
1102

1103
impl From<SpatialResolution> for geoengine_datatypes::primitives::SpatialResolution {
1104
    fn from(value: SpatialResolution) -> Self {
10✔
1105
        Self {
10✔
1106
            x: value.x,
10✔
1107
            y: value.y,
10✔
1108
        }
10✔
1109
    }
10✔
1110
}
1111

1112
#[derive(
1113
    Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, ToSchema, FromSql, ToSql,
4,204✔
1114
)]
1115
#[repr(C)]
1116
#[postgres(transparent)]
1117
pub struct TimeInstance(i64);
1118

1119
impl FromStr for TimeInstance {
1120
    type Err = geoengine_datatypes::primitives::DateTimeError;
1121

1122
    fn from_str(s: &str) -> Result<Self, Self::Err> {
×
NEW
1123
        let date_time = DateTimeString::from_str(s)?;
×
1124
        Ok(date_time.into())
×
1125
    }
×
1126
}
1127

1128
impl From<geoengine_datatypes::primitives::TimeInstance> for TimeInstance {
1129
    fn from(value: geoengine_datatypes::primitives::TimeInstance) -> Self {
146✔
1130
        Self(value.inner())
146✔
1131
    }
146✔
1132
}
1133

1134
impl From<TimeInstance> for geoengine_datatypes::primitives::TimeInstance {
1135
    fn from(value: TimeInstance) -> Self {
82✔
1136
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(value.inner())
82✔
1137
    }
82✔
1138
}
1139

1140
impl From<DateTimeString> for TimeInstance {
NEW
1141
    fn from(datetime: DateTimeString) -> Self {
×
1142
        Self::from(&datetime)
×
1143
    }
×
1144
}
1145

1146
impl From<&DateTimeString> for TimeInstance {
NEW
1147
    fn from(datetime: &DateTimeString) -> Self {
×
1148
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(
×
1149
            datetime.datetime.timestamp_millis(),
×
1150
        )
×
1151
        .into()
×
1152
    }
×
1153
}
1154

1155
impl TimeInstance {
1156
    pub const fn inner(self) -> i64 {
82✔
1157
        self.0
82✔
1158
    }
82✔
1159
}
1160

1161
impl<'de> Deserialize<'de> for TimeInstance {
1162
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
18✔
1163
    where
18✔
1164
        D: serde::Deserializer<'de>,
18✔
1165
    {
18✔
1166
        struct IsoStringOrUnixTimestamp;
1167

1168
        impl serde::de::Visitor<'_> for IsoStringOrUnixTimestamp {
1169
            type Value = TimeInstance;
1170

1171
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
×
1172
                formatter.write_str("RFC 3339 timestamp string or Unix timestamp integer")
×
1173
            }
×
1174

1175
            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
×
1176
            where
×
1177
                E: serde::de::Error,
×
1178
            {
×
1179
                TimeInstance::from_str(value).map_err(E::custom)
×
1180
            }
×
1181

1182
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
18✔
1183
            where
18✔
1184
                E: serde::de::Error,
18✔
1185
            {
18✔
1186
                geoengine_datatypes::primitives::TimeInstance::from_millis(v)
18✔
1187
                    .map(Into::into)
18✔
1188
                    .map_err(E::custom)
18✔
1189
            }
18✔
1190

1191
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
18✔
1192
            where
18✔
1193
                E: serde::de::Error,
18✔
1194
            {
18✔
1195
                Self::visit_i64(self, v as i64)
18✔
1196
            }
18✔
1197
        }
1198

1199
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
18✔
1200
    }
18✔
1201
}
1202

1203
/// A time granularity.
1204
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, FromSql, ToSql)]
620✔
1205
#[serde(rename_all = "camelCase")]
1206
pub enum TimeGranularity {
1207
    Millis,
1208
    Seconds,
1209
    Minutes,
1210
    Hours,
1211
    Days,
1212
    Months,
1213
    Years,
1214
}
1215

1216
impl From<geoengine_datatypes::primitives::TimeGranularity> for TimeGranularity {
1217
    fn from(value: geoengine_datatypes::primitives::TimeGranularity) -> Self {
4✔
1218
        match value {
4✔
1219
            geoengine_datatypes::primitives::TimeGranularity::Millis => Self::Millis,
×
1220
            geoengine_datatypes::primitives::TimeGranularity::Seconds => Self::Seconds,
×
1221
            geoengine_datatypes::primitives::TimeGranularity::Minutes => Self::Minutes,
×
1222
            geoengine_datatypes::primitives::TimeGranularity::Hours => Self::Hours,
×
1223
            geoengine_datatypes::primitives::TimeGranularity::Days => Self::Days,
×
1224
            geoengine_datatypes::primitives::TimeGranularity::Months => Self::Months,
4✔
1225
            geoengine_datatypes::primitives::TimeGranularity::Years => Self::Years,
×
1226
        }
1227
    }
4✔
1228
}
1229

1230
impl From<TimeGranularity> for geoengine_datatypes::primitives::TimeGranularity {
1231
    fn from(value: TimeGranularity) -> Self {
4✔
1232
        match value {
4✔
1233
            TimeGranularity::Millis => Self::Millis,
×
1234
            TimeGranularity::Seconds => Self::Seconds,
×
1235
            TimeGranularity::Minutes => Self::Minutes,
×
1236
            TimeGranularity::Hours => Self::Hours,
×
1237
            TimeGranularity::Days => Self::Days,
×
1238
            TimeGranularity::Months => Self::Months,
4✔
1239
            TimeGranularity::Years => Self::Years,
×
1240
        }
1241
    }
4✔
1242
}
1243

1244
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, FromSql, ToSql)]
412✔
1245
pub struct TimeStep {
1246
    pub granularity: TimeGranularity,
1247
    pub step: u32, // TODO: ensure on deserialization it is > 0
1248
}
1249

1250
impl From<geoengine_datatypes::primitives::TimeStep> for TimeStep {
1251
    fn from(value: geoengine_datatypes::primitives::TimeStep) -> Self {
4✔
1252
        Self {
4✔
1253
            granularity: value.granularity.into(),
4✔
1254
            step: value.step,
4✔
1255
        }
4✔
1256
    }
4✔
1257
}
1258

1259
impl From<TimeStep> for geoengine_datatypes::primitives::TimeStep {
1260
    fn from(value: TimeStep) -> Self {
4✔
1261
        Self {
4✔
1262
            granularity: value.granularity.into(),
4✔
1263
            step: value.step,
4✔
1264
        }
4✔
1265
    }
4✔
1266
}
1267

1268
/// Stores time intervals in ms in close-open semantic [start, end)
1269
#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ToSql, FromSql, ToSchema)]
1,876✔
1270
pub struct TimeInterval {
1271
    start: TimeInstance,
1272
    end: TimeInstance,
1273
}
1274

1275
impl From<TimeInterval> for geoengine_datatypes::primitives::TimeInterval {
1276
    fn from(value: TimeInterval) -> Self {
41✔
1277
        geoengine_datatypes::primitives::TimeInterval::new_unchecked::<
41✔
1278
            geoengine_datatypes::primitives::TimeInstance,
41✔
1279
            geoengine_datatypes::primitives::TimeInstance,
41✔
1280
        >(value.start.into(), value.end.into())
41✔
1281
    }
41✔
1282
}
1283

1284
impl From<geoengine_datatypes::primitives::TimeInterval> for TimeInterval {
1285
    fn from(value: geoengine_datatypes::primitives::TimeInterval) -> Self {
64✔
1286
        Self {
64✔
1287
            start: value.start().into(),
64✔
1288
            end: value.end().into(),
64✔
1289
        }
64✔
1290
    }
64✔
1291
}
1292

1293
impl core::fmt::Debug for TimeInterval {
1294
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
×
1295
        write!(
×
1296
            f,
×
1297
            "TimeInterval [{}, {})",
×
1298
            self.start.inner(),
×
1299
            &self.end.inner()
×
1300
        )
×
1301
    }
×
1302
}
1303

1304
#[derive(
1305
    Debug,
1306
    Ord,
1307
    PartialOrd,
1308
    Eq,
1309
    PartialEq,
1310
    Hash,
1311
    Deserialize,
1312
    Serialize,
1313
    Copy,
1314
    Clone,
1315
    ToSchema,
860✔
1316
    FromSql,
×
1317
    ToSql,
1318
)]
1319
pub enum RasterDataType {
1320
    U8,
1321
    U16,
1322
    U32,
1323
    U64,
1324
    I8,
1325
    I16,
1326
    I32,
1327
    I64,
1328
    F32,
1329
    F64,
1330
}
1331

1332
impl From<geoengine_datatypes::raster::RasterDataType> for RasterDataType {
1333
    fn from(value: geoengine_datatypes::raster::RasterDataType) -> Self {
4✔
1334
        match value {
4✔
1335
            geoengine_datatypes::raster::RasterDataType::U8 => Self::U8,
4✔
1336
            geoengine_datatypes::raster::RasterDataType::U16 => Self::U16,
×
1337
            geoengine_datatypes::raster::RasterDataType::U32 => Self::U32,
×
1338
            geoengine_datatypes::raster::RasterDataType::U64 => Self::U64,
×
1339
            geoengine_datatypes::raster::RasterDataType::I8 => Self::I8,
×
1340
            geoengine_datatypes::raster::RasterDataType::I16 => Self::I16,
×
1341
            geoengine_datatypes::raster::RasterDataType::I32 => Self::I32,
×
1342
            geoengine_datatypes::raster::RasterDataType::I64 => Self::I64,
×
1343
            geoengine_datatypes::raster::RasterDataType::F32 => Self::F32,
×
1344
            geoengine_datatypes::raster::RasterDataType::F64 => Self::F64,
×
1345
        }
1346
    }
4✔
1347
}
1348

1349
impl From<RasterDataType> for geoengine_datatypes::raster::RasterDataType {
1350
    fn from(value: RasterDataType) -> Self {
4✔
1351
        match value {
4✔
1352
            RasterDataType::U8 => Self::U8,
4✔
1353
            RasterDataType::U16 => Self::U16,
×
1354
            RasterDataType::U32 => Self::U32,
×
1355
            RasterDataType::U64 => Self::U64,
×
1356
            RasterDataType::I8 => Self::I8,
×
1357
            RasterDataType::I16 => Self::I16,
×
1358
            RasterDataType::I32 => Self::I32,
×
1359
            RasterDataType::I64 => Self::I64,
×
1360
            RasterDataType::F32 => Self::F32,
×
1361
            RasterDataType::F64 => Self::F64,
×
1362
        }
1363
    }
4✔
1364
}
1365

1366
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
×
1367
#[serde(rename_all = "UPPERCASE")]
1368
pub enum ResamplingMethod {
1369
    Nearest,
1370
    Average,
1371
    Bilinear,
1372
    Cubic,
1373
    CubicSpline,
1374
    Lanczos,
1375
}
1376

1377
impl std::fmt::Display for ResamplingMethod {
1378
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
1379
        match self {
×
1380
            ResamplingMethod::Nearest => write!(f, "NEAREST"),
×
1381
            ResamplingMethod::Average => write!(f, "AVERAGE"),
×
1382
            ResamplingMethod::Bilinear => write!(f, "BILINEAR"),
×
1383
            ResamplingMethod::Cubic => write!(f, "CUBIC"),
×
1384
            ResamplingMethod::CubicSpline => write!(f, "CUBICSPLINE"),
×
1385
            ResamplingMethod::Lanczos => write!(f, "LANCZOS"),
×
1386
        }
1387
    }
×
1388
}
1389

1390
impl From<ResamplingMethod> for geoengine_datatypes::util::gdal::ResamplingMethod {
1391
    fn from(value: ResamplingMethod) -> Self {
×
1392
        match value {
×
1393
            ResamplingMethod::Nearest => Self::Nearest,
×
1394
            ResamplingMethod::Average => Self::Average,
×
1395
            ResamplingMethod::Bilinear => Self::Bilinear,
×
1396
            ResamplingMethod::Cubic => Self::Cubic,
×
1397
            ResamplingMethod::CubicSpline => Self::CubicSpline,
×
1398
            ResamplingMethod::Lanczos => Self::Lanczos,
×
1399
        }
1400
    }
×
1401
}
1402

1403
/// `RgbaColor` defines a 32 bit RGB color with alpha value
1404
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1405
pub struct RgbaColor(pub [u8; 4]);
1406

1407
impl PartialSchema for RgbaColor {
1408
    fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::Schema> {
10,384✔
1409
        use utoipa::openapi::schema::{ArrayBuilder, ObjectBuilder, SchemaType, Type};
1410
        ArrayBuilder::new()
10,384✔
1411
            .items(ObjectBuilder::new().schema_type(SchemaType::Type(Type::Integer)))
10,384✔
1412
            .min_items(Some(4))
10,384✔
1413
            .max_items(Some(4))
10,384✔
1414
            .into()
10,384✔
1415
    }
10,384✔
1416
}
1417

1418
// manual implementation utoipa generates an integer field
1419
impl ToSchema for RgbaColor {}
1420

1421
impl From<geoengine_datatypes::operations::image::RgbaColor> for RgbaColor {
1422
    fn from(color: geoengine_datatypes::operations::image::RgbaColor) -> Self {
27✔
1423
        Self(color.into_inner())
27✔
1424
    }
27✔
1425
}
1426

1427
impl From<RgbaColor> for geoengine_datatypes::operations::image::RgbaColor {
1428
    fn from(color: RgbaColor) -> Self {
22✔
1429
        Self::new(color.0[0], color.0[1], color.0[2], color.0[3])
22✔
1430
    }
22✔
1431
}
1432

1433
/// A container type for breakpoints that specify a value to color mapping
1434
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
1435
pub struct Breakpoint {
1436
    pub value: NotNanF64,
1437
    pub color: RgbaColor,
1438
}
1439

1440
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
1441
pub struct NotNanF64(NotNan<f64>);
1442

1443
impl From<NotNan<f64>> for NotNanF64 {
1444
    fn from(value: NotNan<f64>) -> Self {
10✔
1445
        Self(value)
10✔
1446
    }
10✔
1447
}
1448

1449
impl From<NotNanF64> for NotNan<f64> {
1450
    fn from(value: NotNanF64) -> Self {
8✔
1451
        value.0
8✔
1452
    }
8✔
1453
}
1454

1455
impl ToSql for NotNanF64 {
1456
    fn to_sql(
×
1457
        &self,
×
1458
        ty: &postgres_types::Type,
×
1459
        w: &mut bytes::BytesMut,
×
1460
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
1461
        <f64 as ToSql>::to_sql(&self.0.into_inner(), ty, w)
×
1462
    }
×
1463

1464
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1465
        <f64 as ToSql>::accepts(ty)
×
1466
    }
×
1467

1468
    postgres_types::to_sql_checked!();
1469
}
1470

1471
impl<'a> FromSql<'a> for NotNanF64 {
1472
    fn from_sql(
×
1473
        ty: &postgres_types::Type,
×
1474
        raw: &'a [u8],
×
1475
    ) -> Result<NotNanF64, Box<dyn std::error::Error + Sync + Send>> {
×
1476
        let value = <f64 as FromSql>::from_sql(ty, raw)?;
×
1477

1478
        Ok(NotNanF64(value.try_into()?))
×
1479
    }
×
1480

1481
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1482
        <f64 as FromSql>::accepts(ty)
×
1483
    }
×
1484
}
1485

1486
impl PartialSchema for Breakpoint {
1487
    fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::Schema> {
2,084✔
1488
        use utoipa::openapi::schema::{Object, ObjectBuilder, Ref, SchemaType, Type};
1489
        ObjectBuilder::new()
2,084✔
1490
            .property("value", Object::with_type(SchemaType::Type(Type::Number)))
2,084✔
1491
            .property("color", Ref::from_schema_name("RgbaColor"))
2,084✔
1492
            .required("value")
2,084✔
1493
            .required("color")
2,084✔
1494
            .into()
2,084✔
1495
    }
2,084✔
1496
}
1497

1498
// manual implementation because of NotNan
1499
impl ToSchema for Breakpoint {}
1500

1501
impl From<geoengine_datatypes::operations::image::Breakpoint> for Breakpoint {
1502
    fn from(breakpoint: geoengine_datatypes::operations::image::Breakpoint) -> Self {
10✔
1503
        Self {
10✔
1504
            value: breakpoint.value.into(),
10✔
1505
            color: breakpoint.color.into(),
10✔
1506
        }
10✔
1507
    }
10✔
1508
}
1509

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

1519
type_tag!(LinearGradient);
1520
type_tag!(LogarithmicGradient);
1521
type_tag!(Palette);
1522

1523
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
4,156✔
1524
#[serde(rename_all = "camelCase")]
1525
pub struct LinearGradient {
1526
    #[schema(inline)]
1527
    r#type: LogarithmicGradientTag,
1528
    pub breakpoints: Vec<Breakpoint>,
1529
    pub no_data_color: RgbaColor,
1530
    pub over_color: RgbaColor,
1531
    pub under_color: RgbaColor,
1532
}
1533

1534
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
4,156✔
1535
#[serde(rename_all = "camelCase")]
1536
pub struct LogarithmicGradient {
1537
    #[schema(inline)]
1538
    r#type: LogarithmicGradientTag,
1539
    pub breakpoints: Vec<Breakpoint>,
1540
    pub no_data_color: RgbaColor,
1541
    pub over_color: RgbaColor,
1542
    pub under_color: RgbaColor,
1543
}
1544

1545
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
4,144✔
1546
#[serde(rename_all = "camelCase")]
1547
pub struct PaletteColorizer {
1548
    #[schema(inline)]
1549
    r#type: PaletteTag,
1550
    pub colors: Palette,
1551
    pub no_data_color: RgbaColor,
1552
    pub default_color: RgbaColor,
1553
}
1554

1555
/// A colorizer specifies a mapping between raster values and an output image
1556
/// There are different variants that perform different kinds of mapping.
1557
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
4,140✔
1558
#[serde(rename_all = "camelCase", untagged)]
1559
#[schema(discriminator = "type")]
1560
pub enum Colorizer {
1561
    LinearGradient(LinearGradient),
1562
    LogarithmicGradient(LogarithmicGradient),
1563
    Palette(PaletteColorizer),
1564
}
1565

1566
impl From<geoengine_datatypes::operations::image::Colorizer> for Colorizer {
1567
    fn from(v: geoengine_datatypes::operations::image::Colorizer) -> Self {
5✔
1568
        match v {
5✔
1569
            geoengine_datatypes::operations::image::Colorizer::LinearGradient {
1570
                breakpoints,
5✔
1571
                no_data_color,
5✔
1572
                over_color,
5✔
1573
                under_color,
5✔
1574
            } => Self::LinearGradient(LinearGradient {
5✔
1575
                r#type: Default::default(),
5✔
1576
                breakpoints: breakpoints
5✔
1577
                    .into_iter()
5✔
1578
                    .map(Into::into)
5✔
1579
                    .collect::<Vec<Breakpoint>>(),
5✔
1580
                no_data_color: no_data_color.into(),
5✔
1581
                over_color: over_color.into(),
5✔
1582
                under_color: under_color.into(),
5✔
1583
            }),
5✔
1584
            geoengine_datatypes::operations::image::Colorizer::LogarithmicGradient {
1585
                breakpoints,
×
1586
                no_data_color,
×
1587
                over_color,
×
1588
                under_color,
×
1589
            } => Self::LogarithmicGradient(LogarithmicGradient {
×
NEW
1590
                r#type: Default::default(),
×
1591
                breakpoints: breakpoints
×
1592
                    .into_iter()
×
1593
                    .map(Into::into)
×
1594
                    .collect::<Vec<Breakpoint>>(),
×
1595
                no_data_color: no_data_color.into(),
×
1596
                over_color: over_color.into(),
×
1597
                under_color: under_color.into(),
×
1598
            }),
×
1599
            geoengine_datatypes::operations::image::Colorizer::Palette {
1600
                colors,
×
1601
                no_data_color,
×
1602
                default_color,
×
NEW
1603
            } => Self::Palette(PaletteColorizer {
×
NEW
1604
                r#type: Default::default(),
×
1605
                colors: colors.into(),
×
1606
                no_data_color: no_data_color.into(),
×
1607
                default_color: default_color.into(),
×
NEW
1608
            }),
×
1609
        }
1610
    }
5✔
1611
}
1612

1613
impl From<Colorizer> for geoengine_datatypes::operations::image::Colorizer {
1614
    fn from(v: Colorizer) -> Self {
4✔
1615
        match v {
4✔
1616
            Colorizer::LinearGradient(linear_gradient) => Self::LinearGradient {
4✔
1617
                breakpoints: linear_gradient
4✔
1618
                    .breakpoints
4✔
1619
                    .into_iter()
4✔
1620
                    .map(Into::into)
4✔
1621
                    .collect::<Vec<geoengine_datatypes::operations::image::Breakpoint>>(),
4✔
1622
                no_data_color: linear_gradient.no_data_color.into(),
4✔
1623
                over_color: linear_gradient.over_color.into(),
4✔
1624
                under_color: linear_gradient.under_color.into(),
4✔
1625
            },
4✔
1626
            Colorizer::LogarithmicGradient(logarithmic_gradient) => Self::LogarithmicGradient {
×
1627
                breakpoints: logarithmic_gradient
×
1628
                    .breakpoints
×
1629
                    .into_iter()
×
1630
                    .map(Into::into)
×
1631
                    .collect::<Vec<geoengine_datatypes::operations::image::Breakpoint>>(),
×
1632
                no_data_color: logarithmic_gradient.no_data_color.into(),
×
1633
                over_color: logarithmic_gradient.over_color.into(),
×
1634
                under_color: logarithmic_gradient.under_color.into(),
×
1635
            },
×
1636

1637
            Colorizer::Palette(PaletteColorizer {
1638
                colors,
×
1639
                no_data_color,
×
1640
                default_color,
×
NEW
1641
                ..
×
NEW
1642
            }) => Self::Palette {
×
1643
                colors: colors.into(),
×
1644
                no_data_color: no_data_color.into(),
×
1645
                default_color: default_color.into(),
×
1646
            },
×
1647
        }
1648
    }
4✔
1649
}
1650

1651
type_tag!(SingleBand);
1652
type_tag!(MultiBand);
1653

1654
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, ToSchema)]
348✔
1655
#[serde(rename_all = "camelCase", untagged)]
1656
#[schema(discriminator = "type")]
1657
pub enum RasterColorizer {
1658
    SingleBand(SingleBandRasterColorizer),
1659
    MultiBand(MultiBandRasterColorizer),
1660
}
1661

1662
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, ToSchema)]
352✔
1663
#[serde(rename_all = "camelCase")]
1664
pub struct SingleBandRasterColorizer {
1665
    #[schema(inline)]
1666
    pub r#type: SingleBandTag,
1667
    pub band: u32,
1668
    pub band_colorizer: Colorizer,
1669
}
1670

1671
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, ToSchema)]
352✔
1672
#[serde(rename_all = "camelCase")]
1673
pub struct MultiBandRasterColorizer {
1674
    #[schema(inline)]
1675
    pub r#type: MultiBandTag,
1676

1677
    /// The band index of the red channel.
1678
    pub red_band: u32,
1679
    /// The minimum value for the red channel.
1680
    pub red_min: f64,
1681
    /// The maximum value for the red channel.
1682
    pub red_max: f64,
1683
    /// A scaling factor for the red channel between 0 and 1.
1684
    #[serde(default = "num_traits::One::one")]
1685
    pub red_scale: f64,
1686

1687
    /// The band index of the green channel.
1688
    pub green_band: u32,
1689
    /// The minimum value for the red channel.
1690
    pub green_min: f64,
1691
    /// The maximum value for the red channel.
1692
    pub green_max: f64,
1693
    /// A scaling factor for the green channel between 0 and 1.
1694
    #[serde(default = "num_traits::One::one")]
1695
    pub green_scale: f64,
1696

1697
    /// The band index of the blue channel.
1698
    pub blue_band: u32,
1699
    /// The minimum value for the red channel.
1700
    pub blue_min: f64,
1701
    /// The maximum value for the red channel.
1702
    pub blue_max: f64,
1703
    /// A scaling factor for the blue channel between 0 and 1.
1704
    #[serde(default = "num_traits::One::one")]
1705
    pub blue_scale: f64,
1706

1707
    /// The color to use for no data values.
1708
    /// If not specified, the no data values will be transparent.
1709
    #[serde(default = "rgba_transparent")]
1710
    pub no_data_color: RgbaColor,
1711
}
1712

1713
fn rgba_transparent() -> RgbaColor {
×
1714
    RgbaColor([0, 0, 0, 0])
×
1715
}
×
1716

1717
impl Eq for RasterColorizer {}
1718

1719
impl RasterColorizer {
1720
    pub fn band_selection(&self) -> BandSelection {
6✔
1721
        match self {
6✔
1722
            RasterColorizer::SingleBand(SingleBandRasterColorizer { band, .. }) => {
4✔
1723
                BandSelection(vec![*band as usize])
4✔
1724
            }
1725
            RasterColorizer::MultiBand(MultiBandRasterColorizer {
1726
                red_band,
2✔
1727
                green_band,
2✔
1728
                blue_band,
2✔
1729
                ..
2✔
1730
            }) => {
2✔
1731
                let mut bands = Vec::with_capacity(3);
2✔
1732
                for band in [
6✔
1733
                    *red_band as usize,
2✔
1734
                    *green_band as usize,
2✔
1735
                    *blue_band as usize,
2✔
1736
                ] {
1737
                    if !bands.contains(&band) {
6✔
1738
                        bands.push(band);
4✔
1739
                    }
4✔
1740
                }
1741
                bands.sort_unstable(); // bands will be returned in ascending order anyway
2✔
1742
                BandSelection(bands)
2✔
1743
            }
1744
        }
1745
    }
6✔
1746
}
1747

1748
impl From<geoengine_datatypes::operations::image::RasterColorizer> for RasterColorizer {
1749
    fn from(v: geoengine_datatypes::operations::image::RasterColorizer) -> Self {
×
1750
        match v {
×
1751
            geoengine_datatypes::operations::image::RasterColorizer::SingleBand {
1752
                band,
×
1753
                band_colorizer: colorizer,
×
NEW
1754
            } => Self::SingleBand(SingleBandRasterColorizer {
×
NEW
1755
                r#type: Default::default(),
×
1756
                band,
×
1757
                band_colorizer: colorizer.into(),
×
NEW
1758
            }),
×
1759
            geoengine_datatypes::operations::image::RasterColorizer::MultiBand {
1760
                red_band,
×
1761
                green_band,
×
1762
                blue_band,
×
1763
                rgb_params,
×
NEW
1764
            } => Self::MultiBand(MultiBandRasterColorizer {
×
NEW
1765
                r#type: Default::default(),
×
1766
                red_band,
×
1767
                green_band,
×
1768
                blue_band,
×
1769
                red_min: rgb_params.red_min,
×
1770
                red_max: rgb_params.red_max,
×
1771
                red_scale: rgb_params.red_scale,
×
1772
                green_min: rgb_params.green_min,
×
1773
                green_max: rgb_params.green_max,
×
1774
                green_scale: rgb_params.green_scale,
×
1775
                blue_min: rgb_params.blue_min,
×
1776
                blue_max: rgb_params.blue_max,
×
1777
                blue_scale: rgb_params.blue_scale,
×
1778
                no_data_color: rgb_params.no_data_color.into(),
×
NEW
1779
            }),
×
1780
        }
1781
    }
×
1782
}
1783

1784
impl From<RasterColorizer> for geoengine_datatypes::operations::image::RasterColorizer {
1785
    fn from(v: RasterColorizer) -> Self {
6✔
1786
        match v {
6✔
1787
            RasterColorizer::SingleBand(SingleBandRasterColorizer {
1788
                band,
4✔
1789
                band_colorizer: colorizer,
4✔
1790
                ..
4✔
1791
            }) => Self::SingleBand {
4✔
1792
                band,
4✔
1793
                band_colorizer: colorizer.into(),
4✔
1794
            },
4✔
1795
            RasterColorizer::MultiBand(MultiBandRasterColorizer {
1796
                red_band,
2✔
1797
                red_min,
2✔
1798
                red_max,
2✔
1799
                red_scale,
2✔
1800
                green_band,
2✔
1801
                green_min,
2✔
1802
                green_max,
2✔
1803
                green_scale,
2✔
1804
                blue_band,
2✔
1805
                blue_min,
2✔
1806
                blue_max,
2✔
1807
                blue_scale,
2✔
1808
                no_data_color,
2✔
1809
                ..
2✔
1810
            }) => Self::MultiBand {
2✔
1811
                red_band,
2✔
1812
                green_band,
2✔
1813
                blue_band,
2✔
1814
                rgb_params: RgbParams {
2✔
1815
                    red_min,
2✔
1816
                    red_max,
2✔
1817
                    red_scale,
2✔
1818
                    green_min,
2✔
1819
                    green_max,
2✔
1820
                    green_scale,
2✔
1821
                    blue_min,
2✔
1822
                    blue_max,
2✔
1823
                    blue_scale,
2✔
1824
                    no_data_color: no_data_color.into(),
2✔
1825
                },
2✔
1826
            },
2✔
1827
        }
1828
    }
6✔
1829
}
1830

1831
/// A map from value to color
1832
///
1833
/// It is assumed that is has at least one and at most 256 entries.
1834
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
4,156✔
1835
#[serde(try_from = "SerializablePalette", into = "SerializablePalette")]
1836
#[schema(value_type = HashMap<f64, RgbaColor>)]
1837
pub struct Palette(pub HashMap<NotNan<f64>, RgbaColor>);
1838

1839
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1840
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
×
1841
        Self(
×
1842
            palette
×
1843
                .into_inner()
×
1844
                .into_iter()
×
1845
                .map(|(value, color)| (value, color.into()))
×
1846
                .collect(),
×
1847
        )
×
1848
    }
×
1849
}
1850

1851
impl From<Palette> for geoengine_datatypes::operations::image::Palette {
1852
    fn from(palette: Palette) -> Self {
×
1853
        Self::new(
×
1854
            palette
×
1855
                .0
×
1856
                .into_iter()
×
1857
                .map(|(value, color)| (value, color.into()))
×
1858
                .collect(),
×
1859
        )
×
1860
    }
×
1861
}
1862

1863
/// A type that is solely for serde's serializability.
1864
/// You cannot serialize floats as JSON map keys.
1865
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1866
pub struct SerializablePalette(HashMap<String, RgbaColor>);
1867

1868
impl From<Palette> for SerializablePalette {
1869
    fn from(palette: Palette) -> Self {
×
1870
        Self(
×
1871
            palette
×
1872
                .0
×
1873
                .into_iter()
×
1874
                .map(|(k, v)| (k.to_string(), v))
×
1875
                .collect(),
×
1876
        )
×
1877
    }
×
1878
}
1879

1880
impl TryFrom<SerializablePalette> for Palette {
1881
    type Error = <NotNan<f64> as FromStr>::Err;
1882

1883
    fn try_from(palette: SerializablePalette) -> Result<Self, Self::Error> {
×
1884
        let mut inner = HashMap::<NotNan<f64>, RgbaColor>::with_capacity(palette.0.len());
×
1885
        for (k, v) in palette.0 {
×
1886
            inner.insert(k.parse()?, v);
×
1887
        }
1888
        Ok(Self(inner))
×
1889
    }
×
1890
}
1891

1892
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
944✔
1893
pub struct RasterPropertiesKey {
1894
    pub domain: Option<String>,
1895
    pub key: String,
1896
}
1897

1898
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1899
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1900
        Self {
×
1901
            domain: value.domain,
×
1902
            key: value.key,
×
1903
        }
×
1904
    }
×
1905
}
1906

1907
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1908
    fn from(value: RasterPropertiesKey) -> Self {
×
1909
        Self {
×
1910
            domain: value.domain,
×
1911
            key: value.key,
×
1912
        }
×
1913
    }
×
1914
}
1915

1916
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
636✔
1917
pub enum RasterPropertiesEntryType {
1918
    Number,
1919
    String,
1920
}
1921

1922
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1923
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1924
        match value {
×
1925
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1926
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1927
        }
1928
    }
×
1929
}
1930

1931
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1932
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1933
        match value {
×
1934
            RasterPropertiesEntryType::Number => Self::Number,
×
1935
            RasterPropertiesEntryType::String => Self::String,
×
1936
        }
1937
    }
×
1938
}
1939

1940
#[derive(PartialEq, Eq, Clone, Debug)]
1941
pub struct DateTimeParseFormat {
1942
    fmt: String,
1943
    has_tz: bool,
1944
    has_time: bool,
1945
}
1946

1947
impl PartialSchema for DateTimeParseFormat {
1948
    fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::Schema> {
224✔
1949
        use utoipa::openapi::schema::{ObjectBuilder, SchemaType, Type};
1950
        ObjectBuilder::new()
224✔
1951
            .schema_type(SchemaType::Type(Type::String))
224✔
1952
            .into()
224✔
1953
    }
224✔
1954
}
1955

1956
impl ToSchema for DateTimeParseFormat {}
1957

1958
impl<'de> Deserialize<'de> for DateTimeParseFormat {
1959
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4✔
1960
    where
4✔
1961
        D: serde::Deserializer<'de>,
4✔
1962
    {
4✔
1963
        let s = String::deserialize(deserializer)?;
4✔
1964
        Ok(geoengine_datatypes::primitives::DateTimeParseFormat::custom(s).into())
4✔
1965
    }
4✔
1966
}
1967

1968
impl Serialize for DateTimeParseFormat {
1969
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
5✔
1970
    where
5✔
1971
        S: serde::Serializer,
5✔
1972
    {
5✔
1973
        serializer.serialize_str(&self.fmt)
5✔
1974
    }
5✔
1975
}
1976

1977
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1978
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
8✔
1979
        Self {
8✔
1980
            fmt: value.parse_format().to_string(),
8✔
1981
            has_tz: value.has_tz(),
8✔
1982
            has_time: value.has_time(),
8✔
1983
        }
8✔
1984
    }
8✔
1985
}
1986

1987
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1988
    fn from(value: DateTimeParseFormat) -> Self {
4✔
1989
        Self::custom(value.fmt)
4✔
1990
    }
4✔
1991
}
1992

1993
impl DateTimeParseFormat {
1994
    // this is used as default value
1995
    pub fn unix() -> Self {
×
1996
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1997
    }
×
1998
}
1999

2000
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
188✔
2001
pub struct NoGeometry;
2002

2003
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
2004
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
2005
        Self {}
×
2006
    }
×
2007
}
2008

2009
impl From<NoGeometry> for geoengine_datatypes::primitives::NoGeometry {
2010
    fn from(_: NoGeometry) -> Self {
×
2011
        Self {}
×
2012
    }
×
2013
}
2014

2015
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
188✔
2016
pub struct MultiPoint {
2017
    pub coordinates: Vec<Coordinate2D>,
2018
}
2019

2020
impl From<geoengine_datatypes::primitives::MultiPoint> for MultiPoint {
2021
    fn from(value: geoengine_datatypes::primitives::MultiPoint) -> Self {
×
2022
        Self {
×
2023
            coordinates: value.points().iter().map(|x| (*x).into()).collect(),
×
2024
        }
×
2025
    }
×
2026
}
2027

2028
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
2029
    fn from(value: MultiPoint) -> Self {
×
2030
        Self::new(value.coordinates.into_iter().map(Into::into).collect())
×
2031
            .expect("it should always be able to convert it")
×
2032
    }
×
2033
}
2034

2035
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
188✔
2036
pub struct MultiLineString {
2037
    pub coordinates: Vec<Vec<Coordinate2D>>,
2038
}
2039

2040
impl From<geoengine_datatypes::primitives::MultiLineString> for MultiLineString {
2041
    fn from(value: geoengine_datatypes::primitives::MultiLineString) -> Self {
×
2042
        Self {
×
2043
            coordinates: value
×
2044
                .lines()
×
2045
                .iter()
×
2046
                .map(|x| x.iter().map(|x| (*x).into()).collect())
×
2047
                .collect(),
×
2048
        }
×
2049
    }
×
2050
}
2051

2052
impl From<MultiLineString> for geoengine_datatypes::primitives::MultiLineString {
2053
    fn from(value: MultiLineString) -> Self {
×
2054
        Self::new(
×
2055
            value
×
2056
                .coordinates
×
2057
                .into_iter()
×
2058
                .map(|x| x.into_iter().map(Into::into).collect())
×
2059
                .collect(),
×
2060
        )
×
2061
        .expect("it should always be able to convert it")
×
2062
    }
×
2063
}
2064

2065
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
188✔
2066
pub struct MultiPolygon {
2067
    pub polygons: Vec<Vec<Vec<Coordinate2D>>>,
2068
}
2069

2070
impl From<geoengine_datatypes::primitives::MultiPolygon> for MultiPolygon {
2071
    fn from(value: geoengine_datatypes::primitives::MultiPolygon) -> Self {
×
2072
        Self {
×
2073
            polygons: value
×
2074
                .polygons()
×
2075
                .iter()
×
2076
                .map(|x| {
×
2077
                    x.iter()
×
2078
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
2079
                        .collect()
×
2080
                })
×
2081
                .collect(),
×
2082
        }
×
2083
    }
×
2084
}
2085

2086
impl From<MultiPolygon> for geoengine_datatypes::primitives::MultiPolygon {
2087
    fn from(value: MultiPolygon) -> Self {
×
2088
        Self::new(
×
2089
            value
×
2090
                .polygons
×
2091
                .iter()
×
2092
                .map(|x| {
×
2093
                    x.iter()
×
2094
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
2095
                        .collect()
×
2096
                })
×
2097
                .collect(),
×
2098
        )
×
2099
        .expect("it should always be able to convert it")
×
2100
    }
×
2101
}
2102

2103
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
2104
pub struct StringPair((String, String));
2105

2106
pub type GdalConfigOption = StringPair;
2107
pub type AxisLabels = StringPair;
2108

2109
impl PartialSchema for StringPair {
2110
    fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::Schema> {
172✔
2111
        use utoipa::openapi::schema::{ArrayBuilder, Object, SchemaType, Type};
2112
        ArrayBuilder::new()
172✔
2113
            .items(Object::with_type(SchemaType::Type(Type::String)))
172✔
2114
            .min_items(Some(2))
172✔
2115
            .max_items(Some(2))
172✔
2116
            .into()
172✔
2117
    }
172✔
2118
}
2119

2120
impl ToSchema for StringPair {
2121
    // fn aliases() -> Vec<(&'a str, utoipa::openapi::Schema)> { // TODO: how to do this?
2122
    //     let utoipa::openapi::RefOr::T(unpacked_schema) = Self::schema().1 else {
2123
    //         unreachable!()
2124
    //     };
2125
    //     vec![
2126
    //         ("GdalConfigOption", unpacked_schema.clone()),
2127
    //         ("AxisLabels", unpacked_schema),
2128
    //     ]
2129
    // }
2130
}
2131

2132
impl From<(String, String)> for StringPair {
2133
    fn from(value: (String, String)) -> Self {
32✔
2134
        Self(value)
32✔
2135
    }
32✔
2136
}
2137

2138
impl From<StringPair> for (String, String) {
2139
    fn from(value: StringPair) -> Self {
×
2140
        value.0
×
2141
    }
×
2142
}
2143

2144
impl From<StringPair> for geoengine_datatypes::util::StringPair {
2145
    fn from(value: StringPair) -> Self {
2✔
2146
        Self::new(value.0 .0, value.0 .1)
2✔
2147
    }
2✔
2148
}
2149

2150
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
44✔
2151
pub enum PlotOutputFormat {
2152
    JsonPlain,
2153
    JsonVega,
2154
    ImagePng,
2155
}
2156

2157
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, PartialOrd, Deserialize, ToSchema)]
728✔
2158
pub struct CacheTtlSeconds(u32);
2159

2160
const MAX_CACHE_TTL_SECONDS: u32 = 31_536_000; // 1 year
2161

2162
impl CacheTtlSeconds {
2163
    pub fn new(seconds: u32) -> Self {
×
2164
        Self(seconds.min(MAX_CACHE_TTL_SECONDS))
×
2165
    }
×
2166

2167
    pub fn max() -> Self {
×
2168
        Self(MAX_CACHE_TTL_SECONDS)
×
2169
    }
×
2170

2171
    pub fn seconds(self) -> u32 {
×
2172
        self.0
×
2173
    }
×
2174
}
2175

2176
impl From<geoengine_datatypes::primitives::CacheTtlSeconds> for CacheTtlSeconds {
2177
    fn from(value: geoengine_datatypes::primitives::CacheTtlSeconds) -> Self {
5✔
2178
        Self(value.seconds())
5✔
2179
    }
5✔
2180
}
2181

2182
impl From<CacheTtlSeconds> for geoengine_datatypes::primitives::CacheTtlSeconds {
2183
    fn from(value: CacheTtlSeconds) -> Self {
7✔
2184
        Self::new(value.0)
7✔
2185
    }
7✔
2186
}
2187

2188
impl ToSql for CacheTtlSeconds {
2189
    fn to_sql(
×
2190
        &self,
×
2191
        ty: &postgres_types::Type,
×
2192
        w: &mut bytes::BytesMut,
×
2193
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
2194
        <i32 as ToSql>::to_sql(&(self.0 as i32), ty, w)
×
2195
    }
×
2196

2197
    fn accepts(ty: &postgres_types::Type) -> bool {
×
2198
        <i32 as ToSql>::accepts(ty)
×
2199
    }
×
2200

2201
    postgres_types::to_sql_checked!();
2202
}
2203

2204
impl<'a> FromSql<'a> for CacheTtlSeconds {
2205
    fn from_sql(
×
2206
        ty: &postgres_types::Type,
×
2207
        raw: &'a [u8],
×
2208
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
2209
        Ok(Self(<i32 as FromSql>::from_sql(ty, raw)? as u32))
×
2210
    }
×
2211

2212
    fn accepts(ty: &postgres_types::Type) -> bool {
×
2213
        <i32 as FromSql>::accepts(ty)
×
2214
    }
×
2215
}
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