eo_identifiers/
lib.rs

1//! Parsers for naming conventions of earth observation products and datasets
2//!
3//! # Example
4//!
5//! ```rust
6//! use eo_identifiers::Identifier;
7//! use eo_identifiers::identifiers::sentinel2::ProductLevel;
8//! use std::str::FromStr;
9//! use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
10//!
11//! let ident = Identifier::from_str("S2A_MSIL1C_20170105T013442_N0204_R031_T53NMJ_20170105T013443")
12//!     .unwrap();
13//!
14//! if let Identifier::Sentinel2Product(product) = ident {
15//!     assert_eq!(product.product_level, ProductLevel::L1C);
16//!     assert_eq!(
17//!         product.start_datetime,
18//!         NaiveDateTime::new(
19//!             NaiveDate::from_ymd_opt(2017, 1, 5).unwrap(),
20//!             NaiveTime::from_hms_opt(1, 34, 42).unwrap()
21//!         )
22//!     );
23//!     assert_eq!(product.relative_orbit_number, 31);
24//! }
25//! else {
26//!     unreachable!();
27//! }
28//! ```
29mod common_parsers;
30mod from_str;
31pub mod identifiers;
32
33use chrono::NaiveDateTime;
34pub use nom;
35
36pub use from_str::ParseError;
37
38// Writing Parsers With nom Parser Combinator Framework: https://iximiuz.com/en/posts/rust-writing-parsers-with-nom/
39
40#[cfg(feature = "serde")]
41use serde::{Deserialize, Serialize};
42
43pub trait Name {
44    fn name(&self) -> &str;
45}
46
47pub trait NameLong {
48    fn name_long(&self) -> &str;
49}
50
51#[derive(PartialOrd, PartialEq, Eq, Debug, Clone, Copy, Hash)]
52#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
53pub enum Mission {
54    Sentinel1,
55    Sentinel2,
56    Sentinel3,
57    Landsat1,
58    Landsat2,
59    Landsat3,
60    Landsat4,
61    Landsat5,
62    Landsat6,
63    Landsat7,
64    Landsat8,
65    Landsat9,
66}
67
68impl Name for Mission {
69    fn name(&self) -> &str {
70        match self {
71            Mission::Sentinel1 => "Sentinel 1",
72            Mission::Sentinel2 => "Sentinel 2",
73            Mission::Sentinel3 => "Sentinel 3",
74            Mission::Landsat1 => "Landsat 1",
75            Mission::Landsat2 => "Landsat 2",
76            Mission::Landsat3 => "Landsat 3",
77            Mission::Landsat4 => "Landsat 4",
78            Mission::Landsat5 => "Landsat 5",
79            Mission::Landsat6 => "Landsat 6",
80            Mission::Landsat7 => "Landsat 7",
81            Mission::Landsat8 => "Landsat 8",
82            Mission::Landsat9 => "Landsat 9",
83        }
84    }
85}
86
87/// Identifier of a earth observation product or dataset
88#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
89#[derive(PartialOrd, PartialEq, Eq, Debug, Clone, Hash)]
90pub enum Identifier {
91    Sentinel1Product(identifiers::sentinel1::Product),
92    Sentinel1Dataset(identifiers::sentinel1::Dataset),
93    Sentinel2Product(identifiers::sentinel2::Product),
94    Sentinel3Product(identifiers::sentinel3::Product),
95    LandsatSceneId(identifiers::landsat::SceneId),
96    LandsatProduct(identifiers::landsat::Product),
97}
98
99impl From<identifiers::sentinel1::Product> for Identifier {
100    fn from(p: identifiers::sentinel1::Product) -> Self {
101        Self::Sentinel1Product(p)
102    }
103}
104
105impl From<identifiers::sentinel1::Dataset> for Identifier {
106    fn from(p: identifiers::sentinel1::Dataset) -> Self {
107        Self::Sentinel1Dataset(p)
108    }
109}
110
111impl From<identifiers::sentinel2::Product> for Identifier {
112    fn from(p: identifiers::sentinel2::Product) -> Self {
113        Self::Sentinel2Product(p)
114    }
115}
116
117impl From<identifiers::sentinel3::Product> for Identifier {
118    fn from(p: identifiers::sentinel3::Product) -> Self {
119        Self::Sentinel3Product(p)
120    }
121}
122
123impl From<identifiers::landsat::SceneId> for Identifier {
124    fn from(p: identifiers::landsat::SceneId) -> Self {
125        Self::LandsatSceneId(p)
126    }
127}
128
129impl From<identifiers::landsat::Product> for Identifier {
130    fn from(p: identifiers::landsat::Product) -> Self {
131        Self::LandsatProduct(p)
132    }
133}
134
135impl Identifier {
136    /// mission
137    pub fn mission(&self) -> Mission {
138        match self {
139            Identifier::Sentinel1Product(p) => p.mission_id.into(),
140            Identifier::Sentinel1Dataset(ds) => ds.mission_id.into(),
141            Identifier::Sentinel2Product(p) => p.mission_id.into(),
142            Identifier::Sentinel3Product(p) => p.mission_id.into(),
143            Identifier::LandsatSceneId(s) => s.mission.into(),
144            Identifier::LandsatProduct(p) => p.mission.into(),
145        }
146    }
147
148    /// sensing start datetime
149    pub fn start_datetime(&self) -> NaiveDateTime {
150        match self {
151            Identifier::Sentinel1Product(p) => p.start_datetime,
152            Identifier::Sentinel1Dataset(ds) => ds.start_datetime,
153            Identifier::Sentinel2Product(p) => p.start_datetime,
154            Identifier::Sentinel3Product(p) => p.start_datetime,
155            Identifier::LandsatSceneId(s) => {
156                s.acquire_date.and_hms_opt(0, 0, 0).expect("valid time")
157            }
158            Identifier::LandsatProduct(p) => {
159                p.acquire_date.and_hms_opt(0, 0, 0).expect("valid time")
160            }
161        }
162    }
163
164    /// sensing stop datetime
165    pub fn stop_datetime(&self) -> Option<NaiveDateTime> {
166        match self {
167            Identifier::Sentinel1Product(p) => Some(p.stop_datetime),
168            Identifier::Sentinel1Dataset(ds) => Some(ds.stop_datetime),
169            Identifier::Sentinel2Product(_) => None,
170            Identifier::Sentinel3Product(p) => Some(p.stop_datetime),
171            Identifier::LandsatSceneId(_) => None,
172            Identifier::LandsatProduct(_) => None,
173        }
174    }
175}