use std::path::Path;
use ndarray::Array2;
use tracing::info;
use tempfile;
use gdal::Dataset;
use gdal::raster::Buffer;
use gdal::raster::ResampleAlg;
use std::process::Command;
use crate::io::gdal::{GdalSarReader, to_gdal_path};
use super::errors::SafeError;
use super::types::SafeMetadata;
pub fn load_polarization_data(
file_path: &Path,
metadata: &mut SafeMetadata,
) -> Result<Array2<f32>, SafeError> {
info!("Loading underlying data from: {:?}", file_path);
let gdal_reader = GdalSarReader::open(file_path)
.map_err(|e| SafeError::Parse(format!("GDAL error: {}", e)))?;
metadata.geotransform = Some(gdal_reader.metadata.geotransform);
metadata.projection = Some(gdal_reader.metadata.projection.clone());
metadata.crs = Some(gdal_reader.metadata.projection.clone());
let arr_f32 = gdal_reader
.read_band(1, Some(ResampleAlg::NearestNeighbour))
.map_err(|e| SafeError::Parse(format!("GDAL error: {}", e)))?;
let (rows, cols) = (arr_f32.nrows(), arr_f32.ncols());
metadata.lines = rows;
metadata.samples = cols;
Ok(arr_f32)
}
pub fn load_polarization_data_with_options(
file_path: &Path,
metadata: &mut SafeMetadata,
target_crs: Option<&str>,
resample_alg: Option<ResampleAlg>,
target_size: Option<usize>,
) -> Result<Array2<f32>, SafeError> {
if let Some(dst) = target_crs {
info!("Warping to target CRS: {}", dst);
let tmp_in = file_path;
let stem = file_path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("warped");
let mut tmp_builder = tempfile::Builder::new();
let tmp_file = tmp_builder
.prefix(&format!("{}_", stem))
.suffix("_warped.vrt")
.tempfile()
.map_err(|e| SafeError::Parse(format!("tempfile error: {}", e)))?;
let tmp_out = tmp_file.path().to_path_buf();
let resample_str = match resample_alg.unwrap_or(ResampleAlg::Bilinear) {
ResampleAlg::NearestNeighbour => "near",
ResampleAlg::Bilinear => "bilinear",
ResampleAlg::Cubic => "cubic",
ResampleAlg::Lanczos => "lanczos",
_ => "bilinear",
};
let src_vsi = to_gdal_path(tmp_in);
let src_ds = Dataset::open(Path::new(src_vsi.as_ref()))
.map_err(|e| SafeError::Parse(format!("GDAL open error: {}", e)))?;
let ds_proj = src_ds.projection();
let parse_epsg = |wkt: &str| -> Option<String> {
let key = "AUTHORITY[\"EPSG\",\"";
if let Some(idx) = wkt.rfind(key) {
let start = idx + key.len();
if let Some(end) = wkt[start..].find('"') {
let code = &wkt[start..start + end];
return Some(format!("EPSG:{}", code));
}
}
None
};
if !ds_proj.is_empty() {
if let Some(epsg) = parse_epsg(&ds_proj).or_else(|| {
if ds_proj.starts_with("EPSG:") {
Some(ds_proj.clone())
} else {
None
}
}) {
if epsg.eq_ignore_ascii_case(dst) {
info!("Input already in target CRS ({}); skipping warp", dst);
let gdal_reader = GdalSarReader::open(file_path)
.map_err(|e| SafeError::Parse(format!("GDAL error: {}", e)))?;
metadata.geotransform = Some(gdal_reader.metadata.geotransform);
metadata.projection = Some(gdal_reader.metadata.projection.clone());
metadata.crs = Some(gdal_reader.metadata.projection.clone());
let arr_f32 = gdal_reader
.read_band(1, Some(ResampleAlg::NearestNeighbour))
.map_err(|e| SafeError::Parse(format!("GDAL error: {}", e)))?;
let (rows, cols) = (arr_f32.nrows(), arr_f32.ncols());
metadata.lines = rows;
metadata.samples = cols;
return Ok(arr_f32);
}
}
}
let mut args: Vec<String> = vec![
"-of".into(),
"VRT".into(),
"-overwrite".into(),
"-r".into(),
resample_str.into(),
"-multi".into(),
"-wo".into(),
"NUM_THREADS=ALL_CPUS".into(),
"-wm".into(),
"512".into(),
"--config".into(),
"GDAL_CACHEMAX".into(),
"512".into(),
];
if let Some(ts) = target_size {
let (size_x, size_y) = src_ds.raster_size();
let (orig_cols, orig_rows) = (size_x as usize, size_y as usize);
let long_side = orig_cols.max(orig_rows);
let scale = (ts as f64 / long_side as f64).min(1.0);
let out_cols = ((orig_cols as f64) * scale).round().max(1.0) as usize;
let out_rows = ((orig_rows as f64) * scale).round().max(1.0) as usize;
args.push("-ts".into());
args.push(out_cols.to_string());
args.push(out_rows.to_string());
}
if ds_proj.is_empty() {
args.push("-tps".into());
let src_gcp_proj = src_ds.gcp_projection().unwrap_or_else(|| "".to_string());
let src_srs = if src_gcp_proj.trim().is_empty() {
"EPSG:4326".to_string()
} else {
src_gcp_proj
};
args.push("-s_srs".into());
args.push(src_srs);
}
args.push("-t_srs".into());
args.push(dst.to_string());
args.push(src_vsi.into_owned());
args.push(tmp_out.to_str().unwrap().to_string());
let status = Command::new("gdalwarp")
.args(args.iter().map(|s| s.as_str()))
.status()
.map_err(|e| SafeError::Parse(format!("gdalwarp exec error: {}", e)))?;
if !status.success() {
let _ = std::fs::remove_file(&tmp_out);
return Err(SafeError::Parse("gdalwarp failed".to_string()));
}
let ds: Dataset = Dataset::open(&tmp_out)
.map_err(|e| SafeError::Parse(format!("GDAL open warped error: {}", e)))?;
if let Ok(gt) = ds.geo_transform() {
metadata.geotransform = Some(gt);
}
let proj = ds.projection();
if !proj.is_empty() {
metadata.projection = Some(proj.clone());
metadata.crs = Some(proj);
}
let (size_x, size_y) = ds.raster_size();
let band = ds
.rasterband(1)
.map_err(|e| SafeError::Parse(format!("GDAL error: {}", e)))?;
let buf: Buffer<f32> = band
.read_as((0, 0), (size_x, size_y), (size_x, size_y), None)
.map_err(|e| SafeError::Parse(format!("GDAL error: {}", e)))?;
let data_vec = buf.data().to_vec();
let data = Array2::from_shape_vec((size_y as usize, size_x as usize), data_vec)
.map_err(|_| SafeError::Parse("warped array shape error".to_string()))?;
metadata.lines = size_y as usize;
metadata.samples = size_x as usize;
let _ = std::fs::remove_file(&tmp_out);
return Ok(data);
}
if let Some(ts) = target_size {
info!("Reading at target size (long side): {}", ts);
let gdal_reader = GdalSarReader::open(file_path)
.map_err(|e| SafeError::Parse(format!("GDAL error: {}", e)))?;
metadata.geotransform = Some(gdal_reader.metadata.geotransform);
metadata.projection = Some(gdal_reader.metadata.projection.clone());
metadata.crs = Some(gdal_reader.metadata.projection.clone());
let (orig_cols, orig_rows) = (gdal_reader.metadata.size_x, gdal_reader.metadata.size_y);
let long_side = orig_cols.max(orig_rows);
let scale = (ts as f64 / long_side as f64).min(1.0);
let out_cols = ((orig_cols as f64) * scale).round().max(1.0) as usize;
let out_rows = ((orig_rows as f64) * scale).round().max(1.0) as usize;
let reduction = (orig_cols.max(orig_rows) as f64 / ts as f64).max(1.0);
let chosen_alg = match resample_alg {
Some(alg) => alg,
None => {
if reduction >= 4.0 {
ResampleAlg::Average
} else {
ResampleAlg::Lanczos
}
}
};
let arr_f32 = gdal_reader
.read_band_resampled(1, out_cols, out_rows, Some(chosen_alg))
.map_err(|e| SafeError::Parse(format!("GDAL error: {}", e)))?;
metadata.lines = out_rows;
metadata.samples = out_cols;
return Ok(arr_f32);
}
load_polarization_data(file_path, metadata)
}