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

geo-engine / geoengine / 10178074589

31 Jul 2024 09:34AM UTC coverage: 91.068% (+0.4%) from 90.682%
10178074589

push

github

web-flow
Merge pull request #973 from geo-engine/remove-XGB-update-toolchain

Remove-XGB-update-toolchain

81 of 88 new or added lines in 29 files covered. (92.05%)

456 existing lines in 119 files now uncovered.

131088 of 143945 relevant lines covered (91.07%)

53581.03 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
    Internal {
41
        dataset_id: DatasetId,
42
    },
43
    External(ExternalDataId),
44
}
45

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1008
impl TryFrom<BandSelection> for geoengine_datatypes::primitives::BandSelection {
1009
    type Error = Error;
1010

1011
    fn try_from(value: BandSelection) -> Result<Self> {
×
1012
        geoengine_datatypes::primitives::BandSelection::new(
×
1013
            value.0.into_iter().map(|b| b as u32).collect(),
×
1014
        )
×
1015
        .context(error::DataType)
×
1016
    }
×
1017
}
1018

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

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

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

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

1051
impl FromStr for TimeInstance {
1052
    type Err = geoengine_datatypes::primitives::DateTimeError;
1053

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

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

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

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

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

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

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

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

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

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

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

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

18✔
1131
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
18✔
1132
    }
18✔
1133
}
1134

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1401
    postgres_types::to_sql_checked!();
1402
}
1403

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

1411
        Ok(NotNanF64(value.try_into()?))
×
1412
    }
×
1413

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

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

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

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

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

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

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

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

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

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

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

1588
impl RasterColorizer {
1589
    pub fn band_selection(&self) -> BandSelection {
×
1590
        match self {
×
1591
            RasterColorizer::SingleBand { band, .. } => BandSelection(vec![*band as usize]),
×
1592
        }
×
1593
    }
×
1594
}
1595

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

1616
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1617
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
×
1618
        Self(
×
1619
            palette
×
1620
                .into_inner()
×
1621
                .into_iter()
×
1622
                .map(|(value, color)| (value, color.into()))
×
1623
                .collect(),
×
1624
        )
×
1625
    }
×
1626
}
1627

1628
impl From<Palette> for geoengine_datatypes::operations::image::Palette {
1629
    fn from(palette: Palette) -> Self {
×
1630
        Self::new(
×
1631
            palette
×
1632
                .0
×
1633
                .into_iter()
×
1634
                .map(|(value, color)| (value, color.into()))
×
1635
                .collect(),
×
1636
        )
×
1637
    }
×
1638
}
1639

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

1645
impl From<Palette> for SerializablePalette {
1646
    fn from(palette: Palette) -> Self {
×
1647
        Self(
×
1648
            palette
×
1649
                .0
×
1650
                .into_iter()
×
1651
                .map(|(k, v)| (k.to_string(), v))
×
1652
                .collect(),
×
1653
        )
×
1654
    }
×
1655
}
1656

1657
impl TryFrom<SerializablePalette> for Palette {
1658
    type Error = <NotNan<f64> as FromStr>::Err;
1659

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

1669
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
2✔
1670
pub struct RasterPropertiesKey {
1671
    pub domain: Option<String>,
1672
    pub key: String,
1673
}
1674

1675
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1676
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1677
        Self {
×
1678
            domain: value.domain,
×
1679
            key: value.key,
×
1680
        }
×
1681
    }
×
1682
}
1683

1684
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1685
    fn from(value: RasterPropertiesKey) -> Self {
×
1686
        Self {
×
1687
            domain: value.domain,
×
1688
            key: value.key,
×
1689
        }
×
1690
    }
×
1691
}
1692

1693
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
2✔
1694
pub enum RasterPropertiesEntryType {
1695
    Number,
1696
    String,
1697
}
1698

1699
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1700
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1701
        match value {
×
1702
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1703
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1704
        }
1705
    }
×
1706
}
1707

1708
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1709
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1710
        match value {
×
1711
            RasterPropertiesEntryType::Number => Self::Number,
×
1712
            RasterPropertiesEntryType::String => Self::String,
×
1713
        }
1714
    }
×
1715
}
1716

1717
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, ToSchema)]
16✔
1718
pub struct DateTimeParseFormat {
1719
    fmt: String,
1720
    has_tz: bool,
1721
    has_time: bool,
1722
}
1723

1724
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1725
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
4✔
1726
        Self {
4✔
1727
            fmt: value._to_parse_format().to_string(),
4✔
1728
            has_tz: value.has_tz(),
4✔
1729
            has_time: value.has_time(),
4✔
1730
        }
4✔
1731
    }
4✔
1732
}
1733

1734
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1735
    fn from(value: DateTimeParseFormat) -> Self {
4✔
1736
        Self::custom(value.fmt)
4✔
1737
    }
4✔
1738
}
1739

1740
impl DateTimeParseFormat {
1741
    // this is used as default value
1742
    pub fn unix() -> Self {
×
1743
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1744
    }
×
1745
}
1746

1747
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1748
pub struct NoGeometry;
1749

1750
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
1751
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
1752
        Self {}
×
1753
    }
×
1754
}
1755

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

1762
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1763
pub struct MultiPoint {
1764
    pub coordinates: Vec<Coordinate2D>,
1765
}
1766

1767
impl From<geoengine_datatypes::primitives::MultiPoint> for MultiPoint {
1768
    fn from(value: geoengine_datatypes::primitives::MultiPoint) -> Self {
×
1769
        Self {
×
1770
            coordinates: value.points().iter().map(|x| (*x).into()).collect(),
×
1771
        }
×
1772
    }
×
1773
}
1774

1775
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
1776
    fn from(value: MultiPoint) -> Self {
×
1777
        Self::new(value.coordinates.into_iter().map(Into::into).collect())
×
1778
            .expect("it should always be able to convert it")
×
1779
    }
×
1780
}
1781

1782
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1783
pub struct MultiLineString {
1784
    pub coordinates: Vec<Vec<Coordinate2D>>,
1785
}
1786

1787
impl From<geoengine_datatypes::primitives::MultiLineString> for MultiLineString {
1788
    fn from(value: geoengine_datatypes::primitives::MultiLineString) -> Self {
×
1789
        Self {
×
1790
            coordinates: value
×
1791
                .lines()
×
1792
                .iter()
×
1793
                .map(|x| x.iter().map(|x| (*x).into()).collect())
×
1794
                .collect(),
×
1795
        }
×
1796
    }
×
1797
}
1798

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

1812
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1813
pub struct MultiPolygon {
1814
    pub polygons: Vec<Vec<Vec<Coordinate2D>>>,
1815
}
1816

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

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

1850
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
1✔
1851
pub struct StringPair((String, String));
1852

1853
pub type GdalConfigOption = StringPair;
1854
pub type AxisLabels = StringPair;
1855

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

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

1880
impl From<(String, String)> for StringPair {
1881
    fn from(value: (String, String)) -> Self {
26✔
1882
        Self(value)
26✔
1883
    }
26✔
1884
}
1885

1886
impl From<StringPair> for (String, String) {
1887
    fn from(value: StringPair) -> Self {
×
1888
        value.0
×
1889
    }
×
1890
}
1891

1892
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
2✔
1893
pub enum PlotOutputFormat {
1894
    JsonPlain,
1895
    JsonVega,
1896
    ImagePng,
1897
}
1898

1899
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, PartialOrd, Deserialize, ToSchema)]
5✔
1900
pub struct CacheTtlSeconds(u32);
1901

1902
const MAX_CACHE_TTL_SECONDS: u32 = 31_536_000; // 1 year
1903

1904
impl CacheTtlSeconds {
1905
    pub fn new(seconds: u32) -> Self {
×
1906
        Self(seconds.min(MAX_CACHE_TTL_SECONDS))
×
1907
    }
×
1908

1909
    pub fn max() -> Self {
×
1910
        Self(MAX_CACHE_TTL_SECONDS)
×
1911
    }
×
1912

1913
    pub fn seconds(self) -> u32 {
×
1914
        self.0
×
1915
    }
×
1916
}
1917

1918
impl From<geoengine_datatypes::primitives::CacheTtlSeconds> for CacheTtlSeconds {
1919
    fn from(value: geoengine_datatypes::primitives::CacheTtlSeconds) -> Self {
5✔
1920
        Self(value.seconds())
5✔
1921
    }
5✔
1922
}
1923

1924
impl From<CacheTtlSeconds> for geoengine_datatypes::primitives::CacheTtlSeconds {
1925
    fn from(value: CacheTtlSeconds) -> Self {
9✔
1926
        Self::new(value.0)
9✔
1927
    }
9✔
1928
}
1929

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

1939
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1940
        <i32 as ToSql>::accepts(ty)
×
1941
    }
×
1942

1943
    postgres_types::to_sql_checked!();
1944
}
1945

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

1954
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1955
        <i32 as FromSql>::accepts(ty)
×
1956
    }
×
1957
}
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