oxigdal-metadata 0.1.0

Metadata standards support for OxiGDAL - ISO 19115, FGDC, INSPIRE, DataCite, DCAT
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
//! FGDC (Federal Geographic Data Committee) metadata standard.
//!
//! This module implements the FGDC-STD-001-1998 Content Standard for
//! Digital Geospatial Metadata (CSDGM).
//!
//! # Overview
//!
//! The FGDC metadata standard is widely used in the United States for
//! documenting geospatial data. It provides a comprehensive structure
//! for describing data identification, quality, organization, spatial
//! reference, and distribution.
//!
//! # Examples
//!
//! ```no_run
//! use oxigdal_metadata::fgdc::*;
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let metadata = FgdcMetadata::builder()
//!     .title("Digital Elevation Model")
//!     .abstract_text("30-meter resolution DEM")
//!     .purpose("Terrain analysis and visualization")
//!     .build()?;
//! # Ok(())
//! # }
//! ```

use crate::common::{BoundingBox, ContactInfo};
use crate::error::{MetadataError, Result};
use serde::{Deserialize, Serialize};

/// Complete FGDC metadata record.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct FgdcMetadata {
    /// Identification information
    pub idinfo: IdentificationInfo,
    /// Data quality information
    pub dataqual: Option<DataQualityInfo>,
    /// Spatial data organization information
    pub spdoinfo: Option<SpatialDataOrganizationInfo>,
    /// Spatial reference information
    pub spref: Option<SpatialReferenceInfo>,
    /// Entity and attribute information
    pub eainfo: Option<EntityAttributeInfo>,
    /// Distribution information
    pub distinfo: Option<DistributionInfo>,
    /// Metadata reference information
    pub metainfo: MetadataReferenceInfo,
}

/// Builder for FGDC metadata.
pub struct FgdcBuilder {
    metadata: FgdcMetadata,
}

impl FgdcMetadata {
    /// Create a new builder.
    pub fn builder() -> FgdcBuilder {
        FgdcBuilder {
            metadata: Self::default(),
        }
    }
}

impl FgdcBuilder {
    /// Set the title.
    pub fn title(mut self, title: impl Into<String>) -> Self {
        self.metadata.idinfo.citation.citeinfo.title = title.into();
        self
    }

    /// Set the abstract.
    pub fn abstract_text(mut self, abstract_text: impl Into<String>) -> Self {
        self.metadata.idinfo.descript.abstract_text = abstract_text.into();
        self
    }

    /// Set the purpose.
    pub fn purpose(mut self, purpose: impl Into<String>) -> Self {
        self.metadata.idinfo.descript.purpose = Some(purpose.into());
        self
    }

    /// Set the bounding box.
    pub fn bbox(mut self, bbox: BoundingBox) -> Self {
        self.metadata.idinfo.spdom.bounding = bbox;
        self
    }

    /// Add keywords.
    pub fn keywords(mut self, theme: impl Into<String>, keywords: Vec<impl Into<String>>) -> Self {
        let kw = Keywords {
            theme: Some(theme.into()),
            theme_key: keywords.into_iter().map(|k| k.into()).collect(),
            place: Vec::new(),
            temporal: Vec::new(),
        };
        self.metadata.idinfo.keywords.push(kw);
        self
    }

    /// Build the metadata.
    pub fn build(self) -> Result<FgdcMetadata> {
        if self.metadata.idinfo.citation.citeinfo.title.is_empty() {
            return Err(MetadataError::MissingField("title".to_string()));
        }
        Ok(self.metadata)
    }
}

/// Identification information.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct IdentificationInfo {
    /// Citation
    pub citation: Citation,
    /// Description
    pub descript: Description,
    /// Time period
    pub timeperd: TimePeriod,
    /// Status
    pub status: Status,
    /// Spatial domain
    pub spdom: SpatialDomain,
    /// Keywords
    pub keywords: Vec<Keywords>,
    /// Access constraints
    pub accconst: Option<String>,
    /// Use constraints
    pub useconst: Option<String>,
    /// Point of contact
    pub ptcontac: Option<Contact>,
    /// Browse graphic
    pub browse: Vec<BrowseGraphic>,
    /// Native data set environment
    pub native: Option<String>,
}

/// Citation information.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Citation {
    /// Citation info
    pub citeinfo: CiteInfo,
}

/// Citation info.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CiteInfo {
    /// Originator
    pub origin: Vec<String>,
    /// Publication date
    pub pubdate: Option<String>,
    /// Title
    pub title: String,
    /// Edition
    pub edition: Option<String>,
    /// Geospatial data presentation form
    pub geoform: Option<String>,
    /// Series information
    pub serinfo: Option<SeriesInfo>,
    /// Publication information
    pub pubinfo: Option<PublicationInfo>,
    /// Online linkage
    pub onlink: Vec<String>,
}

/// Series information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SeriesInfo {
    /// Series name
    pub sername: String,
    /// Issue identification
    pub issue: Option<String>,
}

/// Publication information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicationInfo {
    /// Publication place
    pub pubplace: String,
    /// Publisher
    pub publish: String,
}

/// Description.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Description {
    /// Abstract
    pub abstract_text: String,
    /// Purpose
    pub purpose: Option<String>,
    /// Supplemental information
    pub supplinf: Option<String>,
}

/// Time period.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimePeriod {
    /// Time period info
    pub timeinfo: TimeInfo,
    /// Current reference
    pub current: String,
}

impl Default for TimePeriod {
    fn default() -> Self {
        Self {
            timeinfo: TimeInfo::Single {
                caldate: chrono::Utc::now().format("%Y%m%d").to_string(),
            },
            current: "ground condition".to_string(),
        }
    }
}

/// Time information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TimeInfo {
    /// Single date/time
    Single {
        /// Calendar date
        caldate: String,
    },
    /// Multiple dates/times
    Multiple {
        /// Calendar dates
        caldate: Vec<String>,
    },
    /// Range of dates/times
    Range {
        /// Beginning date
        begdate: String,
        /// Ending date
        enddate: String,
    },
}

/// Status.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Status {
    /// Progress
    pub progress: Progress,
    /// Maintenance and update frequency
    pub update: MaintenanceFrequency,
}

impl Default for Status {
    fn default() -> Self {
        Self {
            progress: Progress::Complete,
            update: MaintenanceFrequency::AsNeeded,
        }
    }
}

/// Progress code.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum Progress {
    /// Complete
    Complete,
    /// In work
    InWork,
    /// Planned
    Planned,
}

/// Maintenance frequency.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum MaintenanceFrequency {
    /// Continually
    Continually,
    /// Daily
    Daily,
    /// Weekly
    Weekly,
    /// Monthly
    Monthly,
    /// Annually
    Annually,
    /// Unknown
    Unknown,
    /// As needed
    AsNeeded,
    /// Irregular
    Irregular,
    /// None planned
    NonePlanned,
}

/// Spatial domain.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpatialDomain {
    /// Bounding coordinates
    pub bounding: BoundingBox,
}

impl Default for SpatialDomain {
    fn default() -> Self {
        Self {
            bounding: BoundingBox::new(-180.0, 180.0, -90.0, 90.0),
        }
    }
}

/// Keywords.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Keywords {
    /// Theme
    pub theme: Option<String>,
    /// Theme keywords
    pub theme_key: Vec<String>,
    /// Place keywords
    pub place: Vec<String>,
    /// Temporal keywords
    pub temporal: Vec<String>,
}

/// Contact.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Contact {
    /// Contact information
    pub cntinfo: ContactInfo,
}

/// Browse graphic.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BrowseGraphic {
    /// Browse file name
    pub browsen: String,
    /// Browse file description
    pub browsed: String,
    /// Browse file type
    pub browset: String,
}

/// Data quality information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataQualityInfo {
    /// Attribute accuracy
    pub attracc: Option<AttributeAccuracy>,
    /// Logical consistency
    pub logic: Option<String>,
    /// Completeness
    pub complete: Option<String>,
    /// Positional accuracy
    pub posacc: Option<PositionalAccuracy>,
    /// Lineage
    pub lineage: Lineage,
}

/// Attribute accuracy.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AttributeAccuracy {
    /// Attribute accuracy report
    pub attraccr: String,
}

/// Positional accuracy.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PositionalAccuracy {
    /// Horizontal positional accuracy
    pub horizpa: Option<HorizontalPositionalAccuracy>,
    /// Vertical positional accuracy
    pub vertacc: Option<VerticalPositionalAccuracy>,
}

/// Horizontal positional accuracy.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HorizontalPositionalAccuracy {
    /// Horizontal positional accuracy report
    pub horizpar: String,
}

/// Vertical positional accuracy.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerticalPositionalAccuracy {
    /// Vertical positional accuracy report
    pub vertaccr: String,
}

/// Lineage.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Lineage {
    /// Source information
    pub srcinfo: Vec<SourceInfo>,
    /// Process step
    pub procstep: Vec<ProcessStep>,
}

/// Source information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceInfo {
    /// Source citation
    pub srccite: Citation,
    /// Source scale denominator
    pub srcscale: Option<i32>,
    /// Type of source media
    pub typesrc: Option<String>,
    /// Source contribution
    pub srccontr: String,
}

/// Process step.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessStep {
    /// Process description
    pub procdesc: String,
    /// Process date
    pub procdate: String,
}

/// Spatial data organization information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpatialDataOrganizationInfo {
    /// Indirect spatial reference
    pub indspref: Option<String>,
    /// Direct spatial reference method
    pub direct: DirectSpatialReferenceMethod,
}

/// Direct spatial reference method.
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum DirectSpatialReferenceMethod {
    /// Point
    Point,
    /// Vector
    Vector,
    /// Raster
    Raster,
}

/// Spatial reference information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpatialReferenceInfo {
    /// Horizontal coordinate system definition
    pub horizsys: Option<HorizontalCoordinateSystem>,
    /// Vertical coordinate system definition
    pub vertdef: Option<VerticalCoordinateSystem>,
}

/// Horizontal coordinate system.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum HorizontalCoordinateSystem {
    /// Geographic
    Geographic {
        /// Latitude resolution
        latres: f64,
        /// Longitude resolution
        longres: f64,
        /// Geodetic model
        geodetic: GeodeticModel,
    },
    /// Planar
    Planar {
        /// Map projection
        mapproj: Option<String>,
        /// Grid coordinate system
        gridsys: Option<String>,
    },
}

/// Geodetic model.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeodeticModel {
    /// Horizontal datum name
    pub horizdn: String,
    /// Ellipsoid name
    pub ellips: String,
    /// Semi-major axis
    pub semiaxis: f64,
    /// Denominator of flattening ratio
    pub denflat: f64,
}

/// Vertical coordinate system.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VerticalCoordinateSystem {
    /// Altitude system definition
    pub altsys: AltitudeSystem,
}

/// Altitude system.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AltitudeSystem {
    /// Altitude datum name
    pub altdatum: String,
    /// Altitude resolution
    pub altres: f64,
    /// Altitude units
    pub altunits: String,
    /// Altitude encoding method
    pub altenc: String,
}

/// Entity and attribute information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntityAttributeInfo {
    /// Detailed description
    pub detailed: Vec<DetailedDescription>,
    /// Overview description
    pub overview: Option<OverviewDescription>,
}

/// Detailed description.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetailedDescription {
    /// Entity type
    pub enttyp: EntityType,
    /// Attributes
    pub attr: Vec<Attribute>,
}

/// Entity type.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EntityType {
    /// Entity type label
    pub enttypl: String,
    /// Entity type definition
    pub enttypd: String,
    /// Entity type definition source
    pub enttypds: String,
}

/// Attribute.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attribute {
    /// Attribute label
    pub attrlabl: String,
    /// Attribute definition
    pub attrdef: String,
    /// Attribute definition source
    pub attrdefs: String,
}

/// Overview description.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OverviewDescription {
    /// Entity and attribute overview
    pub eaover: String,
    /// Entity and attribute detail citation
    pub eadetcit: String,
}

/// Distribution information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DistributionInfo {
    /// Distributor
    pub distrib: Distributor,
    /// Standard order process
    pub stdorder: Vec<StandardOrderProcess>,
}

/// Distributor.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Distributor {
    /// Contact information
    pub cntinfo: ContactInfo,
}

/// Standard order process.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StandardOrderProcess {
    /// Non-digital form
    pub nondig: Option<String>,
    /// Digital form
    pub digform: Vec<DigitalForm>,
    /// Fees
    pub fees: Option<String>,
}

/// Digital form.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DigitalForm {
    /// Digital transfer information
    pub digtinfo: DigitalTransferInfo,
    /// Digital transfer option
    pub digtopt: DigitalTransferOption,
}

/// Digital transfer information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DigitalTransferInfo {
    /// Format name
    pub formname: String,
    /// Format version number
    pub formvern: Option<String>,
    /// Transfer size
    pub transize: Option<f64>,
}

/// Digital transfer option.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DigitalTransferOption {
    /// Online option
    pub onlinopt: Vec<OnlineOption>,
}

/// Online option.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OnlineOption {
    /// Computer contact information
    pub computer: ComputerContactInfo,
}

/// Computer contact information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComputerContactInfo {
    /// Network address
    pub networka: NetworkAddress,
}

/// Network address.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NetworkAddress {
    /// Network resource name
    pub networkr: String,
}

/// Metadata reference information.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetadataReferenceInfo {
    /// Metadata date
    pub metd: String,
    /// Metadata contact
    pub metc: Contact,
    /// Metadata standard name
    pub metstdn: String,
    /// Metadata standard version
    pub metstdv: String,
}

impl Default for MetadataReferenceInfo {
    fn default() -> Self {
        Self {
            metd: chrono::Utc::now().format("%Y%m%d").to_string(),
            metc: Contact {
                cntinfo: ContactInfo {
                    individual_name: None,
                    organization_name: None,
                    position_name: None,
                    email: None,
                    phone: None,
                    address: None,
                    online_resource: None,
                },
            },
            metstdn: "FGDC Content Standard for Digital Geospatial Metadata".to_string(),
            metstdv: "FGDC-STD-001-1998".to_string(),
        }
    }
}

#[cfg(feature = "xml")]
impl FgdcMetadata {
    /// Export to XML format.
    pub fn to_xml(&self) -> Result<String> {
        use quick_xml::se::to_string;
        to_string(&self).map_err(|e| MetadataError::XmlError(e.to_string()))
    }

    /// Import from XML format.
    pub fn from_xml(xml: &str) -> Result<Self> {
        use quick_xml::de::from_str;
        from_str(xml).map_err(|e| MetadataError::XmlError(e.to_string()))
    }
}

impl FgdcMetadata {
    /// Export to JSON format.
    pub fn to_json(&self) -> Result<String> {
        serde_json::to_string_pretty(&self).map_err(|e| MetadataError::JsonError(e.to_string()))
    }

    /// Import from JSON format.
    pub fn from_json(json: &str) -> Result<Self> {
        serde_json::from_str(json).map_err(|e| MetadataError::JsonError(e.to_string()))
    }
}