163 lines
5.7 KiB
Python
163 lines
5.7 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from typing import Any, Dict, List, Sequence
|
|
|
|
def _normalize_path(value: Any) -> str:
|
|
text = str(value or "").strip()
|
|
if not text:
|
|
return ""
|
|
return os.path.normpath(os.path.abspath(text))
|
|
|
|
|
|
def _dedupe_existing_files(paths: Sequence[Any]) -> List[str]:
|
|
normalized_paths: List[str] = []
|
|
seen: set[str] = set()
|
|
for path in paths:
|
|
normalized = _normalize_path(path)
|
|
if not normalized or normalized in seen:
|
|
continue
|
|
seen.add(normalized)
|
|
normalized_paths.append(normalized)
|
|
return normalized_paths
|
|
|
|
|
|
def _inspect_raster(path: str) -> Dict[str, Any]:
|
|
gdal_error: Exception | None = None
|
|
try:
|
|
from osgeo import gdal
|
|
|
|
gdal.UseExceptions()
|
|
dataset = gdal.Open(path, gdal.GA_ReadOnly)
|
|
if dataset is None:
|
|
raise RuntimeError("GDAL returned no dataset")
|
|
|
|
band_count = int(dataset.RasterCount or 0)
|
|
width = int(dataset.RasterXSize or 0)
|
|
height = int(dataset.RasterYSize or 0)
|
|
if band_count <= 0 or width <= 0 or height <= 0:
|
|
raise ValueError(
|
|
f"invalid raster geometry bands={band_count} width={width} height={height}"
|
|
)
|
|
|
|
return {
|
|
"width": width,
|
|
"height": height,
|
|
"bands": band_count,
|
|
"driver": dataset.GetDriver().ShortName if dataset.GetDriver() is not None else "",
|
|
"projection_present": bool(dataset.GetProjection()),
|
|
"geo_transform_present": bool(dataset.GetGeoTransform(can_return_null=True)),
|
|
"reader": "gdal",
|
|
}
|
|
except Exception as exc:
|
|
gdal_error = exc
|
|
|
|
try:
|
|
import rasterio
|
|
|
|
with rasterio.open(path) as dataset:
|
|
band_count = int(dataset.count or 0)
|
|
width = int(dataset.width or 0)
|
|
height = int(dataset.height or 0)
|
|
if band_count <= 0 or width <= 0 or height <= 0:
|
|
raise ValueError(
|
|
f"invalid raster geometry bands={band_count} width={width} height={height}"
|
|
)
|
|
|
|
return {
|
|
"width": width,
|
|
"height": height,
|
|
"bands": band_count,
|
|
"driver": str(dataset.driver or ""),
|
|
"projection_present": bool(dataset.crs),
|
|
"geo_transform_present": dataset.transform is not None,
|
|
"reader": "rasterio",
|
|
}
|
|
except Exception as exc:
|
|
if gdal_error is not None:
|
|
raise RuntimeError(f"gdal={gdal_error}; rasterio={exc}") from exc
|
|
raise
|
|
|
|
|
|
def validate_isce2_result_files(
|
|
primary_file: Any,
|
|
source_files: Sequence[Any] | None = None,
|
|
) -> Dict[str, Any]:
|
|
normalized_primary = _normalize_path(primary_file)
|
|
normalized_sources = _dedupe_existing_files(source_files or [])
|
|
if normalized_primary and normalized_primary not in normalized_sources and os.path.isfile(normalized_primary):
|
|
normalized_sources.insert(0, normalized_primary)
|
|
|
|
issues: List[str] = []
|
|
accepted_sources: List[str] = []
|
|
metrics: Dict[str, Any] = {
|
|
"primary_exists": False,
|
|
"primary_non_empty": False,
|
|
"primary_readable": False,
|
|
"primary_size_bytes": 0,
|
|
"source_file_count": len(normalized_sources),
|
|
"coh_present": False,
|
|
}
|
|
|
|
primary_metadata: Dict[str, Any] = {}
|
|
if not normalized_primary:
|
|
issues.append("Primary ISCE2 displacement file path is empty.")
|
|
elif not os.path.isfile(normalized_primary):
|
|
issues.append(f"Primary ISCE2 displacement file not found: {normalized_primary}")
|
|
else:
|
|
metrics["primary_exists"] = True
|
|
try:
|
|
primary_size = int(os.path.getsize(normalized_primary))
|
|
except OSError:
|
|
primary_size = 0
|
|
metrics["primary_size_bytes"] = primary_size
|
|
if primary_size <= 0:
|
|
issues.append(f"Primary ISCE2 displacement file is empty: {normalized_primary}")
|
|
else:
|
|
metrics["primary_non_empty"] = True
|
|
try:
|
|
primary_metadata = _inspect_raster(normalized_primary)
|
|
metrics["primary_readable"] = True
|
|
metrics["primary_raster"] = primary_metadata
|
|
accepted_sources.append(normalized_primary)
|
|
except Exception as exc:
|
|
issues.append(
|
|
f"Primary ISCE2 displacement file is not a readable GeoTIFF: {normalized_primary}: {exc}"
|
|
)
|
|
|
|
coh_path = ""
|
|
coh_metadata: Dict[str, Any] = {}
|
|
for candidate in normalized_sources:
|
|
if candidate == normalized_primary or not os.path.isfile(candidate):
|
|
continue
|
|
try:
|
|
candidate_size = int(os.path.getsize(candidate))
|
|
except OSError:
|
|
candidate_size = 0
|
|
if candidate_size <= 0:
|
|
issues.append(f"Auxiliary ISCE2 output file is empty: {candidate}")
|
|
continue
|
|
try:
|
|
coh_metadata = _inspect_raster(candidate)
|
|
coh_path = candidate
|
|
accepted_sources.append(candidate)
|
|
break
|
|
except Exception as exc:
|
|
issues.append(
|
|
f"Auxiliary ISCE2 output file is not a readable GeoTIFF: {candidate}: {exc}"
|
|
)
|
|
|
|
if coh_path:
|
|
metrics["coh_present"] = True
|
|
metrics["coh_raster"] = coh_metadata
|
|
|
|
accepted = bool(metrics["primary_readable"])
|
|
return {
|
|
"accepted": accepted,
|
|
"primary_file": normalized_primary if accepted else "",
|
|
"source_files": accepted_sources if accepted_sources else ([normalized_primary] if accepted else []),
|
|
"coh_file": coh_path,
|
|
"issues": issues,
|
|
"metrics": metrics,
|
|
}
|