chore: sync repository with current workspace state

This commit is contained in:
2026-05-07 11:16:40 +08:00
parent 39cf92044f
commit 69d15bffcb
71 changed files with 3382 additions and 6776 deletions
+17
View File
@@ -177,9 +177,26 @@ PYINT_DEM_MODE=local_fabdem
PYINT_FABDEM_ROOT=
# When PYINT_DEM_MODE=prepared_file, point this to the same prepared WGS84 DEM.
PYINT_PREPARED_DEM_PATH=
# 0 means do not derive looks from a target output grid.
PYINT_DEFAULT_TARGET_GRID_SIZE_M=0
# Source DEM resolution recorded in PyINT/Gamma run metadata.
PYINT_DEM_RESOLUTION_M=30.0
PYINT_OPENTOPO_DEM_TYPE=SRTMGL1
PYINT_OPENTOPO_API_KEY=
PYINT_DEM_STRICT=true
# Gamma unwrap support and quality support reporting are intentionally separate.
PYINT_UNWRAP_COH_THRESHOLD=0.05
PYINT_PRODUCT_COH_THRESHOLD=0.20
PYINT_GAMMA_NODATA_VALUE=-9999.0
PYINT_GEO_INTERP=1
PYINT_ATMCOR_ENABLED=false
PYINT_ATMCOR_USE_FOR_DISP=false
PYINT_REFLATTEN_ENABLED=true
PYINT_REFLATTEN_MODEL=plane
PYINT_REFLATTEN_COH_THRESHOLD=0.70
PYINT_REFLATTEN_FALLBACK_COH_THRESHOLD=0.20
PYINT_REFLATTEN_RANGE_STEP=32
PYINT_REFLATTEN_AZIMUTH_STEP=32
PYINT_ORBIT_POLICY=require_txt
PYINT_ORBIT_POOL_TXT=D:\orbit_pools\envi
PYINT_RECORD_INPUT_ASSETS=true
+81
View File
@@ -265,9 +265,26 @@ class Settings(BaseSettings):
PYINT_DEM_MODE: str = "local_fabdem"
PYINT_FABDEM_ROOT: str = ""
PYINT_PREPARED_DEM_PATH: str = ""
PYINT_DEM_RESOLUTION_M: float = 30.0
PYINT_OPENTOPO_DEM_TYPE: str = "SRTMGL1"
PYINT_OPENTOPO_API_KEY: str = ""
PYINT_DEM_STRICT: bool = True
PYINT_UNWRAP_COH_THRESHOLD: float = 0.05
PYINT_PRODUCT_COH_THRESHOLD: float = 0.20
PYINT_REFERENCE_MODE: str = "none"
PYINT_REFERENCE_COH_THRESHOLD: float = 0.30
PYINT_DERAMP_MODE: str = "none"
PYINT_DERAMP_COH_THRESHOLD: float = 0.30
PYINT_GAMMA_NODATA_VALUE: float = -9999.0
PYINT_GEO_INTERP: str = "1"
PYINT_ATMCOR_ENABLED: bool = False
PYINT_ATMCOR_USE_FOR_DISP: bool = False
PYINT_REFLATTEN_ENABLED: bool = True
PYINT_REFLATTEN_MODEL: str = "plane"
PYINT_REFLATTEN_COH_THRESHOLD: float = 0.70
PYINT_REFLATTEN_FALLBACK_COH_THRESHOLD: float = 0.20
PYINT_REFLATTEN_RANGE_STEP: int = 32
PYINT_REFLATTEN_AZIMUTH_STEP: int = 32
PYINT_ORBIT_POLICY: str = "require_txt"
PYINT_ORBIT_POOL_TXT: str = ""
PYINT_RECORD_INPUT_ASSETS: bool = True
@@ -491,6 +508,70 @@ class Settings(BaseSettings):
if pyint_dem_mode not in {"local_fabdem", "opentopo", "prepared_file"}:
pyint_dem_mode = "local_fabdem"
object.__setattr__(self, "PYINT_DEM_MODE", pyint_dem_mode)
object.__setattr__(self, "PYINT_DEM_RESOLUTION_M", max(0.1, float(self.PYINT_DEM_RESOLUTION_M or 30.0)))
object.__setattr__(
self,
"PYINT_UNWRAP_COH_THRESHOLD",
min(1.0, max(0.0, float(self.PYINT_UNWRAP_COH_THRESHOLD or 0.05))),
)
object.__setattr__(
self,
"PYINT_PRODUCT_COH_THRESHOLD",
min(1.0, max(0.0, float(self.PYINT_PRODUCT_COH_THRESHOLD or 0.20))),
)
pyint_reference_mode = str(self.PYINT_REFERENCE_MODE or "none").strip().lower() or "none"
if pyint_reference_mode not in {"none", "coh_median"}:
pyint_reference_mode = "none"
object.__setattr__(self, "PYINT_REFERENCE_MODE", pyint_reference_mode)
object.__setattr__(
self,
"PYINT_REFERENCE_COH_THRESHOLD",
min(1.0, max(0.0, float(self.PYINT_REFERENCE_COH_THRESHOLD or 0.30))),
)
pyint_deramp_mode = str(self.PYINT_DERAMP_MODE or "none").strip().lower() or "none"
if pyint_deramp_mode not in {"none", "plane"}:
pyint_deramp_mode = "none"
object.__setattr__(self, "PYINT_DERAMP_MODE", pyint_deramp_mode)
object.__setattr__(
self,
"PYINT_DERAMP_COH_THRESHOLD",
min(1.0, max(0.0, float(self.PYINT_DERAMP_COH_THRESHOLD or 0.30))),
)
object.__setattr__(
self,
"PYINT_GAMMA_NODATA_VALUE",
float(self.PYINT_GAMMA_NODATA_VALUE if self.PYINT_GAMMA_NODATA_VALUE is not None else -9999.0),
)
pyint_geo_interp = str(self.PYINT_GEO_INTERP or "0").strip()
if pyint_geo_interp not in {"0", "1"}:
pyint_geo_interp = "1"
object.__setattr__(self, "PYINT_GEO_INTERP", pyint_geo_interp)
pyint_reflatten_model = str(self.PYINT_REFLATTEN_MODEL or "plane").strip().lower() or "plane"
if pyint_reflatten_model in {"linear"}:
pyint_reflatten_model = "plane"
if pyint_reflatten_model not in {"plane", "quadratic"}:
pyint_reflatten_model = "plane"
object.__setattr__(self, "PYINT_REFLATTEN_MODEL", pyint_reflatten_model)
object.__setattr__(
self,
"PYINT_REFLATTEN_COH_THRESHOLD",
min(1.0, max(0.0, float(self.PYINT_REFLATTEN_COH_THRESHOLD or 0.70))),
)
object.__setattr__(
self,
"PYINT_REFLATTEN_FALLBACK_COH_THRESHOLD",
min(1.0, max(0.0, float(self.PYINT_REFLATTEN_FALLBACK_COH_THRESHOLD or 0.20))),
)
object.__setattr__(
self,
"PYINT_REFLATTEN_RANGE_STEP",
max(1, int(self.PYINT_REFLATTEN_RANGE_STEP or 32)),
)
object.__setattr__(
self,
"PYINT_REFLATTEN_AZIMUTH_STEP",
max(1, int(self.PYINT_REFLATTEN_AZIMUTH_STEP or 32)),
)
if not self.PYINT_OPENTOPO_DEM_TYPE:
object.__setattr__(self, "PYINT_OPENTOPO_DEM_TYPE", "SRTMGL1")
pyint_orbit_policy = str(self.PYINT_ORBIT_POLICY or "require_txt").strip().lower() or "require_txt"
+626 -25
View File
@@ -8,7 +8,9 @@ from pathlib import Path
from typing import Any, Dict, List
from ..config import get_env_text, read_bool_env, settings
from ..services.dinsar_completion_files import repair_managed_completion_files
from ..services.dinsar_naming import write_run_metadata
from ..services.isce2_result_validator import validate_isce2_result_files
from ..services.pyint_input_assets_service import (
get_pyint_dem_summary,
get_pyint_orbit_context,
@@ -17,11 +19,33 @@ from ..services.pyint_input_assets_service import (
)
from ..services.pyint_service import (
DEFAULT_AZIMUTH_LOOKS,
DEFAULT_DEM_RESOLUTION_M,
DEFAULT_DERAMP_COH_THRESHOLD,
DEFAULT_DERAMP_MODE,
DEFAULT_ATMCOR_ENABLED,
DEFAULT_ATMCOR_USE_FOR_DISP,
DEFAULT_GEO_INTERP,
DEFAULT_PARALLEL_WORKERS,
DEFAULT_PRODUCT_COH_THRESHOLD,
DEFAULT_RANGE_LOOKS,
DEFAULT_REFLATTEN_AZIMUTH_STEP,
DEFAULT_REFLATTEN_COH_THRESHOLD,
DEFAULT_REFLATTEN_ENABLED,
DEFAULT_REFLATTEN_FALLBACK_COH_THRESHOLD,
DEFAULT_REFLATTEN_MODEL,
DEFAULT_REFLATTEN_RANGE_STEP,
DEFAULT_REFERENCE_COH_THRESHOLD,
DEFAULT_REFERENCE_MODE,
DEFAULT_TARGET_GRID_SIZE_M,
DEFAULT_UNWRAP_COH_THRESHOLD,
MAX_LOOKS,
MAX_PARALLEL_WORKERS,
REFLATTEN_MODEL_CHOICES,
TARGET_GRID_SIZE_MAX_M,
TARGET_GRID_SIZE_MIN_M,
build_project_name,
calculate_dem_oversampling,
calculate_looks_from_task_dir,
check_pyint_environment,
infer_scene_date_from_archives,
infer_task_identity,
@@ -31,10 +55,11 @@ from ..services.pyint_service import (
to_wsl_path,
validate_pyint_root_dir,
)
from ..services.wsl_service import run_wsl_command
from ..services.wsl_service import run_wsl_command_stream
from .base import DinsarEngine, EngineAvailability, EngineProfile, RunRequest, RunResult
RERUN_MODE_UNFINISHED_ONLY = "unfinished_only"
DEFAULT_COHERENCE_MASK_THRESHOLD = DEFAULT_PRODUCT_COH_THRESHOLD
def _read_env(name: str, default: str = "") -> str:
@@ -121,6 +146,85 @@ class PyintEngine(DinsarEngine):
def _dem_mode(self) -> str:
return str(getattr(settings, "PYINT_DEM_MODE", "local_fabdem") or "local_fabdem").strip().lower()
@property
def _dem_resolution_m(self) -> float:
return max(0.1, float(getattr(settings, "PYINT_DEM_RESOLUTION_M", DEFAULT_DEM_RESOLUTION_M) or DEFAULT_DEM_RESOLUTION_M))
@property
def _default_unwrap_coh_threshold(self) -> float:
return float(getattr(settings, "PYINT_UNWRAP_COH_THRESHOLD", DEFAULT_UNWRAP_COH_THRESHOLD) or DEFAULT_UNWRAP_COH_THRESHOLD)
@property
def _default_product_coh_threshold(self) -> float:
return float(getattr(settings, "PYINT_PRODUCT_COH_THRESHOLD", DEFAULT_PRODUCT_COH_THRESHOLD) or DEFAULT_PRODUCT_COH_THRESHOLD)
@property
def _default_reference_mode(self) -> str:
return str(getattr(settings, "PYINT_REFERENCE_MODE", DEFAULT_REFERENCE_MODE) or DEFAULT_REFERENCE_MODE).strip().lower()
@property
def _default_reference_coh_threshold(self) -> float:
return float(getattr(settings, "PYINT_REFERENCE_COH_THRESHOLD", DEFAULT_REFERENCE_COH_THRESHOLD) or DEFAULT_REFERENCE_COH_THRESHOLD)
@property
def _default_deramp_mode(self) -> str:
return str(getattr(settings, "PYINT_DERAMP_MODE", DEFAULT_DERAMP_MODE) or DEFAULT_DERAMP_MODE).strip().lower()
@property
def _default_deramp_coh_threshold(self) -> float:
return float(getattr(settings, "PYINT_DERAMP_COH_THRESHOLD", DEFAULT_DERAMP_COH_THRESHOLD) or DEFAULT_DERAMP_COH_THRESHOLD)
@property
def _gamma_nodata_value(self) -> float:
return float(getattr(settings, "PYINT_GAMMA_NODATA_VALUE", -9999.0) if getattr(settings, "PYINT_GAMMA_NODATA_VALUE", None) is not None else -9999.0)
@property
def _geo_interp(self) -> str:
value = str(getattr(settings, "PYINT_GEO_INTERP", DEFAULT_GEO_INTERP) or DEFAULT_GEO_INTERP).strip()
return value if value in {"0", "1"} else DEFAULT_GEO_INTERP
@property
def _atmcor_enabled(self) -> bool:
return bool(getattr(settings, "PYINT_ATMCOR_ENABLED", DEFAULT_ATMCOR_ENABLED))
@property
def _atmcor_use_for_disp(self) -> bool:
return bool(getattr(settings, "PYINT_ATMCOR_USE_FOR_DISP", DEFAULT_ATMCOR_USE_FOR_DISP))
@property
def _reflatten_enabled(self) -> bool:
return bool(getattr(settings, "PYINT_REFLATTEN_ENABLED", DEFAULT_REFLATTEN_ENABLED))
@property
def _reflatten_model(self) -> str:
value = str(getattr(settings, "PYINT_REFLATTEN_MODEL", DEFAULT_REFLATTEN_MODEL) or DEFAULT_REFLATTEN_MODEL).strip().lower()
if value == "linear":
value = "plane"
return value if value in REFLATTEN_MODEL_CHOICES else DEFAULT_REFLATTEN_MODEL
@property
def _reflatten_coh_threshold(self) -> float:
return float(getattr(settings, "PYINT_REFLATTEN_COH_THRESHOLD", DEFAULT_REFLATTEN_COH_THRESHOLD) or DEFAULT_REFLATTEN_COH_THRESHOLD)
@property
def _reflatten_fallback_coh_threshold(self) -> float:
return float(
getattr(
settings,
"PYINT_REFLATTEN_FALLBACK_COH_THRESHOLD",
DEFAULT_REFLATTEN_FALLBACK_COH_THRESHOLD,
)
or DEFAULT_REFLATTEN_FALLBACK_COH_THRESHOLD
)
@property
def _reflatten_range_step(self) -> int:
return max(1, int(getattr(settings, "PYINT_REFLATTEN_RANGE_STEP", DEFAULT_REFLATTEN_RANGE_STEP) or DEFAULT_REFLATTEN_RANGE_STEP))
@property
def _reflatten_azimuth_step(self) -> int:
return max(1, int(getattr(settings, "PYINT_REFLATTEN_AZIMUTH_STEP", DEFAULT_REFLATTEN_AZIMUTH_STEP) or DEFAULT_REFLATTEN_AZIMUTH_STEP))
@property
def _fabdem_root(self) -> str:
return _read_env("PYINT_FABDEM_ROOT", "")
@@ -197,25 +301,39 @@ class PyintEngine(DinsarEngine):
"label": "强制重跑",
"type": "boolean",
"default": False,
"section": "Execution",
"description": "删除当前 run_key 对应的工作区后重跑。",
},
"target_grid_size_m": {
"label": "目标网格尺寸(米)",
"type": "number",
"default": DEFAULT_TARGET_GRID_SIZE_M,
"step": 1,
"min": TARGET_GRID_SIZE_MIN_M,
"max": TARGET_GRID_SIZE_MAX_M,
"section": "Advanced",
"description": "可选。仅在未手动填写 looks 时用于估算多视数;不会重采样 DEM 或改写 Gamma 产品。",
"recommendation": "保持 0 使用显式或默认的 Gamma/PyINT looks。",
},
"range_looks": {
"label": "距离向多视",
"label": "距离向多视(手动覆盖)",
"type": "number",
"default": DEFAULT_RANGE_LOOKS,
"step": 1,
"min": 1,
"max": MAX_LOOKS,
"description": "PyINT 模板中的 range_looks。",
"section": "Execution",
"description": "PyINT/Gamma 模板中的 range_looks。",
},
"azimuth_looks": {
"label": "方位向多视",
"label": "方位向多视(手动覆盖)",
"type": "number",
"default": DEFAULT_AZIMUTH_LOOKS,
"step": 1,
"min": 1,
"max": MAX_LOOKS,
"description": "PyINT 模板中的 azimuth_looks。",
"section": "Execution",
"description": "PyINT/Gamma 模板中的 azimuth_looks。",
},
"parallel_workers": {
"label": "并行数",
@@ -224,18 +342,121 @@ class PyintEngine(DinsarEngine):
"step": 1,
"min": 1,
"max": MAX_PARALLEL_WORKERS,
"section": "Execution",
"description": "同步控制 raw2slc/coreg/diff/unwrap/geocode 的并行数。",
},
"coherence_mask_threshold": {
"label": "Coherence quality",
"type": "number",
"default": self._default_product_coh_threshold,
"step": 0.05,
"min": 0.0,
"max": 1.0,
"section": "Delivery",
"description": "Only used for quality support statistics. It is not applied as a Python product mask.",
"recommendation": "Use 0.20 by default for LT-1 single-pair reporting; raise it for stricter review maps.",
},
"unwrap_coh_threshold": {
"label": "Unwrap coherence",
"type": "number",
"default": self._default_unwrap_coh_threshold,
"step": 0.05,
"min": 0.0,
"max": 1.0,
"section": "Advanced",
"description": "Minimum coherence used by Gamma rascc_mask/mcf during unwrapping.",
"recommendation": "Use 0.05 for ENVI-like permissive LT-1 unwrapping; raise it only when low-coherence bridges cause unwrap artifacts.",
},
"geo_interp": {
"label": "Geocode interpolation",
"type": "select",
"default": self._geo_interp,
"enum": ["0", "1"],
"section": "Advanced",
"description": "Gamma geocode_back interpolation mode: 0 nearest, 1 bicubic spline.",
},
"atmcor": {
"label": "Gamma atmcor",
"type": "boolean",
"default": self._atmcor_enabled,
"section": "Advanced",
"description": "Run PyINT/Gamma atm_correction stage using atm_mod_2d/atm_sim_2d/sub_phase.",
},
"atmcor_use_for_disp": {
"label": "Use atmcor for disp",
"type": "boolean",
"default": self._atmcor_use_for_disp,
"section": "Advanced",
"description": "Use the Gamma atmospheric-corrected unwrapped phase as dispmap input when available.",
},
"reflatten": {
"label": "Gamma residual reflatten",
"type": "boolean",
"default": self._reflatten_enabled,
"section": "Gamma Refinement",
"description": "After unwrapping, fit and remove residual long-wavelength phase ramps with Gamma rascc_mask/quad_fit/quad_sub.",
"recommendation": "Keep enabled for LT-1 D-InSAR unless validating the raw PyINT/Gamma baseline.",
},
"reflatten_model": {
"label": "Reflatten model",
"type": "select",
"default": self._reflatten_model,
"enum": ["plane", "quadratic"],
"section": "Gamma Refinement",
"description": "Gamma quad_fit model used for residual phase trend removal.",
"recommendation": "plane is safer for single-pair production; use quadratic only when a clear curved residual ramp remains.",
},
"reflatten_coh_threshold": {
"label": "Reflatten coherence",
"type": "number",
"default": self._reflatten_coh_threshold,
"step": 0.05,
"min": 0.0,
"max": 1.0,
"section": "Gamma Refinement",
"description": "Coherence threshold used to build the fit mask.",
"recommendation": "Keep the primary fit conservative at 0.70; the backend can retry with a looser fallback.",
},
"reflatten_fallback_coh_threshold": {
"label": "Reflatten fallback coherence",
"type": "number",
"default": self._reflatten_fallback_coh_threshold,
"step": 0.05,
"min": 0.0,
"max": 1.0,
"section": "Gamma Refinement",
"description": "Fallback coherence threshold if the primary reflatten fit does not have enough usable samples.",
},
"reflatten_range_step": {
"label": "Reflatten range step",
"type": "number",
"default": self._reflatten_range_step,
"step": 1,
"min": 1,
"section": "Gamma Refinement",
"description": "Sampling step in range pixels for Gamma quad_fit control points.",
},
"reflatten_azimuth_step": {
"label": "Reflatten azimuth step",
"type": "number",
"default": self._reflatten_azimuth_step,
"step": 1,
"min": 1,
"section": "Gamma Refinement",
"description": "Sampling step in azimuth lines for Gamma quad_fit control points.",
},
"unwrap": {
"label": "执行解缠",
"type": "boolean",
"default": True,
"section": "Execution",
"description": "关闭后仅做到差分干涉图,不做解缠。",
},
"geocode": {
"label": "执行地理编码",
"type": "boolean",
"default": True,
"section": "Execution",
"description": "关闭后不导出地理编码结果。",
},
},
@@ -257,10 +478,36 @@ class PyintEngine(DinsarEngine):
return False
return bool(value)
for key in ("force", "unwrap", "geocode"):
for key in ("force", "unwrap", "geocode", "atmcor", "atmcor_use_for_disp", "reflatten"):
if key in normalized:
normalized[key] = _coerce_bool(normalized[key])
if "geo_interp" in normalized and normalized["geo_interp"] is not None:
value = str(normalized["geo_interp"] or "").strip()
if not value:
normalized.pop("geo_interp", None)
elif value not in {"0", "1"}:
raise ValueError("geo_interp must be 0 or 1.")
else:
normalized["geo_interp"] = value
if "target_grid_size_m" in normalized and str(normalized["target_grid_size_m"] or "").strip() == "":
normalized.pop("target_grid_size_m", None)
if "target_grid_size_m" in normalized and normalized["target_grid_size_m"] is not None:
try:
grid_size = float(normalized["target_grid_size_m"])
except (TypeError, ValueError) as exc:
raise ValueError("目标网格尺寸必须为数字。") from exc
if int(grid_size) != grid_size:
raise ValueError("目标网格尺寸必须使用整数米。")
grid_size = int(grid_size)
if grid_size < TARGET_GRID_SIZE_MIN_M or grid_size > TARGET_GRID_SIZE_MAX_M:
raise ValueError(
f"目标网格尺寸必须在 {TARGET_GRID_SIZE_MIN_M}{TARGET_GRID_SIZE_MAX_M} 米之间。"
)
normalized["target_grid_size_m"] = grid_size
for key, maximum, label in (
("range_looks", MAX_LOOKS, "距离向多视"),
("azimuth_looks", MAX_LOOKS, "方位向多视"),
@@ -268,6 +515,9 @@ class PyintEngine(DinsarEngine):
):
if key not in normalized or normalized[key] is None:
continue
if str(normalized[key]).strip() == "":
normalized.pop(key, None)
continue
try:
parsed = int(normalized[key])
except (TypeError, ValueError) as exc:
@@ -276,6 +526,59 @@ class PyintEngine(DinsarEngine):
raise ValueError(f"{label}必须在 1 到 {maximum} 之间。")
normalized[key] = parsed
for mode_key, choices in (
("reference_mode", {"none", "coh_median"}),
("deramp_mode", {"none", "plane"}),
("reflatten_model", {"plane", "linear", "quadratic"}),
):
if mode_key not in normalized or normalized[mode_key] is None:
continue
value = str(normalized[mode_key] or "").strip().lower()
if not value:
normalized.pop(mode_key, None)
continue
if mode_key == "reflatten_model" and value == "linear":
value = "plane"
if value not in choices:
supported = ", ".join(sorted(choices))
raise ValueError(f"{mode_key} must be one of: {supported}.")
normalized[mode_key] = value
for threshold_key in (
"coherence_mask_threshold",
"unwrap_coh_threshold",
"reference_coh_threshold",
"deramp_coh_threshold",
"reflatten_coh_threshold",
"reflatten_fallback_coh_threshold",
):
if threshold_key not in normalized or normalized[threshold_key] is None:
continue
if str(normalized[threshold_key]).strip() == "":
normalized.pop(threshold_key, None)
continue
try:
parsed_threshold = float(normalized[threshold_key])
except (TypeError, ValueError) as exc:
raise ValueError(f"{threshold_key} must be a number.") from exc
if parsed_threshold < 0.0 or parsed_threshold > 1.0:
raise ValueError(f"{threshold_key} must be between 0.0 and 1.0.")
normalized[threshold_key] = parsed_threshold
for step_key in ("reflatten_range_step", "reflatten_azimuth_step"):
if step_key not in normalized or normalized[step_key] is None:
continue
if str(normalized[step_key]).strip() == "":
normalized.pop(step_key, None)
continue
try:
parsed_step = int(normalized[step_key])
except (TypeError, ValueError) as exc:
raise ValueError(f"{step_key} must be an integer.") from exc
if parsed_step < 1:
raise ValueError(f"{step_key} must be greater than or equal to 1.")
normalized[step_key] = parsed_step
return normalized
def _has_completed_task_result(self, task_dir: str, profile_code: str) -> bool:
@@ -413,7 +716,10 @@ class PyintEngine(DinsarEngine):
total_tasks = len(task_dirs)
run_started_at = datetime.utcnow()
run_started_at_text = run_started_at.isoformat(timespec="seconds") + "Z"
run_key = f"run_{run_started_at.strftime('%Y%m%dT%H%M%SZ')}_{self.engine_code}_{request.profile}"
managed_run_key = str(extra.get("__managed_run_key") or "").strip()
run_key = managed_run_key or f"run_{run_started_at.strftime('%Y%m%dT%H%M%SZ')}_{self.engine_code}_{request.profile}"
managed_run_dir_override = str(extra.get("__managed_run_dir") or "").strip()
managed_native_output_dir_override = str(extra.get("__managed_native_output_dir") or "").strip()
progress_callback = request.progress_callback
def emit_progress(event_type: str, **payload: Any) -> None:
@@ -426,9 +732,44 @@ class PyintEngine(DinsarEngine):
timeout = max(60, int(request.timeout_seconds or self.default_timeout_seconds))
force = bool(extra.get("force"))
range_looks = int(extra.get("range_looks", DEFAULT_RANGE_LOOKS))
azimuth_looks = int(extra.get("azimuth_looks", DEFAULT_AZIMUTH_LOOKS))
target_grid_size_m = int(extra.get("target_grid_size_m") or 0)
manual_range_looks = extra.get("range_looks")
manual_azimuth_looks = extra.get("azimuth_looks")
parallel_workers = int(extra.get("parallel_workers", DEFAULT_PARALLEL_WORKERS))
dem_resolution_m = self._dem_resolution_m
dem_oversampling = calculate_dem_oversampling(
dem_resolution_m=dem_resolution_m,
target_grid_size_m=target_grid_size_m,
)
dem_lat_ovr = float(dem_oversampling["oversampling"])
dem_lon_ovr = float(dem_oversampling["oversampling"])
unwrap_coh_threshold = float(extra.get("unwrap_coh_threshold", self._default_unwrap_coh_threshold))
coherence_mask_threshold = float(extra.get("coherence_mask_threshold", self._default_product_coh_threshold))
reference_mode = "none"
reference_coh_threshold = float(self._default_reference_coh_threshold)
deramp_mode = "none"
deramp_coh_threshold = float(self._default_deramp_coh_threshold)
gamma_nodata_value = self._gamma_nodata_value
geo_interp = str(extra.get("geo_interp", self._geo_interp) or self._geo_interp).strip()
if geo_interp not in {"0", "1"}:
geo_interp = DEFAULT_GEO_INTERP
atmcor = bool(extra.get("atmcor", self._atmcor_enabled))
atmcor_use_for_disp = bool(extra.get("atmcor_use_for_disp", self._atmcor_use_for_disp)) if atmcor else False
reflatten = bool(extra.get("reflatten", self._reflatten_enabled))
reflatten_model = str(extra.get("reflatten_model", self._reflatten_model) or self._reflatten_model).strip().lower()
if reflatten_model == "linear":
reflatten_model = "plane"
if reflatten_model not in {"plane", "quadratic"}:
reflatten_model = DEFAULT_REFLATTEN_MODEL
reflatten_coh_threshold = float(extra.get("reflatten_coh_threshold", self._reflatten_coh_threshold))
reflatten_fallback_coh_threshold = float(
extra.get(
"reflatten_fallback_coh_threshold",
self._reflatten_fallback_coh_threshold,
)
)
reflatten_range_step = int(extra.get("reflatten_range_step", self._reflatten_range_step))
reflatten_azimuth_step = int(extra.get("reflatten_azimuth_step", self._reflatten_azimuth_step))
unwrap = bool(extra.get("unwrap", True))
geocode = bool(extra.get("geocode", True))
@@ -443,6 +784,57 @@ class PyintEngine(DinsarEngine):
wsl_prepared_dem_path = to_wsl_path(prepared_dem_path) if prepared_dem_path else ""
shared_orbit_context = get_pyint_orbit_context()
def resolve_pair_looks(task_dir: str) -> Dict[str, Any]:
manual_range = int(manual_range_looks) if manual_range_looks is not None else None
manual_azimuth = int(manual_azimuth_looks) if manual_azimuth_looks is not None else None
calculation: Dict[str, Any] = {}
error_text = ""
if target_grid_size_m > 0 and (manual_range is None or manual_azimuth is None):
try:
calculation = calculate_looks_from_task_dir(
task_dir,
target_grid_size_m,
)
except Exception as exc:
error_text = str(exc)
calculation = {
"mode": "fallback_default",
"target_resolution_m": target_grid_size_m,
"error": error_text,
}
elif manual_range is None or manual_azimuth is None:
calculation = {
"mode": "gamma_default_looks",
"target_resolution_m": None,
}
range_looks = manual_range
if range_looks is None:
range_looks = int(calculation.get("range_looks") or DEFAULT_RANGE_LOOKS)
azimuth_looks = manual_azimuth
if azimuth_looks is None:
azimuth_looks = int(calculation.get("azimuth_looks") or DEFAULT_AZIMUTH_LOOKS)
if manual_range is not None or manual_azimuth is not None:
calculation = {
**calculation,
"mode": "manual_override" if calculation else "manual",
"manual_range_looks": manual_range,
"manual_azimuth_looks": manual_azimuth,
}
calculation["resolved_range_looks"] = int(range_looks)
calculation["resolved_azimuth_looks"] = int(azimuth_looks)
calculation["target_grid_size_m"] = int(target_grid_size_m)
return {
"range_looks": int(range_looks),
"azimuth_looks": int(azimuth_looks),
"calculation": calculation,
"error": error_text,
}
task_results: List[Dict[str, Any]] = []
output_dirs: List[str] = []
pairs_processed = 0
@@ -458,11 +850,19 @@ class PyintEngine(DinsarEngine):
slave_date = task_identity["slave_date"]
work_run_root = os.path.normpath(os.path.join(self._work_root, pair_key, run_key))
output_dir = os.path.normpath(os.path.join(self._output_root, pair_key, "runs", run_key, "native"))
run_dir = os.path.normpath(managed_run_dir_override) if managed_run_dir_override else os.path.normpath(
os.path.join(self._output_root, pair_key, "runs", run_key)
)
output_dir = (
os.path.normpath(managed_native_output_dir_override)
if managed_native_output_dir_override
else os.path.join(run_dir, "native")
)
template_root = os.path.normpath(os.path.join(self._template_root, pair_key, run_key))
project_name = build_project_name(pair_key, run_key)
project_dir = os.path.join(work_run_root, project_name)
input_assets_dir = os.path.join(work_run_root, "input_assets")
# Keep input assets outside the run root because the WSL pipeline may delete run_root on --force.
input_assets_dir = os.path.join(self._work_root, pair_key, "input_assets", run_key)
wsl_task_dir = to_wsl_path(task_dir)
wsl_project_dir = to_wsl_path(project_dir)
@@ -663,6 +1063,45 @@ class PyintEngine(DinsarEngine):
else ""
)
look_resolution = resolve_pair_looks(task_dir)
range_looks = int(look_resolution["range_looks"])
azimuth_looks = int(look_resolution["azimuth_looks"])
look_calculation = dict(look_resolution.get("calculation") or {})
look_message = (
f"PyINT looks resolved for {task_alias}: "
f"range={range_looks}, azimuth={azimuth_looks}, "
f"target_grid={target_grid_size_m or 'not_set'}m, mode={look_calculation.get('mode', 'unknown')}"
)
if look_resolution.get("error"):
look_message += f", fallback_reason={look_resolution['error']}"
emit_progress(
"log",
pair_index=pair_index,
pair_total=total_tasks,
task_name=task_name,
task_alias=task_alias,
pair_key=pair_key,
level="WARNING" if look_resolution.get("error") else "INFO",
source="looks",
message=look_message,
)
emit_progress(
"log",
pair_index=pair_index,
pair_total=total_tasks,
task_name=task_name,
task_alias=task_alias,
pair_key=pair_key,
level="INFO",
source="dem",
message=(
f"PyINT DEM oversampling for {task_alias}: "
f"dem_resolution={dem_resolution_m:g}m, target_grid={target_grid_size_m or 'not_set'}m, "
f"dem_lat_ovr={dem_lat_ovr:g}, dem_lon_ovr={dem_lon_ovr:g}, "
f"actual_grid={float(dem_oversampling.get('actual_grid_size_m') or 0.0):g}m"
),
)
cmd_parts = [
f"{quote_shell(self._python)} {quote_shell(self._pipeline_script)} {quote_shell(wsl_task_dir)}",
f"--project-dir {quote_shell(wsl_project_dir)}",
@@ -679,10 +1118,24 @@ class PyintEngine(DinsarEngine):
f"--orbit-policy {quote_shell(self._orbit_policy)}",
f"--range-looks {range_looks}",
f"--azimuth-looks {azimuth_looks}",
f"--dem-resolution-m {dem_resolution_m}",
f"--dem-lat-ovr {dem_lat_ovr}",
f"--dem-lon-ovr {dem_lon_ovr}",
f"--parallel-workers {parallel_workers}",
f"--master-date {quote_shell(master_date)}" if master_date else "",
f"--slave-date {quote_shell(slave_date)}" if slave_date else "",
f"--time-baseline-days {time_baseline_days}",
f"--target-grid-size-m {target_grid_size_m}",
f"--unwrap-coh-threshold {unwrap_coh_threshold}",
f"--coherence-mask-threshold {coherence_mask_threshold}",
f"--geo-interp {quote_shell(geo_interp)}",
f"--gamma-nodata-value {gamma_nodata_value}",
"--reflatten" if reflatten else "--no-reflatten",
f"--reflatten-model {quote_shell(reflatten_model)}",
f"--reflatten-coh-threshold {reflatten_coh_threshold}",
f"--reflatten-fallback-coh-threshold {reflatten_fallback_coh_threshold}",
f"--reflatten-range-step {reflatten_range_step}",
f"--reflatten-azimuth-step {reflatten_azimuth_step}",
f"--input-assets-dir {quote_shell(wsl_input_assets_dir)}" if wsl_input_assets_dir else "",
f"--input-assets-json {quote_shell(wsl_input_assets_json)}" if wsl_input_assets_json else "",
f"--lt1-precise-orbit-enabled {'true' if self._lt1_precise_orbit_enabled else 'false'}",
@@ -695,6 +1148,8 @@ class PyintEngine(DinsarEngine):
f"--lt1-precise-orbit-backup {'true' if self._lt1_precise_orbit_backup else 'false'}",
f"--lt1-precise-orbit-orb-filt-degree {self._lt1_precise_orbit_orb_filt_degree}",
"--unwrap" if unwrap else "--no-unwrap",
"--atmcor" if atmcor else "--no-atmcor",
"--atmcor-use-for-disp" if atmcor_use_for_disp else "--no-atmcor-use-for-disp",
"--geocode" if geocode else "--no-geocode",
]
if self._dem_mode == "local_fabdem" and wsl_fabdem_root:
@@ -712,19 +1167,61 @@ class PyintEngine(DinsarEngine):
cmd_parts.append("--force")
cmd = " ".join(part for part in cmd_parts if part)
rc, stdout, stderr = run_wsl_command(
def _emit_stream_log(level: str, source: str, text: str) -> None:
line = str(text or "").strip()
if not line:
return
max_len = 2000
if len(line) > max_len:
line = line[:max_len] + "...<truncated>"
emit_progress(
"log",
pair_index=pair_index,
pair_total=total_tasks,
task_name=task_name,
task_alias=task_alias,
pair_key=pair_key,
level=level,
source=source,
message=line,
)
rc, stdout, stderr = run_wsl_command_stream(
cmd,
distro=self._distro,
timeout=timeout,
stdout_callback=lambda line: _emit_stream_log("INFO", "stdout", line),
stderr_callback=lambda line: _emit_stream_log("WARNING", "stderr", line),
)
success = rc == 0
error_text = stderr.strip() if stderr else ""
validation_result: Dict[str, Any] = {}
completion_files_result: Dict[str, Any] = {}
primary_file = ""
source_files: List[str] = []
if success:
pairs_processed += 1
try:
os.makedirs(output_dir, exist_ok=True)
write_run_metadata(
output_dir,
{
os.makedirs(run_dir, exist_ok=True)
standard_disp_path = os.path.join(run_dir, "assets", "disp", "disp.tif")
standard_coh_path = os.path.join(run_dir, "assets", "coh", "coh.tif")
if geocode:
validation_sources = [standard_disp_path]
if os.path.isfile(standard_coh_path):
validation_sources.append(standard_coh_path)
validation_result = validate_isce2_result_files(
standard_disp_path,
validation_sources,
)
if not bool(validation_result.get("accepted")):
issues = validation_result.get("issues") or []
issue_text = "; ".join(str(item) for item in issues[:3]) or "unknown validation error"
raise RuntimeError(f"PyINT standard GeoTIFF validation failed: {issue_text}")
primary_file = str(validation_result.get("primary_file") or standard_disp_path)
source_files = list(validation_result.get("source_files") or validation_sources)
run_metadata = {
"run_key": run_key,
"pair_key": pair_key,
"task_name": task_name,
@@ -734,15 +1231,50 @@ class PyintEngine(DinsarEngine):
"source_root": os.path.normpath(request.root_dir),
"task_dir": os.path.normpath(task_dir),
"work_dir": work_run_root,
"output_dir": output_dir,
"output_dir": run_dir,
"native_output_dir": output_dir,
"project_dir": project_dir,
"runtime_id": settings.PYINT_RUNTIME_ID,
"started_at": run_started_at_text,
"finished_at": datetime.utcnow().isoformat(timespec="seconds") + "Z",
"primary_file": primary_file,
"source_files": source_files,
"acceptance": validation_result,
"params": {
"force": force,
"target_grid_size_m": target_grid_size_m,
"dem_resolution_m": dem_resolution_m,
"dem_oversampling": dem_oversampling,
"dem_lat_ovr": dem_lat_ovr,
"dem_lon_ovr": dem_lon_ovr,
"range_looks": range_looks,
"azimuth_looks": azimuth_looks,
"manual_range_looks": manual_range_looks,
"manual_azimuth_looks": manual_azimuth_looks,
"look_calculation": look_calculation,
"parallel_workers": parallel_workers,
"unwrap_coh_threshold": unwrap_coh_threshold,
"coherence_quality_threshold": coherence_mask_threshold,
"reference_mode": reference_mode,
"reference_coh_threshold": reference_coh_threshold,
"deramp_mode": deramp_mode,
"deramp_coh_threshold": deramp_coh_threshold,
"gamma_nodata_value": gamma_nodata_value,
"geo_interp": geo_interp,
"atmcor": atmcor,
"atmcor_use_for_disp": atmcor_use_for_disp,
"reflatten": reflatten,
"reflatten_model": reflatten_model,
"reflatten_coh_threshold": reflatten_coh_threshold,
"reflatten_fallback_coh_threshold": reflatten_fallback_coh_threshold,
"reflatten_range_step": reflatten_range_step,
"reflatten_azimuth_step": reflatten_azimuth_step,
"gamma_native_export": {
"python_data_processing_applied": False,
"coherence_mask_applied": False,
"reference_applied": False,
"deramp_applied": False,
},
"unwrap": unwrap,
"geocode": geocode,
},
@@ -765,10 +1297,24 @@ class PyintEngine(DinsarEngine):
"policy_version": pair_meta.get("policy_version"),
"selection_strategy": pair_meta.get("selection_strategy"),
"input_assets": input_assets_summary,
},
}
write_run_metadata(run_dir, run_metadata)
write_run_metadata(output_dir, run_metadata)
if geocode and primary_file:
completion_files_result = repair_managed_completion_files(
run_dir,
primary_file=primary_file,
source_files=source_files,
run_meta=run_metadata,
)
output_dirs.append(output_dir)
else:
output_dirs.append(run_dir)
pairs_processed += 1
except Exception as exc:
success = False
error_text = str(exc)
stderr = (stderr.rstrip() + "\n" + error_text) if stderr else error_text
if not success:
pairs_failed += 1
emit_progress(
@@ -780,7 +1326,7 @@ class PyintEngine(DinsarEngine):
pair_key=pair_key,
success=success,
returncode=rc,
error=stderr.strip() if stderr else "",
error=error_text,
)
task_results.append(
{
@@ -791,13 +1337,45 @@ class PyintEngine(DinsarEngine):
"task_dir": task_dir,
"work_dir": work_run_root,
"project_dir": project_dir,
"output_dir": output_dir,
"run_dir": run_dir,
"output_dir": run_dir,
"native_output_dir": output_dir,
"primary_file": primary_file,
"source_files": source_files,
"acceptance": validation_result,
"completion_files": completion_files_result,
"target_grid_size_m": target_grid_size_m,
"dem_resolution_m": dem_resolution_m,
"dem_oversampling": dem_oversampling,
"dem_lat_ovr": dem_lat_ovr,
"dem_lon_ovr": dem_lon_ovr,
"range_looks": range_looks,
"azimuth_looks": azimuth_looks,
"manual_range_looks": manual_range_looks,
"manual_azimuth_looks": manual_azimuth_looks,
"look_calculation": look_calculation,
"unwrap_coh_threshold": unwrap_coh_threshold,
"coherence_quality_threshold": coherence_mask_threshold,
"reference_mode": reference_mode,
"reference_coh_threshold": reference_coh_threshold,
"deramp_mode": deramp_mode,
"deramp_coh_threshold": deramp_coh_threshold,
"gamma_nodata_value": gamma_nodata_value,
"geo_interp": geo_interp,
"atmcor": atmcor,
"atmcor_use_for_disp": atmcor_use_for_disp,
"gamma_native_export": {
"python_data_processing_applied": False,
"coherence_mask_applied": False,
"reference_applied": False,
"deramp_applied": False,
},
"command": cmd,
"success": success,
"returncode": rc,
"stdout_tail": stdout[-3000:] if stdout else "",
"stderr_tail": stderr[-3000:] if stderr else "",
"error": stderr.strip() if stderr else "",
"error": error_text,
"wsl_task_dir": wsl_task_dir,
"wsl_project_dir": wsl_project_dir,
"wsl_output_dir": wsl_output_dir,
@@ -849,9 +1427,32 @@ class PyintEngine(DinsarEngine):
"started_at": run_started_at_text,
"force": force,
"timeout_seconds": timeout,
"range_looks": range_looks,
"azimuth_looks": azimuth_looks,
"target_grid_size_m": target_grid_size_m,
"dem_resolution_m": dem_resolution_m,
"dem_oversampling": dem_oversampling,
"dem_lat_ovr": dem_lat_ovr,
"dem_lon_ovr": dem_lon_ovr,
"range_looks": last_task_result.get("range_looks"),
"azimuth_looks": last_task_result.get("azimuth_looks"),
"manual_range_looks": manual_range_looks,
"manual_azimuth_looks": manual_azimuth_looks,
"parallel_workers": parallel_workers,
"unwrap_coh_threshold": unwrap_coh_threshold,
"coherence_quality_threshold": coherence_mask_threshold,
"reference_mode": reference_mode,
"reference_coh_threshold": reference_coh_threshold,
"deramp_mode": deramp_mode,
"deramp_coh_threshold": deramp_coh_threshold,
"gamma_nodata_value": gamma_nodata_value,
"geo_interp": geo_interp,
"atmcor": atmcor,
"atmcor_use_for_disp": atmcor_use_for_disp,
"gamma_native_export": {
"python_data_processing_applied": False,
"coherence_mask_applied": False,
"reference_applied": False,
"deramp_applied": False,
},
"unwrap": unwrap,
"geocode": geocode,
"command": last_task_result.get("command", ""),
File diff suppressed because it is too large Load Diff
+1
View File
@@ -268,6 +268,7 @@ async def submit_run(
else:
job_type = JOB_TYPE_PYINT_RUN
max_attempts = PYINT_PRODUCTION_JOB_MAX_ATTEMPTS
create_managed_run = True
if validation_summary is not None:
validated_task_count = validation_summary.get("task_count", 0)
payload["extra"].update(
@@ -27,6 +27,7 @@ from .workflow_service import workflow_service
TASK_TYPE_DINSAR_PRODUCTION = "IDL_RUN_DINSAR"
TASK_TYPE_ISCE2_DINSAR_PRODUCTION = "ISCE2_RUN"
TASK_TYPE_PYINT_DINSAR_PRODUCTION = "PYINT_RUN"
RUN_STATUS_PENDING = "PENDING"
RUN_STATUS_RUNNING = "RUNNING"
RUN_STATUS_COMPLETED = "COMPLETED"
@@ -75,6 +76,8 @@ def _task_type_for_engine(engine_code: str) -> str:
return TASK_TYPE_DINSAR_PRODUCTION
if normalized == "isce2":
return TASK_TYPE_ISCE2_DINSAR_PRODUCTION
if normalized in {"pyint", "gamma"}:
return TASK_TYPE_PYINT_DINSAR_PRODUCTION
raise ValueError(f"Unsupported engine for D-InSAR production run: {engine_code}")
@@ -84,6 +87,8 @@ def _workflow_name_for_engine(engine_code: str) -> str:
return "dinsar_sarscape_production"
if normalized == "isce2":
return "dinsar_isce2_production"
if normalized in {"pyint", "gamma"}:
return "dinsar_pyint_gamma_production"
raise ValueError(f"Unsupported engine for D-InSAR production run: {engine_code}")
@@ -93,6 +98,8 @@ def _workflow_step_name_for_engine(engine_code: str) -> str:
return RUNS_STEP_NAME
if normalized == "isce2":
return "Execute ISCE2 D-InSAR items"
if normalized in {"pyint", "gamma"}:
return "Execute PyINT/Gamma D-InSAR items"
raise ValueError(f"Unsupported engine for D-InSAR production run: {engine_code}")
@@ -329,6 +336,82 @@ def _execution_dir(item: DinsarProductionRunItemORM, run_key: str) -> str:
return os.path.join(item.results_root_dir, "runs", run_key)
def _first_text(*values: Any) -> str:
for value in values:
text = str(value or "").strip()
if text:
return text
return ""
def _read_json_if_exists(path: str) -> Dict[str, Any]:
text = str(path or "").strip()
if not text or not os.path.isfile(text):
return {}
try:
with open(text, "r", encoding="utf-8") as fp:
payload = json.load(fp)
return payload if isinstance(payload, dict) else {}
except Exception:
return {}
def _maybe_join(base: str, *parts: str) -> str:
text = str(base or "").strip()
if not text:
return ""
return os.path.normpath(os.path.join(text, *parts))
def _build_output_paths(
*,
engine_code: str,
item: DinsarProductionRunItemORM,
run_key: str,
output_dir: str,
manifest_path: Optional[str] = None,
) -> Dict[str, Any]:
run_dir = os.path.normpath(str(output_dir or _execution_dir(item, run_key)))
native_dir = _maybe_join(run_dir, "native")
paths: Dict[str, Any] = {
"run_dir": run_dir,
"native_dir": native_dir,
"assets_dir": _maybe_join(run_dir, "assets"),
"quality_dir": _maybe_join(run_dir, "quality"),
"manifest_path": str(manifest_path or "").strip(),
}
if str(engine_code or "").strip().lower() in {"pyint", "gamma"}:
pair_key = _first_text(item.pair_key, os.path.basename(os.path.dirname(os.path.dirname(run_dir))))
project_name = f"{pair_key}_{run_key}" if pair_key and run_key else ""
work_root = _maybe_join(settings.PYINT_WORK_ROOT, pair_key, run_key)
project_dir = _maybe_join(work_root, project_name) if project_name else ""
summary_payload = _read_json_if_exists(_maybe_join(native_dir, "pyint_run_summary.json"))
summary_project_dir = _first_text(summary_payload.get("project_dir"))
project_dir = summary_project_dir or project_dir
master_date = _first_text(summary_payload.get("master_date"))
slave_date = _first_text(summary_payload.get("slave_date"))
pair_name = f"{master_date}-{slave_date}" if master_date and slave_date else ""
ifgrams_dir = _maybe_join(project_dir, "ifgrams", pair_name) if pair_name else _maybe_join(project_dir, "ifgrams")
paths.update(
{
"work_dir": work_root,
"project_dir": project_dir,
"ifgrams_dir": ifgrams_dir,
"reflatten_dir": _maybe_join(run_dir, "gamma_reflatten"),
"native_reflatten_dir": _maybe_join(native_dir, "reflatten"),
"pyint_summary_path": _maybe_join(native_dir, "pyint_run_summary.json"),
"stdout_log": _maybe_join(work_root, "pyint.stdout.log"),
"stderr_log": _maybe_join(work_root, "pyint.stderr.log"),
}
)
return paths
def _sanitize_pointer_fragment(value: str, default: str) -> str:
text = _SAFE_POINTER_RE.sub("_", str(value or "").strip()).strip("._")
return text or default
@@ -616,6 +699,7 @@ class DinsarProductionService:
)
result = await db.execute(stmt)
runs = result.scalars().all()
run_ids = [run.run_id for run in runs if run.run_id]
pending_reconcile = [
run
for run in runs
@@ -636,6 +720,15 @@ class DinsarProductionService:
) or changed
if changed:
await db.commit()
items_by_run_id: Dict[str, List[DinsarProductionRunItemORM]] = {}
if run_ids:
items_result = await db.execute(
select(DinsarProductionRunItemORM)
.where(DinsarProductionRunItemORM.run_id.in_(run_ids))
.order_by(DinsarProductionRunItemORM.order_index.asc(), DinsarProductionRunItemORM.id.asc())
)
for item in items_result.scalars().all():
items_by_run_id.setdefault(item.run_id, []).append(item)
return {
"runs": [
{
@@ -656,6 +749,29 @@ class DinsarProductionService:
"completed_items": run.completed_items,
"failed_items": run.failed_items,
"skipped_items": run.skipped_items,
"items": [
{
"task_name": item.task_name,
"task_alias": item.task_alias,
"pair_key": item.pair_key,
"status": item.status,
"current_step": item.current_step,
"latest_run_key": item.latest_run_key,
"latest_output_dir": item.latest_output_dir,
"latest_manifest_path": item.latest_manifest_path,
"last_error": item.last_error,
"paths": _build_output_paths(
engine_code=run.engine_code,
item=item,
run_key=str(item.latest_run_key or ""),
output_dir=str(item.latest_output_dir or _execution_dir(item, str(item.latest_run_key or ""))),
manifest_path=item.latest_manifest_path,
)
if item.latest_run_key
else {},
}
for item in items_by_run_id.get(run.run_id, [])[:5]
],
}
for run in runs
],
+81 -1
View File
@@ -2058,6 +2058,25 @@ async def _handle_queued_engine_run(
pair_index = max(0, int(event.get("pair_index") or 0))
task_label = str(event.get("task_alias") or event.get("task_name") or "").strip()
if event_type == "log":
level = str(event.get("level") or "INFO").strip().upper()
if level not in {"DEBUG", "INFO", "WARNING", "ERROR"}:
level = "INFO"
source = str(event.get("source") or "").strip()
message = str(event.get("message") or "").strip()
if not message:
continue
label = task_label or str(progress_state.get("pair_label") or "").strip() or "pair"
prefix = f"{engine_title} {pair_index}/{pair_total} {label}"
if source:
prefix = f"{prefix} {source}"
await task_service.add_log(
job.task_id,
level,
f"{prefix}: {message}",
)
continue
if event_type == "pair_started":
progress = min(
90,
@@ -2496,7 +2515,22 @@ async def _run_wsl_dinsar_production_controller(
if event is None:
return
event_type = str(event.get("event") or "").strip().lower()
if event_type == "pair_started":
if event_type == "log":
level = str(event.get("level") or "INFO").strip().upper()
if level not in {"DEBUG", "INFO", "WARNING", "ERROR"}:
level = "INFO"
source = str(event.get("source") or "").strip()
message = str(event.get("message") or "").strip()
if message:
prefix = f"[{item_index}/{total_items}] {engine_title} {item_label}"
if source:
prefix = f"{prefix} {source}"
await task_service.add_log(
job.task_id,
level,
f"{prefix}: {message}",
)
elif event_type == "pair_started":
progress_state["message"] = (
f"[{engine_code}/{run.profile_code}] Running "
f"{item_index}/{total_items}: {item_label}"
@@ -2892,6 +2926,52 @@ async def _handle_isce2_run(job: SystemJobORM) -> None:
async def _handle_pyint_run(job: SystemJobORM) -> None:
production_run_id = str((job.payload or {}).get("production_run_id") or "").strip()
if production_run_id:
try:
await _run_wsl_dinsar_production_controller(
job,
engine_code="pyint",
engine_title="PyINT/Gamma",
fallback_timeout_seconds=settings.PYINT_DEFAULT_TIMEOUT_SECONDS,
)
except Exception as exc:
latest_message = f"PyINT/Gamma D-InSAR production controller failed: {exc}"
try:
async with AsyncSessionLocal() as db:
run = await dinsar_production_service.get_run(production_run_id, db)
if run is not None and str(run.status or "").strip().upper() not in {"COMPLETED", "FAILED", "CANCELLED"}:
summary_payload = dict(run.summary_json or {})
summary_payload["controller_error"] = str(exc)
await dinsar_production_service.finalize_run(
run,
db=db,
status="FAILED",
summary_payload=summary_payload,
latest_message=latest_message,
)
dinsar_production_service.append_run_log(
run.run_id,
f"[controller-failed] {exc}",
)
except Exception:
pass
try:
current_task = await task_service.get_task(job.task_id)
if current_task and current_task.status not in {"COMPLETED", "FAILED", "CANCELLED"}:
await task_service.add_log(job.task_id, "ERROR", latest_message)
await task_service.update_task(
job.task_id,
status="FAILED",
progress=100,
message=latest_message,
)
except Exception:
pass
raise
return
await _handle_queued_engine_run(
job,
engine_title="PyINT",
@@ -316,6 +316,7 @@ def get_pyint_dem_summary() -> Dict[str, Any]:
"hdr_exists": bool(prepared_dem_info.get("hdr_exists")),
"vrt_exists": bool(prepared_dem_info.get("vrt_exists")),
},
"configured_resolution_m": float(getattr(settings, "PYINT_DEM_RESOLUTION_M", 30.0) or 30.0),
"opentopo_dem_type": opentopo_dem_type,
"opentopo_api_key_configured": bool(opentopo_api_key),
"status": status,
+270 -18
View File
@@ -4,6 +4,8 @@ from __future__ import annotations
import os
import re
import shlex
import math
import defusedxml.ElementTree as ET
from dataclasses import dataclass, field
from datetime import datetime
from pathlib import Path
@@ -11,12 +13,83 @@ from typing import Any, Dict, Iterable, List, Optional
from ..config import get_env_text, read_bool_env, settings
from .dinsar_naming import PAIR_META_FILENAME, build_fallback_pair_key, find_json_sidecar
from .wsl_service import run_wsl_command
from .wsl_service import run_wsl_exec
LT1_INPUT_GLOBS = ("LT1*.tar.gz", "LT1*.tiff")
DEFAULT_RANGE_LOOKS = 2
DEFAULT_AZIMUTH_LOOKS = 2
DEFAULT_DEM_RESOLUTION_M = 30.0
DEFAULT_UNWRAP_COH_THRESHOLD = 0.05
DEFAULT_PRODUCT_COH_THRESHOLD = 0.20
DEFAULT_REFERENCE_MODE = "none"
DEFAULT_REFERENCE_COH_THRESHOLD = 0.30
DEFAULT_DERAMP_MODE = "none"
DEFAULT_DERAMP_COH_THRESHOLD = 0.30
DEFAULT_GEO_INTERP = "1"
DEFAULT_ATMCOR_ENABLED = False
DEFAULT_ATMCOR_USE_FOR_DISP = False
DEFAULT_REFLATTEN_ENABLED = True
DEFAULT_REFLATTEN_MODEL = "plane"
DEFAULT_REFLATTEN_COH_THRESHOLD = 0.70
DEFAULT_REFLATTEN_FALLBACK_COH_THRESHOLD = 0.20
DEFAULT_REFLATTEN_RANGE_STEP = 32
DEFAULT_REFLATTEN_AZIMUTH_STEP = 32
DEM_OVERSAMPLING_MIN = 0.25
DEM_OVERSAMPLING_MAX = 16.0
REFERENCE_MODE_CHOICES = {"none", "coh_median"}
DERAMP_MODE_CHOICES = {"none", "plane"}
REFLATTEN_MODEL_CHOICES = {"plane", "linear", "quadratic"}
def _read_default_target_grid_size_m() -> int:
for name in ("PYINT_DEFAULT_TARGET_GRID_SIZE_M",):
text = str(get_env_text(name, "") or "").strip()
if not text:
continue
try:
value = float(text)
except (TypeError, ValueError):
continue
if value > 0:
return int(value)
return 0
def _read_float_env(names: Iterable[str], default: float) -> float:
for name in names:
text = str(get_env_text(name, "") or "").strip()
if not text:
continue
try:
value = float(text)
except (TypeError, ValueError):
continue
if math.isfinite(value):
return value
return float(default)
DEFAULT_TARGET_GRID_SIZE_M = _read_default_target_grid_size_m()
TARGET_GRID_SIZE_MIN_M = 0
TARGET_GRID_SIZE_MAX_M = 100
DEFAULT_DEM_RESOLUTION_M = _read_float_env(("PYINT_DEM_RESOLUTION_M",), DEFAULT_DEM_RESOLUTION_M)
DEFAULT_UNWRAP_COH_THRESHOLD = _read_float_env(
("PYINT_UNWRAP_COH_THRESHOLD",),
DEFAULT_UNWRAP_COH_THRESHOLD,
)
DEFAULT_PRODUCT_COH_THRESHOLD = _read_float_env(
("PYINT_PRODUCT_COH_THRESHOLD", "PYINT_COHERENCE_MASK_THRESHOLD"),
DEFAULT_PRODUCT_COH_THRESHOLD,
)
DEFAULT_REFERENCE_COH_THRESHOLD = _read_float_env(
("PYINT_REFERENCE_COH_THRESHOLD",),
DEFAULT_REFERENCE_COH_THRESHOLD,
)
DEFAULT_DERAMP_COH_THRESHOLD = _read_float_env(
("PYINT_DERAMP_COH_THRESHOLD",),
DEFAULT_DERAMP_COH_THRESHOLD,
)
DEFAULT_PARALLEL_WORKERS = 1
MAX_LOOKS = 32
MAX_PARALLEL_WORKERS = 16
@@ -76,6 +149,180 @@ def normalize_date_text(value: Any) -> str:
return ""
def _local_xml_tag_name(tag: Any) -> str:
text = str(tag or "")
return text.split("}")[-1] if "}" in text else text
def _read_xml_first_parameter(xml_file: str, names: Iterable[str]) -> Optional[str]:
path = os.path.normpath(str(xml_file or "").strip())
if not path or not os.path.isfile(path):
return None
wanted = {str(name or "").strip().lower() for name in names if str(name or "").strip()}
if not wanted:
return None
try:
tree = ET.parse(path)
root = tree.getroot()
except Exception:
return None
for elem in root.iter():
local_name = _local_xml_tag_name(elem.tag).lower()
if local_name in wanted and elem.text and str(elem.text).strip():
return str(elem.text).strip()
return None
def _read_scene_geometry_metadata(metadata_path: str) -> Dict[str, Any]:
source = os.path.normpath(str(metadata_path or "").strip())
range_spacing = _read_xml_first_parameter(
source,
("PixelSpacingRg", "columnSpacing", "slantRange", "range_pixel_spacing"),
)
azimuth_spacing = _read_xml_first_parameter(
source,
("PixelSpacingAz", "rowSpacing", "projectedSpacingAzimuth", "azimuth_pixel_spacing"),
)
incidence_angle = _read_xml_first_parameter(
source,
("IncidenceAngle", "incidence_angle"),
)
if not all((range_spacing, azimuth_spacing, incidence_angle)):
raise ValueError(f"Cannot read range/azimuth spacing and incidence angle from: {source}")
return {
"source": source,
"range_pixel_spacing_m": float(range_spacing),
"azimuth_pixel_spacing_m": float(azimuth_spacing),
"incidence_angle_deg": float(incidence_angle),
}
def _scene_geometry_metadata_candidates(directory: str, patterns: Iterable[str]) -> List[str]:
root = os.path.normpath(str(directory or "").strip())
if not root or not os.path.isdir(root):
return []
candidates: List[str] = []
for pattern in patterns:
candidates.extend(str(path) for path in Path(root).glob(pattern) if path.is_file())
return [
os.path.normpath(path)
for path in sorted(
set(candidates),
key=lambda item: (0 if item.lower().endswith(".sml") else 1, item.lower()),
)
]
def resolve_scene_geometry_metadata_files(scene_dir: str) -> List[str]:
return _scene_geometry_metadata_candidates(
scene_dir,
(
"*.sml",
"*.SML",
"*.meta.xml",
"*.META.XML",
),
)
def resolve_scene_geometry_metadata_file(scene_dir: str) -> str:
candidates = resolve_scene_geometry_metadata_files(scene_dir)
return candidates[0] if candidates else ""
def calculate_looks_from_scene_metadata(
*,
master_metadata: str,
slave_metadata: str,
target_resolution_m: float,
) -> Dict[str, Any]:
target_resolution = float(target_resolution_m)
if target_resolution <= 0:
raise ValueError("target_resolution_m must be greater than 0")
master = _read_scene_geometry_metadata(master_metadata)
slave = _read_scene_geometry_metadata(slave_metadata)
avg_azimuth = (
float(master["azimuth_pixel_spacing_m"]) + float(slave["azimuth_pixel_spacing_m"])
) / 2.0
master_ground_range = float(master["range_pixel_spacing_m"]) / math.sin(
math.radians(float(master["incidence_angle_deg"]))
)
slave_ground_range = float(slave["range_pixel_spacing_m"]) / math.sin(
math.radians(float(slave["incidence_angle_deg"]))
)
avg_ground_range = (master_ground_range + slave_ground_range) / 2.0
range_ratio = target_resolution / avg_ground_range
azimuth_ratio = target_resolution / avg_azimuth
range_looks = max(1, int(math.floor(range_ratio + 0.5)))
azimuth_looks = max(1, int(math.floor(azimuth_ratio + 0.5)))
return {
"mode": "target_grid_size",
"target_resolution_m": target_resolution,
"range_looks": range_looks,
"azimuth_looks": azimuth_looks,
"avg_ground_range_spacing_m": avg_ground_range,
"avg_azimuth_spacing_m": avg_azimuth,
"range_look_ratio": range_ratio,
"azimuth_look_ratio": azimuth_ratio,
"resolved_ground_range_spacing_m": avg_ground_range * range_looks,
"resolved_azimuth_spacing_m": avg_azimuth * azimuth_looks,
"master": master,
"slave": slave,
}
def calculate_looks_from_task_dir(task_dir: str, target_resolution_m: float) -> Dict[str, Any]:
task_root = os.path.normpath(str(task_dir or "").strip())
master_candidates = resolve_scene_geometry_metadata_files(os.path.join(task_root, "master"))
slave_candidates = resolve_scene_geometry_metadata_files(os.path.join(task_root, "slave"))
if not master_candidates or not slave_candidates:
raise ValueError(f"Cannot find SML/meta XML metadata under task: {task_root}")
errors: List[str] = []
for master_metadata in master_candidates:
for slave_metadata in slave_candidates:
try:
return calculate_looks_from_scene_metadata(
master_metadata=master_metadata,
slave_metadata=slave_metadata,
target_resolution_m=target_resolution_m,
)
except Exception as exc:
errors.append(f"{os.path.basename(master_metadata)} + {os.path.basename(slave_metadata)}: {exc}")
detail = "; ".join(errors[:3]) if errors else "unknown metadata parsing error"
raise ValueError(f"Cannot calculate looks from task metadata under {task_root}: {detail}")
def calculate_dem_oversampling(
*,
dem_resolution_m: float,
target_grid_size_m: float,
) -> Dict[str, Any]:
dem_resolution = float(dem_resolution_m or 0.0)
target_grid = float(target_grid_size_m or 0.0)
if not math.isfinite(dem_resolution) or dem_resolution <= 0:
dem_resolution = DEFAULT_DEM_RESOLUTION_M
raw_factor = dem_resolution / target_grid if math.isfinite(target_grid) and target_grid > 0 else None
oversampling = 1.0
actual_grid = dem_resolution / oversampling if oversampling > 0 else dem_resolution
mismatch_ratio = abs(actual_grid - target_grid) / target_grid if target_grid > 0 else None
return {
"mode": "gamma_dem_oversampling",
"dem_resolution_m": dem_resolution,
"target_grid_size_m": target_grid,
"raw_oversampling": raw_factor,
"oversampling": oversampling,
"actual_grid_size_m": actual_grid,
"mismatch_ratio": mismatch_ratio,
"min_oversampling": DEM_OVERSAMPLING_MIN,
"max_oversampling": DEM_OVERSAMPLING_MAX,
}
def slugify_text(value: Any, *, default: str = "item", max_len: int = 96) -> str:
text = _SAFE_TEXT_RE.sub("_", str(value or "").strip()).strip("._")
if not text:
@@ -278,7 +525,14 @@ def _gamma_prefix(gamma_env_script_wsl: str) -> str:
script = str(gamma_env_script_wsl or "").strip()
if not script:
return ""
return f". {quote_shell(script)} >/dev/null 2>&1 && "
return f". {quote_shell(script)} >/dev/null 2>&1 || exit 1; "
def _pyint_path_prefix(pyint_home_wsl: str) -> str:
home = str(pyint_home_wsl or "").strip().rstrip("/")
if not home:
return ""
return f"export PATH={quote_shell(home + '/pyint')}:\"$PATH\" && "
def check_pyint_environment(
@@ -320,7 +574,10 @@ def check_pyint_environment(
def add(name: str, ok: bool, detail: str = "", skipped: bool = False) -> None:
checks.append(PyintCheck(name=name, ok=ok, detail=detail, skipped=skipped))
rc, out, err = run_wsl_command("echo pyint_alive", distro=distro_value, timeout=15)
def run_check(command: str, timeout: int = 30):
return run_wsl_exec(["bash", "-lc", command], distro=distro_value, timeout=timeout)
rc, out, err = run_check("echo pyint_alive", timeout=15)
wsl_ok = rc == 0 and "pyint_alive" in out
add("WSL distro", wsl_ok, out or err or distro_value)
@@ -331,17 +588,15 @@ def check_pyint_environment(
message=f"WSL distro is unavailable: {distro_value}",
)
rc, out, err = run_wsl_command(
rc, out, err = run_check(
f"{quote_shell(python_value)} --version",
distro=distro_value,
timeout=15,
)
add("WSL Python", rc == 0, out or err or python_value)
if pyint_home_wsl:
rc, out, err = run_wsl_command(
rc, out, err = run_check(
f"test -d {quote_shell(pyint_home_wsl)} && echo ok",
distro=distro_value,
timeout=10,
)
add("PYINT_HOME", rc == 0 and "ok" in out, pyint_home_wsl or err)
@@ -349,9 +604,8 @@ def check_pyint_environment(
add("PYINT_HOME", False, "PYINT_HOME is empty")
if pyint_app_wsl:
rc, out, err = run_wsl_command(
rc, out, err = run_check(
f"test -f {quote_shell(pyint_app_wsl)} && echo ok",
distro=distro_value,
timeout=10,
)
add("pyintApp.py", rc == 0 and "ok" in out, pyint_app_wsl or err)
@@ -367,17 +621,15 @@ def check_pyint_environment(
if not path_text:
add(name, False, f"{name} is empty")
continue
rc, out, err = run_wsl_command(
rc, out, err = run_check(
f"test -d {quote_shell(path_text)} && test -w {quote_shell(path_text)} && echo ok",
distro=distro_value,
timeout=10,
)
add(name, rc == 0 and "ok" in out, path_text or err)
if gamma_env_wsl:
rc, out, err = run_wsl_command(
rc, out, err = run_check(
f"test -f {quote_shell(gamma_env_wsl)} && echo ok",
distro=distro_value,
timeout=10,
)
add("GAMMA env script", rc == 0 and "ok" in out, gamma_env_wsl or err)
@@ -385,16 +637,16 @@ def check_pyint_environment(
add("GAMMA env script", True, "Not configured; using current PATH", skipped=True)
gamma_prefix = _gamma_prefix(gamma_env_wsl)
pyint_prefix = _pyint_path_prefix(pyint_home_wsl)
for name, command_name in (
("GAMMA LT1 import", "LT1_import_SLC_from_zipfiles1"),
("GAMMA geocode_back", "geocode_back"),
):
rc, out, err = run_wsl_command(
gamma_prefix + f"command -v {quote_shell(command_name)}",
distro=distro_value,
rc, out, err = run_check(
gamma_prefix + pyint_prefix + f"command -v {quote_shell(command_name)} >/dev/null 2>&1 && echo ok",
timeout=10,
)
add(name, rc == 0 and bool(out.strip()), out or err or command_name)
add(name, rc == 0 and "ok" in out, out or err or command_name)
helper_path = (
Path(__file__).resolve().parent.parent
@@ -412,7 +664,7 @@ def check_pyint_environment(
+ gamma_prefix
+ f"{quote_shell(python_value)} {quote_shell(pyint_app_wsl)} -h >/dev/null"
)
rc, out, err = run_wsl_command(smoke_cmd, distro=distro_value, timeout=60)
rc, out, err = run_check(smoke_cmd, timeout=60)
add("PyINT smoke test", rc == 0, out or err or "pyintApp.py -h")
else:
add("PyINT smoke test", True, "Skipped", skipped=True)
+103 -1
View File
@@ -11,8 +11,10 @@ from __future__ import annotations
import os
import shutil
import subprocess
import threading
import time
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional, Sequence, Tuple
from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple
# ---------------------------------------------------------------------------
@@ -101,6 +103,79 @@ def _run_windows_command(
)
def _run_windows_command_stream(
args: List[str],
timeout: int = 30,
env: Optional[Dict[str, str]] = None,
stdout_callback: Optional[Callable[[str], None]] = None,
stderr_callback: Optional[Callable[[str], None]] = None,
) -> Tuple[int, str, str]:
proc_env = os.environ.copy()
if env:
proc_env.update(env)
stdout_parts: List[str] = []
stderr_parts: List[str] = []
try:
proc = subprocess.Popen(
args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=False,
env=proc_env,
)
except FileNotFoundError:
return -2, "", "wsl.exe not found"
except Exception as exc:
return -3, "", str(exc)
def _drain(stream: Any, parts: List[str], callback: Optional[Callable[[str], None]]) -> None:
for raw_line in iter(stream.readline, b""):
text = _decode_subprocess_output(raw_line)
if not text:
continue
parts.append(text)
if callback:
try:
callback(text)
except Exception:
pass
threads = [
threading.Thread(target=_drain, args=(proc.stdout, stdout_parts, stdout_callback), daemon=True),
threading.Thread(target=_drain, args=(proc.stderr, stderr_parts, stderr_callback), daemon=True),
]
for thread in threads:
thread.start()
timed_out = False
deadline = time.monotonic() + max(1, int(timeout or 30))
while proc.poll() is None:
if time.monotonic() >= deadline:
timed_out = True
try:
proc.kill()
except Exception:
pass
break
time.sleep(0.2)
try:
returncode = proc.wait(timeout=10)
except subprocess.TimeoutExpired:
returncode = -1
for thread in threads:
thread.join(timeout=5)
stdout = "\n".join(stdout_parts)
stderr = "\n".join(stderr_parts)
if timed_out:
timeout_text = f"command timed out ({timeout}s)"
stderr = f"{stderr}\n{timeout_text}".strip()
return -1, stdout, stderr
return returncode, stdout, stderr
def run_wsl_command(
cmd: str,
distro: Optional[str] = None,
@@ -127,6 +202,33 @@ def run_wsl_command(
return -3, "", str(exc)
def run_wsl_command_stream(
cmd: str,
distro: Optional[str] = None,
timeout: int = 30,
env: Optional[Dict[str, str]] = None,
stdout_callback: Optional[Callable[[str], None]] = None,
stderr_callback: Optional[Callable[[str], None]] = None,
) -> Tuple[int, str, str]:
"""Run a WSL bash command and stream decoded stdout/stderr lines to callbacks."""
wsl_exe = _find_wsl_executable()
if not wsl_exe:
return -2, "", "wsl.exe not found"
wsl_args = [wsl_exe]
if distro:
wsl_args += ["-d", distro]
wsl_args += ["bash", "-lc", cmd]
return _run_windows_command_stream(
wsl_args,
timeout=timeout,
env=env,
stdout_callback=stdout_callback,
stderr_callback=stderr_callback,
)
def run_wsl_exec(
argv: Sequence[str],
distro: Optional[str] = None,
+1 -1
View File
@@ -60,7 +60,7 @@ for _gamma_dir in \
_gamma_profile_prepend_path "${_gamma_dir}"
done
_gamma_profile_repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
_gamma_profile_repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
_gamma_profile_pyint_dir="${_gamma_profile_repo_root}/third_party/PyINT/pyint"
_gamma_profile_prepend_path "${_gamma_profile_pyint_dir}"
-181
View File
@@ -1,181 +0,0 @@
# ISCE2 SBAS Time-Series Experiments
This folder is the isolated sandbox for validating the SBAS/time-series route before wiring it into production code.
## Purpose
- verify LT-1 stack compatibility with ISCE2 stack tooling
- validate MintPy input and output expectations
- record runnable command templates
- collect conclusions that should later be promoted into `docs/` or backend services
- maintain the current SBAS product contract before backend embedding
## Structure
- `notes/`
- experiment notes, pitfalls, conclusions
- `configs/`
- sample templates, parameter files, manifest drafts
- `scripts/`
- throwaway or semi-stable experiment scripts
- `scratch/`
- local temporary workspace placeholder only
## Rules
- do not commit raw SAR scenes
- do not commit large DEM or orbit datasets
- do not commit large intermediate outputs
- keep production code changes out of this folder unless the goal is to prototype file layout or commands
- when an experiment becomes stable, move the result back into the formal backend or `docs/`
## Suggested first experiments
1. Check whether LT-1/LUTAN1 scenes can be ingested by official ISCE2 stack tooling.
2. Determine whether MintPy can consume the generated stack layout without extra conversion.
3. Record the minimal command chain needed for one small AOI smoke test.
4. Draft the first `psinsar` manifest and product directory convention.
## Current scripts
- `backend/app/isce2_pipeline/lt1_input_resolver.py`
- shared LT-1 input helper reused by D-InSAR and stack experiments
- centralizes DEM resolution, orbit-pool resolution, and LT-1 precise-orbit XML generation
- `scripts/scan_lt1_stack_candidates.py`
- scan LT-1 single-scene folders and build a stack candidate manifest
- `scripts/build_lt1_stack_prep.py`
- consume a selected stack manifest
- resolve orbit pool and DEM
- generate a dry-run `scratch/...` workspace for `stripmapStack --nofocus`
- write the current adapter contract and a preflight run script
- `scripts/materialize_lt1_stack_scenes.py`
- consume `scratch/.../stack_input_manifest.json`
- materialize one or more LT-1 acquisitions into `SLC/YYYYMMDD/`
- write `YYYYMMDD.slc`, `YYYYMMDD.slc.xml`, and `data`
- `scripts/install_isce2_stack_runtime_ubuntu2404.sh`
- install known WSL `isce2` runtime dependencies
- default pip mirror is Tsinghua
- `scripts/install_mintpy_runtime_ubuntu2404.sh`
- create or update a dedicated WSL `mintpy` conda environment
- default conda channels use Tsinghua mirror URLs
- `scripts/install_mintpy_into_cloned_isce2_env_ubuntu2404.sh`
- clone the working WSL `isce2` env into a dedicated unified-env target such as `isce2_mintpy`
- install MintPy into that clone with Tsinghua mirror channels
- `scripts/run_mintpy_unified_env_ubuntu2404.sh`
- run MintPy commands directly inside the cloned unified env
- `scripts/run_mintpy_sbas_unified_env_smoketest_ubuntu2404.sh`
- run the current LT-1 SBAS smoke test in the cloned unified env
- reuses the same strict-mask and patched-launcher helpers as the bridge route
- `scripts/run_mintpy_with_isce_ubuntu2404.sh`
- run MintPy commands in the dedicated `mintpy` env
- bridge only the top-level WSL `isce` package into the `mintpy` env
- avoids pulling conflicting `h5py` / numeric packages from the `isce2` env
- `scripts/create_mintpy_all_ifgram_mask.py`
- build a strict `maskAllValid.h5` from `inputs/ifgramStack.h5`
- keep only pixels valid in all interferograms before SBAS inversion
- `scripts/run_smallbaselineApp_patched.py`
- repo-local launcher for MintPy `smallbaselineApp`
- applies a local workaround for the MintPy `1.6.2` single-pixel partial-network inversion bug
- `scripts/run_mintpy_sbas_smoketest_ubuntu2404.sh`
- run the current LT-1 SBAS smoke test in three steps:
- `load_data`
- strict-mask generation
- `modify_network -> velocity`
- `scripts/export_mintpy_publish_products_ubuntu2404.sh`
- geocode MintPy outputs into latitude/longitude grids
- convert selected outputs into GeoTIFF
- build a publish-style bundle with `manifest.json`, `assets/`, `preview/`, and `metadata/`
- defaults to the bridge runner but now also supports `MINTPY_RUNNER=...` override
- `scripts/export_mintpy_publish_products_unified_env_ubuntu2404.sh`
- run the same publish export logic through the cloned unified env runner
- `scripts/export_conda_env_snapshot_ubuntu2404.sh`
- export one WSL conda environment into reproducible snapshot files
- writes `no_builds.yml`, `explicit.txt`, `conda_list.txt`, and `runtime_versions.txt`
- `scripts/export_phase4_env_snapshots_ubuntu2404.sh`
- export both `isce2` and `isce2_mintpy_v1` snapshots for the current phase-4 record
- `scripts/build_mintpy_publish_bundle.py`
- generate `preview/velocity_preview.png`
- summarize quality masks
- write the publish-style `manifest.json`
- `scripts/prepare_lt1_stack_dem.py`
- clip a stack-local DEM window from the source DEM
- store it under `scratch/.../inputs/dem/`
- avoid global-DEM bbox problems during `createWaterMask`
- `scripts/run_generated_stack_runfile_ubuntu2404.sh`
- execute one generated `run_XX_*` file under `Ubuntu-24.04`
- standardize `PATH`, `PYTHONPATH`, and log output
- `scripts/create_synthetic_watermask.py`
- create a local all-land `geom_reference/waterMask.rdr`
- used only when `run_01_reference` cannot download `SWBD` from Earthdata
## Reproducible Flow
1. Generate or refresh the sample stack workspace with `build_lt1_stack_prep.py`.
2. Materialize the LT-1 acquisitions with `materialize_lt1_stack_scenes.py`.
3. Clip a stack-local DEM window with `prepare_lt1_stack_dem.py`.
4. In `Ubuntu-24.04`, run `scripts/install_isce2_stack_runtime_ubuntu2404.sh`.
5. Regenerate the stack workspace so it picks the local DEM.
6. Run the generated wrapper through the validated chain:
- `scripts/run_generated_stack_runfile_ubuntu2404.sh <scratch_root_wsl> run_01_reference`
- `scripts/run_generated_stack_runfile_ubuntu2404.sh <scratch_root_wsl> run_02_focus_split`
- `scripts/run_generated_stack_runfile_ubuntu2404.sh <scratch_root_wsl> run_03_geo2rdr_coarseResamp`
- `scripts/run_generated_stack_runfile_ubuntu2404.sh <scratch_root_wsl> run_04_refineSecondaryTiming`
- `scripts/run_generated_stack_runfile_ubuntu2404.sh <scratch_root_wsl> run_05_invertMisreg`
- `scripts/run_generated_stack_runfile_ubuntu2404.sh <scratch_root_wsl> run_06_fineResamp`
- `scripts/run_generated_stack_runfile_ubuntu2404.sh <scratch_root_wsl> run_07_grid_baseline`
- if Earthdata credentials are missing, the wrapper now auto-generates a synthetic all-land `waterMask.rdr` from `shadowMask.rdr` and treats `run_01_reference` as recovered
7. In `Ubuntu-24.04`, run `scripts/install_mintpy_runtime_ubuntu2404.sh` before the first MintPy validation.
8. In `Ubuntu-24.04`, run `scripts/run_mintpy_with_isce_ubuntu2404.sh prep_isce.py ...` for the first MintPy metadata preparation on stripmapStack outputs.
9. In `Ubuntu-24.04`, run:
- `scripts/run_mintpy_sbas_smoketest_ubuntu2404.sh <cfg_wsl> <mintpy_work_dir_wsl>`
10. Review:
- `notes/PHASE2_MINTPY_SBAS_SMOKETEST.md`
11. In `Ubuntu-24.04`, run:
- `scripts/export_mintpy_publish_products_ubuntu2404.sh <mintpy_work_dir_wsl> <publish_dir_wsl>`
12. Review:
- `notes/PHASE3_PUBLISH_EXPORT_SMOKETEST.md`
13. Record findings under `notes/` before promoting anything into backend code.
## Product Contract
Formal product guidance now lives in:
- `docs/ISCE2_SBAS_PRODUCT_SPEC.md`
Current practical rule:
- runtime success is proven by radar-coordinate `timeseries.h5` and `velocity.h5`
- publish success is proven by a geocoded bundle under `publish/.../`
- system embedding should treat `manifest.json` as the publish entrypoint
- true time-series capability should be judged against `assets/geo_timeseries.h5`, not only `assets/velocity.tif`
## Unified-Environment Track
The bridge route remains the validated baseline.
A separate unified-environment experiment is now staged in:
- `notes/PHASE4_UNIFIED_ENV_EXPERIMENT.md`
- `notes/PHASE4_UNIFIED_ENV_DECISION.md`
Current practical rule:
- the unified env has now completed the same SBAS smoke test chain and publish export in experiment scope
- the unified env is now the preferred SBAS experiment runtime
- do not replace or mutate the current `isce2` env used by D-InSAR production
- do not replace the bridge route as the default fallback until the comparison note is fully written
- current successful unified env:
- `/home/administrator/miniconda3/envs/isce2_mintpy_v1`
- current successful unified SBAS work dir:
- `scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas_unified_v1`
- current successful unified publish dir:
- `scratch/lt1a_strip1_hh_descending_e123p3_n46p1/publish/mintpy_sbas_unified_v1`
## Current Offline Assumption
- the LT-1 sample experiment already has local SAR scenes, local orbit XML, and a local DEM
- the remaining optional online dependency is the `SWBD` water mask normally fetched by `createWaterMask.py`
- for this experiment track, do not download `SWBD`
- continue with the local synthetic all-land `waterMask.rdr` fallback until the stack route is otherwise stable
- current validated MintPy boundary is radar-coordinate `timeseries.h5` plus `velocity.h5`
- current validated publish boundary is a geocoded experiment bundle under `publish/.../`
@@ -1 +0,0 @@
@@ -1,227 +0,0 @@
# packages in environment at /home/administrator/miniconda3/envs/isce2:
#
# Name Version Build Channel
_openmp_mutex 4.5 20_gnu conda-forge
aom 3.9.1 hac33072_0 conda-forge
attr 2.5.2 h39aace5_0 conda-forge
aws-c-auth 0.9.3 hef928c7_0 conda-forge
aws-c-cal 0.9.13 h2c9d079_1 conda-forge
aws-c-common 0.12.6 hb03c661_0 conda-forge
aws-c-compression 0.3.1 h8b1a151_9 conda-forge
aws-c-event-stream 0.5.7 h28f887f_1 conda-forge
aws-c-http 0.10.7 ha8fc4e3_5 conda-forge
aws-c-io 0.23.3 hdaf4b65_5 conda-forge
aws-c-mqtt 0.13.3 hc63082f_11 conda-forge
aws-c-s3 0.11.3 h06ab39a_1 conda-forge
aws-c-sdkutils 0.2.4 h8b1a151_4 conda-forge
aws-checksums 0.2.7 h8b1a151_5 conda-forge
aws-crt-cpp 0.35.2 h8824e59_6 conda-forge
aws-sdk-cpp 1.11.606 hf38915e_9 conda-forge
azure-core-cpp 1.16.1 h3a458e0_0 conda-forge
azure-identity-cpp 1.13.2 h3a5f585_1 conda-forge
azure-storage-blobs-cpp 12.15.0 h2a74896_1 conda-forge
azure-storage-common-cpp 12.11.0 h3d7a050_1 conda-forge
azure-storage-files-datalake-cpp 12.13.0 hf38f1be_1 conda-forge
backports.zstd 1.3.0 py311h6b1f9c4_0 conda-forge
blosc 1.21.6 he440d0b_1 conda-forge
brotli-python 1.2.0 py311h66f275b_1 conda-forge
brunsli 0.1 hd1e3526_2 conda-forge
bzip2 1.0.8 hda65f42_9 conda-forge
c-ares 1.34.6 hb03c661_0 conda-forge
c-blosc2 2.23.1 hc31b594_0 conda-forge
ca-certificates 2026.2.25 hbd8a1cb_0 conda-forge
cached-property 1.5.2 hd8ed1ab_1 conda-forge
cached_property 1.5.2 pyha770c72_1 conda-forge
cairo 1.18.4 he90730b_1 conda-forge
capnproto 1.2.0 hfc315d8_0 conda-forge
certifi 2026.2.25 pyhd8ed1ab_0 conda-forge
cfitsio 4.6.3 ha0b56bc_0 conda-forge
charls 2.4.3 hecca717_0 conda-forge
charset-normalizer 3.4.5 pyhd8ed1ab_0 conda-forge
contourpy 1.3.3 pypi_0 pypi
cycler 0.12.1 pypi_0 pypi
cyrus-sasl 2.1.28 hac629b4_1 conda-forge
dav1d 1.2.1 hd590300_0 conda-forge
fftw 3.3.10 nompi_h3b011a4_112 conda-forge
fmt 12.0.0 h2b0788b_0 conda-forge
font-ttf-dejavu-sans-mono 2.37 hab24e00_0 conda-forge
font-ttf-inconsolata 3.000 h77eed37_0 conda-forge
font-ttf-source-code-pro 2.038 h77eed37_0 conda-forge
font-ttf-ubuntu 0.83 h77eed37_3 conda-forge
fontconfig 2.17.1 h27c8c51_0 conda-forge
fonts-conda-ecosystem 1 0 conda-forge
fonts-conda-forge 1 hc364b38_1 conda-forge
fonttools 4.62.1 pypi_0 pypi
freetype 2.14.2 ha770c72_0 conda-forge
freexl 2.0.0 h9dce30a_2 conda-forge
gdal 3.10.3 py311h34ccccb_27 conda-forge
geos 3.14.1 h480dda7_0 conda-forge
geotiff 1.7.4 h1000f5c_4 conda-forge
giflib 5.2.2 hd590300_0 conda-forge
h2 4.3.0 pyhcf101f3_0 conda-forge
h5py 3.15.1 nompi_py311h0b2f468_101 conda-forge
hdf4 4.2.15 h2a13503_7 conda-forge
hdf5 1.14.6 nompi_h19486de_106 conda-forge
hpack 4.1.0 pyhd8ed1ab_0 conda-forge
hyperframe 6.1.0 pyhd8ed1ab_0 conda-forge
icu 78.2 h33c6efd_0 conda-forge
idna 3.11 pyhd8ed1ab_0 conda-forge
imagecodecs 2026.3.6 py311h9837d23_1 conda-forge
imageio 2.37.0 pyhfb79c49_0 conda-forge
isce2 2.6.4 py311h916084f_2 conda-forge
json-c 0.18 h6688a6e_0 conda-forge
jxrlib 1.1 hd590300_3 conda-forge
kealib 1.6.2 hb2f3951_2 conda-forge
keyutils 1.6.3 hb9d3cd8_0 conda-forge
kiwisolver 1.5.0 pypi_0 pypi
krb5 1.22.2 ha1258a1_0 conda-forge
lazy-loader 0.5 pyhd8ed1ab_0 conda-forge
lcms2 2.18 h0c24ade_0 conda-forge
ld_impl_linux-64 2.45.1 default_hbd61a6d_101 conda-forge
lerc 4.1.0 hdb68285_0 conda-forge
libabseil 20250512.1 cxx17_hba17884_0 conda-forge
libacl 2.3.2 h0f662aa_0 conda-forge
libaec 1.1.5 h088129d_0 conda-forge
libarchive 3.8.5 gpl_hc2c16d8_100 conda-forge
libavif16 1.4.0 hcfa2d63_0 conda-forge
libblas 3.11.0 5_h4a7cf45_openblas conda-forge
libbrotlicommon 1.2.0 hb03c661_1 conda-forge
libbrotlidec 1.2.0 hb03c661_1 conda-forge
libbrotlienc 1.2.0 hb03c661_1 conda-forge
libcblas 3.11.0 5_h0358290_openblas conda-forge
libcrc32c 1.1.2 h9c3ff4c_0 conda-forge
libcurl 8.18.0 hcf29cc6_1 conda-forge
libdeflate 1.25 h17f619e_0 conda-forge
libedit 3.1.20250104 pl5321h7949ede_0 conda-forge
libev 4.33 hd590300_2 conda-forge
libexpat 2.7.4 hecca717_0 conda-forge
libffi 3.5.2 h3435931_0 conda-forge
libfreetype 2.14.2 ha770c72_0 conda-forge
libfreetype6 2.14.2 h73754d4_0 conda-forge
libgcc 15.2.0 he0feb66_18 conda-forge
libgcc-ng 15.2.0 h69a702a_18 conda-forge
libgdal 3.10.3 h3b705f5_27 conda-forge
libgdal-core 3.10.3 h1f481a6_27 conda-forge
libgdal-fits 3.10.3 hec9d828_27 conda-forge
libgdal-grib 3.10.3 hb20eef8_27 conda-forge
libgdal-hdf4 3.10.3 ha810028_27 conda-forge
libgdal-hdf5 3.10.3 h966a9c2_27 conda-forge
libgdal-jp2openjpeg 3.10.3 hdd07572_27 conda-forge
libgdal-kea 3.10.3 h2bf108d_27 conda-forge
libgdal-netcdf 3.10.3 ha526aae_27 conda-forge
libgdal-pdf 3.10.3 h20efda7_27 conda-forge
libgdal-pg 3.10.3 h55c2262_27 conda-forge
libgdal-postgisraster 3.10.3 h55c2262_27 conda-forge
libgdal-tiledb 3.10.3 h6c35068_27 conda-forge
libgdal-xls 3.10.3 hdee084c_27 conda-forge
libgfortran 15.2.0 h69a702a_18 conda-forge
libgfortran5 15.2.0 h68bc16d_18 conda-forge
libgl 1.7.0 ha4b6fd6_2 conda-forge
libglib 2.86.4 h6548e54_1 conda-forge
libglvnd 1.7.0 ha4b6fd6_2 conda-forge
libglx 1.7.0 ha4b6fd6_2 conda-forge
libgomp 15.2.0 he0feb66_18 conda-forge
libgoogle-cloud 2.39.0 hdb79228_0 conda-forge
libgoogle-cloud-storage 2.39.0 hdbdcf42_0 conda-forge
libgrpc 1.73.1 h3288cfb_1 conda-forge
libhwy 1.3.0 h4c17acf_1 conda-forge
libiconv 1.18 h3b78370_2 conda-forge
libjpeg-turbo 3.1.2 hb03c661_0 conda-forge
libjxl 0.11.2 ha09017c_0 conda-forge
libkml 1.3.0 haa4a5bd_1022 conda-forge
liblapack 3.11.0 5_h47877c9_openblas conda-forge
liblzma 5.8.2 hb03c661_0 conda-forge
libnetcdf 4.9.3 nompi_hbf2fc22_104 conda-forge
libnghttp2 1.67.0 had1ee68_0 conda-forge
libnsl 2.0.1 hb9d3cd8_1 conda-forge
libntlm 1.8 hb9d3cd8_0 conda-forge
libopenblas 0.3.30 pthreads_h94d23a6_4 conda-forge
libpng 1.6.55 h421ea60_0 conda-forge
libpq 18.3 h9abb657_0 conda-forge
libprotobuf 6.31.1 h49aed37_4 conda-forge
libre2-11 2025.11.05 h7b12aa8_0 conda-forge
librttopo 1.1.0 h46dd2a8_20 conda-forge
libspatialite 5.1.0 gpl_h2abfd87_119 conda-forge
libsqlite 3.52.0 hf4e2dac_0 conda-forge
libssh2 1.11.1 hcf80075_0 conda-forge
libstdcxx 15.2.0 h934c35e_18 conda-forge
libstdcxx-ng 15.2.0 hdf11a46_18 conda-forge
libtiff 4.7.1 h9d88235_1 conda-forge
liburing 2.14 hb700be7_0 conda-forge
libuuid 2.41.3 h5347b49_0 conda-forge
libwebp-base 1.6.0 hd42ef1d_0 conda-forge
libxcb 1.17.0 h8a09558_0 conda-forge
libxcrypt 4.4.36 hd590300_1 conda-forge
libxml2 2.15.2 he237659_0 conda-forge
libxml2-16 2.15.2 hca6bf5a_0 conda-forge
libxml2-devel 2.15.2 he237659_0 conda-forge
libxslt 1.1.43 h711ed8c_1 conda-forge
libzip 1.11.2 h6991a6a_0 conda-forge
libzlib 1.3.1 hb9d3cd8_2 conda-forge
libzopfli 1.0.3 h9c3ff4c_0 conda-forge
lz4-c 1.10.0 h5888daf_1 conda-forge
lzo 2.10 h280c20c_1002 conda-forge
matplotlib 3.10.8 pypi_0 pypi
minizip 4.0.10 h05a5f5f_0 conda-forge
ncurses 6.5 h2d0b736_3 conda-forge
networkx 3.6.1 pyhcf101f3_0 conda-forge
nspr 4.38 h29cc59b_0 conda-forge
nss 3.118 h445c969_0 conda-forge
numpy 1.26.4 py311h64a7726_0 conda-forge
openjpeg 2.5.4 h55fea9a_0 conda-forge
openjph 0.26.3 h8d634f6_0 conda-forge
openldap 2.6.10 hbde042b_1 conda-forge
openmotif 2.3.8 hf55c2fc_5 conda-forge
openssl 3.6.1 h35e630c_1 conda-forge
packaging 26.0 pyhcf101f3_0 conda-forge
pcre2 10.47 haa7fec5_0 conda-forge
pillow 12.1.1 py311hf88fc01_0 conda-forge
pip 26.0.1 pyh8b19718_0 conda-forge
pixman 0.46.4 h54a6638_1 conda-forge
poppler 25.07.0 h13eef12_1 conda-forge
poppler-data 0.4.12 hd8ed1ab_0 conda-forge
postgresql 18.3 h9d31465_0 conda-forge
proj 9.7.1 he0df7b0_3 conda-forge
pthread-stubs 0.4 hb9d3cd8_1002 conda-forge
pyparsing 3.3.2 pypi_0 pypi
pysocks 1.7.1 pyha55dd90_7 conda-forge
python 3.11.15 hd63d673_0_cpython conda-forge
python-dateutil 2.9.0.post0 pypi_0 pypi
python_abi 3.11 8_cp311 conda-forge
rav1e 0.8.1 h1fbca29_0 conda-forge
re2 2025.11.05 h5301d42_0 conda-forge
readline 8.3 h853b02a_0 conda-forge
requests 2.32.5 pyhcf101f3_1 conda-forge
s2n 1.6.2 he8a4886_1 conda-forge
scikit-image 0.26.0 np2py311h2a99c40_0 conda-forge
scipy 1.17.1 py311hbe70eeb_0 conda-forge
setuptools 82.0.1 pyh332efcf_0 conda-forge
six 1.17.0 pypi_0 pypi
snappy 1.2.2 h03e3b7b_1 conda-forge
spdlog 1.16.0 hffee6e0_1 conda-forge
sqlite 3.52.0 h04a0ce9_0 conda-forge
svt-av1 4.0.1 hecca717_0 conda-forge
tifffile 2026.3.3 pyhd8ed1ab_0 conda-forge
tiledb 2.29.2 h8821262_1 conda-forge
tk 8.6.13 noxft_h366c992_103 conda-forge
tzcode 2026a h280c20c_0 conda-forge
tzdata 2025c hc9c84f9_1 conda-forge
uriparser 0.9.8 hac33072_0 conda-forge
urllib3 2.6.3 pyhd8ed1ab_0 conda-forge
wheel 0.46.3 pyhd8ed1ab_0 conda-forge
xerces-c 3.3.0 hd9031aa_1 conda-forge
xorg-libice 1.1.2 hb9d3cd8_0 conda-forge
xorg-libsm 1.2.6 he73a12e_0 conda-forge
xorg-libx11 1.8.13 he1eb515_0 conda-forge
xorg-libxau 1.0.12 hb03c661_1 conda-forge
xorg-libxdmcp 1.1.5 hb03c661_1 conda-forge
xorg-libxext 1.3.7 hb03c661_0 conda-forge
xorg-libxft 2.3.9 h355ab9f_0 conda-forge
xorg-libxmu 1.3.1 hb03c661_0 conda-forge
xorg-libxp 1.0.4 hb03c661_0 conda-forge
xorg-libxrender 0.9.12 hb9d3cd8_0 conda-forge
xorg-libxt 1.3.1 hb9d3cd8_0 conda-forge
zfp 1.0.1 h909a3a2_5 conda-forge
zlib 1.3.1 hb9d3cd8_2 conda-forge
zlib-ng 2.3.3 hceb46e0_1 conda-forge
zstd 1.5.7 hb78ec9c_6 conda-forge
@@ -1,221 +0,0 @@
# This file may be used to create an environment using:
# $ conda create --name <env> --file <this file>
# platform: linux-64
# created-by: conda 26.1.1
@EXPLICIT
https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda
https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda
https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda
https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda
https://conda.anaconda.org/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda
https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda
https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda
https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda
https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.2-h39aace5_0.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.12.6-hb03c661_0.conda
https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda
https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda
https://conda.anaconda.org/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda
https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda
https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda
https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda
https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda
https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda
https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda
https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda
https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda
https://conda.anaconda.org/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda
https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda
https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda
https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda
https://conda.anaconda.org/conda-forge/linux-64/rav1e-0.8.1-h1fbca29_0.conda
https://conda.anaconda.org/conda-forge/linux-64/tzcode-2026a-h280c20c_0.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.13-h2c9d079_1.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.1-h8b1a151_9.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h8b1a151_4.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.7-h8b1a151_5.conda
https://conda.anaconda.org/conda-forge/linux-64/capnproto-1.2.0-hfc315d8_0.conda
https://conda.anaconda.org/conda-forge/linux-64/charls-2.4.3-hecca717_0.conda
https://conda.anaconda.org/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda
https://conda.anaconda.org/conda-forge/linux-64/fmt-12.0.0-h2b0788b_0.conda
https://conda.anaconda.org/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda
https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda
https://conda.anaconda.org/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda
https://conda.anaconda.org/conda-forge/linux-64/jxrlib-1.1-hd590300_3.conda
https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libacl-2.3.2-h0f662aa_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda
https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda
https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda
https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda
https://conda.anaconda.org/conda-forge/linux-64/libhwy-1.3.0-h4c17acf_1.conda
https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.55-h421ea60_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_18.conda
https://conda.anaconda.org/conda-forge/linux-64/liburing-2.14-hb700be7_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda
https://conda.anaconda.org/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda
https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda
https://conda.anaconda.org/conda-forge/linux-64/nspr-4.38-h29cc59b_0.conda
https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda
https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda
https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda
https://conda.anaconda.org/conda-forge/linux-64/s2n-1.6.2-he8a4886_1.conda
https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda
https://conda.anaconda.org/conda-forge/linux-64/svt-av1-4.0.1-hecca717_0.conda
https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda
https://conda.anaconda.org/conda-forge/linux-64/zfp-1.0.1-h909a3a2_5.conda
https://conda.anaconda.org/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda
https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda
https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda
https://conda.anaconda.org/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.23.3-hdaf4b65_5.conda
https://conda.anaconda.org/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda
https://conda.anaconda.org/conda-forge/linux-64/brunsli-0.1-hd1e3526_2.conda
https://conda.anaconda.org/conda-forge/linux-64/c-blosc2-2.23.1-hc31b594_0.conda
https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.10-nompi_h3b011a4_112.conda
https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda
https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda
https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_101.conda
https://conda.anaconda.org/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.2-h73754d4_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.4-h6548e54_1.conda
https://conda.anaconda.org/conda-forge/linux-64/libjxl-0.11.2-ha09017c_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda
https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_4.conda
https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda
https://conda.anaconda.org/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda
https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda
https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.2-hca6bf5a_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libzopfli-1.0.3-h9c3ff4c_0.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/minizip-4.0.10-h05a5f5f_0.conda
https://conda.anaconda.org/conda-forge/linux-64/spdlog-1.16.0-hffee6e0_1.conda
https://conda.anaconda.org/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda
https://conda.anaconda.org/conda-forge/linux-64/xerces-c-3.3.0-hd9031aa_1.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-c-event-stream-0.5.7-h28f887f_1.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.10.7-ha8fc4e3_5.conda
https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda
https://conda.anaconda.org/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda
https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.18-h0c24ade_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libavif16-1.4.0-hcfa2d63_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda
https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.18.0-hcf29cc6_1.conda
https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.2-ha770c72_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda
https://conda.anaconda.org/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1022.conda
https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.2-he237659_0.conda
https://conda.anaconda.org/conda-forge/linux-64/nss-3.118-h445c969_0.conda
https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda
https://conda.anaconda.org/conda-forge/linux-64/openjph-0.26.3-h8d634f6_0.conda
https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda
https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_0.conda
https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.52.0-h04a0ce9_0.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxt-1.3.1-hb9d3cd8_0.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.9.3-hef928c7_0.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-c-mqtt-0.13.3-hc63082f_11.conda
https://conda.anaconda.org/conda-forge/linux-64/azure-core-cpp-1.16.1-h3a458e0_0.conda
https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.3.0-py311h6b1f9c4_0.conda
https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py311h66f275b_1.conda
https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2
https://conda.anaconda.org/conda-forge/noarch/certifi-2026.2.25-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/linux-64/cfitsio-4.6.3-ha0b56bc_0.conda
https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.5-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.17.1-h27c8c51_0.conda
https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.2-ha770c72_0.conda
https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h19486de_106.conda
https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libarchive-3.8.5-gpl_hc2c16d8_100.conda
https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda
https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda
https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.73.1-h3288cfb_1.conda
https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda
https://conda.anaconda.org/conda-forge/linux-64/libxml2-devel-2.15.2-he237659_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda
https://conda.anaconda.org/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda
https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-hbde042b_1.conda
https://conda.anaconda.org/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda
https://conda.anaconda.org/conda-forge/linux-64/pillow-12.1.1-py311hf88fc01_0.conda
https://conda.anaconda.org/conda-forge/linux-64/proj-9.7.1-he0df7b0_3.conda
https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda
https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxmu-1.3.1-hb03c661_0.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxp-1.0.4-hb03c661_0.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.11.3-h06ab39a_1.conda
https://conda.anaconda.org/conda-forge/linux-64/azure-identity-cpp-1.13.2-h3a5f585_1.conda
https://conda.anaconda.org/conda-forge/linux-64/azure-storage-common-cpp-12.11.0-h3d7a050_1.conda
https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2
https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda
https://conda.anaconda.org/conda-forge/linux-64/geotiff-1.7.4-h1000f5c_4.conda
https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda
https://conda.anaconda.org/conda-forge/linux-64/kealib-1.6.2-hb2f3951_2.conda
https://conda.anaconda.org/conda-forge/noarch/lazy-loader-0.5-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-2.39.0-hdb79228_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.9.3-nompi_hbf2fc22_104.conda
https://conda.anaconda.org/conda-forge/linux-64/libpq-18.3-h9abb657_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libspatialite-5.1.0-gpl_h2abfd87_119.conda
https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py311h64a7726_0.conda
https://conda.anaconda.org/conda-forge/noarch/wheel-0.46.3-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/linux-64/xorg-libxft-2.3.9-h355ab9f_0.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-crt-cpp-0.35.2-h8824e59_6.conda
https://conda.anaconda.org/conda-forge/linux-64/azure-storage-blobs-cpp-12.15.0-h2a74896_1.conda
https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py311h0b2f468_101.conda
https://conda.anaconda.org/conda-forge/linux-64/imagecodecs-2026.3.6-py311h9837d23_1.conda
https://conda.anaconda.org/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-core-3.10.3-h1f481a6_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_0.conda
https://conda.anaconda.org/conda-forge/linux-64/openmotif-2.3.8-hf55c2fc_5.conda
https://conda.anaconda.org/conda-forge/noarch/pip-26.0.1-pyh8b19718_0.conda
https://conda.anaconda.org/conda-forge/linux-64/poppler-25.07.0-h13eef12_1.conda
https://conda.anaconda.org/conda-forge/linux-64/postgresql-18.3-h9d31465_0.conda
https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py311hbe70eeb_0.conda
https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/linux-64/aws-sdk-cpp-1.11.606-hf38915e_9.conda
https://conda.anaconda.org/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.13.0-hf38f1be_1.conda
https://conda.anaconda.org/conda-forge/linux-64/gdal-3.10.3-py311h34ccccb_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-fits-3.10.3-hec9d828_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-grib-3.10.3-hb20eef8_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-hdf4-3.10.3-ha810028_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-hdf5-3.10.3-h966a9c2_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-jp2openjpeg-3.10.3-hdd07572_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-pdf-3.10.3-h20efda7_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-pg-3.10.3-h55c2262_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-postgisraster-3.10.3-h55c2262_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-xls-3.10.3-hdee084c_27.conda
https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda
https://conda.anaconda.org/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-kea-3.10.3-h2bf108d_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-netcdf-3.10.3-ha526aae_27.conda
https://conda.anaconda.org/conda-forge/linux-64/scikit-image-0.26.0-np2py311h2a99c40_0.conda
https://conda.anaconda.org/conda-forge/linux-64/tiledb-2.29.2-h8821262_1.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-tiledb-3.10.3-h6c35068_27.conda
https://conda.anaconda.org/conda-forge/linux-64/libgdal-3.10.3-h3b705f5_27.conda
https://conda.anaconda.org/conda-forge/linux-64/isce2-2.6.4-py311h916084f_2.conda
@@ -1,231 +0,0 @@
name: isce2
channels:
- conda-forge
- defaults
dependencies:
- _openmp_mutex=4.5
- aom=3.9.1
- attr=2.5.2
- aws-c-auth=0.9.3
- aws-c-cal=0.9.13
- aws-c-common=0.12.6
- aws-c-compression=0.3.1
- aws-c-event-stream=0.5.7
- aws-c-http=0.10.7
- aws-c-io=0.23.3
- aws-c-mqtt=0.13.3
- aws-c-s3=0.11.3
- aws-c-sdkutils=0.2.4
- aws-checksums=0.2.7
- aws-crt-cpp=0.35.2
- aws-sdk-cpp=1.11.606
- azure-core-cpp=1.16.1
- azure-identity-cpp=1.13.2
- azure-storage-blobs-cpp=12.15.0
- azure-storage-common-cpp=12.11.0
- azure-storage-files-datalake-cpp=12.13.0
- backports.zstd=1.3.0
- blosc=1.21.6
- brotli-python=1.2.0
- brunsli=0.1
- bzip2=1.0.8
- c-ares=1.34.6
- c-blosc2=2.23.1
- ca-certificates=2026.2.25
- cached-property=1.5.2
- cached_property=1.5.2
- cairo=1.18.4
- capnproto=1.2.0
- certifi=2026.2.25
- cfitsio=4.6.3
- charls=2.4.3
- charset-normalizer=3.4.5
- cyrus-sasl=2.1.28
- dav1d=1.2.1
- fftw=3.3.10
- fmt=12.0.0
- font-ttf-dejavu-sans-mono=2.37
- font-ttf-inconsolata=3.000
- font-ttf-source-code-pro=2.038
- font-ttf-ubuntu=0.83
- fontconfig=2.17.1
- fonts-conda-ecosystem=1
- fonts-conda-forge=1
- freetype=2.14.2
- freexl=2.0.0
- gdal=3.10.3
- geos=3.14.1
- geotiff=1.7.4
- giflib=5.2.2
- h2=4.3.0
- h5py=3.15.1
- hdf4=4.2.15
- hdf5=1.14.6
- hpack=4.1.0
- hyperframe=6.1.0
- icu=78.2
- idna=3.11
- imagecodecs=2026.3.6
- imageio=2.37.0
- isce2=2.6.4
- json-c=0.18
- jxrlib=1.1
- kealib=1.6.2
- keyutils=1.6.3
- krb5=1.22.2
- lazy-loader=0.5
- lcms2=2.18
- ld_impl_linux-64=2.45.1
- lerc=4.1.0
- libabseil=20250512.1
- libacl=2.3.2
- libaec=1.1.5
- libarchive=3.8.5
- libavif16=1.4.0
- libblas=3.11.0
- libbrotlicommon=1.2.0
- libbrotlidec=1.2.0
- libbrotlienc=1.2.0
- libcblas=3.11.0
- libcrc32c=1.1.2
- libcurl=8.18.0
- libdeflate=1.25
- libedit=3.1.20250104
- libev=4.33
- libexpat=2.7.4
- libffi=3.5.2
- libfreetype=2.14.2
- libfreetype6=2.14.2
- libgcc=15.2.0
- libgcc-ng=15.2.0
- libgdal=3.10.3
- libgdal-core=3.10.3
- libgdal-fits=3.10.3
- libgdal-grib=3.10.3
- libgdal-hdf4=3.10.3
- libgdal-hdf5=3.10.3
- libgdal-jp2openjpeg=3.10.3
- libgdal-kea=3.10.3
- libgdal-netcdf=3.10.3
- libgdal-pdf=3.10.3
- libgdal-pg=3.10.3
- libgdal-postgisraster=3.10.3
- libgdal-tiledb=3.10.3
- libgdal-xls=3.10.3
- libgfortran=15.2.0
- libgfortran5=15.2.0
- libgl=1.7.0
- libglib=2.86.4
- libglvnd=1.7.0
- libglx=1.7.0
- libgomp=15.2.0
- libgoogle-cloud=2.39.0
- libgoogle-cloud-storage=2.39.0
- libgrpc=1.73.1
- libhwy=1.3.0
- libiconv=1.18
- libjpeg-turbo=3.1.2
- libjxl=0.11.2
- libkml=1.3.0
- liblapack=3.11.0
- liblzma=5.8.2
- libnetcdf=4.9.3
- libnghttp2=1.67.0
- libnsl=2.0.1
- libntlm=1.8
- libopenblas=0.3.30
- libpng=1.6.55
- libpq=18.3
- libprotobuf=6.31.1
- libre2-11=2025.11.05
- librttopo=1.1.0
- libspatialite=5.1.0
- libsqlite=3.52.0
- libssh2=1.11.1
- libstdcxx=15.2.0
- libstdcxx-ng=15.2.0
- libtiff=4.7.1
- liburing=2.14
- libuuid=2.41.3
- libwebp-base=1.6.0
- libxcb=1.17.0
- libxcrypt=4.4.36
- libxml2=2.15.2
- libxml2-16=2.15.2
- libxml2-devel=2.15.2
- libxslt=1.1.43
- libzip=1.11.2
- libzlib=1.3.1
- libzopfli=1.0.3
- lz4-c=1.10.0
- lzo=2.10
- minizip=4.0.10
- ncurses=6.5
- networkx=3.6.1
- nspr=4.38
- nss=3.118
- numpy=1.26.4
- openjpeg=2.5.4
- openjph=0.26.3
- openldap=2.6.10
- openmotif=2.3.8
- openssl=3.6.1
- packaging=26.0
- pcre2=10.47
- pillow=12.1.1
- pip=26.0.1
- pixman=0.46.4
- poppler=25.07.0
- poppler-data=0.4.12
- postgresql=18.3
- proj=9.7.1
- pthread-stubs=0.4
- pysocks=1.7.1
- python=3.11.15
- python_abi=3.11
- rav1e=0.8.1
- re2=2025.11.05
- readline=8.3
- requests=2.32.5
- s2n=1.6.2
- scikit-image=0.26.0
- scipy=1.17.1
- setuptools=82.0.1
- snappy=1.2.2
- spdlog=1.16.0
- sqlite=3.52.0
- svt-av1=4.0.1
- tifffile=2026.3.3
- tiledb=2.29.2
- tk=8.6.13
- tzcode=2026a
- tzdata=2025c
- uriparser=0.9.8
- urllib3=2.6.3
- wheel=0.46.3
- xerces-c=3.3.0
- xorg-libice=1.1.2
- xorg-libsm=1.2.6
- xorg-libx11=1.8.13
- xorg-libxau=1.0.12
- xorg-libxdmcp=1.1.5
- xorg-libxext=1.3.7
- xorg-libxft=2.3.9
- xorg-libxmu=1.3.1
- xorg-libxp=1.0.4
- xorg-libxrender=0.9.12
- xorg-libxt=1.3.1
- zfp=1.0.1
- zlib=1.3.1
- zlib-ng=2.3.3
- zstd=1.5.7
- pip:
- contourpy==1.3.3
- cycler==0.12.1
- fonttools==4.62.1
- kiwisolver==1.5.0
- matplotlib==3.10.8
- pyparsing==3.3.2
- python-dateutil==2.9.0.post0
- six==1.17.0
prefix: /home/administrator/miniconda3/envs/isce2
@@ -1,12 +0,0 @@
python_executable=/home/administrator/miniconda3/envs/isce2/bin/python
python_version=3.11.15
isce_present=True
mintpy_present=False
h5py_present=True
isce_file=/home/administrator/miniconda3/envs/isce2/lib/python3.11/site-packages/isce/__init__.py
isce_version=2.6.3
2026-04-06 15:31:15,137 - h5py._conv - DEBUG - Creating converter from 7 to 5
2026-04-06 15:31:15,138 - h5py._conv - DEBUG - Creating converter from 5 to 7
2026-04-06 15:31:15,138 - h5py._conv - DEBUG - Creating converter from 7 to 5
2026-04-06 15:31:15,139 - h5py._conv - DEBUG - Creating converter from 5 to 7
h5py_version=3.15.1
@@ -1,350 +0,0 @@
# packages in environment at /home/administrator/miniconda3/envs/isce2_mintpy_v1:
#
# Name Version Build Channel
_openmp_mutex 4.5 20_gnu https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
_x86_64-microarch-level 3 3_skylake https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aom 3.9.1 hac33072_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
argcomplete 3.6.3 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
attr 2.5.2 hb03c661_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
attrs 26.1.0 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-c-auth 0.9.3 hef928c7_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-c-cal 0.9.13 h2c9d079_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-c-common 0.12.6 hb03c661_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-c-compression 0.3.1 h8b1a151_9 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-c-event-stream 0.5.7 h28f887f_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-c-http 0.10.7 ha8fc4e3_5 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-c-io 0.23.3 hdaf4b65_5 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-c-mqtt 0.13.3 hc63082f_11 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-c-s3 0.11.3 h06ab39a_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-c-sdkutils 0.2.4 h8b1a151_4 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-checksums 0.2.7 h8b1a151_5 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-crt-cpp 0.35.2 h8824e59_6 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
aws-sdk-cpp 1.11.606 hf38915e_9 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
azure-core-cpp 1.16.1 h3a458e0_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
azure-identity-cpp 1.13.2 h3a5f585_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
azure-storage-blobs-cpp 12.15.0 h2a74896_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
azure-storage-common-cpp 12.11.0 h3d7a050_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
azure-storage-files-datalake-cpp 12.13.0 hf38f1be_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
backports.zstd 1.3.0 py311h6b1f9c4_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
blosc 1.21.6 he440d0b_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
bokeh 3.9.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
brotli 1.2.0 hed03a55_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
brotli-bin 1.2.0 hb03c661_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
brotli-python 1.2.0 py311h66f275b_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
brunsli 0.1 hd1e3526_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
bzip2 1.0.8 hda65f42_9 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
c-ares 1.34.6 hb03c661_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
c-blosc2 2.23.1 hc31b594_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
ca-certificates 2026.2.25 hbd8a1cb_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cached-property 1.5.2 hd8ed1ab_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cached_property 1.5.2 pyha770c72_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cairo 1.18.4 he90730b_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
capnproto 1.2.0 hfc315d8_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cartopy 0.25.0 py311hed34c8f_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cdsapi 0.7.7 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
certifi 2026.2.25 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cffi 2.0.0 py311h03d9500_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cfgv 3.5.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cfitsio 4.6.3 ha0b56bc_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
charls 2.4.3 hecca717_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
charset-normalizer 3.4.5 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
click 8.3.1 pyh8f84b5b_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cloudpickle 3.1.2 pyhcf101f3_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
configobj 5.0.9 pyhd8ed1ab_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
contourpy 1.3.3 py311h724c32c_4 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cvxopt 1.3.3 py311h3d1f434_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cycler 0.12.1 pyhcf101f3_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cyrus-sasl 2.1.28 hac629b4_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
cytoolz 1.1.0 py311h49ec1c0_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
dask 2026.3.0 pyhc364b38_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
dask-core 2026.3.0 pyhc364b38_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
dask-jobqueue 0.9.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
dav1d 1.2.1 hd590300_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
distlib 0.4.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
distributed 2026.3.0 pyhc364b38_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
donfig 0.8.1.post1 pyhd8ed1ab_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
dsdp 5.8 hd9d9efa_1203 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
eccodes 2.46.0 h83bc92c_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
ecmwf-datastores-client 0.5.1 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
fftw 3.3.10 nompi_h3b011a4_112 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
filelock 3.25.2 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
fmt 12.0.0 h2b0788b_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
font-ttf-dejavu-sans-mono 2.37 hab24e00_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
font-ttf-inconsolata 3.000 h77eed37_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
font-ttf-source-code-pro 2.038 h77eed37_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
font-ttf-ubuntu 0.83 h77eed37_3 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
fontconfig 2.17.1 h27c8c51_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
fonts-conda-ecosystem 1 0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
fonts-conda-forge 1 hc364b38_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
fonttools 4.62.1 pypi_0 pypi
freeglut 3.2.2 ha6d2627_3 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
freetype 2.14.2 ha770c72_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
freexl 2.0.0 h9dce30a_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
fsspec 2026.3.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
gdal 3.10.3 py311h34ccccb_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
geos 3.14.1 h480dda7_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
geotiff 1.7.4 h1000f5c_4 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
gflags 2.2.2 h5888daf_1005 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
giflib 5.2.2 hd590300_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
glog 0.7.1 hbabe93e_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
glpk 5.0 h445213a_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
gmp 6.3.0 hac33072_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
gsl 2.7 he838d99_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
h2 4.3.0 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
h5py 3.15.1 nompi_py311h0b2f468_101 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
hdf4 4.2.15 h2a13503_7 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
hdf5 1.14.6 nompi_h19486de_106 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
hpack 4.1.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
hyperframe 6.1.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
icu 78.2 h33c6efd_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
identify 2.6.18 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
idna 3.11 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
imagecodecs 2026.3.6 py311h9837d23_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
imageio 2.37.0 pyhfb79c49_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
importlib-metadata 8.8.0 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
isce2 2.6.4 py311h916084f_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
jasper 4.2.9 h1588d4d_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
jinja2 3.1.6 pyhcf101f3_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
joblib 1.5.3 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
json-c 0.18 h6688a6e_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
jxrlib 1.1 hd590300_3 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
kealib 1.6.2 hb2f3951_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
keyutils 1.6.3 hb9d3cd8_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
kiwisolver 1.5.0 py311h724c32c_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
krb5 1.22.2 ha1258a1_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
lazy-loader 0.5 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
lcms2 2.18 h0c24ade_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
ld_impl_linux-64 2.45.1 default_hbd61a6d_102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
lerc 4.1.0 hdb68285_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libabseil 20250512.1 cxx17_hba17884_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libacl 2.3.2 h0f662aa_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libaec 1.1.5 h088129d_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libamd 3.3.3 haaf9dc3_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libarchive 3.8.5 gpl_hc2c16d8_100 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libarrow 22.0.0 h2937f24_4_cuda https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libarrow-acero 22.0.0 hb826db4_4_cuda https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libarrow-compute 22.0.0 h58682fd_4_cuda https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libarrow-dataset 22.0.0 hb826db4_4_cuda https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libarrow-substrait 22.0.0 h9d9f3f8_4_cuda https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libattr 2.5.2 hb03c661_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libavif16 1.4.0 hcfa2d63_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libblas 3.11.0 5_h4a7cf45_openblas https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libbrotlicommon 1.2.0 hb03c661_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libbrotlidec 1.2.0 hb03c661_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libbrotlienc 1.2.0 hb03c661_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libbtf 2.3.2 h32481e8_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libcamd 3.3.3 h32481e8_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libcblas 3.11.0 5_h0358290_openblas https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libccolamd 3.3.4 h32481e8_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libcholmod 5.3.1 h59ddab4_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libcolamd 3.3.4 h32481e8_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libcrc32c 1.1.2 h9c3ff4c_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libcurl 8.18.0 hcf29cc6_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libcxsparse 4.4.1 h32481e8_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libdeflate 1.25 h17f619e_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libedit 3.1.20250104 pl5321h7949ede_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libev 4.33 hd590300_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libevent 2.1.12 hf998b51_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libexpat 2.7.4 hecca717_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libffi 3.5.2 h3435931_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libfreetype 2.14.2 ha770c72_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libfreetype6 2.14.2 h73754d4_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgcc 15.2.0 he0feb66_18 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgcc-ng 15.2.0 h69a702a_18 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal 3.10.3 h3b705f5_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-core 3.10.3 h1f481a6_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-fits 3.10.3 hec9d828_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-grib 3.10.3 hb20eef8_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-hdf4 3.10.3 ha810028_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-hdf5 3.10.3 h966a9c2_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-jp2openjpeg 3.10.3 hdd07572_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-kea 3.10.3 h2bf108d_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-netcdf 3.10.3 ha526aae_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-pdf 3.10.3 h20efda7_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-pg 3.10.3 h55c2262_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-postgisraster 3.10.3 h55c2262_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-tiledb 3.10.3 h6c35068_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgdal-xls 3.10.3 hdee084c_27 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgfortran 15.2.0 h69a702a_18 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgfortran5 15.2.0 h68bc16d_18 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgl 1.7.0 ha4b6fd6_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libglib 2.86.4 h6548e54_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libglu 9.0.3 h5888daf_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libglvnd 1.7.0 ha4b6fd6_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libglx 1.7.0 ha4b6fd6_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgomp 15.2.0 he0feb66_18 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgoogle-cloud 2.39.0 hdb79228_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgoogle-cloud-storage 2.39.0 hdbdcf42_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libgrpc 1.73.1 h3288cfb_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libhwy 1.3.0 h4c17acf_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libiconv 1.18 h3b78370_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libjpeg-turbo 3.1.2 hb03c661_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libjxl 0.11.2 ha09017c_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libklu 2.3.5 hf24d653_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libkml 1.3.0 haa4a5bd_1022 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
liblapack 3.11.0 5_h47877c9_openblas https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libldl 3.3.2 h32481e8_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
liblzma 5.8.2 hb03c661_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libnetcdf 4.9.3 nompi_hbf2fc22_104 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libnghttp2 1.67.0 had1ee68_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libnsl 2.0.1 hb9d3cd8_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libntlm 1.8 hb9d3cd8_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libopenblas 0.3.30 pthreads_h94d23a6_4 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libopengl 1.7.0 ha4b6fd6_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libopentelemetry-cpp 1.21.0 hb9b0907_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libopentelemetry-cpp-headers 1.21.0 ha770c72_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libparquet 22.0.0 h31208bf_4_cuda https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libparu 1.0.0 h17147ab_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libpng 1.6.55 h421ea60_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libpq 18.3 h9abb657_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libprotobuf 6.31.1 h49aed37_4 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
librbio 4.3.4 h32481e8_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libre2-11 2025.11.05 h7b12aa8_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
librttopo 1.1.0 h46dd2a8_20 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libspatialite 5.1.0 gpl_h2abfd87_119 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libspex 3.2.3 had10066_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libspqr 4.3.4 h852d39f_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libsqlite 3.52.0 hf4e2dac_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libssh2 1.11.1 hcf80075_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libstdcxx 15.2.0 h934c35e_18 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libstdcxx-ng 15.2.0 hdf11a46_18 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libsuitesparseconfig 7.10.1 h92d6892_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libthrift 0.22.0 h454ac66_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libtiff 4.7.1 h9d88235_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libumfpack 6.3.5 heb53515_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
liburing 2.14 hb700be7_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libutf8proc 2.11.3 hfe17d71_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libuuid 2.41.3 h5347b49_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libwebp-base 1.6.0 hd42ef1d_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libxcb 1.17.0 h8a09558_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libxcrypt 4.4.36 hd590300_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libxml2 2.15.2 he237659_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libxml2-16 2.15.2 hca6bf5a_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libxml2-devel 2.15.2 he237659_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libxslt 1.1.43 h711ed8c_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libzip 1.11.2 h6991a6a_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libzlib 1.3.1 hb9d3cd8_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
libzopfli 1.0.3 h9c3ff4c_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
locket 1.0.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
lxml 6.0.2 py311h8840267_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
lz4 4.4.5 py311h1c460e0_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
lz4-c 1.10.0 h5888daf_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
lzo 2.10 h280c20c_1002 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
markdown-it-py 4.0.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
markupsafe 3.0.3 py311h3778330_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
matplotlib-base 3.10.8 py311h0f3be63_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
mdurl 0.1.2 pyhd8ed1ab_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
metis 5.1.0 hd0bcaf9_1007 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
minizip 4.0.10 h05a5f5f_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
mintpy 1.6.3 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
mpfr 4.2.2 he0a73b1_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
msgpack-python 1.1.2 py311hdf67eae_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
multiurl 0.3.7 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
munkres 1.1.4 pyhd8ed1ab_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
narwhals 2.18.1 pyhcf101f3_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
ncurses 6.5 h2d0b736_3 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
networkx 3.6.1 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
nlohmann_json 3.12.0 h54a6638_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
nodeenv 1.10.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
nspr 4.38 h29cc59b_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
nss 3.118 h445c969_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
numpy 1.26.4 py311h64a7726_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
openjpeg 2.5.4 h55fea9a_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
openjph 0.26.3 h8d634f6_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
openldap 2.6.10 hbde042b_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
openmotif 2.3.8 he4bd66d_6 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
openssl 3.6.1 h35e630c_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
orc 2.2.1 hd747db4_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
packaging 26.0 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pandas 3.0.2 py311h8032f78_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
partd 1.4.2 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pcre2 10.47 haa7fec5_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pillow 12.1.1 py311hf88fc01_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pip 26.0.1 pyh8b19718_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pixman 0.46.4 h54a6638_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
platformdirs 4.9.4 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
poppler 25.07.0 h13eef12_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
poppler-data 0.4.12 hd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
postgresql 18.3 h9d31465_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pre-commit 4.5.1 pyha770c72_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
proj 9.7.1 he0df7b0_3 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
prometheus-cpp 1.3.0 ha5d0236_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
psutil 7.2.2 py311haee01d2_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pthread-stubs 0.4 hb9d3cd8_1002 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pyaps3 0.3.7 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pyarrow 22.0.0 py311h38be061_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pyarrow-core 22.0.0 py311hbabfba9_2_cuda https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pycparser 2.22 pyh29332c3_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pygments 2.20.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pygrib 2.1.8 py311he4f3390_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pykdtree 1.4.3 py311h0372a8f_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pykml 0.2.0 pyhd8ed1ab_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pyparsing 3.3.2 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pyproj 3.7.2 py311h400b93a_3 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pyresample 1.35.0 py311h1ddb823_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pyshp 3.0.3 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pysocks 1.7.1 pyha55dd90_7 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pysolid 0.3.4 py311h9bb1bfa_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
python 3.11.15 hd63d673_0_cpython https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
python-dateutil 2.9.0.post0 pyhe01879c_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
python-discovery 1.2.1 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
python_abi 3.11 8_cp311 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pytz 2026.1.post1 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
pyyaml 6.0.3 py311h3778330_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
qhull 2020.2 h434a139_5 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
rav1e 0.8.1 h1fbca29_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
re2 2025.11.05 h5301d42_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
readline 8.3 h853b02a_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
requests 2.32.5 pyhcf101f3_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
rich 14.3.3 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
s2n 1.6.2 he8a4886_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
scikit-image 0.26.0 np2py311h2a99c40_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
scipy 1.17.1 py311hbe70eeb_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
setuptools 82.0.1 pyh332efcf_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
shapely 2.1.2 py311h8a92878_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
six 1.17.0 pyhe01879c_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
snappy 1.2.2 h03e3b7b_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
sortedcontainers 2.4.0 pyhd8ed1ab_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
spdlog 1.16.0 hffee6e0_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
sqlite 3.52.0 h04a0ce9_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
suitesparse 7.10.1 ha0f6916_7100102 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
svt-av1 4.0.1 hecca717_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
tblib 3.2.2 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
tifffile 2026.3.3 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
tiledb 2.29.2 h8821262_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
tk 8.6.13 noxft_h366c992_103 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
toolz 1.1.0 pyhd8ed1ab_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
tornado 6.5.5 py311h49ec1c0_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
tqdm 4.67.3 pyh8f84b5b_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
typing_extensions 4.15.0 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
tzcode 2026a h280c20c_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
tzdata 2025c hc9c84f9_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
ukkonen 1.1.0 py311hdf67eae_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
unicodedata2 17.0.1 py311h49ec1c0_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
uriparser 0.9.8 hac33072_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
urllib3 2.6.3 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
utm 0.7.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
virtualenv 21.2.0 pyhcf101f3_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
wheel 0.46.3 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xerces-c 3.3.0 hd9031aa_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libice 1.1.2 hb9d3cd8_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libsm 1.2.6 he73a12e_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libx11 1.8.13 he1eb515_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libxau 1.0.12 hb03c661_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libxdmcp 1.1.5 hb03c661_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libxext 1.3.7 hb03c661_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libxfixes 6.0.2 hb03c661_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libxft 2.3.9 h355ab9f_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libxi 1.8.2 hb9d3cd8_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libxmu 1.3.1 hb03c661_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libxp 1.0.4 hb03c661_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libxrender 0.9.12 hb9d3cd8_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xorg-libxt 1.3.1 hb9d3cd8_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
xyzservices 2026.3.0 pyhd8ed1ab_0 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
yaml 0.2.5 h280c20c_3 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
zfp 1.0.1 h909a3a2_5 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
zict 3.0.0 pyhd8ed1ab_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
zipp 3.23.0 pyhcf101f3_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
zlib 1.3.1 hb9d3cd8_2 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
zlib-ng 2.3.3 hceb46e0_1 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
zstd 1.5.7 hb78ec9c_6 https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
@@ -1,352 +0,0 @@
# This file may be used to create an environment using:
# $ conda create --name <env> --file <this file>
# platform: linux-64
# created-by: conda 26.1.1
@EXPLICIT
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/_x86_64-microarch-level-3-3_skylake.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/ca-certificates-2026.2.25-hbd8a1cb_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgomp-15.2.0-he0feb66_18.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libopentelemetry-cpp-headers-1.21.0-ha770c72_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/nlohmann_json-3.12.0-h54a6638_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/poppler-data-0.4.12-hd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/python_abi-3.11-8_cp311.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgcc-15.2.0-he0feb66_18.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-c-common-0.12.6-hb03c661_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/json-c-0.18-h6688a6e_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libattr-2.5.2-hb03c661_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libexpat-2.7.4-hecca717_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_18.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_18.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/liblzma-5.8.2-hb03c661_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_18.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libutf8proc-2.11.3-hfe17d71_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/lzo-2.10-h280c20c_1002.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/openssl-3.6.1-h35e630c_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/rav1e-0.8.1-h1fbca29_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/tzcode-2026a-h280c20c_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/attr-2.5.2-hb03c661_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-c-cal-0.9.13-h2c9d079_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-c-compression-0.3.1-h8b1a151_9.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h8b1a151_4.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-checksums-0.2.7-h8b1a151_5.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/capnproto-1.2.0-hfc315d8_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/charls-2.4.3-hecca717_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/dav1d-1.2.1-hd590300_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/fmt-12.0.0-h2b0788b_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/geos-3.14.1-h480dda7_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/gflags-2.2.2-h5888daf_1005.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/icu-78.2-h33c6efd_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/jxrlib-1.1-hd590300_3.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libev-4.33-hd590300_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libevent-2.1.12-hf998b51_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_18.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libglu-9.0.3-h5888daf_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libhwy-1.3.0-h4c17acf_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libpng-1.6.55-h421ea60_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_18.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/liburing-2.14-hb700be7_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libzip-1.11.2-h6991a6a_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/lz4-c-1.10.0-h5888daf_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/metis-5.1.0-hd0bcaf9_1007.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/nspr-4.38-h29cc59b_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/readline-8.3-h853b02a_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/s2n-1.6.2-he8a4886_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/svt-av1-4.0.1-hecca717_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/zfp-1.0.1-h909a3a2_5.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/zlib-1.3.1-hb9d3cd8_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aom-3.9.1-hac33072_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-c-io-0.23.3-hdaf4b65_5.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/blosc-1.21.6-he440d0b_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/brunsli-0.1-hd1e3526_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/c-blosc2-2.23.1-hc31b594_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/fftw-3.3.10-nompi_h3b011a4_112.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/glog-0.7.1-hbabe93e_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/gmp-6.3.0-hac33072_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/hdf4-4.2.15-h2a13503_7.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libacl-2.3.2-h0f662aa_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libcrc32c-1.1.2-h9c3ff4c_0.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libfreetype6-2.14.2-h73754d4_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libglib-2.86.4-h6548e54_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libjxl-0.11.2-ha09017c_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_4.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/librttopo-1.1.0-h46dd2a8_20.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libsqlite-3.52.0-hf4e2dac_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libsuitesparseconfig-7.10.1-h92d6892_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libthrift-0.22.0-h454ac66_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libxml2-16-2.15.2-hca6bf5a_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libzopfli-1.0.3-h9c3ff4c_0.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/minizip-4.0.10-h05a5f5f_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/spdlog-1.16.0-hffee6e0_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/uriparser-0.9.8-hac33072_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xerces-c-3.3.0-hd9031aa_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-c-event-stream-0.5.7-h28f887f_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-c-http-0.10.7-ha8fc4e3_5.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/freexl-2.0.0-h9dce30a_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/glpk-5.0-h445213a_0.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/lcms2-2.18-h0c24ade_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libamd-3.3.3-haaf9dc3_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libavif16-1.4.0-hcfa2d63_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libbtf-2.3.2-h32481e8_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libcamd-3.3.3-h32481e8_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libccolamd-3.3.4-h32481e8_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libcolamd-3.3.4-h32481e8_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libcurl-8.18.0-hcf29cc6_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libcxsparse-4.4.1-h32481e8_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libfreetype-2.14.2-ha770c72_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libkml-1.3.0-haa4a5bd_1022.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libldl-3.3.2-h32481e8_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/librbio-4.3.4-h32481e8_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libxml2-2.15.2-he237659_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/mpfr-4.2.2-he0a73b1_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/nss-3.118-h445c969_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/openjph-0.26.3-h8d634f6_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/orc-2.2.1-hd747db4_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/re2-2025.11.05-h5301d42_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/sqlite-3.52.0-h04a0ce9_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libxt-1.3.1-hb9d3cd8_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/argcomplete-3.6.3-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-c-auth-0.9.3-hef928c7_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-c-mqtt-0.13.3-hc63082f_11.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/azure-core-cpp-1.16.1-h3a458e0_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/backports.zstd-1.3.0-py311h6b1f9c4_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/brotli-python-1.2.0-py311h66f275b_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/certifi-2026.2.25-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/cfitsio-4.6.3-ha0b56bc_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/charset-normalizer-3.4.5-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/cloudpickle-3.1.2-pyhcf101f3_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/filelock-3.25.2-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/fontconfig-2.17.1-h27c8c51_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/freetype-2.14.2-ha770c72_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/fsspec-2026.3.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/hdf5-1.14.6-nompi_h19486de_106.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/kiwisolver-1.5.0-py311h724c32c_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libarchive-3.8.5-gpl_hc2c16d8_100.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgrpc-1.73.1-h3288cfb_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libspex-3.2.3-had10066_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libxml2-devel-2.15.2-he237659_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/lz4-4.4.5-py311h1c460e0_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/msgpack-python-1.1.2-py311hdf67eae_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/narwhals-2.18.1-pyhcf101f3_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/networkx-3.6.1-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/openldap-2.6.10-hbde042b_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/packaging-26.0-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pillow-12.1.1-py311hf88fc01_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/platformdirs-4.9.4-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/proj-9.7.1-he0df7b0_3.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/prometheus-cpp-1.3.0-ha5d0236_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/psutil-7.2.2-py311haee01d2_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/pyshp-3.0.3-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/pytz-2026.1.post1-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pyyaml-6.0.3-py311h3778330_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/tblib-3.2.2-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/toolz-1.1.0-pyhd8ed1ab_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/tornado-6.5.5-py311h49ec1c0_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/tqdm-4.67.3-pyh8f84b5b_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/unicodedata2-17.0.1-py311h49ec1c0_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/utm-0.7.0-pyhd8ed1ab_0.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libxi-1.8.2-hb9d3cd8_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libxmu-1.3.1-hb03c661_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libxp-1.0.4-hb03c661_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/xyzservices-2026.3.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/zict-3.0.0-pyhd8ed1ab_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-c-s3-0.11.3-h06ab39a_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/azure-identity-cpp-1.13.2-h3a5f585_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/azure-storage-common-cpp-12.11.0-h3d7a050_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/cffi-2.0.0-py311h03d9500_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/configobj-5.0.9-pyhd8ed1ab_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/cytoolz-1.1.0-py311h49ec1c0_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/donfig-0.8.1.post1-pyhd8ed1ab_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/dsdp-5.8-hd9d9efa_1203.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/fonttools-4.62.0-py311h3778330_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/freeglut-3.2.2-ha6d2627_3.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/geotiff-1.7.4-h1000f5c_4.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/gsl-2.7-he838d99_0.tar.bz2
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/kealib-1.6.2-hb2f3951_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/lazy-loader-0.5-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libcholmod-5.3.1-h59ddab4_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgoogle-cloud-2.39.0-hdb79228_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libnetcdf-4.9.3-nompi_hbf2fc22_104.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libopentelemetry-cpp-1.21.0-hb9b0907_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libpq-18.3-h9abb657_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libspatialite-5.1.0-gpl_h2abfd87_119.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/lxml-6.0.2-py311h8840267_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/numpy-1.26.4-py311h64a7726_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/partd-1.4.2-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pyproj-3.7.2-py311h400b93a_3.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/python-discovery-1.2.1-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/wheel-0.46.3-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/xorg-libxft-2.3.9-h355ab9f_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-crt-cpp-0.35.2-h8824e59_6.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/azure-storage-blobs-cpp-12.15.0-h2a74896_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/contourpy-1.3.3-py311h724c32c_4.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/dask-core-2026.3.0-pyhc364b38_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/h5py-3.15.1-nompi_py311h0b2f468_101.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/imagecodecs-2026.3.6-py311h9837d23_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/imageio-2.37.0-pyhfb79c49_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/jasper-4.2.9-h1588d4d_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-core-3.10.3-h1f481a6_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgoogle-cloud-storage-2.39.0-hdbdcf42_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libklu-2.3.5-hf24d653_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libspqr-4.3.4-h852d39f_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libumfpack-6.3.5-heb53515_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/openmotif-2.3.8-he4bd66d_6.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pandas-3.0.2-py311h8032f78_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/pip-26.0.1-pyh8b19718_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/poppler-25.07.0-h13eef12_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/postgresql-18.3-h9d31465_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pykdtree-1.4.3-py311h0372a8f_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/pykml-0.2.0-pyhd8ed1ab_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/rich-14.3.3-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/scipy-1.17.1-py311hbe70eeb_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/shapely-2.1.2-py311h8a92878_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/ukkonen-1.1.0-py311hdf67eae_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/urllib3-2.6.3-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/virtualenv-21.2.0-pyhcf101f3_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/aws-sdk-cpp-1.11.606-hf38915e_9.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/azure-storage-files-datalake-cpp-12.13.0-hf38f1be_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/bokeh-3.9.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/distributed-2026.3.0-pyhc364b38_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/eccodes-2.46.0-h83bc92c_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/gdal-3.10.3-py311h34ccccb_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/identify-2.6.18-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-fits-3.10.3-hec9d828_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-grib-3.10.3-hb20eef8_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-hdf4-3.10.3-ha810028_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-hdf5-3.10.3-h966a9c2_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-jp2openjpeg-3.10.3-hdd07572_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-pdf-3.10.3-h20efda7_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-pg-3.10.3-h55c2262_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-postgisraster-3.10.3-h55c2262_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-xls-3.10.3-hdee084c_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libparu-1.0.0-h17147ab_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/matplotlib-base-3.10.8-py311h0f3be63_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pyresample-1.35.0-py311h1ddb823_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pysolid-0.3.4-py311h9bb1bfa_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/requests-2.32.5-pyhcf101f3_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/tifffile-2026.3.3-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/cartopy-0.25.0-py311hed34c8f_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/dask-jobqueue-0.9.0-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libarrow-22.0.0-h2937f24_4_cuda.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-kea-3.10.3-h2bf108d_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-netcdf-3.10.3-ha526aae_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/multiurl-0.3.7-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/pre-commit-4.5.1-pyha770c72_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pygrib-2.1.8-py311he4f3390_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/scikit-image-0.26.0-np2py311h2a99c40_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/suitesparse-7.10.1-ha0f6916_7100102.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/tiledb-2.29.2-h8821262_1.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/cvxopt-1.3.3-py311h3d1f434_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/ecmwf-datastores-client-0.5.1-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libarrow-compute-22.0.0-h58682fd_4_cuda.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-tiledb-3.10.3-h6c35068_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libparquet-22.0.0-h31208bf_4_cuda.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/cdsapi-0.7.7-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libarrow-acero-22.0.0-hb826db4_4_cuda.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libgdal-3.10.3-h3b705f5_27.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pyarrow-core-22.0.0-py311hbabfba9_2_cuda.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/isce2-2.6.4-py311h916084f_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libarrow-dataset-22.0.0-hb826db4_4_cuda.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/pyaps3-0.3.7-pyhd8ed1ab_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/libarrow-substrait-22.0.0-h9d9f3f8_4_cuda.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/linux-64/pyarrow-22.0.0-py311h38be061_2.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/dask-2026.3.0-pyhc364b38_0.conda
https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/noarch/mintpy-1.6.3-pyhd8ed1ab_0.conda
@@ -1,354 +0,0 @@
name: isce2_mintpy_v1
channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
- defaults
dependencies:
- _openmp_mutex=4.5
- _x86_64-microarch-level=3
- aom=3.9.1
- argcomplete=3.6.3
- attr=2.5.2
- attrs=26.1.0
- aws-c-auth=0.9.3
- aws-c-cal=0.9.13
- aws-c-common=0.12.6
- aws-c-compression=0.3.1
- aws-c-event-stream=0.5.7
- aws-c-http=0.10.7
- aws-c-io=0.23.3
- aws-c-mqtt=0.13.3
- aws-c-s3=0.11.3
- aws-c-sdkutils=0.2.4
- aws-checksums=0.2.7
- aws-crt-cpp=0.35.2
- aws-sdk-cpp=1.11.606
- azure-core-cpp=1.16.1
- azure-identity-cpp=1.13.2
- azure-storage-blobs-cpp=12.15.0
- azure-storage-common-cpp=12.11.0
- azure-storage-files-datalake-cpp=12.13.0
- backports.zstd=1.3.0
- blosc=1.21.6
- bokeh=3.9.0
- brotli=1.2.0
- brotli-bin=1.2.0
- brotli-python=1.2.0
- brunsli=0.1
- bzip2=1.0.8
- c-ares=1.34.6
- c-blosc2=2.23.1
- ca-certificates=2026.2.25
- cached-property=1.5.2
- cached_property=1.5.2
- cairo=1.18.4
- capnproto=1.2.0
- cartopy=0.25.0
- cdsapi=0.7.7
- certifi=2026.2.25
- cffi=2.0.0
- cfgv=3.5.0
- cfitsio=4.6.3
- charls=2.4.3
- charset-normalizer=3.4.5
- click=8.3.1
- cloudpickle=3.1.2
- configobj=5.0.9
- contourpy=1.3.3
- cvxopt=1.3.3
- cycler=0.12.1
- cyrus-sasl=2.1.28
- cytoolz=1.1.0
- dask=2026.3.0
- dask-core=2026.3.0
- dask-jobqueue=0.9.0
- dav1d=1.2.1
- distlib=0.4.0
- distributed=2026.3.0
- donfig=0.8.1.post1
- dsdp=5.8
- eccodes=2.46.0
- ecmwf-datastores-client=0.5.1
- fftw=3.3.10
- filelock=3.25.2
- fmt=12.0.0
- font-ttf-dejavu-sans-mono=2.37
- font-ttf-inconsolata=3.000
- font-ttf-source-code-pro=2.038
- font-ttf-ubuntu=0.83
- fontconfig=2.17.1
- fonts-conda-ecosystem=1
- fonts-conda-forge=1
- freeglut=3.2.2
- freetype=2.14.2
- freexl=2.0.0
- fsspec=2026.3.0
- gdal=3.10.3
- geos=3.14.1
- geotiff=1.7.4
- gflags=2.2.2
- giflib=5.2.2
- glog=0.7.1
- glpk=5.0
- gmp=6.3.0
- gsl=2.7
- h2=4.3.0
- h5py=3.15.1
- hdf4=4.2.15
- hdf5=1.14.6
- hpack=4.1.0
- hyperframe=6.1.0
- icu=78.2
- identify=2.6.18
- idna=3.11
- imagecodecs=2026.3.6
- imageio=2.37.0
- importlib-metadata=8.8.0
- isce2=2.6.4
- jasper=4.2.9
- jinja2=3.1.6
- joblib=1.5.3
- json-c=0.18
- jxrlib=1.1
- kealib=1.6.2
- keyutils=1.6.3
- kiwisolver=1.5.0
- krb5=1.22.2
- lazy-loader=0.5
- lcms2=2.18
- ld_impl_linux-64=2.45.1
- lerc=4.1.0
- libabseil=20250512.1
- libacl=2.3.2
- libaec=1.1.5
- libamd=3.3.3
- libarchive=3.8.5
- libarrow=22.0.0
- libarrow-acero=22.0.0
- libarrow-compute=22.0.0
- libarrow-dataset=22.0.0
- libarrow-substrait=22.0.0
- libattr=2.5.2
- libavif16=1.4.0
- libblas=3.11.0
- libbrotlicommon=1.2.0
- libbrotlidec=1.2.0
- libbrotlienc=1.2.0
- libbtf=2.3.2
- libcamd=3.3.3
- libcblas=3.11.0
- libccolamd=3.3.4
- libcholmod=5.3.1
- libcolamd=3.3.4
- libcrc32c=1.1.2
- libcurl=8.18.0
- libcxsparse=4.4.1
- libdeflate=1.25
- libedit=3.1.20250104
- libev=4.33
- libevent=2.1.12
- libexpat=2.7.4
- libffi=3.5.2
- libfreetype=2.14.2
- libfreetype6=2.14.2
- libgcc=15.2.0
- libgcc-ng=15.2.0
- libgdal=3.10.3
- libgdal-core=3.10.3
- libgdal-fits=3.10.3
- libgdal-grib=3.10.3
- libgdal-hdf4=3.10.3
- libgdal-hdf5=3.10.3
- libgdal-jp2openjpeg=3.10.3
- libgdal-kea=3.10.3
- libgdal-netcdf=3.10.3
- libgdal-pdf=3.10.3
- libgdal-pg=3.10.3
- libgdal-postgisraster=3.10.3
- libgdal-tiledb=3.10.3
- libgdal-xls=3.10.3
- libgfortran=15.2.0
- libgfortran5=15.2.0
- libgl=1.7.0
- libglib=2.86.4
- libglu=9.0.3
- libglvnd=1.7.0
- libglx=1.7.0
- libgomp=15.2.0
- libgoogle-cloud=2.39.0
- libgoogle-cloud-storage=2.39.0
- libgrpc=1.73.1
- libhwy=1.3.0
- libiconv=1.18
- libjpeg-turbo=3.1.2
- libjxl=0.11.2
- libklu=2.3.5
- libkml=1.3.0
- liblapack=3.11.0
- libldl=3.3.2
- liblzma=5.8.2
- libnetcdf=4.9.3
- libnghttp2=1.67.0
- libnsl=2.0.1
- libntlm=1.8
- libopenblas=0.3.30
- libopengl=1.7.0
- libopentelemetry-cpp=1.21.0
- libopentelemetry-cpp-headers=1.21.0
- libparquet=22.0.0
- libparu=1.0.0
- libpng=1.6.55
- libpq=18.3
- libprotobuf=6.31.1
- librbio=4.3.4
- libre2-11=2025.11.05
- librttopo=1.1.0
- libspatialite=5.1.0
- libspex=3.2.3
- libspqr=4.3.4
- libsqlite=3.52.0
- libssh2=1.11.1
- libstdcxx=15.2.0
- libstdcxx-ng=15.2.0
- libsuitesparseconfig=7.10.1
- libthrift=0.22.0
- libtiff=4.7.1
- libumfpack=6.3.5
- liburing=2.14
- libutf8proc=2.11.3
- libuuid=2.41.3
- libwebp-base=1.6.0
- libxcb=1.17.0
- libxcrypt=4.4.36
- libxml2=2.15.2
- libxml2-16=2.15.2
- libxml2-devel=2.15.2
- libxslt=1.1.43
- libzip=1.11.2
- libzlib=1.3.1
- libzopfli=1.0.3
- locket=1.0.0
- lxml=6.0.2
- lz4=4.4.5
- lz4-c=1.10.0
- lzo=2.10
- markdown-it-py=4.0.0
- markupsafe=3.0.3
- matplotlib-base=3.10.8
- mdurl=0.1.2
- metis=5.1.0
- minizip=4.0.10
- mintpy=1.6.3
- mpfr=4.2.2
- msgpack-python=1.1.2
- multiurl=0.3.7
- munkres=1.1.4
- narwhals=2.18.1
- ncurses=6.5
- networkx=3.6.1
- nlohmann_json=3.12.0
- nodeenv=1.10.0
- nspr=4.38
- nss=3.118
- numpy=1.26.4
- openjpeg=2.5.4
- openjph=0.26.3
- openldap=2.6.10
- openmotif=2.3.8
- openssl=3.6.1
- orc=2.2.1
- packaging=26.0
- pandas=3.0.2
- partd=1.4.2
- pcre2=10.47
- pillow=12.1.1
- pip=26.0.1
- pixman=0.46.4
- platformdirs=4.9.4
- poppler=25.07.0
- poppler-data=0.4.12
- postgresql=18.3
- pre-commit=4.5.1
- proj=9.7.1
- prometheus-cpp=1.3.0
- psutil=7.2.2
- pthread-stubs=0.4
- pyaps3=0.3.7
- pyarrow=22.0.0
- pyarrow-core=22.0.0
- pycparser=2.22
- pygments=2.20.0
- pygrib=2.1.8
- pykdtree=1.4.3
- pykml=0.2.0
- pyparsing=3.3.2
- pyproj=3.7.2
- pyresample=1.35.0
- pyshp=3.0.3
- pysocks=1.7.1
- pysolid=0.3.4
- python=3.11.15
- python-dateutil=2.9.0.post0
- python-discovery=1.2.1
- python_abi=3.11
- pytz=2026.1.post1
- pyyaml=6.0.3
- qhull=2020.2
- rav1e=0.8.1
- re2=2025.11.05
- readline=8.3
- requests=2.32.5
- rich=14.3.3
- s2n=1.6.2
- scikit-image=0.26.0
- scipy=1.17.1
- setuptools=82.0.1
- shapely=2.1.2
- six=1.17.0
- snappy=1.2.2
- sortedcontainers=2.4.0
- spdlog=1.16.0
- sqlite=3.52.0
- suitesparse=7.10.1
- svt-av1=4.0.1
- tblib=3.2.2
- tifffile=2026.3.3
- tiledb=2.29.2
- tk=8.6.13
- toolz=1.1.0
- tornado=6.5.5
- tqdm=4.67.3
- typing_extensions=4.15.0
- tzcode=2026a
- tzdata=2025c
- ukkonen=1.1.0
- unicodedata2=17.0.1
- uriparser=0.9.8
- urllib3=2.6.3
- utm=0.7.0
- virtualenv=21.2.0
- wheel=0.46.3
- xerces-c=3.3.0
- xorg-libice=1.1.2
- xorg-libsm=1.2.6
- xorg-libx11=1.8.13
- xorg-libxau=1.0.12
- xorg-libxdmcp=1.1.5
- xorg-libxext=1.3.7
- xorg-libxfixes=6.0.2
- xorg-libxft=2.3.9
- xorg-libxi=1.8.2
- xorg-libxmu=1.3.1
- xorg-libxp=1.0.4
- xorg-libxrender=0.9.12
- xorg-libxt=1.3.1
- xyzservices=2026.3.0
- yaml=0.2.5
- zfp=1.0.1
- zict=3.0.0
- zipp=3.23.0
- zlib=1.3.1
- zlib-ng=2.3.3
- zstd=1.5.7
- pip:
- fonttools==4.62.1
prefix: /home/administrator/miniconda3/envs/isce2_mintpy_v1
@@ -1,14 +0,0 @@
python_executable=/home/administrator/miniconda3/envs/isce2_mintpy_v1/bin/python
python_version=3.11.15
isce_present=True
mintpy_present=True
h5py_present=True
isce_file=/home/administrator/miniconda3/envs/isce2_mintpy_v1/lib/python3.11/site-packages/isce/__init__.py
isce_version=2.6.3
mintpy_file=/home/administrator/miniconda3/envs/isce2_mintpy_v1/lib/python3.11/site-packages/mintpy/__init__.py
mintpy_version=1.6.2
2026-04-06 15:31:20,151 - h5py._conv - DEBUG - Creating converter from 7 to 5
2026-04-06 15:31:20,152 - h5py._conv - DEBUG - Creating converter from 5 to 7
2026-04-06 15:31:20,153 - h5py._conv - DEBUG - Creating converter from 7 to 5
2026-04-06 15:31:20,153 - h5py._conv - DEBUG - Creating converter from 5 to 7
h5py_version=3.15.1
@@ -1,64 +0,0 @@
# vim: set filetype=cfg:
## LT-1 stripmapStack -> MintPy SBAS smoke-test config
## Workspace:
## phase2_bridge_smoketest_20260406
## Sample stack:
## LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1
## dates: 20250118, 20250315, 20250510, 20250705, 20250830
## Work dir suggestion:
## /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/mintpy_sbas_unified_phase2_20260407
########## computing resource configuration
mintpy.compute.cluster = none
mintpy.compute.numWorker = 4
mintpy.compute.maxMemory = 8.0
########## 1. load_data
mintpy.load.processor = isce
mintpy.load.autoPath = no
mintpy.load.updateMode = yes
mintpy.load.compression = lzf
mintpy.load.metaFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/merged/SLC/20250510/referenceShelve/data.dat
mintpy.load.baselineDir = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/baselines
mintpy.load.unwFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/Igrams/*/filt*_snaphu.unw
mintpy.load.corFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/Igrams/*/filt_*.cor
mintpy.load.connCompFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/Igrams/*/filt*_snaphu.unw.conncomp
mintpy.load.intFile = None
mintpy.load.demFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/geom_reference/hgt.rdr
mintpy.load.lookupYFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/geom_reference/lat.rdr
mintpy.load.lookupXFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/geom_reference/lon.rdr
mintpy.load.incAngleFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/geom_reference/los.rdr
mintpy.load.azAngleFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/geom_reference/los.rdr
mintpy.load.shadowMaskFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/geom_reference/shadowMask.rdr
mintpy.load.waterMaskFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/geom_reference/waterMask.rdr
########## 2. modify_network
mintpy.network.coherenceBased = no
mintpy.network.areaRatioBased = no
########## 3. reference_point
mintpy.reference.yx = 1994,52
mintpy.reference.maskFile = maskAllValid.h5
########## 4. correct_unwrap_error
mintpy.unwrapError.method = no
########## 5. invert_network
mintpy.networkInversion.weightFunc = no
mintpy.networkInversion.maskDataset = no
mintpy.networkInversion.minRedundancy = 1.0
mintpy.networkInversion.waterMaskFile = maskAllValid.h5
########## 6-10. optional corrections disabled for the first unified-env replay
mintpy.solidEarthTides = no
mintpy.ionosphericDelay.method = no
mintpy.troposphericDelay.method = no
mintpy.deramp = no
mintpy.topographicResidual = no
########## 11-13. outputs
mintpy.reference.date = 20250510
mintpy.geocode = no
mintpy.save.kmz = no
mintpy.save.hdfEos5 = no
mintpy.plot = no
@@ -1,117 +0,0 @@
{
"schema_version": "psinsar.sbas.v1",
"catalog_name": "psinsar",
"mode": "sbas",
"engine_code": "isce2",
"processor_code": "isce2_stack_mintpy",
"sample_group_key": "LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1",
"reference_date": "20250510",
"reference_point_yx": [
1994,
52
],
"stack_dates": [
"20250118",
"20250315",
"20250510",
"20250705",
"20250830"
],
"network_pair_count": 10,
"mintpy_work_dir_wsl": "/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas_v5",
"publish_dir_wsl": "/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/publish/mintpy_sbas_v5",
"quality_summary": {
"strict_valid_pixels": 1219001,
"strict_valid_pixel_ratio": 0.2991,
"mask_temp_coh_pixels": 62987,
"mask_temp_coh_threshold": 0.7
},
"artifacts": [
{
"product_type": "timeseries_cube",
"role": "primary",
"dataset": "timeseries",
"publish_relpath": "assets/geo_timeseries.h5",
"source_relpath": "assets/geo_timeseries.h5"
},
{
"product_type": "velocity_map",
"role": "primary",
"dataset": "velocity",
"publish_relpath": "assets/geo_velocity.h5",
"source_relpath": "assets/geo_velocity.h5"
},
{
"product_type": "velocity_geotiff",
"role": "primary",
"dataset": "velocity",
"publish_relpath": "assets/velocity.tif",
"source_relpath": "assets/velocity.tif"
},
{
"product_type": "temporal_coherence",
"role": "quality",
"dataset": "temporalCoherence",
"publish_relpath": "assets/geo_temporalCoherence.h5",
"source_relpath": "assets/geo_temporalCoherence.h5"
},
{
"product_type": "temporal_coherence_geotiff",
"role": "quality",
"dataset": "temporalCoherence",
"publish_relpath": "assets/temporalCoherence.tif",
"source_relpath": "assets/temporalCoherence.tif"
},
{
"product_type": "quality_mask",
"role": "quality",
"dataset": "mask",
"publish_relpath": "assets/geo_maskTempCoh.h5",
"source_relpath": "assets/geo_maskTempCoh.h5"
},
{
"product_type": "quality_mask_geotiff",
"role": "quality",
"dataset": "mask",
"publish_relpath": "assets/maskTempCoh.tif",
"source_relpath": "assets/maskTempCoh.tif"
},
{
"product_type": "ifgram_network",
"role": "diagnostic",
"dataset": "mask",
"publish_relpath": "runtime/numTriNonzeroIntAmbiguity.h5",
"source_relpath": "numTriNonzeroIntAmbiguity.h5"
},
{
"product_type": "preview_png",
"role": "primary",
"publish_relpath": "preview/velocity_preview.png",
"source_relpath": "preview/velocity_preview.png"
},
{
"product_type": "diagnostic_png",
"role": "diagnostic",
"publish_relpath": "preview/numTriNonzeroIntAmbiguity.png",
"source_relpath": "preview/numTriNonzeroIntAmbiguity.png"
}
],
"retained_runtime_artifacts": [
{
"purpose": "strict_all_ifgram_mask",
"source_relpath": "maskAllValid.h5"
},
{
"purpose": "average_spatial_coherence",
"source_relpath": "avgSpatialCoh.h5"
},
{
"purpose": "config_backup",
"source_relpath": "smallbaselineApp.cfg"
}
],
"notes": [
"This is a sample experiment manifest for system-integration design only.",
"Current experiment includes geocoded HDF5 outputs, GeoTIFF exports, and a publish-style manifest bundle."
]
}
@@ -1,62 +0,0 @@
# vim: set filetype=cfg:
## LT-1 stripmapStack -> MintPy SBAS smoke-test config
## Sample stack:
## LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1
## dates: 20250118, 20250315, 20250510, 20250705, 20250830
## Work dir suggestion:
## /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas
########## computing resource configuration
mintpy.compute.cluster = none
mintpy.compute.numWorker = 4
mintpy.compute.maxMemory = 8.0
########## 1. load_data
mintpy.load.processor = isce
mintpy.load.autoPath = no
mintpy.load.updateMode = yes
mintpy.load.compression = lzf
mintpy.load.metaFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/merged/SLC/20250510/referenceShelve/data.dat
mintpy.load.baselineDir = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/baselines
mintpy.load.unwFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/Igrams/*/filt*_snaphu.unw
mintpy.load.corFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/Igrams/*/filt_*.cor
mintpy.load.connCompFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/Igrams/*/filt*_snaphu.unw.conncomp
mintpy.load.intFile = None
mintpy.load.demFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/geom_reference/hgt.rdr
mintpy.load.lookupYFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/geom_reference/lat.rdr
mintpy.load.lookupXFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/geom_reference/lon.rdr
mintpy.load.incAngleFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/geom_reference/los.rdr
mintpy.load.azAngleFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/geom_reference/los.rdr
mintpy.load.shadowMaskFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/geom_reference/shadowMask.rdr
mintpy.load.waterMaskFile = /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/geom_reference/waterMask.rdr
########## 2. modify_network
mintpy.network.coherenceBased = no
mintpy.network.areaRatioBased = no
########## 3. reference_point
mintpy.reference.yx = 1994,52
mintpy.reference.maskFile = maskAllValid.h5
########## 4. correct_unwrap_error
mintpy.unwrapError.method = no
########## 5. invert_network
mintpy.networkInversion.weightFunc = no
mintpy.networkInversion.maskDataset = no
mintpy.networkInversion.minRedundancy = 1.0
mintpy.networkInversion.waterMaskFile = maskAllValid.h5
########## 6-10. optional corrections disabled for first offline LT-1 smoke test
mintpy.solidEarthTides = no
mintpy.ionosphericDelay.method = no
mintpy.troposphericDelay.method = no
mintpy.deramp = no
mintpy.topographicResidual = no
########## 11-13. outputs
mintpy.reference.date = 20250510
mintpy.geocode = no
mintpy.save.kmz = no
mintpy.save.hdfEos5 = no
mintpy.plot = no
@@ -1,175 +0,0 @@
{
"source_root_windows": "F:\\Insar_data_pool_1",
"source_root_wsl": "/mnt/f/Insar_data_pool_1",
"group_key": "LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1",
"tile_key": "E123.3_N46.1",
"scene_count": 5,
"reference_strategy": "middle_by_date",
"reference_date": "20250510",
"stack_group": {
"satellite": "LT1A",
"imaging_mode": "STRIP1",
"polarization": "HH",
"orbit_direction": "DESCENDING",
"receiving_stations": [
"KSC",
"SYC"
]
},
"proposed_scratch_windows": "Z:\\Code\\Insar_management_system_v2\\experiments\\isce2_sbas_timeseries\\scratch\\lt1a_strip1_hh_descending_e123p3_n46p1",
"proposed_scratch_wsl": "/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1",
"proposed_layout": {
"stack_input_manifest": "/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_input_manifest.json",
"slc_dir": "/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/SLC",
"orbits_dir": "/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/orbits",
"logs_dir": "/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/logs"
},
"stack_prep_assessment": {
"current_scene_layout": "per_scene_folder_with_tiff_meta_rpc",
"official_stripmapStack_expected_layout": "SLC/YYYYMMDD/YYYYMMDD.raw or YYYYMMDD.slc",
"direct_compatibility": "unproven",
"lt1_adapter_required_likely": true,
"notes": [
"Current repo can read these scene folders as RadarData assets.",
"Official stripmapStack helper scripts do not advertise LT-1/LUTAN1 preparation hooks.",
"A custom LT-1 stack preparation layer is likely needed before official stack execution."
]
},
"scenes": [
{
"folder_name": "LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780",
"folder_path": "F:\\Insar_data_pool_1\\LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780",
"folder_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780",
"tiff_path": "F:\\Insar_data_pool_1\\LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780\\LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780.tiff",
"tiff_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780/LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780.tiff",
"meta_path": "F:\\Insar_data_pool_1\\LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780\\LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780.meta.xml",
"meta_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780/LT1A_MONO_SYC_STRIP1_016197_E123.3_N46.1_20250118_SLC_HH_S2A_0000623780.meta.xml",
"file_size_bytes": 1632365702,
"satellite": "LT1A",
"imaging_date": "20250118",
"imaging_mode": "STRIP1",
"polarization": "HH",
"orbit_direction": "DESCENDING",
"satellite_mode": "MONOSTATIC",
"receiving_station": "SYC",
"orbit_circle": "16197",
"scene_center_lon": 123.3290291621,
"scene_center_lat": 46.0963941396,
"acquisition_time_utc": "2025-01-18T22:5:18.778600",
"product_type": "COMPLEX",
"product_level": "LEVEL2A",
"product_unique_id": "0000623780",
"tile_key": "E123.3_N46.1",
"group_key": "LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1",
"orbit_txt_expected_name": "LT1A_GpsData_GAS_C_20250118.txt"
},
{
"folder_name": "LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238",
"folder_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238",
"folder_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238",
"tiff_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238\\LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238.tiff",
"tiff_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238/LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238.tiff",
"meta_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238\\LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238.meta.xml",
"meta_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238/LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238.meta.xml",
"file_size_bytes": 1630628342,
"satellite": "LT1A",
"imaging_date": "20250315",
"imaging_mode": "STRIP1",
"polarization": "HH",
"orbit_direction": "DESCENDING",
"satellite_mode": "MONOSTATIC",
"receiving_station": "KSC",
"orbit_circle": "17030",
"scene_center_lon": 123.3320867599,
"scene_center_lat": 46.1149596404,
"acquisition_time_utc": "2025-03-15T22:5:19.382029",
"product_type": "COMPLEX",
"product_level": "LEVEL2A",
"product_unique_id": "0000678238",
"tile_key": "E123.3_N46.1",
"group_key": "LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1",
"orbit_txt_expected_name": "LT1A_GpsData_GAS_C_20250315.txt"
},
{
"folder_name": "LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820",
"folder_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820",
"folder_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820",
"tiff_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820\\LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820.tiff",
"tiff_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820/LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820.tiff",
"meta_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820\\LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820.meta.xml",
"meta_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820/LT1A_MONO_KSC_STRIP1_017863_E123.3_N46.1_20250510_SLC_HH_S2A_0000738820.meta.xml",
"file_size_bytes": 1631554934,
"satellite": "LT1A",
"imaging_date": "20250510",
"imaging_mode": "STRIP1",
"polarization": "HH",
"orbit_direction": "DESCENDING",
"satellite_mode": "MONOSTATIC",
"receiving_station": "KSC",
"orbit_circle": "17863",
"scene_center_lon": 123.3311389524,
"scene_center_lat": 46.1155150436,
"acquisition_time_utc": "2025-05-10T22:5:20.466535",
"product_type": "COMPLEX",
"product_level": "LEVEL2A",
"product_unique_id": "0000738820",
"tile_key": "E123.3_N46.1",
"group_key": "LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1",
"orbit_txt_expected_name": "LT1A_GpsData_GAS_C_20250510.txt"
},
{
"folder_name": "LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680",
"folder_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680",
"folder_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680",
"tiff_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680\\LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680.tiff",
"tiff_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680/LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680.tiff",
"meta_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680\\LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680.meta.xml",
"meta_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680/LT1A_MONO_KSC_STRIP1_018697_E123.3_N46.1_20250705_SLC_HH_S2A_0000796680.meta.xml",
"file_size_bytes": 1633523942,
"satellite": "LT1A",
"imaging_date": "20250705",
"imaging_mode": "STRIP1",
"polarization": "HH",
"orbit_direction": "DESCENDING",
"satellite_mode": "MONOSTATIC",
"receiving_station": "KSC",
"orbit_circle": "18697",
"scene_center_lon": 123.3345082568,
"scene_center_lat": 46.1000329747,
"acquisition_time_utc": "2025-07-05T22:5:20.665803",
"product_type": "COMPLEX",
"product_level": "LEVEL2A",
"product_unique_id": "0000796680",
"tile_key": "E123.3_N46.1",
"group_key": "LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1",
"orbit_txt_expected_name": "LT1A_GpsData_GAS_C_20250705.txt"
},
{
"folder_name": "LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029",
"folder_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029",
"folder_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029",
"tiff_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029\\LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029.tiff",
"tiff_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029/LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029.tiff",
"meta_path": "F:\\Insar_data_pool_1\\LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029\\LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029.meta.xml",
"meta_path_wsl": "/mnt/f/Insar_data_pool_1/LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029/LT1A_MONO_KSC_STRIP1_019530_E123.3_N46.1_20250830_SLC_HH_S2A_0000857029.meta.xml",
"file_size_bytes": 1629875486,
"satellite": "LT1A",
"imaging_date": "20250830",
"imaging_mode": "STRIP1",
"polarization": "HH",
"orbit_direction": "DESCENDING",
"satellite_mode": "MONOSTATIC",
"receiving_station": "KSC",
"orbit_circle": "19530",
"scene_center_lon": 123.3286377886,
"scene_center_lat": 46.1152894101,
"acquisition_time_utc": "2025-08-30T22:5:26.558015",
"product_type": "COMPLEX",
"product_level": "LEVEL2A",
"product_unique_id": "0000857029",
"tile_key": "E123.3_N46.1",
"group_key": "LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1",
"orbit_txt_expected_name": "LT1A_GpsData_GAS_C_20250830.txt"
}
]
}
@@ -1 +0,0 @@
@@ -1,62 +0,0 @@
# Phase 0 Environment Check
Updated: 2026-04-03
## Confirmed
- Project workspace:
- Windows repo root: `Z:\Code\Insar_management_system_v2`
- WSL mount path: `/mnt/z/Code/Insar_management_system_v2`
- Windows project Python env:
- `C:\Users\Administrator\.conda\envs\InSAR`
- WSL experiment distro:
- `Ubuntu-24.04`
- WSL project access:
- `/mnt/z/Code/Insar_management_system_v2`
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries`
- WSL system Python:
- `Python 3.12.3`
- WSL conda root:
- `/home/administrator/miniconda3`
- WSL conda envs found:
- `base`
- `isce2`
- ISCE2 env package:
- `isce2 2.6.4`
- ISCE2 Python import path:
- `/home/administrator/miniconda3/envs/isce2/lib/python3.11/site-packages/isce/__init__.py`
- Lutan1 sensor module:
- `/home/administrator/miniconda3/envs/isce2/lib/python3.11/site-packages/isce/components/isceobj/Sensor/Lutan1.py`
- Official stack directories present:
- `/home/administrator/miniconda3/envs/isce2/share/isce2/stripmapStack`
- `/home/administrator/miniconda3/envs/isce2/share/isce2/topsStack`
## Confirmed Gaps
- `conda` is not currently on the default shell `PATH` inside `Ubuntu-24.04`.
- Use `/home/administrator/miniconda3/bin/conda` directly in scripts.
- `MintPy` is not installed in the `isce2` env yet.
- `conda list -n isce2 mintpy` returned no match.
- Calling `conda list -n isce2 ...` from a WSL bash script triggered a segmentation fault once.
- For experiment scripts, prefer `conda run -n isce2 python ...` checks over `conda list`.
## Implication
Phase 0 can start immediately for:
- LT-1 / ISCE2 stack compatibility checks
- workspace and path validation
- command-chain drafting
But the full SBAS chain cannot run end to end until one of these is true:
- `MintPy` is installed into `isce2`, or
- a separate WSL env with `MintPy` is prepared
## Next Checks
1. Verify ISCE2 stack scripts actually exist in `Ubuntu-24.04`.
2. Read `stripmapStack/README.md` and `stackStripMap.py` to identify required stack inputs.
3. Decide whether `MintPy` should share the `isce2` env or live in a separate env.
4. Verify LT-1 / LUTAN1 support is usable for stack-mode inputs, not only single-pair mode.
5. Draft a minimal stack experiment command chain under this folder.
@@ -1,123 +0,0 @@
# Phase 0 Repo Reader Findings
Updated: 2026-04-03
## Existing repo reader path
The current repository already has a stable LT-1 single-scene metadata ingestion path.
Main code path:
- `backend/app/services/data_service.py`
- `scan_radar_data()`
- `backend/app/utils.py`
- `parse_lt1_radar_filename()`
- `find_xml_file()`
- `parse_xml_metadata()`
## What the existing reader does
### 1. Folder-name parsing
`parse_lt1_radar_filename()` extracts from the directory name:
- `satellite`
- `satellite_mode`
- `receiving_station`
- `imaging_mode`
- `orbit_circle`
- `scene_center_lon`
- `scene_center_lat`
- `imaging_date`
- `acquisition_time_utc`
- `product_type`
- `polarization`
- `product_level`
- `product_unique_id`
Example supported name:
```text
LT1B_MONO_SYC_STRIP1_018153_E135.4_N48.3_20250701_SLC_HH_S2A_0000790171
```
### 2. XML discovery
`find_xml_file()` prefers:
- `*.meta.xml`
and falls back to:
- the only XML file in the directory, if there is just one
### 3. XML parsing
`parse_xml_metadata()` extracts:
- `orbit_direction`
- `imaging_mode`
- `polarization`
- `receiving_station`
- `satellite_mode`
- `orbit_circle` from `absOrbit`
- `scene_center_lon`
- `scene_center_lat`
- `acquisition_time_utc`
- `product_type`
- `product_level`
- `product_unique_id`
- `look_direction`
- corner coordinates and coverage polygon
### 4. Merge rule
`scan_radar_data()` merges:
- folder-name metadata
- XML metadata
with XML preferred for most fields, except `product_unique_id` where the folder-name value is preserved if present.
## Why this matters for SBAS experiments
This means the SBAS experiment should not invent a separate metadata interpretation unless absolutely necessary.
Recommended rule:
- reuse the same field semantics already used by `RadarDataORM`
- reuse the same `.meta.xml` discovery logic
- treat `scan_radar_data()` output as the canonical single-scene asset layer
## Data layout check against `F:\Insar_data_pool_1`
Sample scene directories under `F:\Insar_data_pool_1` are compatible with the current single-scene reader:
- one folder per scene
- directory name matches LT-1 parser expectations
- contains `*.meta.xml`
- contains `*.tiff`
- contains preview and auxiliary files
Example sample directory:
```text
F:\Insar_data_pool_1\LT1A_MONO_KSC_STRIP1_017030_E123.3_N46.1_20250315_SLC_HH_S2A_0000678238
```
Example files inside:
- `...meta.xml`
- `...tiff`
- `...rpc`
- `...browse.jpg`
- `...thumb.jpg`
## Practical implication
For phase 1 design and experiments:
- the current repo already knows how to ingest these LT-1 scene folders as `RadarData`
- stack preparation should build on top of this asset layer
- the real unknown is not scene metadata parsing
- the real unknown is how to transform these scene folders into a stack layout acceptable to `stripmapStack`
@@ -1,92 +0,0 @@
# Phase 0 Sample Stack Selection
Updated: 2026-04-03
## Selected baseline sample
Current baseline sample stack:
- group key:
- `LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1`
- manifest:
- `experiments/isce2_sbas_timeseries/configs/sample_stack_e123p3_n46p1.json`
## Sample summary
- satellite:
- `LT1A`
- mode:
- `STRIP1`
- polarization:
- `HH`
- orbit direction:
- `DESCENDING`
- scene count:
- `5`
- dates:
- `20250118`
- `20250315`
- `20250510`
- `20250705`
- `20250830`
- recommended reference date:
- `20250510`
- receiving stations observed:
- `SYC`
- `KSC`
## Why this sample is useful
- It already satisfies a minimal SBAS smoke-test stack size.
- All scenes share the same:
- satellite
- imaging mode
- polarization
- orbit direction
- tile key
- The dates are evenly spaced enough to act as a first time-series experiment set.
## Important adjacent-tile signal
This sample is not isolated.
The same date sequence also appears in multiple neighboring descending tiles, including:
- `E123.5_N46.6`
- `E123.6_N47.0`
- `E123.8_N47.5`
- `E123.9_N48.0`
- `E124.5_N49.9`
- `E124.7_N50.3`
- `E124.8_N50.8`
- `E125.0_N51.3`
- `E125.1_N51.8`
- `E125.3_N52.2`
- `E125.5_N52.7`
These neighboring tiles share the same 5 acquisition dates:
- `20250118`
- `20250315`
- `20250510`
- `20250705`
- `20250830`
## Implication
This strongly suggests the data pool contains a larger repeated strip-family, not just isolated scenes.
Recommended experiment order:
1. Start with one tile-level stack smoke test using `E123.3_N46.1`.
2. If stack-prep works, expand to multiple adjacent tiles with the same date family.
3. Only after that, test a wider strip or mosaic strategy.
## Current risk judgment
The main remaining uncertainty is still not scene selection.
The main uncertainty is:
- how to convert LT-1 per-scene `tiff + meta.xml` folders
- into a stack layout and sensor input form acceptable to the ISCE2 stripmap stack workflow
@@ -1,140 +0,0 @@
# Phase 0 Stack Findings
Updated: 2026-04-03
## Confirmed
- Official stack tooling exists in the `isce2` env:
- `/home/administrator/miniconda3/envs/isce2/share/isce2/stripmapStack`
- `/home/administrator/miniconda3/envs/isce2/share/isce2/topsStack`
- `stackStripMap.py` exists and is the stripmap stack entry point.
- `prepStripmap4timeseries.py` exists in the official `stripmapStack` toolset.
- `stackStripMap.py` expects:
- an SLC root directory via `-s/--slc_directory`
- a DEM via `-d/--dem`
- an optional reference date via `-m/--reference_date`
- temporal and baseline thresholds
- The script scans date subdirectories under the SLC root.
- Default behavior looks for `<date>.raw` inside each acquisition directory.
- With `--nofocus`, it instead looks for `<date>.slc`.
- Deeper code inspection confirms:
- `topo.py` opens `<date>/data`
- `geo2rdr.py` opens each secondary `<date>/data`
- `refineSecondaryTiming` uses both `<date>.slc` and the acquisition directory as metadata roots
## Important implication
Official stack processing expects a stack-style input layout such as:
```text
SLC/
YYYYMMDD/
YYYYMMDD.raw
```
or, when data are already focused:
```text
SLC/
YYYYMMDD/
YYYYMMDD.slc
YYYYMMDD.slc.xml
data
```
This is different from the current repository's custom LT-1 single-pair production flow.
## Time-series bridge signal
- `prepStripmap4timeseries.py` takes:
- pair/interferogram directories
- baseline directory
- geometry directory
- shelve metadata directory
- The script writes `.rsc` sidecars and explicitly references `pysar`-style downstream usage.
This is useful because it confirms the official stripmap stack toolset already contains a bridge from stack outputs toward time-series preparation.
The weak point is still the LT-1 stack input/preparation stage, not the existence of a downstream time-series bridge.
## Existing repo bridge signal
The repository's current LT-1 single-pair pipeline already proves one important thing:
- `stripmapApp.py` can be driven with:
- `sensor name = LUTAN1`
- direct `tiff` input path
- direct `orbitFile` XML path
See:
- `backend/app/isce2_pipeline/run_lt1_dinsar_pipeline.py`
The generated XML writes:
- `Reference -> tiff`
- `Reference -> orbitFile`
- `Secondary -> tiff`
- `Secondary -> orbitFile`
This suggests a promising adapter direction:
- do not try to pretend LT-1 is ALOS or another officially prepared raw sensor
- instead, explore generating LT-1-aware stack configs directly from:
- scene `tiff`
- scene `meta.xml`
- converted orbit XML
That does not prove the official stack driver will accept this without modification.
But it is the strongest current indication for how a custom LT-1 `stack-prep` layer should be shaped.
## LT-1 / LUTAN1 signal so far
- ISCE core does include a `Lutan1.py` sensor module.
- But no direct `lutan` match was found in the `stripmapStack` helper scripts.
- The `stripmapStack` README examples and preparation hints mention:
- `prepRawALOS.py`
- `prepRawSensor.py`
- The README explicitly states automatic raw-data preparation support is currently oriented to:
- ALOS
- CSK
- `prepRawSensors.py` automatic raw detection currently covers:
- Envisat
- ERS CEOS
- ERS ENV
- ALOS1
- CSK
- `prepSlcSensors.py` automatic SLC detection currently covers:
- Envisat
- ALOS1
- CSK
- RSAT2
- TSX/TDX
- No LT-1 or LUTAN1 hook was found in these official stack preparation scripts.
## Interim conclusion
Current evidence suggests:
- ISCE2 core can parse LT-1/LUTAN1 at the sensor level.
- Official `stripmapStack` tooling is present.
- But the official stack preparation helpers do not currently advertise LT-1/LUTAN1 support.
- There is no direct evidence yet that LT-1 can be fed into the official stack helpers without an adapter step.
- `--nofocus` does not remove the need for acquisition metadata preparation.
- It still needs a per-date `data` shelve and an ISCE-style `.slc` image.
This means the working assumption should be:
- `LT-1 stack via official stripmapStack` is possible but unproven
- an LT-1-specific stack preparation or conversion layer is required unless an existing hidden tool can materialize `data` + `.slc` directly for LUTAN1
## Next checks
1. Implement a dry-run LT-1 stack-prep workspace generator against the selected sample manifest.
2. Design a scene materializer that transforms one LT-1 scene into:
- `YYYYMMDD.slc`
- `YYYYMMDD.slc.xml`
- `data`
3. Decide whether that materializer should:
- call ISCE/LUTAN1 directly, or
- reuse parts of the existing pair pipeline
4. After materialization is proven, run `stackStripMap.py --nofocus` on one tile-level stack
@@ -1,285 +0,0 @@
# Phase 1 Stack Generation Smoke Test
Updated: 2026-04-05
Follow-up note:
- MintPy SBAS continuation is now recorded separately in:
- `notes/PHASE2_MINTPY_SBAS_SMOKETEST.md`
## Goal
Validate that one LT-1 stack can be transformed from:
- per-scene `tiff + meta.xml + orbit.xml`
into:
- `stripmapStack --nofocus` compatible acquisition directories
- a generated stripmap stack work plan
without changing production code yet.
## Sample
- group key:
- `LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1`
- dates:
- `20250118`
- `20250315`
- `20250510`
- `20250705`
- `20250830`
- reference date:
- `20250510`
## Commands Used
1. Build dry-run stack workspace:
```text
C:\Users\Administrator\.conda\envs\InSAR\python.exe experiments\isce2_sbas_timeseries\scripts\build_lt1_stack_prep.py --manifest-path experiments\isce2_sbas_timeseries\configs\sample_stack_e123p3_n46p1.json
```
2. Materialize LT-1 acquisitions inside `Ubuntu-24.04`:
```text
wsl -d Ubuntu-24.04 /home/administrator/miniconda3/bin/conda run -n isce2 python /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/materialize_lt1_stack_scenes.py --stack-manifest /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_input_manifest.json
```
3. Run generated wrapper:
```text
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/run_stripmap_stack_dryrun.sh
```
4. Execute the frozen stack step chain inside `Ubuntu-24.04`:
```text
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_generated_stack_runfile_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1 run_01_reference
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_generated_stack_runfile_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1 run_02_focus_split
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_generated_stack_runfile_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1 run_03_geo2rdr_coarseResamp
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_generated_stack_runfile_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1 run_04_refineSecondaryTiming
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_generated_stack_runfile_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1 run_05_invertMisreg
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_generated_stack_runfile_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1 run_06_fineResamp
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_generated_stack_runfile_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1 run_07_grid_baseline
```
5. Regenerate the stack in `interferogram` workflow mode and execute the new pair-processing stage:
```text
C:\Users\Administrator\.conda\envs\InSAR\python.exe experiments\isce2_sbas_timeseries\scripts\build_lt1_stack_prep.py --manifest-path experiments\isce2_sbas_timeseries\configs\sample_stack_e123p3_n46p1.json --workflow interferogram
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/run_stripmap_stack_dryrun.sh
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_generated_stack_runfile_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1 run_08_igram
```
6. Prepare MintPy metadata in the dedicated `mintpy` env while bridging the working `isce2` Python package:
```text
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/install_mintpy_runtime_ubuntu2404.sh
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_mintpy_with_isce_ubuntu2404.sh prep_isce.py -f "/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/Igrams/*/filt_*.unw" -m /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/Igrams/20250118_20250315/referenceShelve/data.dat -b /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/baselines -g /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/geom_reference
```
## Confirmed Results
- All 5 acquisitions were materialized into:
- `scratch/lt1a_strip1_hh_descending_e123p3_n46p1/SLC/YYYYMMDD/`
- Each acquisition directory now contains:
- `YYYYMMDD.slc`
- `YYYYMMDD.slc.xml`
- `YYYYMMDD.slc.vrt`
- `data.dat/.dir/.bak`
- Example materialized output:
- `20250510.slc`
- size: `3262658784` bytes
- `stackStripMap.py --nofocus` ran successfully far enough to:
- discover all 5 acquisitions
- estimate stack baselines
- select interferometric pairs
- generate stack config files
- generate run files
## Baseline Snapshot
Relative to reference date `20250510`, the generated stack reported:
- `20250118`
- `-199.6873591838194`
- `20250315`
- `-89.0330513325973`
- `20250705`
- `-561.9056433404087`
- `20250830`
- `215.98283570831154`
The generated network reported:
- minimum connection degree:
- `4.0`
- number of pairs:
- `10`
## Generated Stack Work Products
Under `scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/`:
- `baselines/`
- `configs/`
- `run_files/`
- `pairs.pdf`
Generated run files:
- `run_01_reference`
- `run_02_focus_split`
- `run_03_geo2rdr_coarseResamp`
- `run_04_refineSecondaryTiming`
- `run_05_invertMisreg`
- `run_06_fineResamp`
- `run_07_grid_baseline`
## Important Runtime Fixes
- `matplotlib` was missing from the `isce2` env.
- fixed by installing it with `pip` inside the WSL `isce2` environment
- `stackStripMap.py` could not import `stripmapStack.Stack` by default.
- fixed by exporting:
- `PYTHONPATH=/home/administrator/miniconda3/envs/isce2/share/isce2`
- `PATH=/home/administrator/miniconda3/envs/isce2/share/isce2/stripmapStack:$PATH`
- the generated run files now include these prefixes automatically
- `run_01_reference` reached `topo` successfully but `createWaterMask.py` failed without Earthdata credentials.
- root cause:
- `SWBD` download requires `~/.netrc` for `urs.earthdata.nasa.gov`
- current judgment:
- local DEM is already sufficient for this experiment
- the only missing optional online input is the water-body mask download
- experimental fallback:
- `scripts/run_generated_stack_runfile_ubuntu2404.sh` now auto-generates a synthetic all-land `geom_reference/waterMask.rdr`
- the helper script is `scripts/create_synthetic_watermask.py`
- limitation:
- this fallback preserves stack execution but does not provide a true coastline mask
- LT-1 input preparation now has a shared helper:
- `backend/app/isce2_pipeline/lt1_input_resolver.py`
- purpose:
- centralize DEM resolution
- centralize orbit-pool resolution
- centralize LT-1 precise-orbit XML reuse or generation
- compatibility rule:
- this is a refactor of shared input-prep logic
- the original D-InSAR execution path was not removed
- `run_lt1_dinsar_pipeline.py` still keeps its original public workflow entry and now calls the helper internally
## Latest Execution Status
- `run_01_reference`
- `topo` completed successfully in `Ubuntu-24.04`
- local `geom_reference/waterMask.rdr` was synthesized from `shadowMask.rdr`
- `run_02_focus_split`
- completed successfully
- generated configs were effectively no-op under the current `--nofocus` contract
- `run_03_geo2rdr_coarseResamp`
- completed successfully in `Ubuntu-24.04`
- generated `offsets/<date>/range.off` and `azimuth.off` for:
- `20250118`
- `20250315`
- `20250705`
- `20250830`
- generated `coregSLC/Coarse/<date>/YYYYMMDD.slc` products for:
- `20250118`
- `20250315`
- `20250705`
- `20250830`
- runtime observation:
- this stage is long-running and mostly silent in the log file
- progress is easier to confirm from product directories than from stdout
- `run_04_refineSecondaryTiming`
- completed successfully in `Ubuntu-24.04`
- generated pair-level `refineSecondaryTiming/pairs/<pair>/misreg.*` for all 10 pairs
- log observation:
- `Bad match at level 1` and `correlation error` appeared in the log
- despite that noise, the stage exited `0` and downstream inversion succeeded
- `run_05_invertMisreg`
- completed successfully in `Ubuntu-24.04`
- generated date-level `refineSecondaryTiming/dates/<date>/misreg.*` for:
- `20250118`
- `20250315`
- `20250510`
- `20250705`
- `20250830`
- inversion observation:
- design matrix was reported as full rank
- RMSE in azimuth was `0.002341399255443996` pixels
- RMSE in range was `0.0027480408593210303` pixels
- `run_06_fineResamp`
- completed successfully in `Ubuntu-24.04`
- generated fine coregistered `merged/SLC/<date>/YYYYMMDD.slc` for all 5 dates
- each merged date directory now also includes:
- `referenceShelve/`
- `secondaryShelve/`
- `run_07_grid_baseline`
- completed successfully in `Ubuntu-24.04`
- generated `merged/baselines/<date>/` baseline grids for all 5 dates
- each date-level baseline directory now includes:
- raw baseline raster
- `.xml`
- `.vrt`
- `.full.vrt`
- `build_lt1_stack_prep.py --workflow interferogram`
- now regenerates the official stripmapStack command in `interferogram` mode instead of hard-coding `slc`
- regenerated run files now include:
- `run_08_igram`
- `run_08_igram`
- completed successfully in `Ubuntu-24.04`
- generated 10 pair directories under `stack_work/Igrams/`
- each pair now includes:
- wrapped interferogram `.int`
- amplitude `.amp`
- filtered interferogram `filt_*.int`
- coherence `filt_*.cor`
- unwrapped phase `filt_*_snaphu.unw`
- connected components `*.unw.conncomp`
- `referenceShelve/data.*`
- MintPy runtime bootstrap
- `scripts/install_mintpy_runtime_ubuntu2404.sh` created a dedicated WSL env:
- `mintpy`
- verified commands:
- `smallbaselineApp.py`
- `prep_isce.py`
- installed MintPy version:
- `1.6.2`
- `prep_isce.py`
- first run in the clean `mintpy` env failed because `mintpy.utils.isce_utils` imports `isce`
- experimental resolution:
- `scripts/run_mintpy_with_isce_ubuntu2404.sh` now bridges only the top-level `isce` package from the WSL `isce2` env into the `mintpy` env
- result:
- `prep_isce.py` completed successfully over the LT-1 `stripmapStack` outputs
- geometry `.rsc` files were written under `stack_work/geom_reference/`
- observation `.rsc` files were written for all 10 unwrapped interferograms under `stack_work/Igrams/*/`
## Current Boundary
This smoke test confirms:
- LT-1 scenes can be materialized into `stripmapStack` acquisition directories
- the official stripmap stack driver can build the stack work plan over those LT-1 products
- the generated `run_01` to `run_07` chain can complete offline in `Ubuntu-24.04` over the sample LT-1 stack
- local DEM plus local orbit data are sufficient for this stack-preparation stage
- Earthdata credentials are not a hard blocker for this experiment track because the wrapper can recover `run_01_reference` with a synthetic all-land `waterMask`
- the same LT-1 stack can be regenerated in `interferogram` workflow mode to produce 10 filtered and unwrapped pair products
- MintPy metadata preparation is now viable through the dedicated `mintpy` env plus the explicit ISCE bridge wrapper
This smoke test does not yet confirm:
- `smallbaselineApp.py` execution beyond `prep_isce.py`
- final time-series or velocity products
Follow-up status:
- both of the above were later confirmed in:
- `notes/PHASE2_MINTPY_SBAS_SMOKETEST.md`
## Next Tasks
1. Keep this note focused on stack-generation findings only.
2. Use `notes/PHASE2_MINTPY_SBAS_SMOKETEST.md` for MintPy SBAS runtime conclusions.
3. Promote the stable runtime and artifact contract into backend workflow code and `docs/`.
@@ -1,157 +0,0 @@
# Phase 2 Bridge Smoketest
Date: 2026-04-06
## Scope
Validate the current SBAS bridge chain with the previously verified LT-1 sample stack:
1. `build_lt1_stack_prep.py`
2. `materialize_lt1_stack_scenes.py`
3. `build_lt1_stack_prep.py` refresh
This run validates the current bridge boundary only:
- raw LT-1 scenes
- local precise orbit pool
- local prepared DEM
- fresh scratch workspace
It does not run:
- `stripmapStack`
- MintPy
- geocode/export/publish
## Inputs
- sample manifest:
- `experiments/isce2_sbas_timeseries/configs/sample_stack_e123p3_n46p1.json`
- orbit pool:
- `/mnt/d/orbit_pools/isce2`
- DEM:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/inputs/dem/stack_dem_window.wgs84`
## Workspace
- scratch root:
- `experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406`
## Result
- final readiness: `True`
- scene count: `5`
- reference date: `20250510`
- all orbits resolved: `True`
- all `.slc/.slc.xml` present: `True`
- all `data` shelves present: `True`
## Materialization Summary
- dates:
- `20250118`
- `20250315`
- `20250510`
- `20250705`
- `20250830`
- status counts:
- `materialized: 5`
- total bytes written:
- `16313641344`
## Artifacts
- selected manifest used for this WSL run:
- `experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/selected_stack_manifest_wsl.json`
- generated stack manifest:
- `experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_input_manifest.json`
- materialization summary:
- `experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/materialization_summary.json`
- generated stack dry-run wrapper:
- `experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/run_stripmap_stack_dryrun.sh`
- synthetic water-mask recovery report:
- `experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/logs/run_01_reference.synthetic_watermask.json`
## Finding
`build_lt1_stack_prep.py` currently reads `scene["tiff_path"]` and `scene["meta_path"]` directly.
When the script is run inside WSL against the sample manifest, the original `F:\...` Windows paths are not readable as Linux paths.
For this smoketest, a temporary WSL-path manifest copy was generated and used:
- `selected_stack_manifest_wsl.json`
This is an experiment-side workaround only.
No production/system logic was changed for this run.
## Update 2026-04-07
The same fresh workspace was then continued through the stripmap stack run files.
### Additional Result
- `run_01_reference`
- reached `createWaterMask`
- failed on remote `SWBD` retrieval from:
- `https://e4ftl01.cr.usgs.gov/MEASURES/SRTMSWBD.003/...`
- recovered with a local synthetic all-land `waterMask.rdr`
- `run_02_focus_split`
- completed
- `run_03_geo2rdr_coarseResamp`
- completed
- `run_04_refineSecondaryTiming`
- completed
- `run_05_invertMisreg`
- completed
- `run_06_fineResamp`
- completed
- `run_07_grid_baseline`
- completed
- `run_08_igram`
- completed through interferogram generation, filtering, coherence, and `snaphu` unwrapping
### Interferogram Snapshot
- pair count on disk:
- `10`
- verified pair folders:
- `20250118_20250315`
- `20250118_20250510`
- `20250118_20250705`
- `20250118_20250830`
- `20250315_20250510`
- `20250315_20250705`
- `20250315_20250830`
- `20250510_20250705`
- `20250510_20250830`
- `20250705_20250830`
- verified key files in every pair directory:
- `filt_<date12>.int`
- `filt_<date12>.cor`
- `filt_<date12>_snaphu.unw`
- `filt_<date12>_snaphu.unw.conncomp`
### Finding Update
The original `run_generated_stack_runfile_ubuntu2404.sh` fallback only matched the `.netrc` credential failure text.
This workspace showed a second offline failure mode:
- `createWaterMask` started normally
- ISCE2 `DataRetriever` failed during `SWBD` file retrieval
- the wrapper therefore did not auto-recover on the first attempt
The experiment helper has now been widened to recognize both:
- missing Earthdata credential text
- direct `SWBD` retrieval failure text from `createWaterMask`
No production/system runtime was changed by this fix.
## Recommended Next Step
Use this fresh workspace to continue with:
1. unified-env MintPy smoketest using:
- `configs/phase2_bridge_smoketest_20260406_smallbaseline.cfg`
2. publish-style geocode/export if MintPy succeeds
3. compare this fresh replay with the earlier baseline workspace
@@ -1,200 +0,0 @@
# Phase 2 MintPy SBAS Smoke Test
Updated: 2026-04-05
Follow-up note:
- publish-style geocode/export continuation is now recorded separately in:
- `notes/PHASE3_PUBLISH_EXPORT_SMOKETEST.md`
## Goal
Validate that the LT-1 sample stack can continue from `stripmapStack` interferogram products into MintPy SBAS outputs under `Ubuntu-24.04`.
## Sample
- group key:
- `LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1`
- dates:
- `20250118`
- `20250315`
- `20250510`
- `20250705`
- `20250830`
- pair count:
- `10`
- reference date:
- `20250510`
- reference point:
- `y/x = 1994,52`
## Successful Run
Successful SBAS smoke-test work directory:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas_v5`
Successful WSL command:
```text
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_mintpy_sbas_smoketest_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/configs/sample_smallbaseline_lt1_e123p3_n46p1.cfg /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas_v5
```
This run completed through:
- `load_data`
- `modify_network`
- `reference_point`
- `quick_overview`
- `invert_network`
- `reference_date`
- `velocity`
Disabled for this first offline smoke test:
- unwrap-error correction
- solid-earth-tide correction
- ionosphere correction
- troposphere correction
- deramp
- topographic residual correction
- geocode
## Output Snapshot
Generated under `mintpy_sbas_v5/`:
- `timeseries.h5`
- size: `82576752` bytes
- `velocity.h5`
- size: `83086408` bytes
- `temporalCoherence.h5`
- size: `16632424` bytes
- `maskTempCoh.h5`
- size: `4136792` bytes
- `avgSpatialCoh.h5`
- size: `16631784` bytes
- `numTriNonzeroIntAmbiguity.h5`
- size: `16632000` bytes
- `numTriNonzeroIntAmbiguity.png`
- size: `232878` bytes
Quality summary from the successful run:
- strict valid pixels for inversion:
- `1219001 / 4076199`
- `29.91%`
- reliable pixels in `maskTempCoh.h5` with threshold `0.7`:
- `62987`
## Required Runtime Decisions
### 1. Keep MintPy separate from ISCE2
- keep stack processing in WSL conda env:
- `isce2`
- keep MintPy in WSL conda env:
- `mintpy`
Reason:
- avoids mutating the already working ISCE2 processing env on the development machine
### 2. Bridge only the top-level `isce` package
Required helper:
- `scripts/run_mintpy_with_isce_ubuntu2404.sh`
Current rule:
- do not add the entire `isce2` `site-packages` into `PYTHONPATH`
- only bridge the top-level `isce` package into a cache directory
Reason:
- adding the whole `site-packages` caused MintPy to import `h5py` from the wrong env and fail during `load_data`
### 3. Do not load `wrapPhase` for this LT-1 smoke test
Current config rule:
- `mintpy.load.intFile = None`
Reason:
- loading `filt_*.int` into MintPy `wrapPhase` caused HDF5 type-conversion failure during `load_data`
### 4. Build a strict `maskAllValid.h5` before inversion
Required helper:
- `scripts/create_mintpy_all_ifgram_mask.py`
Current rule:
- keep only pixels that are finite and non-zero in all unwrapped interferograms
- also require non-zero connected components in all interferograms
Reason:
- this reduces unstable partial-network pixels before SBAS inversion
### 5. Use the repo-local patched launcher
Required helper:
- `scripts/run_smallbaselineApp_patched.py`
Current workaround:
- patch `mintpy.ifgram_inversion.estimate_timeseries()` at runtime
- coerce shape-`(1,)` inversion-quality output into a scalar for the single-pixel partial-network branch
Reason:
- MintPy `1.6.2` hit a `ValueError: setting an array element with a sequence`
- failure point:
- `mintpy/ifgram_inversion.py`
- partial-network pixel branch inside `run_ifgram_inversion_patch()`
Current judgment:
- this is a MintPy runtime issue in the current environment
- it is better to keep the workaround in repo-local launcher code than silently editing the third-party env
## Current Boundary
This smoke test now confirms:
- LT-1 `stripmapStack` outputs can be loaded by MintPy in the dedicated `mintpy` env
- the LT-1 sample stack can be inverted into radar-coordinate `timeseries.h5`
- the LT-1 sample stack can generate radar-coordinate `velocity.h5`
- the current repo-local workaround chain is reproducible in `Ubuntu-24.04`
This smoke test does not yet confirm:
- geocoded SBAS exports
- atmospheric or DEM-residual correction quality
- product publishing into backend `psinsar` catalog
- frontend rendering of published SBAS products
## System Embedding Implications
The current experiment suggests the future backend runtime contract should be:
1. Run ISCE2 stack workflow in WSL `isce2`.
2. Run MintPy through the repo-controlled bridge runner instead of calling upstream `smallbaselineApp.py` directly.
3. Generate a strict inversion mask after `load_data`.
4. Persist the following files as first-class workflow artifacts:
- `timeseries.h5`
- `velocity.h5`
- `temporalCoherence.h5`
- `maskTempCoh.h5`
- `numTriNonzeroIntAmbiguity.h5`
- `numTriNonzeroIntAmbiguity.png`
5. Convert these into a stable publish manifest before catalog registration.
Current sample publish-manifest draft:
- `configs/sample_psinsar_manifest_lt1_e123p3_n46p1.json`
@@ -1,142 +0,0 @@
# Phase 3 Publish Export Smoke Test
Updated: 2026-04-06
## Goal
Validate that the successful MintPy SBAS experiment can be converted into a publish-style artifact bundle without touching the main system.
## Inputs
Source MintPy work directory:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas_v5`
Source stack:
- group key:
- `LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1`
- dates:
- `20250118`
- `20250315`
- `20250510`
- `20250705`
- `20250830`
## Successful Command
```text
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/export_mintpy_publish_products_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas_v5 /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/publish/mintpy_sbas_v5
```
## Current Export Scripts
- `scripts/export_mintpy_publish_products_ubuntu2404.sh`
- geocode core MintPy outputs
- convert selected outputs to GeoTIFF
- copy preview and metadata files
- build root `manifest.json`
- `scripts/build_mintpy_publish_bundle.py`
- generate `preview/velocity_preview.png`
- generate `metadata/source_quality_summary.json`
- generate publish-style root `manifest.json`
## Geocode Contract
Current experiment settings:
- lookup source:
- `inputs/geometryRadar.h5`
- output pixel size:
- latitude step:
- `-0.000185185`
- longitude step:
- `0.000185185`
- interpolation:
- `nearest`
Observed geocoded grid:
- extent:
- south:
- `45.80391`
- north:
- `46.42206`
- west:
- `122.930244`
- east:
- `123.76654`
- shape:
- rows:
- `3338`
- columns:
- `4516`
## Generated Publish Bundle
Successful publish-style directory:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/publish/mintpy_sbas_v5`
Main outputs:
- `manifest.json`
- `assets/geo_timeseries.h5`
- `assets/geo_velocity.h5`
- `assets/geo_temporalCoherence.h5`
- `assets/geo_maskTempCoh.h5`
- `assets/velocity.tif`
- `assets/temporalCoherence.tif`
- `assets/maskTempCoh.tif`
- `preview/velocity_preview.png`
- `preview/numTriNonzeroIntAmbiguity.png`
- `metadata/smallbaselineApp.cfg`
- `metadata/source_quality_summary.json`
## Current Interpretation
This confirms:
- the experiment now supports radar-coordinate MintPy inversion
- the experiment also supports geocoded HDF5 exports
- the experiment can produce publish-style GeoTIFF outputs
- the experiment can build a stable manifest-driven bundle outside the MintPy work directory
This does not yet confirm:
- direct backend catalog registration
- frontend rendering against the real system APIs
- whether `EPSG:4326` should remain the final publish CRS decision
## Important Notes
### 1. Current CRS assumption
`save_gdal.py` warned that no explicit `EPSG` metadata was found and assumed:
- `EPSG:4326`
Current judgment:
- acceptable for this experiment because the geocoded outputs are in latitude/longitude grids
- should still be checked when formalizing the production publish contract
### 2. Group key in the generated manifest
Because PowerShell treats `|` specially, passing group keys on the command line is awkward from Windows.
Current practical rule:
- the sample manifest stored in git remains the clean reference:
- `configs/sample_psinsar_manifest_lt1_e123p3_n46p1.json`
- the generated publish bundle manifest can be post-filled or generated from backend metadata later
## System Embedding Implication
At this point the experiment-layer chain is split cleanly into three stages:
1. `stripmapStack` preprocessing
2. MintPy SBAS inversion
3. geocode + publish-bundle export
That means the future system workflow can wire them as separate workflow steps without changing the validated experiment logic first.
@@ -1,120 +0,0 @@
# Phase 4 Unified-Environment Decision
Updated: 2026-04-06
## Decision
Current experiment preference:
- prefer the unified WSL environment for SBAS experiment work
Current production safety rule:
- do not replace or mutate the existing D-InSAR production environment
- keep pair-oriented D-InSAR on the existing WSL `isce2` env
- keep the current backend/public D-InSAR entry unchanged
Current environment split:
- D-InSAR production baseline:
- `isce2`
- SBAS experiment preferred runtime:
- `isce2_mintpy_v1`
## Why This Decision Is Reasonable
The unified env has now completed the full current experiment chain:
- MintPy command invocation without the `isce` bridge
- `load_data`
- strict-mask generation
- `modify_network -> velocity`
- publish export to geocoded HDF5, GeoTIFF, preview, and `manifest.json`
This makes the unified env a valid experiment baseline.
At the same time, the existing pair-oriented D-InSAR route is already working and should not be destabilized just to simplify the SBAS experiment runtime.
The safest rule is therefore:
- let SBAS experiments move forward in a separate unified env
- do not touch the current `isce2` production env used by D-InSAR
## Evidence
Successful unified env:
- `/home/administrator/miniconda3/envs/isce2_mintpy_v1`
Successful unified SBAS work directory:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas_unified_v1`
Successful unified publish directory:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/publish/mintpy_sbas_unified_v1`
Matched quality indicators:
- `maskAllValid`:
- `1219001 / 4076199`
- `29.91%`
- `maskTempCoh`:
- `62987 / 4076199`
- `1.55%`
Key package difference:
- current `isce2` env does not provide the MintPy-side package set needed for this SBAS route
- current `isce2_mintpy_v1` env includes:
- `mintpy`
- `cartopy`
- `pyaps3`
- `pykml`
- `cvxopt`
## What This Decision Does Not Mean
It does not mean:
- the backend should immediately switch to unified-env execution
- the bridge route must be deleted now
- the current D-InSAR runtime should be modified
It only means:
- for the next experiment steps, unified env is the preferred path
- for current production safety, `isce2` remains untouched
## Required Guardrails
For the next phase, keep these rules:
- do not install MintPy into the existing `isce2` env
- do not redirect current D-InSAR scripts to `isce2_mintpy_v1`
- do not remove the bridge-based helpers yet
- keep all SBAS work in experiment scripts, notes, and scratch directories
## Reproducibility
Environment snapshots should be exported and kept with the experiment record.
Current snapshot command:
```text
wsl -d Ubuntu-24.04 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/export_phase4_env_snapshots_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/configs/env_snapshots/20260406
```
## Next Stable Experiment Steps
Before any system integration work:
1. Keep the unified env as the default SBAS experiment runtime.
2. Preserve environment snapshots for both `isce2` and `isce2_mintpy_v1`.
3. Write one comparison note focused on:
- runtime simplicity
- reproducibility
- remaining workarounds
- risk to D-InSAR production
4. Optionally repeat the chain on one more LT-1 sample stack.
5. Only after the experiment is stable, design the system integration boundary.
@@ -1,221 +0,0 @@
# Phase 4 Unified-Environment Experiment
Updated: 2026-04-06
## Goal
Validate whether the current WSL `isce2` runtime can be cloned and extended with MintPy so that the SBAS experiment can run without the temporary `isce` bridge helper.
This is still an experiment-layer task.
Do not change the production backend yet.
## Why run this phase
The bridge-based route is already validated, but a unified environment may be cleaner because:
- many ISCE2 + MintPy users operate in one environment
- command invocation becomes simpler
- future worker deployment may be easier if one runtime is stable
The bridge-based route still remains the fallback baseline until this phase is verified.
## Current Known Starting Point
WSL distro:
- `Ubuntu-24.04`
Current environments observed on 2026-04-06:
- `isce2`
- `mintpy`
Observed package state:
- `isce2` env:
- `isce2 2.6.4`
- `h5py 3.15.1`
- `mintpy` not installed
- dedicated `mintpy` env:
- `mintpy 1.6.3`
## Initial Hypothesis
Expected best-case outcome:
- clone `isce2` into `isce2_mintpy`
- install `mintpy` directly into the clone
- reuse the same repo-local strict-mask and patched-launcher helpers
- run the same LT-1 smoke test without the `isce` bridge wrapper
Main risk areas:
- package solver may replace or downgrade key ISCE2-side numeric dependencies
- MintPy may still require the same repo-local runtime workaround even in a unified env
- GDAL / h5py / pyaps3 dependency changes may alter the known-good stack behavior
## Reproducible Commands
### 1. Bootstrap the unified env
```text
wsl -d Ubuntu-24.04 env TARGET_ENV=isce2_mintpy_v1 BOOTSTRAP_MODE=recreate USE_TUNA_MIRROR=1 MINTPY_SPEC=mintpy=1.6.3 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/install_mintpy_into_cloned_isce2_env_ubuntu2404.sh
```
Why `BOOTSTRAP_MODE=recreate`:
- direct `conda create --clone` was not stable enough for this machine
- it still followed source-package URLs and hit channel/TOS friction
- the successful path was:
- export the current `isce2` dependency list
- recreate the env through Tsinghua mirror channels
- reinstall exported pip packages
- install MintPy into the recreated env
Optional environment override:
```text
TARGET_ENV=isce2_mintpy_v1 MINTPY_SPEC='mintpy=1.6.3'
```
### 2. Run MintPy commands directly inside the unified env
```text
wsl -d Ubuntu-24.04 env MINTPY_ENV=isce2_mintpy_v1 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_mintpy_unified_env_ubuntu2404.sh prep_isce.py -h
```
### 3. Re-run the current LT-1 SBAS smoke test in the unified env
```text
wsl -d Ubuntu-24.04 env MINTPY_ENV=isce2_mintpy_v1 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_mintpy_sbas_unified_env_smoketest_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/configs/sample_smallbaseline_lt1_e123p3_n46p1.cfg /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas_unified_v1
```
### 4. Export publish bundle in the unified env
```text
wsl -d Ubuntu-24.04 env MINTPY_ENV=isce2_mintpy_v1 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/export_mintpy_publish_products_unified_env_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas_unified_v1 /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/publish/mintpy_sbas_unified_v1
```
## Comparison Checklist
When this phase is executed, compare it against the bridge route on:
- package versions after install
- whether `prep_isce.py` imports cleanly without bridge
- whether `load_data` succeeds
- whether the strict-mask step is still required
- whether the patched launcher is still required
- whether output files match the existing bridge-based artifact set
- whether geocode/export still succeeds from the unified env
## Current Status
Successful environment:
- `/home/administrator/miniconda3/envs/isce2_mintpy_v1`
Observed package/version state in the successful unified env:
- `conda list` shows:
- `mintpy 1.6.3`
- runtime `mintpy.__version__` reports:
- `1.6.2`
- `isce` import path:
- `/home/administrator/miniconda3/envs/isce2_mintpy_v1/lib/python3.11/site-packages/isce/__init__.py`
- `mintpy` import path:
- `/home/administrator/miniconda3/envs/isce2_mintpy_v1/lib/python3.11/site-packages/mintpy/__init__.py`
- `h5py`:
- `3.15.1`
Successful unified-env SBAS work directory:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/stack_work/mintpy_sbas_unified_v1`
Successful unified-env publish directory:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1/publish/mintpy_sbas_unified_v1`
Validated in the unified env:
- `prep_isce.py -h` works without the `isce` bridge
- `load_data` succeeded
- strict-mask generation succeeded
- `modify_network -> velocity` succeeded
- publish export succeeded through geocoded HDF5, GeoTIFF, preview, and `manifest.json`
Observed unified-env output set under `mintpy_sbas_unified_v1/`:
- `timeseries.h5`
- `velocity.h5`
- `temporalCoherence.h5`
- `maskTempCoh.h5`
- `maskAllValid.h5`
- `avgSpatialCoh.h5`
- `numTriNonzeroIntAmbiguity.h5`
- `numTriNonzeroIntAmbiguity.png`
Observed publish bundle under `publish/mintpy_sbas_unified_v1/`:
- `manifest.json`
- `assets/geo_timeseries.h5`
- `assets/geo_velocity.h5`
- `assets/geo_temporalCoherence.h5`
- `assets/geo_maskTempCoh.h5`
- `assets/velocity.tif`
- `assets/temporalCoherence.tif`
- `assets/maskTempCoh.tif`
- `preview/velocity_preview.png`
- `preview/numTriNonzeroIntAmbiguity.png`
- `metadata/smallbaselineApp.cfg`
- `metadata/source_quality_summary.json`
Quality summary matched the bridge-based route:
- `maskAllValid`:
- `1219001 / 4076199`
- `29.91%`
- `maskTempCoh`:
- `62987 / 4076199`
- `1.55%`
Still required in the unified env:
- strict `maskAllValid.h5` before inversion
- repo-local patched `smallbaselineApp` launcher
Prepared:
- unified-env bootstrap script
- unified-env MintPy runner
- unified-env SBAS smoke-test runner
- unified-env publish-export wrapper
Completed:
- actual clone + install execution
- actual smoke-test result capture
- actual publish-export capture
Pending:
- deeper comparison of output metadata against the bridge route
- decide whether unified env or bridge env should be the default production candidate
- decide whether to pin the runtime to the conda package label `1.6.3` or the internal MintPy version string `1.6.2`
## Current Judgment
At experiment level, the unified environment is now viable.
This phase confirms:
- the current LT-1 SBAS route does not fundamentally require the `isce` bridge
- a recreated `isce2 + mintpy` WSL env can complete:
- MintPy load/inversion
- geocode/export
- publish-bundle generation
Current recommendation:
- keep the bridge route as the already-known baseline until a fuller diff is written
- but treat the unified env as a valid candidate for the future production runtime
@@ -1,193 +0,0 @@
# Phase 4 Unified-Env Replay On Fresh Phase-2 Workspace
Updated: 2026-04-08
## Goal
Re-run the already validated unified-env SBAS route on the fresh workspace:
- `scratch/phase2_bridge_smoketest_20260406`
This checks that the current experiment does not rely on the older sample workspace only.
## Inputs
- WSL distro:
- `Ubuntu-24.04`
- unified env:
- `/home/administrator/miniconda3/envs/isce2_mintpy_v1`
- stack config:
- `configs/phase2_bridge_smoketest_20260406_smallbaseline.cfg`
- stack workspace:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406`
- MintPy work dir:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/mintpy_sbas_unified_phase2_20260407`
- publish dir:
- `/mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/publish/mintpy_sbas_unified_phase2_20260407`
## Successful Commands
Unified-env MintPy smoke test:
```text
wsl -d Ubuntu-24.04 env MINTPY_ENV=isce2_mintpy_v1 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/run_mintpy_sbas_unified_env_smoketest_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/configs/phase2_bridge_smoketest_20260406_smallbaseline.cfg /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/mintpy_sbas_unified_phase2_20260407
```
Unified-env publish export:
```text
wsl -d Ubuntu-24.04 env MINTPY_ENV=isce2_mintpy_v1 bash /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scripts/export_mintpy_publish_products_unified_env_ubuntu2404.sh /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/stack_work/mintpy_sbas_unified_phase2_20260407 /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/phase2_bridge_smoketest_20260406/publish/mintpy_sbas_unified_phase2_20260407
```
## Result
The fresh workspace replay succeeded through:
- `load_data`
- strict `maskAllValid.h5` generation
- `modify_network`
- `reference_point`
- `quick_overview`
- `invert_network`
- `reference_date`
- `velocity`
- geocode/export
- publish-bundle generation
This confirms the current fresh workspace now reaches the same experiment boundary as the earlier baseline sample:
- radar-coordinate MintPy runtime products
- geocoded publish bundle
## Runtime Output Snapshot
Generated under `stack_work/mintpy_sbas_unified_phase2_20260407/`:
- `timeseries.h5`
- `82579888` bytes
- `velocity.h5`
- `83086408` bytes
- `temporalCoherence.h5`
- `16632424` bytes
- `maskTempCoh.h5`
- `4136848` bytes
- `maskAllValid.h5`
- `4095215` bytes
- `avgSpatialCoh.h5`
- `16631784` bytes
- `numTriNonzeroIntAmbiguity.h5`
- `16632000` bytes
- `numTriNonzeroIntAmbiguity.png`
- `232923` bytes
## Publish Bundle Snapshot
Generated under `publish/mintpy_sbas_unified_phase2_20260407/`:
- `manifest.json`
- `32442` bytes
- `assets/geo_timeseries.h5`
- `304443360` bytes
- `assets/geo_velocity.h5`
- `305627744` bytes
- `assets/geo_temporalCoherence.h5`
- `61141152` bytes
- `assets/geo_maskTempCoh.h5`
- `15260536` bytes
- `assets/velocity.tif`
- `60318026` bytes
- `assets/temporalCoherence.tif`
- `60318026` bytes
- `assets/maskTempCoh.tif`
- `15094802` bytes
- `preview/velocity_preview.png`
- `813928` bytes
- `preview/numTriNonzeroIntAmbiguity.png`
- `232923` bytes
- `metadata/smallbaselineApp.cfg`
- `26419` bytes
- `metadata/source_quality_summary.json`
- `403` bytes
## Quality Summary
From `metadata/source_quality_summary.json`:
- `maskAllValid`
- `1219067 / 4076199`
- `29.91%`
- `maskTempCoh`
- `63092 / 4076199`
- `1.55%`
- preview stretch:
- `vmin = -0.2251889556646347`
- `vmax = 0.2251889556646347`
## Findings
### 1. The fresh workspace replay is reproducible
The new workspace produced:
- the full 10-pair interferogram stack
- MintPy `timeseries.h5`
- MintPy `velocity.h5`
- publish-layer geocoded HDF5 / GeoTIFF / preview / `manifest.json`
This is the current strongest experiment proof that the SBAS route is not tied to the earlier historical scratch directory.
### 2. The same two MintPy experiment helpers are still required
The unified env still depends on:
- `create_mintpy_all_ifgram_mask.py`
- `run_smallbaselineApp_patched.py`
Current interpretation:
- unified env removes the temporary `isce` bridge
- it does not remove the current strict-mask or patched-launcher workarounds
### 3. Offline water-mask strategy remains valid
This replay consumed the synthetic all-land `waterMask.rdr` created earlier in the fresh workspace.
No Earthdata / `SWBD` download was needed for the MintPy or publish stages.
### 4. Geocode/export completed with a tolerable warning
`save_gdal.py` warned that no EPSG / UTM metadata was found and assumed:
- `EPSG:4326`
For this experiment chain, that is acceptable because the export was already driven by latitude / longitude lookup plus explicit `--lalo` sampling.
This warning should still be recorded for later production hardening.
### 5. `group_key` should come from system metadata
This host-side replay exported a valid publish bundle, and the bundle should carry:
- `"group_key": "LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1"`
Reason:
- the bundle builder accepts `group_key` as a CLI argument
- the LT-1 group key contains pipe characters:
- `LT1A|STRIP1|HH|DESCENDING|E123.3_N46.1`
- when invoked naively from Windows PowerShell into WSL, the value may be truncated or split by the host shell
Current judgment:
- this is not a blocker for the SBAS scientific chain
- future system embedding should write `group_key` from task/run metadata inside backend code instead of relying on manual shell arguments
## Current Judgment
The current fresh workspace now confirms the full experiment chain:
- `raw LT-1 -> stripmapStack -> MintPy SBAS -> geocode/export -> publish bundle`
At experiment level, the unified env remains the preferred SBAS runtime.
At production-safety level, the existing D-InSAR `isce2` environment should still remain untouched.
@@ -1 +0,0 @@
@@ -1,647 +0,0 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import importlib.util
import json
import re
import sys
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path, PurePosixPath
from typing import Any, Dict, List, Optional
REPO_ROOT = Path(__file__).resolve().parents[3]
DEFAULT_MANIFEST_PATH = (
REPO_ROOT
/ "experiments"
/ "isce2_sbas_timeseries"
/ "configs"
/ "sample_stack_e123p3_n46p1.json"
)
DEFAULT_SHARED_ENV_NAME = "insar_wsl_v1"
DEFAULT_CONDA_ROOT_WSL = "/home/administrator/miniconda3"
DEFAULT_CONDA_WSL = f"{DEFAULT_CONDA_ROOT_WSL}/bin/conda"
SUPPORTED_STACK_WORKFLOWS = ("slc", "interferogram", "ionosphere")
def windows_to_wsl(path: str | Path) -> str:
text = str(path)
match = re.match(r"^([A-Za-z]):[\\/](.*)$", text)
if not match:
return text.replace("\\", "/")
drive = match.group(1).lower()
tail = match.group(2).replace("\\", "/").lstrip("/")
return f"/mnt/{drive}/{tail}"
def load_isce2_input_helper_module():
helper_path = REPO_ROOT / "backend" / "app" / "isce2_pipeline" / "lt1_input_resolver.py"
spec = importlib.util.spec_from_file_location("lt1_orbit_helper", helper_path)
if spec is None or spec.loader is None:
raise RuntimeError(f"Unable to load orbit helper module: {helper_path}")
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
ISCE2_INPUT_HELPER = load_isce2_input_helper_module()
def _default_python_wsl(env_name: str) -> str:
normalized_env = str(env_name or "").strip() or DEFAULT_SHARED_ENV_NAME
return f"{DEFAULT_CONDA_ROOT_WSL}/envs/{normalized_env}/bin/python"
def _resolve_runtime_paths(env_values: Dict[str, Any]) -> Dict[str, str]:
env_name = str(
env_values.get("TIMESERIES_ENV_NAME")
or env_values.get("WSL_SHARED_CONDA_ENV")
or DEFAULT_SHARED_ENV_NAME
).strip() or DEFAULT_SHARED_ENV_NAME
python_wsl = str(
env_values.get("TIMESERIES_PYTHON")
or env_values.get("WSL_SHARED_PYTHON")
or env_values.get("ISCE2_PYTHON")
or _default_python_wsl(env_name)
).strip() or _default_python_wsl(env_name)
python_path = PurePosixPath(python_wsl)
env_root = python_path.parent.parent
conda_root = env_root.parent.parent
conda_bin_wsl = str(conda_root / "bin" / "conda")
isce2_share_wsl = str(env_root / "share" / "isce2")
stripmap_stack_dir_wsl = str(PurePosixPath(isce2_share_wsl) / "stripmapStack")
stack_script_wsl = str(PurePosixPath(stripmap_stack_dir_wsl) / "stackStripMap.py")
stack_text_cmd = (
f"export PATH={stripmap_stack_dir_wsl}:$PATH; "
f"export PYTHONPATH={stripmap_stack_dir_wsl}:{isce2_share_wsl}${{PYTHONPATH:+:$PYTHONPATH}}; "
)
return {
"env_name": env_name,
"python_wsl": python_wsl,
"conda_bin_wsl": conda_bin_wsl,
"isce2_share_wsl": isce2_share_wsl,
"stripmap_stack_dir_wsl": stripmap_stack_dir_wsl,
"stack_script_wsl": stack_script_wsl,
"stack_text_cmd": stack_text_cmd,
}
def require_file(path: Path, label: str) -> None:
if not path.exists():
raise FileNotFoundError(f"Missing {label}: {path}")
def shelve_stem_exists(stem: Path) -> bool:
for suffix in ("", ".db", ".dat", ".dir", ".bak"):
if Path(str(stem) + suffix).exists():
return True
return False
@dataclass
class ScenePlan:
date: str
target_dir_windows: str
target_dir_wsl: str
source_scene_json_windows: str
source_scene_json_wsl: str
source_tiff_windows: str
source_tiff_wsl: str
source_meta_windows: str
source_meta_wsl: str
orbit_xml_windows: Optional[str]
orbit_xml_wsl: Optional[str]
orbit_xml_exists: bool
orbit_resolution_mode: Optional[str]
orbit_resolution_error: Optional[str]
source_exists: bool
scene_start_utc: str
scene_stop_utc: str
orbit_window_start_utc: str
orbit_window_stop_utc: str
expected_slc_windows: str
expected_slc_wsl: str
expected_slc_xml_windows: str
expected_slc_xml_wsl: str
expected_data_shelve_windows: str
expected_data_shelve_wsl: str
materialized_slc_exists: bool
materialized_data_exists: bool
stack_ready: bool
status: str
def build_scene_plan(
scene: Dict[str, Any],
slc_root: Path,
orbit_pool: Optional[Path],
orbit_stage_dir: Path,
margin_sec: float,
) -> ScenePlan:
date = str(scene["imaging_date"])
satellite = str(scene["satellite"])
source_tiff = Path(scene["tiff_path"])
source_meta = Path(scene["meta_path"])
require_file(source_tiff, f"scene TIFF for {date}")
require_file(source_meta, f"scene meta XML for {date}")
scene_start_dt, scene_stop_dt = ISCE2_INPUT_HELPER.parse_scene_window(source_meta, margin_sec=0.0)
orbit_window_start_dt, orbit_window_stop_dt = ISCE2_INPUT_HELPER.parse_scene_window(
source_meta,
margin_sec=margin_sec,
)
scene_start_utc = scene_start_dt.isoformat()
scene_stop_utc = scene_stop_dt.isoformat()
orbit_window_start_utc = orbit_window_start_dt.isoformat()
orbit_window_stop_utc = orbit_window_stop_dt.isoformat()
target_dir = slc_root / date
expected_slc = target_dir / f"{date}.slc"
expected_slc_xml = target_dir / f"{date}.slc.xml"
expected_data = target_dir / "data"
source_scene_json = target_dir / "source_scene.json"
orbit_xml: Optional[Path] = None
orbit_resolution_mode: Optional[str] = None
orbit_resolution_error: Optional[str] = None
if orbit_pool is not None:
try:
orbit_resolution = ISCE2_INPUT_HELPER.ensure_lt1_orbit_xml(
date_yyyymmdd=date,
satellite=satellite,
annotation_xml=source_meta,
orbit_root=orbit_pool,
orbit_output_dir=orbit_stage_dir,
margin_sec=margin_sec,
)
orbit_xml = orbit_resolution.path
orbit_resolution_mode = orbit_resolution.source
except Exception as exc:
orbit_resolution_error = str(exc)
materialized_slc_exists = expected_slc.exists() and expected_slc_xml.exists()
materialized_data_exists = shelve_stem_exists(expected_data)
stack_ready = bool(orbit_xml and materialized_slc_exists and materialized_data_exists)
if not orbit_xml:
status = "missing_orbit_xml"
elif not materialized_slc_exists and not materialized_data_exists:
status = "waiting_for_scene_materializer"
elif not materialized_slc_exists:
status = "missing_slc"
elif not materialized_data_exists:
status = "missing_data_shelve"
else:
status = "ready"
return ScenePlan(
date=date,
target_dir_windows=str(target_dir),
target_dir_wsl=windows_to_wsl(target_dir),
source_scene_json_windows=str(source_scene_json),
source_scene_json_wsl=windows_to_wsl(source_scene_json),
source_tiff_windows=str(source_tiff),
source_tiff_wsl=windows_to_wsl(source_tiff),
source_meta_windows=str(source_meta),
source_meta_wsl=windows_to_wsl(source_meta),
orbit_xml_windows=str(orbit_xml) if orbit_xml else None,
orbit_xml_wsl=windows_to_wsl(orbit_xml) if orbit_xml else None,
orbit_xml_exists=bool(orbit_xml),
orbit_resolution_mode=orbit_resolution_mode,
orbit_resolution_error=orbit_resolution_error,
source_exists=True,
scene_start_utc=scene_start_utc,
scene_stop_utc=scene_stop_utc,
orbit_window_start_utc=orbit_window_start_utc,
orbit_window_stop_utc=orbit_window_stop_utc,
expected_slc_windows=str(expected_slc),
expected_slc_wsl=windows_to_wsl(expected_slc),
expected_slc_xml_windows=str(expected_slc_xml),
expected_slc_xml_wsl=windows_to_wsl(expected_slc_xml),
expected_data_shelve_windows=str(expected_data),
expected_data_shelve_wsl=windows_to_wsl(expected_data),
materialized_slc_exists=materialized_slc_exists,
materialized_data_exists=materialized_data_exists,
stack_ready=stack_ready,
status=status,
)
def render_stack_command(
slc_dir_wsl: str,
dem_wsl: str,
work_dir_wsl: str,
reference_date: str,
workflow: str,
runtime: Dict[str, str],
) -> List[str]:
return [
runtime["conda_bin_wsl"],
"run",
"-n",
runtime["env_name"],
"python",
runtime["stack_script_wsl"],
"-s",
slc_dir_wsl,
"-d",
dem_wsl,
"-w",
work_dir_wsl,
"-m",
reference_date,
"--nofocus",
"-W",
workflow,
"-u",
"snaphu",
"-c",
runtime["stack_text_cmd"],
]
def shell_quote(value: str) -> str:
return "'" + value.replace("'", "'\"'\"'") + "'"
def render_shell_command(argv: List[str]) -> str:
return " ".join(shell_quote(item) for item in argv)
def build_blockers(scene_plans: List[ScenePlan], orbit_pool: Optional[Path], dem_path: Optional[Path]) -> List[str]:
blockers: List[str] = []
if orbit_pool is None:
blockers.append("ORBIT_POOL_ISCE2 was not resolved.")
if dem_path is None:
blockers.append("Prepared DEM with .xml sidecar was not resolved.")
missing_orbit = [item.date for item in scene_plans if not item.orbit_xml_exists]
if missing_orbit:
blockers.append("Missing orbit XML for dates: " + ", ".join(missing_orbit))
orbit_errors = [f"{item.date}: {item.orbit_resolution_error}" for item in scene_plans if item.orbit_resolution_error]
if orbit_errors:
blockers.append("Orbit resolution errors: " + "; ".join(orbit_errors))
missing_slc = [item.date for item in scene_plans if not item.materialized_slc_exists]
if missing_slc:
blockers.append("Materialized .slc/.slc.xml are missing for dates: " + ", ".join(missing_slc))
missing_data = [item.date for item in scene_plans if not item.materialized_data_exists]
if missing_data:
blockers.append("ISCE data shelve is missing for dates: " + ", ".join(missing_data))
return blockers
def render_contract_markdown(report: Dict[str, Any]) -> str:
lines: List[str] = []
ready = bool(report["readiness"]["ready_for_stackStripMap_nofocus"])
lines.append("# LT-1 Stack Prep Contract")
lines.append("")
lines.append(f"Generated: {report['generated_at_utc']}")
lines.append("")
lines.append("## Selected Stack")
lines.append("")
lines.append(f"- Group key: `{report['group_key']}`")
lines.append(f"- Reference date: `{report['reference_date']}`")
lines.append(f"- Workflow: `{report['processing_workflow']}`")
lines.append(f"- Scene count: `{report['scene_count']}`")
lines.append("")
lines.append("## Resolved Runtime Inputs")
lines.append("")
lines.append(f"- Orbit pool (Windows): `{report['resolved_dependencies']['orbit_pool_windows'] or 'UNRESOLVED'}`")
lines.append(f"- Orbit pool (WSL): `{report['resolved_dependencies']['orbit_pool_wsl'] or 'UNRESOLVED'}`")
lines.append(f"- DEM (Windows): `{report['resolved_dependencies']['dem_path_windows'] or 'UNRESOLVED'}`")
lines.append(f"- DEM (WSL): `{report['resolved_dependencies']['dem_path_wsl'] or 'UNRESOLVED'}`")
lines.append("")
lines.append("## Confirmed stripmapStack Contract")
lines.append("")
lines.append("- `stackStripMap.py --nofocus` discovers dates from `SLC/YYYYMMDD/YYYYMMDD.slc`.")
lines.append("- `topo.py` opens `SLC/YYYYMMDD/data` for the reference acquisition.")
lines.append("- `geo2rdr.py` opens `SLC/YYYYMMDD/data` for each secondary acquisition.")
lines.append("- Therefore each acquisition directory must contain at least:")
lines.append(" - `YYYYMMDD.slc`")
lines.append(" - `YYYYMMDD.slc.xml`")
lines.append(" - `data` shelve with `frame` and optional `doppler`")
lines.append("")
lines.append("## Scene Status")
lines.append("")
lines.append("| Date | Orbit XML | SLC | Data | Status |")
lines.append("| --- | --- | --- | --- | --- |")
for scene in report["scenes"]:
orbit_ok = "yes" if scene["orbit_xml_exists"] else "no"
slc_ok = "yes" if scene["materialized_slc_exists"] else "no"
data_ok = "yes" if scene["materialized_data_exists"] else "no"
lines.append(f"| {scene['date']} | {orbit_ok} | {slc_ok} | {data_ok} | `{scene['status']}` |")
lines.append("")
lines.append("## Draft stackStripMap Command")
lines.append("")
lines.append("```bash")
lines.append(report["stack_command"]["shell"])
lines.append("```")
lines.append("")
lines.append("## Current Blockers")
lines.append("")
blockers = report["readiness"]["blocking_reasons"]
if blockers:
for blocker in blockers:
lines.append(f"- {blocker}")
else:
lines.append("- none")
lines.append("")
lines.append("## Next Tasks")
lines.append("")
if ready:
lines.append("- Execute `run_01_reference` and confirm geometry generation succeeds.")
lines.append("- Execute `run_02` to `run_07` step by step and record any LT-1-specific failures.")
lines.append("- Inspect `baselines/`, `configs/`, and the first coarse coregistration outputs.")
lines.append("- Install MintPy only after the stack run outputs are stable.")
else:
lines.append("- Use the LT-1 scene materializer to finish the remaining acquisitions under the generated `SLC/` root.")
lines.append("- Re-run the generated preflight script, then execute `stackStripMap.py --nofocus`.")
lines.append("- Install MintPy only after the stack materializer contract is working end to end.")
lines.append("")
return "\n".join(lines)
def render_run_script(report: Dict[str, Any]) -> str:
slc_dir = report["workspace"]["slc_dir_wsl"]
work_dir = report["workspace"]["stack_work_dir_wsl"]
dem_path = report["resolved_dependencies"]["dem_path_wsl"] or "__MISSING_DEM__"
reference_date = report["reference_date"]
dates = " ".join(scene["date"] for scene in report["scenes"])
command = report["stack_command"]["shell"]
runtime = report["runtime"]
return f"""#!/usr/bin/env bash
set -euo pipefail
SLC_DIR={shell_quote(slc_dir)}
WORK_DIR={shell_quote(work_dir)}
DEM={shell_quote(dem_path)}
REFERENCE_DATE={shell_quote(reference_date)}
CONDA_ENV={shell_quote(runtime["env_name"])}
CONDA_BIN={shell_quote(runtime["conda_bin_wsl"])}
ISCE2_SHARE={shell_quote(runtime["isce2_share_wsl"])}
STRIPMAP_STACK_DIR={shell_quote(runtime["stripmap_stack_dir_wsl"])}
DATES=({dates})
export PYTHONPATH="$STRIPMAP_STACK_DIR:$ISCE2_SHARE${{PYTHONPATH:+:$PYTHONPATH}}"
export PATH="$STRIPMAP_STACK_DIR:$PATH"
echo "LT-1 stripmap stack dry-run preflight"
echo "SLC root: $SLC_DIR"
echo "Work dir: $WORK_DIR"
echo "DEM: $DEM"
echo "Reference date: $REFERENCE_DATE"
echo "Conda env: $CONDA_ENV"
echo "Conda bin: $CONDA_BIN"
echo "PYTHONPATH: $PYTHONPATH"
echo "PATH prefix: $STRIPMAP_STACK_DIR"
missing=0
for d in "${{DATES[@]}}"; do
if [[ ! -f "$SLC_DIR/$d/$d.slc" ]]; then
echo "MISSING: $SLC_DIR/$d/$d.slc"
missing=1
fi
if [[ ! -f "$SLC_DIR/$d/$d.slc.xml" ]]; then
echo "MISSING: $SLC_DIR/$d/$d.slc.xml"
missing=1
fi
if [[ ! -e "$SLC_DIR/$d/data" && ! -e "$SLC_DIR/$d/data.db" && ! -e "$SLC_DIR/$d/data.dat" && ! -e "$SLC_DIR/$d/data.dir" && ! -e "$SLC_DIR/$d/data.bak" ]]; then
echo "MISSING: $SLC_DIR/$d/data"
missing=1
fi
done
if [[ "$missing" -ne 0 ]]; then
echo "Dry-run only. LT-1 scene materialization is still missing."
exit 2
fi
echo "Preflight passed. Running stackStripMap."
{command}
"""
def write_json(path: Path, payload: Dict[str, Any]) -> None:
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False), encoding="utf-8")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Build a dry-run LT-1 SBAS stack-prep workspace for ISCE2 stripmapStack."
)
parser.add_argument(
"--manifest-path",
default=str(DEFAULT_MANIFEST_PATH),
help="Path to the selected stack manifest JSON.",
)
parser.add_argument(
"--scratch-root",
default=None,
help="Override the stack scratch root directory. Defaults to proposed_scratch_windows in the manifest.",
)
parser.add_argument(
"--orbit-pool",
default=None,
help="Override ORBIT_POOL_ISCE2 (Windows path containing LT1A_GpsData_GAS_C_YYYYMMDD.xml).",
)
parser.add_argument(
"--dem-path",
default=None,
help="Override the prepared DEM base path (must have a .xml sidecar).",
)
parser.add_argument(
"--orbit-margin-sec",
type=float,
default=60.0,
help="Margin used when reporting the recommended orbit clip window.",
)
parser.add_argument(
"--workflow",
default="slc",
choices=SUPPORTED_STACK_WORKFLOWS,
help="stripmapStack workflow to generate: slc, interferogram, or ionosphere.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
env_values = ISCE2_INPUT_HELPER.load_env_file(REPO_ROOT / ".env")
runtime = _resolve_runtime_paths(env_values)
manifest_path = Path(args.manifest_path).resolve()
require_file(manifest_path, "stack manifest")
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
scratch_root = Path(args.scratch_root or manifest["proposed_scratch_windows"]).resolve()
slc_root = scratch_root / "SLC"
orbits_dir = scratch_root / "orbits"
logs_dir = scratch_root / "logs"
notes_dir = scratch_root / "notes"
inputs_dir = scratch_root / "inputs"
stack_work_dir = scratch_root / "stack_work"
for path in (scratch_root, slc_root, orbits_dir, logs_dir, notes_dir, inputs_dir, stack_work_dir):
path.mkdir(parents=True, exist_ok=True)
orbit_pool = ISCE2_INPUT_HELPER.resolve_orbit_pool_path(
explicit_path=args.orbit_pool,
env_values=env_values,
default_candidates=ISCE2_INPUT_HELPER.DEFAULT_WINDOWS_ORBIT_POOL_CANDIDATES,
)
local_dem_candidate = inputs_dir / "dem" / "stack_dem_window.wgs84"
dem_path = ISCE2_INPUT_HELPER.resolve_prepared_dem_path(
explicit_path=args.dem_path,
env_values=env_values,
extra_candidates=[local_dem_candidate],
default_candidates=ISCE2_INPUT_HELPER.DEFAULT_WINDOWS_DEM_CANDIDATES,
)
scene_plans = [
build_scene_plan(
scene,
slc_root=slc_root,
orbit_pool=orbit_pool,
orbit_stage_dir=orbits_dir,
margin_sec=args.orbit_margin_sec,
)
for scene in manifest["scenes"]
]
scene_plans.sort(key=lambda item: item.date)
for plan, source_scene in zip(scene_plans, sorted(manifest["scenes"], key=lambda item: item["imaging_date"])):
target_dir = Path(plan.target_dir_windows)
target_dir.mkdir(parents=True, exist_ok=True)
scene_payload = dict(source_scene)
scene_payload["stack_prep"] = {
"date": plan.date,
"target_dir_windows": plan.target_dir_windows,
"target_dir_wsl": plan.target_dir_wsl,
"orbit_xml_windows": plan.orbit_xml_windows,
"orbit_xml_wsl": plan.orbit_xml_wsl,
"orbit_resolution_mode": plan.orbit_resolution_mode,
"orbit_resolution_error": plan.orbit_resolution_error,
"scene_start_utc": plan.scene_start_utc,
"scene_stop_utc": plan.scene_stop_utc,
"orbit_window_start_utc": plan.orbit_window_start_utc,
"orbit_window_stop_utc": plan.orbit_window_stop_utc,
"expected_slc_windows": plan.expected_slc_windows,
"expected_slc_xml_windows": plan.expected_slc_xml_windows,
"expected_data_shelve_windows": plan.expected_data_shelve_windows,
"status": plan.status,
}
write_json(target_dir / "source_scene.json", scene_payload)
stack_command_argv = render_stack_command(
slc_dir_wsl=windows_to_wsl(slc_root),
dem_wsl=windows_to_wsl(dem_path) if dem_path else "__MISSING_DEM__",
work_dir_wsl=windows_to_wsl(stack_work_dir),
reference_date=manifest["reference_date"],
workflow=args.workflow,
runtime=runtime,
)
blockers = build_blockers(scene_plans, orbit_pool=orbit_pool, dem_path=dem_path)
readiness = {
"all_orbits_resolved": all(item.orbit_xml_exists for item in scene_plans),
"all_materialized_slc_present": all(item.materialized_slc_exists for item in scene_plans),
"all_data_shelves_present": all(item.materialized_data_exists for item in scene_plans),
"ready_for_stackStripMap_nofocus": not blockers,
"blocking_reasons": blockers,
}
report: Dict[str, Any] = {
"manifest_version": 1,
"generated_at_utc": datetime.utcnow().replace(microsecond=0).isoformat() + "Z",
"source_manifest_windows": str(manifest_path),
"source_manifest_wsl": windows_to_wsl(manifest_path),
"group_key": manifest["group_key"],
"tile_key": manifest["tile_key"],
"scene_count": manifest["scene_count"],
"reference_date": manifest["reference_date"],
"reference_strategy": manifest["reference_strategy"],
"processing_workflow": args.workflow,
"sensor_name": "LUTAN1",
"stack_driver": "isce2.stripmapStack.stackStripMap",
"runtime": runtime,
"workspace": {
"root_windows": str(scratch_root),
"root_wsl": windows_to_wsl(scratch_root),
"slc_dir_windows": str(slc_root),
"slc_dir_wsl": windows_to_wsl(slc_root),
"orbits_dir_windows": str(orbits_dir),
"orbits_dir_wsl": windows_to_wsl(orbits_dir),
"logs_dir_windows": str(logs_dir),
"logs_dir_wsl": windows_to_wsl(logs_dir),
"notes_dir_windows": str(notes_dir),
"notes_dir_wsl": windows_to_wsl(notes_dir),
"inputs_dir_windows": str(inputs_dir),
"inputs_dir_wsl": windows_to_wsl(inputs_dir),
"stack_work_dir_windows": str(stack_work_dir),
"stack_work_dir_wsl": windows_to_wsl(stack_work_dir),
},
"resolved_dependencies": {
"orbit_pool_windows": str(orbit_pool) if orbit_pool else None,
"orbit_pool_wsl": windows_to_wsl(orbit_pool) if orbit_pool else None,
"dem_path_windows": str(dem_path) if dem_path else None,
"dem_path_wsl": windows_to_wsl(dem_path) if dem_path else None,
},
"stack_contract": {
"mode": "nofocus",
"workflow": args.workflow,
"required_per_acquisition_files": [
"YYYYMMDD.slc",
"YYYYMMDD.slc.xml",
"data shelve",
],
"current_source_layout": "per_scene_folder_with_tiff_meta_rpc",
"adapter_needed": True,
"adapter_goal": "materialize a stripmapStack-ready date directory from LT-1 TIFF/meta/orbit inputs",
},
"stack_command": {
"argv": stack_command_argv,
"shell": render_shell_command(stack_command_argv),
},
"readiness": readiness,
"scenes": [plan.__dict__ for plan in scene_plans],
"next_tasks": [
"Use the LT-1 scene materializer to build YYYYMMDD.slc and data shelve for the remaining acquisitions.",
"Keep raw scene data external and store only lightweight source manifests plus generated ISCE products under scratch/SLC/YYYYMMDD.",
f"Run stripmapStack in --nofocus mode with workflow={args.workflow} once every date directory is materialized.",
"Install MintPy only after stackStripMap produces stable interferogram outputs.",
],
}
report_path = scratch_root / "stack_input_manifest.json"
contract_path = scratch_root / "stack_prep_contract.md"
run_script_path = scratch_root / "run_stripmap_stack_dryrun.sh"
write_json(report_path, report)
contract_path.write_text(render_contract_markdown(report), encoding="utf-8")
run_script_path.write_text(render_run_script(report), encoding="utf-8", newline="\n")
print(f"Manifest: {report_path}")
print(f"Contract: {contract_path}")
print(f"Run script: {run_script_path}")
print(f"Scratch root: {scratch_root}")
print(f"Orbit pool: {orbit_pool if orbit_pool else 'UNRESOLVED'}")
print(f"DEM: {dem_path if dem_path else 'UNRESOLVED'}")
print(f"Ready: {readiness['ready_for_stackStripMap_nofocus']}")
if blockers:
print("Blockers:")
for blocker in blockers:
print(f" - {blocker}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,202 +0,0 @@
#!/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())
@@ -1,33 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
CONDA_BIN="${CONDA_BIN:-/home/administrator/miniconda3/bin/conda}"
REPO_ROOT="${REPO_ROOT:-/mnt/z/Code/Insar_management_system_v2}"
EXP_ROOT="${EXP_ROOT:-$REPO_ROOT/experiments/isce2_sbas_timeseries}"
echo "== repo =="
echo "$REPO_ROOT"
test -d "$REPO_ROOT"
echo "== experiment root =="
echo "$EXP_ROOT"
test -d "$EXP_ROOT"
echo "== python3 =="
python3 --version
echo "== conda env list =="
"$CONDA_BIN" env list
echo "== isce2 runtime =="
"$CONDA_BIN" run -n isce2 python -c "import sys; import isce; print(sys.executable); print(isce.__file__)"
echo "== Lutan1 sensor module =="
"$CONDA_BIN" run -n isce2 python -c "from isce.components.isceobj.Sensor import Lutan1; print(Lutan1.__file__)"
echo "== mintpy import check =="
"$CONDA_BIN" run -n isce2 python -c "import importlib.util; print('mintpy:present' if importlib.util.find_spec('mintpy') else 'mintpy:missing')"
echo "== candidate ISCE stack directories =="
find /home/administrator/miniconda3/envs/isce2 -maxdepth 6 \
\( -iname 'stripmapStack' -o -iname 'topsStack' -o -iname 'stack' \) 2>/dev/null || true
@@ -1,83 +0,0 @@
#!/usr/bin/env python3
"""Create a strict MintPy mask containing only pixels valid in all interferograms."""
from __future__ import annotations
import argparse
from pathlib import Path
import h5py
import numpy as np
def build_mask(ifgram_stack: Path, output_path: Path, block_rows: int) -> None:
with h5py.File(ifgram_stack, "r") as src:
unwrap = src["unwrapPhase"]
conn = src.get("connectComponent")
num_ifg, length, width = unwrap.shape
mask = np.ones((length, width), dtype=np.bool_)
print(f"Input stack: {ifgram_stack}")
print(f"Interferograms: {num_ifg}")
print(f"Shape: {length} x {width}")
print(f"Block rows: {block_rows}")
for row0 in range(0, length, block_rows):
row1 = min(row0 + block_rows, length)
block = unwrap[:, row0:row1, :]
block_mask = np.all(np.isfinite(block) & (block != 0.0), axis=0)
if conn is not None:
conn_block = conn[:, row0:row1, :]
block_mask &= np.all(conn_block != 0, axis=0)
mask[row0:row1, :] = block_mask
print(f"Processed rows {row0}:{row1}")
attrs = dict(src.attrs)
output_path.parent.mkdir(parents=True, exist_ok=True)
with h5py.File(output_path, "w") as dst:
dst.create_dataset("mask", data=mask, dtype=np.bool_)
for key, value in attrs.items():
dst.attrs[key] = value
dst.attrs["FILE_TYPE"] = "mask"
dst.attrs["DATASET_NAME"] = "mask"
dst.attrs["SOURCE_FILE"] = str(ifgram_stack)
dst.attrs["MASK_RULE"] = "all_ifgrams_finite_nonzero_and_conncomp_nonzero"
valid_pixels = int(mask.sum())
total_pixels = int(mask.size)
print(f"Output mask: {output_path}")
print(f"Valid pixels: {valid_pixels}/{total_pixels} ({valid_pixels / total_pixels * 100:.2f}%)")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Create a strict mask of pixels valid in all MintPy interferograms."
)
parser.add_argument("--ifgram-stack", required=True, help="Path to MintPy inputs/ifgramStack.h5")
parser.add_argument("--output", required=True, help="Output HDF5 path, e.g. maskAllValid.h5")
parser.add_argument(
"--block-rows",
type=int,
default=256,
help="Number of image rows processed per block.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
build_mask(
ifgram_stack=Path(args.ifgram_stack),
output_path=Path(args.output),
block_rows=args.block_rows,
)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,145 +0,0 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
from datetime import datetime
from pathlib import Path
import xml.etree.ElementTree as ET
import numpy as np
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=(
"Create a synthetic stripmapStack water mask in radar coordinates. "
"The default fill value 1 means all-land, which preserves downstream pixels."
)
)
parser.add_argument(
"--like-image",
required=True,
help="Existing ISCE image base path or .xml path used only for shape/metadata, for example shadowMask.rdr",
)
parser.add_argument(
"--output",
required=True,
help="Output water-mask base path, for example .../geom_reference/waterMask.rdr",
)
parser.add_argument(
"--fill-value",
type=int,
default=1,
choices=(0, 1),
help="Pixel value to write. 1 keeps all pixels, 0 masks all pixels.",
)
parser.add_argument(
"--force",
action="store_true",
help="Overwrite an existing output mask.",
)
parser.add_argument(
"--report",
default=None,
help="Optional JSON report path.",
)
return parser.parse_args()
def resolve_like_paths(value: str) -> tuple[Path, Path]:
candidate = Path(value)
if candidate.suffix == ".xml":
xml_path = candidate
image_path = Path(str(candidate)[:-4])
else:
image_path = candidate
xml_path = Path(str(candidate) + ".xml")
if not xml_path.exists():
raise FileNotFoundError(f"Template image XML not found: {xml_path}")
return image_path, xml_path
def maybe_unlink(path: Path) -> None:
if path.exists():
path.unlink()
def require_xml_value(root: ET.Element, property_name: str) -> str:
value_node = root.find(f"./property[@name='{property_name}']/value")
if value_node is None or value_node.text is None:
raise ValueError(f"Missing XML property '{property_name}'")
return value_node.text.strip()
def write_template_metadata(template_image: Path, template_xml: Path, output: Path) -> tuple[int, int]:
root = ET.parse(template_xml).getroot()
width = int(require_xml_value(root, "width"))
length = int(require_xml_value(root, "length"))
file_name_node = root.find("./property[@name='file_name']/value")
if file_name_node is None:
raise ValueError(f"Missing XML file_name entry: {template_xml}")
file_name_node.text = str(output)
xml_output = Path(str(output) + ".xml")
ET.indent(root, space=" ")
ET.ElementTree(root).write(xml_output, encoding="utf-8")
hdr_template = template_image.with_suffix(".hdr")
hdr_output = output.with_suffix(".hdr")
if hdr_template.exists():
hdr_text = hdr_template.read_text(encoding="utf-8", errors="ignore")
hdr_output.write_text(hdr_text.replace(str(template_image), str(output)), encoding="utf-8")
vrt_template = Path(str(template_image) + ".vrt")
vrt_output = Path(str(output) + ".vrt")
if vrt_template.exists():
vrt_text = vrt_template.read_text(encoding="utf-8", errors="ignore")
vrt_text = vrt_text.replace(template_image.name, output.name)
vrt_output.write_text(vrt_text, encoding="utf-8")
return width, length
def main() -> int:
args = parse_args()
template_image, template_xml = resolve_like_paths(args.like_image)
output = Path(args.output)
if output.exists() and not args.force:
raise FileExistsError(f"Output already exists, use --force to overwrite: {output}")
output.parent.mkdir(parents=True, exist_ok=True)
width, length = write_template_metadata(template_image=template_image, template_xml=template_xml, output=output)
mask = np.full((length, width), args.fill_value, dtype=np.uint8)
mask.tofile(output)
maybe_unlink(output.with_suffix(".rdr.aux.xml"))
report = {
"generated_at_utc": datetime.utcnow().replace(microsecond=0).isoformat() + "Z",
"template_xml": str(template_xml),
"output": str(output),
"width": width,
"length": length,
"fill_value": args.fill_value,
"data_type": "BYTE",
"note": "Synthetic all-land water mask for local stripmapStack experiments without Earthdata SWBD access.",
}
report_path = Path(args.report) if args.report else output.parent / "synthetic_watermask_report.json"
report_path.write_text(json.dumps(report, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"Template: {template_xml}")
print(f"Output: {output}")
print(f"Shape: {length} x {width}")
print(f"Value: {args.fill_value}")
print(f"Report: {report_path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,76 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 2 ]]; then
echo "Usage: $0 <env-name> <output-dir-wsl>" >&2
exit 1
fi
ENV_NAME="$1"
OUTPUT_DIR="$2"
CONDA_BIN="${CONDA_BIN:-/home/administrator/miniconda3/bin/conda}"
if [[ ! -x "$CONDA_BIN" ]]; then
echo "Missing conda binary: $CONDA_BIN" >&2
exit 1
fi
mkdir -p "$OUTPUT_DIR"
SAFE_NAME="${ENV_NAME//[^A-Za-z0-9._-]/_}"
YAML_PATH="$OUTPUT_DIR/${SAFE_NAME}.no_builds.yml"
EXPLICIT_PATH="$OUTPUT_DIR/${SAFE_NAME}.explicit.txt"
LIST_PATH="$OUTPUT_DIR/${SAFE_NAME}.conda_list.txt"
RUNTIME_PATH="$OUTPUT_DIR/${SAFE_NAME}.runtime_versions.txt"
echo "Exporting conda environment snapshot"
echo "Env: $ENV_NAME"
echo "Output dir: $OUTPUT_DIR"
"$CONDA_BIN" env export -n "$ENV_NAME" --no-builds > "$YAML_PATH"
"$CONDA_BIN" list -n "$ENV_NAME" --explicit > "$EXPLICIT_PATH"
"$CONDA_BIN" list -n "$ENV_NAME" > "$LIST_PATH"
"$CONDA_BIN" run -n "$ENV_NAME" python -c "
import importlib.util
import logging
import platform
import sys
logging.getLogger().setLevel(logging.WARNING)
def version_of(name):
try:
mod = __import__(name)
return getattr(mod, '__version__', '<missing>')
except Exception as exc:
return f'<import failed: {exc}>'
for line in [
f'python_executable={sys.executable}',
f'python_version={platform.python_version()}',
f'isce_present={importlib.util.find_spec(\"isce\") is not None}',
f'mintpy_present={importlib.util.find_spec(\"mintpy\") is not None}',
f'h5py_present={importlib.util.find_spec(\"h5py\") is not None}',
]:
print(line)
if importlib.util.find_spec('isce') is not None:
import isce
print(f'isce_file={isce.__file__}')
print(f'isce_version={getattr(isce, \"__version__\", \"<missing>\")}')
if importlib.util.find_spec('mintpy') is not None:
import mintpy
print(f'mintpy_file={mintpy.__file__}')
print(f'mintpy_version={getattr(mintpy, \"__version__\", \"<missing>\")}')
if importlib.util.find_spec('h5py') is not None:
import h5py
print(f'h5py_version={h5py.__version__}')
" > "$RUNTIME_PATH"
echo "Wrote: $YAML_PATH"
echo "Wrote: $EXPLICIT_PATH"
echo "Wrote: $LIST_PATH"
echo "Wrote: $RUNTIME_PATH"
@@ -1,63 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 2 || $# -gt 3 ]]; then
echo "Usage: $0 <mintpy-work-dir-wsl> <publish-dir-wsl> [group-key]" >&2
exit 1
fi
MINTPY_WORK_DIR="$1"
PUBLISH_DIR="$2"
GROUP_KEY="${3:-}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
MINTPY_RUNNER="${MINTPY_RUNNER:-$SCRIPT_DIR/run_mintpy_with_isce_ubuntu2404.sh}"
PUBLISH_BUILDER="$SCRIPT_DIR/build_mintpy_publish_bundle.py"
GEO_LAT_STEP="${GEO_LAT_STEP:--0.000185185}"
GEO_LON_STEP="${GEO_LON_STEP:-0.000185185}"
GEO_INTERP_METHOD="${GEO_INTERP_METHOD:-nearest}"
ASSETS_DIR="$PUBLISH_DIR/assets"
PREVIEW_DIR="$PUBLISH_DIR/preview"
METADATA_DIR="$PUBLISH_DIR/metadata"
mkdir -p "$ASSETS_DIR" "$PREVIEW_DIR" "$METADATA_DIR"
LOOKUP_FILE="$MINTPY_WORK_DIR/inputs/geometryRadar.h5"
echo "MintPy publish export"
echo "Work dir: $MINTPY_WORK_DIR"
echo "Publish dir: $PUBLISH_DIR"
echo "Lookup file: $LOOKUP_FILE"
echo "Runner: $MINTPY_RUNNER"
echo "Geo step: $GEO_LAT_STEP, $GEO_LON_STEP"
echo "Interp: $GEO_INTERP_METHOD"
for src in velocity.h5 temporalCoherence.h5 maskTempCoh.h5 timeseries.h5; do
bash "$MINTPY_RUNNER" geocode.py \
"$MINTPY_WORK_DIR/$src" \
-l "$LOOKUP_FILE" \
--lalo "$GEO_LAT_STEP" "$GEO_LON_STEP" \
-i "$GEO_INTERP_METHOD" \
--outdir "$ASSETS_DIR" \
--update
done
bash "$MINTPY_RUNNER" save_gdal.py "$ASSETS_DIR/geo_velocity.h5" -d velocity -o "$ASSETS_DIR/velocity.tif"
bash "$MINTPY_RUNNER" save_gdal.py "$ASSETS_DIR/geo_temporalCoherence.h5" -d temporalCoherence -o "$ASSETS_DIR/temporalCoherence.tif"
bash "$MINTPY_RUNNER" save_gdal.py "$ASSETS_DIR/geo_maskTempCoh.h5" -d mask -o "$ASSETS_DIR/maskTempCoh.tif"
cp "$MINTPY_WORK_DIR/smallbaselineApp.cfg" "$METADATA_DIR/smallbaselineApp.cfg"
cp "$MINTPY_WORK_DIR/numTriNonzeroIntAmbiguity.png" "$PREVIEW_DIR/numTriNonzeroIntAmbiguity.png"
if [[ -n "$GROUP_KEY" ]]; then
bash "$MINTPY_RUNNER" python "$PUBLISH_BUILDER" \
--mintpy-work-dir "$MINTPY_WORK_DIR" \
--publish-dir "$PUBLISH_DIR" \
--group-key "$GROUP_KEY"
else
bash "$MINTPY_RUNNER" python "$PUBLISH_BUILDER" \
--mintpy-work-dir "$MINTPY_WORK_DIR" \
--publish-dir "$PUBLISH_DIR"
fi
@@ -1,9 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
GENERIC_EXPORTER="$SCRIPT_DIR/export_mintpy_publish_products_ubuntu2404.sh"
export MINTPY_RUNNER="${MINTPY_RUNNER:-$SCRIPT_DIR/run_mintpy_unified_env_ubuntu2404.sh}"
bash "$GENERIC_EXPORTER" "$@"
@@ -1,14 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <output-dir-wsl>" >&2
exit 1
fi
OUTPUT_DIR="$1"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
EXPORTER="$SCRIPT_DIR/export_conda_env_snapshot_ubuntu2404.sh"
bash "$EXPORTER" isce2 "$OUTPUT_DIR"
bash "$EXPORTER" isce2_mintpy_v1 "$OUTPUT_DIR"
@@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
CONDA_BIN="${CONDA_BIN:-/home/administrator/miniconda3/bin/conda}"
CONDA_ENV="${CONDA_ENV:-isce2}"
PIP_INDEX_URL="${PIP_INDEX_URL:-https://pypi.tuna.tsinghua.edu.cn/simple}"
PACKAGES=(
matplotlib
)
if [[ ! -x "$CONDA_BIN" ]]; then
echo "Missing conda binary: $CONDA_BIN" >&2
exit 1
fi
echo "ISCE2 stack runtime bootstrap"
echo "Conda: $CONDA_BIN"
echo "Env: $CONDA_ENV"
echo "Index: $PIP_INDEX_URL"
for pkg in "${PACKAGES[@]}"; do
echo "Installing $pkg into $CONDA_ENV"
"$CONDA_BIN" run -n "$CONDA_ENV" python -m pip install -i "$PIP_INDEX_URL" "$pkg"
done
echo "Runtime bootstrap complete"
@@ -1,128 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
CONDA_BIN="${CONDA_BIN:-/home/administrator/miniconda3/bin/conda}"
SOURCE_ENV="${SOURCE_ENV:-isce2}"
TARGET_ENV="${TARGET_ENV:-isce2_mintpy}"
PYTHON_VERSION="${PYTHON_VERSION:-3.11}"
BOOTSTRAP_MODE="${BOOTSTRAP_MODE:-clone}"
USE_TUNA_MIRROR="${USE_TUNA_MIRROR:-1}"
CHANNEL_CONDA_FORGE="${CHANNEL_CONDA_FORGE:-https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge}"
CHANNEL_MAIN="${CHANNEL_MAIN:-https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main}"
CHANNEL_R="${CHANNEL_R:-https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r}"
MINTPY_SPEC="${MINTPY_SPEC:-mintpy}"
CLONE_OFFLINE="${CLONE_OFFLINE:-1}"
if [[ ! -x "$CONDA_BIN" ]]; then
echo "Missing conda binary: $CONDA_BIN" >&2
exit 1
fi
channel_args=()
if [[ "$USE_TUNA_MIRROR" == "1" ]]; then
channel_args=(
--override-channels
-c "$CHANNEL_CONDA_FORGE"
-c "$CHANNEL_MAIN"
-c "$CHANNEL_R"
)
fi
env_exists() {
"$CONDA_BIN" env list | awk '{print $1}' | grep -Fxq "$1"
}
echo "Unified ISCE2 + MintPy runtime bootstrap"
echo "Conda: $CONDA_BIN"
echo "Source env: $SOURCE_ENV"
echo "Target env: $TARGET_ENV"
echo "Python: $PYTHON_VERSION"
echo "Mode: $BOOTSTRAP_MODE"
echo "MintPy spec: $MINTPY_SPEC"
echo "Use mirror: $USE_TUNA_MIRROR"
echo "Clone offline:$CLONE_OFFLINE"
if ! env_exists "$SOURCE_ENV"; then
echo "Missing source environment: $SOURCE_ENV" >&2
exit 1
fi
if [[ "$BOOTSTRAP_MODE" != "clone" && "$BOOTSTRAP_MODE" != "recreate" ]]; then
echo "Unsupported BOOTSTRAP_MODE: $BOOTSTRAP_MODE" >&2
exit 1
fi
if env_exists "$TARGET_ENV"; then
echo "Environment $TARGET_ENV already exists. Reusing it."
else
if [[ "$BOOTSTRAP_MODE" == "clone" ]]; then
echo "Cloning $SOURCE_ENV into $TARGET_ENV"
clone_args=("${channel_args[@]}" -y -n "$TARGET_ENV" --clone "$SOURCE_ENV")
if [[ "$CLONE_OFFLINE" == "1" ]]; then
clone_args+=(--offline)
fi
"$CONDA_BIN" create "${clone_args[@]}"
else
tmp_export="$(mktemp)"
tmp_conda_specs="$(mktemp)"
tmp_pip_specs="$(mktemp)"
trap 'rm -f "$tmp_export" "$tmp_conda_specs" "$tmp_pip_specs"' EXIT
echo "Exporting $SOURCE_ENV into a recreate spec"
"$CONDA_BIN" env export -n "$SOURCE_ENV" --no-builds > "$tmp_export"
awk \
'
/^dependencies:/ {
in_dependencies = 1
next
}
/^prefix:/ {
exit
}
in_dependencies == 1 && /^ - pip:$/ {
exit
}
in_dependencies == 1 && /^ - / {
print substr($0, 5)
}
' "$tmp_export" > "$tmp_conda_specs"
awk \
'
/^ - pip:$/ {
in_pip = 1
next
}
/^prefix:/ {
exit
}
in_pip == 1 && /^ - / {
print substr($0, 7)
}
' "$tmp_export" > "$tmp_pip_specs"
mapfile -t conda_specs < "$tmp_conda_specs"
if [[ ${#conda_specs[@]} -eq 0 ]]; then
echo "Failed to extract conda dependency specs from $SOURCE_ENV export" >&2
exit 1
fi
echo "Recreating $TARGET_ENV from exported dependency list"
"$CONDA_BIN" create -y -n "$TARGET_ENV" "${channel_args[@]}" "${conda_specs[@]}"
if [[ -s "$tmp_pip_specs" ]]; then
mapfile -t pip_specs < "$tmp_pip_specs"
echo "Reinstalling exported pip packages into $TARGET_ENV"
"$CONDA_BIN" run -n "$TARGET_ENV" python -m pip install "${pip_specs[@]}"
fi
fi
fi
echo "Installing MintPy into $TARGET_ENV"
"$CONDA_BIN" install -y -n "$TARGET_ENV" "${channel_args[@]}" "$MINTPY_SPEC"
echo "Verifying unified runtime imports"
"$CONDA_BIN" run -n "$TARGET_ENV" python -c "import sys; import isce; import mintpy; import h5py; print(sys.executable); print(isce.__file__); print(mintpy.__file__); print('h5py=' + h5py.__version__)"
echo "Unified runtime bootstrap complete"
@@ -1,47 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
CONDA_BIN="${CONDA_BIN:-/home/administrator/miniconda3/bin/conda}"
TARGET_ENV="${TARGET_ENV:-mintpy}"
PYTHON_VERSION="${PYTHON_VERSION:-3.11}"
USE_TUNA_MIRROR="${USE_TUNA_MIRROR:-1}"
CHANNEL_CONDA_FORGE="${CHANNEL_CONDA_FORGE:-https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge}"
CHANNEL_MAIN="${CHANNEL_MAIN:-https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main}"
CHANNEL_R="${CHANNEL_R:-https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r}"
if [[ ! -x "$CONDA_BIN" ]]; then
echo "Missing conda binary: $CONDA_BIN" >&2
exit 1
fi
channel_args=()
if [[ "$USE_TUNA_MIRROR" == "1" ]]; then
channel_args=(
--override-channels
-c "$CHANNEL_CONDA_FORGE"
-c "$CHANNEL_MAIN"
-c "$CHANNEL_R"
)
fi
env_exists() {
"$CONDA_BIN" env list | awk '{print $1}' | grep -Fxq "$TARGET_ENV"
}
echo "MintPy runtime bootstrap"
echo "Conda: $CONDA_BIN"
echo "Target env: $TARGET_ENV"
echo "Python: $PYTHON_VERSION"
echo "Use mirror: $USE_TUNA_MIRROR"
if env_exists; then
echo "Environment $TARGET_ENV already exists. Installing or updating MintPy."
"$CONDA_BIN" install -y -n "$TARGET_ENV" "${channel_args[@]}" mintpy
else
echo "Creating environment $TARGET_ENV with MintPy."
"$CONDA_BIN" create -y -n "$TARGET_ENV" "${channel_args[@]}" "python=$PYTHON_VERSION" mintpy
fi
echo "Verifying MintPy import"
"$CONDA_BIN" run -n "$TARGET_ENV" python -c "import mintpy; print(mintpy.__file__)"
echo "MintPy runtime bootstrap complete"
@@ -1,201 +0,0 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import os
import shelve
import shutil
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional
def choose_path(scene: Dict[str, Any], primary_key: str, fallback_key: str) -> str:
primary = scene.get(primary_key)
fallback = scene.get(fallback_key)
for candidate in (primary, fallback):
if candidate and Path(candidate).exists():
return str(Path(candidate))
raise FileNotFoundError(
f"Neither {primary_key} nor {fallback_key} exists for scene {scene.get('date') or scene.get('imaging_date')}"
)
def load_manifest(path: Path) -> Dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8"))
def remove_existing_shelve(stem: Path) -> None:
for suffix in ("", ".db", ".dat", ".dir", ".bak"):
candidate = Path(str(stem) + suffix)
if candidate.exists():
if candidate.is_dir():
shutil.rmtree(candidate)
else:
candidate.unlink()
def shelve_stem_exists(stem: Path) -> bool:
for suffix in ("", ".db", ".dat", ".dir", ".bak"):
if Path(str(stem) + suffix).exists():
return True
return False
@dataclass
class SceneResult:
date: str
output_dir: str
slc_path: str
data_shelve: str
status: str
bytes_written: Optional[int]
started_at_utc: str
ended_at_utc: str
def materialize_one_scene(
scene: Dict[str, Any],
force: bool,
) -> SceneResult:
import isce
from isceobj.Sensor import createSensor
date = str(scene["date"])
output_dir = Path(scene["target_dir_wsl"])
output_dir.mkdir(parents=True, exist_ok=True)
slc_path = Path(scene["expected_slc_wsl"])
slc_xml_path = Path(scene["expected_slc_xml_wsl"])
data_shelve = Path(scene["expected_data_shelve_wsl"])
tiff_path = choose_path(scene, "source_tiff_wsl", "source_tiff_windows")
orbit_xml = choose_path(scene, "orbit_xml_wsl", "orbit_xml_windows")
if force:
for path in (slc_path, slc_xml_path, Path(str(slc_path) + ".vrt")):
if path.exists():
path.unlink()
remove_existing_shelve(data_shelve)
if slc_path.exists() and slc_xml_path.exists() and shelve_stem_exists(data_shelve):
now = datetime.utcnow().replace(microsecond=0).isoformat() + "Z"
return SceneResult(
date=date,
output_dir=str(output_dir),
slc_path=str(slc_path),
data_shelve=str(data_shelve),
status="skipped_existing",
bytes_written=slc_path.stat().st_size,
started_at_utc=now,
ended_at_utc=now,
)
started_at = datetime.utcnow().replace(microsecond=0).isoformat() + "Z"
sensor = createSensor("LUTAN1")
sensor.configure()
sensor.tiff = tiff_path
sensor.orbitFile = orbit_xml
sensor.output = str(slc_path)
sensor.extractImage()
sensor.extractDoppler()
sensor.frame.getImage().renderHdr()
remove_existing_shelve(data_shelve)
with shelve.open(str(data_shelve)) as db:
db["frame"] = sensor.frame
ended_at = datetime.utcnow().replace(microsecond=0).isoformat() + "Z"
report = {
"date": date,
"source_tiff": tiff_path,
"orbit_xml": orbit_xml,
"output_slc": str(slc_path),
"output_slc_xml": str(slc_xml_path),
"data_shelve": str(data_shelve),
"frame_lines": sensor.frame.getNumberOfLines(),
"frame_samples": sensor.frame.getNumberOfSamples(),
"started_at_utc": started_at,
"ended_at_utc": ended_at,
}
(output_dir / "materialization_report.json").write_text(
json.dumps(report, indent=2, ensure_ascii=False),
encoding="utf-8",
)
return SceneResult(
date=date,
output_dir=str(output_dir),
slc_path=str(slc_path),
data_shelve=str(data_shelve),
status="materialized",
bytes_written=slc_path.stat().st_size if slc_path.exists() else None,
started_at_utc=started_at,
ended_at_utc=ended_at,
)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Materialize LT-1 stack acquisitions into stripmapStack-ready SLC/date directories."
)
parser.add_argument(
"--stack-manifest",
required=True,
help="Path to stack_input_manifest.json generated by build_lt1_stack_prep.py",
)
parser.add_argument(
"--dates",
nargs="+",
default=None,
help="Optional subset of acquisition dates to materialize, for example 20250510 20250705",
)
parser.add_argument(
"--force",
action="store_true",
help="Overwrite existing .slc/.xml/data outputs for the selected dates.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
manifest_path = Path(args.stack_manifest)
if not manifest_path.exists():
raise FileNotFoundError(f"Stack manifest not found: {manifest_path}")
manifest = load_manifest(manifest_path)
scenes = list(manifest.get("scenes", []))
if not scenes:
raise ValueError(f"No scenes found in stack manifest: {manifest_path}")
selected_dates = set(args.dates or [])
if selected_dates:
scenes = [scene for scene in scenes if str(scene["date"]) in selected_dates]
if not scenes:
raise ValueError(f"No matching dates found in manifest for selection: {sorted(selected_dates)}")
results: List[SceneResult] = []
for scene in scenes:
print(f"Materializing {scene['date']} -> {scene['target_dir_wsl']}")
result = materialize_one_scene(scene, force=args.force)
results.append(result)
print(f" status={result.status} slc={result.slc_path}")
report = {
"generated_at_utc": datetime.utcnow().replace(microsecond=0).isoformat() + "Z",
"stack_manifest": str(manifest_path),
"results": [result.__dict__ for result in results],
}
report_path = manifest_path.parent / "materialization_summary.json"
report_path.write_text(json.dumps(report, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"Summary: {report_path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,59 +0,0 @@
# Phase 0 Practical TODO
## Immediate
- [x] Run `scripts/check_env_ubuntu2404.sh` inside `Ubuntu-24.04`.
- [x] Use `scripts/scan_lt1_stack_candidates.py` to keep one baseline sample stack manifest current.
- [x] Treat `E123.3_N46.1` as the first tile-level smoke-test sample unless a better sample appears.
- [x] Confirm ISCE2 stack-processing scripts are present.
- [x] Confirm the official helper scripts do not advertise LT-1/LUTAN1 stack prep.
- [x] Record required orbit, DEM, and metadata adaptations.
- [x] Run `scripts/build_lt1_stack_prep.py` to keep the dry-run stack workspace current.
## Before first end-to-end run
- [x] Implement an LT-1 scene materializer that creates `YYYYMMDD.slc`, `YYYYMMDD.slc.xml`, and `data`.
- [x] Materialize the remaining acquisitions for `E123.3_N46.1` under `scratch/.../SLC/`.
- [x] Smoke-test the materializer on the reference date `20250510`.
- [x] Run the generated `run_stripmap_stack_dryrun.sh` preflight and then `stackStripMap.py --nofocus`.
- [x] Inspect the produced `baseline/`, `configs/`, and `run_files/` outputs.
- [x] Prepare a stack-local DEM to avoid global-DEM bbox behavior during `createWaterMask`.
- [x] Add a reproducible synthetic `waterMask` fallback for `run_01_reference` when Earthdata credentials are unavailable.
Working rule: DEM is already local and sufficient; do not download `SWBD` during this experiment stage.
- [x] Extract shared LT-1 input preparation helper for DEM/orbit resolution.
Compatibility rule: original D-InSAR entry logic remains in place; only the duplicated input-prep internals were consolidated.
- [x] Decide MintPy installation strategy after stack generation is stable.
Decision: default to a dedicated WSL conda env named `mintpy` so the working `isce2` processing env stays unchanged on the development machine.
- [x] Freeze the first smoke-test command chain.
Frozen chain: `run_01_reference -> run_02_focus_split -> run_03_geo2rdr_coarseResamp -> run_04_refineSecondaryTiming -> run_05_invertMisreg -> run_06_fineResamp -> run_07_grid_baseline`
- [x] Execute `run_01_reference` through the WSL wrapper and verify the fallback-recovered geometry outputs.
- [x] Execute `run_02` to `run_07` and record LT-1-specific failures if they appear.
Result: all stages exited `0` in `Ubuntu-24.04`. `run_04_refineSecondaryTiming` logs still contain `Bad match at level 1` and `correlation error`, but pair-level `misreg`, date-level `misreg`, merged SLC, and merged baseline products were all generated successfully.
## Next Focus
- [x] Run `scripts/install_mintpy_runtime_ubuntu2404.sh` in `Ubuntu-24.04` and verify the new env.
Result: dedicated WSL env `mintpy` was created successfully and `smallbaselineApp.py` / `prep_isce.py` are available.
- [x] Validate MintPy ingestion against the current `stack_work/merged/` outputs.
Result: `build_lt1_stack_prep.py --workflow interferogram` plus `run_08_igram` produced `Igrams/*/filt_*_snaphu.unw`, and `prep_isce.py` completed successfully after bridging the working `isce2` Python package into the `mintpy` env.
- [x] Draft the first `smallbaselineApp.cfg` for the LT-1 sample stack.
Result: `configs/sample_smallbaseline_lt1_e123p3_n46p1.cfg` now records the first runnable LT-1 stripmapStack -> MintPy SBAS contract.
- [x] Execute the first MintPy workflow steps after `prep_isce.py`.
Result: the repo-local smoke-test chain now reaches radar-coordinate `timeseries.h5` and `velocity.h5` in `stack_work/mintpy_sbas_v5/`.
Current helper chain:
- `scripts/run_mintpy_with_isce_ubuntu2404.sh`
- `scripts/create_mintpy_all_ifgram_mask.py`
- `scripts/run_smallbaselineApp_patched.py`
- `scripts/run_mintpy_sbas_smoketest_ubuntu2404.sh`
- [x] Draft the first production-side SBAS artifact manifest and publish contract.
Result:
- `configs/sample_psinsar_manifest_lt1_e123p3_n46p1.json`
- `docs/ISCE2_SBAS_TIMESERIES_DESIGN.md`
## New Follow-up
- [ ] Decide whether production should keep the repo-local patched MintPy launcher or pin an upstream-fixed MintPy version.
- [x] Add the geocode/export stage needed for publishable SBAS rasters and previews.
Result: experiment-layer publish export now succeeds into `publish/mintpy_sbas_v5/` with geocoded HDF5, GeoTIFF, preview PNG, and `manifest.json`.
- [ ] Wire the validated SBAS runtime chain into backend workflow submission and artifact publishing.
- [ ] Run a separate unified-environment experiment by cloning the current WSL `isce2` env and installing MintPy directly inside it.
@@ -1,152 +0,0 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
from pathlib import Path
from typing import Any, Dict, List
def load_manifest(path: Path) -> Dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8"))
def resolve_dem_source(arg_value: str | None, manifest: Dict[str, Any]) -> Path:
candidates: List[str] = []
if arg_value:
candidates.append(arg_value)
resolved = manifest.get("resolved_dependencies", {})
for key in ("dem_path_wsl", "dem_path_windows"):
value = resolved.get(key)
if value:
candidates.append(value)
for candidate in candidates:
path = Path(candidate)
if path.exists() and Path(str(path) + ".xml").exists():
return path
raise FileNotFoundError("Unable to resolve source DEM from arguments or stack manifest")
def compute_bbox(manifest: Dict[str, Any], margin_deg: float) -> List[float]:
lons: List[float] = []
lats: List[float] = []
for scene in manifest["scenes"]:
lon = scene.get("scene_center_lon")
lat = scene.get("scene_center_lat")
if lon is None or lat is None:
source_scene_json = scene.get("source_scene_json_wsl") or scene.get("source_scene_json_windows")
if source_scene_json and Path(source_scene_json).exists():
source_payload = json.loads(Path(source_scene_json).read_text(encoding="utf-8"))
lon = source_payload.get("scene_center_lon")
lat = source_payload.get("scene_center_lat")
if lon is not None and lat is not None:
lons.append(float(lon))
lats.append(float(lat))
if not lons or not lats:
raise ValueError("Stack manifest does not include usable scene center coordinates")
west = min(lons) - margin_deg
east = max(lons) + margin_deg
south = min(lats) - margin_deg
north = max(lats) + margin_deg
return [south, north, west, east]
def prepare_dem(source_dem: Path, output_dem: Path, bbox: List[float]) -> None:
from osgeo import gdal
from isce.applications.gdal2isce_xml import gdal2isce_xml
south, north, west, east = bbox
src_open_path = Path(str(source_dem) + ".vrt")
if not src_open_path.exists():
src_open_path = source_dem
output_dem.parent.mkdir(parents=True, exist_ok=True)
output_vrt = Path(str(output_dem) + ".vrt")
output_xml = Path(str(output_dem) + ".xml")
output_hdr = Path(str(output_dem) + ".hdr")
fallback_hdr = output_dem.with_suffix(".hdr")
src_ds = gdal.Open(str(src_open_path), gdal.GA_ReadOnly)
if src_ds is None:
raise RuntimeError(f"Unable to open DEM source: {src_open_path}")
translate_options = gdal.TranslateOptions(
format="ENVI",
projWin=[west, north, east, south],
)
out_ds = gdal.Translate(str(output_dem), src_ds, options=translate_options)
if out_ds is None:
raise RuntimeError("gdal.Translate failed while clipping the DEM")
out_ds = None
src_ds = None
vrt_ds = gdal.Open(str(output_dem), gdal.GA_ReadOnly)
if vrt_ds is None:
raise RuntimeError(f"Unable to reopen clipped DEM: {output_dem}")
gdal.Translate(str(output_vrt), vrt_ds, options=gdal.TranslateOptions(format="VRT"))
vrt_ds = None
gdal2isce_xml(str(output_vrt))
if not output_xml.exists():
raise RuntimeError(f"Expected ISCE XML was not created: {output_xml}")
if not output_hdr.exists() and not fallback_hdr.exists():
raise RuntimeError(f"Expected ENVI header was not created: {output_hdr} or {fallback_hdr}")
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Clip a local DEM window for one LT-1 stack workspace."
)
parser.add_argument(
"--stack-manifest",
required=True,
help="Path to stack_input_manifest.json generated by build_lt1_stack_prep.py",
)
parser.add_argument(
"--source-dem",
default=None,
help="Override source DEM base path.",
)
parser.add_argument(
"--margin-deg",
type=float,
default=1.0,
help="Margin around stack scene-center extents in degrees.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
manifest_path = Path(args.stack_manifest)
if not manifest_path.exists():
raise FileNotFoundError(f"Stack manifest not found: {manifest_path}")
manifest = load_manifest(manifest_path)
source_dem = resolve_dem_source(args.source_dem, manifest)
bbox = compute_bbox(manifest, margin_deg=args.margin_deg)
workspace = manifest["workspace"]
dem_dir = Path(workspace["inputs_dir_wsl"]) / "dem"
output_dem = dem_dir / "stack_dem_window.wgs84"
prepare_dem(source_dem=source_dem, output_dem=output_dem, bbox=bbox)
report = {
"stack_manifest": str(manifest_path),
"source_dem": str(source_dem),
"output_dem": str(output_dem),
"bbox_south_north_west_east": bbox,
}
report_path = dem_dir / "stack_dem_window_report.json"
report_path.write_text(json.dumps(report, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"Source DEM: {source_dem}")
print(f"Output DEM: {output_dem}")
print(f"BBox: {bbox}")
print(f"Report: {report_path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -1,106 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 2 ]]; then
echo "Usage: $0 <scratch_root_wsl> <run_file_name>" >&2
echo "Example: $0 /mnt/z/Code/Insar_management_system_v2/experiments/isce2_sbas_timeseries/scratch/lt1a_strip1_hh_descending_e123p3_n46p1 run_01_reference" >&2
exit 1
fi
SCRATCH_ROOT="$1"
RUN_FILE_NAME="$2"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONDA_ROOT="${CONDA_ROOT:-/home/administrator/miniconda3}"
CONDA_ENV="${CONDA_ENV:-insar_wsl_v1}"
CONDA_BIN="${CONDA_BIN:-$CONDA_ROOT/bin/conda}"
ISCE2_SHARE="${ISCE2_SHARE:-$CONDA_ROOT/envs/$CONDA_ENV/share/isce2}"
STRIPMAP_STACK_DIR="${STRIPMAP_STACK_DIR:-$ISCE2_SHARE/stripmapStack}"
SYNTHETIC_WATERMASK_SCRIPT="${SYNTHETIC_WATERMASK_SCRIPT:-$SCRIPT_DIR/create_synthetic_watermask.py}"
ALLOW_SYNTHETIC_WATERMASK="${ALLOW_SYNTHETIC_WATERMASK:-1}"
STACK_WORK="$SCRATCH_ROOT/stack_work"
RUN_FILE="$STACK_WORK/run_files/$RUN_FILE_NAME"
LOG_DIR="$STACK_WORK/logs"
LOG_FILE="$LOG_DIR/$RUN_FILE_NAME.log"
if [[ ! -x "$CONDA_BIN" ]]; then
echo "Missing conda binary: $CONDA_BIN" >&2
exit 1
fi
if [[ ! -f "$RUN_FILE" ]]; then
echo "Run file not found: $RUN_FILE" >&2
exit 1
fi
mkdir -p "$LOG_DIR"
export PYTHONPATH="$STRIPMAP_STACK_DIR:$ISCE2_SHARE${PYTHONPATH:+:$PYTHONPATH}"
export PATH="$STRIPMAP_STACK_DIR:$PATH"
recover_reference_watermask() {
local like_image="$STACK_WORK/geom_reference/shadowMask.rdr"
local output_mask="$STACK_WORK/geom_reference/waterMask.rdr"
local report_path="$LOG_DIR/$RUN_FILE_NAME.synthetic_watermask.json"
local watermask_failure_pattern='Please create a \.netrc file|Running: createWaterMask|DataRetriever - ERROR|There was a problem in retrieving the file|SRTMSWBD\.003|SWBD'
if [[ "$RUN_FILE_NAME" != "run_01_reference" ]]; then
return 1
fi
if [[ "$ALLOW_SYNTHETIC_WATERMASK" != "1" ]]; then
return 1
fi
if [[ ! -f "$LOG_FILE" ]]; then
return 1
fi
# Recover only the known offline water-mask failure modes observed in this
# experiment: missing Earthdata credentials or SWBD retrieval failure.
if ! grep -Eq "$watermask_failure_pattern" "$LOG_FILE"; then
return 1
fi
if [[ ! -f "$like_image" || ! -f "$like_image.xml" ]]; then
echo "Synthetic water-mask fallback could not find template image: $like_image" >&2
return 1
fi
if [[ ! -f "$SYNTHETIC_WATERMASK_SCRIPT" ]]; then
echo "Synthetic water-mask helper script not found: $SYNTHETIC_WATERMASK_SCRIPT" >&2
return 1
fi
echo "Earthdata credentials are unavailable. Creating a synthetic all-land water mask."
"$CONDA_BIN" run -n "$CONDA_ENV" python "$SYNTHETIC_WATERMASK_SCRIPT" \
--like-image "$like_image" \
--output "$output_mask" \
--fill-value 1 \
--force \
--report "$report_path"
}
echo "Executing stripmap stack run file"
echo "Scratch root: $SCRATCH_ROOT"
echo "Run file: $RUN_FILE"
echo "Log file: $LOG_FILE"
echo "Conda env: $CONDA_ENV"
echo "Conda bin: $CONDA_BIN"
echo "ISCE2 share: $ISCE2_SHARE"
echo "PYTHONPATH: $PYTHONPATH"
echo "PATH prefix: $STRIPMAP_STACK_DIR"
set -o pipefail
"$CONDA_BIN" run -n "$CONDA_ENV" bash "$RUN_FILE" 2>&1 | tee "$LOG_FILE"
RUN_STATUS=${PIPESTATUS[0]}
if [[ "$RUN_STATUS" -eq 0 ]]; then
exit 0
fi
if recover_reference_watermask; then
echo "Recovered $RUN_FILE_NAME with a synthetic all-land water mask."
exit 0
fi
exit "$RUN_STATUS"
@@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 2 ]]; then
echo "Usage: $0 <smallbaseline-config-wsl> <mintpy-work-dir-wsl>" >&2
exit 1
fi
CFG_PATH="$1"
WORK_DIR="$2"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BRIDGE_RUNNER="$SCRIPT_DIR/run_mintpy_with_isce_ubuntu2404.sh"
PATCHED_APP="$SCRIPT_DIR/run_smallbaselineApp_patched.py"
STRICT_MASK_BUILDER="$SCRIPT_DIR/create_mintpy_all_ifgram_mask.py"
echo "MintPy SBAS smoketest"
echo "Config: $CFG_PATH"
echo "Work dir: $WORK_DIR"
bash "$BRIDGE_RUNNER" python "$PATCHED_APP" "$CFG_PATH" --dir "$WORK_DIR" --dostep load_data
bash "$BRIDGE_RUNNER" python "$STRICT_MASK_BUILDER" \
--ifgram-stack "$WORK_DIR/inputs/ifgramStack.h5" \
--output "$WORK_DIR/maskAllValid.h5"
bash "$BRIDGE_RUNNER" python "$PATCHED_APP" "$CFG_PATH" --dir "$WORK_DIR" --start modify_network --end velocity
@@ -1,26 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 2 ]]; then
echo "Usage: $0 <smallbaseline-config-wsl> <mintpy-work-dir-wsl>" >&2
exit 1
fi
CFG_PATH="$1"
WORK_DIR="$2"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
UNIFIED_RUNNER="$SCRIPT_DIR/run_mintpy_unified_env_ubuntu2404.sh"
PATCHED_APP="$SCRIPT_DIR/run_smallbaselineApp_patched.py"
STRICT_MASK_BUILDER="$SCRIPT_DIR/create_mintpy_all_ifgram_mask.py"
echo "MintPy SBAS unified-env smoketest"
echo "Config: $CFG_PATH"
echo "Work dir: $WORK_DIR"
bash "$UNIFIED_RUNNER" python "$PATCHED_APP" "$CFG_PATH" --dir "$WORK_DIR" --dostep load_data
bash "$UNIFIED_RUNNER" python "$STRICT_MASK_BUILDER" \
--ifgram-stack "$WORK_DIR/inputs/ifgramStack.h5" \
--output "$WORK_DIR/maskAllValid.h5"
bash "$UNIFIED_RUNNER" python "$PATCHED_APP" "$CFG_PATH" --dir "$WORK_DIR" --start modify_network --end velocity
@@ -1,23 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <mintpy-command> [args...]" >&2
echo "Example: $0 prep_isce.py -h" >&2
exit 1
fi
CONDA_BIN="${CONDA_BIN:-/home/administrator/miniconda3/bin/conda}"
MINTPY_ENV="${MINTPY_ENV:-isce2_mintpy}"
if [[ ! -x "$CONDA_BIN" ]]; then
echo "Missing conda binary: $CONDA_BIN" >&2
exit 1
fi
echo "MintPy command in unified env"
echo "Conda: $CONDA_BIN"
echo "Target env: $MINTPY_ENV"
echo "Command: $*"
"$CONDA_BIN" run -n "$MINTPY_ENV" "$@"
@@ -1,45 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <mintpy-command> [args...]" >&2
echo "Example: $0 prep_isce.py -h" >&2
exit 1
fi
CONDA_BIN="${CONDA_BIN:-/home/administrator/miniconda3/bin/conda}"
MINTPY_ENV="${MINTPY_ENV:-mintpy}"
ISCE_SITE_PACKAGES="${ISCE_SITE_PACKAGES:-/home/administrator/miniconda3/envs/isce2/lib/python3.11/site-packages}"
ISCE_PACKAGE_DIR="${ISCE_PACKAGE_DIR:-$ISCE_SITE_PACKAGES/isce}"
ISCE_BRIDGE_DIR="${ISCE_BRIDGE_DIR:-$HOME/.cache/mintpy_isce_bridge}"
if [[ ! -x "$CONDA_BIN" ]]; then
echo "Missing conda binary: $CONDA_BIN" >&2
exit 1
fi
if [[ ! -d "$ISCE_SITE_PACKAGES" ]]; then
echo "Missing ISCE site-packages directory: $ISCE_SITE_PACKAGES" >&2
exit 1
fi
if [[ ! -d "$ISCE_PACKAGE_DIR" ]]; then
echo "Missing ISCE package directory: $ISCE_PACKAGE_DIR" >&2
exit 1
fi
mkdir -p "$ISCE_BRIDGE_DIR"
ln -sfn "$ISCE_PACKAGE_DIR" "$ISCE_BRIDGE_DIR/isce"
# Bridge only the top-level ISCE package into the MintPy env.
# The package itself extends sys.path to its internal components on import,
# which avoids shadowing MintPy's own numpy/h5py stack with the isce2 env.
export PYTHONPATH="$ISCE_BRIDGE_DIR${PYTHONPATH:+:$PYTHONPATH}"
echo "MintPy command bridge"
echo "Conda: $CONDA_BIN"
echo "MintPy env: $MINTPY_ENV"
echo "ISCE bridge: $ISCE_BRIDGE_DIR -> $ISCE_PACKAGE_DIR"
echo "Command: $*"
"$CONDA_BIN" run -n "$MINTPY_ENV" "$@"
@@ -1,38 +0,0 @@
#!/usr/bin/env python3
"""Run MintPy smallbaselineApp with a local workaround for a single-pixel inversion bug."""
from __future__ import annotations
import sys
import numpy as np
import mintpy.ifgram_inversion as ifgram_inversion
from mintpy.cli.smallbaselineApp import main as mintpy_smallbaseline_main
_ORIGINAL_ESTIMATE_TIMESERIES = ifgram_inversion.estimate_timeseries
def _patched_estimate_timeseries(*args, **kwargs):
ts, inv_quality, num_inv_obs = _ORIGINAL_ESTIMATE_TIMESERIES(*args, **kwargs)
# MintPy 1.6.2 may return a shape-(1,) inversion quality array for the
# single-pixel partial-network branch, while the caller expects a scalar.
if isinstance(inv_quality, np.ndarray) and inv_quality.size == 1:
inv_quality = np.asarray(inv_quality).reshape(-1)[0].item()
if isinstance(num_inv_obs, np.ndarray) and num_inv_obs.size == 1:
num_inv_obs = int(np.asarray(num_inv_obs).reshape(-1)[0])
return ts, inv_quality, num_inv_obs
def main(argv: list[str] | None = None) -> int:
ifgram_inversion.estimate_timeseries = _patched_estimate_timeseries
print("Applied local MintPy estimate_timeseries single-pixel fix.")
return mintpy_smallbaseline_main(argv)
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))
@@ -1,373 +0,0 @@
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import importlib.util
import json
import os
import re
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional
def _repo_root() -> Path:
return Path(__file__).resolve().parents[3]
def _load_utils_module():
utils_path = _repo_root() / "backend" / "app" / "utils.py"
spec = importlib.util.spec_from_file_location("repo_utils", utils_path)
if spec is None or spec.loader is None:
raise RuntimeError(f"Unable to load repo utils module: {utils_path}")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
UTILS = _load_utils_module()
LT1_NAME_RE = re.compile(
r"^(?P<satellite>LT1[AB])_"
r"(?P<satellite_mode>[^_]+)_"
r"(?P<receiving_station>[^_]+)_"
r"(?P<imaging_mode>[^_]+)_"
r"(?P<abs_orbit>\d+)_"
r"(?P<lon>E\d+\.\d+)_"
r"(?P<lat>N\d+\.\d+)_"
r"(?P<date>\d{8})_"
r"(?P<product_type>[^_]+)_"
r"(?P<polarization>[^_]+)_"
r"(?P<product_level>[^_]+)_"
r"(?P<product_unique_id>\d+)$"
)
@dataclass
class SceneRecord:
folder_name: str
folder_path: str
folder_path_wsl: str
tiff_path: str
tiff_path_wsl: str
meta_path: str
meta_path_wsl: str
file_size_bytes: int
satellite: str
imaging_date: str
imaging_mode: Optional[str]
polarization: Optional[str]
orbit_direction: Optional[str]
satellite_mode: Optional[str]
receiving_station: Optional[str]
orbit_circle: Optional[str]
scene_center_lon: Optional[float]
scene_center_lat: Optional[float]
acquisition_time_utc: Optional[str]
product_type: Optional[str]
product_level: Optional[str]
product_unique_id: Optional[str]
tile_key: str
group_key: str
orbit_txt_expected_name: str
def windows_to_wsl(path: str | Path) -> str:
text = str(path)
match = re.match(r"^([A-Za-z]):[\\/](.*)$", os.path.normpath(text))
if not match:
return text.replace("\\", "/")
drive = match.group(1).lower()
normalized_tail = match.group(2).replace("\\", "/").lstrip("/")
return f"/mnt/{drive}/{normalized_tail}"
def choose_tiff(folder: Path) -> Optional[Path]:
candidates = sorted(folder.glob("*.tiff"))
if not candidates:
return None
slc_candidates = [path for path in candidates if "_SLC_" in path.name]
if len(slc_candidates) == 1:
return slc_candidates[0]
if len(candidates) == 1:
return candidates[0]
return candidates[0]
def merge_metadata(name_meta: Dict[str, Any], xml_meta: Dict[str, Any]) -> Dict[str, Any]:
merged = dict(name_meta or {})
prefer_name_keys = {"product_unique_id"}
for key, value in (xml_meta or {}).items():
if value in (None, ""):
continue
if key in prefer_name_keys and merged.get(key):
continue
merged[key] = value
return merged
def parse_scene(folder: Path) -> Optional[SceneRecord]:
match = LT1_NAME_RE.match(folder.name)
if not match:
return None
name_meta = UTILS.get_parser(folder.name, UTILS.RADAR_PARSERS)
if not name_meta:
return None
xml_file_path = UTILS.find_xml_file(str(folder))
if not xml_file_path:
return None
coverage_polygon, xml_meta = UTILS.parse_xml_metadata(xml_file_path)
if not coverage_polygon:
return None
tiff_path = choose_tiff(folder)
if tiff_path is None:
return None
merged = merge_metadata(name_meta, xml_meta or {})
tile_key = f"{match.group('lon')}_{match.group('lat')}"
orbit_direction = str(merged.get("orbit_direction") or "").upper() or None
group_key = "|".join(
[
str(merged.get("satellite") or ""),
str(merged.get("imaging_mode") or ""),
str(merged.get("polarization") or ""),
str(orbit_direction or ""),
tile_key,
]
)
satellite = str(merged.get("satellite") or "")
imaging_date = str(merged.get("imaging_date") or "")
return SceneRecord(
folder_name=folder.name,
folder_path=str(folder),
folder_path_wsl=windows_to_wsl(folder),
tiff_path=str(tiff_path),
tiff_path_wsl=windows_to_wsl(tiff_path),
meta_path=str(xml_file_path),
meta_path_wsl=windows_to_wsl(xml_file_path),
file_size_bytes=tiff_path.stat().st_size,
satellite=satellite,
imaging_date=imaging_date,
imaging_mode=merged.get("imaging_mode"),
polarization=merged.get("polarization"),
orbit_direction=orbit_direction,
satellite_mode=merged.get("satellite_mode"),
receiving_station=merged.get("receiving_station"),
orbit_circle=merged.get("orbit_circle"),
scene_center_lon=merged.get("scene_center_lon"),
scene_center_lat=merged.get("scene_center_lat"),
acquisition_time_utc=merged.get("acquisition_time_utc"),
product_type=merged.get("product_type"),
product_level=merged.get("product_level"),
product_unique_id=merged.get("product_unique_id"),
tile_key=tile_key,
group_key=group_key,
orbit_txt_expected_name=f"{satellite}_GpsData_GAS_C_{imaging_date}.txt",
)
def scan_scenes(root_dir: Path) -> List[SceneRecord]:
scenes: List[SceneRecord] = []
for entry in sorted(root_dir.iterdir()):
if not entry.is_dir():
continue
scene = parse_scene(entry)
if scene:
scenes.append(scene)
return scenes
def build_group_summary(scenes: List[SceneRecord]) -> List[Dict[str, Any]]:
groups: Dict[str, List[SceneRecord]] = {}
for scene in scenes:
groups.setdefault(scene.group_key, []).append(scene)
summary: List[Dict[str, Any]] = []
for key, items in groups.items():
items.sort(key=lambda item: item.imaging_date)
first = items[0]
summary.append(
{
"group_key": key,
"count": len(items),
"satellite": first.satellite,
"imaging_mode": first.imaging_mode,
"polarization": first.polarization,
"orbit_direction": first.orbit_direction,
"tile_key": first.tile_key,
"dates": [item.imaging_date for item in items],
"receiving_stations": sorted({item.receiving_station for item in items if item.receiving_station}),
}
)
summary.sort(key=lambda item: (-item["count"], item["group_key"]))
return summary
def select_group(
summary: List[Dict[str, Any]],
tile_key: Optional[str],
group_key: Optional[str],
min_scenes: int,
) -> Optional[str]:
if group_key:
return group_key
if tile_key:
for item in summary:
if item["tile_key"] == tile_key and item["count"] >= min_scenes:
return item["group_key"]
return None
for item in summary:
if item["count"] >= min_scenes:
return item["group_key"]
return None
def build_manifest(root_dir: Path, group_key: str, scenes: List[SceneRecord]) -> Dict[str, Any]:
group_scenes = [scene for scene in scenes if scene.group_key == group_key]
if not group_scenes:
raise ValueError(f"Group not found: {group_key}")
group_scenes.sort(key=lambda item: item.imaging_date)
first = group_scenes[0]
reference_index = len(group_scenes) // 2
reference_scene = group_scenes[reference_index]
slug = (
f"{first.satellite.lower()}_"
f"{(first.imaging_mode or 'unknown').lower()}_"
f"{(first.polarization or 'unknown').lower()}_"
f"{(first.orbit_direction or 'unknown').lower()}_"
f"{first.tile_key.lower().replace('.', 'p')}"
)
scratch_root = _repo_root() / "experiments" / "isce2_sbas_timeseries" / "scratch" / slug
scratch_root_wsl = windows_to_wsl(scratch_root)
return {
"source_root_windows": str(root_dir),
"source_root_wsl": windows_to_wsl(root_dir),
"group_key": group_key,
"tile_key": first.tile_key,
"scene_count": len(group_scenes),
"reference_strategy": "middle_by_date",
"reference_date": reference_scene.imaging_date,
"stack_group": {
"satellite": first.satellite,
"imaging_mode": first.imaging_mode,
"polarization": first.polarization,
"orbit_direction": first.orbit_direction,
"receiving_stations": sorted({item.receiving_station for item in group_scenes if item.receiving_station}),
},
"proposed_scratch_windows": str(scratch_root),
"proposed_scratch_wsl": scratch_root_wsl,
"proposed_layout": {
"stack_input_manifest": f"{scratch_root_wsl}/stack_input_manifest.json",
"slc_dir": f"{scratch_root_wsl}/SLC",
"orbits_dir": f"{scratch_root_wsl}/orbits",
"logs_dir": f"{scratch_root_wsl}/logs",
},
"stack_prep_assessment": {
"current_scene_layout": "per_scene_folder_with_tiff_meta_rpc",
"official_stripmapStack_expected_layout": "SLC/YYYYMMDD/YYYYMMDD.raw or YYYYMMDD.slc",
"direct_compatibility": "unproven",
"lt1_adapter_required_likely": True,
"notes": [
"Current repo can read these scene folders as RadarData assets.",
"Official stripmapStack helper scripts do not advertise LT-1/LUTAN1 preparation hooks.",
"A custom LT-1 stack preparation layer is likely needed before official stack execution.",
],
},
"scenes": [asdict(scene) for scene in group_scenes],
}
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description="Scan LT-1 scene folders and build a dry-run SBAS stack-prep manifest."
)
parser.add_argument(
"--root-dir",
default=r"F:\Insar_data_pool_1",
help="Windows root directory containing LT-1 scene folders.",
)
parser.add_argument(
"--min-scenes",
type=int,
default=4,
help="Minimum scenes required for candidate groups.",
)
parser.add_argument(
"--top-n",
type=int,
default=20,
help="How many candidate groups to print.",
)
parser.add_argument(
"--tile-key",
default=None,
help="Pick one candidate by tile key, for example E123.3_N46.1.",
)
parser.add_argument(
"--group-key",
default=None,
help="Pick one candidate by full group key.",
)
parser.add_argument(
"--manifest-path",
default=None,
help="Optional JSON output path for the selected group's dry-run manifest.",
)
return parser
def main() -> int:
args = build_parser().parse_args()
root_dir = Path(args.root_dir)
if not root_dir.exists():
raise FileNotFoundError(f"Root directory does not exist: {root_dir}")
scenes = scan_scenes(root_dir)
summary = build_group_summary(scenes)
print(f"scanned_scenes={len(scenes)}")
print(f"candidate_groups={len(summary)}")
print("top_candidates:")
for item in summary[: args.top_n]:
print(
json.dumps(
{
"count": item["count"],
"tile_key": item["tile_key"],
"group_key": item["group_key"],
"dates": item["dates"],
"receiving_stations": item["receiving_stations"],
},
ensure_ascii=False,
)
)
selected_group = select_group(summary, args.tile_key, args.group_key, args.min_scenes)
if not selected_group:
print("selected_group=None")
return 0
manifest = build_manifest(root_dir, selected_group, scenes)
print(f"selected_group={selected_group}")
print(f"reference_date={manifest['reference_date']}")
if args.manifest_path:
manifest_path = Path(args.manifest_path)
manifest_path.parent.mkdir(parents=True, exist_ok=True)
manifest_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"manifest_written={manifest_path}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+67 -7
View File
@@ -129,6 +129,7 @@ function epochSeconds(value) {
function taskToRunRow(task) {
const taskId = task?.task_id || '';
const message = task?.message || task?.task_name || '';
return {
run_id: taskId,
record_type: 'task',
@@ -145,7 +146,8 @@ function taskToRunRow(task) {
workflow_run_id: '',
root_dir: '',
publish_root_dir: '',
message: task?.message || task?.task_name || '',
message,
inferred_paths: inferTaskPaths(task),
total_items: null,
completed_items: null,
failed_items: null,
@@ -153,6 +155,17 @@ function taskToRunRow(task) {
};
}
function inferTaskPaths(task) {
const message = String(task?.message || '').trim();
const workMatch = message.match(/work_dir=([^)\s]+)/);
const workDir = workMatch ? workMatch[1] : '';
if (!workDir) return {};
return {
work_dir: workDir,
ifgrams_dir: workDir ? `${workDir}\\<project>\\ifgrams` : '',
};
}
function mergeRunRows(productionRuns, recentTasks, limit = 20) {
const rows = (productionRuns || []).map(run => ({
...run,
@@ -206,6 +219,49 @@ function formatPyintPreciseOrbitMode(mode) {
return PYINT_PRECISE_ORBIT_MODE_LABEL[mode] || mode || '-';
}
function getSchemaOptions(schema) {
if (Array.isArray(schema?.enum) && schema.enum.length > 0) return schema.enum;
if (Array.isArray(schema?.choices) && schema.choices.length > 0) return schema.choices;
return [];
}
function formatPathValue(value) {
const text = String(value || '').trim();
return text || '-';
}
function RunPathBlock({ run }) {
const items = Array.isArray(run?.items) ? run.items : [];
const item = items.find(entry => entry?.status === 'RUNNING') || items[0] || null;
const paths = item?.paths || run?.inferred_paths || {};
if (!item && Object.keys(paths).length === 0) return null;
const rows = [
['任务', item?.task_alias || item?.task_name || run?.message || '-'],
['运行目录', paths.run_dir],
['native', paths.native_dir],
['assets', paths.assets_dir],
];
if (run?.engine === 'pyint') {
rows.push(['work', paths.work_dir]);
rows.push(['project', paths.project_dir]);
rows.push(['ifgrams', paths.ifgrams_dir]);
rows.push(['重去平日志', paths.reflatten_dir]);
}
return (
<div style={{ marginTop: 4, display: 'flex', flexDirection: 'column', gap: 2 }}>
{rows.filter(([, value]) => String(value || '').trim()).map(([label, value]) => (
<div key={label} style={{ display: 'flex', gap: 6, alignItems: 'flex-start' }}>
<span style={{ minWidth: 58, color: '#94a3b8' }}>{label}</span>
<span style={{ fontFamily: 'monospace', color: '#334155', wordBreak: 'break-all' }}>
{formatPathValue(value)}
</span>
</div>
))}
</div>
);
}
function PreviewIssueList({ title, items, tone = 'warning' }) {
if (!Array.isArray(items) || items.length === 0) {
return null;
@@ -387,7 +443,8 @@ function ParamField({ name, schema, value, disabled, onChange }) {
);
}
if (Array.isArray(schema.enum) && schema.enum.length > 0) {
const options = getSchemaOptions(schema);
if (options.length > 0) {
return (
<div style={{ minWidth: 180, flex: '0 1 220px' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
@@ -407,12 +464,12 @@ function ParamField({ name, schema, value, disabled, onChange }) {
)}
</div>
<select
value={value ?? schema.default ?? schema.enum[0]}
value={value ?? schema.default ?? options[0]}
disabled={disabled || isReadonly}
onChange={event => onChange(name, event.target.value)}
style={inputStyle}
>
{schema.enum.map(option => (
{options.map(option => (
<option key={option} value={option}>{option}</option>
))}
</select>
@@ -493,7 +550,7 @@ export default function DinsarProductionPanel({ readOnly = false, onJobQueued })
const currentParamSections = buildParamSections(currentParamSchema);
const currentDefaultTimeoutSec = Number(currentEngineObj?.default_timeout_seconds || 0) || 0;
const currentParamHelpText = selectedEngine === 'pyint'
? 'PyINT/Gamma 默认按目标网格尺寸自动换算多视,逻辑与 ENVI/SARscape 自定义流程一致;通常只需要设置目标网格、并行度和是否执行解缠/地理编码。'
? 'PyINT/Gamma 按目标网格尺寸自动换算多视;新增 Gamma 残余重去平在解缠后执行 rascc_mask/quad_fit/quad_sub,再导出 native 和标准 GeoTIFF。'
: selectedEngine === 'isce2'
? '这些参数现在按执行、交付、增强分组展示。结果异常时,优先尝试关闭增强项,再回看基础几何和配对质量。'
: '这些参数影响当前引擎的生产模板。建议先使用默认值,只有在结果边界、噪声或几何表现异常时再逐项调整。';
@@ -1090,7 +1147,7 @@ export default function DinsarProductionPanel({ readOnly = false, onJobQueued })
)}
{selectedEngine === 'pyint' && currentDefaultTimeoutSec > 0 && (
<div style={{ fontSize: 11, color: '#94a3b8', marginTop: 4 }}>
PyINT 默认按单对任务使用 {currentDefaultTimeoutSec} 当前会逐对串行创建工作区并运行外部 PyINT / Gamma 流程
PyINT 默认按单对任务使用 {currentDefaultTimeoutSec} 当前会逐对串行创建工作区并运行外部 PyINT / Gamma 流程native 会在主流程和重去平后统一写入
</div>
)}
</div>
@@ -1512,7 +1569,7 @@ export default function DinsarProductionPanel({ readOnly = false, onJobQueued })
<table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12 }}>
<thead>
<tr style={{ background: '#f8fafc' }}>
{['运行ID', '引擎', '状态', '时间', '操作'].map(header => (
{['运行ID', '引擎', '状态', '时间', '路径', '操作'].map(header => (
<th
key={header}
style={{ padding: '4px 8px', textAlign: 'left', borderBottom: '1px solid #e2e8f0', color: '#64748b' }}
@@ -1538,6 +1595,9 @@ export default function DinsarProductionPanel({ readOnly = false, onJobQueued })
<td style={{ padding: '4px 8px', color: '#94a3b8' }}>
{run.started_at ? new Date(run.started_at * 1000).toLocaleString() : '-'}
</td>
<td style={{ padding: '4px 8px', maxWidth: 520, fontSize: 11 }}>
<RunPathBlock run={run} />
</td>
<td style={{ padding: '4px 8px' }}>
<button
onClick={() => handleViewLog(run)}
+24 -3
View File
@@ -1,17 +1,38 @@
@echo off
setlocal
chcp 65001 >nul
cd /d "%~dp0"
set "APP=%~dp0license_issuer_gui.pyw"
set "PYTHONDONTWRITEBYTECODE=1"
if not defined PYTHON_PATH set "PYTHON_PATH=C:\ProgramData\anaconda3\envs\InSAR\python.exe"
for %%I in ("%PYTHON_PATH%") do set "PYTHONW_PATH=%%~dpIpythonw.exe"
if exist "%PYTHONW_PATH%" (
start "" "%PYTHONW_PATH%" "%APP%"
exit /b 0
)
if exist "%PYTHON_PATH%" (
"%PYTHON_PATH%" "%APP%"
if errorlevel 1 pause
exit /b %errorlevel%
)
where pyw >nul 2>nul
if %errorlevel%==0 (
start "" pyw -3 "%~dp0license_issuer_gui.pyw"
start "" pyw -3 "%APP%"
exit /b 0
)
where pythonw >nul 2>nul
if %errorlevel%==0 (
start "" pythonw "%~dp0license_issuer_gui.pyw"
start "" pythonw "%APP%"
exit /b 0
)
python "%~dp0license_issuer_gui.pyw"
echo [!] Python not found.
echo Expected: %PYTHON_PATH%
echo Or install Python Launcher / pythonw and add it to PATH.
pause
exit /b 1
+1
View File
@@ -135,6 +135,7 @@ def update_template(template_file):
templateDict['atmcor_all'] = '0' # if 1, run atmospheric correction
templateDict['atmcor_all_parallel'] = '1' # multi-processor number used
templateDict['atmcor_use_for_disp'] = '0' # if 1, use atmcor unw as dispmap input
templateDict['geocode_all'] = '0'
templateDict['geocode_all_parallel'] = '1' # multi-processor number used
+25 -6
View File
@@ -16,6 +16,13 @@ import argparse
from pyint import _utils as ut
def _run_or_raise(call_str, stage):
rc = os.system(call_str)
if rc != 0:
raise RuntimeError('%s failed with rc=%s: %s' % (stage, rc, call_str))
return rc
INTRODUCTION = '''
-------------------------------------------------------------------
Unwrap differential interferogram using GAMMA.
@@ -74,10 +81,22 @@ def main(argv):
################ prepare file for parallel processing ###############
HGTSIM = demDir + '/' + masterDate + '_' + rlks + 'rlks.rdc.dem'
Mamp0 = rslcDir + '/' + Mdate + '/' + Mdate + '_' + rlks + 'rlks.amp'
Samp0 = rslcDir + '/' + Sdate + '/' + Sdate + '_' + rlks + 'rlks.amp'
MampPar0 = rslcDir + '/' + Mdate + '/' + Mdate + '_' + rlks + 'rlks.amp.par'
SampPar0 = rslcDir + '/' + Sdate + '/' + Sdate + '_' + rlks + 'rlks.amp.par'
Mamp = workDir + '/' + Mdate + '_' + rlks + 'rlks.amp'
MampPar = workDir + '/' + Mdate + '_' + rlks + 'rlks.amp.par'
Samp = workDir + '/' + Sdate + '_' + rlks + 'rlks.amp'
SampPar = workDir + '/' + Sdate + '_' + rlks + 'rlks.amp.par'
if not os.path.isfile(Mamp):
ut.copy_file(Mamp0, Mamp)
if not os.path.isfile(Samp):
ut.copy_file(Samp0, Samp)
if not os.path.isfile(MampPar):
ut.copy_file(MampPar0, MampPar)
if not os.path.isfile(SampPar):
ut.copy_file(SampPar0, SampPar)
diff_par = workDir + '/' + Mdate + '_' + Sdate + '_diff_par'
UNWlks = workDir + '/' + Pair + '_' +rlks + 'rlks.diff_filt.unw'
CORMASK = workDir + '/' + Pair + '_' +rlks + 'rlks.diff_filt.cor'
@@ -88,18 +107,18 @@ def main(argv):
nLine = ut.read_gamma_par(MampPar, 'read', 'azimuth_lines')
###############################################################
call_str = "create_diff_par " + MampPar + " " + SampPar + " " + diff_par + " 1 0 "
os.system(call_str)
_run_or_raise(call_str, 'create_diff_par_atmcor')
call_str = "atm_mod_2d " + UNWlks + " " + HGTSIM + " " + CORMASK + " " + diff_par + " " + " - 0 a0 a1 sigma sigma_h s1"
os.system(call_str)
_run_or_raise(call_str, 'atm_mod_2d')
call_str = "atm_sim_2d " + diff_par + " " + HGTSIM + " a0 a1 " + ATM_PHASE
os.system(call_str)
_run_or_raise(call_str, 'atm_sim_2d')
call_str = "sub_phase " + UNWlks + " " + ATM_PHASE + " " + diff_par + " " + ATMCOR_UNW + " 0 0 0 "
os.system(call_str)
_run_or_raise(call_str, 'sub_phase_atmcor')
call_str = 'rasrmg ' + ATMCOR_UNW + ' ' + Mamp + ' ' + nWidth + ' - - - - - - - - - - '
os.system(call_str)
_run_or_raise(call_str, 'rasrmg_atmcor_unw')
print("Correct atmospheric phase is done!")
sys.exit(1)
sys.exit(0)
if __name__ == '__main__':
main(sys.argv[:])
+2 -2
View File
@@ -92,7 +92,7 @@ def main(argv):
else:
ifgList=ifgList0[:,0]
err_txt = scratchDir + '/' + projectName + '/unwrap_gamma_all.err'
err_txt = scratchDir + '/' + projectName + '/atm_correction_gamma_all.err'
if os.path.isfile(err_txt): os.remove(err_txt)
data_para = []
@@ -114,7 +114,7 @@ def main(argv):
print("Correct atmospheric phase for project %s is done! " % projectName)
ut.print_process_time(start_time, time.time())
sys.exit(1)
sys.exit(0)
if __name__ == '__main__':
main(sys.argv[:])
+71 -2
View File
@@ -23,6 +23,39 @@ def _run_or_raise(call_str, stage):
return rc
def _is_binary_all_zero(path, chunk_size=1024 * 1024):
if not os.path.isfile(path):
return False
with open(path, 'rb') as handle:
while True:
chunk = handle.read(chunk_size)
if not chunk:
return True
if any(chunk):
return False
def _run_slc_diff_intf(
Mrslc,
Srslc,
MrslcPar,
SrslcPar,
OFF,
SIM_UNW,
DIFF_IFG,
rlks,
azlks,
spsflg,
azfflg,
):
call_str = (
'SLC_diff_intf ' + Mrslc + ' ' + Srslc + ' ' + MrslcPar + ' ' + SrslcPar
+ ' ' + OFF + ' ' + SIM_UNW + ' ' + DIFF_IFG + ' ' + rlks + ' ' + azlks
+ ' ' + spsflg + ' ' + azfflg + ' - 1 1'
)
_run_or_raise(call_str, 'SLC_diff_intf')
INTRODUCTION = '''
-------------------------------------------------------------------
Generate differential interferogram image from SLC using GAMMA.
@@ -138,8 +171,44 @@ def main(argv):
_run_or_raise(call_str, 'phase_sim_orb')
DIFF_IFG = workDir + '/' + Pair + '_' + rlks + 'rlks.diff'
call_str = 'SLC_diff_intf ' + Mrslc + ' ' + Srslc + ' ' + MrslcPar + ' ' + SrslcPar + ' ' + OFF + ' ' + SIM_UNW + ' ' + DIFF_IFG + ' ' + rlks + ' ' + azlks + ' ' + templateDict['Igram_Spsflg'] + ' ' + templateDict['Igram_Azfflg'] + ' - 1 1'
_run_or_raise(call_str, 'SLC_diff_intf')
_run_slc_diff_intf(
Mrslc,
Srslc,
MrslcPar,
SrslcPar,
OFF,
SIM_UNW,
DIFF_IFG,
rlks,
azlks,
templateDict['Igram_Spsflg'],
templateDict['Igram_Azfflg'],
)
if _is_binary_all_zero(DIFF_IFG):
if templateDict['Igram_Azfflg'] == '1':
print(
'SLC_diff_intf produced an all-zero interferogram with azimuth common-band '
'filtering enabled; retrying once with Igram_Azfflg=0.'
)
os.remove(DIFF_IFG)
_run_slc_diff_intf(
Mrslc,
Srslc,
MrslcPar,
SrslcPar,
OFF,
SIM_UNW,
DIFF_IFG,
rlks,
azlks,
templateDict['Igram_Spsflg'],
'0',
)
if _is_binary_all_zero(DIFF_IFG):
raise RuntimeError(
'SLC_diff_intf produced an invalid all-zero interferogram after one bounded '
'azimuth-filter fallback; no further automatic retries will be attempted: %s' % DIFF_IFG
)
##### filtering process & coherence estimation ###########
DIFFFILT = workDir + '/' + Pair + '_' + rlks + 'rlks.diff_filt'
+46 -33
View File
@@ -32,6 +32,26 @@ def _resolve_existing_master_date(slc_dir, requested_date):
print('masterDate %s not found; using existing SLC date: %s' % (requested_date, resolved))
return resolved
def _run_or_raise(call_str, stage):
rc = os.system(call_str)
if rc != 0:
raise RuntimeError('%s failed with rc=%s: %s' % (stage, rc, call_str))
return rc
def _run_or_warn(call_str, stage):
rc = os.system(call_str)
if rc != 0:
print('WARNING: %s returned rc=%s; continuing with subsequent GAMMA refinement: %s' % (stage, rc, call_str))
return rc
def _remove_files(paths):
for path in paths:
if os.path.isfile(path):
os.remove(path)
def cmdLineParse():
parser = argparse.ArgumentParser(description='Generate radar-coordinates based DEM.',\
formatter_class=argparse.RawTextHelpFormatter,\
@@ -78,38 +98,32 @@ def main(argv):
azlks = templateDict['azimuth_looks']
if not os.path.isdir(processDir):
call_str = 'mkdir ' + processDir
os.system(call_str)
os.makedirs(processDir)
simDir = scratchDir + '/' + projectName + "/DEM"
if not os.path.isdir(simDir):
call_str='mkdir ' + simDir
workDir = simDir
if 'DEM' in templateDict:
dem = templateDict['DEM']
if not os.path.isfile(dem):
dem = DEMDir + '/' + projectName + '/' + projectName + '.dem'
call_str = 'echo DEM= ' + dem + ' >> ' + templateFile
os.system(call_str)
with open(templateFile, 'a') as stream:
stream.write('DEM= ' + dem + '\n')
templateDict['DEM'] = dem
else:
dem = DEMDir + '/' + projectName + '/' + projectName + '.dem'
call_str = 'echo DEM = ' + dem + ' >> ' + templateFile
os.system(call_str)
with open(templateFile, 'a') as stream:
stream.write('DEM = ' + dem + '\n')
demPar = dem + ".par"
if not os.path.isfile(dem):
call_str = 'makedem_pyint.py ' + projectName
os.system(call_str)
_run_or_raise(call_str, 'makedem_pyint')
# Parameter setting for simPhase
latovrSimphase = templateDict['dem_lat_ovr']
lonovrSimphase = templateDict['dem_lon_ovr']
rposSimphase = templateDict['Simphase_rpos']
azposSimphase = templateDict['Simphase_azpos']
rwinSimphase = templateDict['Simphase_rwin']
@@ -154,12 +168,7 @@ def main(argv):
### remove DEM look up table if it existed for considering gamma overlapping
if os.path.isfile(UTMDEM):
os.remove(UTMDEM)
if os.path.isfile(UTMDEMpar):
os.remove(UTMDEMpar)
if os.path.isfile(UTM2RDC):
os.remove(UTM2RDC)
_remove_files([UTMDEM, UTMDEMpar, UTM2RDC, SIMSARUTM, PIX, LSMAP])
nWidthUTMDEM0 = ut.read_gamma_par(demPar, 'read', 'width')
DateFormat = ut.read_gamma_par(demPar, 'read', 'data_format:')
@@ -174,42 +183,46 @@ def main(argv):
if not os.path.isfile(tmp_dem):
call_str = 'replace_values ' + dem + ' -32767 0 ' + tmp_dem + ' ' + nWidthUTMDEM0 + ' 2 ' + DF_type
os.system(call_str)
_run_or_raise(call_str, 'replace_values_dem_voids')
call_str = 'cp ' + tmp_dem + ' ' + dem
os.system(call_str)
_run_or_raise(call_str, 'copy_dem_without_voids')
call_str = "multi_look " + MslcImg + " " + MslcPar + " " + MamprlksImg + " " + MamprlksPar + " " + rlks + " " + azlks
os.system(call_str)
_run_or_raise(call_str, 'multi_look_master_for_dem')
call_str = 'gc_map1 ' + MamprlksPar + ' ' + '-' + ' ' + demPar + ' ' + dem + ' ' + UTMDEMpar + ' ' + UTMDEM + ' ' + UTM2RDC + ' ' + latovrSimphase + ' ' + lonovrSimphase + ' ' + SIMSARUTM + ' - - - - ' + PIX + ' ' + LSMAP + ' - 3 128'
#call_str = 'gc_map2 ' + MamprlksPar + ' ' + demPar + ' ' + dem + ' ' + UTMDEMpar + ' ' + UTMDEM + ' ' + UTM2RDC + ' ' + latovrSimphase + ' ' + lonovrSimphase + ' ' + ' ' + LSMAP + ' - - - - ' + SIMSARUTM + ' - - - ' + PIX
os.system(call_str)
def run_gc_map1(stage, lat_ovr, lon_ovr):
call = 'gc_map1 ' + MamprlksPar + ' ' + '-' + ' ' + demPar + ' ' + dem + ' ' + UTMDEMpar + ' ' + UTMDEM + ' ' + UTM2RDC + ' ' + lat_ovr + ' ' + lon_ovr + ' ' + SIMSARUTM + ' - - - - ' + PIX + ' ' + LSMAP + ' - 3 128'
_run_or_raise(call, stage)
run_gc_map1('gc_map1_initial_dem_segment', latovrSimphase, lonovrSimphase)
nWidthUTMDEM = ut.read_gamma_par(UTMDEMpar, 'read', 'width')
nLinePWR1 = ut.read_gamma_par(MamprlksPar, 'read', 'azimuth_lines')
nWidth = ut.read_gamma_par(MamprlksPar, 'read', 'range_samples')
call_str = 'geocode ' + UTM2RDC + ' ' + SIMSARUTM + ' ' + nWidthUTMDEM + ' ' + SIMSARRDC + ' ' + nWidth + ' ' + nLinePWR1 + ' 0 0'
os.system(call_str)
# 30 m DEM grids can be much sparser than LT-1 multi-look radar pixels.
# Keep this in GAMMA by widening geocode's search radius for RDC filling.
call_str = 'geocode ' + UTM2RDC + ' ' + SIMSARUTM + ' ' + nWidthUTMDEM + ' ' + SIMSARRDC + ' ' + nWidth + ' ' + nLinePWR1 + ' 0 0 - - 2 64 1'
_run_or_raise(call_str, 'geocode_sim_sar_to_rdc')
call_str = 'create_diff_par ' + MamprlksPar + ' ' + MamprlksPar + ' ' + SIMDIFFpar + ' 1 < ' + BLANK
os.system(call_str)
_run_or_raise(call_str, 'create_diff_par_sim')
call_str = 'init_offsetm ' + SIMSARRDC + ' ' + MamprlksImg + ' ' + SIMDIFFpar + ' 2 2 ' + rposSimphase + ' ' + azposSimphase #+ ' - - - 512'
os.system(call_str)
_run_or_warn(call_str, 'init_offsetm_sim')
call_str = 'offset_pwrm ' + SIMSARRDC + ' ' + MamprlksImg + ' ' + SIMDIFFpar + ' ' + SIMOFFS + ' ' + SIMSNR + ' ' + rwinSimphase + ' ' + azwinSimphase + ' ' + SIMOFFSET #+ ' - 128 128 ' + threshSimphase
os.system(call_str)
_run_or_raise(call_str, 'offset_pwrm_sim')
call_str = 'offset_fitm ' + SIMOFFS + ' ' + SIMSNR + ' ' + SIMDIFFpar + ' ' + SIMCOFF + ' ' + SIMCOFFSETS + ' - > ' + OFFSTD
os.system(call_str)
_run_or_raise(call_str, 'offset_fitm_sim')
call_str = 'gc_map_fine ' + UTM2RDC + ' ' + nWidthUTMDEM + ' ' + SIMDIFFpar + ' ' + UTMTORDC + ' 1'
#print(call_str)
os.system(call_str)
_run_or_raise(call_str, 'gc_map_fine')
call_str = 'geocode ' + UTMTORDC + ' ' + UTMDEM + ' ' + nWidthUTMDEM + ' ' + HGTSIM + ' ' + nWidth + ' ' + nLinePWR1 + ' 0 0 - - 1 1 1'
os.system(call_str)
call_str = 'geocode ' + UTMTORDC + ' ' + UTMDEM + ' ' + nWidthUTMDEM + ' ' + HGTSIM + ' ' + nWidth + ' ' + nLinePWR1 + ' 0 0 - - 2 64 1'
_run_or_raise(call_str, 'geocode_dem_to_rdc')
required_outputs = [UTMDEMpar, UTMDEM, UTMTORDC, HGTSIM]
+23 -26
View File
@@ -8,21 +8,16 @@
import os
import sys
import argparse
import numpy as np
from pyint import _utils as ut
def sanitize_gamma_float(filepath, valid_max=1e6):
"""清理 GAMMA 浮点数据文件中的无效值 (NaN/Inf/极端值 → 0.0)"""
data = np.fromfile(filepath, dtype=np.float32)
bad_mask = ~np.isfinite(data) | (np.abs(data) > valid_max)
n_bad = int(np.sum(bad_mask))
if n_bad > 0:
data[bad_mask] = 0.0
data.tofile(filepath)
print(f' [sanitize] {os.path.basename(filepath)}: '
f'清理 {n_bad} 个无效像素')
def _run_or_raise(call_str, stage):
rc = os.system(call_str)
if rc != 0:
raise RuntimeError('%s failed with rc=%s: %s' % (stage, rc, call_str))
return rc
def geocode(inFile, outFile, UTMTORDC, nWidth, nWidthUTMDEM, nLineUTMDEM, geo_interp='0'):
@@ -37,7 +32,7 @@ def geocode(inFile, outFile, UTMTORDC, nWidth, nWidthUTMDEM, nLineUTMDEM, geo_in
else:
call_str = 'geocode_back ' + inFile + ' ' + nWidth + ' ' + UTMTORDC + ' ' + outFile + ' ' + nWidthUTMDEM + ' ' + nLineUTMDEM+ ' ' + geo_interp + ' 1'
os.system(call_str)
_run_or_raise(call_str, 'geocode_back_' + os.path.basename(outFile))
return
@@ -160,13 +155,10 @@ def geocode_pot(projectName, Pair, templateDict):
if not os.path.isfile(geo_real):
os.system(f'cpx_to_real {geo_disp} {geo_real} {eqa_width} 0')
sanitize_gamma_float(geo_real, disp_max_m)
if not os.path.isfile(geo_imag):
os.system(f'cpx_to_real {geo_disp} {geo_imag} {eqa_width} 1')
sanitize_gamma_float(geo_imag, disp_max_m)
if not os.path.isfile(geo_mag):
os.system(f'cpx_to_real {geo_disp} {geo_mag} {eqa_width} 3')
sanitize_gamma_float(geo_mag, disp_max_m)
# ===== Step D: 地理编码 MLI 背景 =====
geo_mli = workDir + '/geo_' + masterDate + '.mli'
@@ -288,10 +280,13 @@ def main(argv):
# --- 基础产品地理编码 (hyp3/licsbas 均需) ---
geo_interp = templateDict['geo_interp']
atmcor_use_for_disp = str(templateDict.get('atmcor_use_for_disp', '0')).strip() == '1'
geocode(Mamp, GeoMamp, UTMTORDC, nWidth, nWidthUTMDEM, nLineUTMDEM, geo_interp=geo_interp)
geocode(CORIFG, GeoCOR, UTMTORDC, nWidth, nWidthUTMDEM, nLineUTMDEM, geo_interp=geo_interp)
geocode(DIFFIFG, GeoDIFF, UTMTORDC, nWidth, nWidthUTMDEM, nLineUTMDEM, geo_interp=geo_interp)
geocode(UNWIFG, GeoUNW, UTMTORDC, nWidth, nWidthUTMDEM, nLineUTMDEM, geo_interp=geo_interp)
if os.path.isfile(ATMCOR_UNW):
geocode(ATMCOR_UNW, GeoATMCOR_UNW, UTMTORDC, nWidth, nWidthUTMDEM, nLineUTMDEM, geo_interp=geo_interp)
geocode(diffifg, geodiff, UTMTORDC, nWidth, nWidthUTMDEM, nLineUTMDEM, geo_interp=geo_interp)
geocode(rdcdem, Geodem, UTMTORDC, nWidth, nWidthUTMDEM, nLineUTMDEM, geo_interp=geo_interp)
@@ -303,29 +298,31 @@ def main(argv):
# --- hyp3 专属: dispmap (LOS/vert 位移场) ---
if need_hyp3:
print(' [hyp3] dispmap + geocode 位移场')
los_disp_rdc = workDir + '/' + Pair + '_' + rlks + 'rlks.los_disp'
vert_disp_rdc = workDir + '/' + Pair + '_' + rlks + 'rlks.vert_disp'
disp_unw_source = ATMCOR_UNW if atmcor_use_for_disp and os.path.isfile(ATMCOR_UNW) else UNWIFG
disp_tag = '.atmcor' if disp_unw_source == ATMCOR_UNW else ''
los_disp_rdc = workDir + '/' + Pair + '_' + rlks + 'rlks' + disp_tag + '.los_disp'
vert_disp_rdc = workDir + '/' + Pair + '_' + rlks + 'rlks' + disp_tag + '.vert_disp'
if os.path.isfile(UNWIFG) and os.path.isfile(SLCpar) and os.path.isfile(OFFpar):
if os.path.isfile(disp_unw_source) and os.path.isfile(SLCpar) and os.path.isfile(OFFpar):
hgt_arg = rdcdem if os.path.isfile(rdcdem) else '-'
if not os.path.isfile(los_disp_rdc):
os.system(f'dispmap {UNWIFG} {hgt_arg} {SLCpar} {OFFpar} {los_disp_rdc} 0')
_run_or_raise(f'dispmap {disp_unw_source} {hgt_arg} {SLCpar} {OFFpar} {los_disp_rdc} 0', 'dispmap_los')
if not os.path.isfile(vert_disp_rdc):
os.system(f'dispmap {UNWIFG} {hgt_arg} {SLCpar} {OFFpar} {vert_disp_rdc} 1')
_run_or_raise(f'dispmap {disp_unw_source} {hgt_arg} {SLCpar} {OFFpar} {vert_disp_rdc} 1', 'dispmap_vertical')
geo_los = workDir + '/geo_' + Pair + '_' + rlks + 'rlks.los_disp'
geo_vert = workDir + '/geo_' + Pair + '_' + rlks + 'rlks.vert_disp'
geo_los = workDir + '/geo_' + Pair + '_' + rlks + 'rlks' + disp_tag + '.los_disp'
geo_vert = workDir + '/geo_' + Pair + '_' + rlks + 'rlks' + disp_tag + '.vert_disp'
if os.path.isfile(los_disp_rdc) and not os.path.isfile(geo_los):
os.system(f'geocode_back {los_disp_rdc} {nWidth} {UTMTORDC} {geo_los} {nWidthUTMDEM} {nLineUTMDEM} 1 0')
_run_or_raise(f'geocode_back {los_disp_rdc} {nWidth} {UTMTORDC} {geo_los} {nWidthUTMDEM} {nLineUTMDEM} {geo_interp} 0', 'geocode_back_los_disp')
if os.path.isfile(vert_disp_rdc) and not os.path.isfile(geo_vert):
os.system(f'geocode_back {vert_disp_rdc} {nWidth} {UTMTORDC} {geo_vert} {nWidthUTMDEM} {nLineUTMDEM} 1 0')
_run_or_raise(f'geocode_back {vert_disp_rdc} {nWidth} {UTMTORDC} {geo_vert} {nWidthUTMDEM} {nLineUTMDEM} {geo_interp} 0', 'geocode_back_vert_disp')
# --- hyp3 专属: 缠绕相位 ---
if need_hyp3:
print(' [hyp3] 提取缠绕相位')
geo_wrapped_pha = workDir + '/geo_' + Pair + '_' + rlks + 'rlks.diff_filt.pha'
if os.path.isfile(GeoDIFF) and not os.path.isfile(geo_wrapped_pha):
os.system(f'cpx_to_real {GeoDIFF} {geo_wrapped_pha} {nWidthUTMDEM} 4')
_run_or_raise(f'cpx_to_real {GeoDIFF} {geo_wrapped_pha} {nWidthUTMDEM} 4', 'cpx_to_real_wrapped_phase')
# --- hyp3/licsbas 共需: look_vector ---
if need_hyp3 or need_licsbas:
@@ -334,7 +331,7 @@ def main(argv):
lv_phi = workDir + '/lv_phi'
if not os.path.isfile(lv_theta) or not os.path.isfile(lv_phi):
if os.path.isfile(SLCpar) and os.path.isfile(OFFpar) and os.path.isfile(UTMDEMpar0) and os.path.isfile(UTMDEM):
os.system(f'look_vector {SLCpar} {OFFpar} {UTMDEMpar0} {UTMDEM} {lv_theta} {lv_phi}')
_run_or_raise(f'look_vector {SLCpar} {OFFpar} {UTMDEMpar0} {UTMDEM} {lv_theta} {lv_phi}', 'look_vector')
# --- BMP 可视化 ---
os.system('rasmph_pwr ' + GeoDIFF + ' ' + GeoMamp + ' ' + nWidthUTMDEM + ' - - - - ')
+1 -1
View File
@@ -114,7 +114,6 @@ def _build_dem_from_existing_source(
print(f"Using prepared DEM source: {source_dem}")
print(f"Clipping prepared DEM window: west={west}, south={south}, east={east}, north={north}")
_run_checked(
[
"gdal_translate",
@@ -130,6 +129,7 @@ def _build_dem_from_existing_source(
],
cwd=str(work_dir),
)
_run_checked(
[
"makedem.py",
+4
View File
@@ -193,6 +193,10 @@ diff_all_parallel = 4 # Number of parallel processors
unwrap_all = 1 # Unwrap interferograms [0: skip; 1: process]
unwrap_all_parallel = 4 # Number of parallel processors
atmcor_all = 0 # Gamma atmospheric phase correction [0: skip; 1: process]
atmcor_all_parallel = 1 # Number of parallel processors
atmcor_use_for_disp = 0 # Use atmcor unwrapped phase for dispmap [0: no; 1: yes]
geocode_all = 1 # Geocode products [0: skip; 1: process]
geocode_all_parallel = 4 # Number of parallel processors
-1
View File
@@ -108,7 +108,6 @@ def main(argv):
CORMASK = workDir + '/' + Pair + '_' +rlks + 'rlks.diff_filt.cor'
WRAPlks = workDir + '/' + Pair + '_' +rlks + 'rlks.diff_filt'
UNWlks = workDir + '/' + Pair + '_' +rlks + 'rlks.diff_filt.unw'
CORMASKbmp = CORMASK.replace('.diff_filt.cor','.diff_filt.cor_mask.bmp')
if os.path.isfile(CORMASKbmp):