Files
insar-management-system-v2/backend/app/services/isce2_result_validator.py
T

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,
}