Skip to main content

oxigdal_metadata/
lib.rs

1//! Comprehensive metadata standards support for OxiGDAL.
2//!
3//! This crate provides support for multiple geospatial and data catalog metadata standards:
4//!
5//! - **ISO 19115** - Geographic Information - Metadata
6//! - **FGDC** - Federal Geographic Data Committee
7//! - **INSPIRE** - EU INSPIRE Directive
8//! - **DataCite** - DOI metadata for research data
9//! - **DCAT** - W3C Data Catalog Vocabulary
10//!
11//! # Features
12//!
13//! - Metadata extraction from datasets (GeoTIFF, NetCDF, HDF5, STAC)
14//! - Metadata validation and quality scoring
15//! - Cross-standard transformation
16//! - XML and JSON serialization
17//!
18//! # Examples
19//!
20//! ```no_run
21//! use oxigdal_metadata::*;
22//!
23//! # fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
24//! // Create ISO 19115 metadata
25//! let iso = iso19115::Iso19115Metadata::builder()
26//!     .title("Sentinel-2 Imagery")
27//!     .abstract_text("Satellite imagery from Sentinel-2")
28//!     .keywords(vec!["satellite", "sentinel-2"])
29//!     .build()?;
30//!
31//! // Validate
32//! let validation = validate::validate_iso19115(&iso)?;
33//! if !validation.is_complete() {
34//!     println!("Missing fields: {:?}", validation.missing_fields());
35//! }
36//!
37//! # Ok(())
38//! # }
39//! ```
40
41#![cfg_attr(not(feature = "std"), no_std)]
42#![warn(missing_docs)]
43
44#[cfg(feature = "std")]
45extern crate std;
46
47pub mod datacite;
48pub mod dcat;
49pub mod error;
50pub mod extract;
51pub mod fgdc;
52pub mod inspire;
53pub mod iso19115;
54pub mod transform;
55pub mod validate;
56
57pub use error::{MetadataError, Result};
58
59/// Common metadata types and utilities.
60pub mod common {
61    use serde::{Deserialize, Serialize};
62
63    /// Contact information.
64    #[derive(Debug, Clone, Serialize, Deserialize)]
65    pub struct ContactInfo {
66        /// Individual name
67        pub individual_name: Option<String>,
68        /// Organization name
69        pub organization_name: Option<String>,
70        /// Position name
71        pub position_name: Option<String>,
72        /// Email address
73        pub email: Option<String>,
74        /// Phone number
75        pub phone: Option<String>,
76        /// Address
77        pub address: Option<Address>,
78        /// Online resource
79        pub online_resource: Option<String>,
80    }
81
82    /// Address information.
83    #[derive(Debug, Clone, Serialize, Deserialize)]
84    pub struct Address {
85        /// Delivery point (street address)
86        pub delivery_point: Option<String>,
87        /// City
88        pub city: Option<String>,
89        /// Administrative area (state/province)
90        pub administrative_area: Option<String>,
91        /// Postal code
92        pub postal_code: Option<String>,
93        /// Country
94        pub country: Option<String>,
95    }
96
97    /// Bounding box for geographic extent.
98    #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
99    pub struct BoundingBox {
100        /// West longitude
101        pub west: f64,
102        /// East longitude
103        pub east: f64,
104        /// South latitude
105        pub south: f64,
106        /// North latitude
107        pub north: f64,
108    }
109
110    impl BoundingBox {
111        /// Create a new bounding box.
112        ///
113        /// # Arguments
114        ///
115        /// * `west` - West longitude
116        /// * `east` - East longitude
117        /// * `south` - South latitude
118        /// * `north` - North latitude
119        pub fn new(west: f64, east: f64, south: f64, north: f64) -> Self {
120            Self {
121                west,
122                east,
123                south,
124                north,
125            }
126        }
127
128        /// Check if the bounding box is valid.
129        pub fn is_valid(&self) -> bool {
130            self.west <= self.east
131                && self.south <= self.north
132                && self.west >= -180.0
133                && self.east <= 180.0
134                && self.south >= -90.0
135                && self.north <= 90.0
136        }
137    }
138
139    /// Keyword with optional thesaurus.
140    #[derive(Debug, Clone, Serialize, Deserialize)]
141    pub struct Keyword {
142        /// Keyword text
143        pub keyword: String,
144        /// Thesaurus name
145        pub thesaurus: Option<String>,
146    }
147
148    /// Temporal extent.
149    #[derive(Debug, Clone, Serialize, Deserialize)]
150    pub struct TemporalExtent {
151        /// Start date/time
152        pub start: Option<chrono::DateTime<chrono::Utc>>,
153        /// End date/time
154        pub end: Option<chrono::DateTime<chrono::Utc>>,
155    }
156
157    /// License or usage constraints.
158    #[derive(Debug, Clone, Serialize, Deserialize)]
159    pub struct License {
160        /// License name
161        pub name: String,
162        /// License URL
163        pub url: Option<String>,
164    }
165}