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

geo-engine / geoengine / 7141787690

08 Dec 2023 01:12PM UTC coverage: 89.679% (+0.01%) from 89.665%
7141787690

push

github

web-flow
Merge pull request #895 from geo-engine/raster_colorizer_bands

add raster colorizer for bands

722 of 783 new or added lines in 33 files covered. (92.21%)

8 existing lines in 5 files now uncovered.

113154 of 126176 relevant lines covered (89.68%)

59848.81 hits per line

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

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

18
identifier!(DataProviderId);
×
19

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

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

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

35
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize, ToSchema)]
2✔
36
#[serde(rename_all = "camelCase", tag = "type")]
37
/// The identifier for loadable data. It is used in the source operators to get the loading info (aka parametrization)
38
/// for accessing the data. Internal data is loaded from datasets, external from `DataProvider`s.
39
pub enum DataId {
40
    #[serde(rename_all = "camelCase")]
41
    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,
24✔
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)]
24✔
354
pub struct SpatialReference {
355
    authority: SpatialReferenceAuthority,
356
    code: u32,
357
}
358

359
impl SpatialReference {
360
    pub fn proj_string(self) -> Result<String> {
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 {
8✔
498
        match value {
8✔
499
            SpatialReferenceOption::SpatialReference(sr) => Self::SpatialReference(sr.into()),
8✔
500
            SpatialReferenceOption::Unreferenced => Self::Unreferenced,
×
501
        }
502
    }
8✔
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>
10✔
540
    where
10✔
541
        E: serde::de::Error,
10✔
542
    {
10✔
543
        if v.is_empty() {
10✔
544
            return Ok(SpatialReferenceOption::Unreferenced);
×
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
    }
10✔
551
}
552

553
impl<'de> Deserialize<'de> for SpatialReferenceOption {
554
    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
10✔
555
    where
10✔
556
        D: Deserializer<'de>,
10✔
557
    {
10✔
558
        deserializer.deserialize_str(SpatialReferenceOptionDeserializeVisitor)
10✔
559
    }
10✔
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,
24✔
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 {
8✔
661
        match value {
8✔
662
            VectorDataType::Data => Self::Data,
×
663
            VectorDataType::MultiPoint => Self::MultiPoint,
8✔
664
            VectorDataType::MultiLineString => Self::MultiLineString,
×
665
            VectorDataType::MultiPolygon => Self::MultiPolygon,
×
666
        }
667
    }
8✔
668
}
669

670
#[derive(
671
    Clone,
×
672
    Copy,
673
    Debug,
×
674
    Deserialize,
80✔
675
    PartialEq,
8✔
676
    PartialOrd,
×
677
    Serialize,
17✔
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)]
5✔
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)]
58✔
801
#[serde(rename_all = "camelCase", tag = "type")]
802
pub enum Measurement {
803
    Unitless,
804
    Continuous(ContinuousMeasurement),
805
    Classification(ClassificationMeasurement),
806
}
807

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1000
/// The spatial resolution in SRS units
1001
#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, ToSchema)]
25✔
1002
pub struct SpatialResolution {
1003
    pub x: f64,
1004
    pub y: f64,
1005
}
1006

1007
impl From<geoengine_datatypes::primitives::SpatialResolution> for SpatialResolution {
1008
    fn from(value: geoengine_datatypes::primitives::SpatialResolution) -> Self {
6✔
1009
        Self {
6✔
1010
            x: value.x,
6✔
1011
            y: value.y,
6✔
1012
        }
6✔
1013
    }
6✔
1014
}
1015

1016
impl From<SpatialResolution> for geoengine_datatypes::primitives::SpatialResolution {
1017
    fn from(value: SpatialResolution) -> Self {
10✔
1018
        Self {
10✔
1019
            x: value.x,
10✔
1020
            y: value.y,
10✔
1021
        }
10✔
1022
    }
10✔
1023
}
1024

1025
#[derive(
1026
    Clone, Copy, Serialize, PartialEq, Eq, PartialOrd, Ord, Debug, ToSchema, FromSql, ToSql,
32✔
1027
)]
1028
#[repr(C)]
1029
#[postgres(transparent)]
1030
pub struct TimeInstance(i64);
1031

1032
impl FromStr for TimeInstance {
1033
    type Err = geoengine_datatypes::primitives::DateTimeError;
1034

1035
    fn from_str(s: &str) -> Result<Self, Self::Err> {
×
1036
        let date_time = DateTime::from_str(s)?;
×
1037
        Ok(date_time.into())
×
1038
    }
×
1039
}
1040

1041
impl From<geoengine_datatypes::primitives::TimeInstance> for TimeInstance {
1042
    fn from(value: geoengine_datatypes::primitives::TimeInstance) -> Self {
142✔
1043
        Self(value.inner())
142✔
1044
    }
142✔
1045
}
1046

1047
impl From<TimeInstance> for geoengine_datatypes::primitives::TimeInstance {
1048
    fn from(value: TimeInstance) -> Self {
74✔
1049
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(value.inner())
74✔
1050
    }
74✔
1051
}
1052

1053
impl From<DateTime> for TimeInstance {
1054
    fn from(datetime: DateTime) -> Self {
×
1055
        Self::from(&datetime)
×
1056
    }
×
1057
}
1058

1059
impl From<&DateTime> for TimeInstance {
1060
    fn from(datetime: &DateTime) -> Self {
×
1061
        geoengine_datatypes::primitives::TimeInstance::from_millis_unchecked(
×
1062
            datetime.datetime.timestamp_millis(),
×
1063
        )
×
1064
        .into()
×
1065
    }
×
1066
}
1067

1068
impl TimeInstance {
1069
    pub const fn inner(self) -> i64 {
74✔
1070
        self.0
74✔
1071
    }
74✔
1072
}
1073

1074
impl<'de> Deserialize<'de> for TimeInstance {
1075
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
18✔
1076
    where
18✔
1077
        D: serde::Deserializer<'de>,
18✔
1078
    {
18✔
1079
        struct IsoStringOrUnixTimestamp;
18✔
1080

18✔
1081
        impl<'de> serde::de::Visitor<'de> for IsoStringOrUnixTimestamp {
18✔
1082
            type Value = TimeInstance;
18✔
1083

18✔
1084
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
18✔
1085
                formatter.write_str("RFC 3339 timestamp string or Unix timestamp integer")
×
1086
            }
×
1087

18✔
1088
            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
18✔
1089
            where
×
1090
                E: serde::de::Error,
×
1091
            {
×
1092
                TimeInstance::from_str(value).map_err(E::custom)
×
1093
            }
×
1094

18✔
1095
            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
18✔
1096
            where
18✔
1097
                E: serde::de::Error,
18✔
1098
            {
18✔
1099
                geoengine_datatypes::primitives::TimeInstance::from_millis(v)
18✔
1100
                    .map(Into::into)
18✔
1101
                    .map_err(E::custom)
18✔
1102
            }
18✔
1103

18✔
1104
            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
18✔
1105
            where
18✔
1106
                E: serde::de::Error,
18✔
1107
            {
18✔
1108
                Self::visit_i64(self, v as i64)
18✔
1109
            }
18✔
1110
        }
18✔
1111

18✔
1112
        deserializer.deserialize_any(IsoStringOrUnixTimestamp)
18✔
1113
    }
18✔
1114
}
1115

1116
/// A time granularity.
1117
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, FromSql, ToSql)]
8✔
1118
#[serde(rename_all = "camelCase")]
1119
pub enum TimeGranularity {
1120
    Millis,
1121
    Seconds,
1122
    Minutes,
1123
    Hours,
1124
    Days,
1125
    Months,
1126
    Years,
1127
}
1128

1129
impl From<geoengine_datatypes::primitives::TimeGranularity> for TimeGranularity {
1130
    fn from(value: geoengine_datatypes::primitives::TimeGranularity) -> Self {
4✔
1131
        match value {
4✔
1132
            geoengine_datatypes::primitives::TimeGranularity::Millis => Self::Millis,
×
1133
            geoengine_datatypes::primitives::TimeGranularity::Seconds => Self::Seconds,
×
1134
            geoengine_datatypes::primitives::TimeGranularity::Minutes => Self::Minutes,
×
1135
            geoengine_datatypes::primitives::TimeGranularity::Hours => Self::Hours,
×
1136
            geoengine_datatypes::primitives::TimeGranularity::Days => Self::Days,
×
1137
            geoengine_datatypes::primitives::TimeGranularity::Months => Self::Months,
4✔
1138
            geoengine_datatypes::primitives::TimeGranularity::Years => Self::Years,
×
1139
        }
1140
    }
4✔
1141
}
1142

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

1157
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema, FromSql, ToSql)]
20✔
1158
pub struct TimeStep {
1159
    pub granularity: TimeGranularity,
1160
    pub step: u32, // TODO: ensure on deserialization it is > 0
1161
}
1162

1163
impl From<geoengine_datatypes::primitives::TimeStep> for TimeStep {
1164
    fn from(value: geoengine_datatypes::primitives::TimeStep) -> Self {
4✔
1165
        Self {
4✔
1166
            granularity: value.granularity.into(),
4✔
1167
            step: value.step,
4✔
1168
        }
4✔
1169
    }
4✔
1170
}
1171

1172
impl From<TimeStep> for geoengine_datatypes::primitives::TimeStep {
1173
    fn from(value: TimeStep) -> Self {
4✔
1174
        Self {
4✔
1175
            granularity: value.granularity.into(),
4✔
1176
            step: value.step,
4✔
1177
        }
4✔
1178
    }
4✔
1179
}
1180

1181
/// Stores time intervals in ms in close-open semantic [start, end)
1182
#[derive(Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ToSql, FromSql, ToSchema)]
45✔
1183
pub struct TimeInterval {
1184
    start: TimeInstance,
1185
    end: TimeInstance,
1186
}
1187

1188
impl From<TimeInterval> for geoengine_datatypes::primitives::TimeInterval {
1189
    fn from(value: TimeInterval) -> Self {
37✔
1190
        geoengine_datatypes::primitives::TimeInterval::new_unchecked::<
37✔
1191
            geoengine_datatypes::primitives::TimeInstance,
37✔
1192
            geoengine_datatypes::primitives::TimeInstance,
37✔
1193
        >(value.start.into(), value.end.into())
37✔
1194
    }
37✔
1195
}
1196

1197
impl From<geoengine_datatypes::primitives::TimeInterval> for TimeInterval {
1198
    fn from(value: geoengine_datatypes::primitives::TimeInterval) -> Self {
62✔
1199
        Self {
62✔
1200
            start: value.start().into(),
62✔
1201
            end: value.end().into(),
62✔
1202
        }
62✔
1203
    }
62✔
1204
}
1205

1206
impl core::fmt::Debug for TimeInterval {
1207
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
×
1208
        write!(
×
1209
            f,
×
1210
            "TimeInterval [{}, {})",
×
1211
            self.start.inner(),
×
1212
            &self.end.inner()
×
1213
        )
×
1214
    }
×
1215
}
1216

1217
#[derive(
1218
    Debug,
×
1219
    Ord,
×
1220
    PartialOrd,
×
1221
    Eq,
1222
    PartialEq,
×
1223
    Hash,
×
1224
    Deserialize,
8✔
1225
    Serialize,
5✔
1226
    Copy,
1227
    Clone,
×
1228
    ToSchema,
2✔
1229
    FromSql,
×
1230
    ToSql,
×
1231
)]
1232
pub enum RasterDataType {
1233
    U8,
1234
    U16,
1235
    U32,
1236
    U64,
1237
    I8,
1238
    I16,
1239
    I32,
1240
    I64,
1241
    F32,
1242
    F64,
1243
}
1244

1245
impl From<geoengine_datatypes::raster::RasterDataType> for RasterDataType {
1246
    fn from(value: geoengine_datatypes::raster::RasterDataType) -> Self {
4✔
1247
        match value {
4✔
1248
            geoengine_datatypes::raster::RasterDataType::U8 => Self::U8,
4✔
1249
            geoengine_datatypes::raster::RasterDataType::U16 => Self::U16,
×
1250
            geoengine_datatypes::raster::RasterDataType::U32 => Self::U32,
×
1251
            geoengine_datatypes::raster::RasterDataType::U64 => Self::U64,
×
1252
            geoengine_datatypes::raster::RasterDataType::I8 => Self::I8,
×
1253
            geoengine_datatypes::raster::RasterDataType::I16 => Self::I16,
×
1254
            geoengine_datatypes::raster::RasterDataType::I32 => Self::I32,
×
1255
            geoengine_datatypes::raster::RasterDataType::I64 => Self::I64,
×
1256
            geoengine_datatypes::raster::RasterDataType::F32 => Self::F32,
×
1257
            geoengine_datatypes::raster::RasterDataType::F64 => Self::F64,
×
1258
        }
1259
    }
4✔
1260
}
1261

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

1279
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
×
1280
#[serde(rename_all = "UPPERCASE")]
1281
pub enum ResamplingMethod {
1282
    Nearest,
1283
    Average,
1284
    Bilinear,
1285
    Cubic,
1286
    CubicSpline,
1287
    Lanczos,
1288
}
1289

1290
impl std::fmt::Display for ResamplingMethod {
1291
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
×
1292
        match self {
×
1293
            ResamplingMethod::Nearest => write!(f, "NEAREST"),
×
1294
            ResamplingMethod::Average => write!(f, "AVERAGE"),
×
1295
            ResamplingMethod::Bilinear => write!(f, "BILINEAR"),
×
1296
            ResamplingMethod::Cubic => write!(f, "CUBIC"),
×
1297
            ResamplingMethod::CubicSpline => write!(f, "CUBICSPLINE"),
×
1298
            ResamplingMethod::Lanczos => write!(f, "LANCZOS"),
×
1299
        }
1300
    }
×
1301
}
1302

1303
impl From<ResamplingMethod> for geoengine_datatypes::util::gdal::ResamplingMethod {
1304
    fn from(value: ResamplingMethod) -> Self {
×
1305
        match value {
×
1306
            ResamplingMethod::Nearest => Self::Nearest,
×
1307
            ResamplingMethod::Average => Self::Average,
×
1308
            ResamplingMethod::Bilinear => Self::Bilinear,
×
1309
            ResamplingMethod::Cubic => Self::Cubic,
×
1310
            ResamplingMethod::CubicSpline => Self::CubicSpline,
×
1311
            ResamplingMethod::Lanczos => Self::Lanczos,
×
1312
        }
1313
    }
×
1314
}
1315

1316
/// `RgbaColor` defines a 32 bit RGB color with alpha value
1317
#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
25✔
1318
pub struct RgbaColor(pub [u8; 4]);
1319

1320
// manual implementation utoipa generates an integer field
1321
impl<'a> ToSchema<'a> for RgbaColor {
1322
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1323
        use utoipa::openapi::*;
2✔
1324
        (
2✔
1325
            "RgbaColor",
2✔
1326
            ArrayBuilder::new()
2✔
1327
                .items(ObjectBuilder::new().schema_type(SchemaType::Integer))
2✔
1328
                .min_items(Some(4))
2✔
1329
                .max_items(Some(4))
2✔
1330
                .into(),
2✔
1331
        )
2✔
1332
    }
2✔
1333
}
1334

1335
impl From<geoengine_datatypes::operations::image::RgbaColor> for RgbaColor {
1336
    fn from(color: geoengine_datatypes::operations::image::RgbaColor) -> Self {
25✔
1337
        Self(color.into_inner())
25✔
1338
    }
25✔
1339
}
1340

1341
impl From<RgbaColor> for geoengine_datatypes::operations::image::RgbaColor {
1342
    fn from(color: RgbaColor) -> Self {
20✔
1343
        Self::new(color.0[0], color.0[1], color.0[2], color.0[3])
20✔
1344
    }
20✔
1345
}
1346

1347
/// A container type for breakpoints that specify a value to color mapping
1348
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
40✔
1349
pub struct Breakpoint {
1350
    pub value: NotNanF64,
1351
    pub color: RgbaColor,
1352
}
1353

1354
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
10✔
1355
pub struct NotNanF64(NotNan<f64>);
1356

1357
impl From<NotNan<f64>> for NotNanF64 {
1358
    fn from(value: NotNan<f64>) -> Self {
10✔
1359
        Self(value)
10✔
1360
    }
10✔
1361
}
1362

1363
impl From<NotNanF64> for NotNan<f64> {
1364
    fn from(value: NotNanF64) -> Self {
8✔
1365
        value.0
8✔
1366
    }
8✔
1367
}
1368

1369
impl ToSql for NotNanF64 {
1370
    fn to_sql(
×
1371
        &self,
×
1372
        ty: &postgres_types::Type,
×
1373
        w: &mut bytes::BytesMut,
×
1374
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
1375
        <f64 as ToSql>::to_sql(&self.0.into_inner(), ty, w)
×
1376
    }
×
1377

1378
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1379
        <f64 as ToSql>::accepts(ty)
×
1380
    }
×
1381

1382
    postgres_types::to_sql_checked!();
1383
}
1384

1385
impl<'a> FromSql<'a> for NotNanF64 {
1386
    fn from_sql(
×
1387
        ty: &postgres_types::Type,
×
1388
        raw: &'a [u8],
×
1389
    ) -> Result<NotNanF64, Box<dyn std::error::Error + Sync + Send>> {
×
1390
        let value = <f64 as FromSql>::from_sql(ty, raw)?;
×
1391

1392
        Ok(NotNanF64(value.try_into()?))
×
1393
    }
×
1394

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

1400
// manual implementation because of NotNan
1401
impl<'a> ToSchema<'a> for Breakpoint {
1402
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
2✔
1403
        use utoipa::openapi::*;
2✔
1404
        (
2✔
1405
            "Breakpoint",
2✔
1406
            ObjectBuilder::new()
2✔
1407
                .property("value", Object::with_type(SchemaType::Number))
2✔
1408
                .property("color", Ref::from_schema_name("RgbaColor"))
2✔
1409
                .into(),
2✔
1410
        )
2✔
1411
    }
2✔
1412
}
1413

1414
impl From<geoengine_datatypes::operations::image::Breakpoint> for Breakpoint {
1415
    fn from(breakpoint: geoengine_datatypes::operations::image::Breakpoint) -> Self {
10✔
1416
        Self {
10✔
1417
            value: breakpoint.value.into(),
10✔
1418
            color: breakpoint.color.into(),
10✔
1419
        }
10✔
1420
    }
10✔
1421
}
1422

1423
impl From<Breakpoint> for geoengine_datatypes::operations::image::Breakpoint {
1424
    fn from(breakpoint: Breakpoint) -> Self {
8✔
1425
        Self {
8✔
1426
            value: breakpoint.value.into(),
8✔
1427
            color: breakpoint.color.into(),
8✔
1428
        }
8✔
1429
    }
8✔
1430
}
1431

1432
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
36✔
1433
#[serde(rename_all = "camelCase")]
1434
pub struct LinearGradient {
1435
    pub breakpoints: Vec<Breakpoint>,
1436
    pub no_data_color: RgbaColor,
1437
    pub over_color: RgbaColor,
1438
    pub under_color: RgbaColor,
1439
}
1440

1441
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
2✔
1442
#[serde(rename_all = "camelCase")]
1443
pub struct LogarithmicGradient {
1444
    pub breakpoints: Vec<Breakpoint>,
1445
    pub no_data_color: RgbaColor,
1446
    pub over_color: RgbaColor,
1447
    pub under_color: RgbaColor,
1448
}
1449

1450
/// A colorizer specifies a mapping between raster values and an output image
1451
/// There are different variants that perform different kinds of mapping.
1452
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
8✔
1453
#[serde(rename_all = "camelCase", tag = "type")]
1454
pub enum Colorizer {
1455
    #[serde(rename_all = "camelCase")]
1456
    LinearGradient(LinearGradient),
1457
    #[serde(rename_all = "camelCase")]
1458
    LogarithmicGradient(LogarithmicGradient),
1459
    #[serde(rename_all = "camelCase")]
1460
    Palette {
1461
        colors: Palette,
1462
        no_data_color: RgbaColor,
1463
        default_color: RgbaColor,
1464
    },
1465
    Rgba,
1466
}
1467

1468
impl From<geoengine_datatypes::operations::image::Colorizer> for Colorizer {
1469
    fn from(v: geoengine_datatypes::operations::image::Colorizer) -> Self {
5✔
1470
        match v {
5✔
1471
            geoengine_datatypes::operations::image::Colorizer::LinearGradient {
1472
                breakpoints,
5✔
1473
                no_data_color,
5✔
1474
                over_color,
5✔
1475
                under_color,
5✔
1476
            } => Self::LinearGradient(LinearGradient {
5✔
1477
                breakpoints: breakpoints
5✔
1478
                    .into_iter()
5✔
1479
                    .map(Into::into)
5✔
1480
                    .collect::<Vec<Breakpoint>>(),
5✔
1481
                no_data_color: no_data_color.into(),
5✔
1482
                over_color: over_color.into(),
5✔
1483
                under_color: under_color.into(),
5✔
1484
            }),
5✔
1485
            geoengine_datatypes::operations::image::Colorizer::LogarithmicGradient {
1486
                breakpoints,
×
1487
                no_data_color,
×
1488
                over_color,
×
1489
                under_color,
×
1490
            } => Self::LogarithmicGradient(LogarithmicGradient {
×
1491
                breakpoints: breakpoints
×
1492
                    .into_iter()
×
1493
                    .map(Into::into)
×
1494
                    .collect::<Vec<Breakpoint>>(),
×
1495
                no_data_color: no_data_color.into(),
×
1496
                over_color: over_color.into(),
×
1497
                under_color: under_color.into(),
×
1498
            }),
×
1499
            geoengine_datatypes::operations::image::Colorizer::Palette {
1500
                colors,
×
1501
                no_data_color,
×
1502
                default_color,
×
1503
            } => Self::Palette {
×
1504
                colors: colors.into(),
×
1505
                no_data_color: no_data_color.into(),
×
1506
                default_color: default_color.into(),
×
1507
            },
×
1508
            geoengine_datatypes::operations::image::Colorizer::Rgba => Self::Rgba,
×
1509
        }
1510
    }
5✔
1511
}
1512

1513
impl From<Colorizer> for geoengine_datatypes::operations::image::Colorizer {
1514
    fn from(v: Colorizer) -> Self {
4✔
1515
        match v {
4✔
1516
            Colorizer::LinearGradient(linear_gradient) => Self::LinearGradient {
4✔
1517
                breakpoints: linear_gradient
4✔
1518
                    .breakpoints
4✔
1519
                    .into_iter()
4✔
1520
                    .map(Into::into)
4✔
1521
                    .collect::<Vec<geoengine_datatypes::operations::image::Breakpoint>>(),
4✔
1522
                no_data_color: linear_gradient.no_data_color.into(),
4✔
1523
                over_color: linear_gradient.over_color.into(),
4✔
1524
                under_color: linear_gradient.under_color.into(),
4✔
1525
            },
4✔
NEW
1526
            Colorizer::LogarithmicGradient(logarithmic_gradient) => Self::LogarithmicGradient {
×
NEW
1527
                breakpoints: logarithmic_gradient
×
NEW
1528
                    .breakpoints
×
NEW
1529
                    .into_iter()
×
NEW
1530
                    .map(Into::into)
×
NEW
1531
                    .collect::<Vec<geoengine_datatypes::operations::image::Breakpoint>>(),
×
NEW
1532
                no_data_color: logarithmic_gradient.no_data_color.into(),
×
NEW
1533
                over_color: logarithmic_gradient.over_color.into(),
×
NEW
1534
                under_color: logarithmic_gradient.under_color.into(),
×
NEW
1535
            },
×
1536

1537
            Colorizer::Palette {
NEW
1538
                colors,
×
NEW
1539
                no_data_color,
×
NEW
1540
                default_color,
×
NEW
1541
            } => Self::Palette {
×
NEW
1542
                colors: colors.into(),
×
NEW
1543
                no_data_color: no_data_color.into(),
×
NEW
1544
                default_color: default_color.into(),
×
NEW
1545
            },
×
NEW
1546
            Colorizer::Rgba => Self::Rgba,
×
1547
        }
1548
    }
4✔
1549
}
1550

1551
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, ToSchema)]
28✔
1552
#[serde(rename_all = "camelCase", tag = "type")]
1553
pub enum RasterColorizer {
1554
    #[serde(rename_all = "camelCase")]
1555
    SingleBand {
1556
        band: u32,
1557
        band_colorizer: Colorizer,
1558
    },
1559
    // TODO: multiband colorizer, e.g.
1560
    // MultiBand {
1561
    //     red: ...,
1562
    //     green: ...,
1563
    //     blue: ..,
1564
    // },
1565
}
1566

1567
impl RasterColorizer {
NEW
1568
    pub fn band_selection(&self) -> BandSelection {
×
NEW
1569
        match self {
×
NEW
1570
            RasterColorizer::SingleBand { band, .. } => BandSelection::new_single(*band),
×
NEW
1571
        }
×
NEW
1572
    }
×
1573
}
1574

1575
impl From<geoengine_datatypes::operations::image::RasterColorizer> for RasterColorizer {
NEW
1576
    fn from(v: geoengine_datatypes::operations::image::RasterColorizer) -> Self {
×
NEW
1577
        match v {
×
NEW
1578
            geoengine_datatypes::operations::image::RasterColorizer::SingleBand {
×
NEW
1579
                band,
×
NEW
1580
                band_colorizer: colorizer,
×
NEW
1581
            } => Self::SingleBand {
×
NEW
1582
                band,
×
NEW
1583
                band_colorizer: colorizer.into(),
×
NEW
1584
            },
×
NEW
1585
        }
×
NEW
1586
    }
×
1587
}
1588
/// A map from value to color
1589
///
1590
/// It is assumed that is has at least one and at most 256 entries.
1591
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1592
#[serde(try_from = "SerializablePalette", into = "SerializablePalette")]
1593
pub struct Palette(pub HashMap<NotNan<f64>, RgbaColor>);
1594

1595
impl From<geoengine_datatypes::operations::image::Palette> for Palette {
1596
    fn from(palette: geoengine_datatypes::operations::image::Palette) -> Self {
×
1597
        Self(
×
1598
            palette
×
1599
                .into_inner()
×
1600
                .into_iter()
×
1601
                .map(|(value, color)| (value, color.into()))
×
1602
                .collect(),
×
1603
        )
×
1604
    }
×
1605
}
1606

1607
impl From<Palette> for geoengine_datatypes::operations::image::Palette {
NEW
1608
    fn from(palette: Palette) -> Self {
×
NEW
1609
        Self::new(
×
NEW
1610
            palette
×
NEW
1611
                .0
×
NEW
1612
                .into_iter()
×
NEW
1613
                .map(|(value, color)| (value, color.into()))
×
NEW
1614
                .collect(),
×
NEW
1615
        )
×
NEW
1616
    }
×
1617
}
1618

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

1624
impl From<Palette> for SerializablePalette {
1625
    fn from(palette: Palette) -> Self {
×
1626
        Self(
×
1627
            palette
×
1628
                .0
×
1629
                .into_iter()
×
1630
                .map(|(k, v)| (k.to_string(), v))
×
1631
                .collect(),
×
1632
        )
×
1633
    }
×
1634
}
1635

1636
impl TryFrom<SerializablePalette> for Palette {
1637
    type Error = <NotNan<f64> as FromStr>::Err;
1638

1639
    fn try_from(palette: SerializablePalette) -> Result<Self, Self::Error> {
×
1640
        let mut inner = HashMap::<NotNan<f64>, RgbaColor>::with_capacity(palette.0.len());
×
1641
        for (k, v) in palette.0 {
×
1642
            inner.insert(k.parse()?, v);
×
1643
        }
1644
        Ok(Self(inner))
×
1645
    }
×
1646
}
1647

1648
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Hash, Eq, PartialOrd, Ord, ToSchema)]
2✔
1649
pub struct RasterPropertiesKey {
1650
    pub domain: Option<String>,
1651
    pub key: String,
1652
}
1653

1654
impl From<geoengine_datatypes::raster::RasterPropertiesKey> for RasterPropertiesKey {
1655
    fn from(value: geoengine_datatypes::raster::RasterPropertiesKey) -> Self {
×
1656
        Self {
×
1657
            domain: value.domain,
×
1658
            key: value.key,
×
1659
        }
×
1660
    }
×
1661
}
1662

1663
impl From<RasterPropertiesKey> for geoengine_datatypes::raster::RasterPropertiesKey {
1664
    fn from(value: RasterPropertiesKey) -> Self {
×
1665
        Self {
×
1666
            domain: value.domain,
×
1667
            key: value.key,
×
1668
        }
×
1669
    }
×
1670
}
1671

1672
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
2✔
1673
pub enum RasterPropertiesEntryType {
1674
    Number,
1675
    String,
1676
}
1677

1678
impl From<geoengine_datatypes::raster::RasterPropertiesEntryType> for RasterPropertiesEntryType {
1679
    fn from(value: geoengine_datatypes::raster::RasterPropertiesEntryType) -> Self {
×
1680
        match value {
×
1681
            geoengine_datatypes::raster::RasterPropertiesEntryType::Number => Self::Number,
×
1682
            geoengine_datatypes::raster::RasterPropertiesEntryType::String => Self::String,
×
1683
        }
1684
    }
×
1685
}
1686

1687
impl From<RasterPropertiesEntryType> for geoengine_datatypes::raster::RasterPropertiesEntryType {
1688
    fn from(value: RasterPropertiesEntryType) -> Self {
×
1689
        match value {
×
1690
            RasterPropertiesEntryType::Number => Self::Number,
×
1691
            RasterPropertiesEntryType::String => Self::String,
×
1692
        }
1693
    }
×
1694
}
1695

1696
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, ToSchema)]
28✔
1697
pub struct DateTimeParseFormat {
1698
    fmt: String,
1699
    has_tz: bool,
1700
    has_time: bool,
1701
}
1702

1703
impl From<geoengine_datatypes::primitives::DateTimeParseFormat> for DateTimeParseFormat {
1704
    fn from(value: geoengine_datatypes::primitives::DateTimeParseFormat) -> Self {
4✔
1705
        Self {
4✔
1706
            fmt: value._to_parse_format().to_string(),
4✔
1707
            has_tz: value.has_tz(),
4✔
1708
            has_time: value.has_time(),
4✔
1709
        }
4✔
1710
    }
4✔
1711
}
1712

1713
impl From<DateTimeParseFormat> for geoengine_datatypes::primitives::DateTimeParseFormat {
1714
    fn from(value: DateTimeParseFormat) -> Self {
4✔
1715
        Self::custom(value.fmt)
4✔
1716
    }
4✔
1717
}
1718

1719
impl DateTimeParseFormat {
1720
    // this is used as default value
1721
    pub fn unix() -> Self {
×
1722
        geoengine_datatypes::primitives::DateTimeParseFormat::unix().into()
×
1723
    }
×
1724
}
1725

1726
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, ToSchema)]
2✔
1727
pub struct NoGeometry;
1728

1729
impl From<geoengine_datatypes::primitives::NoGeometry> for NoGeometry {
1730
    fn from(_: geoengine_datatypes::primitives::NoGeometry) -> Self {
×
1731
        Self {}
×
1732
    }
×
1733
}
1734

1735
impl From<NoGeometry> for geoengine_datatypes::primitives::NoGeometry {
1736
    fn from(_: NoGeometry) -> Self {
×
1737
        Self {}
×
1738
    }
×
1739
}
1740

1741
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1742
pub struct MultiPoint {
1743
    pub coordinates: Vec<Coordinate2D>,
1744
}
1745

1746
impl From<geoengine_datatypes::primitives::MultiPoint> for MultiPoint {
1747
    fn from(value: geoengine_datatypes::primitives::MultiPoint) -> Self {
×
1748
        Self {
×
1749
            coordinates: value.points().iter().map(|x| (*x).into()).collect(),
×
1750
        }
×
1751
    }
×
1752
}
1753

1754
impl From<MultiPoint> for geoengine_datatypes::primitives::MultiPoint {
1755
    fn from(value: MultiPoint) -> Self {
×
1756
        Self::new(value.coordinates.into_iter().map(Into::into).collect()).unwrap()
×
1757
    }
×
1758
}
1759

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

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

1777
impl From<MultiLineString> for geoengine_datatypes::primitives::MultiLineString {
1778
    fn from(value: MultiLineString) -> Self {
×
1779
        Self::new(
×
1780
            value
×
1781
                .coordinates
×
1782
                .into_iter()
×
1783
                .map(|x| x.into_iter().map(Into::into).collect())
×
1784
                .collect(),
×
1785
        )
×
1786
        .unwrap()
×
1787
    }
×
1788
}
1789

1790
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
2✔
1791
pub struct MultiPolygon {
1792
    pub polygons: Vec<Vec<Vec<Coordinate2D>>>,
1793
}
1794

1795
impl From<geoengine_datatypes::primitives::MultiPolygon> for MultiPolygon {
1796
    fn from(value: geoengine_datatypes::primitives::MultiPolygon) -> Self {
×
1797
        Self {
×
1798
            polygons: value
×
1799
                .polygons()
×
1800
                .iter()
×
1801
                .map(|x| {
×
1802
                    x.iter()
×
1803
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1804
                        .collect()
×
1805
                })
×
1806
                .collect(),
×
1807
        }
×
1808
    }
×
1809
}
1810

1811
impl From<MultiPolygon> for geoengine_datatypes::primitives::MultiPolygon {
1812
    fn from(value: MultiPolygon) -> Self {
×
1813
        Self::new(
×
1814
            value
×
1815
                .polygons
×
1816
                .iter()
×
1817
                .map(|x| {
×
1818
                    x.iter()
×
1819
                        .map(|y| y.iter().map(|y| (*y).into()).collect())
×
1820
                        .collect()
×
1821
                })
×
1822
                .collect(),
×
1823
        )
×
1824
        .unwrap()
×
1825
    }
×
1826
}
1827

1828
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
4✔
1829
pub struct StringPair((String, String));
1830

1831
pub type GdalConfigOption = StringPair;
1832
pub type AxisLabels = StringPair;
1833

1834
impl<'a> ToSchema<'a> for StringPair {
1835
    fn schema() -> (&'a str, utoipa::openapi::RefOr<utoipa::openapi::Schema>) {
6✔
1836
        use utoipa::openapi::*;
6✔
1837
        (
6✔
1838
            "StringPair",
6✔
1839
            ArrayBuilder::new()
6✔
1840
                .items(Object::with_type(SchemaType::String))
6✔
1841
                .min_items(Some(2))
6✔
1842
                .max_items(Some(2))
6✔
1843
                .into(),
6✔
1844
        )
6✔
1845
    }
6✔
1846

1847
    fn aliases() -> Vec<(&'a str, utoipa::openapi::Schema)> {
6✔
1848
        let utoipa::openapi::RefOr::T(unpacked_schema) = Self::schema().1 else {
6✔
1849
            unreachable!()
×
1850
        };
1851
        vec![
6✔
1852
            ("GdalConfigOption", unpacked_schema.clone()),
6✔
1853
            ("AxisLabels", unpacked_schema),
6✔
1854
        ]
6✔
1855
    }
6✔
1856
}
1857

1858
impl From<(String, String)> for StringPair {
1859
    fn from(value: (String, String)) -> Self {
26✔
1860
        Self(value)
26✔
1861
    }
26✔
1862
}
1863

1864
impl From<StringPair> for (String, String) {
1865
    fn from(value: StringPair) -> Self {
×
1866
        value.0
×
1867
    }
×
1868
}
1869

1870
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Serialize, ToSchema)]
2✔
1871
pub enum PlotOutputFormat {
1872
    JsonPlain,
1873
    JsonVega,
1874
    ImagePng,
1875
}
1876

1877
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, PartialOrd, Deserialize, ToSchema)]
6✔
1878
pub struct CacheTtlSeconds(u32);
1879

1880
const MAX_CACHE_TTL_SECONDS: u32 = 31_536_000; // 1 year
1881

1882
impl CacheTtlSeconds {
1883
    pub fn new(seconds: u32) -> Self {
×
1884
        Self(seconds.min(MAX_CACHE_TTL_SECONDS))
×
1885
    }
×
1886

1887
    pub fn max() -> Self {
×
1888
        Self(MAX_CACHE_TTL_SECONDS)
×
1889
    }
×
1890

1891
    pub fn seconds(self) -> u32 {
×
1892
        self.0
×
1893
    }
×
1894
}
1895

1896
impl From<geoengine_datatypes::primitives::CacheTtlSeconds> for CacheTtlSeconds {
1897
    fn from(value: geoengine_datatypes::primitives::CacheTtlSeconds) -> Self {
5✔
1898
        Self(value.seconds())
5✔
1899
    }
5✔
1900
}
1901

1902
impl From<CacheTtlSeconds> for geoengine_datatypes::primitives::CacheTtlSeconds {
1903
    fn from(value: CacheTtlSeconds) -> Self {
8✔
1904
        Self::new(value.0)
8✔
1905
    }
8✔
1906
}
1907

1908
impl ToSql for CacheTtlSeconds {
1909
    fn to_sql(
×
1910
        &self,
×
1911
        ty: &postgres_types::Type,
×
1912
        w: &mut bytes::BytesMut,
×
1913
    ) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>> {
×
1914
        <i32 as ToSql>::to_sql(&(self.0 as i32), ty, w)
×
1915
    }
×
1916

1917
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1918
        <i32 as ToSql>::accepts(ty)
×
1919
    }
×
1920

1921
    postgres_types::to_sql_checked!();
1922
}
1923

1924
impl<'a> FromSql<'a> for CacheTtlSeconds {
1925
    fn from_sql(
×
1926
        ty: &postgres_types::Type,
×
1927
        raw: &'a [u8],
×
1928
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
×
1929
        Ok(Self(<i32 as FromSql>::from_sql(ty, raw)? as u32))
×
1930
    }
×
1931

1932
    fn accepts(ty: &postgres_types::Type) -> bool {
×
1933
        <i32 as FromSql>::accepts(ty)
×
1934
    }
×
1935
}
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