chore: initialize insar management system v2

This commit is contained in:
2026-04-14 13:16:01 +08:00
commit ecc72ec9cd
361 changed files with 2142522 additions and 0 deletions
@@ -0,0 +1,202 @@
#!/usr/bin/env python3
"""Build a publish-style manifest and preview bundle from MintPy outputs."""
from __future__ import annotations
import argparse
import json
from pathlib import Path
import h5py
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def _decode_date(value):
return value.decode() if isinstance(value, (bytes, np.bytes_)) else str(value)
def _read_h5_summary(h5_path: Path) -> dict:
with h5py.File(h5_path, "r") as f:
datasets = sorted(f.keys())
attrs = {k: (v.item() if hasattr(v, "item") else v) for k, v in f.attrs.items()}
serializable_attrs = {}
for key, value in attrs.items():
if isinstance(value, bytes):
serializable_attrs[key] = value.decode()
elif isinstance(value, np.ndarray):
serializable_attrs[key] = value.tolist()
else:
serializable_attrs[key] = value
summary = {
"path": h5_path.name,
"datasets": datasets,
"attrs": serializable_attrs,
}
if "date" in f:
summary["dates"] = [_decode_date(x) for x in f["date"][:]]
return summary
def _write_velocity_preview(geo_velocity_h5: Path, output_png: Path) -> dict:
with h5py.File(geo_velocity_h5, "r") as f:
velocity = f["velocity"][:]
finite = np.isfinite(velocity)
valid = velocity[finite]
if valid.size == 0:
raise RuntimeError(f"No finite velocity values found in {geo_velocity_h5}")
vmax = float(np.nanpercentile(np.abs(valid), 98))
vmax = max(vmax, 1e-6)
vmin = -vmax
fig = plt.figure(figsize=(10, 7), dpi=150)
ax = fig.add_subplot(111)
im = ax.imshow(velocity, cmap="RdBu_r", vmin=vmin, vmax=vmax)
ax.set_title("Velocity Preview (m/year)")
ax.set_xticks([])
ax.set_yticks([])
cbar = fig.colorbar(im, ax=ax, shrink=0.82)
cbar.set_label("m/year")
fig.tight_layout()
output_png.parent.mkdir(parents=True, exist_ok=True)
fig.savefig(output_png, bbox_inches="tight")
plt.close(fig)
return {
"vmin": vmin,
"vmax": vmax,
"valid_pixels": int(valid.size),
}
def _count_mask_pixels(mask_h5: Path) -> dict:
with h5py.File(mask_h5, "r") as f:
dataset_name = "mask" if "mask" in f else "waterMask"
data = f[dataset_name][:]
total = int(data.size)
valid = int(np.count_nonzero(data))
return {
"dataset": dataset_name,
"valid_pixels": valid,
"total_pixels": total,
"valid_ratio": valid / total if total else 0.0,
}
def build_bundle(mintpy_work_dir: Path, publish_dir: Path, group_key: str | None) -> None:
assets_dir = publish_dir / "assets"
preview_dir = publish_dir / "preview"
metadata_dir = publish_dir / "metadata"
geo_velocity_h5 = assets_dir / "geo_velocity.h5"
geo_timeseries_h5 = assets_dir / "geo_timeseries.h5"
geo_temporal_coh_h5 = assets_dir / "geo_temporalCoherence.h5"
geo_mask_temp_coh_h5 = assets_dir / "geo_maskTempCoh.h5"
preview_stats = _write_velocity_preview(
geo_velocity_h5=geo_velocity_h5,
output_png=preview_dir / "velocity_preview.png",
)
with h5py.File(mintpy_work_dir / "timeseries.h5", "r") as ts_file:
ref_date = ts_file.attrs.get("REF_DATE")
ref_x = ts_file.attrs.get("REF_X")
ref_y = ts_file.attrs.get("REF_Y")
stack_dates = [_decode_date(x) for x in ts_file["date"][:]]
manifest = {
"schema_version": "psinsar.publish.v1",
"catalog_name": "psinsar",
"mode": "sbas",
"engine_code": "isce2",
"processor_code": "isce2_stack_mintpy",
"group_key": group_key,
"mintpy_work_dir": str(mintpy_work_dir),
"publish_dir": str(publish_dir),
"reference_date": _decode_date(ref_date) if ref_date is not None else None,
"reference_point": {
"x": int(ref_x) if ref_x is not None else None,
"y": int(ref_y) if ref_y is not None else None,
},
"stack_dates": stack_dates,
"artifacts": [
{"product_type": "timeseries_cube", "path": "assets/geo_timeseries.h5"},
{"product_type": "velocity_map", "path": "assets/geo_velocity.h5"},
{"product_type": "velocity_geotiff", "path": "assets/velocity.tif"},
{"product_type": "temporal_coherence", "path": "assets/geo_temporalCoherence.h5"},
{"product_type": "temporal_coherence_geotiff", "path": "assets/temporalCoherence.tif"},
{"product_type": "quality_mask", "path": "assets/geo_maskTempCoh.h5"},
{"product_type": "quality_mask_geotiff", "path": "assets/maskTempCoh.tif"},
{"product_type": "preview_png", "path": "preview/velocity_preview.png"},
{"product_type": "diagnostic_png", "path": "preview/numTriNonzeroIntAmbiguity.png"},
],
"quality": {
"mask_all_valid": _count_mask_pixels(mintpy_work_dir / "maskAllValid.h5"),
"mask_temp_coh": _count_mask_pixels(mintpy_work_dir / "maskTempCoh.h5"),
"velocity_preview": preview_stats,
},
"summaries": {
"geo_velocity": _read_h5_summary(geo_velocity_h5),
"geo_timeseries": _read_h5_summary(geo_timeseries_h5),
"geo_temporal_coherence": _read_h5_summary(geo_temporal_coh_h5),
"geo_mask_temp_coh": _read_h5_summary(geo_mask_temp_coh_h5),
},
"metadata_files": [
"metadata/smallbaselineApp.cfg",
"metadata/source_quality_summary.json",
],
}
summary_json = {
"maskAllValid": manifest["quality"]["mask_all_valid"],
"maskTempCoh": manifest["quality"]["mask_temp_coh"],
"preview": manifest["quality"]["velocity_preview"],
}
publish_dir.mkdir(parents=True, exist_ok=True)
metadata_dir.mkdir(parents=True, exist_ok=True)
(publish_dir / "manifest.json").write_text(
json.dumps(manifest, indent=2, ensure_ascii=False),
encoding="utf-8",
)
(metadata_dir / "source_quality_summary.json").write_text(
json.dumps(summary_json, indent=2, ensure_ascii=False),
encoding="utf-8",
)
print(f"Wrote manifest: {publish_dir / 'manifest.json'}")
print(f"Wrote quality summary: {metadata_dir / 'source_quality_summary.json'}")
print(f"Wrote preview: {preview_dir / 'velocity_preview.png'}")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Build publish-style artifacts for MintPy SBAS outputs.")
parser.add_argument("--mintpy-work-dir", required=True, help="MintPy work directory containing timeseries.h5, velocity.h5, etc.")
parser.add_argument("--publish-dir", required=True, help="Publish output directory.")
parser.add_argument("--group-key", default=None, help="Optional stack group key to embed in manifest.")
return parser.parse_args()
def main() -> int:
args = parse_args()
build_bundle(
mintpy_work_dir=Path(args.mintpy_work_dir),
publish_dir=Path(args.publish_dir),
group_key=args.group_key,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())