use oxigdal_cli::util::raster::{self, CogWriteOptions};
use oxigdal_core::{
buffer::RasterBuffer,
io::FileDataSource,
types::{GeoTransform, NoDataValue, RasterDataType},
};
use oxigdal_geotiff::{Compression, GeoTiffReader};
use std::path::PathBuf;
type TestResult = anyhow::Result<()>;
fn make_uint8_band(width: u64, height: u64) -> anyhow::Result<RasterBuffer> {
let pixels: Vec<u8> = (0..(width * height) as usize)
.map(|i| (i % 256) as u8)
.collect();
RasterBuffer::new(
pixels,
width,
height,
RasterDataType::UInt8,
NoDataValue::None,
)
.map_err(|e| anyhow::anyhow!("{}", e))
}
fn standard_geotransform() -> GeoTransform {
GeoTransform {
origin_x: 0.0,
origin_y: 100.0,
pixel_width: 1.0,
pixel_height: -1.0,
row_rotation: 0.0,
col_rotation: 0.0,
}
}
fn tmp_path(filename: &str) -> PathBuf {
let unique = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let mut p = std::env::temp_dir();
p.push(format!(
"oxigdal_cli_conv_{}_{}_{}",
std::process::id(),
unique,
filename
));
p
}
#[test]
fn test_convert_geotiff_identity() -> TestResult {
let band = make_uint8_band(64, 64)?;
let out_path = tmp_path("identity.tif");
let _ = std::fs::remove_file(&out_path);
raster::write_multi_band(
&out_path,
&[band],
Some(standard_geotransform()),
Some(4326),
None,
)?;
assert!(out_path.exists(), "output file should exist after write");
let info = raster::read_raster_info(&out_path)?;
assert_eq!(info.width, 64, "width mismatch");
assert_eq!(info.height, 64, "height mismatch");
assert_eq!(info.bands, 1, "band count mismatch");
assert_eq!(info.epsg_code, Some(4326), "EPSG mismatch");
let _ = std::fs::remove_file(&out_path);
Ok(())
}
#[test]
fn test_convert_cog_roundtrip() -> TestResult {
let band = make_uint8_band(256, 256)?;
let out_path = tmp_path("cog_roundtrip.tif");
let _ = std::fs::remove_file(&out_path);
let options = CogWriteOptions {
geo_transform: Some(standard_geotransform()),
epsg_code: Some(4326),
no_data_value: None,
overview_levels: vec![2, 4],
tile_size: 256,
compression: Compression::Lzw,
};
raster::write_raster_cog(&out_path, &[band], options)?;
assert!(out_path.exists(), "COG output should exist");
let info = raster::read_raster_info(&out_path)?;
assert_eq!(info.width, 256);
assert_eq!(info.height, 256);
assert_eq!(info.bands, 1);
assert_eq!(info.epsg_code, Some(4326));
let _ = std::fs::remove_file(&out_path);
Ok(())
}
#[test]
fn test_convert_compression_deflate() -> TestResult {
let band = make_uint8_band(256, 256)?;
let out_path = tmp_path("cog_deflate.tif");
let _ = std::fs::remove_file(&out_path);
let options = CogWriteOptions {
geo_transform: None,
epsg_code: None,
no_data_value: None,
overview_levels: Vec::new(),
tile_size: 256,
compression: Compression::AdobeDeflate,
};
raster::write_raster_cog(&out_path, &[band], options)?;
let info = raster::read_raster_info(&out_path)?;
assert_eq!(info.width, 256);
assert_eq!(info.height, 256);
let _ = std::fs::remove_file(&out_path);
Ok(())
}
#[test]
fn test_convert_cog_multiband() -> TestResult {
let r = make_uint8_band(128, 128)?;
let g = make_uint8_band(128, 128)?;
let b = make_uint8_band(128, 128)?;
let out_path = tmp_path("cog_multiband.tif");
let _ = std::fs::remove_file(&out_path);
let options = CogWriteOptions {
geo_transform: Some(standard_geotransform()),
epsg_code: None,
no_data_value: None,
overview_levels: Vec::new(),
tile_size: 128,
compression: Compression::Lzw,
};
raster::write_raster_cog(&out_path, &[r, g, b], options)?;
let info = raster::read_raster_info(&out_path)?;
assert_eq!(info.width, 128);
assert_eq!(info.height, 128);
assert_eq!(info.bands, 3);
let _ = std::fs::remove_file(&out_path);
Ok(())
}
#[test]
fn test_convert_tile_size_respected() -> TestResult {
let band = make_uint8_band(128, 128)?;
let out_path = tmp_path("cog_tile128.tif");
let _ = std::fs::remove_file(&out_path);
let options = CogWriteOptions {
geo_transform: None,
epsg_code: None,
no_data_value: None,
overview_levels: Vec::new(),
tile_size: 128,
compression: Compression::Lzw,
};
raster::write_raster_cog(&out_path, &[band], options)?;
let source =
FileDataSource::open(&out_path).map_err(|e| anyhow::anyhow!("open datasource: {}", e))?;
let reader =
GeoTiffReader::open(source).map_err(|e| anyhow::anyhow!("open geotiff reader: {}", e))?;
let tile_size = reader.tile_size();
assert!(tile_size.is_some(), "COG must be tiled");
let (tw, th) = tile_size.ok_or_else(|| anyhow::anyhow!("tile size missing"))?;
assert_eq!(tw, 128, "tile width should be 128");
assert_eq!(th, 128, "tile height should be 128");
let _ = std::fs::remove_file(&out_path);
Ok(())
}
#[test]
fn test_convert_cog_rejects_empty_bands() {
let out_path = tmp_path("cog_empty.tif");
let result = raster::write_raster_cog(&out_path, &[], CogWriteOptions::default());
assert!(
result.is_err(),
"write_raster_cog should error on empty band list"
);
}
#[test]
fn test_cloud_uri_classification() {
use oxigdal_cli::util::cloud::is_cloud_uri;
assert!(is_cloud_uri("s3://bucket/key"));
assert!(is_cloud_uri("gs://bucket/obj"));
assert!(is_cloud_uri("az://container/blob"));
assert!(!is_cloud_uri("/local/path.tif"));
assert!(!is_cloud_uri("file:///local.tif"));
assert!(!is_cloud_uri("relative/path.tif"));
assert!(!is_cloud_uri(""));
}