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

geo-engine / geoengine / 10618683645

29 Aug 2024 03:43PM UTC coverage: 91.108% (-0.009%) from 91.117%
10618683645

push

github

web-flow
Merge pull request #955 from 1lutz/detailed-error-log

More detailed error messages

203 of 267 new or added lines in 35 files covered. (76.03%)

20 existing lines in 9 files now uncovered.

133154 of 146149 relevant lines covered (91.11%)

52778.13 hits per line

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

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

17
identifier!(DataProviderId);
18

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1576
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
16✔
1577
#[serde(rename_all = "camelCase", tag = "type")]
1578
pub enum RasterColorizer {
1579
    #[serde(rename_all = "camelCase")]
1580
    #[schema(title = "SingleBandRasterColorizer")]
1581
    SingleBand {
1582
        band: u32,
1583
        band_colorizer: Colorizer,
1584
    },
1585
    // TODO: multiband colorizer, e.g.
1586
    // MultiBand {
1587
    //     red: ...,
1588
    //     green: ...,
1589
    //     blue: ..,
1590
    // },
1591
}
1592

1593
impl RasterColorizer {
1594
    pub fn band_selection(&self) -> BandSelection {
×
1595
        match self {
×
1596
            RasterColorizer::SingleBand { band, .. } => BandSelection(vec![*band as usize]),
×
1597
        }
×
1598
    }
×
1599
}
1600

1601
impl From<geoengine_datatypes::operations::image::RasterColorizer> for RasterColorizer {
1602
    fn from(v: geoengine_datatypes::operations::image::RasterColorizer) -> Self {
×
1603
        match v {
×
1604
            geoengine_datatypes::operations::image::RasterColorizer::SingleBand {
×
1605
                band,
×
1606
                band_colorizer: colorizer,
×
1607
            } => Self::SingleBand {
×
1608
                band,
×
1609
                band_colorizer: colorizer.into(),
×
1610
            },
×
1611
        }
×
1612
    }
×
1613
}
1614
/// A map from value to color
1615
///
1616
/// It is assumed that is has at least one and at most 256 entries.
1617
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1618
#[serde(try_from = "SerializablePalette", into = "SerializablePalette")]
1619
pub struct Palette(pub HashMap<NotNan<f64>, RgbaColor>);
1620

1621
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1622
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
×
1623
        Self(
×
1624
            palette
×
1625
                .into_inner()
×
1626
                .into_iter()
×
1627
                .map(|(value, color)| (value, color.into()))
×
1628
                .collect(),
×
1629
        )
×
1630
    }
×
1631
}
1632

1633
impl From<Palette> for geoengine_datatypes::operations::image::Palette {
1634
    fn from(palette: Palette) -> Self {
×
1635
        Self::new(
×
1636
            palette
×
1637
                .0
×
1638
                .into_iter()
×
1639
                .map(|(value, color)| (value, color.into()))
×
1640
                .collect(),
×
1641
        )
×
1642
    }
×
1643
}
1644

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

1650
impl From<Palette> for SerializablePalette {
1651
    fn from(palette: Palette) -> Self {
×
1652
        Self(
×
1653
            palette
×
1654
                .0
×
1655
                .into_iter()
×
1656
                .map(|(k, v)| (k.to_string(), v))
×
1657
                .collect(),
×
1658
        )
×
1659
    }
×
1660
}
1661

1662
impl TryFrom<SerializablePalette> for Palette {
1663
    type Error = <NotNan<f64> as FromStr>::Err;
1664

1665
    fn try_from(palette: SerializablePalette) -> Result<Self, Self::Error> {
×
1666
        let mut inner = HashMap::<NotNan<f64>, RgbaColor>::with_capacity(palette.0.len());
×
1667
        for (k, v) in palette.0 {
×
1668
            inner.insert(k.parse()?, v);
×
1669
        }
1670
        Ok(Self(inner))
×
1671
    }
×
1672
}
1673

1674
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
2✔
1675
pub struct RasterPropertiesKey {
1676
    pub domain: Option<String>,
1677
    pub key: String,
1678
}
1679

1680
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1681
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1682
        Self {
×
1683
            domain: value.domain,
×
1684
            key: value.key,
×
1685
        }
×
1686
    }
×
1687
}
1688

1689
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1690
    fn from(value: RasterPropertiesKey) -> Self {
×
1691
        Self {
×
1692
            domain: value.domain,
×
1693
            key: value.key,
×
1694
        }
×
1695
    }
×
1696
}
1697

1698
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
2✔
1699
pub enum RasterPropertiesEntryType {
1700
    Number,
1701
    String,
1702
}
1703

1704
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1705
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1706
        match value {
×
1707
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1708
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1709
        }
1710
    }
×
1711
}
1712

1713
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1714
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1715
        match value {
×
1716
            RasterPropertiesEntryType::Number => Self::Number,
×
1717
            RasterPropertiesEntryType::String => Self::String,
×
1718
        }
1719
    }
×
1720
}
1721

1722
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, ToSchema)]
16✔
1723
pub struct DateTimeParseFormat {
1724
    fmt: String,
1725
    has_tz: bool,
1726
    has_time: bool,
1727
}
1728

1729
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1730
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
4✔
1731
        Self {
4✔
1732
            fmt: value._to_parse_format().to_string(),
4✔
1733
            has_tz: value.has_tz(),
4✔
1734
            has_time: value.has_time(),
4✔
1735
        }
4✔
1736
    }
4✔
1737
}
1738

1739
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1740
    fn from(value: DateTimeParseFormat) -> Self {
4✔
1741
        Self::custom(value.fmt)
4✔
1742
    }
4✔
1743
}
1744

1745
impl DateTimeParseFormat {
1746
    // this is used as default value
1747
    pub fn unix() -> Self {
×
1748
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1749
    }
×
1750
}
1751

1752
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1753
pub struct NoGeometry;
1754

1755
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
1756
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
1757
        Self {}
×
1758
    }
×
1759
}
1760

1761
impl From<NoGeometry> for geoengine_datatypes::primitives::NoGeometry {
1762
    fn from(_: NoGeometry) -> Self {
×
1763
        Self {}
×
1764
    }
×
1765
}
1766

1767
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1768
pub struct MultiPoint {
1769
    pub coordinates: Vec<Coordinate2D>,
1770
}
1771

1772
impl From<geoengine_datatypes::primitives::MultiPoint> for MultiPoint {
1773
    fn from(value: geoengine_datatypes::primitives::MultiPoint) -> Self {
×
1774
        Self {
×
1775
            coordinates: value.points().iter().map(|x| (*x).into()).collect(),
×
1776
        }
×
1777
    }
×
1778
}
1779

1780
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
1781
    fn from(value: MultiPoint) -> Self {
×
1782
        Self::new(value.coordinates.into_iter().map(Into::into).collect())
×
1783
            .expect("it should always be able to convert it")
×
1784
    }
×
1785
}
1786

1787
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1788
pub struct MultiLineString {
1789
    pub coordinates: Vec<Vec<Coordinate2D>>,
1790
}
1791

1792
impl From<geoengine_datatypes::primitives::MultiLineString> for MultiLineString {
1793
    fn from(value: geoengine_datatypes::primitives::MultiLineString) -> Self {
×
1794
        Self {
×
1795
            coordinates: value
×
1796
                .lines()
×
1797
                .iter()
×
1798
                .map(|x| x.iter().map(|x| (*x).into()).collect())
×
1799
                .collect(),
×
1800
        }
×
1801
    }
×
1802
}
1803

1804
impl From<MultiLineString> for geoengine_datatypes::primitives::MultiLineString {
1805
    fn from(value: MultiLineString) -> Self {
×
1806
        Self::new(
×
1807
            value
×
1808
                .coordinates
×
1809
                .into_iter()
×
1810
                .map(|x| x.into_iter().map(Into::into).collect())
×
1811
                .collect(),
×
1812
        )
×
1813
        .expect("it should always be able to convert it")
×
1814
    }
×
1815
}
1816

1817
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1818
pub struct MultiPolygon {
1819
    pub polygons: Vec<Vec<Vec<Coordinate2D>>>,
1820
}
1821

1822
impl From<geoengine_datatypes::primitives::MultiPolygon> for MultiPolygon {
1823
    fn from(value: geoengine_datatypes::primitives::MultiPolygon) -> Self {
×
1824
        Self {
×
1825
            polygons: value
×
1826
                .polygons()
×
1827
                .iter()
×
1828
                .map(|x| {
×
1829
                    x.iter()
×
1830
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1831
                        .collect()
×
1832
                })
×
1833
                .collect(),
×
1834
        }
×
1835
    }
×
1836
}
1837

1838
impl From<MultiPolygon> for geoengine_datatypes::primitives::MultiPolygon {
1839
    fn from(value: MultiPolygon) -> Self {
×
1840
        Self::new(
×
1841
            value
×
1842
                .polygons
×
1843
                .iter()
×
1844
                .map(|x| {
×
1845
                    x.iter()
×
1846
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1847
                        .collect()
×
1848
                })
×
1849
                .collect(),
×
1850
        )
×
1851
        .expect("it should always be able to convert it")
×
1852
    }
×
1853
}
1854

1855
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
1✔
1856
pub struct StringPair((String, String));
1857

1858
pub type GdalConfigOption = StringPair;
1859
pub type AxisLabels = StringPair;
1860

1861
impl<'a> ToSchema<'a> for StringPair {
1862
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
6✔
1863
        use utoipa::openapi::*;
6✔
1864
        (
6✔
1865
            "StringPair",
6✔
1866
            ArrayBuilder::new()
6✔
1867
                .items(Object::with_type(SchemaType::String))
6✔
1868
                .min_items(Some(2))
6✔
1869
                .max_items(Some(2))
6✔
1870
                .into(),
6✔
1871
        )
6✔
1872
    }
6✔
1873

1874
    fn aliases() -> Vec<(&'a str, utoipa::openapi::Schema)> {
6✔
1875
        let utoipa::openapi::RefOr::T(unpacked_schema) = Self::schema().1 else {
6✔
1876
            unreachable!()
×
1877
        };
1878
        vec![
6✔
1879
            ("GdalConfigOption", unpacked_schema.clone()),
6✔
1880
            ("AxisLabels", unpacked_schema),
6✔
1881
        ]
6✔
1882
    }
6✔
1883
}
1884

1885
impl From<(String, String)> for StringPair {
1886
    fn from(value: (String, String)) -> Self {
26✔
1887
        Self(value)
26✔
1888
    }
26✔
1889
}
1890

1891
impl From<StringPair> for (String, String) {
1892
    fn from(value: StringPair) -> Self {
×
1893
        value.0
×
1894
    }
×
1895
}
1896

1897
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
2✔
1898
pub enum PlotOutputFormat {
1899
    JsonPlain,
1900
    JsonVega,
1901
    ImagePng,
1902
}
1903

1904
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, PartialOrd, Deserialize, ToSchema)]
5✔
1905
pub struct CacheTtlSeconds(u32);
1906

1907
const MAX_CACHE_TTL_SECONDS: u32 = 31_536_000; // 1 year
1908

1909
impl CacheTtlSeconds {
1910
    pub fn new(seconds: u32) -> Self {
×
1911
        Self(seconds.min(MAX_CACHE_TTL_SECONDS))
×
1912
    }
×
1913

1914
    pub fn max() -> Self {
×
1915
        Self(MAX_CACHE_TTL_SECONDS)
×
1916
    }
×
1917

1918
    pub fn seconds(self) -> u32 {
×
1919
        self.0
×
1920
    }
×
1921
}
1922

1923
impl From<geoengine_datatypes::primitives::CacheTtlSeconds> for CacheTtlSeconds {
1924
    fn from(value: geoengine_datatypes::primitives::CacheTtlSeconds) -> Self {
5✔
1925
        Self(value.seconds())
5✔
1926
    }
5✔
1927
}
1928

1929
impl From<CacheTtlSeconds> for geoengine_datatypes::primitives::CacheTtlSeconds {
1930
    fn from(value: CacheTtlSeconds) -> Self {
9✔
1931
        Self::new(value.0)
9✔
1932
    }
9✔
1933
}
1934

1935
impl ToSql for CacheTtlSeconds {
1936
    fn to_sql(
×
1937
        &self,
×
1938
        ty: &postgres_types::Type,
×
1939
        w: &mut bytes::BytesMut,
×
1940
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
1941
        <i32 as ToSql>::to_sql(&(self.0 as i32), ty, w)
×
1942
    }
×
1943

1944
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1945
        <i32 as ToSql>::accepts(ty)
×
1946
    }
×
1947

1948
    postgres_types::to_sql_checked!();
1949
}
1950

1951
impl<'a> FromSql<'a> for CacheTtlSeconds {
1952
    fn from_sql(
×
1953
        ty: &postgres_types::Type,
×
1954
        raw: &'a [u8],
×
1955
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
1956
        Ok(Self(<i32 as FromSql>::from_sql(ty, raw)? as u32))
×
1957
    }
×
1958

1959
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1960
        <i32 as FromSql>::accepts(ty)
×
1961
    }
×
1962
}
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