use crate::{
reader::{ColourStop, Layer, LayerGeometry},
utils::style::{is_builtin_palette, parse_style_file},
};
use csv::{ReaderBuilder, WriterBuilder};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
path::Path,
time::{Duration, UNIX_EPOCH},
};
#[derive(Serialize, Deserialize, Clone)]
pub struct LayerMetadata {
pub layer: String,
pub size_bytes: u64,
pub last_modified: u64,
pub crs_code: i32,
pub min_value: f32,
pub max_value: f32,
pub is_cog: bool,
#[serde(default)]
pub extent_minx: f64,
#[serde(default)]
pub extent_miny: f64,
#[serde(default)]
pub extent_maxx: f64,
#[serde(default)]
pub extent_maxy: f64,
}
impl LayerMetadata {
pub fn from_layer(layer: &Layer) -> Self {
let last_modified = layer
.last_modified
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
LayerMetadata {
layer: layer.layer.clone(),
size_bytes: layer.size_bytes,
last_modified,
crs_code: layer.geometry.crs_code,
min_value: layer.min_value,
max_value: layer.max_value,
is_cog: layer.is_cog,
extent_minx: layer.extent.0,
extent_miny: layer.extent.1,
extent_maxx: layer.extent.2,
extent_maxy: layer.extent.3,
}
}
pub fn to_layer(&self, path: &Path) -> Layer {
let style_name = path
.parent()
.and_then(|p| p.file_name())
.and_then(|s| s.to_str())
.unwrap_or("default");
let colour_stops: Vec<ColourStop> = if is_builtin_palette(style_name) {
Vec::new()
} else {
let style_path = path.parent().unwrap().join("style.txt");
parse_style_file(&style_path).unwrap_or_default()
};
let last_modified = UNIX_EPOCH + Duration::from_secs(self.last_modified);
let extent = (
self.extent_minx,
self.extent_miny,
self.extent_maxx,
self.extent_maxy,
);
Layer {
layer: self.layer.clone(),
style: style_name.to_string(),
path: path.to_path_buf(),
size_bytes: self.size_bytes,
geometry: LayerGeometry {
crs_name: "EPSG".to_string(),
crs_code: self.crs_code,
},
extent,
colour_stops,
min_value: self.min_value,
max_value: self.max_value,
is_cog: self.is_cog,
last_modified,
}
}
}
pub type MetadataCache = HashMap<String, LayerMetadata>;
pub fn load_cache(cache_path: &Path) -> MetadataCache {
let mut cache = MetadataCache::new();
if let Ok(mut rdr) = ReaderBuilder::new().has_headers(true).from_path(cache_path) {
for meta in rdr.deserialize::<LayerMetadata>().flatten() {
cache.insert(meta.layer.clone(), meta);
}
}
cache
}
pub fn save_cache(cache_path: &Path, cache: &MetadataCache) {
if let Ok(mut wtr) = WriterBuilder::new().has_headers(true).from_path(cache_path) {
for meta in cache.values() {
let _ = wtr.serialize(meta);
}
let _ = wtr.flush();
}
}
pub fn key_for(path: &Path, _root: &Path) -> String {
path.file_stem()
.and_then(|s| s.to_str())
.unwrap_or_default()
.to_string()
}