1use chrono::{DateTime, FixedOffset};
7use gdal::vector::Geometry;
8use itertools::Itertools;
9use log::debug;
10use std::path::PathBuf;
11use stac::ItemCollection;
12
13pub fn unique_datetimes_in_range(dates: Vec<DateTime<FixedOffset>>) -> Vec<DateTime<FixedOffset>> {
15 let six_hours = chrono::Duration::hours(6);
16 let mut result: Vec<DateTime<FixedOffset>> = vec![];
17 let mut last_date = dates[0];
18
19 for &date in dates[1..].iter() {
20 if date - last_date <= six_hours {
21 } else {
22 result.push(last_date);
23 last_date = date;
24 }
25 }
26 result.push(last_date); result
29}
30
31pub fn get_sorted_datetimes(feature_collection: &ItemCollection) -> Vec<DateTime<FixedOffset>> {
33 let mut dates: Vec<DateTime<FixedOffset>> = feature_collection
34 .items
35 .iter()
36 .map(|i| {
37 DateTime::parse_from_rfc3339(&i.properties.datetime.to_owned().unwrap().to_rfc3339())
38 .unwrap()
39 })
40 .collect();
41 dates.sort();
42
43 dates
44}
45
46pub fn get_asset_names(feature_collection: &ItemCollection) -> Vec<String> {
48 let mut names = Vec::new();
49
50 for item in &feature_collection.items {
51 for asset in item.assets.values() {
52 if let Some(eo_bands) = asset.additional_fields.get("eo:bands") {
53 let eo_band = &eo_bands[0];
55 let mut name = eo_band["common_name"]
56 .as_str()
57 .unwrap_or_else(|| eo_band["name"].as_str().unwrap())
58 .to_owned();
59 if name == "None" {
60 name = eo_band["name"]
61 .as_str()
62 .unwrap_or("unknown")
63 .to_owned();
64 }
65 names.push(name);
66 } else {
67 names.push(asset.title.clone().unwrap_or_else(|| "unknown".to_string()));
69 }
70 }
71 }
72
73 let mut names = names.into_iter().unique().collect::<Vec<_>>();
74 names.sort();
75 names
76}
77
78fn get_name_from_bands(bands: Option<&serde_json::value::Value>) -> Option<String> {
79 if let Some(json_array) = bands {
80 if let Some(json_object) = json_array.get(0) {
81 if let Some(name_field) = json_object.get("common_name") {
83 if let Some(name) = name_field.as_str() {
84 return Some(name.to_owned());
85 }
86 }
87 if let Some(name_field) = json_object.get("name") {
90 if let Some(name) = name_field.as_str() {
91 return Some(name.to_owned());
92 }
93 }
94 }
95 }
96 None
97}
98
99fn canonical_asset_name(asset: &stac::Asset) -> String {
100 if let Some(eo_bands) = asset.additional_fields.get("eo:bands") {
102 let eo_band = &eo_bands[0];
103 let mut name = eo_band["common_name"]
104 .as_str()
105 .or_else(|| eo_band["name"].as_str())
106 .unwrap_or("unknown")
107 .to_string();
108 if name == "None" {
109 name = eo_band["name"].as_str().unwrap_or("unknown").to_string();
110 }
111 return name.to_lowercase();
112 }
113
114 if let Some(title) = &asset.title {
116 debug!("title {:?}", title);
118 match title.as_str() {
119 "Surface Temperature Band" => "surface temperature band".to_string(),
120 "Pixel Quality Assessment Band" => "pixel quality assessment band".to_string(),
121 "RADSAT QA Band" => "qa_radsat".to_string(),
122 _ => title.to_lowercase().replace(' ', "_"),
123 }
124 } else {
125 "unknown".to_string()
127 }
128}
129
130pub fn get_sources_for_asset(items: &Vec<stac::Item>, asset_name: &str) -> Vec<PathBuf> {
132 let mut sources = Vec::new();
133
134 for item in items {
135 for asset in item.assets.values() {
136 let name = canonical_asset_name(asset);
137
138 if name == asset_name.to_lowercase() {
139 sources.push(PathBuf::from(&asset.href));
140 }
141 }
142 }
143 sources
144}
145
146pub fn get_asset_href(
148 feature_collection: &ItemCollection,
149 date_time: &DateTime<FixedOffset>,
150 asset_name: &str,
151) -> PathBuf {
152 let mut found_asset = stac::Asset::new("test");
153 for item in &feature_collection.items {
154 let date = DateTime::parse_from_rfc3339(
155 &item.properties.datetime.to_owned().unwrap().to_rfc3339(),
156 )
157 .unwrap();
158
159 for asset in item.assets.values() {
160 let asset = asset.clone();
161 let bands = asset.additional_fields.get("eo:bands");
162 let mut names = Vec::new();
163 if let Some(name) = get_name_from_bands(bands) {
164 names.push(name);
165 } else {
166 panic!("Name not found.");
167 }
168
169 if (names[0] == asset_name) && (date == *date_time) {
170 found_asset = asset.clone();
171 }
172 }
173 }
174 PathBuf::from(found_asset.href)
175}
176
177pub fn get_items_for_date(
179 feature_collection: &ItemCollection,
180 date_time: &DateTime<FixedOffset>,
181) -> Vec<stac::Item> {
182 let mut found_items = Vec::new();
183
184 for item in &feature_collection.items {
185 let date = DateTime::parse_from_rfc3339(
186 &item.properties.datetime.to_owned().unwrap().to_rfc3339(),
187 )
188 .unwrap();
189
190 let delta = *date_time - date;
191
192 if delta.abs() <= chrono::Duration::hours(24) {
193 found_items.push(item.to_owned());
194 }
195 }
196 found_items
197}
198
199pub fn swap_coordinates(gdal_geometry: &Geometry) -> Geometry {
201 let wkt = gdal_geometry.wkt().unwrap();
202
203 let mut swapped_wkt = String::new();
204 swapped_wkt.push_str("POLYGON ((");
205 let tokens: Vec<&str> = wkt.split([',', '(', ')']).collect();
206
207 for token in tokens {
209 if !token.starts_with("POLY") {
210 let coordinates: Vec<&str> = token.split(' ').collect();
212 if coordinates.len() == 2 {
213 let x = coordinates[0].trim();
215 let y = coordinates[1].trim();
216
217 let to_append = &format!("{} {} {}, ", y, x, 0);
219
220 swapped_wkt.push_str(to_append);
221 }
222 }
223
224 }
226 swapped_wkt.push_str("))");
227 let mut result = swapped_wkt;
228
229 if let Some(last_comma_index) = result.rfind(',') {
231 result.remove(last_comma_index);
233 }
234
235 Geometry::from_wkt(&result).unwrap()
236}