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

geo-engine / geoengine / 11911118784

19 Nov 2024 10:06AM UTC coverage: 90.448% (-0.2%) from 90.687%
11911118784

push

github

web-flow
Merge pull request #994 from geo-engine/workspace-dependencies

use workspace dependencies, update toolchain, use global lock in expression

9 of 11 new or added lines in 6 files covered. (81.82%)

369 existing lines in 74 files now uncovered.

132871 of 146904 relevant lines covered (90.45%)

54798.62 hits per line

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

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

18
identifier!(DataProviderId);
19

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

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

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

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

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

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

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

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

79
impl From<ExternalDataId> for geoengine_datatypes::dataset::DataId {
80
    fn from(value: ExternalDataId) -> Self {
×
81
        Self::External(value.into())
×
82
    }
×
83
}
84

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

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

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

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

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

137
impl From<geoengine_datatypes::dataset::DatasetId> for DatasetId {
138
    fn from(id: geoengine_datatypes::dataset::DatasetId) -> Self {
6✔
139
        Self(id.0)
6✔
140
    }
6✔
141
}
142

143
impl From<&DatasetId> for geoengine_datatypes::dataset::DatasetId {
144
    fn from(value: &DatasetId) -> Self {
2✔
145
        Self(value.0)
2✔
146
    }
2✔
147
}
148

149
impl From<&ExternalDataId> for geoengine_datatypes::dataset::ExternalDataId {
150
    fn from(value: &ExternalDataId) -> Self {
×
151
        Self {
×
152
            provider_id: value.provider_id.into(),
×
153
            layer_id: value.layer_id.clone().into(),
×
154
        }
×
155
    }
×
156
}
157

158
/// The user-facing identifier for loadable data.
159
/// It can be resolved into a [`DataId`].
160
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)]
161
// TODO: Have separate type once `geoengine_datatypes::dataset::NamedData` is not part of the API anymore.
162
#[serde(
163
    from = "geoengine_datatypes::dataset::NamedData",
164
    into = "geoengine_datatypes::dataset::NamedData"
165
)]
166
pub struct NamedData {
167
    pub namespace: Option<String>,
168
    pub provider: Option<String>,
169
    pub name: String,
170
}
171

172
impl From<geoengine_datatypes::dataset::NamedData> for NamedData {
173
    fn from(
×
174
        geoengine_datatypes::dataset::NamedData {
×
175
            namespace,
×
176
            provider,
×
177
            name,
×
178
        }: geoengine_datatypes::dataset::NamedData,
×
179
    ) -> Self {
×
180
        Self {
×
181
            namespace,
×
182
            provider,
×
183
            name,
×
184
        }
×
185
    }
×
186
}
187

188
impl From<&geoengine_datatypes::dataset::NamedData> for NamedData {
189
    fn from(named_data: &geoengine_datatypes::dataset::NamedData) -> Self {
×
190
        Self::from(named_data.clone())
×
191
    }
×
192
}
193

194
impl From<NamedData> for geoengine_datatypes::dataset::NamedData {
195
    fn from(
1✔
196
        NamedData {
1✔
197
            namespace,
1✔
198
            provider,
1✔
199
            name,
1✔
200
        }: NamedData,
1✔
201
    ) -> Self {
1✔
202
        Self {
1✔
203
            namespace,
1✔
204
            provider,
1✔
205
            name,
1✔
206
        }
1✔
207
    }
1✔
208
}
209

210
impl From<&NamedData> for geoengine_datatypes::dataset::NamedData {
211
    fn from(named_data: &NamedData) -> Self {
×
212
        Self::from(named_data.clone())
×
213
    }
×
214
}
215

216
impl<'a> ToSchema<'a> for NamedData {
217
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
218
        use utoipa::openapi::*;
219
        (
2✔
220
            "NamedData",
2✔
221
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
222
        )
2✔
223
    }
2✔
224
}
225

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

229
impl From<LayerId> for geoengine_datatypes::dataset::LayerId {
230
    fn from(value: LayerId) -> Self {
13✔
231
        Self(value.0)
13✔
232
    }
13✔
233
}
234

235
impl std::fmt::Display for LayerId {
236
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
×
237
        write!(f, "{}", self.0)
×
238
    }
×
239
}
240

241
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
2✔
242
#[serde(rename_all = "camelCase")]
243
pub struct ExternalDataId {
244
    pub provider_id: DataProviderId,
245
    pub layer_id: LayerId,
246
}
247

248
impl From<geoengine_datatypes::dataset::ExternalDataId> for ExternalDataId {
249
    fn from(id: geoengine_datatypes::dataset::ExternalDataId) -> Self {
×
250
        Self {
×
251
            provider_id: id.provider_id.into(),
×
252
            layer_id: id.layer_id.into(),
×
253
        }
×
254
    }
×
255
}
256

257
impl From<geoengine_datatypes::dataset::DataProviderId> for DataProviderId {
258
    fn from(id: geoengine_datatypes::dataset::DataProviderId) -> Self {
×
259
        Self(id.0)
×
260
    }
×
261
}
262

263
impl From<geoengine_datatypes::dataset::LayerId> for LayerId {
264
    fn from(id: geoengine_datatypes::dataset::LayerId) -> Self {
1✔
265
        Self(id.0)
1✔
266
    }
1✔
267
}
268

269
/// A spatial reference authority that is part of a spatial reference definition
270
#[derive(
271
    Debug,
272
    Copy,
273
    Clone,
274
    Eq,
275
    PartialEq,
276
    Ord,
277
    PartialOrd,
278
    Serialize,
279
    Deserialize,
×
280
    ToSchema,
2✔
281
    FromSql,
×
282
    ToSql,
×
283
)]
284
#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
285
pub enum SpatialReferenceAuthority {
286
    Epsg,
287
    SrOrg,
288
    Iau2000,
289
    Esri,
290
}
291

292
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceAuthority>
293
    for SpatialReferenceAuthority
294
{
295
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceAuthority) -> Self {
50✔
296
        match value {
50✔
297
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Epsg => Self::Epsg,
49✔
298
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::SrOrg => Self::SrOrg,
1✔
299
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Iau2000 => {
300
                Self::Iau2000
×
301
            }
302
            geoengine_datatypes::spatial_reference::SpatialReferenceAuthority::Esri => Self::Esri,
×
303
        }
304
    }
50✔
305
}
306

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

320
impl FromStr for SpatialReferenceAuthority {
321
    type Err = error::Error;
322

323
    fn from_str(s: &str) -> Result<Self, Self::Err> {
36✔
324
        Ok(match s {
36✔
325
            "EPSG" => SpatialReferenceAuthority::Epsg,
36✔
326
            "SR-ORG" => SpatialReferenceAuthority::SrOrg,
×
327
            "IAU2000" => SpatialReferenceAuthority::Iau2000,
×
328
            "ESRI" => SpatialReferenceAuthority::Esri,
×
329
            _ => {
330
                return Err(error::Error::InvalidSpatialReferenceString {
×
331
                    spatial_reference_string: s.into(),
×
332
                })
×
333
            }
334
        })
335
    }
36✔
336
}
337

338
impl std::fmt::Display for SpatialReferenceAuthority {
339
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46✔
340
        write!(
46✔
341
            f,
46✔
342
            "{}",
46✔
343
            match self {
46✔
344
                SpatialReferenceAuthority::Epsg => "EPSG",
46✔
345
                SpatialReferenceAuthority::SrOrg => "SR-ORG",
×
346
                SpatialReferenceAuthority::Iau2000 => "IAU2000",
×
347
                SpatialReferenceAuthority::Esri => "ESRI",
×
348
            }
349
        )
350
    }
46✔
351
}
352

353
/// A spatial reference consists of an authority and a code
354
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, FromSql, ToSql)]
×
355
pub struct SpatialReference {
356
    authority: SpatialReferenceAuthority,
357
    code: u32,
358
}
359

360
impl SpatialReference {
361
    pub fn proj_string(self) -> Result<String> {
19✔
362
        match self.authority {
×
363
            SpatialReferenceAuthority::Epsg | SpatialReferenceAuthority::Iau2000 => {
364
                Ok(format!("{}:{}", self.authority, self.code))
19✔
365
            }
366
            // poor-mans integration of Meteosat Second Generation
367
            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()),
×
368
            SpatialReferenceAuthority::SrOrg | SpatialReferenceAuthority::Esri => {
369
                Err(error::Error::ProjStringUnresolvable { spatial_ref: self })
×
370
                //TODO: we might need to look them up somehow! Best solution would be a registry where we can store user definexd srs strings.
371
            }
372
        }
373
    }
19✔
374

375
    /// Return the srs-string "authority:code"
376
    #[allow(clippy::trivially_copy_pass_by_ref)]
377
    pub fn srs_string(&self) -> String {
19✔
378
        format!("{}:{}", self.authority, self.code)
19✔
379
    }
19✔
380
}
381

382
impl From<geoengine_datatypes::spatial_reference::SpatialReference> for SpatialReference {
383
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReference) -> Self {
50✔
384
        Self {
50✔
385
            authority: (*value.authority()).into(),
50✔
386
            code: value.code(),
50✔
387
        }
50✔
388
    }
50✔
389
}
390

391
impl From<SpatialReference> for geoengine_datatypes::spatial_reference::SpatialReference {
392
    fn from(value: SpatialReference) -> Self {
12✔
393
        geoengine_datatypes::spatial_reference::SpatialReference::new(
12✔
394
            value.authority.into(),
12✔
395
            value.code,
12✔
396
        )
12✔
397
    }
12✔
398
}
399

400
impl SpatialReference {
401
    pub fn new(authority: SpatialReferenceAuthority, code: u32) -> Self {
41✔
402
        Self { authority, code }
41✔
403
    }
41✔
404

405
    pub fn authority(&self) -> &SpatialReferenceAuthority {
1✔
406
        &self.authority
1✔
407
    }
1✔
408

409
    pub fn code(self) -> u32 {
1✔
410
        self.code
1✔
411
    }
1✔
412
}
413

414
impl FromStr for SpatialReference {
415
    type Err = error::Error;
416

417
    fn from_str(s: &str) -> Result<Self, Self::Err> {
36✔
418
        let mut split = s.split(':');
36✔
419

36✔
420
        match (split.next(), split.next(), split.next()) {
36✔
421
            (Some(authority), Some(code), None) => Ok(Self::new(
36✔
422
                authority.parse()?,
36✔
423
                code.parse::<u32>().context(error::ParseU32)?,
36✔
424
            )),
425
            _ => Err(error::Error::InvalidSpatialReferenceString {
×
426
                spatial_reference_string: s.into(),
×
427
            }),
×
428
        }
429
    }
36✔
430
}
431

432
impl std::fmt::Display for SpatialReference {
433
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
7✔
434
        write!(f, "{}:{}", self.authority, self.code)
7✔
435
    }
7✔
436
}
437

438
impl Serialize for SpatialReference {
439
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
1✔
440
    where
1✔
441
        S: Serializer,
1✔
442
    {
1✔
443
        serializer.serialize_str(&self.to_string())
1✔
444
    }
1✔
445
}
446

447
/// Helper struct for deserializing a `SpatialReferencce`
448
struct SpatialReferenceDeserializeVisitor;
449

450
impl<'de> Visitor<'de> for SpatialReferenceDeserializeVisitor {
451
    type Value = SpatialReference;
452

453
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
454
        formatter.write_str("a spatial reference in the form authority:code")
×
455
    }
×
456

457
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
18✔
458
    where
18✔
459
        E: serde::de::Error,
18✔
460
    {
18✔
461
        v.parse().map_err(serde::de::Error::custom)
18✔
462
    }
18✔
463
}
464

465
impl<'de> Deserialize<'de> for SpatialReference {
466
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
18✔
467
    where
18✔
468
        D: Deserializer<'de>,
18✔
469
    {
18✔
470
        deserializer.deserialize_str(SpatialReferenceDeserializeVisitor)
18✔
471
    }
18✔
472
}
473

474
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
475
pub enum SpatialReferenceOption {
476
    SpatialReference(SpatialReference),
477
    Unreferenced,
478
}
479

480
impl From<geoengine_datatypes::spatial_reference::SpatialReferenceOption>
481
    for SpatialReferenceOption
482
{
483
    fn from(value: geoengine_datatypes::spatial_reference::SpatialReferenceOption) -> Self {
19✔
484
        match value {
19✔
485
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::SpatialReference(s) => {
19✔
486
                Self::SpatialReference(s.into())
19✔
487
            }
488
            geoengine_datatypes::spatial_reference::SpatialReferenceOption::Unreferenced => {
489
                Self::Unreferenced
×
490
            }
491
        }
492
    }
19✔
493
}
494

495
impl From<SpatialReferenceOption>
496
    for geoengine_datatypes::spatial_reference::SpatialReferenceOption
497
{
498
    fn from(value: SpatialReferenceOption) -> Self {
9✔
499
        match value {
9✔
500
            SpatialReferenceOption::SpatialReference(sr) => Self::SpatialReference(sr.into()),
8✔
501
            SpatialReferenceOption::Unreferenced => Self::Unreferenced,
1✔
502
        }
503
    }
9✔
504
}
505

506
impl From<SpatialReference> for SpatialReferenceOption {
507
    fn from(spatial_reference: SpatialReference) -> Self {
10✔
508
        Self::SpatialReference(spatial_reference)
10✔
509
    }
10✔
510
}
511

512
impl std::fmt::Display for SpatialReferenceOption {
513
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6✔
514
        match self {
6✔
515
            SpatialReferenceOption::SpatialReference(p) => write!(f, "{p}"),
6✔
516
            SpatialReferenceOption::Unreferenced => Ok(()),
×
517
        }
518
    }
6✔
519
}
520

521
impl Serialize for SpatialReferenceOption {
522
    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
6✔
523
    where
6✔
524
        S: Serializer,
6✔
525
    {
6✔
526
        serializer.serialize_str(&self.to_string())
6✔
527
    }
6✔
528
}
529

530
/// Helper struct for deserializing a `SpatialReferenceOption`
531
struct SpatialReferenceOptionDeserializeVisitor;
532

533
impl<'de> Visitor<'de> for SpatialReferenceOptionDeserializeVisitor {
534
    type Value = SpatialReferenceOption;
535

536
    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
×
537
        formatter.write_str("a spatial reference in the form authority:code")
×
538
    }
×
539

540
    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
11✔
541
    where
11✔
542
        E: serde::de::Error,
11✔
543
    {
11✔
544
        if v.is_empty() {
11✔
545
            return Ok(SpatialReferenceOption::Unreferenced);
1✔
546
        }
10✔
547

548
        let spatial_reference: SpatialReference = v.parse().map_err(serde::de::Error::custom)?;
10✔
549

550
        Ok(spatial_reference.into())
10✔
551
    }
11✔
552
}
553

554
impl<'de> Deserialize<'de> for SpatialReferenceOption {
555
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
11✔
556
    where
11✔
557
        D: Deserializer<'de>,
11✔
558
    {
11✔
559
        deserializer.deserialize_str(SpatialReferenceOptionDeserializeVisitor)
11✔
560
    }
11✔
561
}
562

563
impl From<Option<SpatialReference>> for SpatialReferenceOption {
564
    fn from(option: Option<SpatialReference>) -> Self {
×
565
        match option {
×
566
            Some(p) => SpatialReferenceOption::SpatialReference(p),
×
567
            None => SpatialReferenceOption::Unreferenced,
×
568
        }
569
    }
×
570
}
571

572
impl From<SpatialReferenceOption> for Option<SpatialReference> {
573
    fn from(option: SpatialReferenceOption) -> Self {
14✔
574
        match option {
14✔
575
            SpatialReferenceOption::SpatialReference(p) => Some(p),
14✔
576
            SpatialReferenceOption::Unreferenced => None,
×
577
        }
578
    }
14✔
579
}
580

581
impl ToSql for SpatialReferenceOption {
582
    fn to_sql(
×
583
        &self,
×
584
        ty: &postgres_types::Type,
×
585
        out: &mut bytes::BytesMut,
×
586
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>>
×
587
    where
×
588
        Self: Sized,
×
589
    {
×
590
        match self {
×
591
            SpatialReferenceOption::SpatialReference(sref) => sref.to_sql(ty, out),
×
592
            SpatialReferenceOption::Unreferenced => Ok(postgres_types::IsNull::Yes),
×
593
        }
594
    }
×
595

596
    fn accepts(ty: &postgres_types::Type) -> bool
×
597
    where
×
598
        Self: Sized,
×
599
    {
×
600
        <SpatialReference as ToSql>::accepts(ty)
×
601
    }
×
602

603
    fn to_sql_checked(
×
604
        &self,
×
605
        ty: &postgres_types::Type,
×
606
        out: &mut bytes::BytesMut,
×
607
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
608
        match self {
×
609
            SpatialReferenceOption::SpatialReference(sref) => sref.to_sql_checked(ty, out),
×
610
            SpatialReferenceOption::Unreferenced => Ok(postgres_types::IsNull::Yes),
×
611
        }
612
    }
×
613
}
614

615
impl<'a> FromSql<'a> for SpatialReferenceOption {
616
    fn from_sql(
×
617
        ty: &postgres_types::Type,
×
618
        raw: &'a [u8],
×
619
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
620
        Ok(SpatialReferenceOption::SpatialReference(
×
621
            SpatialReference::from_sql(ty, raw)?,
×
622
        ))
623
    }
×
624

625
    fn from_sql_null(
×
626
        _: &postgres_types::Type,
×
627
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
628
        Ok(SpatialReferenceOption::Unreferenced)
×
629
    }
×
630

631
    fn accepts(ty: &postgres_types::Type) -> bool {
×
632
        <SpatialReference as FromSql>::accepts(ty)
×
633
    }
×
634
}
635

636
/// An enum that contains all possible vector data types
637
#[derive(
638
    Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Deserialize, Serialize, Copy, Clone, ToSchema,
26✔
639
)]
640
pub enum VectorDataType {
641
    Data,
642
    MultiPoint,
643
    MultiLineString,
644
    MultiPolygon,
645
}
646

647
impl From<geoengine_datatypes::collections::VectorDataType> for VectorDataType {
648
    fn from(value: geoengine_datatypes::collections::VectorDataType) -> Self {
2✔
649
        match value {
2✔
650
            geoengine_datatypes::collections::VectorDataType::Data => Self::Data,
×
651
            geoengine_datatypes::collections::VectorDataType::MultiPoint => Self::MultiPoint,
2✔
652
            geoengine_datatypes::collections::VectorDataType::MultiLineString => {
653
                Self::MultiLineString
×
654
            }
655
            geoengine_datatypes::collections::VectorDataType::MultiPolygon => Self::MultiPolygon,
×
656
        }
657
    }
2✔
658
}
659

660
impl From<VectorDataType> for geoengine_datatypes::collections::VectorDataType {
661
    fn from(value: VectorDataType) -> Self {
9✔
662
        match value {
9✔
663
            VectorDataType::Data => Self::Data,
1✔
664
            VectorDataType::MultiPoint => Self::MultiPoint,
8✔
665
            VectorDataType::MultiLineString => Self::MultiLineString,
×
666
            VectorDataType::MultiPolygon => Self::MultiPolygon,
×
667
        }
668
    }
9✔
669
}
670

671
#[derive(
672
    Clone,
673
    Copy,
674
    Debug,
675
    Deserialize,
48✔
676
    PartialEq,
677
    PartialOrd,
678
    Serialize,
679
    Default,
680
    ToSchema,
2✔
681
    ToSql,
×
682
    FromSql,
×
683
)]
684
pub struct Coordinate2D {
685
    pub x: f64,
686
    pub y: f64,
687
}
688

689
impl From<geoengine_datatypes::primitives::Coordinate2D> for Coordinate2D {
690
    fn from(coordinate: geoengine_datatypes::primitives::Coordinate2D) -> Self {
72✔
691
        Self {
72✔
692
            x: coordinate.x,
72✔
693
            y: coordinate.y,
72✔
694
        }
72✔
695
    }
72✔
696
}
697

698
impl From<Coordinate2D> for geoengine_datatypes::primitives::Coordinate2D {
699
    fn from(coordinate: Coordinate2D) -> Self {
26✔
700
        Self {
26✔
701
            x: coordinate.x,
26✔
702
            y: coordinate.y,
26✔
703
        }
26✔
704
    }
26✔
705
}
706

707
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema, ToSql, FromSql)]
3✔
708
#[serde(rename_all = "camelCase")]
709
/// A bounding box that includes all border points.
710
/// Note: may degenerate to a point!
711
pub struct BoundingBox2D {
712
    pub lower_left_coordinate: Coordinate2D,
713
    pub upper_right_coordinate: Coordinate2D,
714
}
715

716
impl From<geoengine_datatypes::primitives::BoundingBox2D> for BoundingBox2D {
717
    fn from(bbox: geoengine_datatypes::primitives::BoundingBox2D) -> Self {
28✔
718
        Self {
28✔
719
            lower_left_coordinate:
28✔
720
                geoengine_datatypes::primitives::AxisAlignedRectangle::lower_left(&bbox).into(),
28✔
721
            upper_right_coordinate:
28✔
722
                geoengine_datatypes::primitives::AxisAlignedRectangle::upper_right(&bbox).into(),
28✔
723
        }
28✔
724
    }
28✔
725
}
726

727
impl From<BoundingBox2D> for geoengine_datatypes::primitives::BoundingBox2D {
728
    fn from(bbox: BoundingBox2D) -> Self {
1✔
729
        Self::new_unchecked(
1✔
730
            bbox.lower_left_coordinate.into(),
1✔
731
            bbox.upper_right_coordinate.into(),
1✔
732
        )
1✔
733
    }
1✔
734
}
735

736
/// An object that composes the date and a timestamp with time zone.
737
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ToSchema)]
2✔
738
pub struct DateTime {
739
    datetime: chrono::DateTime<chrono::Utc>,
740
}
741

742
impl FromStr for DateTime {
743
    type Err = geoengine_datatypes::primitives::DateTimeError;
744

745
    fn from_str(input: &str) -> Result<Self, Self::Err> {
×
746
        let date_time = chrono::DateTime::<chrono::FixedOffset>::from_str(input).map_err(|e| {
×
747
            Self::Err::DateParse {
×
748
                source: Box::new(e),
×
749
            }
×
750
        })?;
×
751

752
        Ok(date_time.into())
×
753
    }
×
754
}
755

756
impl From<chrono::DateTime<chrono::FixedOffset>> for DateTime {
757
    fn from(datetime: chrono::DateTime<chrono::FixedOffset>) -> Self {
×
758
        Self {
×
759
            datetime: datetime.into(),
×
760
        }
×
761
    }
×
762
}
763

764
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
42✔
765
#[serde(rename_all = "camelCase")]
766
pub enum FeatureDataType {
767
    Category,
768
    Int,
769
    Float,
770
    Text,
771
    Bool,
772
    DateTime,
773
}
774

775
impl From<geoengine_datatypes::primitives::FeatureDataType> for FeatureDataType {
776
    fn from(value: geoengine_datatypes::primitives::FeatureDataType) -> Self {
2✔
777
        match value {
2✔
778
            geoengine_datatypes::primitives::FeatureDataType::Category => Self::Category,
×
779
            geoengine_datatypes::primitives::FeatureDataType::Int => Self::Int,
1✔
780
            geoengine_datatypes::primitives::FeatureDataType::Float => Self::Float,
×
781
            geoengine_datatypes::primitives::FeatureDataType::Text => Self::Text,
1✔
782
            geoengine_datatypes::primitives::FeatureDataType::Bool => Self::Bool,
×
783
            geoengine_datatypes::primitives::FeatureDataType::DateTime => Self::DateTime,
×
784
        }
785
    }
2✔
786
}
787

788
impl From<FeatureDataType> for geoengine_datatypes::primitives::FeatureDataType {
789
    fn from(value: FeatureDataType) -> Self {
20✔
790
        match value {
20✔
791
            FeatureDataType::Category => Self::Category,
×
792
            FeatureDataType::Int => Self::Int,
4✔
793
            FeatureDataType::Float => Self::Float,
4✔
794
            FeatureDataType::Text => Self::Text,
12✔
795
            FeatureDataType::Bool => Self::Bool,
×
796
            FeatureDataType::DateTime => Self::DateTime,
×
797
        }
798
    }
20✔
799
}
800

801
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
29✔
802
#[serde(rename_all = "camelCase", tag = "type")]
803
pub enum Measurement {
804
    #[schema(title = "UnitlessMeasurement")]
805
    Unitless,
806
    Continuous(ContinuousMeasurement),
807
    Classification(ClassificationMeasurement),
808
}
809

810
impl From<geoengine_datatypes::primitives::Measurement> for Measurement {
811
    fn from(value: geoengine_datatypes::primitives::Measurement) -> Self {
6✔
812
        match value {
6✔
813
            geoengine_datatypes::primitives::Measurement::Unitless => Self::Unitless,
2✔
814
            geoengine_datatypes::primitives::Measurement::Continuous(cm) => {
4✔
815
                Self::Continuous(cm.into())
4✔
816
            }
817
            geoengine_datatypes::primitives::Measurement::Classification(cm) => {
×
818
                Self::Classification(cm.into())
×
819
            }
820
        }
821
    }
6✔
822
}
823

824
impl From<Measurement> for geoengine_datatypes::primitives::Measurement {
825
    fn from(value: Measurement) -> Self {
24✔
826
        match value {
24✔
827
            Measurement::Unitless => Self::Unitless,
20✔
828
            Measurement::Continuous(cm) => Self::Continuous(cm.into()),
4✔
829
            Measurement::Classification(cm) => Self::Classification(cm.into()),
×
830
        }
831
    }
24✔
832
}
833

834
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
12✔
835
pub struct ContinuousMeasurement {
836
    pub measurement: String,
837
    pub unit: Option<String>,
838
}
839

840
impl From<geoengine_datatypes::primitives::ContinuousMeasurement> for ContinuousMeasurement {
841
    fn from(value: geoengine_datatypes::primitives::ContinuousMeasurement) -> Self {
4✔
842
        Self {
4✔
843
            measurement: value.measurement,
4✔
844
            unit: value.unit,
4✔
845
        }
4✔
846
    }
4✔
847
}
848

849
impl From<ContinuousMeasurement> for geoengine_datatypes::primitives::ContinuousMeasurement {
850
    fn from(value: ContinuousMeasurement) -> Self {
4✔
851
        Self {
4✔
852
            measurement: value.measurement,
4✔
853
            unit: value.unit,
4✔
854
        }
4✔
855
    }
4✔
856
}
857

858
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, ToSchema)]
2✔
859
#[serde(
860
    try_from = "SerializableClassificationMeasurement",
861
    into = "SerializableClassificationMeasurement"
862
)]
863
pub struct ClassificationMeasurement {
864
    pub measurement: String,
865
    pub classes: HashMap<u8, String>,
866
}
867

868
impl From<geoengine_datatypes::primitives::ClassificationMeasurement>
869
    for ClassificationMeasurement
870
{
871
    fn from(value: geoengine_datatypes::primitives::ClassificationMeasurement) -> Self {
×
872
        Self {
×
873
            measurement: value.measurement,
×
874
            classes: value.classes,
×
875
        }
×
876
    }
×
877
}
878

879
impl From<ClassificationMeasurement>
880
    for geoengine_datatypes::primitives::ClassificationMeasurement
881
{
882
    fn from(value: ClassificationMeasurement) -> Self {
×
883
        Self {
×
884
            measurement: value.measurement,
×
885
            classes: value.classes,
×
886
        }
×
887
    }
×
888
}
889

890
/// A type that is solely for serde's serializability.
891
/// You cannot serialize floats as JSON map keys.
892
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
×
893
pub struct SerializableClassificationMeasurement {
894
    pub measurement: String,
895
    // use a BTreeMap to preserve the order of the keys
896
    pub classes: BTreeMap<String, String>,
897
}
898

899
impl From<ClassificationMeasurement> for SerializableClassificationMeasurement {
900
    fn from(measurement: ClassificationMeasurement) -> Self {
×
901
        let mut classes = BTreeMap::new();
×
902
        for (k, v) in measurement.classes {
×
903
            classes.insert(k.to_string(), v);
×
904
        }
×
905
        Self {
×
906
            measurement: measurement.measurement,
×
907
            classes,
×
908
        }
×
909
    }
×
910
}
911

912
impl TryFrom<SerializableClassificationMeasurement> for ClassificationMeasurement {
913
    type Error = <u8 as FromStr>::Err;
914

915
    fn try_from(measurement: SerializableClassificationMeasurement) -> Result<Self, Self::Error> {
×
916
        let mut classes = HashMap::with_capacity(measurement.classes.len());
×
917
        for (k, v) in measurement.classes {
×
918
            classes.insert(k.parse::<u8>()?, v);
×
919
        }
920
        Ok(Self {
×
921
            measurement: measurement.measurement,
×
922
            classes,
×
923
        })
×
924
    }
×
925
}
926

927
/// A partition of space that include the upper left but excludes the lower right coordinate
928
#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, Debug, ToSchema, FromSql, ToSql)]
15✔
929
#[serde(rename_all = "camelCase")]
930
pub struct SpatialPartition2D {
931
    pub upper_left_coordinate: Coordinate2D,
932
    pub lower_right_coordinate: Coordinate2D,
933
}
934

935
impl From<geoengine_datatypes::primitives::SpatialPartition2D> for SpatialPartition2D {
936
    fn from(value: geoengine_datatypes::primitives::SpatialPartition2D) -> Self {
6✔
937
        Self {
6✔
938
            upper_left_coordinate: value.upper_left().into(),
6✔
939
            lower_right_coordinate: value.lower_right().into(),
6✔
940
        }
6✔
941
    }
6✔
942
}
943

944
impl From<SpatialPartition2D> for geoengine_datatypes::primitives::SpatialPartition2D {
945
    fn from(value: SpatialPartition2D) -> Self {
10✔
946
        Self::new_unchecked(
10✔
947
            value.upper_left_coordinate.into(),
10✔
948
            value.lower_right_coordinate.into(),
10✔
949
        )
10✔
950
    }
10✔
951
}
952

953
/// A spatio-temporal rectangle with a specified resolution
954
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
6✔
955
#[serde(rename_all = "camelCase")]
956
#[aliases(
957
    VectorQueryRectangle = QueryRectangle<BoundingBox2D>,
958
    RasterQueryRectangle = QueryRectangle<SpatialPartition2D>,
959
    PlotQueryRectangle = QueryRectangle<BoundingBox2D>)
960
]
961
pub struct QueryRectangle<SpatialBounds> {
962
    pub spatial_bounds: SpatialBounds,
963
    pub time_interval: TimeInterval,
964
    pub spatial_resolution: SpatialResolution,
965
}
966

967
impl
968
    From<
969
        geoengine_datatypes::primitives::QueryRectangle<
970
            geoengine_datatypes::primitives::SpatialPartition2D,
971
            geoengine_datatypes::primitives::BandSelection,
972
        >,
973
    > for RasterQueryRectangle
974
{
975
    fn from(
2✔
976
        value: geoengine_datatypes::primitives::QueryRectangle<
2✔
977
            geoengine_datatypes::primitives::SpatialPartition2D,
2✔
978
            geoengine_datatypes::primitives::BandSelection,
2✔
979
        >,
2✔
980
    ) -> Self {
2✔
981
        Self {
2✔
982
            spatial_bounds: value.spatial_bounds.into(),
2✔
983
            time_interval: value.time_interval.into(),
2✔
984
            spatial_resolution: value.spatial_resolution.into(),
2✔
985
        }
2✔
986
    }
2✔
987
}
988

989
impl From<QueryRectangle<SpatialPartition2D>>
990
    for geoengine_datatypes::primitives::RasterQueryRectangle
991
{
992
    fn from(value: QueryRectangle<SpatialPartition2D>) -> Self {
3✔
993
        Self {
3✔
994
            spatial_bounds: value.spatial_bounds.into(),
3✔
995
            time_interval: value.time_interval.into(),
3✔
996
            spatial_resolution: value.spatial_resolution.into(),
3✔
997
            attributes: geoengine_datatypes::primitives::BandSelection::first(), // TODO: adjust once API supports attribute selection
3✔
998
        }
3✔
999
    }
3✔
1000
}
1001

1002
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema)]
2✔
1003
pub struct BandSelection(pub Vec<usize>);
1004

1005
impl From<geoengine_datatypes::primitives::BandSelection> for BandSelection {
1006
    fn from(value: geoengine_datatypes::primitives::BandSelection) -> Self {
×
1007
        Self(value.as_vec().into_iter().map(|b| b as usize).collect())
×
1008
    }
×
1009
}
1010

1011
impl TryFrom<BandSelection> for geoengine_datatypes::primitives::BandSelection {
1012
    type Error = Error;
1013

1014
    fn try_from(value: BandSelection) -> Result<Self> {
6✔
1015
        geoengine_datatypes::primitives::BandSelection::new(
6✔
1016
            value.0.into_iter().map(|b| b as u32).collect(),
8✔
1017
        )
6✔
1018
        .map_err(Into::into)
6✔
1019
    }
6✔
1020
}
1021

1022
/// The spatial resolution in SRS units
1023
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema)]
15✔
1024
pub struct SpatialResolution {
1025
    pub x: f64,
1026
    pub y: f64,
1027
}
1028

1029
impl From<geoengine_datatypes::primitives::SpatialResolution> for SpatialResolution {
1030
    fn from(value: geoengine_datatypes::primitives::SpatialResolution) -> Self {
6✔
1031
        Self {
6✔
1032
            x: value.x,
6✔
1033
            y: value.y,
6✔
1034
        }
6✔
1035
    }
6✔
1036
}
1037

1038
impl From<SpatialResolution> for geoengine_datatypes::primitives::SpatialResolution {
1039
    fn from(value: SpatialResolution) -> Self {
10✔
1040
        Self {
10✔
1041
            x: value.x,
10✔
1042
            y: value.y,
10✔
1043
        }
10✔
1044
    }
10✔
1045
}
1046

1047
#[derive(
1048
    Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, ToSchema, FromSql, ToSql,
2✔
1049
)]
1050
#[repr(C)]
1051
#[postgres(transparent)]
1052
pub struct TimeInstance(i64);
1053

1054
impl FromStr for TimeInstance {
1055
    type Err = geoengine_datatypes::primitives::DateTimeError;
1056

1057
    fn from_str(s: &str) -> Result<Self, Self::Err> {
×
1058
        let date_time = DateTime::from_str(s)?;
×
1059
        Ok(date_time.into())
×
1060
    }
×
1061
}
1062

1063
impl From<geoengine_datatypes::primitives::TimeInstance> for TimeInstance {
1064
    fn from(value: geoengine_datatypes::primitives::TimeInstance) -> Self {
146✔
1065
        Self(value.inner())
146✔
1066
    }
146✔
1067
}
1068

1069
impl From<TimeInstance> for geoengine_datatypes::primitives::TimeInstance {
1070
    fn from(value: TimeInstance) -> Self {
82✔
1071
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(value.inner())
82✔
1072
    }
82✔
1073
}
1074

1075
impl From<DateTime> for TimeInstance {
1076
    fn from(datetime: DateTime) -> Self {
×
1077
        Self::from(&datetime)
×
1078
    }
×
1079
}
1080

1081
impl From<&DateTime> for TimeInstance {
1082
    fn from(datetime: &DateTime) -> Self {
×
1083
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(
×
1084
            datetime.datetime.timestamp_millis(),
×
1085
        )
×
1086
        .into()
×
1087
    }
×
1088
}
1089

1090
impl TimeInstance {
1091
    pub const fn inner(self) -> i64 {
82✔
1092
        self.0
82✔
1093
    }
82✔
1094
}
1095

1096
impl<'de> Deserialize<'de> for TimeInstance {
1097
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
18✔
1098
    where
18✔
1099
        D: serde::Deserializer<'de>,
18✔
1100
    {
18✔
1101
        struct IsoStringOrUnixTimestamp;
1102

1103
        impl<'de> serde::de::Visitor<'de> for IsoStringOrUnixTimestamp {
1104
            type Value = TimeInstance;
1105

UNCOV
1106
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
×
1107
                formatter.write_str("RFC 3339 timestamp string or Unix timestamp integer")
×
1108
            }
×
1109

UNCOV
1110
            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
×
1111
            where
×
1112
                E: serde::de::Error,
×
1113
            {
×
1114
                TimeInstance::from_str(value).map_err(E::custom)
×
1115
            }
×
1116

1117
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
18✔
1118
            where
18✔
1119
                E: serde::de::Error,
18✔
1120
            {
18✔
1121
                geoengine_datatypes::primitives::TimeInstance::from_millis(v)
18✔
1122
                    .map(Into::into)
18✔
1123
                    .map_err(E::custom)
18✔
1124
            }
18✔
1125

1126
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
18✔
1127
            where
18✔
1128
                E: serde::de::Error,
18✔
1129
            {
18✔
1130
                Self::visit_i64(self, v as i64)
18✔
1131
            }
18✔
1132
        }
1133

1134
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
18✔
1135
    }
18✔
1136
}
1137

1138
/// A time granularity.
1139
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, FromSql, ToSql)]
8✔
1140
#[serde(rename_all = "camelCase")]
1141
pub enum TimeGranularity {
1142
    Millis,
1143
    Seconds,
1144
    Minutes,
1145
    Hours,
1146
    Days,
1147
    Months,
1148
    Years,
1149
}
1150

1151
impl From<geoengine_datatypes::primitives::TimeGranularity> for TimeGranularity {
1152
    fn from(value: geoengine_datatypes::primitives::TimeGranularity) -> Self {
4✔
1153
        match value {
4✔
1154
            geoengine_datatypes::primitives::TimeGranularity::Millis => Self::Millis,
×
1155
            geoengine_datatypes::primitives::TimeGranularity::Seconds => Self::Seconds,
×
1156
            geoengine_datatypes::primitives::TimeGranularity::Minutes => Self::Minutes,
×
1157
            geoengine_datatypes::primitives::TimeGranularity::Hours => Self::Hours,
×
1158
            geoengine_datatypes::primitives::TimeGranularity::Days => Self::Days,
×
1159
            geoengine_datatypes::primitives::TimeGranularity::Months => Self::Months,
4✔
1160
            geoengine_datatypes::primitives::TimeGranularity::Years => Self::Years,
×
1161
        }
1162
    }
4✔
1163
}
1164

1165
impl From<TimeGranularity> for geoengine_datatypes::primitives::TimeGranularity {
1166
    fn from(value: TimeGranularity) -> Self {
4✔
1167
        match value {
4✔
1168
            TimeGranularity::Millis => Self::Millis,
×
1169
            TimeGranularity::Seconds => Self::Seconds,
×
1170
            TimeGranularity::Minutes => Self::Minutes,
×
1171
            TimeGranularity::Hours => Self::Hours,
×
1172
            TimeGranularity::Days => Self::Days,
×
1173
            TimeGranularity::Months => Self::Months,
4✔
1174
            TimeGranularity::Years => Self::Years,
×
1175
        }
1176
    }
4✔
1177
}
1178

1179
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, FromSql, ToSql)]
12✔
1180
pub struct TimeStep {
1181
    pub granularity: TimeGranularity,
1182
    pub step: u32, // TODO: ensure on deserialization it is > 0
1183
}
1184

1185
impl From<geoengine_datatypes::primitives::TimeStep> for TimeStep {
1186
    fn from(value: geoengine_datatypes::primitives::TimeStep) -> Self {
4✔
1187
        Self {
4✔
1188
            granularity: value.granularity.into(),
4✔
1189
            step: value.step,
4✔
1190
        }
4✔
1191
    }
4✔
1192
}
1193

1194
impl From<TimeStep> for geoengine_datatypes::primitives::TimeStep {
1195
    fn from(value: TimeStep) -> Self {
4✔
1196
        Self {
4✔
1197
            granularity: value.granularity.into(),
4✔
1198
            step: value.step,
4✔
1199
        }
4✔
1200
    }
4✔
1201
}
1202

1203
/// Stores time intervals in ms in close-open semantic [start, end)
1204
#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ToSql, FromSql, ToSchema)]
27✔
1205
pub struct TimeInterval {
1206
    start: TimeInstance,
1207
    end: TimeInstance,
1208
}
1209

1210
impl From<TimeInterval> for geoengine_datatypes::primitives::TimeInterval {
1211
    fn from(value: TimeInterval) -> Self {
41✔
1212
        geoengine_datatypes::primitives::TimeInterval::new_unchecked::<
41✔
1213
            geoengine_datatypes::primitives::TimeInstance,
41✔
1214
            geoengine_datatypes::primitives::TimeInstance,
41✔
1215
        >(value.start.into(), value.end.into())
41✔
1216
    }
41✔
1217
}
1218

1219
impl From<geoengine_datatypes::primitives::TimeInterval> for TimeInterval {
1220
    fn from(value: geoengine_datatypes::primitives::TimeInterval) -> Self {
64✔
1221
        Self {
64✔
1222
            start: value.start().into(),
64✔
1223
            end: value.end().into(),
64✔
1224
        }
64✔
1225
    }
64✔
1226
}
1227

1228
impl core::fmt::Debug for TimeInterval {
1229
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
×
1230
        write!(
×
1231
            f,
×
1232
            "TimeInterval [{}, {})",
×
1233
            self.start.inner(),
×
1234
            &self.end.inner()
×
1235
        )
×
1236
    }
×
1237
}
1238

1239
#[derive(
1240
    Debug,
1241
    Ord,
1242
    PartialOrd,
1243
    Eq,
1244
    PartialEq,
1245
    Hash,
1246
    Deserialize,
20✔
1247
    Serialize,
1248
    Copy,
1249
    Clone,
1250
    ToSchema,
2✔
1251
    FromSql,
48✔
1252
    ToSql,
24✔
1253
)]
1254
pub enum RasterDataType {
1255
    U8,
1256
    U16,
1257
    U32,
1258
    U64,
1259
    I8,
1260
    I16,
1261
    I32,
1262
    I64,
1263
    F32,
1264
    F64,
1265
}
1266

1267
impl From<geoengine_datatypes::raster::RasterDataType> for RasterDataType {
1268
    fn from(value: geoengine_datatypes::raster::RasterDataType) -> Self {
4✔
1269
        match value {
4✔
1270
            geoengine_datatypes::raster::RasterDataType::U8 => Self::U8,
4✔
1271
            geoengine_datatypes::raster::RasterDataType::U16 => Self::U16,
×
1272
            geoengine_datatypes::raster::RasterDataType::U32 => Self::U32,
×
1273
            geoengine_datatypes::raster::RasterDataType::U64 => Self::U64,
×
1274
            geoengine_datatypes::raster::RasterDataType::I8 => Self::I8,
×
1275
            geoengine_datatypes::raster::RasterDataType::I16 => Self::I16,
×
1276
            geoengine_datatypes::raster::RasterDataType::I32 => Self::I32,
×
1277
            geoengine_datatypes::raster::RasterDataType::I64 => Self::I64,
×
1278
            geoengine_datatypes::raster::RasterDataType::F32 => Self::F32,
×
1279
            geoengine_datatypes::raster::RasterDataType::F64 => Self::F64,
×
1280
        }
1281
    }
4✔
1282
}
1283

1284
impl From<RasterDataType> for geoengine_datatypes::raster::RasterDataType {
1285
    fn from(value: RasterDataType) -> Self {
4✔
1286
        match value {
4✔
1287
            RasterDataType::U8 => Self::U8,
4✔
1288
            RasterDataType::U16 => Self::U16,
×
1289
            RasterDataType::U32 => Self::U32,
×
1290
            RasterDataType::U64 => Self::U64,
×
1291
            RasterDataType::I8 => Self::I8,
×
1292
            RasterDataType::I16 => Self::I16,
×
1293
            RasterDataType::I32 => Self::I32,
×
1294
            RasterDataType::I64 => Self::I64,
×
1295
            RasterDataType::F32 => Self::F32,
×
1296
            RasterDataType::F64 => Self::F64,
×
1297
        }
1298
    }
4✔
1299
}
1300

1301
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
×
1302
#[serde(rename_all = "UPPERCASE")]
1303
pub enum ResamplingMethod {
1304
    Nearest,
1305
    Average,
1306
    Bilinear,
1307
    Cubic,
1308
    CubicSpline,
1309
    Lanczos,
1310
}
1311

1312
impl std::fmt::Display for ResamplingMethod {
1313
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
1314
        match self {
×
1315
            ResamplingMethod::Nearest => write!(f, "NEAREST"),
×
1316
            ResamplingMethod::Average => write!(f, "AVERAGE"),
×
1317
            ResamplingMethod::Bilinear => write!(f, "BILINEAR"),
×
1318
            ResamplingMethod::Cubic => write!(f, "CUBIC"),
×
1319
            ResamplingMethod::CubicSpline => write!(f, "CUBICSPLINE"),
×
1320
            ResamplingMethod::Lanczos => write!(f, "LANCZOS"),
×
1321
        }
1322
    }
×
1323
}
1324

1325
impl From<ResamplingMethod> for geoengine_datatypes::util::gdal::ResamplingMethod {
1326
    fn from(value: ResamplingMethod) -> Self {
×
1327
        match value {
×
1328
            ResamplingMethod::Nearest => Self::Nearest,
×
1329
            ResamplingMethod::Average => Self::Average,
×
1330
            ResamplingMethod::Bilinear => Self::Bilinear,
×
1331
            ResamplingMethod::Cubic => Self::Cubic,
×
1332
            ResamplingMethod::CubicSpline => Self::CubicSpline,
×
1333
            ResamplingMethod::Lanczos => Self::Lanczos,
×
1334
        }
1335
    }
×
1336
}
1337

1338
/// `RgbaColor` defines a 32 bit RGB color with alpha value
1339
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
22✔
1340
pub struct RgbaColor(pub [u8; 4]);
1341

1342
// manual implementation utoipa generates an integer field
1343
impl<'a> ToSchema<'a> for RgbaColor {
1344
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1345
        use utoipa::openapi::*;
1346
        (
2✔
1347
            "RgbaColor",
2✔
1348
            ArrayBuilder::new()
2✔
1349
                .items(ObjectBuilder::new().schema_type(SchemaType::Integer))
2✔
1350
                .min_items(Some(4))
2✔
1351
                .max_items(Some(4))
2✔
1352
                .into(),
2✔
1353
        )
2✔
1354
    }
2✔
1355
}
1356

1357
impl From<geoengine_datatypes::operations::image::RgbaColor> for RgbaColor {
1358
    fn from(color: geoengine_datatypes::operations::image::RgbaColor) -> Self {
27✔
1359
        Self(color.into_inner())
27✔
1360
    }
27✔
1361
}
1362

1363
impl From<RgbaColor> for geoengine_datatypes::operations::image::RgbaColor {
1364
    fn from(color: RgbaColor) -> Self {
22✔
1365
        Self::new(color.0[0], color.0[1], color.0[2], color.0[3])
22✔
1366
    }
22✔
1367
}
1368

1369
/// A container type for breakpoints that specify a value to color mapping
1370
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
24✔
1371
pub struct Breakpoint {
1372
    pub value: NotNanF64,
1373
    pub color: RgbaColor,
1374
}
1375

1376
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
8✔
1377
pub struct NotNanF64(NotNan<f64>);
1378

1379
impl From<NotNan<f64>> for NotNanF64 {
1380
    fn from(value: NotNan<f64>) -> Self {
10✔
1381
        Self(value)
10✔
1382
    }
10✔
1383
}
1384

1385
impl From<NotNanF64> for NotNan<f64> {
1386
    fn from(value: NotNanF64) -> Self {
8✔
1387
        value.0
8✔
1388
    }
8✔
1389
}
1390

1391
impl ToSql for NotNanF64 {
1392
    fn to_sql(
×
1393
        &self,
×
1394
        ty: &postgres_types::Type,
×
1395
        w: &mut bytes::BytesMut,
×
1396
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
1397
        <f64 as ToSql>::to_sql(&self.0.into_inner(), ty, w)
×
1398
    }
×
1399

1400
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1401
        <f64 as ToSql>::accepts(ty)
×
1402
    }
×
1403

1404
    postgres_types::to_sql_checked!();
1405
}
1406

1407
impl<'a> FromSql<'a> for NotNanF64 {
1408
    fn from_sql(
×
1409
        ty: &postgres_types::Type,
×
1410
        raw: &'a [u8],
×
1411
    ) -> Result<NotNanF64, Box<dyn std::error::Error + Sync + Send>> {
×
1412
        let value = <f64 as FromSql>::from_sql(ty, raw)?;
×
1413

1414
        Ok(NotNanF64(value.try_into()?))
×
1415
    }
×
1416

1417
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1418
        <f64 as FromSql>::accepts(ty)
×
1419
    }
×
1420
}
1421

1422
// manual implementation because of NotNan
1423
impl<'a> ToSchema<'a> for Breakpoint {
1424
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1425
        use utoipa::openapi::*;
1426
        (
2✔
1427
            "Breakpoint",
2✔
1428
            ObjectBuilder::new()
2✔
1429
                .property("value", Object::with_type(SchemaType::Number))
2✔
1430
                .property("color", Ref::from_schema_name("RgbaColor"))
2✔
1431
                .required("value")
2✔
1432
                .required("color")
2✔
1433
                .into(),
2✔
1434
        )
2✔
1435
    }
2✔
1436
}
1437

1438
impl From<geoengine_datatypes::operations::image::Breakpoint> for Breakpoint {
1439
    fn from(breakpoint: geoengine_datatypes::operations::image::Breakpoint) -> Self {
10✔
1440
        Self {
10✔
1441
            value: breakpoint.value.into(),
10✔
1442
            color: breakpoint.color.into(),
10✔
1443
        }
10✔
1444
    }
10✔
1445
}
1446

1447
impl From<Breakpoint> for geoengine_datatypes::operations::image::Breakpoint {
1448
    fn from(breakpoint: Breakpoint) -> Self {
8✔
1449
        Self {
8✔
1450
            value: breakpoint.value.into(),
8✔
1451
            color: breakpoint.color.into(),
8✔
1452
        }
8✔
1453
    }
8✔
1454
}
1455

1456
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
20✔
1457
#[serde(rename_all = "camelCase")]
1458
pub struct LinearGradient {
1459
    pub breakpoints: Vec<Breakpoint>,
1460
    pub no_data_color: RgbaColor,
1461
    pub over_color: RgbaColor,
1462
    pub under_color: RgbaColor,
1463
}
1464

1465
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1466
#[serde(rename_all = "camelCase")]
1467
pub struct LogarithmicGradient {
1468
    pub breakpoints: Vec<Breakpoint>,
1469
    pub no_data_color: RgbaColor,
1470
    pub over_color: RgbaColor,
1471
    pub under_color: RgbaColor,
1472
}
1473

1474
/// A colorizer specifies a mapping between raster values and an output image
1475
/// There are different variants that perform different kinds of mapping.
1476
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
4✔
1477
#[serde(rename_all = "camelCase", tag = "type")]
1478
pub enum Colorizer {
1479
    #[serde(rename_all = "camelCase")]
1480
    LinearGradient(LinearGradient),
1481
    #[serde(rename_all = "camelCase")]
1482
    LogarithmicGradient(LogarithmicGradient),
1483
    #[serde(rename_all = "camelCase")]
1484
    #[schema(title = "PaletteColorizer")]
1485
    Palette {
1486
        colors: Palette,
1487
        no_data_color: RgbaColor,
1488
        default_color: RgbaColor,
1489
    },
1490
}
1491

1492
impl From<geoengine_datatypes::operations::image::Colorizer> for Colorizer {
1493
    fn from(v: geoengine_datatypes::operations::image::Colorizer) -> Self {
5✔
1494
        match v {
5✔
1495
            geoengine_datatypes::operations::image::Colorizer::LinearGradient {
1496
                breakpoints,
5✔
1497
                no_data_color,
5✔
1498
                over_color,
5✔
1499
                under_color,
5✔
1500
            } => Self::LinearGradient(LinearGradient {
5✔
1501
                breakpoints: breakpoints
5✔
1502
                    .into_iter()
5✔
1503
                    .map(Into::into)
5✔
1504
                    .collect::<Vec<Breakpoint>>(),
5✔
1505
                no_data_color: no_data_color.into(),
5✔
1506
                over_color: over_color.into(),
5✔
1507
                under_color: under_color.into(),
5✔
1508
            }),
5✔
1509
            geoengine_datatypes::operations::image::Colorizer::LogarithmicGradient {
1510
                breakpoints,
×
1511
                no_data_color,
×
1512
                over_color,
×
1513
                under_color,
×
1514
            } => Self::LogarithmicGradient(LogarithmicGradient {
×
1515
                breakpoints: breakpoints
×
1516
                    .into_iter()
×
1517
                    .map(Into::into)
×
1518
                    .collect::<Vec<Breakpoint>>(),
×
1519
                no_data_color: no_data_color.into(),
×
1520
                over_color: over_color.into(),
×
1521
                under_color: under_color.into(),
×
1522
            }),
×
1523
            geoengine_datatypes::operations::image::Colorizer::Palette {
1524
                colors,
×
1525
                no_data_color,
×
1526
                default_color,
×
1527
            } => Self::Palette {
×
1528
                colors: colors.into(),
×
1529
                no_data_color: no_data_color.into(),
×
1530
                default_color: default_color.into(),
×
1531
            },
×
1532
        }
1533
    }
5✔
1534
}
1535

1536
impl From<Colorizer> for geoengine_datatypes::operations::image::Colorizer {
1537
    fn from(v: Colorizer) -> Self {
4✔
1538
        match v {
4✔
1539
            Colorizer::LinearGradient(linear_gradient) => Self::LinearGradient {
4✔
1540
                breakpoints: linear_gradient
4✔
1541
                    .breakpoints
4✔
1542
                    .into_iter()
4✔
1543
                    .map(Into::into)
4✔
1544
                    .collect::<Vec<geoengine_datatypes::operations::image::Breakpoint>>(),
4✔
1545
                no_data_color: linear_gradient.no_data_color.into(),
4✔
1546
                over_color: linear_gradient.over_color.into(),
4✔
1547
                under_color: linear_gradient.under_color.into(),
4✔
1548
            },
4✔
1549
            Colorizer::LogarithmicGradient(logarithmic_gradient) => Self::LogarithmicGradient {
×
1550
                breakpoints: logarithmic_gradient
×
1551
                    .breakpoints
×
1552
                    .into_iter()
×
1553
                    .map(Into::into)
×
1554
                    .collect::<Vec<geoengine_datatypes::operations::image::Breakpoint>>(),
×
1555
                no_data_color: logarithmic_gradient.no_data_color.into(),
×
1556
                over_color: logarithmic_gradient.over_color.into(),
×
1557
                under_color: logarithmic_gradient.under_color.into(),
×
1558
            },
×
1559

1560
            Colorizer::Palette {
1561
                colors,
×
1562
                no_data_color,
×
1563
                default_color,
×
1564
            } => Self::Palette {
×
1565
                colors: colors.into(),
×
1566
                no_data_color: no_data_color.into(),
×
1567
                default_color: default_color.into(),
×
1568
            },
×
1569
        }
1570
    }
4✔
1571
}
1572

1573
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, ToSchema)]
46✔
1574
#[serde(rename_all = "camelCase", tag = "type")]
1575
pub enum RasterColorizer {
1576
    #[serde(rename_all = "camelCase")]
1577
    #[schema(title = "SingleBandRasterColorizer")]
1578
    SingleBand {
1579
        band: u32,
1580
        band_colorizer: Colorizer,
1581
    },
1582
    #[serde(rename_all = "camelCase")]
1583
    #[schema(title = "MultiBandRasterColorizer")]
1584
    MultiBand {
1585
        /// The band index of the red channel.
1586
        red_band: u32,
1587
        /// The minimum value for the red channel.
1588
        red_min: f64,
1589
        /// The maximum value for the red channel.
1590
        red_max: f64,
1591
        /// A scaling factor for the red channel between 0 and 1.
1592
        #[serde(default = "num_traits::One::one")]
1593
        red_scale: f64,
1594

1595
        /// The band index of the green channel.
1596
        green_band: u32,
1597
        /// The minimum value for the red channel.
1598
        green_min: f64,
1599
        /// The maximum value for the red channel.
1600
        green_max: f64,
1601
        /// A scaling factor for the green channel between 0 and 1.
1602
        #[serde(default = "num_traits::One::one")]
1603
        green_scale: f64,
1604

1605
        /// The band index of the blue channel.
1606
        blue_band: u32,
1607
        /// The minimum value for the red channel.
1608
        blue_min: f64,
1609
        /// The maximum value for the red channel.
1610
        blue_max: f64,
1611
        /// A scaling factor for the blue channel between 0 and 1.
1612
        #[serde(default = "num_traits::One::one")]
1613
        blue_scale: f64,
1614

1615
        /// The color to use for no data values.
1616
        /// If not specified, the no data values will be transparent.
1617
        #[serde(default = "rgba_transparent")]
1618
        no_data_color: RgbaColor,
1619
    },
1620
}
1621

1622
fn rgba_transparent() -> RgbaColor {
×
1623
    RgbaColor([0, 0, 0, 0])
×
1624
}
×
1625

1626
impl Eq for RasterColorizer {}
1627

1628
impl RasterColorizer {
1629
    pub fn band_selection(&self) -> BandSelection {
6✔
1630
        match self {
6✔
1631
            RasterColorizer::SingleBand { band, .. } => BandSelection(vec![*band as usize]),
4✔
1632
            RasterColorizer::MultiBand {
1633
                red_band,
2✔
1634
                green_band,
2✔
1635
                blue_band,
2✔
1636
                ..
2✔
1637
            } => {
2✔
1638
                let mut bands = Vec::with_capacity(3);
2✔
1639
                for band in [
6✔
1640
                    *red_band as usize,
2✔
1641
                    *green_band as usize,
2✔
1642
                    *blue_band as usize,
2✔
1643
                ] {
1644
                    if !bands.contains(&band) {
6✔
1645
                        bands.push(band);
4✔
1646
                    }
4✔
1647
                }
1648
                bands.sort_unstable(); // bands will be returned in ascending order anyway
2✔
1649
                BandSelection(bands)
2✔
1650
            }
1651
        }
1652
    }
6✔
1653
}
1654

1655
impl From<geoengine_datatypes::operations::image::RasterColorizer> for RasterColorizer {
1656
    fn from(v: geoengine_datatypes::operations::image::RasterColorizer) -> Self {
×
1657
        match v {
×
1658
            geoengine_datatypes::operations::image::RasterColorizer::SingleBand {
1659
                band,
×
1660
                band_colorizer: colorizer,
×
1661
            } => Self::SingleBand {
×
1662
                band,
×
1663
                band_colorizer: colorizer.into(),
×
1664
            },
×
1665
            geoengine_datatypes::operations::image::RasterColorizer::MultiBand {
1666
                red_band,
×
1667
                green_band,
×
1668
                blue_band,
×
1669
                rgb_params,
×
1670
            } => Self::MultiBand {
×
1671
                red_band,
×
1672
                green_band,
×
1673
                blue_band,
×
1674
                red_min: rgb_params.red_min,
×
1675
                red_max: rgb_params.red_max,
×
1676
                red_scale: rgb_params.red_scale,
×
1677
                green_min: rgb_params.green_min,
×
1678
                green_max: rgb_params.green_max,
×
1679
                green_scale: rgb_params.green_scale,
×
1680
                blue_min: rgb_params.blue_min,
×
1681
                blue_max: rgb_params.blue_max,
×
1682
                blue_scale: rgb_params.blue_scale,
×
1683
                no_data_color: rgb_params.no_data_color.into(),
×
1684
            },
×
1685
        }
1686
    }
×
1687
}
1688

1689
impl From<RasterColorizer> for geoengine_datatypes::operations::image::RasterColorizer {
1690
    fn from(v: RasterColorizer) -> Self {
6✔
1691
        match v {
6✔
1692
            RasterColorizer::SingleBand {
1693
                band,
4✔
1694
                band_colorizer: colorizer,
4✔
1695
            } => Self::SingleBand {
4✔
1696
                band,
4✔
1697
                band_colorizer: colorizer.into(),
4✔
1698
            },
4✔
1699
            RasterColorizer::MultiBand {
1700
                red_band,
2✔
1701
                red_min,
2✔
1702
                red_max,
2✔
1703
                red_scale,
2✔
1704
                green_band,
2✔
1705
                green_min,
2✔
1706
                green_max,
2✔
1707
                green_scale,
2✔
1708
                blue_band,
2✔
1709
                blue_min,
2✔
1710
                blue_max,
2✔
1711
                blue_scale,
2✔
1712
                no_data_color,
2✔
1713
            } => Self::MultiBand {
2✔
1714
                red_band,
2✔
1715
                green_band,
2✔
1716
                blue_band,
2✔
1717
                rgb_params: RgbParams {
2✔
1718
                    red_min,
2✔
1719
                    red_max,
2✔
1720
                    red_scale,
2✔
1721
                    green_min,
2✔
1722
                    green_max,
2✔
1723
                    green_scale,
2✔
1724
                    blue_min,
2✔
1725
                    blue_max,
2✔
1726
                    blue_scale,
2✔
1727
                    no_data_color: no_data_color.into(),
2✔
1728
                },
2✔
1729
            },
2✔
1730
        }
1731
    }
6✔
1732
}
1733

1734
/// A map from value to color
1735
///
1736
/// It is assumed that is has at least one and at most 256 entries.
1737
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1738
#[serde(try_from = "SerializablePalette", into = "SerializablePalette")]
1739
pub struct Palette(pub HashMap<NotNan<f64>, RgbaColor>);
1740

1741
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1742
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
×
1743
        Self(
×
1744
            palette
×
1745
                .into_inner()
×
1746
                .into_iter()
×
1747
                .map(|(value, color)| (value, color.into()))
×
1748
                .collect(),
×
1749
        )
×
1750
    }
×
1751
}
1752

1753
impl From<Palette> for geoengine_datatypes::operations::image::Palette {
1754
    fn from(palette: Palette) -> Self {
×
1755
        Self::new(
×
1756
            palette
×
1757
                .0
×
1758
                .into_iter()
×
1759
                .map(|(value, color)| (value, color.into()))
×
1760
                .collect(),
×
1761
        )
×
1762
    }
×
1763
}
1764

1765
/// A type that is solely for serde's serializability.
1766
/// You cannot serialize floats as JSON map keys.
1767
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
×
1768
pub struct SerializablePalette(HashMap<String, RgbaColor>);
1769

1770
impl From<Palette> for SerializablePalette {
1771
    fn from(palette: Palette) -> Self {
×
1772
        Self(
×
1773
            palette
×
1774
                .0
×
1775
                .into_iter()
×
1776
                .map(|(k, v)| (k.to_string(), v))
×
1777
                .collect(),
×
1778
        )
×
1779
    }
×
1780
}
1781

1782
impl TryFrom<SerializablePalette> for Palette {
1783
    type Error = <NotNan<f64> as FromStr>::Err;
1784

1785
    fn try_from(palette: SerializablePalette) -> Result<Self, Self::Error> {
×
1786
        let mut inner = HashMap::<NotNan<f64>, RgbaColor>::with_capacity(palette.0.len());
×
1787
        for (k, v) in palette.0 {
×
1788
            inner.insert(k.parse()?, v);
×
1789
        }
1790
        Ok(Self(inner))
×
1791
    }
×
1792
}
1793

1794
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
2✔
1795
pub struct RasterPropertiesKey {
1796
    pub domain: Option<String>,
1797
    pub key: String,
1798
}
1799

1800
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1801
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1802
        Self {
×
1803
            domain: value.domain,
×
1804
            key: value.key,
×
1805
        }
×
1806
    }
×
1807
}
1808

1809
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1810
    fn from(value: RasterPropertiesKey) -> Self {
×
1811
        Self {
×
1812
            domain: value.domain,
×
1813
            key: value.key,
×
1814
        }
×
1815
    }
×
1816
}
1817

1818
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
2✔
1819
pub enum RasterPropertiesEntryType {
1820
    Number,
1821
    String,
1822
}
1823

1824
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1825
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1826
        match value {
×
1827
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1828
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1829
        }
1830
    }
×
1831
}
1832

1833
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1834
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1835
        match value {
×
1836
            RasterPropertiesEntryType::Number => Self::Number,
×
1837
            RasterPropertiesEntryType::String => Self::String,
×
1838
        }
1839
    }
×
1840
}
1841

1842
#[derive(PartialEq, Eq, Clone, Debug)]
1843
pub struct DateTimeParseFormat {
1844
    fmt: String,
1845
    has_tz: bool,
1846
    has_time: bool,
1847
}
1848

1849
impl<'a> ToSchema<'a> for DateTimeParseFormat {
1850
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1851
        use utoipa::openapi::*;
1852
        (
2✔
1853
            "DateTimeParseFormat",
2✔
1854
            ObjectBuilder::new().schema_type(SchemaType::String).into(),
2✔
1855
        )
2✔
1856
    }
2✔
1857
}
1858

1859
impl<'de> Deserialize<'de> for DateTimeParseFormat {
1860
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4✔
1861
    where
4✔
1862
        D: serde::Deserializer<'de>,
4✔
1863
    {
4✔
1864
        let s = String::deserialize(deserializer)?;
4✔
1865
        Ok(geoengine_datatypes::primitives::DateTimeParseFormat::custom(s).into())
4✔
1866
    }
4✔
1867
}
1868

1869
impl Serialize for DateTimeParseFormat {
1870
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
5✔
1871
    where
5✔
1872
        S: serde::Serializer,
5✔
1873
    {
5✔
1874
        serializer.serialize_str(&self.fmt)
5✔
1875
    }
5✔
1876
}
1877

1878
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1879
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
8✔
1880
        Self {
8✔
1881
            fmt: value._to_parse_format().to_string(),
8✔
1882
            has_tz: value.has_tz(),
8✔
1883
            has_time: value.has_time(),
8✔
1884
        }
8✔
1885
    }
8✔
1886
}
1887

1888
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1889
    fn from(value: DateTimeParseFormat) -> Self {
4✔
1890
        Self::custom(value.fmt)
4✔
1891
    }
4✔
1892
}
1893

1894
impl DateTimeParseFormat {
1895
    // this is used as default value
1896
    pub fn unix() -> Self {
×
1897
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1898
    }
×
1899
}
1900

1901
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1902
pub struct NoGeometry;
1903

1904
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
1905
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
1906
        Self {}
×
1907
    }
×
1908
}
1909

1910
impl From<NoGeometry> for geoengine_datatypes::primitives::NoGeometry {
1911
    fn from(_: NoGeometry) -> Self {
×
1912
        Self {}
×
1913
    }
×
1914
}
1915

1916
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1917
pub struct MultiPoint {
1918
    pub coordinates: Vec<Coordinate2D>,
1919
}
1920

1921
impl From<geoengine_datatypes::primitives::MultiPoint> for MultiPoint {
1922
    fn from(value: geoengine_datatypes::primitives::MultiPoint) -> Self {
×
1923
        Self {
×
1924
            coordinates: value.points().iter().map(|x| (*x).into()).collect(),
×
1925
        }
×
1926
    }
×
1927
}
1928

1929
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
1930
    fn from(value: MultiPoint) -> Self {
×
1931
        Self::new(value.coordinates.into_iter().map(Into::into).collect())
×
1932
            .expect("it should always be able to convert it")
×
1933
    }
×
1934
}
1935

1936
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1937
pub struct MultiLineString {
1938
    pub coordinates: Vec<Vec<Coordinate2D>>,
1939
}
1940

1941
impl From<geoengine_datatypes::primitives::MultiLineString> for MultiLineString {
1942
    fn from(value: geoengine_datatypes::primitives::MultiLineString) -> Self {
×
1943
        Self {
×
1944
            coordinates: value
×
1945
                .lines()
×
1946
                .iter()
×
1947
                .map(|x| x.iter().map(|x| (*x).into()).collect())
×
1948
                .collect(),
×
1949
        }
×
1950
    }
×
1951
}
1952

1953
impl From<MultiLineString> for geoengine_datatypes::primitives::MultiLineString {
1954
    fn from(value: MultiLineString) -> Self {
×
1955
        Self::new(
×
1956
            value
×
1957
                .coordinates
×
1958
                .into_iter()
×
1959
                .map(|x| x.into_iter().map(Into::into).collect())
×
1960
                .collect(),
×
1961
        )
×
1962
        .expect("it should always be able to convert it")
×
1963
    }
×
1964
}
1965

1966
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1967
pub struct MultiPolygon {
1968
    pub polygons: Vec<Vec<Vec<Coordinate2D>>>,
1969
}
1970

1971
impl From<geoengine_datatypes::primitives::MultiPolygon> for MultiPolygon {
1972
    fn from(value: geoengine_datatypes::primitives::MultiPolygon) -> Self {
×
1973
        Self {
×
1974
            polygons: value
×
1975
                .polygons()
×
1976
                .iter()
×
1977
                .map(|x| {
×
1978
                    x.iter()
×
1979
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1980
                        .collect()
×
1981
                })
×
1982
                .collect(),
×
1983
        }
×
1984
    }
×
1985
}
1986

1987
impl From<MultiPolygon> for geoengine_datatypes::primitives::MultiPolygon {
1988
    fn from(value: MultiPolygon) -> Self {
×
1989
        Self::new(
×
1990
            value
×
1991
                .polygons
×
1992
                .iter()
×
1993
                .map(|x| {
×
1994
                    x.iter()
×
1995
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1996
                        .collect()
×
1997
                })
×
1998
                .collect(),
×
1999
        )
×
2000
        .expect("it should always be able to convert it")
×
2001
    }
×
2002
}
2003

2004
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
1✔
2005
pub struct StringPair((String, String));
2006

2007
pub type GdalConfigOption = StringPair;
2008
pub type AxisLabels = StringPair;
2009

2010
impl<'a> ToSchema<'a> for StringPair {
2011
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
6✔
2012
        use utoipa::openapi::*;
2013
        (
6✔
2014
            "StringPair",
6✔
2015
            ArrayBuilder::new()
6✔
2016
                .items(Object::with_type(SchemaType::String))
6✔
2017
                .min_items(Some(2))
6✔
2018
                .max_items(Some(2))
6✔
2019
                .into(),
6✔
2020
        )
6✔
2021
    }
6✔
2022

2023
    fn aliases() -> Vec<(&'a str, utoipa::openapi::Schema)> {
6✔
2024
        let utoipa::openapi::RefOr::T(unpacked_schema) = Self::schema().1 else {
6✔
2025
            unreachable!()
×
2026
        };
2027
        vec![
6✔
2028
            ("GdalConfigOption", unpacked_schema.clone()),
6✔
2029
            ("AxisLabels", unpacked_schema),
6✔
2030
        ]
6✔
2031
    }
6✔
2032
}
2033

2034
impl From<(String, String)> for StringPair {
2035
    fn from(value: (String, String)) -> Self {
32✔
2036
        Self(value)
32✔
2037
    }
32✔
2038
}
2039

2040
impl From<StringPair> for (String, String) {
2041
    fn from(value: StringPair) -> Self {
×
2042
        value.0
×
2043
    }
×
2044
}
2045

2046
impl From<StringPair> for geoengine_datatypes::util::StringPair {
2047
    fn from(value: StringPair) -> Self {
2✔
2048
        Self::new(value.0 .0, value.0 .1)
2✔
2049
    }
2✔
2050
}
2051

2052
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
2✔
2053
pub enum PlotOutputFormat {
2054
    JsonPlain,
2055
    JsonVega,
2056
    ImagePng,
2057
}
2058

2059
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, PartialOrd, Deserialize, ToSchema)]
5✔
2060
pub struct CacheTtlSeconds(u32);
2061

2062
const MAX_CACHE_TTL_SECONDS: u32 = 31_536_000; // 1 year
2063

2064
impl CacheTtlSeconds {
2065
    pub fn new(seconds: u32) -> Self {
×
2066
        Self(seconds.min(MAX_CACHE_TTL_SECONDS))
×
2067
    }
×
2068

2069
    pub fn max() -> Self {
×
2070
        Self(MAX_CACHE_TTL_SECONDS)
×
2071
    }
×
2072

2073
    pub fn seconds(self) -> u32 {
×
2074
        self.0
×
2075
    }
×
2076
}
2077

2078
impl From<geoengine_datatypes::primitives::CacheTtlSeconds> for CacheTtlSeconds {
2079
    fn from(value: geoengine_datatypes::primitives::CacheTtlSeconds) -> Self {
5✔
2080
        Self(value.seconds())
5✔
2081
    }
5✔
2082
}
2083

2084
impl From<CacheTtlSeconds> for geoengine_datatypes::primitives::CacheTtlSeconds {
2085
    fn from(value: CacheTtlSeconds) -> Self {
9✔
2086
        Self::new(value.0)
9✔
2087
    }
9✔
2088
}
2089

2090
impl ToSql for CacheTtlSeconds {
2091
    fn to_sql(
×
2092
        &self,
×
2093
        ty: &postgres_types::Type,
×
2094
        w: &mut bytes::BytesMut,
×
2095
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
2096
        <i32 as ToSql>::to_sql(&(self.0 as i32), ty, w)
×
2097
    }
×
2098

2099
    fn accepts(ty: &postgres_types::Type) -> bool {
×
2100
        <i32 as ToSql>::accepts(ty)
×
2101
    }
×
2102

2103
    postgres_types::to_sql_checked!();
2104
}
2105

2106
impl<'a> FromSql<'a> for CacheTtlSeconds {
2107
    fn from_sql(
×
2108
        ty: &postgres_types::Type,
×
2109
        raw: &'a [u8],
×
2110
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
2111
        Ok(Self(<i32 as FromSql>::from_sql(ty, raw)? as u32))
×
2112
    }
×
2113

2114
    fn accepts(ty: &postgres_types::Type) -> bool {
×
2115
        <i32 as FromSql>::accepts(ty)
×
2116
    }
×
2117
}
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