use std::sync::Arc; use gdal::errors::GdalError; use gdal::Dataset; use tracing::{debug, debug_span, error, info}; use moka::future::Cache; pub struct MyDataset { pub ds: Dataset, } unsafe impl Send for MyDataset {} unsafe impl Sync for MyDataset {} pub struct DatasetRepository { cache: Cache>, basedir: String, } unsafe impl Send for DatasetRepository {} unsafe impl Sync for DatasetRepository {} impl DatasetRepository { pub fn new(basedir: String) -> Self { let c = Cache::builder() .max_capacity(100) // Create the cache. .build(); DatasetRepository { cache: c, basedir } } async fn get(&self, filename: String) -> Option> { let full_filename = format!("{}/{filename}", self.basedir); if !self.cache.contains_key(&full_filename) { info!("Will open {full_filename} because not in cache!"); let ds = Dataset::open(full_filename.clone()); match ds { Err(x) => { error!("File not present"); return None; } Ok(ds) => { let mds = Arc::new(MyDataset { ds: ds }); self.cache.insert(full_filename.clone(), mds).await; } } } self.cache.get(&full_filename).await } } impl Clone for DatasetRepository { fn clone(&self) -> Self { Self { basedir: self.basedir.clone(), cache: self.cache.clone(), } } } pub async fn elevation_from_coordinates( dr: &DatasetRepository, lat: f64, lon: f64, ) -> Result, GdalError> { let span = debug_span!("req", lat=%lat, lon=%lon); let _guard = span.enter(); let filename = get_filename_from_latlon(lat, lon); debug!(filename, "filename"); let ds = &match dr.get(filename).await { Some(x) => x, None => return Ok(None), } .ds; let (px, py) = geo_to_pixel(ds, lat, lon)?; let raster_band = ds.rasterband(1)?; let raster_value = raster_band.read_as::((px, py), (1, 1), (1, 1), None)?; Ok(Some(raster_value.data[0])) } fn get_filename_from_latlon(lat: f64, lon: f64) -> String { // Calculate the rounded values for latitude and longitude let rounded_lat = lat.floor(); let rounded_lon = lon.floor(); // Convert the rounded values to strings with leading zeros if necessary let lat_deg = format!("{:02}", rounded_lat.abs()); let lon_deg = format!("{:03}", rounded_lon.abs()); // Determine the hemisphere prefixes for latitude and longitude let lat_prefix = if rounded_lat >= 0.0 { "N" } else { "S" }; let lon_prefix = if rounded_lon >= 0.0 { "E" } else { "W" }; // Construct the filename let filename = format!( "Copernicus_DSM_30_{}{}_00_{}{}_00_DEM.tif", lat_prefix, lat_deg, lon_prefix, lon_deg ); filename } fn geo_to_pixel(dataset: &Dataset, lat: f64, lon: f64) -> Result<(isize, isize), GdalError> { let transform = dataset.geo_transform()?; let x_pixel = ((lon - transform[0]) / transform[1]).round() as isize; let y_pixel = ((lat - transform[3]) / transform[5]).round() as isize; Ok((x_pixel, y_pixel)) }